Skip to content

Commit

Permalink
Merge pull request #24895 from oscarbenjamin/pr_assumptions_backport
Browse files Browse the repository at this point in the history
Backport of assumptions under evaluate=False fix
  • Loading branch information
oscarbenjamin committed Mar 11, 2023
2 parents 311c94b + c646df2 commit 8bd31f6
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 19 deletions.
16 changes: 12 additions & 4 deletions sympy/core/add.py
Expand Up @@ -678,8 +678,12 @@ def _eval_is_imaginary(self):
return
elif a.is_imaginary:
im_I.append(a*S.ImaginaryUnit)
elif (S.ImaginaryUnit*a).is_extended_real:
im_I.append(a*S.ImaginaryUnit)
elif a.is_Mul and S.ImaginaryUnit in a.args:
coeff, ai = a.as_coeff_mul(S.ImaginaryUnit)
if ai == (S.ImaginaryUnit,) and coeff.is_extended_real:
im_I.append(-coeff)
else:
return
else:
return
b = self.func(*nz)
Expand Down Expand Up @@ -708,8 +712,12 @@ def _eval_is_zero(self):
return
elif a.is_imaginary:
im += 1
elif (S.ImaginaryUnit*a).is_extended_real:
im_or_z = True
elif a.is_Mul and S.ImaginaryUnit in a.args:
coeff, ai = a.as_coeff_mul(S.ImaginaryUnit)
if ai == (S.ImaginaryUnit,) and coeff.is_extended_real:
im_or_z = True
else:
return
else:
return
if z == len(self.args):
Expand Down
14 changes: 14 additions & 0 deletions sympy/core/tests/test_assumptions.py
Expand Up @@ -703,6 +703,18 @@ def test_other_symbol():
assert x.is_integer is False


def test_evaluate_false():
# Previously this failed because the assumptions query would make new
# expressions and some of the evaluation logic would fail under
# evaluate(False).
from sympy.core.parameters import evaluate
from sympy.abc import x, h
f = 2**x**7
with evaluate(False):
fh = f.xreplace({x: x+h})
assert fh.exp.is_rational is None


def test_issue_3825():
"""catch: hash instability"""
x = Symbol("x")
Expand Down Expand Up @@ -1158,9 +1170,11 @@ def test_issue_10302():
r = Symbol('r', real=True)
u = -(3*2**pi)**(1/pi) + 2*3**(1/pi)
i = u + u*I

assert i.is_real is None # w/o simplification this should fail
assert (u + i).is_zero is None
assert (1 + i).is_zero is False

a = Dummy('a', zero=True)
assert (a + I).is_zero is False
assert (a + r*I).is_zero is None
Expand Down
28 changes: 23 additions & 5 deletions sympy/solvers/solveset.py
Expand Up @@ -879,17 +879,35 @@ def _solve_radical(f, unradf, symbol, solveset_solver):
result = Union(*[imageset(Lambda(y, g_y), f_y_sols)
for g_y in g_y_s])

if not isinstance(result, FiniteSet):
solution_set = result
else:
def check_finiteset(solutions):
f_set = [] # solutions for FiniteSet
c_set = [] # solutions for ConditionSet
for s in result:
for s in solutions:
if checksol(f, symbol, s):
f_set.append(s)
else:
c_set.append(s)
solution_set = FiniteSet(*f_set) + ConditionSet(symbol, Eq(f, 0), FiniteSet(*c_set))
return FiniteSet(*f_set) + ConditionSet(symbol, Eq(f, 0), FiniteSet(*c_set))

def check_set(solutions):
if solutions is S.EmptySet:
return solutions
elif isinstance(solutions, ConditionSet):
# XXX: Maybe the base set should be checked?
return solutions
elif isinstance(solutions, FiniteSet):
return check_finiteset(solutions)
elif isinstance(solutions, Complement):
A, B = solutions.args
return Complement(check_set(A), B)
elif isinstance(solutions, Union):
return Union(*[check_set(s) for s in solutions.args])
else:
# XXX: There should be more cases checked here. The cases above
# are all those that come up in the test suite for now.
return solutions

solution_set = check_set(result)

return solution_set

Expand Down
34 changes: 24 additions & 10 deletions sympy/solvers/tests/test_solveset.py
Expand Up @@ -533,11 +533,9 @@ def test_solve_sqrt_3():
sqrt(10)*sin(atan(3*sqrt(111)/251)/3)/3 +
sqrt(30)*cos(atan(3*sqrt(111)/251)/3)/3)]

assert sol._args[0] == FiniteSet(*fset)
assert sol._args[1] == ConditionSet(
R,
Eq(sqrt(2)*R*sqrt(1/(R + 1)) + (R + 1)*(sqrt(2)*sqrt(1/(R + 1)) - 1), 0),
FiniteSet(*cset))
fs = FiniteSet(*fset)
cs = ConditionSet(R, Eq(eq, 0), FiniteSet(*cset))
assert sol == (fs - {-1}) | (cs - {-1})

# the number of real roots will depend on the value of m: for m=1 there are 4
# and for m=-1 there are none.
Expand Down Expand Up @@ -2404,14 +2402,30 @@ def test_issue_11174():


def test_issue_11534():
# eq and eq2 should give the same solution as a Complement
# eq1 and eq2 should not have the same solutions because squaring both
# sides of the radical equation introduces a spurious solution branch.
# The equations have a symbolic parameter y and it is easy to see that for
# y != 0 the solution s1 will not be valid for eq1.
x = Symbol('x', real=True)
y = Symbol('y', real=True)
eq = -y + x/sqrt(-x**2 + 1)
eq1 = -y + x/sqrt(-x**2 + 1)
eq2 = -y**2 + x**2/(-x**2 + 1)
soln = Complement(FiniteSet(-y/sqrt(y**2 + 1), y/sqrt(y**2 + 1)), FiniteSet(-1, 1))
assert solveset(eq, x, S.Reals) == soln
assert solveset(eq2, x, S.Reals) == soln

# We get a ConditionSet here because s1 works in eq1 if y is equal to zero
# although not for any other value of y. That case is redundant though
# because if y=0 then s1=s2 so the solution for eq1 could just be returned
# as s2 - {-1, 1}. In fact we have
# |y/sqrt(y**2 + 1)| < 1
# So the complements are not needed either. The ideal output here would be
# sol1 = s2
# sol2 = s1 | s2.
s1, s2 = FiniteSet(-y/sqrt(y**2 + 1)), FiniteSet(y/sqrt(y**2 + 1))
cset = ConditionSet(x, Eq(eq1, 0), s1)
sol1 = (s2 - {-1, 1}) | (cset - {-1, 1})
sol2 = (s1 | s2) - {-1, 1}

assert solveset(eq1, x, S.Reals) == sol1
assert solveset(eq2, x, S.Reals) == sol2


def test_issue_10477():
Expand Down

0 comments on commit 8bd31f6

Please sign in to comment.