Skip to content

Commit

Permalink
Fix remaining mypy errors in mathics.core (#1147)
Browse files Browse the repository at this point in the history
This fixes the remaining mypy errors in `mathics.core`, with the
exception of `mathics.core.expression` which needs a bit more work.
  • Loading branch information
davidar authored Oct 29, 2024
1 parent dcc607e commit 05e2f08
Show file tree
Hide file tree
Showing 24 changed files with 247 additions and 173 deletions.
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 @@ -167,7 +171,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 @@ -185,10 +193,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 @@ -257,7 +269,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 @@ -268,6 +280,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 @@ -352,16 +365,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 @@ -427,25 +440,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 @@ -500,12 +514,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 @@ -516,7 +532,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

0 comments on commit 05e2f08

Please sign in to comment.