From 3e284b9be8dbc16f3496394d2656888f699a9636 Mon Sep 17 00:00:00 2001 From: rocky Date: Sun, 28 Mar 2021 21:40:53 -0400 Subject: [PATCH 1/2] Try caching Integers and Rationals --- mathics/core/expression.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/mathics/core/expression.py b/mathics/core/expression.py index c140d79a26..d37e7b74f5 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -68,7 +68,7 @@ def system_symbols_dict(d): class BoxError(Exception): def __init__(self, box, form) -> None: - super(BoxError, self).__init__("Box %s cannot be formatted as %s" % (box, form)) + super().__init__("Box %s cannot be formatted as %s" % (box, form)) self.box = box self.form = form @@ -684,7 +684,7 @@ class Expression(BaseExpression): _sequences: Any def __new__(cls, head, *leaves, **kwargs) -> "Expression": - self = super(Expression, cls).__new__(cls) + self = super().__new__(cls) if isinstance(head, str): head = Symbol(head) self._head = head @@ -870,7 +870,7 @@ def do_format(self, evaluation, form): last_evaluated, (symbolname,) ): return expr - expr = super(Expression, self).do_format(evaluation, form) + expr = super().do_format(evaluation, form) self._format_cache[form] = (evaluation.definitions.now, expr) return expr @@ -1640,7 +1640,7 @@ def descend(expr): return Expression(expr._head, *[apply_leaf(leaf) for leaf in expr._leaves]) if options is None: # default ReplaceAll mode; replace breadth first - result, applied = super(Expression, self).apply_rules( + result, applied = super().apply_rules( rules, evaluation, level, options ) if applied: @@ -2114,6 +2114,10 @@ def __new__(cls, value) -> "Integer": self.value = n return self + @lru_cache() + def __init__(self, value) -> "Integer": + super().__init__() + def boxes_to_text(self, **options) -> str: return str(self.value) @@ -2159,7 +2163,7 @@ def evaluate(self, evaluation): def get_sort_key(self, pattern_sort=False): if pattern_sort: - return super(Integer, self).get_sort_key(True) + return super().get_sort_key(True) else: return [0, 0, self.value, 0, 1] @@ -2184,8 +2188,9 @@ def is_zero(self) -> bool: class Rational(Number): + @lru_cache() def __new__(cls, numerator, denominator=1) -> "Rational": - self = super(Rational, cls).__new__(cls) + self = super().__new__(cls) self.value = sympy.Rational(numerator, denominator) return self @@ -2244,7 +2249,7 @@ def evaluate(self, evaluation) -> "Rational": def get_sort_key(self, pattern_sort=False): if pattern_sort: - return super(Rational, self).get_sort_key(True) + return super().get_sort_key(True) else: # HACK: otherwise "Bus error" when comparing 1==1. return [0, 0, sympy.Float(self.value), 0, 1] @@ -2318,7 +2323,7 @@ def evaluate(self, evaluation) -> "Real": def get_sort_key(self, pattern_sort=False): if pattern_sort: - return super(Real, self).get_sort_key(True) + return super().get_sort_key(True) return [0, 0, self.value, 0, 1] def __eq__(self, other) -> bool: @@ -2500,7 +2505,7 @@ class Complex(Number): imag: Any def __new__(cls, real, imag): - self = super(Complex, cls).__new__(cls) + self = super().__new__(cls) if isinstance(real, Complex) or not isinstance(real, Number): raise ValueError("Argument 'real' must be a real number.") if isinstance(imag, Complex) or not isinstance(imag, Number): @@ -2562,7 +2567,7 @@ def default_format(self, evaluation, form) -> str: def get_sort_key(self, pattern_sort=False): if pattern_sort: - return super(Complex, self).get_sort_key(True) + return super().get_sort_key(True) else: return [0, 0, self.real.get_sort_key()[2], self.imag.get_sort_key()[2], 1] @@ -2717,7 +2722,7 @@ class String(Atom): value: str def __new__(cls, value): - self = super(String, cls).__new__(cls) + self = super().__new__(cls) self.value = str(value) return self @@ -2857,7 +2862,7 @@ def default_format(self, evaluation, form) -> str: def get_sort_key(self, pattern_sort=False): if pattern_sort: - return super(String, self).get_sort_key(True) + return super().get_sort_key(True) else: return [0, 1, self.value, 0, 1] @@ -2961,7 +2966,7 @@ def __getnewargs__(self): class StringFromPython(String): def __new__(cls, value): - self = super(StringFromPython, cls).__new__(cls, value) + self = super().__new__(cls, value) if isinstance(value, sympy.NumberSymbol): self.value = "sympy." + str(value) From 162f0320ea44419a83ca7cb121bc677984a3474e Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 10 Apr 2021 07:12:04 -0400 Subject: [PATCH 2/2] same -> sameQ with prototype return It's a small thing, but that's what it is called in Mathics - Clarify, Simplify. --- mathics/builtin/arithmetic.py | 22 ++++---- mathics/builtin/base.py | 5 +- mathics/builtin/calculus.py | 2 +- mathics/builtin/comparison.py | 19 ++++--- mathics/builtin/compilation.py | 7 ++- mathics/builtin/compile/ir.py | 2 +- mathics/builtin/image.py | 3 +- mathics/builtin/inout.py | 16 +++--- mathics/builtin/linalg.py | 2 +- mathics/builtin/lists.py | 37 +++++++------ mathics/builtin/manipulate.py | 2 +- mathics/builtin/numeric.py | 2 +- mathics/builtin/patterns.py | 10 ++-- mathics/builtin/strings.py | 36 ++++++------ mathics/builtin/tensors.py | 6 +- mathics/core/definitions.py | 4 +- mathics/core/expression.py | 95 +++++++++++++++++++------------- mathics/core/numbers.py | 3 +- mathics/core/pattern.py | 10 ++-- test/test_hash.py | 4 +- test/test_parser/test_convert.py | 4 +- test/test_parser/test_util.py | 4 +- 22 files changed, 162 insertions(+), 133 deletions(-) diff --git a/mathics/builtin/arithmetic.py b/mathics/builtin/arithmetic.py index c4719381a7..e481b487dc 100644 --- a/mathics/builtin/arithmetic.py +++ b/mathics/builtin/arithmetic.py @@ -260,7 +260,7 @@ def negate(item): if item.has_form("Times", 1, None): if isinstance(item.leaves[0], Number): neg = -item.leaves[0] - if neg.same(Integer(1)): + if neg.sameQ(Integer(1)): if len(item.leaves) == 1: return neg else: @@ -375,7 +375,7 @@ def append_last(): else: number = Integer(0) - if not number.same(Integer(0)): + if not number.sameQ(Integer(0)): leaves.insert(0, number) if not leaves: @@ -583,7 +583,7 @@ def inverse(item): item.leaves[1], (Integer, Rational, Real) ): neg = -item.leaves[1] - if neg.same(Integer(1)): + if neg.sameQ(Integer(1)): return item.leaves[0] else: return Expression("Power", item.leaves[0], neg) @@ -603,7 +603,7 @@ def inverse(item): negative.append(inverse(item)) elif isinstance(item, Rational): numerator = item.numerator() - if not numerator.same(Integer(1)): + if not numerator.sameQ(Integer(1)): positive.append(numerator) negative.append(item.denominator()) else: @@ -667,7 +667,7 @@ def apply(self, items, evaluation): leaves and item.has_form("Power", 2) and leaves[-1].has_form("Power", 2) - and item.leaves[0].same(leaves[-1].leaves[0]) + and item.leaves[0].sameQ(leaves[-1].leaves[0]) ): leaves[-1] = Expression( "Power", @@ -675,7 +675,7 @@ def apply(self, items, evaluation): Expression("Plus", item.leaves[1], leaves[-1].leaves[1]), ) elif ( - leaves and item.has_form("Power", 2) and item.leaves[0].same(leaves[-1]) + leaves and item.has_form("Power", 2) and item.leaves[0].sameQ(leaves[-1]) ): leaves[-1] = Expression( "Power", leaves[-1], Expression("Plus", item.leaves[1], Integer(1)) @@ -683,12 +683,12 @@ def apply(self, items, evaluation): elif ( leaves and leaves[-1].has_form("Power", 2) - and leaves[-1].leaves[0].same(item) + and leaves[-1].leaves[0].sameQ(item) ): leaves[-1] = Expression( "Power", item, Expression("Plus", Integer(1), leaves[-1].leaves[1]) ) - elif item.get_head().same(SymbolDirectedInfinity): + elif item.get_head().sameQ(SymbolDirectedInfinity): infinity_factor = True if len(item.leaves) > 1: direction = item.leaves[0] @@ -696,7 +696,7 @@ def apply(self, items, evaluation): numbers.append(direction) else: leaves.append(direction) - elif item.same(SymbolInfinity) or item.same(SymbolComplexInfinity): + elif item.sameQ(SymbolInfinity) or item.sameQ(SymbolComplexInfinity): infinity_factor = True else: leaves.append(item) @@ -718,13 +718,13 @@ def apply(self, items, evaluation): else: number = Integer(1) - if number.same(Integer(1)): + if number.sameQ(Integer(1)): number = None elif number.is_zero: if infinity_factor: return Symbol("Indeterminate") return number - elif number.same(Integer(-1)) and leaves and leaves[0].has_form("Plus", None): + elif number.sameQ(Integer(-1)) and leaves and leaves[0].has_form("Plus", None): leaves[0] = Expression( leaves[0].get_head(), *[Expression("Times", Integer(-1), leaf) for leaf in leaves[0].leaves] diff --git a/mathics/builtin/base.py b/mathics/builtin/base.py index 00c64d3f0b..c74064ecdf 100644 --- a/mathics/builtin/base.py +++ b/mathics/builtin/base.py @@ -625,8 +625,9 @@ def get_lookup_name(self): def get_string_value(self): return "-@" + self.get_head_name() + "@-" - def same(self, expr): - return expr.same(self) + def sameQ(self, expr) -> bool: + """Mathics SameQ""" + return expr.sameQ(self) def is_atom(self): return False diff --git a/mathics/builtin/calculus.py b/mathics/builtin/calculus.py index a3c61c0e6a..fe2e9c90bd 100644 --- a/mathics/builtin/calculus.py +++ b/mathics/builtin/calculus.py @@ -163,7 +163,7 @@ def apply(self, f, x, evaluation): elif not f.is_atom() and len(f.leaves) > 1: def summand(leaf, index): - if leaf.same(x): + if leaf.sameQ(x): result = Expression( Expression( Expression( diff --git a/mathics/builtin/comparison.py b/mathics/builtin/comparison.py index cf018855cc..e39006c9fa 100644 --- a/mathics/builtin/comparison.py +++ b/mathics/builtin/comparison.py @@ -4,7 +4,7 @@ from mathics.version import __version__ # noqa used in loading to check consistency. import itertools -from typing import Optional, Union +from typing import Optional, Union, Any import sympy @@ -63,7 +63,7 @@ class SameQ(BinaryOperator): def apply(self, lhs, rhs, evaluation): "lhs_ === rhs_" - if lhs.same(rhs): + if lhs.sameQ(rhs): return SymbolTrue else: return SymbolFalse @@ -89,7 +89,7 @@ class UnsameQ(BinaryOperator): def apply(self, lhs, rhs, evaluation): "lhs_ =!= rhs_" - if lhs.same(rhs): + if lhs.sameQ(rhs): return SymbolFalse else: return SymbolTrue @@ -170,7 +170,7 @@ class ValueQ(Builtin): def apply(self, expr, evaluation): "ValueQ[expr_]" evaluated_expr = expr.evaluate(evaluation) - if expr.same(evaluated_expr): + if expr.sameQ(evaluated_expr): return SymbolFalse return SymbolTrue @@ -222,8 +222,11 @@ def numerify_args(items, evaluation): class _EqualityOperator(_InequalityOperator): "Compares all pairs e.g. a == b == c compares a == b, b == c, and a == c." - def do_compare(self, l1, l2) -> Union[bool, None]: - if l1.same(l2): + def equal2(self, l1: Any, l2: Any) -> Union[bool, None]: + """ + Two-argument Equal[] + """ + if l1.sameQ(l2): return True elif l1 == SymbolTrue and l2 == SymbolFalse: return False @@ -235,7 +238,7 @@ def do_compare(self, l1, l2) -> Union[bool, None]: if len(l1.leaves) != len(l2.leaves): return False for item1, item2 in zip(l1.leaves, l2.leaves): - result = self.do_compare(item1, item2) + result = self.equal2(item1, item2) if not result: return result return True @@ -305,7 +308,7 @@ def apply_other(self, args, evaluation): "%(name)s[args___?(!ExactNumberQ[#]&)]" args = args.get_sequence() for x, y in itertools.combinations(args, 2): - c = self.do_compare(x, y) + c = self.equal2(x, y) if c is None: return if self._op(c) is False: diff --git a/mathics/builtin/compilation.py b/mathics/builtin/compilation.py index eb5113ba79..a797350c0b 100644 --- a/mathics/builtin/compilation.py +++ b/mathics/builtin/compilation.py @@ -184,7 +184,8 @@ def get_sort_key(self, pattern_sort=False): else: return hash(self) - def same(self, other): + def sameQ(self, other) -> bool: + """Mathics SameQ""" return self is other def to_python(self, *args, **kwargs): @@ -248,9 +249,9 @@ def apply(self, argnames, expr, code, args, evaluation): for arg in argseq: if isinstance(arg, Integer): py_args.append(arg.get_int_value()) - elif arg.same(Symbol("True")): + elif arg.sameQ(Symbol("True")): py_args.append(True) - elif arg.same(Symbol("False")): + elif arg.sameQ(Symbol("False")): py_args.append(False) else: py_args.append(arg.round_to_float(evaluation)) diff --git a/mathics/builtin/compile/ir.py b/mathics/builtin/compile/ir.py index babc1e76f2..2caf8075fb 100644 --- a/mathics/builtin/compile/ir.py +++ b/mathics/builtin/compile/ir.py @@ -339,7 +339,7 @@ def _gen_Power(self, expr): return exponent # E ^ exponent - if leaves[0].same(Symbol('E')) and exponent.type == real_type: + if leaves[0].sameQ(Symbol('E')) and exponent.type == real_type: return self.call_fp_intr('llvm.exp', [exponent]) # 2 ^ exponent diff --git a/mathics/builtin/image.py b/mathics/builtin/image.py index 11fd49ef7c..a148c076c5 100644 --- a/mathics/builtin/image.py +++ b/mathics/builtin/image.py @@ -2256,7 +2256,8 @@ def get_sort_key(self, pattern_sort=False): else: return hash(self) - def same(self, other): + def sameQ(self, other) -> bool: + """Mathics SameQ""" if not isinstance(other, Image): return False if self.color_space != other.color_space or self.metadata != other.metadata: diff --git a/mathics/builtin/inout.py b/mathics/builtin/inout.py index aa5cbcc7d9..a863db32ff 100644 --- a/mathics/builtin/inout.py +++ b/mathics/builtin/inout.py @@ -764,7 +764,7 @@ def apply_makeboxes(self, items, sep, f, evaluation): else: result = [] for index, item in enumerate(items): - if index > 0 and not sep.same(String("")): + if index > 0 and not sep.sameQ(String("")): result.append(sep) result.append(MakeBoxes(item, f)) return RowBox(Expression(SymbolList, *result)) @@ -2294,20 +2294,20 @@ def check_options(self, options, evaluation): def check_DigitBlock(self, value, evaluation): py_value = value.get_int_value() - if value.same(Symbol("Infinity")): + if value.sameQ(Symbol("Infinity")): return [0, 0] elif py_value is not None and py_value > 0: return [py_value, py_value] elif value.has_form("List", 2): nleft, nright = value.leaves py_left, py_right = nleft.get_int_value(), nright.get_int_value() - if nleft.same(Symbol("Infinity")): + if nleft.sameQ(Symbol("Infinity")): nleft = 0 elif py_left is not None and py_left > 0: nleft = py_left else: nleft = None - if nright.same(Symbol("Infinity")): + if nright.sameQ(Symbol("Infinity")): nright = 0 elif py_right is not None and py_right > 0: nright = py_right @@ -2319,7 +2319,7 @@ def check_DigitBlock(self, value, evaluation): return evaluation.message(self.get_name(), "dblk", value) def check_ExponentFunction(self, value, evaluation): - if value.same(Symbol("Automatic")): + if value.sameQ(Symbol("Automatic")): return self.default_ExponentFunction def exp_function(x): @@ -2328,7 +2328,7 @@ def exp_function(x): return exp_function def check_NumberFormat(self, value, evaluation): - if value.same(Symbol("Automatic")): + if value.sameQ(Symbol("Automatic")): return self.default_NumberFormat def num_function(man, base, exp, options): @@ -2355,9 +2355,9 @@ def check_ExponentStep(self, value, evaluation): return result def check_SignPadding(self, value, evaluation): - if value.same(Symbol("True")): + if value.sameQ(Symbol("True")): return True - elif value.same(Symbol("False")): + elif value.sameQ(Symbol("False")): return False return evaluation.message(self.get_name(), "opttf", value) diff --git a/mathics/builtin/linalg.py b/mathics/builtin/linalg.py index 3644c46d1f..d4be70eedd 100644 --- a/mathics/builtin/linalg.py +++ b/mathics/builtin/linalg.py @@ -1083,7 +1083,7 @@ def _norm_calc(head, u, v, evaluation): expr_eval = expr.evaluate(evaluation) finally: evaluation.quiet_all = old_quiet_all - if expr_eval.same(expr): + if expr_eval.sameQ(expr): evaluation.message("Norm", "nvm") return None else: diff --git a/mathics/builtin/lists.py b/mathics/builtin/lists.py index d1ae9405da..e67b270ec3 100644 --- a/mathics/builtin/lists.py +++ b/mathics/builtin/lists.py @@ -2452,7 +2452,7 @@ def apply_iter(self, expr, i, imin, imax, di, evaluation): result = from_sympy(result) result = cancel(result) - if not result.same(whole_expr): + if not result.sameQ(whole_expr): return result return @@ -3122,7 +3122,7 @@ def listener(e, tag): for pattern, items in sown: if pattern.does_match(tag, evaluation): for item in items: - if item[0].same(tag): + if item[0].sameQ(tag): item[1].append(e) break else: @@ -3306,7 +3306,8 @@ def __init__(self, test, evaluation, name): def select(self, elem): return self._groups - def same(self, a, b): + def sameQ(self, a, b) -> bool: + """Mathics SameQ""" return _test_pair(self._test, a, b, self._evaluation, self._name) @@ -3334,8 +3335,9 @@ def __init__(self): def select(self, elem): return self._hashes[hash(elem)] - def same(self, a, b): - return a.same(b) + def sameQ(self, a, b) -> bool: + """Mathics SameQ""" + return a.sameQ(b) class _GatherBin: @@ -3413,7 +3415,7 @@ def _gather(self, keys, values, equivalence): for key, value in zip(keys.leaves, values.leaves): selection = equivalence.select(key) for prototype, add_to_bin in selection: # find suitable bin - if equivalence.same(prototype, key): + if equivalence.sameQ(prototype, key): add_to_bin(value) # add to existing bin break else: @@ -3590,10 +3592,10 @@ def apply(self, lists, evaluation, options={}): same_test = self.get_option(options, "SameTest", evaluation) operands = [l.leaves for l in seq] if not _is_sameq(same_test): - same = lambda a, b: _test_pair(same_test, a, b, evaluation, self.get_name()) - operands = [self._remove_duplicates(op, same) for op in operands] + sameQ = lambda a, b: _test_pair(same_test, a, b, evaluation, self.get_name()) + operands = [self._remove_duplicates(op, sameQ) for op in operands] items = functools.reduce( - lambda a, b: [e for e in self._elementwise(a, b, same)], operands + lambda a, b: [e for e in self._elementwise(a, b, sameQ)], operands ) else: items = list( @@ -3632,11 +3634,11 @@ class Union(_SetOperation): _operation = "union" - def _elementwise(self, a, b, same): + def _elementwise(self, a, b, sameQ): for eb in b: yield eb for ea in a: - if not any(same(eb, ea) for eb in b): + if not any(sameQ(eb, ea) for eb in b): yield ea @@ -3666,9 +3668,9 @@ class Intersection(_SetOperation): _operation = "intersection" - def _elementwise(self, a, b, same): + def _elementwise(self, a, b, sameQ): for ea in a: - if any(same(eb, ea) for eb in b): + if any(sameQ(eb, ea) for eb in b): yield ea @@ -3711,9 +3713,9 @@ class Complement(_SetOperation): _operation = "difference" - def _elementwise(self, a, b, same): + def _elementwise(self, a, b, sameQ): for ea in a: - if not any(same(eb, ea) for eb in b): + if not any(sameQ(eb, ea) for eb in b): yield ea @@ -6195,13 +6197,14 @@ def apply(self, list1, list2, evaluation, options={}): same_test = self.get_option(options, "SameTest", evaluation) - def same(a, b): + def sameQ(a, b) -> bool: + """Mathics SameQ""" result = Expression(same_test, a, b).evaluate(evaluation) return result.is_true() self.check_options(None, evaluation, options) for a in list1.leaves: - if not any(same(a, b) for b in list2.leaves): + if not any(sameQ(a, b) for b in list2.leaves): return Symbol("False") return Symbol("True") diff --git a/mathics/builtin/manipulate.py b/mathics/builtin/manipulate.py index e864cc07cb..5cf8d80da5 100755 --- a/mathics/builtin/manipulate.py +++ b/mathics/builtin/manipulate.py @@ -196,7 +196,7 @@ def _add_options_widget(self, symbol, options, default, label, evaluation): default_index = 0 for i, option in enumerate(options.leaves): - if option.same(default): + if option.sameQ(default): default_index = i widget = _create_widget( diff --git a/mathics/builtin/numeric.py b/mathics/builtin/numeric.py index d9d4c0d820..2a7e8daa1b 100644 --- a/mathics/builtin/numeric.py +++ b/mathics/builtin/numeric.py @@ -241,7 +241,7 @@ def apply_with_prec(self, expr, prec, evaluation): name, "System`NValues", nexpr, evaluation ) if result is not None: - if not result.same(nexpr): + if not result.sameQ(nexpr): result = Expression(SymbolN, result, prec).evaluate(evaluation) return result diff --git a/mathics/builtin/patterns.py b/mathics/builtin/patterns.py index 7918081476..033753318c 100644 --- a/mathics/builtin/patterns.py +++ b/mathics/builtin/patterns.py @@ -340,7 +340,7 @@ def apply_list(self, expr, rules, evaluation): result, applied = expr.apply_rules(rules, evaluation) if applied: result = result.evaluate(evaluation) - if applied and not result.same(expr): + if applied and not result.sameQ(expr): expr = result else: break @@ -674,7 +674,7 @@ def init(self, expr): self.content = expr.leaves[0] def match(self, yield_func, expression, vars, evaluation, **kwargs): - if self.content.same(expression): + if self.content.sameQ(expression): yield_func(vars, None) @@ -804,7 +804,7 @@ def match(self, yield_func, expression, vars, evaluation, **kwargs): else: self.pattern.match(yield_func, expression, new_vars, evaluation) else: - if existing.same(expression): + if existing.sameQ(expression): yield_func(vars, None) def get_match_candidates(self, leaves, expression, attributes, evaluation, vars={}): @@ -941,7 +941,7 @@ def get_default_value(name, evaluation, k=None, n=None): name, "System`DefaultValues", defaultexpr, evaluation ) if result is not None: - if result.same(defaultexpr): + if result.sameQ(defaultexpr): result = result.evaluate(evaluation) return result return None @@ -997,7 +997,7 @@ class Blank(_Blank): def match(self, yield_func, expression, vars, evaluation, **kwargs): if not expression.has_form("Sequence", 0): if self.head is not None: - if expression.get_head().same(self.head): + if expression.get_head().sameQ(self.head): yield_func(vars, None) else: yield_func(vars, None) diff --git a/mathics/builtin/strings.py b/mathics/builtin/strings.py index 1d35aae6e2..a9b5febf81 100644 --- a/mathics/builtin/strings.py +++ b/mathics/builtin/strings.py @@ -209,7 +209,7 @@ def recurse(x, quantifiers=q): if patt is not None: if expr.leaves[1].has_form("Blank", 0): pass # ok, no warnings - elif not expr.leaves[1].same(patt): + elif not expr.leaves[1].sameQ(patt): evaluation.message( "StringExpression", "cond", expr.leaves[0], expr, expr.leaves[0] ) @@ -1168,7 +1168,7 @@ def _find(py_stri, py_rules, py_n, flags): raise NotImplementedError() def _apply(self, string, rule, n, evaluation, options, cases): - if n.same(Symbol("System`Private`Null")): + if n.sameQ(Symbol("System`Private`Null")): expr = Expression(self.get_name(), string, rule) n = None else: @@ -2269,17 +2269,17 @@ class HammingDistance(Builtin): } @staticmethod - def _compute(u, v, same, evaluation): + def _compute(u, v, sameQ, evaluation): if len(u) != len(v): evaluation.message("HammingDistance", "idim", u, v) return None else: - return Integer(sum(0 if same(x, y) else 1 for x, y in zip(u, v))) + return Integer(sum(0 if sameQ(x, y) else 1 for x, y in zip(u, v))) def apply_list(self, u, v, evaluation): "HammingDistance[u_List, v_List]" return HammingDistance._compute( - u.leaves, v.leaves, lambda x, y: x.same(y), evaluation + u.leaves, v.leaves, lambda x, y: x.sameQ(y), evaluation ) def apply_string(self, u, v, evaluation, options): @@ -2314,7 +2314,7 @@ def normalize(c): py_b = py_b.lower() return Integer(self._distance(py_a, py_b, lambda u, v: u == v)) elif a.get_head_name() == "System`List" and b.get_head_name() == "System`List": - return Integer(self._distance(a.leaves, b.leaves, lambda u, v: u.same(v))) + return Integer(self._distance(a.leaves, b.leaves, lambda u, v: u.sameQ(v))) else: return Expression("EditDistance", a, b) @@ -2354,14 +2354,14 @@ def _levenshtein_d0(s2): # compute D(0, ...) return list(range(len(s2) + 1)) # see (1), (3) -def _levenshtein_di(c1, s2, i, d_prev, same, cost): # compute one new row +def _levenshtein_di(c1, s2, i, d_prev, sameQ, cost): # compute one new row # given c1 = s1[i], s2, i, d_prev = D(i - 1, ...), compute D(i, ...) yield i # start with D(i, 0) = i, see (2) d_curr_prev_j = i # d_curr_prev_j stores D(i, j - 1) for j, c2 in _one_based(enumerate(s2)): # c2 = s2[[j]] - cond = 0 if same(c1, c2) else cost + cond = 0 if sameQ(c1, c2) else cost d_curr_j = min( # see (4) d_prev[j - 1] + cond, # D(i - 1, j - 1) + cond; substitution @@ -2380,7 +2380,7 @@ def _levenshtein(s1, s2, same): return d_prev[-1] -def _damerau_levenshtein(s1, s2, same): +def _damerau_levenshtein(s1, s2, sameQ): # _damerau_levenshtein works like _levenshtein, except for one additional # rule covering transposition: # @@ -2390,9 +2390,9 @@ def _damerau_levenshtein(s1, s2, same): def row(d_prev_prev, d_prev, i, prev_c1, c1, cost): # given c1 = s1[i], d_prev_prev = D(i - 2), d_prev = D(i - 1), # prev_c1 = s1[[i - 1]], c1 = s1[[i]], compute D(i, ...) - for j, d_curr_j in enumerate(_levenshtein_di(c1, s2, i, d_prev, same, cost)): + for j, d_curr_j in enumerate(_levenshtein_di(c1, s2, i, d_prev, sameQ, cost)): if i > 1 and j > 1: - if same(c1, s2[j - 2]) and same(prev_c1, s2[j - 1]): # transposition? + if sameQ(c1, s2[j - 2]) and sameQ(prev_c1, s2[j - 1]): # transposition? # i.e. if s1[[i]] = s2[[j-1]] and s1[[i-1]] = s2[[j]] d_curr_j = min(d_curr_j, d_prev_prev[j - 2] + cost) yield d_curr_j @@ -2407,8 +2407,8 @@ def row(d_prev_prev, d_prev, i, prev_c1, c1, cost): return d_prev[-1] -def _levenshtein_like_or_border_cases(s1, s2, same, compute): - if len(s1) == len(s2) and all(same(c1, c2) for c1, c2 in zip(s1, s2)): +def _levenshtein_like_or_border_cases(s1, s2, sameQ, compute): + if len(s1) == len(s2) and all(sameQ(c1, c2) for c1, c2 in zip(s1, s2)): return 0 if len(s1) < len(s2): @@ -2417,7 +2417,7 @@ def _levenshtein_like_or_border_cases(s1, s2, same, compute): if len(s2) == 0: return len(s1) - return compute(s1, s2, same) + return compute(s1, s2, sameQ) class EditDistance(_StringDistance): @@ -2453,8 +2453,8 @@ class EditDistance(_StringDistance): = 2 """ - def _distance(self, s1, s2, same): - return _levenshtein_like_or_border_cases(s1, s2, same, _levenshtein) + def _distance(self, s1, s2, sameQ): + return _levenshtein_like_or_border_cases(s1, s2, sameQ, _levenshtein) class DamerauLevenshteinDistance(_StringDistance): @@ -2491,8 +2491,8 @@ class DamerauLevenshteinDistance(_StringDistance): = 1 """ - def _distance(self, s1, s2, same): - return _levenshtein_like_or_border_cases(s1, s2, same, _damerau_levenshtein) + def _distance(self, s1, s2, sameQ): + return _levenshtein_like_or_border_cases(s1, s2, sameQ, _damerau_levenshtein) class RemoveDiacritics(Builtin): diff --git a/mathics/builtin/tensors.py b/mathics/builtin/tensors.py index ec896177a3..f501795454 100644 --- a/mathics/builtin/tensors.py +++ b/mathics/builtin/tensors.py @@ -121,7 +121,7 @@ def get_dimensions(expr, head=None): if expr.is_atom(): return [] else: - if head is not None and not expr.head.same(head): + if head is not None and not expr.head.sameQ(head): return [] sub_dim = None sub = [] @@ -355,13 +355,13 @@ def apply(self, f, lists, evaluation): return if head is None: head = list.head - elif not list.head.same(head): + elif not list.head.sameQ(head): evaluation.message("Outer", "heads", head, list.head) return def rec(item, rest_lists, current): evaluation.check_stopped() - if item.is_atom() or not item.head.same(head): + if item.is_atom() or not item.head.sameQ(head): if rest_lists: return rec(rest_lists[0], rest_lists[1:], current + [item]) else: diff --git a/mathics/core/definitions.py b/mathics/core/definitions.py index c36d04b756..6fb905924f 100644 --- a/mathics/core/definitions.py +++ b/mathics/core/definitions.py @@ -722,7 +722,7 @@ def get_tag_position(pattern, name) -> typing.Optional[str]: def insert_rule(values, rule) -> None: for index, existing in enumerate(values): - if existing.pattern.same(rule.pattern): + if existing.pattern.sameQ(rule.pattern): del values[index] break # use insort_left to guarantee that if equal rules exist, newer rules will @@ -818,7 +818,7 @@ def remove_rule(self, lhs) -> bool: if position: values = self.get_values_list(position) for index, existing in enumerate(values): - if existing.pattern.expr.same(lhs): + if existing.pattern.expr.sameQ(lhs): del values[index] return True return False diff --git a/mathics/core/expression.py b/mathics/core/expression.py index d37e7b74f5..80fbc9b1bf 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -126,7 +126,12 @@ def from_python(arg): # return Symbol(arg) elif isinstance(arg, dict): entries = [ - Expression("Rule", from_python(key), from_python(arg[key]),) for key in arg + Expression( + "Rule", + from_python(key), + from_python(arg[key]), + ) + for key in arg ] return Expression(SymbolList, *entries) elif isinstance(arg, BaseExpression): @@ -345,8 +350,9 @@ def user_hash(self, update) -> None: # __hash__ might only hash a sample of the data available. raise NotImplementedError - def same(self, other) -> bool: - pass + def sameQ(self, other) -> bool: + """Mathics SameQ""" + return id(self) == id(other) def get_sequence(self): if self.get_head().get_name() == "System`Sequence": @@ -1183,17 +1189,18 @@ def get_sort_key(self, pattern_sort=False): else: return [1 if self.is_numeric() else 2, 3, self._head, self._leaves, 1] - def same(self, other) -> bool: + def sameQ(self, other) -> bool: + """Mathics SameQ""" if id(self) == id(other): return True if self.get_head_name() != other.get_head_name(): return False - if not self._head.same(other.get_head()): + if not self._head.sameQ(other.get_head()): return False if len(self._leaves) != len(other.get_leaves()): return False for leaf, other in zip(self._leaves, other.get_leaves()): - if not leaf.same(other): + if not leaf.sameQ(other): return False return True @@ -1207,7 +1214,7 @@ def flatten( sub_level = None if level is None else level - 1 do_flatten = False for leaf in self._leaves: - if leaf.get_head().same(head) and ( + if leaf.get_head().sameQ(head) and ( not pattern_only or leaf.pattern_sequence ): do_flatten = True @@ -1215,7 +1222,7 @@ def flatten( if do_flatten: new_leaves = [] for leaf in self._leaves: - if leaf.get_head().same(head) and ( + if leaf.get_head().sameQ(head) and ( not pattern_only or leaf.pattern_sequence ): new_leaf = leaf.flatten( @@ -1365,7 +1372,7 @@ def flatten_callback(new_leaves, old): if "System`Listable" in attributes: done, threaded = new.thread(evaluation) if done: - if threaded.same(new): + if threaded.sameQ(new): new._timestamp_cache(evaluation) return new, False else: @@ -1394,7 +1401,7 @@ def rules(): if result is not None: if isinstance(result, BoxConstruct): return result, False - if result.same(new): + if result.sameQ(new): new._timestamp_cache(evaluation) return new, False else: @@ -1537,7 +1544,9 @@ def is_list_interior(content): self._leaves[1].boxes_to_mathml(**options), ) elif name == "System`SqrtBox" and len(self._leaves) == 1: - return "%s" % (self._leaves[0].boxes_to_mathml(**options)) + return "%s" % ( + self._leaves[0].boxes_to_mathml(**options) + ) elif name == "System`GraphBox": return "%s" % (self._leaves[0].boxes_to_mathml(**options)) else: @@ -1640,9 +1649,7 @@ def descend(expr): return Expression(expr._head, *[apply_leaf(leaf) for leaf in expr._leaves]) if options is None: # default ReplaceAll mode; replace breadth first - result, applied = super().apply_rules( - rules, evaluation, level, options - ) + result, applied = super().apply_rules(rules, evaluation, level, options) if applied: return result, True head, applied = self._head.apply_rules(rules, evaluation, level, options) @@ -1753,7 +1760,7 @@ def thread(self, evaluation, head=None) -> typing.Tuple[bool, "Expression"]: items = [] dim = None for leaf in self._leaves: - if leaf.get_head().same(head): + if leaf.get_head().sameQ(head): if dim is None: dim = len(leaf._leaves) items = [(items + [innerleaf]) for innerleaf in leaf._leaves] @@ -1776,17 +1783,21 @@ def thread(self, evaluation, head=None) -> typing.Tuple[bool, "Expression"]: return True, Expression(head, *leaves) def is_numeric(self) -> bool: - return self._head.get_name() in system_symbols( - "Sqrt", - "Times", - "Plus", - "Subtract", - "Minus", - "Power", - "Abs", - "Divide", - "Sin", - ) and all(leaf.is_numeric() for leaf in self._leaves) + return ( + self._head.get_name() + in system_symbols( + "Sqrt", + "Times", + "Plus", + "Subtract", + "Minus", + "Power", + "Abs", + "Divide", + "Sin", + ) + and all(leaf.is_numeric() for leaf in self._leaves) + ) # TODO: complete list of numeric functions, or access NumericFunction # attribute @@ -1968,7 +1979,8 @@ def get_sort_key(self, pattern_sort=False): 1, ] - def same(self, other) -> bool: + def sameQ(self, other) -> bool: + """Mathics SameQ""" return isinstance(other, Symbol) and self.name == other.name def replace_vars(self, vars, options={}, in_scoping=True): @@ -1986,7 +1998,7 @@ def evaluate(self, evaluation): rules = evaluation.definitions.get_ownvalues(self.name) for rule in rules: result = rule.apply(self, evaluation, fully=True) - if result is not None and not result.same(self): + if result is not None and not result.sameQ(self): return result.evaluate(evaluation) return self @@ -2154,7 +2166,8 @@ def round(self, d=None) -> typing.Union["MachineReal", "PrecisionReal"]: def get_int_value(self) -> int: return self.value - def same(self, other) -> bool: + def sameQ(self, other) -> bool: + """Mathics SameQ""" return isinstance(other, Integer) and self.value == other.value def evaluate(self, evaluation): @@ -2212,7 +2225,8 @@ def round(self, d=None) -> typing.Union["MachineReal", "PrecisionReal"]: else: return PrecisionReal(self.value.n(d)) - def same(self, other) -> bool: + def sameQ(self, other) -> bool: + """Mathics SameQ""" return isinstance(other, Rational) and self.value == other.value def numerator(self) -> "Integer": @@ -2385,7 +2399,8 @@ def to_mpmath(self): def round(self, d=None) -> "MachineReal": return self - def same(self, other) -> bool: + def sameQ(self, other) -> bool: + """Mathics SameQ""" if isinstance(other, MachineReal): return self.value == other.value elif isinstance(other, PrecisionReal): @@ -2464,7 +2479,8 @@ def round(self, d=None) -> typing.Union["MachineReal", "PrecisionReal"]: d = min(dps(self.get_precision()), d) return PrecisionReal(self.value.n(d)) - def same(self, other) -> bool: + def sameQ(self, other) -> bool: + """Mathics SameQ""" if isinstance(other, PrecisionReal): return self.value == other.value elif isinstance(other, MachineReal): @@ -2511,7 +2527,7 @@ def __new__(cls, real, imag): if isinstance(imag, Complex) or not isinstance(imag, Number): raise ValueError("Argument 'imag' must be a real number.") - if imag.same(Integer(0)): + if imag.sameQ(Integer(0)): return real if isinstance(real, MachineReal) and not isinstance(imag, MachineReal): @@ -2547,7 +2563,7 @@ def do_format(self, evaluation, form) -> "Expression": parts: typing.List[Any] = [] if self.is_machine_precision() or not self.real.is_zero: parts.append(self.real) - if self.imag.same(Integer(1)): + if self.imag.sameQ(Integer(1)): parts.append(Symbol("I")) else: parts.append(Expression("Times", self.imag, Symbol("I"))) @@ -2571,7 +2587,8 @@ def get_sort_key(self, pattern_sort=False): else: return [0, 0, self.real.get_sort_key()[2], self.imag.get_sort_key()[2], 1] - def same(self, other) -> bool: + def sameQ(self, other) -> bool: + """Mathics SameQ""" return ( isinstance(other, Complex) and self.real == other.real @@ -2866,7 +2883,8 @@ def get_sort_key(self, pattern_sort=False): else: return [0, 1, self.value, 0, 1] - def same(self, other) -> bool: + def sameQ(self, other) -> bool: + """Mathics SameQ""" return isinstance(other, String) and self.value == other.value def get_string_value(self) -> str: @@ -2934,7 +2952,8 @@ def get_sort_key(self, pattern_sort=False): else: return [0, 1, self.value, 0, 1] - def same(self, other) -> bool: + def sameQ(self, other) -> bool: + """Mathics SameQ""" # FIX: check if isinstance(other, ByteArrayAtom): return self.value == other.value @@ -2992,7 +3011,7 @@ def get_default_value(name, evaluation, k=None, n=None): name, "System`DefaultValues", defaultexpr, evaluation ) if result is not None: - if result.same(defaultexpr): + if result.sameQ(defaultexpr): result = result.evaluate(evaluation) return result return None diff --git a/mathics/core/numbers.py b/mathics/core/numbers.py index dce1d36bb5..37583fa979 100644 --- a/mathics/core/numbers.py +++ b/mathics/core/numbers.py @@ -90,7 +90,8 @@ def get_type(value) -> typing.Optional[str]: return None -def same(v1, v2) -> bool: +def sameQ(v1, v2) -> bool: + """Mathics SameQ""" return get_type(v1) == get_type(v2) and v1 == v2 diff --git a/mathics/core/pattern.py b/mathics/core/pattern.py index f22c7c1bf5..3c6ad7c9af 100644 --- a/mathics/core/pattern.py +++ b/mathics/core/pattern.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # cython: language_level=3 # cython: profile=False # -*- coding: utf-8 -*- @@ -96,8 +95,9 @@ def is_atom(self): def get_head_name(self): return self.expr.get_head_name() - def same(self, other): - return self.expr.same(other.expr) + def sameQ(self, other) -> bool: + """Mathics SameQ""" + return self.expr.sameQ(other.expr) def get_head(self): return self.expr.get_head() @@ -154,12 +154,12 @@ def match( fully=True, wrap_oneid=True, ): - if expression.same(self.atom): + if expression.sameQ(self.atom): # yield vars, None yield_func(vars, None) def get_match_candidates(self, leaves, expression, attributes, evaluation, vars={}): - return [leaf for leaf in leaves if leaf.same(self.atom)] + return [leaf for leaf in leaves if leaf.sameQ(self.atom)] def get_match_count(self, vars={}): return (1, 1) diff --git a/test/test_hash.py b/test/test_hash.py index 5344156ce8..dd40bb9561 100644 --- a/test/test_hash.py +++ b/test/test_hash.py @@ -45,10 +45,10 @@ def _test_group(k, *group): sys.excepthook(*info) return False - is_same = a.same(b) + is_same = a.sameQ(b) if is_same != _symbol_truth_value(is_same_under_sameq): print( - "%sTest failed: %s and %s are inconsistent under same() and SameQ\n" + "%sTest failed: %s and %s are inconsistent under .sameQ() and SameQ\n" % (sep, repr(a), repr(b)) ) return False diff --git a/test/test_parser/test_convert.py b/test/test_parser/test_convert.py index ec9df03a80..9464281a5b 100644 --- a/test/test_parser/test_convert.py +++ b/test/test_parser/test_convert.py @@ -28,9 +28,9 @@ def check(self, expr1, expr2): expr2 = self.parse(expr2) if expr1 is None: - self.assertIsNone(expr2) + assert expr2 is None else: - self.assertTrue(expr1.same(expr2)) + assert expr1.sameQ(expr2) def scan_error(self, string): self.assertRaises(ScanError, self.parse, string) diff --git a/test/test_parser/test_util.py b/test/test_parser/test_util.py index c1ac5f20d6..7d8ae7b623 100644 --- a/test/test_parser/test_util.py +++ b/test/test_parser/test_util.py @@ -44,7 +44,7 @@ def parse(self, code): return parse(definitions, SingleLineFeeder(code)) def compare(self, expr1, expr2): - self.assertTrue(expr1.same(expr2)) + assert expr1.sameQ(expr2) def test_continuation(self): self.incomplete_error("Sin[") @@ -61,7 +61,7 @@ def parse(self, code): return parse(definitions, MultiLineFeeder(code)) def compare(self, expr1, expr2): - self.assertTrue(expr1.same(expr2)) + assert expr1.sameQ(expr2) def test_trailing_backslash(self): self.incomplete_error("x \\")