Skip to content

Commit

Permalink
Merge pull request #1100 from mathics/add-PossibleZeroQ
Browse files Browse the repository at this point in the history
Add PossibleZeroQ
  • Loading branch information
rocky authored Jan 9, 2021
2 parents af9305b + c207625 commit 57db8c4
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 13 deletions.
74 changes: 68 additions & 6 deletions mathics/builtin/arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,18 @@
)

from mathics.core.expression import (
Complex,
Expression,
Number,
Integer,
Number,
Rational,
Real,
String,
Symbol,
SymbolFalse,
SymbolNull,
Complex,
String,
SymbolTrue,
SymbolFalse,
from_python,
)
from mathics.core.numbers import min_prec, dps, SpecialValueError

Expand Down Expand Up @@ -1368,8 +1369,8 @@ class Indeterminate(SympyConstant):
class NumberQ(Test):
"""
<dl>
<dt>'NumberQ[$expr$]'
<dd>returns 'True' if $expr$ is an explicit number, and 'False' otherwise.
<dt>'NumberQ[$expr$]'
<dd>returns 'True' if $expr$ is an explicit number, and 'False' otherwise.
</dl>
>> NumberQ[3+I]
Expand All @@ -1384,6 +1385,67 @@ def test(self, expr):
return isinstance(expr, Number)


class PossibleZeroQ(SympyFunction):
"""
<dl>
<dt>'PossibleZeroQ[$expr$]'
<dd>returns 'True' if basic symbolic and numerical methods suggest that expr has value zero, and 'False' otherwise.
</dl>
Test whether a numeric expression is zero:
>> PossibleZeroQ[E^(I Pi/4) - (-1)^(1/4)]
= True
The determination is approximate.
Test whether a symbolic expression is likely to be identically zero:
>> PossibleZeroQ[(x + 1) (x - 1) - x^2 + 1]
= True
>> PossibleZeroQ[(E + Pi)^2 - E^2 - Pi^2 - 2 E Pi]
= True
Show that a numeric expression is nonzero:
>> PossibleZeroQ[E^Pi - Pi^E]
= False
>> PossibleZeroQ[1/x + 1/y - (x + y)/(x y)]
= True
Decide that a numeric expression is zero, based on approximate computations:
>> PossibleZeroQ[2^(2 I) - 2^(-2 I) - 2 I Sin[Log[4]]]
= True
>> PossibleZeroQ[Sqrt[x^2] - x]
= False
"""

sympy_name = "_iszero"

def apply(self, expr, evaluation):
"%(name)s[expr_]"
from sympy.matrices.utilities import _iszero

sympy_expr = expr.to_sympy()
result = _iszero(sympy_expr)
if result is None:
# Can't get exact answer, so try approximate equal
numeric_val = Expression("N", expr).evaluate(evaluation)
if numeric_val and hasattr(numeric_val, "is_approx_zero"):
result = numeric_val.is_approx_zero
elif (
Expression("NumericQ", numeric_val).evaluate(evaluation) == SymbolFalse
):
return (
SymbolTrue
if Expression("Simplify", expr).evaluate(evaluation) == Integer(0)
else SymbolFalse
)

return from_python(result)


class RealNumberQ(Test):
"""
<dl>
Expand Down
14 changes: 8 additions & 6 deletions mathics/builtin/numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,18 @@
PrecisionValueError,
)
from mathics.core.expression import (
Integer,
Real,
Complex,
Expression,
Integer,
MachineReal,
Number,
Symbol,
PrecisionReal,
Rational,
Real,
Symbol,
SymbolFalse,
SymbolTrue,
from_python,
MachineReal,
PrecisionReal,
)
from mathics.core.convert import from_sympy

Expand Down Expand Up @@ -748,7 +750,7 @@ def test(expr):
else:
return expr.is_numeric()

return Symbol("True") if test(expr) else Symbol("False")
return SymbolTrue if test(expr) else SymbolFalse


class RealValuedNumericQ(Builtin):
Expand Down
14 changes: 13 additions & 1 deletion mathics/core/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -2090,7 +2090,7 @@ def __neg__(self) -> 'Rational':

@property
def is_zero(self) -> bool:
return self.numerator == 0
return self.numerator().is_zero


class Real(Number):
Expand Down Expand Up @@ -2236,6 +2236,12 @@ def __neg__(self) -> 'MachineReal':
def is_zero(self) -> bool:
return self.value == 0.0

@property
def is_approx_zero(self) -> bool:
# FIXME: figure out how to hook int $MachinePrecision and
# what the right definition here would be.
return abs(self.value) <= 10**-14


class PrecisionReal(Real):
'''
Expand Down Expand Up @@ -2428,6 +2434,12 @@ def __neg__(self):
def is_zero(self) -> bool:
return self.real.is_zero and self.imag.is_zero

@property
def is_approx_zero(self) -> bool:
real_zero = self.real.is_approx_zero if hasattr(self.real, "is_approx_zero") else self.real.is_zero
imag_zero = self.imag.is_approx_zero if hasattr(self.imag, "is_approx_zero") else self.imag.is_zero
return real_zero and imag_zero


def encode_mathml(text: str) -> str:
text = text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
Expand Down

0 comments on commit 57db8c4

Please sign in to comment.