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

Fix remaining mypy errors in mathics.core #1147

Merged
merged 6 commits into from
Oct 29, 2024
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
3 changes: 2 additions & 1 deletion mathics/core/assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,8 @@ def eval_assign_numericq(self, lhs, rhs, evaluation: Evaluation, tags, upset):
if isinstance(target, Symbol):
name = target.get_name()
definition = evaluation.definitions.get_definition(name)
definition.is_numeric = rhs is SymbolTrue
if definition is not None:
definition.is_numeric = rhs is SymbolTrue
return True
else:
evaluation.message("NumericQ", "set", lhs, rhs)
Expand Down
2 changes: 1 addition & 1 deletion mathics/core/atoms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import re
from typing import Any, Dict, Generic, Optional, Tuple, TypeVar, Union

import mpmath
import mpmath # type: ignore[import-untyped]
import sympy

from mathics.core.element import BoxElementMixin, ImmutableValueMixin
Expand Down
31 changes: 26 additions & 5 deletions mathics/core/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,20 @@
from abc import ABC
from functools import total_ordering
from itertools import chain
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union, cast
from typing import (
Any,
Callable,
Dict,
Iterable,
List,
Optional,
Sequence,
Tuple,
Union,
cast,
)

import mpmath
import mpmath # type: ignore[import-untyped]
import pkg_resources
import sympy

Expand Down Expand Up @@ -84,7 +95,7 @@
try:
import ujson
except ImportError:
import json as ujson
import json as ujson # type: ignore[no-redef]

ROOT_DIR = pkg_resources.resource_filename("mathics", "")

Expand Down Expand Up @@ -551,6 +562,12 @@ def get_sympy_names(self) -> List[str]:
return [self.sympy_name]
return []

def to_sympy(self, expr=None, **kwargs):
raise NotImplementedError

def from_sympy(self, elements: Tuple[BaseElement, ...]) -> Expression:
raise NotImplementedError


# This has to come before MPMathFunction
class SympyFunction(SympyObject):
Expand Down Expand Up @@ -651,6 +668,8 @@ def eval(self, z, evaluation: Evaluation):

if not all(isinstance(arg, Number) for arg in args):
return
# mypy isn't yet smart enough to recognise that we can only reach this point if all args are Numbers
args = cast(Sequence[Number], args)

mpmath_function = self.get_mpmath_function(tuple(args))
if mpmath_function is None:
Expand All @@ -663,7 +682,9 @@ def eval(self, z, evaluation: Evaluation):
d = dps(prec)
args = tuple([arg.round(d) for arg in args])

return eval_mpmath_function(mpmath_function, *args, prec=prec)
return eval_mpmath_function(
mpmath_function, *cast(Sequence[Number], args), prec=prec
)


class MPMathMultiFunction(MPMathFunction):
Expand Down Expand Up @@ -1235,7 +1256,7 @@ def get_match_candidates(
) -> Tuple[BaseElement]:
return elements

def get_match_count(self, vars_dict: dict = {}):
def get_match_count(self, vars_dict: Optional[dict] = None):
return (1, 1)

def get_sort_key(self, pattern_sort=False) -> tuple:
Expand Down
6 changes: 3 additions & 3 deletions mathics/core/convert/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,16 @@ def to_mathics_list(
return list_expression


def to_numeric_args(mathics_args: Type[BaseElement], evaluation) -> list:
def to_numeric_args(mathics_args: BaseElement, evaluation) -> tuple:
"""
Convert Mathics arguments, such as the arguments in an evaluation
method a Python list that is suitable for feeding as arguments
into SymPy, NumPy, or mpmath.

We make use of fast conversions for literals.
"""
return (
tuple(mathics_args.value)
return tuple(
mathics_args.value # type: ignore[attr-defined]
if mathics_args.is_literal
else numerify(mathics_args, evaluation).get_sequence()
)
Expand Down
4 changes: 3 additions & 1 deletion mathics/core/convert/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ def _pythonized_mathics_expr(*x):


def expression_to_callable_and_args(
expr: Expression, vars: list = None, evaluation: Optional[Evaluation] = None
expr: Expression,
vars: Optional[list] = None,
evaluation: Optional[Evaluation] = None,
) -> Tuple[Optional[Callable], Optional[list]]:
"""
Return a tuple of Python callable and a list of CompileArgs.
Expand Down
2 changes: 1 addition & 1 deletion mathics/core/convert/mpmath.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from functools import lru_cache
from typing import Optional, Union

import mpmath
import mpmath # type: ignore[import-untyped]
import sympy

from mathics.core.atoms import Complex, MachineReal, MachineReal0, PrecisionReal
Expand Down
2 changes: 1 addition & 1 deletion mathics/core/convert/op.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
try:
import ujson
except ImportError:
import json as ujson
import json as ujson # type: ignore[no-redef]

ROOT_DIR = pkg_resources.resource_filename("mathics", "")

Expand Down
2 changes: 1 addition & 1 deletion mathics/core/convert/regex.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def to_regex_internal(
(None, "") is returned if there is an error of some sort.
"""

def recurse(x: Expression, quantifiers=q) -> Tuple[Optional[str], str]:
def recurse(x: Expression, quantifiers=q) -> Optional[str]:
"""
Shortened way to call to_regexp_internal -
only the expr and quantifiers change here.
Expand Down
50 changes: 33 additions & 17 deletions mathics/core/convert/sympy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Converts expressions from SymPy to Mathics expressions.
Conversion to SymPy is handled directly in BaseElement descendants.
"""
from typing import Optional, Type, Union
from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple, Union, cast

import sympy
from sympy import Symbol as Sympy_Symbol, false as SympyFalse, true as SympyTrue
Expand Down Expand Up @@ -66,6 +66,9 @@
SymbolUnequal,
)

if TYPE_CHECKING:
from mathics.core.builtin import SympyObject

BasicSympy = sympy.Expr


Expand All @@ -74,8 +77,8 @@
SymbolRootSum = Symbol("RootSum")


mathics_to_sympy = {} # here we have: name -> sympy object
sympy_to_mathics = {}
mathics_to_sympy: Dict[str, "SympyObject"] = {} # here we have: name -> sympy object
sympy_to_mathics: Dict[str, "SympyObject"] = {}


sympy_singleton_to_mathics = {
Expand Down Expand Up @@ -104,14 +107,14 @@
}


def is_Cn_expr(name) -> bool:
def is_Cn_expr(name: str) -> bool:
"""Check if name is of the form {prefix}Cnnn"""
if name.startswith(sympy_symbol_prefix) or name.startswith(sympy_slot_prefix):
return False
if not name.startswith("C"):
return False
number = name[1:]
return number and number.isdigit()
return number != "" and number.isdigit()


def to_sympy_matrix(data, **kwargs) -> Optional[sympy.MutableDenseMatrix]:
Expand All @@ -131,6 +134,7 @@ class SympyExpression(BasicSympy):

is_Function = True
nargs = None
expr: Expression

def __new__(cls, *exprs):
# sympy simplify may also recreate the object if simplification occurred
Expand Down Expand Up @@ -166,7 +170,11 @@ def __new__(cls, *args):

def has_any_symbols(self, *syms) -> bool:
"""Check if any of the symbols in syms appears in the expression."""
result = any(arg.has_any_symbols(*syms) for arg in self.args)
result = any(
arg.has_any_symbols(*syms)
for arg in self.args
if isinstance(arg, SympyExpression)
)
return result

def _eval_subs(self, old, new):
Expand All @@ -184,10 +192,14 @@ def _eval_rewrite(self, rule, args, **hints):
return self

@property
def is_commutative(self) -> bool:
def is_commutative(self) -> Optional[bool]:
"""Check if the arguments are commutative."""
return all(getattr(t, "is_commutative", False) for t in self.args)

@is_commutative.setter
def is_commutative(self, value: bool) -> None:
return

def __str__(self) -> str:
return f"{super().__str__()}[{self.expr}])"

Expand Down Expand Up @@ -256,7 +268,7 @@ def symbol_to_sympy(symbol: Symbol, **kwargs) -> Sympy_Symbol:
return builtin.to_sympy(symbol, **kwargs)


def to_numeric_sympy_args(mathics_args: Type[BaseElement], evaluation) -> list:
def to_numeric_sympy_args(mathics_args: BaseElement, evaluation) -> list:
"""
Convert Mathics arguments, such as the arguments in an evaluation
method a Python list that is sutiable for feeding as arguments
Expand All @@ -267,6 +279,7 @@ def to_numeric_sympy_args(mathics_args: Type[BaseElement], evaluation) -> list:
from mathics.eval.numerify import numerify

if mathics_args.is_literal:
assert hasattr(mathics_args, "value")
sympy_args = [mathics_args.value]
else:
args = numerify(mathics_args, evaluation).get_sequence()
Expand Down Expand Up @@ -351,16 +364,16 @@ def old_from_sympy(expr) -> BaseElement:
if expr.is_Symbol:
name = str(expr)
if isinstance(expr, sympy.Dummy):
name = name + (f"__Dummy_{expr.dummy_index}")
name = name + (f"__Dummy_{expr.dummy_index}") # type: ignore[attr-defined]
# Probably, this should be the value attribute
return Symbol(name, sympy_dummy=expr)
if is_Cn_expr(name):
return Expression(SymbolC, Integer(int(name[1:])))
if name.startswith(sympy_symbol_prefix):
name = name[len(sympy_symbol_prefix) :]
if name.startswith(sympy_slot_prefix):
index = name[len(sympy_slot_prefix) :]
return Expression(SymbolSlot, Integer(int(index)))
index = int(name[len(sympy_slot_prefix) :])
return Expression(SymbolSlot, Integer(index))
elif expr.is_NumberSymbol:
name = str(expr)
if name is not None:
Expand Down Expand Up @@ -426,25 +439,26 @@ def old_from_sympy(expr) -> BaseElement:
return expr.expr

if isinstance(expr, sympy.Piecewise):
args = expr.args
return Expression(
SymbolPiecewise,
ListExpression(
*[
to_mathics_list(from_sympy(case), from_sympy(cond))
for case, cond in args
for case, cond in cast(
Sequence[Tuple[sympy.Basic, sympy.Basic]], expr.args
)
]
),
)

if isinstance(expr, SympyPrime):
return Expression(SymbolPrime, from_sympy(expr.args[0]))
if isinstance(expr, sympy.RootSum):
return Expression(SymbolRootSum, from_sympy(expr.poly), from_sympy(expr.fun))
return Expression(SymbolRootSum, from_sympy(expr.poly), from_sympy(expr.fun)) # type: ignore[attr-defined]
if isinstance(expr, sympy.PurePoly):
coeffs = expr.coeffs()
monoms = expr.monoms()
result = []
result: List[BaseElement] = []
for coeff, monom in zip(coeffs, monoms):
factors = []
if coeff != 1:
Expand Down Expand Up @@ -499,12 +513,14 @@ def old_from_sympy(expr) -> BaseElement:
else:
margs.append(from_sympy(arg))
builtin = sympy_to_mathics.get(name)
return builtin.from_sympy(margs)
assert builtin is not None
return builtin.from_sympy(tuple(margs))

elif isinstance(expr, sympy.sign):
name = "Sign"
else:
name = expr.func.__name__
assert name is not None
if is_Cn_expr(name):
return Expression(
Expression(Symbol("C"), Integer(int(name[1:]))),
Expand All @@ -515,7 +531,7 @@ def old_from_sympy(expr) -> BaseElement:
args = [from_sympy(arg) for arg in expr.args]
builtin = sympy_to_mathics.get(name)
if builtin is not None:
return builtin.from_sympy(args)
return builtin.from_sympy(tuple(args))
return Expression(Symbol(name), *args)

if isinstance(expr, sympy.Tuple):
Expand Down
Loading