From c27039df7cd3d93024811fa3542b16e56dd15199 Mon Sep 17 00:00:00 2001 From: mmatera Date: Fri, 30 Aug 2024 20:55:37 -0300 Subject: [PATCH 1/7] sameq non recursive --- mathics/core/expression.py | 88 +++++++++++++++++++++++++--- mathics/doc/latex/mathics-title.pdf | Bin 31692 -> 0 bytes 2 files changed, 80 insertions(+), 8 deletions(-) delete mode 100644 mathics/doc/latex/mathics-title.pdf diff --git a/mathics/core/expression.py b/mathics/core/expression.py index f407f9957..d3caf0cb4 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -1381,14 +1381,86 @@ def sameQ(self, other: BaseElement) -> bool: return False if self is other: return True - if not self._head.sameQ(other._head): - return False - if len(self._elements) != len(other._elements): - return False - return all( - (id(element) == id(oelement) or element.sameQ(oelement)) - for element, oelement in zip(self._elements, other._elements) - ) + + # All this stuff maybe should be in mathics.eval.expression + + print("\n\n\n", 80 * "*", "sameQ") + print(" self:", self) + print(" other:", other) + parents = [ + ( + self, + other, + ) + ] + current = (self._head, other._head) + pos = [0] + len_elements = len(self.elements) + + # The next element in the tree. Maybe should be an iterator? + def next_elem(): + nonlocal len_elements + nonlocal parents + nonlocal pos + + while pos and pos[-1] == len_elements: + print(" end reached in ", pos, "(len=", len_elements, ")") + pos.pop() + parents.pop() + assert len(pos) == len(parents) + if len(pos) > 0: + len_elements = len(parents[-1][0]._elements) + assert len(parents[-1][1]._elements) == len_elements + + if len(pos) == 0: + return None + + print("accesing element of", parents[-1], len_elements, pos) + print("at position ", pos[-1]) + print("current[0]", (parents[-1][0]._elements)) + print("current[1]", (parents[-1][1]._elements)) + current = tuple(p._elements[pos[-1]] for p in parents[-1]) + pos[-1] += 1 + + print( + " next element", + current, + "in pos", + pos, + "with last length", + len_elements, + ) + return current + + while current: + print(f"comparing {current}") + if current[0] is current[1]: + print(" elements are the same. continue") + current = next_elem() + elif all(isinstance(elem, Atom) for elem in current): + print(" elements are Atoms") + if not current[0].sameQ(current[1]): + print(" not the same. Return False") + return False + print(" are the same. Continue with the next") + current = next_elem() + elif all(isinstance(elem, Expression) for elem in current): + print(" both are expressions. \n", "*** Go inside") + len_elements = len(current[0]._elements) + if len_elements != len(current[1]._elements): + print(" different lengths. Return False") + return False + parents.append(current) + print(" parents:\n", parents) + current = tuple((c._head for c in current)) + print("\n current:", current) + pos.append(0) + print(" pos:", pos) + else: # Atom is not the same than an expression + print(f"cannot compare {type(self)} and{type(other)}. Return False.") + return False + + return True def sequences(self): cache = self._cache diff --git a/mathics/doc/latex/mathics-title.pdf b/mathics/doc/latex/mathics-title.pdf deleted file mode 100644 index 996fb5c97437346f437ee6c4294ffa33d8233536..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31692 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 From 863e76d86aa027df25a5aa195644494718d2ac9c Mon Sep 17 00:00:00 2001 From: mmatera Date: Fri, 30 Aug 2024 21:55:13 -0300 Subject: [PATCH 2/7] working 1 --- mathics/core/expression.py | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/mathics/core/expression.py b/mathics/core/expression.py index d3caf0cb4..5736fcfbc 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -1384,9 +1384,10 @@ def sameQ(self, other: BaseElement) -> bool: # All this stuff maybe should be in mathics.eval.expression - print("\n\n\n", 80 * "*", "sameQ") - print(" self:", self) - print(" other:", other) + len_elements = len(self.elements) + if len(other._elements) != len_elements: + return False + parents = [ ( self, @@ -1395,7 +1396,6 @@ def sameQ(self, other: BaseElement) -> bool: ] current = (self._head, other._head) pos = [0] - len_elements = len(self.elements) # The next element in the tree. Maybe should be an iterator? def next_elem(): @@ -1404,7 +1404,6 @@ def next_elem(): nonlocal pos while pos and pos[-1] == len_elements: - print(" end reached in ", pos, "(len=", len_elements, ")") pos.pop() parents.pop() assert len(pos) == len(parents) @@ -1415,49 +1414,25 @@ def next_elem(): if len(pos) == 0: return None - print("accesing element of", parents[-1], len_elements, pos) - print("at position ", pos[-1]) - print("current[0]", (parents[-1][0]._elements)) - print("current[1]", (parents[-1][1]._elements)) current = tuple(p._elements[pos[-1]] for p in parents[-1]) pos[-1] += 1 - - print( - " next element", - current, - "in pos", - pos, - "with last length", - len_elements, - ) return current while current: - print(f"comparing {current}") if current[0] is current[1]: - print(" elements are the same. continue") current = next_elem() elif all(isinstance(elem, Atom) for elem in current): - print(" elements are Atoms") if not current[0].sameQ(current[1]): - print(" not the same. Return False") return False - print(" are the same. Continue with the next") current = next_elem() elif all(isinstance(elem, Expression) for elem in current): - print(" both are expressions. \n", "*** Go inside") len_elements = len(current[0]._elements) if len_elements != len(current[1]._elements): - print(" different lengths. Return False") return False parents.append(current) - print(" parents:\n", parents) current = tuple((c._head for c in current)) - print("\n current:", current) pos.append(0) - print(" pos:", pos) else: # Atom is not the same than an expression - print(f"cannot compare {type(self)} and{type(other)}. Return False.") return False return True From a643b196241a9ffec1cb0b1c514fb4b9db8d3262 Mon Sep 17 00:00:00 2001 From: mmatera Date: Fri, 30 Aug 2024 22:17:06 -0300 Subject: [PATCH 3/7] final --- mathics/core/expression.py | 125 +++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 53 deletions(-) diff --git a/mathics/core/expression.py b/mathics/core/expression.py index 5736fcfbc..7fea34d29 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -88,6 +88,77 @@ ) +def expression_sameQ(self, other): + """ + Iterative implementation of SameQ. + + Run a tree transversal comparison between `self` and `other`. + Return `True` if both tree structures are equal. + + This implementation avoids the issue with + the recursion limit in Python 3.12 + """ + # TODO: Consider a faster implementation. + # Would be better to use iterators and yield + # instead of this light stack implementation? + # Or maybe using some tail recursive implementation? + # Other ideas in https://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion/ + + len_elements = len(self.elements) + if len(other._elements) != len_elements: + return False + + # Initializing a "stack" + parents = [ + ( + self, + other, + ) + ] + current = (self._head, other._head) + pos = [0] + + # The next element in the tree. Maybe should be an iterator? + def next_elem(): + nonlocal len_elements + nonlocal parents + nonlocal pos + + while pos and pos[-1] == len_elements: + pos.pop() + parents.pop() + assert len(pos) == len(parents) + if len(pos) > 0: + len_elements = len(parents[-1][0]._elements) + assert len(parents[-1][1]._elements) == len_elements + + if len(pos) == 0: + return None + + current = tuple(p._elements[pos[-1]] for p in parents[-1]) + pos[-1] += 1 + return current + + while current: + if current[0] is current[1]: + current = next_elem() + elif all(isinstance(elem, Atom) for elem in current): + if not current[0].sameQ(current[1]): + return False + current = next_elem() + elif all(isinstance(elem, Expression) for elem in current): + len_elements = len(current[0]._elements) + if len_elements != len(current[1]._elements): + return False + parents.append(current) + current = tuple((c._head for c in current)) + pos.append(0) + else: # Atom is not the same than an expression + return False + + return True + + class BoxError(Exception): def __init__(self, box, form) -> None: super().__init__("Box %s cannot be formatted as %s" % (box, form)) @@ -1383,59 +1454,7 @@ def sameQ(self, other: BaseElement) -> bool: return True # All this stuff maybe should be in mathics.eval.expression - - len_elements = len(self.elements) - if len(other._elements) != len_elements: - return False - - parents = [ - ( - self, - other, - ) - ] - current = (self._head, other._head) - pos = [0] - - # The next element in the tree. Maybe should be an iterator? - def next_elem(): - nonlocal len_elements - nonlocal parents - nonlocal pos - - while pos and pos[-1] == len_elements: - pos.pop() - parents.pop() - assert len(pos) == len(parents) - if len(pos) > 0: - len_elements = len(parents[-1][0]._elements) - assert len(parents[-1][1]._elements) == len_elements - - if len(pos) == 0: - return None - - current = tuple(p._elements[pos[-1]] for p in parents[-1]) - pos[-1] += 1 - return current - - while current: - if current[0] is current[1]: - current = next_elem() - elif all(isinstance(elem, Atom) for elem in current): - if not current[0].sameQ(current[1]): - return False - current = next_elem() - elif all(isinstance(elem, Expression) for elem in current): - len_elements = len(current[0]._elements) - if len_elements != len(current[1]._elements): - return False - parents.append(current) - current = tuple((c._head for c in current)) - pos.append(0) - else: # Atom is not the same than an expression - return False - - return True + return expression_sameQ(self, other) def sequences(self): cache = self._cache From 7437d7b9a936eb4432a946c918e4b6d5f8572360 Mon Sep 17 00:00:00 2001 From: mmatera Date: Fri, 30 Aug 2024 22:18:13 -0300 Subject: [PATCH 4/7] workflows for Python 3.12 --- .github/workflows/ubuntu-cython.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- .github/workflows/windows.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ubuntu-cython.yml b/.github/workflows/ubuntu-cython.yml index a2c46f313..a9ae50e15 100644 --- a/.github/workflows/ubuntu-cython.yml +++ b/.github/workflows/ubuntu-cython.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.11'] + python-version: ['3.12' '3.11'] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 3d72f36e7..fdcc89d64 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: ['3.11', '3.8', '3.9', '3.10'] + python-version: ['3.12' '3.11', '3.8', '3.9', '3.10'] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 0b1e451bd..3dee8c92e 100755 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -14,7 +14,7 @@ jobs: os: [windows] # "make doctest" on MS Windows fails without showing much of a # trace of where things went wrong on Python before 3.11. - python-version: ['3.11'] + python-version: ['3.12'] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} From c961184f9e8fcf4d38ea8416d2f3ddcdc9e485e3 Mon Sep 17 00:00:00 2001 From: mmatera Date: Fri, 30 Aug 2024 22:36:02 -0300 Subject: [PATCH 5/7] restoring mathics-title --- mathics/doc/latex/mathics-title.pdf | Bin 0 -> 31692 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 mathics/doc/latex/mathics-title.pdf diff --git a/mathics/doc/latex/mathics-title.pdf b/mathics/doc/latex/mathics-title.pdf new file mode 100644 index 0000000000000000000000000000000000000000..996fb5c97437346f437ee6c4294ffa33d8233536 GIT binary patch literal 31692 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 literal 0 HcmV?d00001 From d3a3f34fad5081623d20c88a6d1a1cbe81acb2c5 Mon Sep 17 00:00:00 2001 From: mmatera Date: Sat, 31 Aug 2024 08:08:46 -0300 Subject: [PATCH 6/7] workflows --- .github/workflows/ubuntu-cython.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ubuntu-cython.yml b/.github/workflows/ubuntu-cython.yml index a9ae50e15..44fd4a03b 100644 --- a/.github/workflows/ubuntu-cython.yml +++ b/.github/workflows/ubuntu-cython.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.12' '3.11'] + python-version: ['3.12', '3.11'] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index fdcc89d64..47aef8afe 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: ['3.12' '3.11', '3.8', '3.9', '3.10'] + python-version: ['3.12', '3.11', '3.8', '3.9', '3.10'] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} From 48ff68615c9624b22ec8fa119cfcd8cbba53ba87 Mon Sep 17 00:00:00 2001 From: "R. Bernstein" Date: Sat, 31 Aug 2024 13:17:28 -0400 Subject: [PATCH 7/7] SameQ[] and Expression module tweaks (#1079) @mmatera looked the code over and do not find anything wrong with it. I made some small changes to the docstring comment and addressed some of the lint errors my editor is telling me about. We can't use generators here since there we access and then "rewind" sometimes. And this _is_ essentially a version without the tail recursions. So I've removed the comments concerning doubt. If you have the example that caused the failure on 3.12 without this change, it would be interesting to benchmark just this test inside a Mathics3 session to see if there's any difference in execution time. Other comments I'll put in the specific code changes in this PR. --------- Co-authored-by: Juan Mauricio Matera --- .github/workflows/ubuntu.yml | 2 +- mathics/core/element.py | 8 +++--- mathics/core/expression.py | 52 +++++++++++++++--------------------- 3 files changed, 27 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 104325b0e..38cbf6fc7 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -8,7 +8,7 @@ on: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: matrix: python-version: ['3.12', '3.11', '3.8', '3.9', '3.10'] diff --git a/mathics/core/element.py b/mathics/core/element.py index 4bac5dd7c..71356b029 100644 --- a/mathics/core/element.py +++ b/mathics/core/element.py @@ -5,7 +5,7 @@ Here we have the base class and related function for element inside an Expression. """ - +from abc import ABC from typing import Any, Optional, Tuple from mathics.core.attributes import A_NO_ATTRIBUTES @@ -193,7 +193,7 @@ def __ne__(self, other) -> bool: ) or self.get_sort_key() != other.get_sort_key() -class BaseElement(KeyComparable): +class BaseElement(KeyComparable, ABC): """ This is the base class from which all other Expressions are derived from. If you think of an Expression as tree-like, then a @@ -290,7 +290,7 @@ def get_float_value(self, permit_complex=False): def get_int_value(self): return None - def get_lookup_name(self): + def get_lookup_name(self) -> str: """ Returns symbol name of leftmost head. This method is used to determine which definition must be asked for rules @@ -398,7 +398,7 @@ def is_free(self, form, evaluation) -> bool: def is_inexact(self) -> bool: return self.get_precision() is not None - def sameQ(self, rhs) -> bool: + def sameQ(self, rhs: "BaseElement") -> bool: """Mathics SameQ""" return id(self) == id(rhs) diff --git a/mathics/core/expression.py b/mathics/core/expression.py index 7fea34d29..e27a6fd53 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -5,7 +5,7 @@ import time from bisect import bisect_left from itertools import chain -from typing import Any, Callable, Iterable, List, Optional, Tuple, Type, Union +from typing import Any, Callable, Iterable, Optional, Tuple, Type, Union import sympy @@ -88,21 +88,17 @@ ) -def expression_sameQ(self, other): +def eval_SameQ(self, other): """ - Iterative implementation of SameQ. + Iterative implementation of SameQ[]. - Run a tree transversal comparison between `self` and `other`. + Tree traversal comparison between `self` and `other`. Return `True` if both tree structures are equal. - This implementation avoids the issue with - the recursion limit in Python 3.12 + This non-recursive implementation reduces the Python stack needed + in evaluation. Staring in Python 3.12 there is a limit on the + recursion level. """ - # TODO: Consider a faster implementation. - # Would be better to use iterators and yield - # instead of this light stack implementation? - # Or maybe using some tail recursive implementation? - # Other ideas in https://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion/ len_elements = len(self.elements) if len(other._elements) != len_elements: @@ -234,14 +230,6 @@ def union(expressions, evaluation) -> Optional["ExpressionCache"]: ): return None - # FIXME: this is workaround the current situtation that some - # Atoms, like String, have a cache even though they don't need - # it, by virtue of this getting set up in - # BaseElement.__init__. Removing the self._cache in there the - # causes Boxing to mess up. Untangle this mess. - if expr._cache is None: - return None - symbols = set.union(*[expr._cache.symbols for expr in expressions]) return ExpressionCache( @@ -250,12 +238,12 @@ def union(expressions, evaluation) -> Optional["ExpressionCache"]: class Expression(BaseElement, NumericOperators, EvalMixin): - """ - A Mathics3 M-Expression. + """A Mathics3 (compound) M-Expression. - A Mathics3 M-Expression is a list where the head is a function designator. - (In the more common S-Expression the head is an a Symbol. In Mathics this can be - an expression that acts as a function. + A Mathics3 M-Expression is a list where the head is a function + designator. (In the more common S-Expression the head is an a + Symbol. In Mathics3, this can be an expression that acts as a + function. positional Arguments: - head -- The head of the M-Expression @@ -266,10 +254,11 @@ class Expression(BaseElement, NumericOperators, EvalMixin): Keyword Arguments: - elements_properties -- properties of the collection of elements + """ _head: BaseElement - _elements: List[BaseElement] + _elements: Tuple[BaseElement] _sequences: Any _cache: Optional[ExpressionCache] elements_properties: Optional[ElementsProperties] @@ -492,7 +481,7 @@ def elements(self, values: Iterable): self.elements_properties = None def equal2(self, rhs: Any) -> Optional[bool]: - """Mathics two-argument Equal (==) + """Mathics3 two-argument Equal (==) returns True if self and rhs are identical. """ if self.sameQ(rhs): @@ -762,6 +751,9 @@ def get_head_name(self): return self._head.name if isinstance(self._head, Symbol) else "" def get_lookup_name(self) -> str: + """ + Returns symbol name of leftmost head. + """ lookup_symbol = self._head while True: if isinstance(lookup_symbol, Symbol): @@ -1140,7 +1132,7 @@ def rewrite_apply_eval_step(self, evaluation) -> Tuple["Expression", bool]: # used later, include: HoldFirst / HoldAll / HoldRest / HoldAllComplete. # Note: self._head can be not just a symbol, but some arbitrary expression. - # This is what makes expressions in Mathics be M-expressions rather than + # This is what makes expressions in Mathics3 be M-expressions rather than # S-expressions. head = self._head.evaluate(evaluation) @@ -1447,14 +1439,14 @@ def round_to_float( return None def sameQ(self, other: BaseElement) -> bool: - """Mathics SameQ""" + """Mathics3 SameQ""" if not isinstance(other, Expression): return False if self is other: return True # All this stuff maybe should be in mathics.eval.expression - return expression_sameQ(self, other) + return eval_SameQ(self, other) def sequences(self): cache = self._cache @@ -1558,7 +1550,7 @@ def to_python(self, *args, **kwargs): # ) return py_obj - # Notice that in this case, `to_python` returns a Mathics Expression object, + # Notice that in this case, `to_python` returns a Mathics3 Expression object, # instead of a builtin native object. return self