diff --git a/CHANGES.rst b/CHANGES.rst
index 628d809e7..7e8baf9a0 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -4,11 +4,6 @@ CHANGES
=======
-API
----
-
-* ``eval_sign`` and ``eval_abs`` are now in ``mathics.eval.arithmetic``.
-
Compatibility
-------------
@@ -16,6 +11,12 @@ Compatibility
+Internals
+---
+
+* ``eval_abs`` and ``eval_sign`` extracted from ``Abs`` and ``Sign`` and added to ``mathics.eval.arithmetic``.
+
+
Bugs
----
diff --git a/mathics/builtin/arithmetic.py b/mathics/builtin/arithmetic.py
index fb8300916..6f8ecd74f 100644
--- a/mathics/builtin/arithmetic.py
+++ b/mathics/builtin/arithmetic.py
@@ -7,15 +7,11 @@
Basic arithmetic functions, including complex number arithmetic.
"""
-import sympy
-
-# This tells documentation how to sort this module
-sort_order = "mathics.builtin.mathematical-functions"
-
-
from functools import lru_cache
+from typing import Optional
import mpmath
+import sympy
from mathics.builtin.base import (
Builtin,
@@ -48,6 +44,7 @@
from mathics.core.convert.expression import to_expression
from mathics.core.convert.sympy import SympyExpression, from_sympy, sympy_symbol_prefix
from mathics.core.element import ElementsProperties
+from mathics.core.evaluation import Evaluation
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
from mathics.core.number import dps, min_prec
@@ -69,10 +66,13 @@
SymbolTable,
SymbolUndefined,
)
-from mathics.eval.arithmetic import eval_abs, eval_mpmath_function, eval_sign
+from mathics.eval.arithmetic import eval_Abs, eval_mpmath_function, eval_Sign
from mathics.eval.nevaluator import eval_N
from mathics.eval.numerify import numerify
+# This tells documentation how to sort this module
+sort_order = "mathics.builtin.mathematical-functions"
+
ExpressionComplexInfinity = Expression(SymbolDirectedInfinity)
@@ -98,7 +98,7 @@ def get_mpmath_function(self, args):
return None
return getattr(mpmath, self.mpmath_name)
- def eval(self, z, evaluation):
+ def eval(self, z, evaluation: Evaluation):
"%(name)s[z__]"
args = numerify(z, evaluation).get_sequence()
@@ -171,34 +171,35 @@ def create_infix(items, operator, prec, grouping):
class Abs(_MPMathFunction):
"""
-
- :Absolute value:
- https://en.wikipedia.org/wiki/Absolute_value (
- :SymPy:
- https://docs.sympy.org/latest/modules/functions/elementary.html#sympy.functions.elementary.complexes.Abs,
- :WMA: https://reference.wolfram.com/language/ref/Abs)
-
-
- - 'Abs[$x$]'
-
- returns the absolute value of $x$.
-
-
- >> Abs[-3]
- = 3
-
- >> Plot[Abs[x], {x, -4, 4}]
- = -Graphics-
-
- 'Abs' returns the magnitude of complex numbers:
- >> Abs[3 + I]
- = Sqrt[10]
- >> Abs[3.0 + I]
- = 3.16228
-
- All of the below evaluate to Infinity:
-
- >> Abs[Infinity] == Abs[I Infinity] == Abs[ComplexInfinity]
- = True
+
+ :Absolute value:
+ https://en.wikipedia.org/wiki/Absolute_value (
+ :SymPy:
+ https://docs.sympy.org/latest/modules/functions/
+ elementary.html#sympy.functions.elementary.complexes.Abs,
+ :WMA: https://reference.wolfram.com/language/ref/Abs)
+
+
+ - 'Abs[$x$]'
+
- returns the absolute value of $x$.
+
+
+ >> Abs[-3]
+ = 3
+
+ >> Plot[Abs[x], {x, -4, 4}]
+ = -Graphics-
+
+ 'Abs' returns the magnitude of complex numbers:
+ >> Abs[3 + I]
+ = Sqrt[10]
+ >> Abs[3.0 + I]
+ = 3.16228
+
+ All of the below evaluate to Infinity:
+
+ >> Abs[Infinity] == Abs[I Infinity] == Abs[ComplexInfinity]
+ = True
"""
mpmath_name = "fabs" # mpmath actually uses python abs(x) / x.__abs__()
@@ -208,9 +209,9 @@ class Abs(_MPMathFunction):
summary_text = "absolute value of a number"
sympy_name = "Abs"
- def eval(self, x, evaluation):
- "%(name)s[x_]"
- result = eval_abs(x)
+ def eval(self, x, evaluation: Evaluation):
+ "Abs[x_]"
+ result = eval_Abs(x)
if result is not None:
return result
return super(Abs, self).eval(x, evaluation)
@@ -271,7 +272,7 @@ class Arg(_MPMathFunction):
sympy_name = "arg"
def eval(self, z, evaluation, options={}):
- "%(name)s[z_, OptionsPattern[%(name)s]]"
+ "Arg[z_, OptionsPattern[Arg]]"
if Expression(SymbolPossibleZeroQ, z).evaluate(evaluation) is SymbolTrue:
return Integer0
preference = self.get_option(options, "Method", evaluation).get_string_value()
@@ -309,7 +310,7 @@ class Assuming(Builtin):
summary_text = "set assumptions during the evaluation"
attributes = A_HOLD_REST | A_PROTECTED
- def eval_assuming(self, assumptions, expr, evaluation):
+ def eval_assuming(self, assumptions, expr, evaluation: Evaluation):
"Assuming[assumptions_, expr_]"
assumptions = assumptions.evaluate(evaluation)
if assumptions is SymbolTrue:
@@ -365,11 +366,11 @@ class Boole(Builtin):
= Boole[a == 7]
"""
- summary_text = "translate 'True' to 1, and 'False' to 0"
attributes = A_LISTABLE | A_PROTECTED
+ summary_text = "translate 'True' to 1, and 'False' to 0"
- def eval(self, expr, evaluation):
- "%(name)s[expr_]"
+ def eval(self, expr, evaluation: Evaluation):
+ "Boole[expr_]"
if expr is SymbolTrue:
return Integer1
elif expr is SymbolFalse:
@@ -439,8 +440,8 @@ class Complex_(Builtin):
summary_text = "head for complex numbers"
name = "Complex"
- def eval(self, r, i, evaluation):
- "%(name)s[r_?NumberQ, i_?NumberQ]"
+ def eval(self, r, i, evaluation: Evaluation):
+ "Complex[r_?NumberQ, i_?NumberQ]"
if isinstance(r, Complex) or isinstance(i, Complex):
sym_form = r.to_sympy() + sympy.I * i.to_sympy()
@@ -451,11 +452,13 @@ def eval(self, r, i, evaluation):
class ConditionalExpression(Builtin):
"""
- :WMA link:https://reference.wolfram.com/language/ref/ConditionalExpression.html
+ :WMA link:https://reference.wolfram.com/
+language/ref/ConditionalExpression.html
- 'ConditionalExpression[$expr$, $cond$]'
-
- returns $expr$ if $cond$ evaluates to $True$, $Undefined$ if $cond$ evaluates to $False$.
+
- returns $expr$ if $cond$ evaluates to $True$, $Undefined$ if $cond$ \
+ evaluates to $False$.
>> ConditionalExpression[x^2, True]
@@ -492,7 +495,7 @@ class ConditionalExpression(Builtin):
"expr1_ ^ ConditionalExpression[expr2_, cond_]": "ConditionalExpression[expr1^expr2, cond]",
}
- def eval_generic(self, expr, cond, evaluation):
+ def eval_generic(self, expr, cond, evaluation: Evaluation):
"ConditionalExpression[expr_, cond_]"
# What we need here is a way to evaluate
# cond as a predicate, using assumptions.
@@ -638,20 +641,21 @@ class DirectedInfinity(SympyFunction):
formats = {
"DirectedInfinity[1]": "HoldForm[Infinity]",
- "DirectedInfinity[-1]": "PrecedenceForm[-HoldForm[Infinity], 390]",
+ "DirectedInfinity[-1]": "HoldForm[-Infinity]",
"DirectedInfinity[]": "HoldForm[ComplexInfinity]",
- "DirectedInfinity[z_]": "PrecedenceForm[z HoldForm[Infinity], 390]",
+ "DirectedInfinity[DirectedInfinity[z_]]": "DirectedInfinity[z]",
+ "DirectedInfinity[z_?NumericQ]": "HoldForm[z Infinity]",
}
- def eval_directed_infinity(self, direction, evaluation):
+ def eval_directed_infinity(self, direction, evaluation: Evaluation):
"""DirectedInfinity[direction_]"""
if direction in (Integer1, IntegerM1):
return None
if direction.is_zero:
return ExpressionComplexInfinity
- normalized_direction = eval_sign(direction)
- # TODO: improve eval_sign, to avoid the need of the
+ normalized_direction = eval_Sign(direction)
+ # TODO: improve eval_Sign, to avoid the need of the
# following block:
# ############################################
if normalized_direction is None:
@@ -712,7 +716,7 @@ class I_(Predefined):
summary_text = "imaginary unit"
python_equivalent = 1j
- def evaluate(self, evaluation):
+ def evaluate(self, evaluation: Evaluation):
return Complex(Integer0, Integer1)
@@ -740,17 +744,17 @@ class Im(SympyFunction):
summary_text = "imaginary part"
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
- def eval_complex(self, number, evaluation):
+ def eval_complex(self, number, evaluation: Evaluation):
"Im[number_Complex]"
if isinstance(number, Complex):
return number.imag
- def eval_number(self, number, evaluation):
+ def eval_number(self, number, evaluation: Evaluation):
"Im[number_?NumberQ]"
return Integer0
- def eval(self, number, evaluation):
+ def eval(self, number, evaluation: Evaluation):
"Im[number_]"
return from_sympy(sympy.im(number.to_sympy().expand(complex=True)))
@@ -818,7 +822,7 @@ class Piecewise(SympyFunction):
attributes = A_HOLD_ALL | A_PROTECTED
- def eval(self, items, evaluation):
+ def eval(self, items, evaluation: Evaluation):
"%(name)s[items__]"
result = self.to_sympy(
Expression(SymbolPiecewise, *items.get_sequence()), evaluation=evaluation
@@ -977,8 +981,8 @@ class Rational_(Builtin):
summary_text = "head for rational numbers"
name = "Rational"
- def eval(self, n: Integer, m: Integer, evaluation):
- "%(name)s[n_Integer, m_Integer]"
+ def eval(self, n: Integer, m: Integer, evaluation: Evaluation):
+ "Rational[n_Integer, m_Integer]"
if m.value == 1:
return n
@@ -1011,17 +1015,17 @@ class Re(SympyFunction):
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
sympy_name = "re"
- def eval_complex(self, number, evaluation):
+ def eval_complex(self, number, evaluation: Evaluation):
"Re[number_Complex]"
if isinstance(number, Complex):
return number.real
- def eval_number(self, number, evaluation):
+ def eval_number(self, number, evaluation: Evaluation):
"Re[number_?NumberQ]"
return number
- def eval(self, number, evaluation):
+ def eval(self, number, evaluation: Evaluation):
"Re[number_]"
return from_sympy(sympy.re(number.to_sympy().expand(complex=True)))
@@ -1174,9 +1178,9 @@ class Sign(SympyFunction):
"Sign[Power[a_, b_]]": "Power[Sign[a], b]",
}
- def eval(self, x, evaluation):
+ def eval(self, x, evaluation: Evaluation):
"%(name)s[x_]"
- result = eval_sign(x)
+ result = eval_Sign(x)
if result is not None:
return result
# return None
@@ -1187,7 +1191,7 @@ def eval(self, x, evaluation):
# Unhandled cases. Use sympy
return super(Sign, self).eval(x, evaluation)
- def eval_error(self, x, seqs, evaluation):
+ def eval_error(self, x, seqs, evaluation: Evaluation):
"Sign[x_, seqs__]"
evaluation.message("Sign", "argx", Integer(len(seqs.get_sequence()) + 1))
@@ -1207,7 +1211,8 @@ class Sum(IterationFunction, SympyFunction):
$i$ ranges from $imin$ to $imax$ in steps of $di$.
'Sum[$expr$, {$i$, $imin$, $imax$}, {$j$, $jmin$, $jmax$}, ...]'
- evaluates $expr$ as a multiple sum, with {$i$, ...}, {$j$, ...}, ... being in outermost-to-innermost order.
+ evaluates $expr$ as a multiple sum, with {$i$, ...}, {$j$, ...}, ... being \
+ in outermost-to-innermost order.
@@ -1293,7 +1298,7 @@ class Sum(IterationFunction, SympyFunction):
def get_result(self, items):
return Expression(SymbolPlus, *items)
- def to_sympy(self, expr, **kwargs) -> SympyExpression:
+ def to_sympy(self, expr, **kwargs) -> Optional[SympyExpression]:
"""
Perform summation via sympy.summation
"""
diff --git a/mathics/eval/arithmetic.py b/mathics/eval/arithmetic.py
index d15b17ee8..881eebf7d 100644
--- a/mathics/eval/arithmetic.py
+++ b/mathics/eval/arithmetic.py
@@ -57,7 +57,7 @@ def call_mpmath(
return Symbol(exc.name)
-def eval_abs(expr: BaseElement) -> Optional[BaseElement]:
+def eval_Abs(expr: BaseElement) -> Optional[BaseElement]:
"""
if expr is a number, return the absolute value.
"""
@@ -72,7 +72,7 @@ def eval_abs(expr: BaseElement) -> Optional[BaseElement]:
return None
-def eval_sign(expr: BaseElement) -> Optional[BaseElement]:
+def eval_Sign(expr: BaseElement) -> Optional[BaseElement]:
"""
if expr is a number, return its sign.
"""
diff --git a/test/builtin/arithmetic/test_basic.py b/test/builtin/arithmetic/test_basic.py
index fd52f8fe4..d99b0b9dc 100644
--- a/test/builtin/arithmetic/test_basic.py
+++ b/test/builtin/arithmetic/test_basic.py
@@ -109,6 +109,26 @@ def test_exponential(str_expr, str_expected):
"ComplexInfinity",
"Goes to the previous case because of the rule in Power",
),
+ ],
+)
+def test_multiply(str_expr, str_expected, msg):
+ check_evaluation(
+ str_expr,
+ str_expected,
+ failure_message=msg,
+ hold_expected=True,
+ to_string_expr=True,
+ )
+
+
+@pytest.mark.skip("DirectedInfinity Precedence needs going over")
+@pytest.mark.parametrize(
+ (
+ "str_expr",
+ "str_expected",
+ "msg",
+ ),
+ [
(
"a b DirectedInfinity[1. + 2. I]",
"a b ((0.447214 + 0.894427 I) Infinity)",
@@ -137,7 +157,7 @@ def test_exponential(str_expr, str_expected):
# ("a b DirectedInfinity[-3]", "a b (-Infinity)", ""),
],
)
-def test_multiply(str_expr, str_expected, msg):
+def test_directed_infinity_precedence(str_expr, str_expected, msg):
check_evaluation(
str_expr,
str_expected,