From c4b6752bec143d0d8604e151d23a996f6ab3233e Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:23:33 +0800 Subject: [PATCH 01/21] Update tensors.py according to Pylint(R1721:unnecessary-comprehension) --- mathics/builtin/tensors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mathics/builtin/tensors.py b/mathics/builtin/tensors.py index f084cdd26..47f7b4b14 100644 --- a/mathics/builtin/tensors.py +++ b/mathics/builtin/tensors.py @@ -526,7 +526,7 @@ def eval(self, d, type, evaluation: Evaluation): if isinstance(d, Integer) and type == SymbolSparseArray: d = d.get_int_value() - perms = list(permutations([i for i in range(1, d + 1)])) + perms = list(permutations(list(range(1, d + 1)))) rules = [ Expression( SymbolRule, From 983049d23b0d23ac6bc4676f20a20fd84572e37f Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Thu, 23 Nov 2023 16:35:25 +0800 Subject: [PATCH 02/21] Update SYMBOLS_MANIFEST.txt --- SYMBOLS_MANIFEST.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/SYMBOLS_MANIFEST.txt b/SYMBOLS_MANIFEST.txt index d3ecf4204..43c6e2162 100644 --- a/SYMBOLS_MANIFEST.txt +++ b/SYMBOLS_MANIFEST.txt @@ -574,6 +574,7 @@ System`LessEqual System`LetterCharacter System`LetterNumber System`LetterQ +System`LeviCivitaTensor System`Level System`LevelQ System`LightBlue From e171e0c95e2de78eb562ea04b46f8dd24d02b58b Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:39:29 +0800 Subject: [PATCH 03/21] Fix Outer for SparseArray --- mathics/builtin/tensors.py | 114 +++++++++++++++++++++++++++++++++---- 1 file changed, 103 insertions(+), 11 deletions(-) diff --git a/mathics/builtin/tensors.py b/mathics/builtin/tensors.py index e60f690ec..181a319bb 100644 --- a/mathics/builtin/tensors.py +++ b/mathics/builtin/tensors.py @@ -18,11 +18,12 @@ of any rank can be handled. """ +import itertools from sympy.combinatorics import Permutation from sympy.utilities.iterables import permutations -from mathics.core.atoms import Integer, String +from mathics.core.atoms import Integer, Integer0, Integer1, String from mathics.core.attributes import A_FLAT, A_ONE_IDENTITY, A_PROTECTED from mathics.core.builtin import BinaryOperator, Builtin from mathics.core.convert.python import from_python @@ -30,7 +31,7 @@ from mathics.core.expression import Expression from mathics.core.list import ListExpression from mathics.core.symbols import Atom, Symbol, SymbolFalse, SymbolTrue -from mathics.core.systemsymbols import SymbolRule, SymbolSparseArray +from mathics.core.systemsymbols import SymbolAutomatic, SymbolRule, SymbolSparseArray from mathics.eval.parts import get_part @@ -299,21 +300,25 @@ class Outer(Builtin): = {{0, 1, 0}, {1, 0, 1}, {0, ComplexInfinity, 0}} """ + rules = { + "Outer[f_, a___, b_SparseArray, c___] /; UnsameQ[f, Times]": "Outer[f, a, b // Normal, c]", + } + summary_text = "generalized outer product" def eval(self, f, lists, evaluation: Evaluation): - "Outer[f_, lists__]" + "Outer[f_, lists__] /; Or[SameQ[f, Times], Not[MemberQ[{lists}, _SparseArray]]]" lists = lists.get_sequence() head = None - for list in lists: - if isinstance(list, Atom): + for _list in lists: + if isinstance(_list, Atom): evaluation.message("Outer", "normal") return if head is None: - head = list.head - elif not list.head.sameQ(head): - evaluation.message("Outer", "heads", head, list.head) + head = _list.head + elif not _list.head.sameQ(head): + evaluation.message("Outer", "heads", head, _list.head) return def rec(item, rest_lists, current): @@ -329,7 +334,73 @@ def rec(item, rest_lists, current): elements.append(rec(element, rest_lists, current)) return Expression(head, *elements) - return rec(lists[0], lists[1:], []) + def rec_sparse(item, rest_lists, current): + evaluation.check_stopped() + if isinstance(item, tuple): # (rules) + elements = [] + for element in item: + rec_temp = rec_sparse(element, rest_lists, current) + if isinstance(rec_temp, tuple): + elements.extend(rec_temp) + else: + elements.append(rec_temp) + return tuple(elements) + else: # rule + _pos, _val = item.elements + if rest_lists: + return rec_sparse( + rest_lists[0], + rest_lists[1:], + (current[0] + _pos.elements, current[1] * _val), + ) + else: + return Expression( + SymbolRule, + ListExpression(*(current[0] + _pos.elements)), + current[1] * _val, + ) + + if head.sameQ(SymbolSparseArray): + dims = [] + val = Integer1 + data = [] # data = [(rules), ...] + for _list in lists: + dims.extend(_list.elements[1]) + val *= _list.elements[2] + if _list.elements[2] == Integer0: # _val==0 + data.append(_list.elements[3].elements) # append (rules) + else: # _val!=0, append (rules, other pos->_val) + other_pos = [] + for pos in itertools.product( + *(range(1, d.value + 1) for d in _list.elements[1]) + ): + other_pos.append( + ListExpression(*(Integer(i) for i in pos)) + ) # generate all pos + rules_pos = set( + rule.elements[0] for rule in _list.elements[3].elements + ) # pos of existing rules + other_pos = ( + set(other_pos) - rules_pos + ) # remove pos of existing rules + other_rules = [] + for pos in other_pos: + other_rules.append( + Expression(SymbolRule, pos, _list.elements[2]) + ) # generate other pos->_val + data.append( + _list.elements[3].elements + tuple(other_rules) + ) # append (rules, other pos->_val) + dims = ListExpression(*dims) + return Expression( + SymbolSparseArray, + SymbolAutomatic, + dims, + val, + ListExpression(*rec_sparse(data[0], data[1:], ((), Integer1))), + ) + else: + return rec(lists[0], lists[1:], []) class RotationTransform(Builtin): @@ -455,7 +526,7 @@ class Transpose(Builtin): :WMA: https://reference.wolfram.com/language/ref/Transpose.html)
-
'Transpose[$m$]' +
'Tranpose[$m$]'
transposes rows and columns in the matrix $m$.
@@ -497,6 +568,27 @@ def eval(self, m, evaluation: Evaluation): return ListExpression(*[ListExpression(*row) for row in result]) +class TensorProduct(Builtin): + """ + :Tensor product:https://en.wikipedia.org/wiki/Tensor_product \ + (:WMA link:https://reference.wolfram.com/language/ref/TensorProduct.html) + +
+
'IdentityMatrix[$n$]' +
gives the identity matrix with $n$ rows and columns. +
+ + >> IdentityMatrix[3] + = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}} + """ + + rules = { + "IdentityMatrix[n_Integer]": "DiagonalMatrix[Table[1, {n}]]", + } + + summary_text = "give the identity matrix with a given dimension" + + class LeviCivitaTensor(Builtin): """ :Levi-Civita tensor:https://en.wikipedia.org/wiki/Levi-Civita_symbol \ @@ -526,7 +618,7 @@ def eval(self, d, type, evaluation: Evaluation): if isinstance(d, Integer) and type == SymbolSparseArray: d = d.get_int_value() - perms = list(permutations(list(range(1, d + 1)))) + perms = list(permutations([i for i in range(1, d + 1)])) rules = [ Expression( SymbolRule, From e7b68a3a050cc1f20e951e7297f36665fe030941 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:43:01 +0800 Subject: [PATCH 04/21] Update tensors.py fix small typo --- mathics/builtin/tensors.py | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/mathics/builtin/tensors.py b/mathics/builtin/tensors.py index 181a319bb..a731908c0 100644 --- a/mathics/builtin/tensors.py +++ b/mathics/builtin/tensors.py @@ -526,7 +526,7 @@ class Transpose(Builtin): :WMA: https://reference.wolfram.com/language/ref/Transpose.html)
-
'Tranpose[$m$]' +
'Transpose[$m$]'
transposes rows and columns in the matrix $m$.
@@ -568,27 +568,6 @@ def eval(self, m, evaluation: Evaluation): return ListExpression(*[ListExpression(*row) for row in result]) -class TensorProduct(Builtin): - """ - :Tensor product:https://en.wikipedia.org/wiki/Tensor_product \ - (:WMA link:https://reference.wolfram.com/language/ref/TensorProduct.html) - -
-
'IdentityMatrix[$n$]' -
gives the identity matrix with $n$ rows and columns. -
- - >> IdentityMatrix[3] - = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}} - """ - - rules = { - "IdentityMatrix[n_Integer]": "DiagonalMatrix[Table[1, {n}]]", - } - - summary_text = "give the identity matrix with a given dimension" - - class LeviCivitaTensor(Builtin): """ :Levi-Civita tensor:https://en.wikipedia.org/wiki/Levi-Civita_symbol \ @@ -618,7 +597,7 @@ def eval(self, d, type, evaluation: Evaluation): if isinstance(d, Integer) and type == SymbolSparseArray: d = d.get_int_value() - perms = list(permutations([i for i in range(1, d + 1)])) + perms = list(permutations(list(range(1, d + 1)))) rules = [ Expression( SymbolRule, From 0789bd6c7ed94e998195bc12362c6810ceaf07e7 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Fri, 24 Nov 2023 21:27:45 +0800 Subject: [PATCH 05/21] Update tensors.py --- mathics/builtin/tensors.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/mathics/builtin/tensors.py b/mathics/builtin/tensors.py index a731908c0..24cb62c87 100644 --- a/mathics/builtin/tensors.py +++ b/mathics/builtin/tensors.py @@ -339,11 +339,7 @@ def rec_sparse(item, rest_lists, current): if isinstance(item, tuple): # (rules) elements = [] for element in item: - rec_temp = rec_sparse(element, rest_lists, current) - if isinstance(rec_temp, tuple): - elements.extend(rec_temp) - else: - elements.append(rec_temp) + elements.extend(rec_sparse(element, rest_lists, current)) return tuple(elements) else: # rule _pos, _val = item.elements @@ -354,12 +350,13 @@ def rec_sparse(item, rest_lists, current): (current[0] + _pos.elements, current[1] * _val), ) else: - return Expression( - SymbolRule, - ListExpression(*(current[0] + _pos.elements)), - current[1] * _val, + return ( + Expression( + SymbolRule, + ListExpression(*(current[0] + _pos.elements)), + current[1] * _val, + ), ) - if head.sameQ(SymbolSparseArray): dims = [] val = Integer1 From 58dbb8b6aabc197857d1c17d6aab4cbf6ad9f984 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Sat, 25 Nov 2023 19:13:48 +0800 Subject: [PATCH 06/21] Update tensors.py --- mathics/builtin/tensors.py | 72 +++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/mathics/builtin/tensors.py b/mathics/builtin/tensors.py index f8a292524..3509920c4 100644 --- a/mathics/builtin/tensors.py +++ b/mathics/builtin/tensors.py @@ -357,48 +357,40 @@ def rec_sparse(item, rest_lists, current): current[1] * _val, ), ) - if head.sameQ(SymbolSparseArray): - dims = [] - val = Integer1 - data = [] # data = [(rules), ...] - for _list in lists: - dims.extend(_list.elements[1]) - val *= _list.elements[2] - if _list.elements[2] == Integer0: # _val==0 - data.append(_list.elements[3].elements) # append (rules) - else: # _val!=0, append (rules, other pos->_val) - other_pos = [] - for pos in itertools.product( - *(range(1, d.value + 1) for d in _list.elements[1]) - ): - other_pos.append( - ListExpression(*(Integer(i) for i in pos)) - ) # generate all pos - rules_pos = set( - rule.elements[0] for rule in _list.elements[3].elements - ) # pos of existing rules - other_pos = ( - set(other_pos) - rules_pos - ) # remove pos of existing rules - other_rules = [] - for pos in other_pos: - other_rules.append( - Expression(SymbolRule, pos, _list.elements[2]) - ) # generate other pos->_val - data.append( - _list.elements[3].elements + tuple(other_rules) - ) # append (rules, other pos->_val) - dims = ListExpression(*dims) - return Expression( - SymbolSparseArray, - SymbolAutomatic, - dims, - val, - ListExpression(*rec_sparse(data[0], data[1:], ((), Integer1))), - ) - else: + + # head != SparseArray + if not head.sameQ(SymbolSparseArray): return rec(lists[0], lists[1:], []) + # head == SparseArray + dims = [] + val = Integer1 + data = [] # data = [(rules), ...] + for _list in lists: + _dims, _val, _rules = _list.elements[1:] + dims.extend(_dims) + val *= _val + if _val == Integer0: # _val==0, append (_rules) + data.append(_rules.elements) + else: # _val!=0, append (_rules, other pos->_val) + other_pos = [] + for pos in itertools.product(*(range(1, d.value + 1) for d in _dims)): + other_pos.append(ListExpression(*(Integer(i) for i in pos))) + rules_pos = set(rule.elements[0] for rule in _rules.elements) + other_pos = set(other_pos) - rules_pos + other_rules = [] + for pos in other_pos: + other_rules.append(Expression(SymbolRule, pos, _val)) + data.append(_list.elements[3].elements + tuple(other_rules)) + dims = ListExpression(*dims) + return Expression( + SymbolSparseArray, + SymbolAutomatic, + dims, + val, + ListExpression(*rec_sparse(data[0], data[1:], ((), Integer1))), + ) + class RotationTransform(Builtin): """ From 9583f389506cddf275035ebe874d1d32d0c6a74d Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Sat, 25 Nov 2023 22:48:15 +0800 Subject: [PATCH 07/21] Update tensors.py --- mathics/builtin/tensors.py | 43 +++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/mathics/builtin/tensors.py b/mathics/builtin/tensors.py index 3509920c4..2b9b9d414 100644 --- a/mathics/builtin/tensors.py +++ b/mathics/builtin/tensors.py @@ -30,8 +30,20 @@ from mathics.core.evaluation import Evaluation from mathics.core.expression import Expression from mathics.core.list import ListExpression -from mathics.core.symbols import Atom, Symbol, SymbolFalse, SymbolTrue -from mathics.core.systemsymbols import SymbolAutomatic, SymbolRule, SymbolSparseArray +from mathics.core.symbols import ( + Atom, + Symbol, + SymbolFalse, + SymbolList, + SymbolTimes, + SymbolTrue, +) +from mathics.core.systemsymbols import ( + SymbolAutomatic, + SymbolNormal, + SymbolRule, + SymbolSparseArray, +) from mathics.eval.parts import get_part @@ -300,26 +312,42 @@ class Outer(Builtin): = {{0, 1, 0}, {1, 0, 1}, {0, ComplexInfinity, 0}} """ - rules = { - "Outer[f_, a___, b_SparseArray, c___] /; UnsameQ[f, Times]": "Outer[f, a, b // Normal, c]", - } - summary_text = "generalized outer product" def eval(self, f, lists, evaluation: Evaluation): - "Outer[f_, lists__] /; Or[SameQ[f, Times], Not[MemberQ[{lists}, _SparseArray]]]" + "Outer[f_, lists__]" + # If f=!=Times, or lists contain both SparseArray and List, then convert all SparseArrays to Lists lists = lists.get_sequence() head = None + sparse_to_list = f != SymbolTimes + contain_sparse = False + comtain_list = False + for _list in lists: + if _list.head.sameQ(SymbolSparseArray): + contain_sparse = True + if _list.head.sameQ(SymbolList): + comtain_list = True + sparse_to_list = sparse_to_list or (contain_sparse and comtain_list) + if sparse_to_list: + break + if sparse_to_list: + new_lists = [] for _list in lists: if isinstance(_list, Atom): evaluation.message("Outer", "normal") return + if sparse_to_list: + if _list.head.sameQ(SymbolSparseArray): + _list = Expression(SymbolNormal, _list).evaluate(evaluation) + new_lists.append(_list) if head is None: head = _list.head elif not _list.head.sameQ(head): evaluation.message("Outer", "heads", head, _list.head) return + if sparse_to_list: + lists = new_lists def rec(item, rest_lists, current): evaluation.check_stopped() @@ -391,7 +419,6 @@ def rec_sparse(item, rest_lists, current): ListExpression(*rec_sparse(data[0], data[1:], ((), Integer1))), ) - class RotationTransform(Builtin): """ :WMA link: https://reference.wolfram.com/language/ref/RotationTransform.html From f79957688056ce38dc650cc3fdee104c144c2d43 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Sat, 25 Nov 2023 23:03:15 +0800 Subject: [PATCH 08/21] Update isort-and-black-checks.yml --- .github/workflows/isort-and-black-checks.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/isort-and-black-checks.yml b/.github/workflows/isort-and-black-checks.yml index 37cde2a21..64cf364b9 100644 --- a/.github/workflows/isort-and-black-checks.yml +++ b/.github/workflows/isort-and-black-checks.yml @@ -4,7 +4,11 @@ # https://github.com/cclauss/autoblack name: isort and black check -on: [pull_request] +on: + push: + branches: [ master ] + pull_request: + branches: '**' jobs: build: runs-on: ubuntu-latest From f09b340b5fd13ec186802174d349141f0a05921c Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Sat, 25 Nov 2023 23:09:35 +0800 Subject: [PATCH 09/21] Update tensors.py --- mathics/builtin/tensors.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mathics/builtin/tensors.py b/mathics/builtin/tensors.py index 2b9b9d414..194bc8d19 100644 --- a/mathics/builtin/tensors.py +++ b/mathics/builtin/tensors.py @@ -419,6 +419,7 @@ def rec_sparse(item, rest_lists, current): ListExpression(*rec_sparse(data[0], data[1:], ((), Integer1))), ) + class RotationTransform(Builtin): """ :WMA link: https://reference.wolfram.com/language/ref/RotationTransform.html From 6dddce8f930c705d670f08056b28170d126af313 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Sat, 25 Nov 2023 23:21:38 +0800 Subject: [PATCH 10/21] Update isort-and-black-checks.yml --- .github/workflows/isort-and-black-checks.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/isort-and-black-checks.yml b/.github/workflows/isort-and-black-checks.yml index 64cf364b9..37cde2a21 100644 --- a/.github/workflows/isort-and-black-checks.yml +++ b/.github/workflows/isort-and-black-checks.yml @@ -4,11 +4,7 @@ # https://github.com/cclauss/autoblack name: isort and black check -on: - push: - branches: [ master ] - pull_request: - branches: '**' +on: [pull_request] jobs: build: runs-on: ubuntu-latest From 12917a151c774c3db969589a61a20a26fdec13df Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Sun, 26 Nov 2023 00:45:12 +0800 Subject: [PATCH 11/21] Update osx.yml --- .github/workflows/osx.yml | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index 2269693ac..159024143 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -2,36 +2,29 @@ name: Mathics3 (OSX) on: push: - branches: [ master ] + branches: [master] pull_request: branches: '**' jobs: build: - env: - LDFLAGS: "-L/usr/local/opt/llvm@11/lib" - CPPFLAGS: "-I/usr/local/opt/llvm@11/include" runs-on: macos-latest strategy: matrix: os: [macOS] python-version: ['3.9', '3.10'] steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install OS dependencies - run: | - brew install llvm tesseract - python -m pip install --upgrade pip - - name: Install Mathics3 with full Python dependencies - run: | - # We can comment out after next Mathics-Scanner release - # python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full] - python -m pip install Mathics-Scanner - make develop-full - - name: Test Mathics3 - run: | - make -j3 check + - uses: actions/checkout@v3 + - name: Install OS dependencies + run: | + brew install llvm@11 tesseract + python -m pip install --upgrade pip + - name: Install Mathics-Scanner + run: | + python -m pip install Mathics-Scanner + - name: Install Mathics3 with full Python dependencies + run: | + make develop-full + - name: Test Mathics3 + run: | + make -j3 check From bbfc74cc60be4bdee411d727dd245edbd3ece57d Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Sun, 26 Nov 2023 00:52:51 +0800 Subject: [PATCH 12/21] Update osx.yml --- .github/workflows/osx.yml | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index 159024143..308bef106 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -2,29 +2,36 @@ name: Mathics3 (OSX) on: push: - branches: [master] + branches: [ master ] pull_request: branches: '**' jobs: build: + env: + LDFLAGS: "-L/usr/local/opt/llvm@11/lib" + CPPFLAGS: "-I/usr/local/opt/llvm@11/include" runs-on: macos-latest strategy: matrix: os: [macOS] python-version: ['3.9', '3.10'] steps: - - uses: actions/checkout@v3 - - name: Install OS dependencies - run: | - brew install llvm@11 tesseract - python -m pip install --upgrade pip - - name: Install Mathics-Scanner - run: | - python -m pip install Mathics-Scanner - - name: Install Mathics3 with full Python dependencies - run: | - make develop-full - - name: Test Mathics3 - run: | - make -j3 check + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install OS dependencies + run: | + brew install llvm@11 tesseract + python -m pip install --upgrade pip + - name: Install Mathics3 with full Python dependencies + run: | + # We can comment out after next Mathics-Scanner release + # python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full] + python -m pip install Mathics-Scanner + make develop-full + - name: Test Mathics3 + run: | + make -j3 check From 9be0ca7d6d1080fdb57a67434f5b10bb9a3072c2 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Sun, 26 Nov 2023 01:04:37 +0800 Subject: [PATCH 13/21] Update osx.yml --- .github/workflows/osx.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index 308bef106..ed800bae4 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -9,8 +9,8 @@ on: jobs: build: env: - LDFLAGS: "-L/usr/local/opt/llvm@11/lib" - CPPFLAGS: "-I/usr/local/opt/llvm@11/include" + LDFLAGS: "-L/usr/local/opt/llvm/lib" + CPPFLAGS: "-I/usr/local/opt/llvm/include" runs-on: macos-latest strategy: matrix: @@ -24,7 +24,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install OS dependencies run: | - brew install llvm@11 tesseract + brew install llvm tesseract python -m pip install --upgrade pip - name: Install Mathics3 with full Python dependencies run: | From bb133b1ccf15a47931db0a3b5607561a64d09a36 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Sun, 26 Nov 2023 01:10:05 +0800 Subject: [PATCH 14/21] Update osx.yml --- .github/workflows/osx.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index ed800bae4..4f0eb189c 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -9,8 +9,8 @@ on: jobs: build: env: - LDFLAGS: "-L/usr/local/opt/llvm/lib" - CPPFLAGS: "-I/usr/local/opt/llvm/include" + LDFLAGS: "-L/usr/local/opt/llvm@17/lib" + CPPFLAGS: "-I/usr/local/opt/llvm@17/include" runs-on: macos-latest strategy: matrix: @@ -24,7 +24,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install OS dependencies run: | - brew install llvm tesseract + brew install llvm@17 tesseract python -m pip install --upgrade pip - name: Install Mathics3 with full Python dependencies run: | From 8a0057f2bbb9865668809324a5921d288b5e2df6 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Sun, 26 Nov 2023 01:54:26 +0800 Subject: [PATCH 15/21] Update osx.yml Not sure what the latest llvm that supports Python 3.9 is. Try one by one :( --- .github/workflows/osx.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index 4f0eb189c..da640f1b2 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -9,8 +9,8 @@ on: jobs: build: env: - LDFLAGS: "-L/usr/local/opt/llvm@17/lib" - CPPFLAGS: "-I/usr/local/opt/llvm@17/include" + LDFLAGS: "-L/usr/local/opt/llvm@14/lib" + CPPFLAGS: "-I/usr/local/opt/llvm@14/include" runs-on: macos-latest strategy: matrix: @@ -24,7 +24,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install OS dependencies run: | - brew install llvm@17 tesseract + brew install llvm@14 tesseract python -m pip install --upgrade pip - name: Install Mathics3 with full Python dependencies run: | From e235c04f94b6f59d5cd75d30a7f0a90f6a14da62 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Sun, 26 Nov 2023 12:38:25 +0800 Subject: [PATCH 16/21] Update tensors.py Add some tests --- mathics/builtin/tensors.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mathics/builtin/tensors.py b/mathics/builtin/tensors.py index 194bc8d19..d658728fe 100644 --- a/mathics/builtin/tensors.py +++ b/mathics/builtin/tensors.py @@ -291,10 +291,21 @@ class Outer(Builtin): Outer product of two matrices: >> Outer[Times, {{a, b}, {c, d}}, {{1, 2}, {3, 4}}] = {{{{a, 2 a}, {3 a, 4 a}}, {{b, 2 b}, {3 b, 4 b}}}, {{{c, 2 c}, {3 c, 4 c}}, {{d, 2 d}, {3 d, 4 d}}}} + + Outer product of two sparse arrays: + >> Outer[Times, SparseArray[{{1, 2} -> a, {2, 1} -> b}], SparseArray[{{1, 2} -> c, {2, 1} -> d}]] + = SparseArray[Automatic, {2, 2, 2, 2}, 0, {{1, 2, 1, 2} -> a c, {1, 2, 2, 1} -> a d, {2, 1, 1, 2} -> b c, {2, 1, 2, 1} -> b d}] 'Outer' of multiple lists: >> Outer[f, {a, b}, {x, y, z}, {1, 2}] = {{{f[a, x, 1], f[a, x, 2]}, {f[a, y, 1], f[a, y, 2]}, {f[a, z, 1], f[a, z, 2]}}, {{f[b, x, 1], f[b, x, 2]}, {f[b, y, 1], f[b, y, 2]}, {f[b, z, 1], f[b, z, 2]}}} + + 'Outer' treats input sparse arrays as lists if f=!=Times, or if the input is a mixture of sparse arrays and lists: + >> Outer[f, SparseArray[{{1, 2} -> a, {2, 1} -> b}], SparseArray[{{1, 2} -> c, {2, 1} -> d}]] + = {{{{f[0, 0], f[0, c]}, {f[0, d], f[0, 0]}}, {{f[a, 0], f[a, c]}, {f[a, d], f[a, 0]}}}, {{{f[b, 0], f[b, c]}, {f[b, d], f[b, 0]}}, {{f[0, 0], f[0, c]}, {f[0, d], f[0, 0]}}}} + + >> Outer[Times, SparseArray[{{1, 2} -> a, {2, 1} -> b}], {c, d}] + = {{{0, 0}, {a c, a d}}, {{b c, b d}, {0, 0}}} Arrays can be ragged: >> Outer[Times, {{1, 2}}, {{a, b}, {c, d, e}}] From 4db5ea0f9783a0e48056d4ed79d6c1fddeb4d33a Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Mon, 27 Nov 2023 19:05:38 +0800 Subject: [PATCH 17/21] fix typo --- mathics/builtin/tensors.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mathics/builtin/tensors.py b/mathics/builtin/tensors.py index d658728fe..8a6921e65 100644 --- a/mathics/builtin/tensors.py +++ b/mathics/builtin/tensors.py @@ -333,13 +333,13 @@ def eval(self, f, lists, evaluation: Evaluation): head = None sparse_to_list = f != SymbolTimes contain_sparse = False - comtain_list = False + contain_list = False for _list in lists: if _list.head.sameQ(SymbolSparseArray): contain_sparse = True if _list.head.sameQ(SymbolList): - comtain_list = True - sparse_to_list = sparse_to_list or (contain_sparse and comtain_list) + contain_list = True + sparse_to_list = sparse_to_list or (contain_sparse and contain_list) if sparse_to_list: break if sparse_to_list: From 792d218529e8ba2e11af957b68382759ce30d853 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:17:12 +0800 Subject: [PATCH 18/21] move eval from ``mathics.builtin.tensors`` to here --- mathics/eval/tensors.py | 244 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 mathics/eval/tensors.py diff --git a/mathics/eval/tensors.py b/mathics/eval/tensors.py new file mode 100644 index 000000000..596c792b0 --- /dev/null +++ b/mathics/eval/tensors.py @@ -0,0 +1,244 @@ +import itertools + +from sympy.combinatorics import Permutation +from sympy.utilities.iterables import permutations + +from mathics.core.atoms import Integer, Integer0, Integer1, String +from mathics.core.convert.python import from_python +from mathics.core.evaluation import Evaluation +from mathics.core.expression import Expression +from mathics.core.list import ListExpression +from mathics.core.symbols import ( + Atom, + Symbol, + SymbolFalse, + SymbolList, + SymbolTimes, + SymbolTrue, +) +from mathics.core.systemsymbols import ( + SymbolAutomatic, + SymbolNormal, + SymbolRule, + SymbolSparseArray, +) +from mathics.eval.parts import get_part + + +def get_default_distance(p): + if all(q.is_numeric() for q in p): + return Symbol("SquaredEuclideanDistance") + elif all(q.get_head_name() == "System`List" for q in p): + dimensions = [get_dimensions(q) for q in p] + if len(dimensions) < 1: + return None + d0 = dimensions[0] + if not all(d == d0 for d in dimensions[1:]): + return None + if len(dimensions[0]) == 1: # vectors? + + def is_boolean(x): + return x.get_head_name() == "System`Symbol" and x in ( + SymbolTrue, + SymbolFalse, + ) + + if all(all(is_boolean(e) for e in q.elements) for q in p): + return Symbol("JaccardDissimilarity") + return Symbol("SquaredEuclideanDistance") + elif all(isinstance(q, String) for q in p): + return Symbol("EditDistance") + else: + from mathics.builtin.colors.color_directives import expression_to_color + + if all(expression_to_color(q) is not None for q in p): + return Symbol("ColorDistance") + + return None + + +def get_dimensions(expr, head=None): + if isinstance(expr, Atom): + return [] + else: + if head is not None and not expr.head.sameQ(head): + return [] + sub_dim = None + sub = [] + for element in expr.elements: + sub = get_dimensions(element, expr.head) + if sub_dim is None: + sub_dim = sub + else: + if sub_dim != sub: + sub = [] + break + return [len(expr.elements)] + sub + + +def eval_Inner(f, list1, list2, g, evaluation: Evaluation): + "Evaluates recursively the inner product of list1 and list2" + + m = get_dimensions(list1) + n = get_dimensions(list2) + + if not m or not n: + evaluation.message("Inner", "normal") + return + if list1.get_head() != list2.get_head(): + evaluation.message("Inner", "heads", list1.get_head(), list2.get_head()) + return + if m[-1] != n[0]: + evaluation.message("Inner", "incom", m[-1], len(m), list1, n[0], list2) + return + + head = list1.get_head() + inner_dim = n[0] + + def rec(i_cur, j_cur, i_rest, j_rest): + evaluation.check_stopped() + if i_rest: + elements = [] + for i in range(1, i_rest[0] + 1): + elements.append(rec(i_cur + [i], j_cur, i_rest[1:], j_rest)) + return Expression(head, *elements) + elif j_rest: + elements = [] + for j in range(1, j_rest[0] + 1): + elements.append(rec(i_cur, j_cur + [j], i_rest, j_rest[1:])) + return Expression(head, *elements) + else: + + def summand(i): + part1 = get_part(list1, i_cur + [i]) + part2 = get_part(list2, [i] + j_cur) + return Expression(f, part1, part2) + + part = Expression(g, *[summand(i) for i in range(1, inner_dim + 1)]) + # cur_expr.elements.append(part) + return part + + return rec([], [], m[:-1], n[1:]) + + +def eval_Outer(f, lists, evaluation: Evaluation): + "Evaluates recursively the outer product of lists" + + # If f=!=Times, or lists contain both SparseArray and List, then convert all SparseArrays to Lists + lists = lists.get_sequence() + head = None + sparse_to_list = f != SymbolTimes + contain_sparse = False + contain_list = False + for _list in lists: + if _list.head.sameQ(SymbolSparseArray): + contain_sparse = True + if _list.head.sameQ(SymbolList): + contain_list = True + sparse_to_list = sparse_to_list or (contain_sparse and contain_list) + if sparse_to_list: + break + if sparse_to_list: + new_lists = [] + for _list in lists: + if isinstance(_list, Atom): + evaluation.message("Outer", "normal") + return + if sparse_to_list: + if _list.head.sameQ(SymbolSparseArray): + _list = Expression(SymbolNormal, _list).evaluate(evaluation) + new_lists.append(_list) + if head is None: + head = _list.head + elif not _list.head.sameQ(head): + evaluation.message("Outer", "heads", head, _list.head) + return + if sparse_to_list: + lists = new_lists + + def rec(item, rest_lists, current): + evaluation.check_stopped() + if isinstance(item, Atom) or not item.head.sameQ(head): + if rest_lists: + return rec(rest_lists[0], rest_lists[1:], current + [item]) + else: + return Expression(f, *(current + [item])) + else: + elements = [] + for element in item.elements: + elements.append(rec(element, rest_lists, current)) + return Expression(head, *elements) + + def rec_sparse(item, rest_lists, current): + evaluation.check_stopped() + if isinstance(item, tuple): # (rules) + elements = [] + for element in item: + elements.extend(rec_sparse(element, rest_lists, current)) + return tuple(elements) + else: # rule + _pos, _val = item.elements + if rest_lists: + return rec_sparse( + rest_lists[0], + rest_lists[1:], + (current[0] + _pos.elements, current[1] * _val), + ) + else: + return ( + Expression( + SymbolRule, + ListExpression(*(current[0] + _pos.elements)), + current[1] * _val, + ), + ) + + # head != SparseArray + if not head.sameQ(SymbolSparseArray): + return rec(lists[0], lists[1:], []) + + # head == SparseArray + dims = [] + val = Integer1 + data = [] # data = [(rules), ...] + for _list in lists: + _dims, _val, _rules = _list.elements[1:] + dims.extend(_dims) + val *= _val + if _val == Integer0: # _val==0, append (_rules) + data.append(_rules.elements) + else: # _val!=0, append (_rules, other pos->_val) + other_pos = [] + for pos in itertools.product(*(range(1, d.value + 1) for d in _dims)): + other_pos.append(ListExpression(*(Integer(i) for i in pos))) + rules_pos = set(rule.elements[0] for rule in _rules.elements) + other_pos = set(other_pos) - rules_pos + other_rules = [] + for pos in other_pos: + other_rules.append(Expression(SymbolRule, pos, _val)) + data.append(_rules.elements + tuple(other_rules)) + dims = ListExpression(*dims) + return Expression( + SymbolSparseArray, + SymbolAutomatic, + dims, + val, + ListExpression(*rec_sparse(data[0], data[1:], ((), Integer1))), + ) + + +def eval_LeviCivitaTensor(d, type): + "Evaluates Levi-Civita tensor of rank d" + + if isinstance(d, Integer) and type == SymbolSparseArray: + d = d.get_int_value() + perms = list(permutations(list(range(1, d + 1)))) + rules = [ + Expression( + SymbolRule, + from_python(p), + from_python(Permutation.from_sequence(p).signature()), + ) + for p in perms + ] + return Expression(SymbolSparseArray, from_python(rules), from_python([d] * d)) From 312eaff7595e41be8f7f97ad4926f73da57068f4 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:19:42 +0800 Subject: [PATCH 19/21] Update clusters.py --- mathics/builtin/distance/clusters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mathics/builtin/distance/clusters.py b/mathics/builtin/distance/clusters.py index ebd904540..afd0b36f8 100644 --- a/mathics/builtin/distance/clusters.py +++ b/mathics/builtin/distance/clusters.py @@ -139,7 +139,7 @@ def _cluster(self, p, k, mode, evaluation, options, expr): options, "DistanceFunction", evaluation ) if distance_function_string == "Automatic": - from mathics.builtin.tensors import get_default_distance + from mathics.eval.tensors import get_default_distance distance_function = get_default_distance(dist_p) if distance_function is None: @@ -462,7 +462,7 @@ def eval( options, "DistanceFunction", evaluation ) if distance_function_string == "Automatic": - from mathics.builtin.tensors import get_default_distance + from mathics.eval.tensors import get_default_distance distance_function = get_default_distance(dist_p) if distance_function is None: From 088cbed9c3ee72ad14b34beb0b3e49ed8c947903 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:28:56 +0800 Subject: [PATCH 20/21] Update tensors.py move almost all ``eval()`` to ``mathics.eval.tensors`` --- mathics/builtin/tensors.py | 238 ++----------------------------------- 1 file changed, 10 insertions(+), 228 deletions(-) diff --git a/mathics/builtin/tensors.py b/mathics/builtin/tensors.py index 8a6921e65..bad1a83f4 100644 --- a/mathics/builtin/tensors.py +++ b/mathics/builtin/tensors.py @@ -18,84 +18,18 @@ of any rank can be handled. """ -import itertools -from sympy.combinatorics import Permutation -from sympy.utilities.iterables import permutations - -from mathics.core.atoms import Integer, Integer0, Integer1, String +from mathics.core.atoms import Integer from mathics.core.attributes import A_FLAT, A_ONE_IDENTITY, A_PROTECTED from mathics.core.builtin import BinaryOperator, Builtin -from mathics.core.convert.python import from_python from mathics.core.evaluation import Evaluation -from mathics.core.expression import Expression from mathics.core.list import ListExpression -from mathics.core.symbols import ( - Atom, - Symbol, - SymbolFalse, - SymbolList, - SymbolTimes, - SymbolTrue, -) -from mathics.core.systemsymbols import ( - SymbolAutomatic, - SymbolNormal, - SymbolRule, - SymbolSparseArray, +from mathics.eval.tensors import ( + eval_Inner, + eval_LeviCivitaTensor, + eval_Outer, + get_dimensions, ) -from mathics.eval.parts import get_part - - -def get_default_distance(p): - if all(q.is_numeric() for q in p): - return Symbol("SquaredEuclideanDistance") - elif all(q.get_head_name() == "System`List" for q in p): - dimensions = [get_dimensions(q) for q in p] - if len(dimensions) < 1: - return None - d0 = dimensions[0] - if not all(d == d0 for d in dimensions[1:]): - return None - if len(dimensions[0]) == 1: # vectors? - - def is_boolean(x): - return x.get_head_name() == "System`Symbol" and x in ( - SymbolTrue, - SymbolFalse, - ) - - if all(all(is_boolean(e) for e in q.elements) for q in p): - return Symbol("JaccardDissimilarity") - return Symbol("SquaredEuclideanDistance") - elif all(isinstance(q, String) for q in p): - return Symbol("EditDistance") - else: - from mathics.builtin.colors.color_directives import expression_to_color - - if all(expression_to_color(q) is not None for q in p): - return Symbol("ColorDistance") - - return None - - -def get_dimensions(expr, head=None): - if isinstance(expr, Atom): - return [] - else: - if head is not None and not expr.head.sameQ(head): - return [] - sub_dim = None - sub = [] - for element in expr.elements: - sub = get_dimensions(element, expr.head) - if sub_dim is None: - sub_dim = sub - else: - if sub_dim != sub: - sub = [] - break - return [len(expr.elements)] + sub class ArrayDepth(Builtin): @@ -233,46 +167,7 @@ class Inner(Builtin): def eval(self, f, list1, list2, g, evaluation: Evaluation): "Inner[f_, list1_, list2_, g_]" - m = get_dimensions(list1) - n = get_dimensions(list2) - - if not m or not n: - evaluation.message("Inner", "normal") - return - if list1.get_head() != list2.get_head(): - evaluation.message("Inner", "heads", list1.get_head(), list2.get_head()) - return - if m[-1] != n[0]: - evaluation.message("Inner", "incom", m[-1], len(m), list1, n[0], list2) - return - - head = list1.get_head() - inner_dim = n[0] - - def rec(i_cur, j_cur, i_rest, j_rest): - evaluation.check_stopped() - if i_rest: - elements = [] - for i in range(1, i_rest[0] + 1): - elements.append(rec(i_cur + [i], j_cur, i_rest[1:], j_rest)) - return Expression(head, *elements) - elif j_rest: - elements = [] - for j in range(1, j_rest[0] + 1): - elements.append(rec(i_cur, j_cur + [j], i_rest, j_rest[1:])) - return Expression(head, *elements) - else: - - def summand(i): - part1 = get_part(list1, i_cur + [i]) - part2 = get_part(list2, [i] + j_cur) - return Expression(f, part1, part2) - - part = Expression(g, *[summand(i) for i in range(1, inner_dim + 1)]) - # cur_expr.elements.append(part) - return part - - return rec([], [], m[:-1], n[1:]) + return eval_Inner(f, list1, list2, g, evaluation) class Outer(Builtin): @@ -300,7 +195,7 @@ class Outer(Builtin): >> Outer[f, {a, b}, {x, y, z}, {1, 2}] = {{{f[a, x, 1], f[a, x, 2]}, {f[a, y, 1], f[a, y, 2]}, {f[a, z, 1], f[a, z, 2]}}, {{f[b, x, 1], f[b, x, 2]}, {f[b, y, 1], f[b, y, 2]}, {f[b, z, 1], f[b, z, 2]}}} - 'Outer' treats input sparse arrays as lists if f=!=Times, or if the input is a mixture of sparse arrays and lists: + 'Outer' converts input sparse arrays to lists if f=!=Times, or if the input is a mixture of sparse arrays and lists: >> Outer[f, SparseArray[{{1, 2} -> a, {2, 1} -> b}], SparseArray[{{1, 2} -> c, {2, 1} -> d}]] = {{{{f[0, 0], f[0, c]}, {f[0, d], f[0, 0]}}, {{f[a, 0], f[a, c]}, {f[a, d], f[a, 0]}}}, {{{f[b, 0], f[b, c]}, {f[b, d], f[b, 0]}}, {{f[0, 0], f[0, c]}, {f[0, d], f[0, 0]}}}} @@ -328,107 +223,7 @@ class Outer(Builtin): def eval(self, f, lists, evaluation: Evaluation): "Outer[f_, lists__]" - # If f=!=Times, or lists contain both SparseArray and List, then convert all SparseArrays to Lists - lists = lists.get_sequence() - head = None - sparse_to_list = f != SymbolTimes - contain_sparse = False - contain_list = False - for _list in lists: - if _list.head.sameQ(SymbolSparseArray): - contain_sparse = True - if _list.head.sameQ(SymbolList): - contain_list = True - sparse_to_list = sparse_to_list or (contain_sparse and contain_list) - if sparse_to_list: - break - if sparse_to_list: - new_lists = [] - for _list in lists: - if isinstance(_list, Atom): - evaluation.message("Outer", "normal") - return - if sparse_to_list: - if _list.head.sameQ(SymbolSparseArray): - _list = Expression(SymbolNormal, _list).evaluate(evaluation) - new_lists.append(_list) - if head is None: - head = _list.head - elif not _list.head.sameQ(head): - evaluation.message("Outer", "heads", head, _list.head) - return - if sparse_to_list: - lists = new_lists - - def rec(item, rest_lists, current): - evaluation.check_stopped() - if isinstance(item, Atom) or not item.head.sameQ(head): - if rest_lists: - return rec(rest_lists[0], rest_lists[1:], current + [item]) - else: - return Expression(f, *(current + [item])) - else: - elements = [] - for element in item.elements: - elements.append(rec(element, rest_lists, current)) - return Expression(head, *elements) - - def rec_sparse(item, rest_lists, current): - evaluation.check_stopped() - if isinstance(item, tuple): # (rules) - elements = [] - for element in item: - elements.extend(rec_sparse(element, rest_lists, current)) - return tuple(elements) - else: # rule - _pos, _val = item.elements - if rest_lists: - return rec_sparse( - rest_lists[0], - rest_lists[1:], - (current[0] + _pos.elements, current[1] * _val), - ) - else: - return ( - Expression( - SymbolRule, - ListExpression(*(current[0] + _pos.elements)), - current[1] * _val, - ), - ) - - # head != SparseArray - if not head.sameQ(SymbolSparseArray): - return rec(lists[0], lists[1:], []) - - # head == SparseArray - dims = [] - val = Integer1 - data = [] # data = [(rules), ...] - for _list in lists: - _dims, _val, _rules = _list.elements[1:] - dims.extend(_dims) - val *= _val - if _val == Integer0: # _val==0, append (_rules) - data.append(_rules.elements) - else: # _val!=0, append (_rules, other pos->_val) - other_pos = [] - for pos in itertools.product(*(range(1, d.value + 1) for d in _dims)): - other_pos.append(ListExpression(*(Integer(i) for i in pos))) - rules_pos = set(rule.elements[0] for rule in _rules.elements) - other_pos = set(other_pos) - rules_pos - other_rules = [] - for pos in other_pos: - other_rules.append(Expression(SymbolRule, pos, _val)) - data.append(_list.elements[3].elements + tuple(other_rules)) - dims = ListExpression(*dims) - return Expression( - SymbolSparseArray, - SymbolAutomatic, - dims, - val, - ListExpression(*rec_sparse(data[0], data[1:], ((), Integer1))), - ) + return eval_Outer(f, lists, evaluation) class RotationTransform(Builtin): @@ -647,17 +442,4 @@ class LeviCivitaTensor(Builtin): def eval(self, d, type, evaluation: Evaluation): "LeviCivitaTensor[d_Integer, type_]" - if isinstance(d, Integer) and type == SymbolSparseArray: - d = d.get_int_value() - perms = list(permutations(list(range(1, d + 1)))) - rules = [ - Expression( - SymbolRule, - from_python(p), - from_python(Permutation.from_sequence(p).signature()), - ) - for p in perms - ] - return Expression( - SymbolSparseArray, from_python(rules), from_python([d] * d) - ) + return eval_LeviCivitaTensor(d, type) From daf7d6dd8f6e69d9e0a20f759f9e97cd7a167730 Mon Sep 17 00:00:00 2001 From: Li Xiang <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:55:56 +0800 Subject: [PATCH 21/21] Update clusters.py --- mathics/builtin/distance/clusters.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mathics/builtin/distance/clusters.py b/mathics/builtin/distance/clusters.py index afd0b36f8..382fce982 100644 --- a/mathics/builtin/distance/clusters.py +++ b/mathics/builtin/distance/clusters.py @@ -35,6 +35,7 @@ ) from mathics.eval.nevaluator import eval_N from mathics.eval.parts import walk_levels +from mathics.eval.tensors import get_default_distance class _LazyDistances(LazyDistances): @@ -139,8 +140,6 @@ def _cluster(self, p, k, mode, evaluation, options, expr): options, "DistanceFunction", evaluation ) if distance_function_string == "Automatic": - from mathics.eval.tensors import get_default_distance - distance_function = get_default_distance(dist_p) if distance_function is None: name_of_builtin = strip_context(self.get_name()) @@ -462,8 +461,6 @@ def eval( options, "DistanceFunction", evaluation ) if distance_function_string == "Automatic": - from mathics.eval.tensors import get_default_distance - distance_function = get_default_distance(dist_p) if distance_function is None: evaluation.message(