From c81bd71560c7c1ebc245320dc50c982fd4eb1378 Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 31 Aug 2024 09:43:13 -0400 Subject: [PATCH 1/3] Tweaks to iterative SameQ.. Docstring comment tweak on expression_sameQ, and uncertainty comments of implementation. Some linting was done on expression module. Mathics -> Mathics3 more places. --- mathics/core/element.py | 8 +++---- mathics/core/expression.py | 46 ++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 31 deletions(-) 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..ce87182f5 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 @@ -92,17 +92,13 @@ def expression_sameQ(self, other): """ Iterative implementation of SameQ. - Run a tree transversal comparison between `self` and `other`. + Ttree 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,7 +1439,7 @@ 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: @@ -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 From 646519523c54ce77d7f4dcd14148ba9cd80bb4a9 Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 31 Aug 2024 09:53:39 -0400 Subject: [PATCH 2/3] Rename expression_sameQ to eval_SameQ .. and a few more teaks --- mathics/core/expression.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mathics/core/expression.py b/mathics/core/expression.py index ce87182f5..e27a6fd53 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -88,11 +88,11 @@ ) -def expression_sameQ(self, other): +def eval_SameQ(self, other): """ - Iterative implementation of SameQ. + Iterative implementation of SameQ[]. - Ttree traversal comparison between `self` and `other`. + Tree traversal comparison between `self` and `other`. Return `True` if both tree structures are equal. This non-recursive implementation reduces the Python stack needed @@ -1446,7 +1446,7 @@ def sameQ(self, other: BaseElement) -> bool: 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 From 9b796efb5f8975603ab83d04010c969b8dcfd0d9 Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 31 Aug 2024 10:11:47 -0400 Subject: [PATCH 3/3] Fix CI syntax problem --- .github/workflows/ubuntu-cython.yml | 2 +- .github/workflows/ubuntu.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ubuntu-cython.yml b/.github/workflows/ubuntu-cython.yml index 87d57f237..3a5c28514 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 f8f74af56..38cbf6fc7 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -8,10 +8,10 @@ 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'] + python-version: ['3.12', '3.11', '3.8', '3.9', '3.10'] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }}