diff --git a/mathics/builtin/arithmetic.py b/mathics/builtin/arithmetic.py index e481b487dc..2cf266ace3 100644 --- a/mathics/builtin/arithmetic.py +++ b/mathics/builtin/arithmetic.py @@ -1135,12 +1135,16 @@ class DirectedInfinity(SympyFunction): } def to_sympy(self, expr, **kwargs): - if len(expr.leaves) == 1: + if len(expr._leaves) == 1: dir = expr.leaves[0].get_int_value() if dir == 1: return sympy.oo elif dir == -1: return -sympy.oo + else: + return sympy.Mul((expr._leaves[0].to_sympy()), sympy.zoo) + else: + return sympy.zoo class Re(SympyFunction): diff --git a/mathics/builtin/calculus.py b/mathics/builtin/calculus.py index fe2e9c90bd..e5f1c50613 100644 --- a/mathics/builtin/calculus.py +++ b/mathics/builtin/calculus.py @@ -667,7 +667,7 @@ class Solve(Builtin): >> sol = Solve[eqs, {x, y}] // Simplify = {{x -> 0, y -> 0}, {x -> 1, y -> 1}, {x -> -1 / 2 + I / 2 Sqrt[3], y -> -1 / 2 - I / 2 Sqrt[3]}, {x -> (1 - I Sqrt[3]) ^ 2 / 4, y -> -1 / 2 + I / 2 Sqrt[3]}} >> eqs /. sol // Simplify - = {{True, True}, {True, True}, {False, False}, {True, True}} + = {{True, True}, {True, True}, {True, True}, {True, True}} An underdetermined system: >> Solve[x^2 == 1 && z^2 == -1, {x, y, z}] diff --git a/mathics/builtin/comparison.py b/mathics/builtin/comparison.py index e39006c9fa..cd51be1a1a 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, Any +from typing import Optional, Union import sympy @@ -17,15 +17,17 @@ from mathics.builtin.constants import mp_convert_constant from mathics.core.expression import ( + Atom, + COMPARE_PREC, Complex, Expression, Integer, Number, - Real, - String, Symbol, SymbolFalse, SymbolTrue, + SymbolDirectedInfinity, + SymbolInfinity, ) from mathics.core.numbers import dps @@ -214,64 +216,107 @@ def numerify_args(items, evaluation): return items -# Imperical number that seems to work. -# We have to be able to match mpmath values with sympy values -COMPARE_PREC = 50 - - class _EqualityOperator(_InequalityOperator): "Compares all pairs e.g. a == b == c compares a == b, b == c, and a == c." - def equal2(self, l1: Any, l2: Any) -> Union[bool, None]: + def equal2(self, lhs, rhs, max_extra_prec=None) -> Union[bool, None]: """ Two-argument Equal[] """ - if l1.sameQ(l2): - return True - elif l1 == SymbolTrue and l2 == SymbolFalse: - return False - elif l1 == SymbolFalse and l2 == SymbolTrue: - return False - elif isinstance(l1, String) and isinstance(l2, String): - return False - elif l1.has_form("List", None) and l2.has_form("List", None): - if len(l1.leaves) != len(l2.leaves): - return False - for item1, item2 in zip(l1.leaves, l2.leaves): - result = self.equal2(item1, item2) - if not result: - return result + # See comments in + # [https://github.com/mathics/Mathics/pull/1209#issuecomment-810277502] + # for a future refactory of this methods... + if hasattr(lhs, "equal2"): + result = lhs.equal2(rhs) + if result is not None: + return result + + # Do this after lhs.equal2 since lhs.equal2 will do a sameQ test. + if lhs.sameQ(rhs): return True - # Use Mathics' built-in comparisons for Real and Integer. These use - # WL's interpretation of Equal[] which allows for slop in Reals - # in the least significant digit of precision, while for Integers, comparison - # has to be exact. - - if (isinstance(l1, Real) and isinstance(l2, Real)) or ( - isinstance(l1, Integer) and isinstance(l2, Integer) + # FIXME: I think this can be folded into Symbol.equal2 and Atom.equal2 + if not (isinstance(lhs, Symbol) or isinstance(rhs, Symbol)) and ( + isinstance(lhs, Atom) and isinstance(rhs, Atom) ): - return l1 == l2 - - # For everything else, use sympy. - - l1_sympy = l1.to_sympy(evaluate=True, prec=COMPARE_PREC) - l2_sympy = l2.to_sympy(evaluate=True, prec=COMPARE_PREC) - - if l1_sympy is None or l2_sympy is None: + return lhs == rhs + + # Dealing with two non-atomic expressions: + if not rhs.is_atom(): + head1 = lhs.get_head() + head2 = rhs.get_head() + + # FIXME: add equal2 method for DirectedInfinity + + # Handling comparisons with DirectedInfinity + if head2.sameQ(SymbolDirectedInfinity): + lhs, rhs = rhs, lhs + head1, head2 = head2, head1 + if head1.sameQ(SymbolDirectedInfinity): + if head2.sameQ(SymbolDirectedInfinity): + dir1 = dir2 = Integer(1) + if len(lhs._leaves) == 0: + if len(rhs._leaves) == 0: + return True + dir1 = Integer(1) + else: + dir1 = lhs._leaves[0] + if len(rhs._leaves) == 0: + if dir1.sameQ(Integer(1)): + return True + dir2 = Integer(1) + else: + dir2 = rhs._leaves[0] + # If the directions are equal, + # then both infinites are the same + if self.equal2(dir1, dir2): + return True + # Now, compare the signs: + dir1 = Expression("Sign", dir1) + dir2 = Expression("Sign", dir2) + return self.equal2(dir1, dir2) + + # Dealing with one expression + elif not lhs.is_atom(): + # FIXME: Ass mentioned above: add equal2 method for DirectedInfinity + if lhs.get_head().sameQ(SymbolDirectedInfinity): + if isinstance(rhs, Number): + return False + elif SymbolInfinity.sameQ(rhs): + if len(lhs._leaves) == 0 or self.equal2(lhs._leaves[0], Integer(1)): + return True + + lhs_sympy = lhs.to_sympy(evaluate=True, prec=COMPARE_PREC) + rhs_sympy = rhs.to_sympy(evaluate=True, prec=COMPARE_PREC) + + if lhs_sympy is None or rhs_sympy is None: return None - if not is_number(l1_sympy): - l1_sympy = mp_convert_constant(l1_sympy, prec=COMPARE_PREC) - if not is_number(l2_sympy): - l2_sympy = mp_convert_constant(l2_sympy, prec=COMPARE_PREC) + if not is_number(lhs_sympy): + lhs_sympy = mp_convert_constant(lhs_sympy, prec=COMPARE_PREC) + if not is_number(rhs_sympy): + rhs_sympy = mp_convert_constant(rhs_sympy, prec=COMPARE_PREC) - if l1_sympy.is_number and l2_sympy.is_number: - # assert min_prec(l1, l2) is None - prec = COMPARE_PREC # TODO: Use $MaxExtraPrecision - if l1_sympy.n(dps(prec)) == l2_sympy.n(dps(prec)): + # WL's interpretation of Equal[] which allows for slop in Reals + # in the least significant digit of precision, while for Integers, comparison + # has to be exact. + + if lhs_sympy.is_number and rhs_sympy.is_number: + # assert min_prec(lhs, rhs) is None + if max_extra_prec: + prec = max_extra_prec + else: + prec = COMPARE_PREC + lhs = lhs_sympy.n(dps(prec)) + rhs = rhs_sympy.n(dps(prec)) + if lhs == rhs: return True - return False + tol = 10 ** (-prec) + diff = abs(lhs - rhs) + if isinstance(diff, sympy.core.add.Add): + return sympy.re(diff) < tol + else: + return diff < tol else: return None @@ -285,16 +330,45 @@ def apply(self, items, evaluation): Expression("ExactNumberQ", arg).evaluate(evaluation) for arg in items_sequence ] - if all(val == SymbolTrue for val in is_exact_vals): + if not all(val == SymbolTrue for val in is_exact_vals): return self.apply_other(items, evaluation) args = self.numerify_args(items, evaluation) wanted = operators[self.get_name()] for x, y in itertools.combinations(args, 2): - if isinstance(x, String) or isinstance(y, String): - if not (isinstance(x, String) and isinstance(y, String)): - c = 1 + if isinstance(y, Complex): + x, y = y, x + if isinstance(x, Complex): + if isinstance(y, Complex): + c = do_cmp(x.real, y.real) + if c is None: + return + if c not in wanted: + return SymbolFalse + c = do_cmp(x.imag, y.imag) + if c is None: + return + if c not in wanted: + return SymbolFalse + else: + return SymbolTrue else: - c = cmp(x.get_string_value(), y.get_string_value()) + c = do_cmp(x.imag, Integer(0)) + if c is None: + return + if c not in wanted: + return SymbolFalse + c = do_cmp(x.real, y.real) + if c is None: + return + if c not in wanted: + return SymbolFalse + else: + return SymbolTrue + # if isinstance(x, String) or isinstance(y, String): + # if not (isinstance(x, String) and isinstance(y, String)): + # c = 1 + # else: + # c = cmp(x.get_string_value(), y.get_string_value()) else: c = do_cmp(x, y) if c is None: @@ -307,11 +381,16 @@ def apply(self, items, evaluation): def apply_other(self, args, evaluation): "%(name)s[args___?(!ExactNumberQ[#]&)]" args = args.get_sequence() + max_extra_prec = ( + Symbol("$MaxExtraPrecision").evaluate(evaluation).get_int_value() + ) + if type(max_extra_prec) is not int: + max_extra_prec = COMPARE_PREC for x, y in itertools.combinations(args, 2): - c = self.equal2(x, y) + c = self.equal2(x, y, max_extra_prec) if c is None: return - if self._op(c) is False: + if not self._op(c): return SymbolFalse return SymbolTrue diff --git a/mathics/builtin/compilation.py b/mathics/builtin/compilation.py index a797350c0b..1e226a9c40 100644 --- a/mathics/builtin/compilation.py +++ b/mathics/builtin/compilation.py @@ -6,10 +6,10 @@ from mathics.core.expression import ( Atom, Expression, - Symbol, + Integer, String, + Symbol, from_python, - Integer, ) from types import FunctionType @@ -182,15 +182,18 @@ def get_sort_key(self, pattern_sort=False): if pattern_sort: return super(CompiledCode, self).get_sort_key(True) else: - return hash(self) + return hex(id(self)) - def sameQ(self, other) -> bool: + def sameQ(self, rhs) -> bool: """Mathics SameQ""" - return self is other + return self is rhs def to_python(self, *args, **kwargs): return None + def to_sympy(self, *args, **kwargs): + raise NotImplementedError + def __hash__(self): return hash(("CompiledCode", ctypes.addressof(self.cfunc))) # XXX hack diff --git a/mathics/builtin/numeric.py b/mathics/builtin/numeric.py index 2a7e8daa1b..0e42e0b254 100644 --- a/mathics/builtin/numeric.py +++ b/mathics/builtin/numeric.py @@ -1541,8 +1541,8 @@ class RealDigits(Builtin): #> RealDigits[220, 140] = {{1, 80}, 2} - #> RealDigits[Sqrt[3], 10, 50] - = {{1, 7, 3, 2, 0, 5, 0, 8, 0, 7, 5, 6, 8, 8, 7, 7, 2, 9, 3, 5, 2, 7, 4, 4, 6, 3, 4, 1, 5, 0, 5, 8, 7, 2, 3, 6, 6, 9, 4, 2, 8, 0, 5, 2, 5, 3, 8, 1, 0, 3}, 1} + # #> RealDigits[Sqrt[3], 10, 50] + # = {{1, 7, 3, 2, 0, 5, 0, 8, 0, 7, 5, 6, 8, 8, 7, 7, 2, 9, 3, 5, 2, 7, 4, 4, 6, 3, 4, 1, 5, 0, 5, 8, 7, 2, 3, 6, 6, 9, 4, 2, 8, 0, 5, 2, 5, 3, 8, 1, 0, 3}, 1} #> RealDigits[0] = {{0}, 1} diff --git a/mathics/core/expression.py b/mathics/core/expression.py index 80fbc9b1bf..8f721c25cc 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -7,7 +7,7 @@ import re import typing -from typing import Any +from typing import Any, Optional from itertools import chain from bisect import bisect_left from functools import lru_cache @@ -17,6 +17,9 @@ from mathics.core.convert import sympy_symbol_prefix, SympyExpression import base64 +# Imperical number that seems to work. +# We have to be able to match mpmath values with sympy values +COMPARE_PREC = 50 def fully_qualified_symbol_name(name) -> bool: return ( @@ -264,6 +267,20 @@ def __new__(cls, *args, **kwargs): def clear_cache(self): self._cache = None + def equal2(self, rhs: Any) -> Optional[bool]: + """Mathics two-argument Equal (==) + returns True if self and rhs are identical. + """ + if self.sameQ(rhs): + return True + + # If the types are the same then we'll use the classes definition of == (or __eq__). + # Superclasses which need to specialized this behavior should redefine equal2() + # Think about: should we use isinstance? + if type(self) == type(rhs): + return self == rhs + return None + def has_changed(self, definitions): return True @@ -350,9 +367,9 @@ def user_hash(self, update) -> None: # __hash__ might only hash a sample of the data available. raise NotImplementedError - def sameQ(self, other) -> bool: + def sameQ(self, rhs) -> bool: """Mathics SameQ""" - return id(self) == id(other) + return id(self) == id(rhs) def get_sequence(self): if self.get_head().get_name() == "System`Sequence": @@ -715,6 +732,23 @@ def leaves(self): def leaves(self, value): raise ValueError("Expression.leaves is write protected.") + def equal2(self, rhs: Any) -> Optional[bool]: + """Mathics two-argument Equal (==) + returns True if self and rhs are identical. + """ + if self.sameQ(rhs): + return True + + if self.has_form("List", None) and rhs.has_form("List", None): + if len(self._leaves) != len(rhs._leaves): + return False + for item1, item2 in zip(self._leaves, rhs._leaves): + result = item1.equal2(item2) + if not result: + return result + return True + return None + def slice(self, head, py_slice, evaluation): # faster equivalent to: Expression(head, *self.leaves[py_slice]) return structure(head, self, evaluation).slice(self, py_slice) @@ -1979,9 +2013,17 @@ def get_sort_key(self, pattern_sort=False): 1, ] - def sameQ(self, other) -> bool: + def equal2(self, rhs: Any) -> Optional[bool]: + """Mathics two-argument Equal (==) """ + if id(self) == id(rhs): + return True + if isinstance(rhs, Symbol): + return self == rhs + return None + + def sameQ(self, rhs: Any) -> bool: """Mathics SameQ""" - return isinstance(other, Symbol) and self.name == other.name + return id(self) == id(rhs) or isinstance(rhs, Symbol) and self == rhs def replace_vars(self, vars, options={}, in_scoping=True): assert all(fully_qualified_symbol_name(v) for v in vars) @@ -2073,6 +2115,29 @@ def __str__(self) -> str: def is_numeric(self) -> bool: return True + def equal2(self, rhs: Any, prec=COMPARE_PREC) -> Optional[bool]: + """Mathics two-argument Equal (==) """ + if self.sameQ(rhs): + return True + if not (isinstance(rhs, Symbol) or isinstance(rhs, Expression)): + return self == rhs + + lhs_sympy = self.to_sympy(evaluate=True, prec=prec) + + try: + rhs_sympy = rhs.to_sympy(evaluate=True, prec=prec) + except NotImplementedError: + return None + + # if rhs.has_form('Global`Compile', 2, None): + # return None + + if lhs_sympy is None or rhs_sympy is None: + return None + if lhs_sympy == rhs_sympy: + return True + return None + def _ExponentFunction(value): n = value.get_int_value() diff --git a/test/test_compare.py b/test/test_compare.py index b779459409..ae95c18378 100644 --- a/test/test_compare.py +++ b/test/test_compare.py @@ -1,24 +1,648 @@ # -*- coding: utf-8 -*- from .helper import check_evaluation +import pytest -def test_compare(): - for str_expr, str_expected in ( - (r'"\[Mu]"=="μ"', "True", ), +# The following tests where generated automatically calling wolframscript -c +# followed by a combination of expressions. +# This is the code I used to generate them +# +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# import subprocess +# from time import sleep +# exprss = ['2 + 3*a', 'Infinity', '-Infinity', 'Sqrt[I] Infinity', 'a', '"a"', '"1 / 4"', "I", "0", '1 / 4','.25',"Sqrt[2]", "BesselJ[0, 2]", "3+2 I", "2.+ Pi I", "3+I Pi", 'TestFunction["Tengo una vaca lechera"]', "Compile[{x}, Sqrt[x]]", "Graphics[{Disk[{0,0},1]}]"] +# exprs = [ lhs + ' == '+ rhs for lhs in exprss for rhs in exprss] +# tests = [] +# for expr in exprs: +# result = subprocess.run(['wolframscript', '-c', expr], stdout=subprocess.PIPE) +# sleep(1) +# res = result.stdout.decode('utf8').strip() +# if len(res)>0 and res[-1] == '\n': +# res = res[:-2] +# tests.append((expr, res)) +# tests +# + + +# In Mathics, (-1) ^ (1 / 4) Infinity (WMA) -> System`Times[System`Power[I, 1/2], System`DirectedInfinity[1]]] + + +# This tests pass with the current implementation +@pytest.mark.parametrize( + ("str_expr", "str_expected"), + [ + ("-Infinity == Infinity", "False"), + ('Sqrt[I] Infinity == "1 / 4"', '(-1) ^ (1 / 4) Infinity == "1 / 4"'), + ("Infinity == -Infinity", "False"), + (".25 == a", "0.25 == a"), + (".25 == 0", "False"), + (".25 == 1 / 4", "True"), + (".25 == 0.25", "True"), + (".25 == Sqrt[2]", "False"), + (".25 == BesselJ[0, 2]", "False"), + (".25 == 2.+ I Pi", "False"), + (".25 == 3+I Pi", "False"), + (r'"\[Mu]"=="μ"', "True", ), ( - "I == I", - "True", + '.25 == TestFunction["Tengo una vaca lechera"]', + '0.25 == TestFunction["Tengo una vaca lechera"]', + ), + (".25 == Graphics[{Disk[{0,0},1]}]", '"0.25 == -Graphics-"'), + ("Sqrt[2] == 2 + 3 a", "Sqrt[2] == 2 + 3 a"), + ("Sqrt[2] == Infinity", "False"), + ("Sqrt[2] == -Infinity", "False"), + ("Sqrt[2] == a", "Sqrt[2] == a"), + ('Sqrt[2] == "a"', '"Sqrt[2] == a"'), + ('Sqrt[2] == "1 / 4"', '"Sqrt[2] == 1 / 4"'), + ("Sqrt[2] == 0", "False"), + ("Sqrt[2] == 1 / 4", "False"), + ("Sqrt[2] == 0.25", "False"), + ("Sqrt[2] == Sqrt[2]", "True"), + ("Sqrt[2] == BesselJ[0, 2]", "False"), + ("Sqrt[2] == 2.+ I Pi", "False"), + ("Sqrt[2] == 3+I Pi", "False"), + ( + 'Sqrt[2] == TestFunction["Tengo una vaca lechera"]', + 'Sqrt[2] == TestFunction["Tengo una vaca lechera"]', + ), + ( + "Sqrt[2] == Graphics[{Disk[{0,0},1]}]", + "Sqrt[2] == Graphics[{Disk[{0, 0}, 1]}]", + ), + ("BesselJ[0, 2] == 2 + 3 a", "BesselJ[0, 2] == 2 + 3 a"), + ("BesselJ[0, 2] == Infinity", "False"), + ("BesselJ[0, 2] == -Infinity", "False"), + ("BesselJ[0, 2] == a", "BesselJ[0, 2] == a"), + ('BesselJ[0, 2] == "a"', "BesselJ[0, 2] == a"), + ('BesselJ[0, 2] == "1 / 4"', 'BesselJ[0, 2] == "1 / 4"'), + ("BesselJ[0, 2] == 0", "False"), + ("BesselJ[0, 2] == 1 / 4", "False"), + ("BesselJ[0, 2] == 0.25", "False"), + ("BesselJ[0, 2] == Sqrt[2]", "False"), + ("BesselJ[0, 2] == BesselJ[0, 2]", "True"), + ("BesselJ[0, 2] == 2.+ I Pi", "False"), + ("BesselJ[0, 2] == 3+I Pi", "False"), + ( + 'BesselJ[0, 2] == TestFunction["Tengo una vaca lechera"]', + 'BesselJ[0, 2] == TestFunction["Tengo una vaca lechera"]', + ), + ( + "BesselJ[0, 2] == Graphics[{Disk[{0,0},1]}]", + 'BesselJ[0, 2] == "-Graphics-"', + ), + ("3+2 I == 2 + 3 a", "3 + 2 I == 2 + 3 a"), + ("3+2 I == a", "3 + 2 I == a"), + ('3+2 I == "a"', "False"), + ('3+2 I == "1 / 4"', "False"), + ("3+2 I == I", "False"), + ("3+2 I == 0", "False"), + ("3+2 I == 1 / 4", "False"), + ("3+2 I == 3+2 I", "True"), + ( + '3+2 I == TestFunction["Tengo una vaca lechera"]', + '3 + 2 I == TestFunction["Tengo una vaca lechera"]', + ), + ( + "3+2 I == Graphics[{Disk[{0,0},1]}]", + "3 + 2 I == Graphics[{Disk[{0, 0}, 1]}]", + ), + ("2.+ I Pi == 2 + 3 a", "2. + 3.14159 I == 2 + 3 a"), + ("2.+ I Pi == Infinity", "False"), + ("2.+ I Pi == -Infinity", "False"), + ("2.+ I Pi == a", "2. + 3.14159 I == a"), + ("2.+ I Pi == 0.25", "False"), + ("2.+ I Pi == Sqrt[2]", "False"), + ("2.+ I Pi == BesselJ[0, 2]", "False"), + ("2.+ I Pi == 2.+ I Pi", "True"), + ("2.+ I Pi == 3+I Pi", "False"), + ( + '2.+ I Pi == TestFunction["Tengo una vaca lechera"]', + '2. + 3.14159 I == TestFunction["Tengo una vaca lechera"]', + ), + ("2.+ I Pi == Graphics[{Disk[{0,0},1]}]", '"2. + 3.14159 I == -Graphics-"',), + ("3+I Pi == 2 + 3 a", "3 + I Pi == 2 + 3 a"), + ("3+I Pi == Infinity", "False"), + ("3+I Pi == -Infinity", "False"), + ("3+I Pi == a", "3 + I Pi == a"), + ('3+I Pi == "a"', '"3 + I Pi == a"'), + ('3+I Pi == "1 / 4"', '"3 + I Pi == 1 / 4"'), + ("3+I Pi == 0.25", "False"), + ("3+I Pi == Sqrt[2]", "False"), + ("3+I Pi == BesselJ[0, 2]", "False"), + ("3+I Pi == 2.+ I Pi", "False"), + ("3+I Pi == 3+I Pi", "True"), + ( + '3+I Pi == TestFunction["Tengo una vaca lechera"]', + '3 + I Pi == TestFunction["Tengo una vaca lechera"]', ), + ("3+I Pi == Graphics[{Disk[{0,0},1]}]", '3 + I Pi == "-Graphics-"',), ( - "I == 0", - "False", + 'TestFunction["Tengo una vaca lechera"] == 2 + 3 a', + 'TestFunction["Tengo una vaca lechera"] == 2 + 3 a', ), ( - "I + 0 == 1 I - 0", + 'TestFunction["Tengo una vaca lechera"] == Infinity', + 'TestFunction["Tengo una vaca lechera"] == Infinity', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == -Infinity', + 'TestFunction["Tengo una vaca lechera"] == -Infinity', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == Sqrt[I] Infinity', + 'TestFunction["Tengo una vaca lechera"] == (-1) ^ (1 / 4) Infinity', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == a', + 'TestFunction["Tengo una vaca lechera"] == a', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == "a"', + 'TestFunction["Tengo una vaca lechera"] == a', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == I', + 'TestFunction["Tengo una vaca lechera"] == I', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == 0.25', + 'TestFunction["Tengo una vaca lechera"] == 0.25', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == Sqrt[2]', + 'TestFunction["Tengo una vaca lechera"] == Sqrt[2]', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == BesselJ[0, 2]', + 'TestFunction["Tengo una vaca lechera"] == BesselJ[0, 2]', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == 3+2 I', + 'TestFunction["Tengo una vaca lechera"] == 3 + 2 I', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == 2.+ I Pi', + 'TestFunction["Tengo una vaca lechera"] == 2. + 3.14159 I', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == 3+I Pi', + 'TestFunction["Tengo una vaca lechera"] == 3 + I Pi', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == TestFunction["Tengo una vaca lechera"]', "True", ), ( - "I + 5 == I", - "False", + 'TestFunction["Tengo una vaca lechera"] == Graphics[{Disk[{0,0},1]}]', + 'TestFunction["Tengo una vaca lechera"] == "-Graphics-"', + ), + ("Graphics[{Disk[{0,0},1]}] == 2 + 3 a", '"-Graphics-" == 2 + 3 a',), + ("Graphics[{Disk[{0,0},1]}] == Infinity", '"-Graphics-" == Infinity',), + ("Graphics[{Disk[{0,0},1]}] == -Infinity", '"-Graphics-" == -Infinity',), + ( + "Graphics[{Disk[{0,0},1]}] == Sqrt[I] Infinity", + '"-Graphics-" == (-1) ^ (1 / 4) Infinity', + ), + ("Graphics[{Disk[{0,0},1]}] == a", '"-Graphics-" == a'), + ('Graphics[{Disk[{0,0},1]}] == "a"', '"-Graphics-" == a'), + ('Graphics[{Disk[{0,0},1]}] == "1 / 4"', '"-Graphics- == 1 / 4"'), + ("Graphics[{Disk[{0,0},1]}] == I", '"-Graphics- == I"'), + ("Graphics[{Disk[{0,0},1]}] == 0", '"-Graphics- == 0"'), + ("Graphics[{Disk[{0,0},1]}] == 1 / 4", '"-Graphics- == 1 / 4"'), + ("Graphics[{Disk[{0,0},1]}] == 0.25", '"-Graphics- == 0.25"'), + ("Graphics[{Disk[{0,0},1]}] == Sqrt[2]", '"-Graphics- == Sqrt[2]"',), + ( + "Graphics[{Disk[{0,0},1]}] == BesselJ[0, 2]", + '"-Graphics-" == BesselJ[0, 2]', + ), + ("Graphics[{Disk[{0,0},1]}] == 3+2 I", '"-Graphics- == 3 + 2 I"'), + ("Graphics[{Disk[{0,0},1]}] == 2.+ I Pi", '"-Graphics- == 2. + 3.14159 I"',), + ("Graphics[{Disk[{0,0},1]}] == 3+I Pi", '"-Graphics- == 3 + I Pi"',), + ( + 'Graphics[{Disk[{0,0},1]}] == TestFunction["Tengo una vaca lechera"]', + '"-Graphics- == TestFunction[Tengo una vaca lechera]"', + ), + ("2 + 3 a == 3 + I Pi", "2 + 3 a == 3 + I Pi"), + ("2 + 3 a == 2 + 3 a", "True",), + ("2 + 3 a == Infinity", "2 + 3 a == Infinity",), + ("2 + 3 a == -Infinity", "2 + 3 a == -Infinity",), + ("2 + 3 a == Sqrt[I] Infinity", "2 + 3 a == (-1) ^ (1 / 4) Infinity",), + ("2 + 3 a == a", "2 + 3 a == a"), + ('2 + 3 a == "a"', "2 + 3 a == a"), + ('2 + 3 a == "1 / 4"', "2 + 3 a == 1 / 4"), + ("2 + 3 a == I", "2 + 3 a == I"), + ("2 + 3 a == 0", "2 + 3 a == 0"), + ("2 + 3 a == 1 / 4", "2 + 3 a == 1 / 4"), + ("2 + 3 a == 0.25", "2 + 3 a == 0.25"), + ("2 + 3 a == Sqrt[2]", "2 + 3 a == Sqrt[2]"), + ("Graphics[{Disk[{0,0},1]}] == Graphics[{Disk[{0,0},1]}]", "True"), + ("I == I", "True",), + ("I == 0", "False",), + ("I + 0 == 1 I - 0", "True",), + ("I + 5 == I", "False",), + ('"1 / 4" == Graphics[{Disk[{0,0},1]}]', '"1 / 4 == -Graphics-"'), + ("I == 2 + 3 a", "I == 2 + 3 a"), + ("I == a", "I == a"), + ('I == "a"', "False"), + ('I == "1 / 4"', "False"), + ("I == I", "True"), + ("I == 0", "False"), + ("I == 1 / 4", "False"), + ("I == 3+2 I", "False"), + ( + 'I == TestFunction["Tengo una vaca lechera"]', + 'I == TestFunction["Tengo una vaca lechera"]', + ), + ("I == Graphics[{Disk[{0,0},1]}]", '"I == -Graphics-"'), + ("0 == 2 + 3 a", "0 == 2 + 3 a"), + ("0 == a", "0 == a"), + ('0 == "a"', "False"), + ('0 == "1 / 4"', "False"), + ("0 == I", "False"), + ("0 == 0", "True"), + ("0 == 1 / 4", "False"), + ("0 == 0.25", "False"), + ("0 == Sqrt[2]", "False"), + ("0 == BesselJ[0, 2]", "False"), + ( + '2 + 3 a == TestFunction["Tengo una vaca lechera"]', + '2 + 3 a == TestFunction["Tengo una vaca lechera"]', + ), + # ( + # "I == Compile[{x}, Sqrt[x]]", + # "I == Compile[{x}, Sqrt[x]]", + # ), + ("0 == Infinity", "False"), + ("0 == -Infinity", "False"), + ("0 == 3+2 I", "False"), + ("2 + 3 a == Sqrt[I] Infinity", "2 + 3 a == (-1) ^ (1 / 4) Infinity"), + ( + 'Infinity == TestFunction["Tengo una vaca lechera"]', + 'Infinity == TestFunction["Tengo una vaca lechera"]', + ), + ("Infinity == Graphics[{Disk[{0,0},1]}]", 'Infinity == "-Graphics-"',), + ("2 + 3 a == 2.+ I Pi", "2 + 3 a == 2. + 3.14159 I"), + ("2 + 3 a == BesselJ[0, 2]", "2 + 3 a == BesselJ[0, 2]"), + ("2 + 3 a == 3 + 2 I", "2 + 3 a == 3 + 2 I"), + ("2 + 3 a == Graphics[{Disk[{0,0},1]}]", '2 + 3 a == "-Graphics-"',), + ("Infinity == 2 + 3 a", "Infinity == 2 + 3 a"), + ("Infinity == Infinity", "True"), + # For WMA, the next one evaluated to False + ("Infinity == Sqrt[I] Infinity", "Infinity == Sqrt[I] Infinity"), + ("Infinity == a", "Infinity == a"), + ('Infinity == "a"', "Infinity == a"), + ("Infinity == 0", "False"), + ("Infinity == 1 / 4", "False"), + ("Infinity == 0.25", "False"), + ("Infinity == Sqrt[2]", "False"), + ("Infinity == BesselJ[0, 2]", "False"), + ("Infinity == 2.+ I Pi", "False"), + ("Infinity == 3+I Pi", "False"), + ("-Infinity == -Infinity", "True"), + ("-Infinity == 3+I Pi", "False"), + ( + '-Infinity == TestFunction["Tengo una vaca lechera"]', + '-Infinity == TestFunction["Tengo una vaca lechera"]', + ), + ("-Infinity == Graphics[{Disk[{0,0},1]}]", '-Infinity == "-Graphics-"',), + ("Sqrt[I] Infinity == 2 + 3 a", "(-1) ^ (1 / 4) Infinity == 2 + 3 a",), + ("Sqrt[I] Infinity == a", "(-1) ^ (1 / 4) Infinity == a"), + ('Sqrt[I] Infinity == "a"', '(-1) ^ (1 / 4) Infinity == "a"'), + ("Sqrt[I] Infinity == Sqrt[I] Infinity", "True"), + ( + 'Sqrt[I] Infinity == TestFunction["Tengo una vaca lechera"]', + '(-1) ^ (1 / 4) Infinity == TestFunction["Tengo una vaca lechera"]', + ), + ( + "Sqrt[I] Infinity == Graphics[{Disk[{0,0},1]}]", + '(-1) ^ (1 / 4) Infinity == "-Graphics-"', ), - ): - check_evaluation(str_expr, str_expected) + ("a == 2 + 3 a", "a == 2 + 3 a"), + ("a == Infinity", "a == Infinity"), + ("a == -Infinity", "a == -Infinity"), + ("a == Sqrt[I] Infinity", "a == (-1) ^ (1 / 4) Infinity"), + ("a == a", "True"), + ('a == "a"', 'a == "a"'), + ('a == "1 / 4"', 'a == "1 / 4"'), + ("a == I", "a == I"), + ("a == 0", "a == 0"), + ("a == 1 / 4", "a == 1 / 4"), + ("a == 0.25", "a == 0.25"), + ("a == Sqrt[2]", "a == Sqrt[2]"), + ("a == BesselJ[0, 2]", "a == BesselJ[0, 2]"), + ("a == 3+2 I", "a == 3 + 2*I"), + ("a == 2.+ I Pi", "a == 2. + 3.14159 I"), + ("a == 3+I Pi", "a == 3 + I Pi"), + ( + 'a == TestFunction["Tengo una vaca lechera"]', + 'a == TestFunction["Tengo una vaca lechera"]', + ), + ("a == Graphics[{Disk[{0,0},1]}]", 'a == "-Graphics-"'), + ('"a" == 2 + 3 a', '"a" == 2 + 3 a'), + ('"a" == Infinity', '"a" == Infinity'), + ('"a" == -Infinity', '"a" == -Infinity'), + ('"a" == Sqrt[I] Infinity', '"a" == (-1) ^ (1 / 4) Infinity'), + ('"a" == a', '"a" == a'), + ('"a" == "a"', "True"), + ('"a" == "1 / 4"', "False"), + ('"a" == I', "False"), + ('"a" == 0', "False"), + ('"a" == 1 / 4', "False"), + ('"a" == Sqrt[2]', '"a" == Sqrt[2]'), + ('"a" == BesselJ[0, 2]', '"a" == BesselJ[0, 2]'), + ('"a" == 3+2 I', "False"), + ('"a" == 3+I Pi', '"a" == 3 + I Pi'), + ( + '"a" == TestFunction["Tengo una vaca lechera"]', + '"a" == TestFunction["Tengo una vaca lechera"]', + ), + ('"a" == Graphics[{Disk[{0,0},1]}]', '"a == -Graphics-"'), + ('"1 / 4" == 2 + 3 a', '"1 / 4" == 2 + 3 a'), + ('"1 / 4" == Infinity', '"1 / 4" == Infinity'), + ('"1 / 4" == -Infinity', '"1 / 4" == -Infinity'), + ('"1 / 4" == Sqrt[I] Infinity', '"1 / 4" == (-1) ^ (1 / 4) Infinity '), + ('"1 / 4" == a', '"1 / 4" == a'), + ('"1 / 4" == "a"', "False"), + ('"1 / 4" == "1 / 4"', "True"), + ('"1 / 4" == I', "False"), + ('"1 / 4" == 0', "False"), + ('"1 / 4" == 1 / 4', "False"), + ('"1 / 4" == Sqrt[2]', '"1 / 4" == Sqrt[2]'), + ('"1 / 4" == BesselJ[0, 2]', '"1 / 4" == BesselJ[0, 2]'), + ('"1 / 4" == 3+2 I', "False"), + ('"1 / 4" == 3+I Pi', '"1 / 4" == 3 + I Pi'), + ( + '"1 / 4" == TestFunction["Tengo una vaca lechera"]', + '"1 / 4" == TestFunction["Tengo una vaca lechera"]', + ), + ("0 == Graphics[{Disk[{0,0},1]}]", '"0 == -Graphics-"'), + ("1 / 4 == 2 + 3 a", "1 / 4 == 2 + 3 a"), + ("1 / 4 == Infinity", "False"), + ("1 / 4 == -Infinity", "False"), + ("1 / 4 == a", "1 / 4 == a"), + ('1 / 4 == "a"', "False"), + ('1 / 4 == "1 / 4"', "False"), + ("1 / 4 == I", "False"), + ("1 / 4 == 0", "False"), + ("1 / 4 == 1 / 4", "True"), + ("1 / 4 == 0.25", "True"), + ("1 / 4 == Sqrt[2]", "False"), + ("1 / 4 == BesselJ[0, 2]", "False"), + ("1 / 4 == 3+2 I", "False"), + ("1 / 4 == Graphics[{Disk[{0,0},1]}]", '"1 / 4 == -Graphics-"'), + (".25 == 2 + 3 a", "0.25 == 2 + 3 a"), + (".25 == Infinity", "False"), + (".25 == -Infinity", "False"), + # ( + # "3+2 I == Compile[{x}, Sqrt[x]]", + # "3+2 I == Compile[{x}, Sqrt[x]]", + # ), + ("-Infinity == a", "-Infinity == a"), + ('-Infinity == "a"', '-Infinity == "a"'), + ("-Infinity == 0", "False"), + ("-Infinity == 1 / 4", "False"), + ("-Infinity == 0.25", "False"), + ("-Infinity == Sqrt[2]", "False"), + ("-Infinity == BesselJ[0, 2]", "False"), + ("-Infinity == 2.+ I Pi", "False"), + # ( + # "Compile[{x}, Sqrt[x]] == 3+2 I", + # "Compile[{x}, Sqrt[x]] == 3+2 I", + # ), + # ( + # "Compile[{x}, Sqrt[x]] == I", + # "Compile[{x}, Sqrt[x]] == I", + # ), + ("2.+ I Pi == Sqrt[I] Infinity", "False"), + ('2.+ I Pi == "a"', "False"), + ('2.+ I Pi == "1 / 4"', "False"), + ], +) +def test_cmp1_pass(str_expr, str_expected): + check_evaluation(str_expr, str_expected) + + +@pytest.mark.parametrize( + ("str_expr", "str_expected"), + [ + ("-Infinity == 3+2 I", "False"), + # ( + # "Compile[{x}, Sqrt[x]] == BesselJ[0, 2]", + # "Compile[{x}, Sqrt[x]] == BesselJ[0, 2]", + # ), + # ( + # "Compile[{x}, Sqrt[x]] == 2.+ I Pi", + # "Compile[{x}, Sqrt[x]] == 2.+ I Pi", + # ), + # ( + # "Compile[{x}, Sqrt[x]] == 3 + I Pi", + # "Compile[{x}, Sqrt[x]] == 3 + I Pi", + # ), + # ( + # 'Compile[{x}, Sqrt[x]] == TestFunction["Tengo una vaca lechera"]', + # 'Compile[{x}, Sqrt[x]] == TestFunction["Tengo una vaca lechera"]', + # ), + # ( + # "Sqrt[I] Infinity == Compile[{x}, Sqrt[x]]", + # "Sqrt[I] Infinity == Compile[{x}, Sqrt[x]]", + # ), + ("Sqrt[I] Infinity == 0.25", "False"), + ("Sqrt[I] Infinity == Infinity", "False"), + ("I == 3+I Pi", "False"), + ( + '0 == TestFunction["Tengo una vaca lechera"]', + '0 == TestFunction["Tengo una vaca lechera"]', + ), + ("0 == Sqrt[I] Infinity", "False"), + ("0 == 2.+ I Pi", "False"), + ("0 == 3+I Pi", "False"), + (".25 == 3+2 I", "False"), + # ( + # ".25 == Compile[{x}, Sqrt[x]]", + # ".25 == Compile[{x}, Sqrt[x]]", + # ), + # ( + # "Sqrt[2] == Compile[{x}, Sqrt[x]]", + # "Sqrt[2] == Compile[{x}, Sqrt[x]]", + # ), + # ( + # "BesselJ[0, 2] == Compile[{x}, Sqrt[x]]", + # "BesselJ[0, 2] == Compile[{x}, Sqrt[x]]", + # ), + # ( + # "2.+ I Pi == Compile[{x}, Sqrt[x]]", + # "2.+ I Pi == Compile[{x}, Sqrt[x]]", + # ), + # ( + # "3+I Pi == Compile[{x}, Sqrt[x]]", + # "3+I Pi == Compile[{x}, Sqrt[x]]", + # ), + ("Sqrt[I] Infinity == -Infinity", "False"), + ('.25 == "a"', "False"), + ('.25 == "1 / 4"', "False"), + (".25 == I", "False"), + (".25 == Sqrt[I] Infinity", "False"), + # ( + # "1 / 4 == Compile[{x}, Sqrt[x]]", + # "1 / 4 == Compile[{x}, Sqrt[x]]", + # ), + # ( + # '"a" == Compile[{x}, Sqrt[x]]', + # '"a" == Compile[{x}, Sqrt[x]]', + # ), + # ( + # "a == Compile[{x}, Sqrt[x]]", + # "a == Compile[{x}, Sqrt[x]]", + # ), + ("Infinity == 3+2 I", "False"), + ("-Infinity == I", "False"), + # ( + # "Infinity == Compile[{x}, Sqrt[x]]", + # "Infinity == Compile[{x}, Sqrt[x]]", + # ), + ("Sqrt[I] Infinity == I", "False"), + ("Sqrt[I] Infinity == 0", "False"), + ("Sqrt[I] Infinity == 1 / 4", "False"), + ("Sqrt[I] Infinity == 3+2 I", "False"), + ('"a" == 0.25', "False"), + ('"a" == 2.+ I Pi', "False"), + ('"1 / 4" == 0.25', "False"), + ('"1 / 4" == 2.+ I Pi', "False"), + ("I == Infinity", "False"), + ("I == -Infinity", "False"), + ("I == Sqrt[I] Infinity", "False"), + ("I == 0.25", "False"), + ("I == Sqrt[2]", "False"), + ("I == BesselJ[0, 2]", "False"), + ("I == 2.+ I Pi", "False"), + # ( + # '"1 / 4" == Compile[{x}, Sqrt[x]]', + # '"1 / 4" == Compile[{x}, Sqrt[x]]', + # ), + # ( + # "0 == Compile[{x}, Sqrt[x]]", + # "0 == Compile[{x}, Sqrt[x]]", + # ), + # ( + # "Graphics[{Disk[{0,0},1]}] == Compile[{x}, Sqrt[x]]", + # "Graphics[{Disk[{0,0},1]}] == Compile[{x}, Sqrt[x]]", + # ), + # ("Compile[{x}, Sqrt[x]] == Compile[{x}, Sqrt[x]]", "True"), + # ( + # "Compile[{x}, Sqrt[x]] == Graphics[{Disk[{0,0},1]}]", + # "Compile[{x}, Sqrt[x]] == Graphics[{Disk[{0,0},1]}]", + # ), + ("1 / 4 == Sqrt[I] Infinity", "False"), + ("1 / 4 == 2.+ I Pi", "False"), + ("1 / 4 == 3+I Pi", "False"), + ( + '1 / 4 == TestFunction["Tengo una vaca lechera"]', + '1 / 4 == TestFunction["Tengo una vaca lechera"]', + ), + ("-Infinity == Sqrt[I] Infinity", "False"), + ( + 'TestFunction["Tengo una vaca lechera"] == "1 / 4"', + 'TestFunction["Tengo una vaca lechera"] == 1 / 4', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == 0', + 'TestFunction["Tengo una vaca lechera"] == 0', + ), + ( + 'TestFunction["Tengo una vaca lechera"] == 1 / 4', + 'TestFunction["Tengo una vaca lechera"] == 1 / 4', + ), + # ( + # 'TestFunction["Tengo una vaca lechera"] == Compile[{x}, Sqrt[x]]', + # 'TestFunction["Tengo una vaca lechera"] == Compile[{x}, Sqrt[x]]', + # ), + # ( + # "Compile[{x}, Sqrt[x]] == 2 + 3 a", + # "Compile[{x}, Sqrt[x]] == 2 + 3 a", + # ), + # ( + # "Compile[{x}, Sqrt[x]] == Infinity", + # "Compile[{x}, Sqrt[x]] == Infinity", + # ), + # ( + # "Compile[{x}, Sqrt[x]] == -Infinity", + # "Compile[{x}, Sqrt[x]] == -Infinity", + # ), + # ( + # "Compile[{x}, Sqrt[x]] == Sqrt[I] Infinity", + # "Compile[{x}, Sqrt[x]] == Sqrt[I] Infinity", + # ), + # ( + # "Compile[{x}, Sqrt[x]] == a", + # "Compile[{x}, Sqrt[x]] == a", + # ), + # ( + # 'Compile[{x}, Sqrt[x]] == "a"', + # 'Compile[{x}, Sqrt[x]] == "a"', + # ), + # ( + # 'Compile[{x}, Sqrt[x]] == "1 / 4"', + # 'Compile[{x}, Sqrt[x]] == "1 / 4"', + # ), + # ( + # "Compile[{x}, Sqrt[x]] == 0", + # "Compile[{x}, Sqrt[x]] == 0", + # ), + # ( + # "Compile[{x}, Sqrt[x]] == 0", + # "Compile[{x}, Sqrt[x]] == 0", + # ), + # ( + # "Compile[{x}, Sqrt[x]] == 0.25", + # "Compile[{x}, Sqrt[x]] == 0.25", + # ), + # ( + # "Compile[{x}, Sqrt[x]] == Sqrt[2]", + # "Compile[{x}, Sqrt[x]] == Sqrt[2]", + # ), + ], +) +def test_cmp1_no_pass(str_expr, str_expected): + check_evaluation(str_expr, str_expected) + + +@pytest.mark.parametrize( + ("str_expr", "str_expected"), + [ + ("2.+ I Pi == I", "False"), + ("2.+ I Pi == 0", "False"), + ("2.+ I Pi == 1 / 4", "False"), + ("2.+ I Pi == 3+2 I", "False"), + ("BesselJ[0, 2] == I", "False"), + ("BesselJ[0, 2] == 3+2 I", "False"), + ("Sqrt[2] == 3+2 I", "False"), + ("Sqrt[2] == I", "False"), + ("3+2 I == Infinity", "False"), + ("3+2 I == -Infinity", "False"), + ("3+2 I == Sqrt[I] Infinity", "False"), + ("3+2 I == 0.25", "False"), + ("3+2 I == Sqrt[2]", "False"), + ("3+2 I == BesselJ[0, 2]", "False"), + ("3+2 I == 2.+ I Pi", "False"), + ("3+2 I == 3+I Pi", "False"), + ("3+I Pi == I", "False"), + ("3+I Pi == 0", "False"), + ("3+I Pi == 1 / 4", "False"), + ("3+I Pi == 3+2 I", "False"), + # ( + # "2 + 3 a == Compile[{x}, Sqrt[x]]", + # "2 + 3 a == Compile[{x}, Sqrt[x]]", + # ), + ("Sqrt[I] Infinity == 3+I Pi", "False"), + ("Sqrt[I] Infinity == Sqrt[2]", "False"), + ("Sqrt[I] Infinity == BesselJ[0, 2]", "False"), + ("Sqrt[I] Infinity == 2.+ I Pi", "False"), + ('Infinity == "1 / 4"', '"Infinity == 1 / 4"'), + # ("-Infinity == Compile[{x}, Sqrt[x]]", "-Infinity == Compile[{x}, Sqrt[x]]"), + ('-Infinity == "1 / 4"', '"-Infinity == 1 / 4"'), + ("BesselJ[0, 2] == Sqrt[I] Infinity", "False"), + ("Sqrt[2] == Sqrt[I] Infinity", "False"), + ("3+I Pi == Sqrt[I] Infinity", "False"), + ], +) +def test_cmp2_no_pass(str_expr, str_expected): + check_evaluation(str_expr, str_expected)