Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions mathics/builtin/arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"""

import sys
from typing import Optional
from typing import Any, Final, Optional

import sympy

Expand Down Expand Up @@ -82,7 +82,7 @@
sort_order = "mathics.builtin.mathematical-functions"


map_direction_infinity = {
MAP_DIRECTION_INFINITY: Final[dict[Any, PredefinedExpression]] = {
Integer1: MATHICS3_INFINITY,
IntegerM1: MATHICS3_NEG_INFINITY,
MATHICS3_COMPLEX_I: MATHICS3_I_INFINITY,
Expand Down Expand Up @@ -435,7 +435,8 @@ def eval_complex_infinity(self, evaluation: Evaluation):

def eval_directed_infinity(self, direction, evaluation: Evaluation):
"""DirectedInfinity[direction_]"""
result = map_direction_infinity.get(direction, None)

result = MAP_DIRECTION_INFINITY.get(direction, None)
if result is not None:
return result

Expand All @@ -447,7 +448,7 @@ def eval_directed_infinity(self, direction, evaluation: Evaluation):
if direction is None:
return None

result = map_direction_infinity.get(direction, None)
result = MAP_DIRECTION_INFINITY.get(direction, None)
if result is not None:
return result
if direction.is_zero:
Expand Down
25 changes: 24 additions & 1 deletion mathics/core/atoms/numerics.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,14 @@
min_prec,
prec,
)
from mathics.core.symbols import Atom, NumericOperators, Symbol, SymbolNull, symbol_set
from mathics.core.symbols import (
Atom,
NumericOperators,
Symbol,
SymbolN,
SymbolNull,
symbol_set,
)
from mathics.core.systemsymbols import (
SymbolFullForm,
SymbolI,
Expand Down Expand Up @@ -126,6 +133,14 @@ def is_inexact(self) -> bool:
"""
return False

@property
def is_zero(self) -> bool:
"""
If element is some sort of numeric type, Return True is "element" is zero, and False otherwise.
Subclass methods should override this. The default is False though.
"""
return False

@property
def is_literal(self) -> bool:
"""Number can't change and has a Python representation,
Expand Down Expand Up @@ -1212,3 +1227,11 @@ def is_integer_rational_or_real(expr) -> bool:
Return True if expr is either an Integer, Rational, or Real.
"""
return isinstance(expr, (Integer, Rational, Real))


def is_zero(element) -> Optional[bool]:
"""
If element is some sort of numeric type, Return True is "element" is zero, and False otherwise.
If it is not a numeric type, return None.
"""
return element.is_zero if hasattr(element, "is_zero") else None
10 changes: 5 additions & 5 deletions mathics/core/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -996,11 +996,11 @@ def element_order(self) -> tuple:
if element.has_form(SymbolPower, 2):
var = element.get_element(0).get_name()
expr = element.get_element(1)
assert isinstance(expr, (Expression, NumericOperators))
exp = expr.round_to_float()
if var and exp is not None:
var = wma_str_sort_key(var)
exps[var] = exps.get(var, 0) + exp
if hasattr(expr, "round_to_float"):
exp = expr.round_to_float()
if var and exp is not None:
var = wma_str_sort_key(var)
exps[var] = exps.get(var, 0) + exp
elif name:
name = wma_str_sort_key(name)
exps[name] = exps.get(name, 0) + 1
Expand Down
34 changes: 15 additions & 19 deletions mathics/core/symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,25 +85,6 @@ def __floordiv__(self, other) -> BaseElement:
def __pow__(self, other) -> BaseElement:
return self.create_expression(SymbolPower, self, other)

# FIXME: The name "round_to_float" is misleading when
# permit_complex is True.
def round_to_float(
self, evaluation=None, permit_complex=False
) -> Optional[Union[complex, float]]:
"""
Round to a Python float. Return None if rounding is not possible.
This can happen if self or evaluation is NaN.
"""
value = (
self
if evaluation is None
else self.create_expression(SymbolN, self).evaluate(evaluation)
)
if hasattr(value, "round") and hasattr(value, "get_float_value"):
value = value.round()
return value.get_float_value(permit_complex=permit_complex)
return None


def strip_context(name) -> str:
"""strip context from a symbol name"""
Expand Down Expand Up @@ -592,6 +573,21 @@ def replace_vars(
# assert all(fully_qualified_symbol_name(v) for v in vars)
return vars.get(self.name, self)

def round_to_float(self, evaluation=None) -> Optional[float]:
"""
Round to a Python float. Return None if rounding is not possible.
This can happen if self or evaluation is NaN.
"""
value = (
self
if evaluation is None
else self.create_expression(SymbolN, self).evaluate(evaluation)
)
if hasattr(value, "round") and hasattr(value, "get_float_value"):
value = value.round()
return value.get_float_value()
return None

def sameQ(self, rhs: Any) -> bool:
"""Mathics3 SameQ"""
return self is rhs
Expand Down
13 changes: 7 additions & 6 deletions mathics/eval/numbers/calculus/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Implementation of Series handling functions.
"""
from mathics.core.atoms import Integer, Integer0, Rational
from mathics.core.atoms.numerics import is_zero
from mathics.core.convert.expression import to_mathics_list
from mathics.core.element import BaseElement
from mathics.core.evaluation import Evaluation
Expand All @@ -28,7 +29,7 @@ def same_monomial(expr, x, x0):
"""
Checks if expr == (x-x0)
"""
if x0.is_zero and expr.sameQ(x):
if is_zero(x0) and expr.sameQ(x):
return True
if expr.get_head() is not SymbolPlus:
return False
Expand Down Expand Up @@ -175,7 +176,7 @@ def series_plus_series(series1, series2):
for k, coeff in enumerate(data2):
p = k * int(den2 / den) + offset2
if p < len_newdata:
if data[p].is_zero:
if is_zero(data[p]):
data[p] = coeff
else:
data[p] = Expression(SymbolPlus, data[p], coeff)
Expand Down Expand Up @@ -208,7 +209,7 @@ def series_times_series(series1, series2):
pos = k1 * offset1 + k2 * offset2
if pos >= len_newdata:
continue
if data[pos].is_zero:
if is_zero(data[pos]):
data[pos] = Expression(SymbolTimes, c1, c2)
elif data[pos].get_head() is SymbolPlus:
data[pos] = Expression(
Expand Down Expand Up @@ -254,7 +255,7 @@ def reduce_series_trailing_zeros(series):
if len(data) == 0:
return series
i = 0
while i < len(data) and data[i].is_zero:
while i < len(data) and is_zero(data[i]):
i = i + 1
nmin = nmin + i
data = data[i:]
Expand Down Expand Up @@ -285,7 +286,7 @@ def reduce_dataseries(series, factor):
while notdone:
if (den % factor == 0) and (nmin % factor == 0) and (nmax % factor == 0):
if all(
q.is_zero for q in data[1 + factor :: 2] for r in range(factor - 1)
is_zero(q) for q in data[1 + factor :: 2] for r in range(factor - 1)
):
data = data[0::factor]
nmin, nmax, den = (
Expand Down Expand Up @@ -317,7 +318,7 @@ def reduce_series_plus(series, terms, x, x0):

# Loop over terms
for term in terms:
if term.is_zero:
if is_zero(term):
continue
if isinstance(term, Atom):
other_terms.append(term)
Expand Down
12 changes: 6 additions & 6 deletions mathics/eval/numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
RationalOneHalf,
Real,
)
from mathics.core.atoms.numerics import is_inexact
from mathics.core.atoms.numerics import is_inexact, is_zero
from mathics.core.convert.mpmath import from_mpmath
from mathics.core.convert.sympy import from_sympy
from mathics.core.element import BaseElement
Expand Down Expand Up @@ -94,7 +94,7 @@ def eval_Abs(expr: BaseElement) -> Optional[BaseElement]:
return eval_Abs_number(expr)
if expr.has_form(SymbolPower, 2):
base, exp = expr.elements
if exp.is_zero:
if is_zero(exp):
return Integer1
if test_arithmetic_expr(expr):
abs_base = eval_Abs(base)
Expand Down Expand Up @@ -180,23 +180,23 @@ def eval_RealSign(expr: BaseElement) -> Optional[Integer]:
If the argument is a real algebraic expression,
return the sign of the expression.
"""
if expr.is_zero:
if is_zero(expr):
return Integer0
if isinstance(expr, (Integer, Rational, Real)):
return Integer1 if expr.value > 0 else IntegerM1
if expr in NUMERICAL_CONSTANTS:
return Integer1
if expr.has_form(SymbolAbs, 1):
arg = expr.elements[0]
if arg.is_zero:
if is_zero(arg):
return Integer0
if isinstance(arg, Number):
return Integer1
# Try evaluating to an inexact number
arg_inexact = to_inexact_value(arg)
if arg_inexact is None:
return None
if arg_inexact.is_zero:
if is_zero(arg_inexact):
return Integer0
if isinstance(arg_inexact, Number):
return Integer1
Expand Down Expand Up @@ -330,7 +330,7 @@ def eval_complex_sign(n: BaseElement) -> Optional[BaseElement]:
# SymPy conversion failed; fall back.
return None

if abs_expr.is_zero:
if is_zero(abs_expr):
return abs_expr
if abs_expr is Integer1:
return n
Expand Down
Loading