From 54a76477ad8d2a3755897d84b254048a6cd24718 Mon Sep 17 00:00:00 2001 From: Li-Xiang-Ideal <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:44:11 +0800 Subject: [PATCH 1/3] Fix ArrayQ for SparseArray --- .../testing_expressions/list_oriented.py | 45 ++++-------- mathics/eval/testing_expressions.py | 69 ++++++++++++++++++- 2 files changed, 79 insertions(+), 35 deletions(-) diff --git a/mathics/builtin/testing_expressions/list_oriented.py b/mathics/builtin/testing_expressions/list_oriented.py index b99fe2040..cd643d618 100644 --- a/mathics/builtin/testing_expressions/list_oriented.py +++ b/mathics/builtin/testing_expressions/list_oriented.py @@ -7,10 +7,10 @@ from mathics.core.evaluation import Evaluation from mathics.core.exceptions import InvalidLevelspecError from mathics.core.expression import Expression -from mathics.core.rules import Pattern from mathics.core.symbols import Atom, SymbolFalse, SymbolTrue -from mathics.core.systemsymbols import SymbolSubsetQ +from mathics.core.systemsymbols import SymbolSparseArray, SymbolSubsetQ from mathics.eval.parts import python_levelspec +from mathics.eval.testing_expressions import check_ArrayQ, check_SparseArrayQ class ArrayQ(Builtin): @@ -39,6 +39,14 @@ class ArrayQ(Builtin): = False >> ArrayQ[{{a, b}, {c, d}}, 2, SymbolQ] = True + >> ArrayQ[SparseArray[{{1, 2} -> a, {2, 1} -> b}]] + = True + >> ArrayQ[SparseArray[{{1, 2} -> a, {2, 1} -> b}], 1] + = False + >> ArrayQ[SparseArray[{{1, 2} -> a, {2, 1} -> b}], 2, SymbolQ] + = False + >> ArrayQ[SparseArray[{{1, 1} -> a, {1, 2} -> b}], 2, SymbolQ] + = True """ rules = { @@ -51,37 +59,10 @@ class ArrayQ(Builtin): def eval(self, expr, pattern, test, evaluation: Evaluation): "ArrayQ[expr_, pattern_, test_]" - pattern = Pattern.create(pattern) - - dims = [len(expr.get_elements())] # to ensure an atom is not an array - - def check(level, expr): - if not expr.has_form("List", None): - test_expr = Expression(test, expr) - if test_expr.evaluate(evaluation) != SymbolTrue: - return False - level_dim = None - else: - level_dim = len(expr.elements) - - if len(dims) > level: - if dims[level] != level_dim: - return False - else: - dims.append(level_dim) - if level_dim is not None: - for element in expr.elements: - if not check(level + 1, element): - return False - return True - - if not check(0, expr): - return SymbolFalse + if not isinstance(expr, Atom) and expr.head.sameQ(SymbolSparseArray): + return check_SparseArrayQ(expr, pattern, test, evaluation) - depth = len(dims) - 1 # None doesn't count - if not pattern.does_match(Integer(depth), evaluation): - return SymbolFalse - return SymbolTrue + return check_ArrayQ(expr, pattern, test, evaluation) class DisjointQ(Test): diff --git a/mathics/eval/testing_expressions.py b/mathics/eval/testing_expressions.py index 4046d0c8c..2bd751944 100644 --- a/mathics/eval/testing_expressions.py +++ b/mathics/eval/testing_expressions.py @@ -2,13 +2,15 @@ import sympy -from mathics.core.atoms import Complex, Integer0, Integer1, IntegerM1 +from mathics.core.atoms import Complex, Integer, Integer0, Integer1, IntegerM1 +from mathics.core.evaluation import Evaluation from mathics.core.expression import Expression -from mathics.core.systemsymbols import SymbolDirectedInfinity +from mathics.core.rules import Pattern +from mathics.core.symbols import SymbolFalse, SymbolTimes, SymbolTrue +from mathics.core.systemsymbols import SymbolDirectedInfinity, SymbolSparseArray def do_cmp(x1, x2) -> Optional[int]: - # don't attempt to compare complex numbers for x in (x1, x2): # TODO: Send message General::nord @@ -99,3 +101,64 @@ def expr_min(elements): def is_number(sympy_value) -> bool: return hasattr(sympy_value, "is_number") or isinstance(sympy_value, sympy.Float) + + +def check_ArrayQ(expr, pattern, test, evaluation: Evaluation): + "Check if expr is an Array which test yields true for each of its elements." + + pattern = Pattern.create(pattern) + + dims = [len(expr.get_elements())] # to ensure an atom is not an array + + def check(level, expr): + if not expr.has_form("List", None): + test_expr = Expression(test, expr) + if test_expr.evaluate(evaluation) != SymbolTrue: + return False + level_dim = None + else: + level_dim = len(expr.elements) + + if len(dims) > level: + if dims[level] != level_dim: + return False + else: + dims.append(level_dim) + if level_dim is not None: + for element in expr.elements: + if not check(level + 1, element): + return False + return True + + if not check(0, expr): + return SymbolFalse + + depth = len(dims) - 1 # None doesn't count + if not pattern.does_match(Integer(depth), evaluation): + return SymbolFalse + + return SymbolTrue + + +def check_SparseArrayQ(expr, pattern, test, evaluation: Evaluation): + "Check if expr is a SparseArray which test yields true for each of its elements." + + if not expr.head.sameQ(SymbolSparseArray): + return SymbolFalse + + pattern = Pattern.create(pattern) + dims, default_value, rules = expr.elements[1:] + if not pattern.does_match(Integer(len(dims.elements)), evaluation): + return SymbolFalse + + array_size = Expression(SymbolTimes, *dims.elements).evaluate(evaluation) + if array_size.value > len(rules.elements): # expr is not full + test_expr = Expression(test, default_value) # test default value + if test_expr.evaluate(evaluation) != SymbolTrue: + return SymbolFalse + for rule in rules.elements: + test_expr = Expression(test, rule.elements[-1]) + if test_expr.evaluate(evaluation) != SymbolTrue: + return SymbolFalse + + return SymbolTrue From a4d395d0a2b5604d007cadb4250e0a594dbf44e8 Mon Sep 17 00:00:00 2001 From: Li-Xiang-Ideal <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Tue, 12 Dec 2023 21:58:28 +0800 Subject: [PATCH 2/3] Undo check_SparseArrayQ --- .../builtin/testing_expressions/list_oriented.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/mathics/builtin/testing_expressions/list_oriented.py b/mathics/builtin/testing_expressions/list_oriented.py index cd643d618..d6e26a48a 100644 --- a/mathics/builtin/testing_expressions/list_oriented.py +++ b/mathics/builtin/testing_expressions/list_oriented.py @@ -8,9 +8,9 @@ from mathics.core.exceptions import InvalidLevelspecError from mathics.core.expression import Expression from mathics.core.symbols import Atom, SymbolFalse, SymbolTrue -from mathics.core.systemsymbols import SymbolSparseArray, SymbolSubsetQ +from mathics.core.systemsymbols import SymbolSubsetQ #, SymbolSparseArray from mathics.eval.parts import python_levelspec -from mathics.eval.testing_expressions import check_ArrayQ, check_SparseArrayQ +from mathics.eval.testing_expressions import check_ArrayQ #, check_SparseArrayQ class ArrayQ(Builtin): @@ -39,14 +39,6 @@ class ArrayQ(Builtin): = False >> ArrayQ[{{a, b}, {c, d}}, 2, SymbolQ] = True - >> ArrayQ[SparseArray[{{1, 2} -> a, {2, 1} -> b}]] - = True - >> ArrayQ[SparseArray[{{1, 2} -> a, {2, 1} -> b}], 1] - = False - >> ArrayQ[SparseArray[{{1, 2} -> a, {2, 1} -> b}], 2, SymbolQ] - = False - >> ArrayQ[SparseArray[{{1, 1} -> a, {1, 2} -> b}], 2, SymbolQ] - = True """ rules = { @@ -59,8 +51,8 @@ class ArrayQ(Builtin): def eval(self, expr, pattern, test, evaluation: Evaluation): "ArrayQ[expr_, pattern_, test_]" - if not isinstance(expr, Atom) and expr.head.sameQ(SymbolSparseArray): - return check_SparseArrayQ(expr, pattern, test, evaluation) + # if not isinstance(expr, Atom) and expr.head.sameQ(SymbolSparseArray): + # return check_SparseArrayQ(expr, pattern, test, evaluation) return check_ArrayQ(expr, pattern, test, evaluation) From 4692c5fb8d84e17addee4e11329d7ada4df3b9d7 Mon Sep 17 00:00:00 2001 From: Li-Xiang-Ideal <54926635+Li-Xiang-Ideal@users.noreply.github.com> Date: Wed, 13 Dec 2023 14:07:36 +0800 Subject: [PATCH 3/3] Fix formatting --- mathics/builtin/testing_expressions/list_oriented.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mathics/builtin/testing_expressions/list_oriented.py b/mathics/builtin/testing_expressions/list_oriented.py index d6e26a48a..b2f819331 100644 --- a/mathics/builtin/testing_expressions/list_oriented.py +++ b/mathics/builtin/testing_expressions/list_oriented.py @@ -8,9 +8,9 @@ from mathics.core.exceptions import InvalidLevelspecError from mathics.core.expression import Expression from mathics.core.symbols import Atom, SymbolFalse, SymbolTrue -from mathics.core.systemsymbols import SymbolSubsetQ #, SymbolSparseArray +from mathics.core.systemsymbols import SymbolSubsetQ # , SymbolSparseArray from mathics.eval.parts import python_levelspec -from mathics.eval.testing_expressions import check_ArrayQ #, check_SparseArrayQ +from mathics.eval.testing_expressions import check_ArrayQ # , check_SparseArrayQ class ArrayQ(Builtin):