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
20 changes: 16 additions & 4 deletions mathics/builtin/numbers/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
Numeric, Arithmetic, or Symbolic constants like Pi, E, or Infinity.
"""

# This tells documentation how to sort this module
sort_order = "mathics.builtin.mathematical-constants"

import math
from typing import Dict, Optional

Expand All @@ -25,6 +22,9 @@
from mathics.core.symbols import Atom, Symbol, strip_context
from mathics.core.systemsymbols import SymbolIndeterminate

# This tells documentation how to sort this module
sort_order = "mathics.builtin.mathematical-constants"


def mp_constant(fn: str, d=None) -> mpmath.mpf:
"""
Expand Down Expand Up @@ -191,7 +191,7 @@ def __init__(self, *args, **kwargs):
value_float = self.to_numpy(self.symbol)
NUMERICAL_CONSTANTS[self.symbol] = MachineReal(value_float)

def to_numpy(self, args):
def to_numpy(self, _):
return NUMERICAL_CONSTANTS[self.symbol]


Expand Down Expand Up @@ -286,6 +286,10 @@ class ComplexInfinity(_SympyConstant):
"ComplexInfinity": "DirectedInfinity[]",
}

@property
def sympy(self) -> sympy.core.numbers.ComplexInfinity:
return zoo


class Degree(_MPMathConstant, _NumpyConstant, _SympyConstant):
"""
Expand Down Expand Up @@ -376,6 +380,10 @@ def eval_N(self, precision, evaluation):
"N[E, precision_]"
return self.get_constant(precision, evaluation)

@property
def sympy(self) -> sympy.core.numbers.Exp1:
return SymPyE


class EulerGamma(_MPMathConstant, _NumpyConstant, _SympyConstant):
"""
Expand All @@ -402,6 +410,10 @@ class EulerGamma(_MPMathConstant, _NumpyConstant, _SympyConstant):
numpy_name = "euler_gamma"
sympy_name = "EulerGamma"

@property
def sympy(self) -> sympy.core.numbers.EulerGamma:
return S.EulerGamma


class Glaisher(_MPMathConstant):
"""
Expand Down
78 changes: 43 additions & 35 deletions mathics/core/atoms.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import mpmath
import sympy
from sympy.core import numbers as sympy_numbers

from mathics.core.element import BoxElementMixin, ImmutableValueMixin
from mathics.core.number import (
Expand Down Expand Up @@ -108,7 +109,7 @@ def is_numeric(self, evaluation=None) -> bool:

def to_mpmath(self, precision: Optional[int] = None) -> mpmath.ctx_mp_python.mpf:
"""
Convert self._value to an mpmath number with precision ``precision``
Convert self.value to an mpmath number with precision ``precision``
If ``precision`` is None, use mpmath's default precision.

A mpmath number is the default implementation for Number.
Expand Down Expand Up @@ -139,8 +140,9 @@ def round(self, d: Optional[int] = None) -> "Number":

@property
def value(self) -> T:
"""
Equivalent value in Python's native datatype.
"""Equivalent value in either SymPy's or Python's native
datatype if that exist. Note the SymPy value
and the Python value might be the same thing.
"""
return self._value

Expand Down Expand Up @@ -195,6 +197,8 @@ class Integer(Number[int]):
# dictionary's value is the corresponding Mathics Integer object.
_integers: Dict[Any, "Integer"] = {}

_sympy: sympy_numbers.Integer

# We use __new__ here to ensure that two Integer's that have the same value
# return the same object, and to set an object hash value.
# Consider also @lru_cache, and mechanisms for limiting and
Expand All @@ -209,6 +213,7 @@ def __new__(cls, value) -> "Integer":

# Cache object so we don't allocate again.
self._integers[value] = self
self._sympy = sympy_numbers.Integer(value)

# Set a value for self.__hash__() once so that every time
# it is used this is fast. Note that in contrast to the
Expand Down Expand Up @@ -318,12 +323,16 @@ def round(self, d: Optional[int] = None) -> Union["MachineReal", "PrecisionReal"
d = MACHINE_PRECISION_VALUE
return PrecisionReal(sympy.Float(self.value, d))

def to_sympy(self, **_):
return sympy.Integer(self._value)
@property
def sympy(self) -> sympy_numbers.Integer:
return self._sympy

def sameQ(self, other) -> bool:
def to_sympy(self, **_) -> sympy_numbers.Integer:
return self.sympy

def sameQ(self, rhs) -> bool:
"""Mathics SameQ"""
return isinstance(other, Integer) and self._value == other.value
return isinstance(rhs, Integer) and self._value == rhs.value

def do_copy(self) -> "Integer":
return Integer(self._value)
Expand Down Expand Up @@ -492,25 +501,25 @@ def make_boxes(self, form):
def is_zero(self) -> bool:
return self.value == 0.0

def sameQ(self, other) -> bool:
def sameQ(self, rhs) -> bool:
"""Mathics SameQ for MachineReal.
If the other comparison value is a MachineReal, the values
have to be equal. If the other value is a PrecisionReal though, then
If the rhs comparison value is a MachineReal, the values
have to be equal. If the rhs value is a PrecisionReal though, then
the two values have to be within 1/2 ** (precision) of
other-value's precision. For any other type, sameQ is False.
rhs-value's precision. For any rhs type, sameQ is False.
"""
if isinstance(other, MachineReal):
return self.value == other.value
if isinstance(other, PrecisionReal):
other_value = other.value
if isinstance(rhs, MachineReal):
return self.value == rhs.value
if isinstance(rhs, PrecisionReal):
rhs_value = rhs.value
value = self.to_sympy()
# If sympy fixes the issue, this comparison would be
# enough
if (value - other_value).is_zero:
if (value - rhs_value).is_zero:
return True
# this handles the issue...
diff = abs(value - other_value)
prec = min(value._prec, other_value._prec)
diff = abs(value - rhs_value)
prec = min(value._prec, rhs_value._prec)
return diff < 0.5 ** (prec)
else:
return False
Expand Down Expand Up @@ -539,13 +548,14 @@ class PrecisionReal(Real[sympy.Float]):
# The key is the PrecisionReal's sympy.Float, and the
# dictionary's value is the corresponding Mathics PrecisionReal object.
_precision_reals: Dict[Any, "PrecisionReal"] = {}
_sympy: Number

def __new__(cls, value) -> "PrecisionReal":
n = sympy.Float(value)
self = cls._precision_reals.get(n)
if self is None:
self = Number.__new__(cls)
self._value = n
self._sympy = self._value = n

# Cache object so we don't allocate again.
self._precision_reals[n] = self
Expand Down Expand Up @@ -593,12 +603,12 @@ def round(self, d: Optional[int] = None) -> Union[MachineReal, "PrecisionReal"]:
_prec = min(prec(d), self.value._prec)
return PrecisionReal(sympy.Float(self.value, precision=_prec))

def sameQ(self, other) -> bool:
def sameQ(self, rhs) -> bool:
"""Mathics SameQ for PrecisionReal"""
if isinstance(other, PrecisionReal):
other_value = other.value
elif isinstance(other, MachineReal):
other_value = other.to_sympy()
if isinstance(rhs, PrecisionReal):
other_value = rhs.value
elif isinstance(rhs, MachineReal):
other_value = rhs.to_sympy()
else:
return False
value = self.value
Expand Down Expand Up @@ -679,11 +689,11 @@ def is_literal(self) -> bool:
"""
return True

def sameQ(self, other) -> bool:
def sameQ(self, rhs) -> bool:
"""Mathics SameQ"""
# FIX: check
if isinstance(other, ByteArrayAtom):
return self.value == other.value
if isinstance(rhs, ByteArrayAtom):
return self.value == rhs.value
return False

def get_string_value(self) -> Optional[str]:
Expand Down Expand Up @@ -822,12 +832,10 @@ def get_sort_key(self, pattern_sort=False) -> tuple:
else:
return (0, 0, self.real.get_sort_key()[2], self.imag.get_sort_key()[2], 1)

def sameQ(self, other) -> bool:
def sameQ(self, rhs) -> bool:
"""Mathics SameQ"""
return (
isinstance(other, Complex)
and self.real == other.real
and self.imag == other.imag
isinstance(rhs, Complex) and self.real == rhs.real and self.imag == rhs.imag
)

def round(self, d=None) -> "Complex":
Expand Down Expand Up @@ -951,9 +959,9 @@ def round(self, d=None) -> Union["MachineReal", "PrecisionReal"]:
else:
return PrecisionReal(self.value.n(d))

def sameQ(self, other) -> bool:
def sameQ(self, rhs) -> bool:
"""Mathics SameQ"""
return isinstance(other, Rational) and self.value == other.value
return isinstance(rhs, Rational) and self.value == rhs.value

def numerator(self) -> "Integer":
return Integer(self.value.as_numer_denom()[0])
Expand Down Expand Up @@ -1059,9 +1067,9 @@ def is_literal(self) -> bool:
"""
return True

def sameQ(self, other) -> bool:
def sameQ(self, rhs) -> bool:
"""Mathics SameQ"""
return isinstance(other, String) and self.value == other.value
return isinstance(rhs, String) and self.value == rhs.value

def to_expression(self):
return self
Expand Down
8 changes: 6 additions & 2 deletions mathics/core/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,15 +412,19 @@ def lookup_name(self, name: str) -> str:
return ctx_name
return with_context

def get_package_names(self) -> List[str]:
def get_package_names(self) -> List[Optional[str]]:
"""Return the list of names of the packages loaded in the system."""
try:
packages = self.get_ownvalue("System`$Packages")
except ValueError:
return []

assert packages.has_form("System`List", None)
return [c.get_string_value() for c in packages.get_elements()]
return [
c.get_string_value()
for c in packages.get_elements()
if c.get_string_value() is not None
]
# return sorted({name.split("`")[0] for name in self.get_names()})

def shorten_name(self, name_with_ctx: str) -> str:
Expand Down
2 changes: 1 addition & 1 deletion mathics/core/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ def get_sequence(self) -> Sequence["BaseElement"]:
else:
return tuple([self])

def get_string_value(self):
def get_string_value(self) -> Optional[str]:
return None

@property
Expand Down
7 changes: 2 additions & 5 deletions mathics/core/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,6 @@ def eval_SameQ(self, other):
# The next element in the tree. Maybe should be an iterator?
def next_elem():
nonlocal len_elements
nonlocal parents
nonlocal pos

while pos and pos[-1] == len_elements:
pos.pop()
Expand Down Expand Up @@ -1185,10 +1183,10 @@ def rewrite_apply_eval_step(self, evaluation) -> Tuple[BaseElement, bool]:
self._build_elements_properties()
assert self.elements_properties is not None

recompute_properties = False

# @timeit
def eval_elements():
nonlocal recompute_properties

# @timeit
def eval_range(indices):
nonlocal recompute_properties
Expand Down Expand Up @@ -1237,7 +1235,6 @@ def rest_range(indices):
# * evaluate elements,
# * run to_python() on them in Expression construction, or
# * convert Expression elements from a tuple to a list and back
recompute_properties = False
elements: Sequence[BaseElement]
if self.elements_properties.elements_fully_evaluated:
elements = self._elements
Expand Down
4 changes: 3 additions & 1 deletion mathics/core/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"""

import reprlib
from typing import Optional, Tuple
from typing import Any, Optional, Tuple

from mathics.core.element import ElementsProperties
from mathics.core.evaluation import Evaluation
Expand All @@ -29,6 +29,7 @@ class ListExpression(Expression):
"""

_is_literal: bool
_sympy: Optional[Any]

def __init__(
self,
Expand All @@ -39,6 +40,7 @@ def __init__(
self.options = None
self.pattern_sequence = False
self._head = SymbolList
self._sympy = None

# For debugging:

Expand Down
2 changes: 1 addition & 1 deletion test/builtin/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Unit tests from mathics.builtin.forms.
"""

from test.helper import check_evaluation, session
from test.helper import check_evaluation

import pytest

Expand Down
Loading