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(