Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Make complex->finite in the codebase. #16978

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion sympy/algebras/quaternion.py
Expand Up @@ -247,7 +247,7 @@ def add(self, other):
# If q2 is a number or a sympy expression instead of a quaternion
if not isinstance(q2, Quaternion):
if q1.real_field:
if q2.is_complex:
if q2.is_complex or q2.is_infinite:
ShubhamKJha marked this conversation as resolved.
Show resolved Hide resolved
return Quaternion(re(q2) + q1.a, im(q2) + q1.b, q1.c, q1.d)
else:
# q2 is something strange, do not evaluate:
Expand Down
10 changes: 5 additions & 5 deletions sympy/core/assumptions.py
Expand Up @@ -165,14 +165,14 @@
'integer -> rational',
'rational -> real',
'rational -> algebraic',
'algebraic -> complex & finite',
'transcendental == complex & !algebraic & finite',
'algebraic -> complex',
'transcendental == complex & !algebraic',
'real -> hermitian',
'imaginary -> complex & finite',
'imaginary -> complex',
'imaginary -> antihermitian',
'extended_real -> commutative',
'complex -> commutative',
'complex -> infinite | finite',
'complex -> finite',

'odd == integer & !even',
'even == integer & !odd',
Expand Down Expand Up @@ -376,7 +376,7 @@ def __init__(cls, *args, **kws):
if pname not in cls.__dict__:
setattr(cls, pname, make_property(fact))

# Finally, add any missing automagic property (e.g. for Basic)
# Finally, add any missing automatic property (e.g. for Basic)
ShubhamKJha marked this conversation as resolved.
Show resolved Hide resolved
for fact in _assume_defined:
pname = as_property(fact)
if not hasattr(cls, pname):
Expand Down
10 changes: 5 additions & 5 deletions sympy/core/expr.py
Expand Up @@ -332,7 +332,7 @@ def __ge__(self, other):
except SympifyError:
raise TypeError("Invalid comparison %s >= %s" % (self, other))
for me in (self, other):
if me.is_complex and me.is_extended_real is False:
if (me.is_complex or me.is_infinite) and me.is_extended_real is False:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general this replacement will not be equivalent since if x is complex then (1/x) will be complex or infinite but (1/x).is_complex and (1/x).is_infinite will both return None. It isn't possible to make something equivalent without having extended_complex.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the is_complex check here at all? Shouldn't we just raise if extended_real is False?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Same goes for the other comparison methods below.)

raise TypeError("Invalid comparison of complex %s" % me)
if me is S.NaN:
raise TypeError("Invalid NaN comparison")
Expand All @@ -355,7 +355,7 @@ def __le__(self, other):
except SympifyError:
raise TypeError("Invalid comparison %s <= %s" % (self, other))
for me in (self, other):
if me.is_complex and me.is_extended_real is False:
if (me.is_complex or me.is_infinite) and me.is_extended_real is False:
raise TypeError("Invalid comparison of complex %s" % me)
if me is S.NaN:
raise TypeError("Invalid NaN comparison")
Expand All @@ -378,7 +378,7 @@ def __gt__(self, other):
except SympifyError:
raise TypeError("Invalid comparison %s > %s" % (self, other))
for me in (self, other):
if me.is_complex and me.is_extended_real is False:
if (me.is_complex or me.is_infinite) and me.is_extended_real is False:
raise TypeError("Invalid comparison of complex %s" % me)
if me is S.NaN:
raise TypeError("Invalid NaN comparison")
Expand All @@ -402,7 +402,7 @@ def __lt__(self, other):
except SympifyError:
raise TypeError("Invalid comparison %s < %s" % (self, other))
for me in (self, other):
if me.is_complex and me.is_extended_real is False:
if (me.is_complex or me.is_infinite) and me.is_extended_real is False:
raise TypeError("Invalid comparison of complex %s" % me)
if me is S.NaN:
raise TypeError("Invalid NaN comparison")
Expand Down Expand Up @@ -1039,7 +1039,7 @@ def conjugate(self):

def _eval_transpose(self):
from sympy.functions.elementary.complexes import conjugate
if self.is_complex:
if (self.is_complex or self.is_infinite):
return self
elif self.is_hermitian:
return conjugate(self)
Expand Down
15 changes: 12 additions & 3 deletions sympy/core/mul.py
Expand Up @@ -1105,8 +1105,17 @@ def _eval_is_algebraic_expr(self, syms):

_eval_is_commutative = lambda self: _fuzzy_group(
a.is_commutative for a in self.args)
_eval_is_complex = lambda self: _fuzzy_group(
(a.is_complex for a in self.args), quick_exit=True)

def _eval_is_complex(self):
comp = _fuzzy_group((a.is_complex for a in self.args))
if comp is False:
if any(a.is_infinite for a in self.args):
if any(a.is_zero for a in self.args):
return S.NaN.is_infinite
if any(a.is_zero is None for a in self.args):
return None
return False
return comp

def _eval_is_finite(self):
if all(a.is_finite for a in self.args):
Expand Down Expand Up @@ -1179,7 +1188,7 @@ def _eval_real_imag(self, real):
t_not_re_im = None

for t in self.args:
if t.is_complex is False and t.is_extended_real is False:
if (t.is_complex or t.is_infinite) is False and t.is_extended_real is False:
return False
elif t.is_imaginary: # I
real = not real
Expand Down
2 changes: 1 addition & 1 deletion sympy/core/numbers.py
Expand Up @@ -3342,7 +3342,7 @@ class ComplexInfinity(with_metaclass(Singleton, AtomicExpr)):
is_infinite = True
is_number = True
is_prime = False
is_complex = True
is_complex = False
is_extended_real = False

__slots__ = []
Expand Down
5 changes: 3 additions & 2 deletions sympy/core/power.py
Expand Up @@ -594,7 +594,8 @@ def _eval_is_extended_real(self):
return i.is_integer

def _eval_is_complex(self):
if all(a.is_complex for a in self.args):

if all(a.is_complex for a in self.args) and self._eval_is_finite():
return True

def _eval_is_imaginary(self):
Expand Down Expand Up @@ -846,7 +847,7 @@ def _eval_conjugate(self):

def _eval_transpose(self):
from sympy.functions.elementary.complexes import transpose
i, p = self.exp.is_integer, self.base.is_complex
i, p = self.exp.is_integer, (self.base.is_complex or self.base.is_infinite)
if p:
return self.base**self.exp
if i:
Expand Down
30 changes: 15 additions & 15 deletions sympy/core/tests/test_arit.py
Expand Up @@ -1294,7 +1294,7 @@ def test_Mul_is_imaginary_real():
assert (r*i1*i2).is_real is True

# Github's issue 5874:
nr = Symbol('nr', real=False, complex=True, finite=True) # e.g. I or 1 + I
nr = Symbol('nr', real=False, complex=True) # e.g. I or 1 + I
a = Symbol('a', real=True, nonzero=True)
b = Symbol('b', real=True)
assert (i1*nr).is_real is None
Expand Down Expand Up @@ -1897,38 +1897,38 @@ def test_mul_coeff():


def test_mul_zero_detection():
nz = Dummy(real=True, zero=False, finite=True)
r = Dummy(real=True)
c = Dummy(real=False, complex=True, finite=True)
c2 = Dummy(real=False, complex=True, finite=True)
i = Dummy(imaginary=True, finite=True)
nz = Dummy(real=True, zero=False)
r = Dummy(extended_real=True)
c = Dummy(extended_real=False, complex=True)
c2 = Dummy(extended_real=False, complex=True)
ShubhamKJha marked this conversation as resolved.
Show resolved Hide resolved
i = Dummy(imaginary=True)
e = nz*r*c
assert e.is_imaginary is None
assert e.is_real is None
assert e.is_extended_real is None
e = nz*c
assert e.is_imaginary is None
assert e.is_real is False
assert e.is_extended_real is False
e = nz*i*c
assert e.is_imaginary is False
assert e.is_real is None
assert e.is_extended_real is None
# check for more than one complex; it is important to use
# uniquely named Symbols to ensure that two factors appear
# e.g. if the symbols have the same name they just become
# a single factor, a power.
e = nz*i*c*c2
assert e.is_imaginary is None
assert e.is_real is None
assert e.is_extended_real is None

# _eval_is_real and _eval_is_zero both employ trapping of the
# _eval_is_extended_real and _eval_is_zero both employ trapping of the
# zero value so args should be tested in both directions and
# TO AVOID GETTING THE CACHED RESULT, Dummy MUST BE USED

# real is unknonwn
def test(z, b, e):
if z.is_zero and b.is_finite:
assert e.is_real and e.is_zero
assert e.is_extended_real and e.is_zero
else:
assert e.is_real is None
assert e.is_extended_real is None
if b.is_finite:
if z.is_zero:
assert e.is_zero
Expand Down Expand Up @@ -1973,11 +1973,11 @@ def test_Mul_with_zero_infinite():
inf = Dummy(finite=False)

e = Mul(zer, inf, evaluate=False)
assert e.is_positive is None
assert e.is_extended_positive is None
assert e.is_hermitian is None

e = Mul(inf, zer, evaluate=False)
assert e.is_positive is None
assert e.is_extended_positive is None
assert e.is_hermitian is None

def test_Mul_does_not_cancel_infinities():
Expand Down
8 changes: 4 additions & 4 deletions sympy/core/tests/test_assumptions.py
Expand Up @@ -164,7 +164,7 @@ def test_neg_infinity():

def test_zoo():
zoo = S.ComplexInfinity
assert zoo.is_complex
assert zoo.is_complex is False
assert zoo.is_real is False
assert zoo.is_prime is False

Expand Down Expand Up @@ -1134,8 +1134,8 @@ def test_issue_16313():
def test_issue_16579():
# complex -> finite | infinite
# with work on PR 16603 it may be changed in future to complex -> finite
x = Symbol('x', complex=True, finite=False)
y = Symbol('x', real=True, infinite=False)
#x = Symbol('x', complex=True, finite=False)
ShubhamKJha marked this conversation as resolved.
Show resolved Hide resolved
y = Symbol('x', extended_real=True, infinite=False)

assert x.is_infinite
# assert x.is_infinite
assert y.is_finite
1 change: 0 additions & 1 deletion sympy/functions/elementary/complexes.py
Expand Up @@ -284,7 +284,6 @@ class sign(Function):
Abs, conjugate
"""

is_finite = True
is_complex = True

def doit(self, **hints):
Expand Down
4 changes: 2 additions & 2 deletions sympy/functions/elementary/exponential.py
Expand Up @@ -79,9 +79,9 @@ def _eval_conjugate(self):
def _eval_is_finite(self):
arg = self.args[0]
if arg.is_infinite:
if arg.is_negative:
if arg.is_extended_negative:
return True
if arg.is_positive:
if arg.is_extended_positive:
return False
if arg.is_finite:
return True
Expand Down
4 changes: 2 additions & 2 deletions sympy/functions/elementary/tests/test_complexes.py
Expand Up @@ -494,10 +494,10 @@ def test_Abs_properties():
assert Abs(f).is_extended_nonnegative is True

z = Symbol('z', complex=True, zero=False)
assert Abs(z).is_real is None
assert Abs(z).is_real is True # since complex implies finite
assert Abs(z).is_extended_real is True
assert Abs(z).is_rational is None
assert Abs(z).is_positive is None
assert Abs(z).is_positive is True
assert Abs(z).is_extended_positive is True
assert Abs(z).is_zero is False

Expand Down
43 changes: 43 additions & 0 deletions sympy/functions/elementary/trigonometric.py
Expand Up @@ -473,6 +473,11 @@ def _eval_is_finite(self):
if arg.is_extended_real:
return True

def _eval_is_complex(self):
if self.args[0].is_extended_real \
or self.args[0].is_complex:
return True


class cos(TrigonometricFunction):
"""
Expand Down Expand Up @@ -900,6 +905,11 @@ def _eval_is_finite(self):
if arg.is_extended_real:
return True

def _eval_is_complex(self):
if self.args[0].is_extended_real \
or self.args[0].is_complex:
return True


class tan(TrigonometricFunction):
"""
Expand Down Expand Up @@ -1190,6 +1200,7 @@ def _eval_as_leading_term(self, x):
return self.func(arg)

def _eval_is_extended_real(self):
# FIXME: currently tan(pi/2) return zoo
return self.args[0].is_extended_real

def _eval_is_real(self):
Expand All @@ -1200,9 +1211,18 @@ def _eval_is_real(self):
def _eval_is_finite(self):
arg = self.args[0]

if arg.is_real and (arg / pi - S.Half).is_integer is False:
return True

if arg.is_imaginary:
return True

def _eval_is_complex(self):
arg = self.args[0]

if arg.is_real and (arg / pi - S.Half).is_integer is False:
return True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we had extended_complex then methods like this wouldn't be needed. We could just say that extended_complex is True and let _eval_is_finite imply complex.



class cot(TrigonometricFunction):
"""
Expand Down Expand Up @@ -1483,9 +1503,21 @@ def _eval_expand_trig(self, **hints):

def _eval_is_finite(self):
arg = self.args[0]
if arg.is_real and (arg/pi).is_integer is False:
return True
if arg.is_imaginary:
return True

def _eval_is_real(self):
arg = self.args[0]
if arg.is_real and (arg/pi).is_integer is False:
return True

def _eval_is_complex(self):
arg = self.args[0]
if arg.is_real and (arg / pi).is_integer is False:
return True

def _eval_subs(self, old, new):
arg = self.args[0]
argnew = arg.subs(old, new)
Expand Down Expand Up @@ -1677,6 +1709,12 @@ def fdiff(self, argindex=1):
else:
raise ArgumentIndexError(self, argindex)

def _eval_is_complex(self):
arg = self.args[0]

if arg.is_real and (arg / pi - S.Half).is_integer is False:
return True

@staticmethod
@cacheit
def taylor_term(n, x, *previous_terms):
Expand Down Expand Up @@ -1757,6 +1795,11 @@ def fdiff(self, argindex=1):
else:
raise ArgumentIndexError(self, argindex)

def _eval_is_complex(self):
arg = self.args[0]
if arg.is_real and (arg / pi).is_integer is False:
return True

@staticmethod
@cacheit
def taylor_term(n, x, *previous_terms):
Expand Down
4 changes: 2 additions & 2 deletions sympy/functions/special/tests/test_zeta_functions.py
Expand Up @@ -217,8 +217,8 @@ def test_stieltjes_evalf():


def test_issue_10475():
a = Symbol('a', real=True)
b = Symbol('b', positive=True)
a = Symbol('a', extended_real=True)
b = Symbol('b', extended_positive=True)
s = Symbol('s', zero=False)

assert zeta(2 + I).is_finite
Expand Down
2 changes: 1 addition & 1 deletion sympy/physics/wigner.py
Expand Up @@ -196,7 +196,7 @@ def wigner_3j(j_1, j_2, j_3, m_1, m_2, m_3):
_Factlist[int(j_1 + j_2 + j_3 + 1)]

ressqrt = sqrt(argsqrt)
if ressqrt.is_complex:
if ressqrt.is_complex or ressqrt.is_infinite:
ressqrt = ressqrt.as_real_imag()[0]

imin = max(-j_3 + j_1 + m_2, -j_3 + j_2 - m_1, 0)
Expand Down
2 changes: 1 addition & 1 deletion sympy/printing/fcode.py
Expand Up @@ -204,7 +204,7 @@ def _print_sign(self, expr):
arg, = expr.args
if arg.is_integer:
new_expr = merge(0, isign(1, arg), Eq(arg, 0))
elif arg.is_complex:
elif (arg.is_complex or arg.is_infinite):
new_expr = merge(cmplx(literal_dp(0), literal_dp(0)), arg/Abs(arg), Eq(Abs(arg), literal_dp(0)))
else:
new_expr = merge(literal_dp(0), dsign(literal_dp(1), arg), Eq(arg, literal_dp(0)))
Expand Down