Skip to content

Commit eb831c2

Browse files
authored
Simplify & inference fns moved under mathics.eval (#1140)
1 parent 69eda63 commit eb831c2

5 files changed

Lines changed: 100 additions & 76 deletions

File tree

mathics/builtin/arithmetic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
import sympy
1212

13-
from mathics.builtin.inference import get_assumptions_list
1413
from mathics.builtin.numeric import Abs
1514
from mathics.builtin.scoping import dynamic_scoping
1615
from mathics.core.atoms import (
@@ -72,6 +71,7 @@
7271
SymbolUndefined,
7372
)
7473
from mathics.eval.arithmetic import eval_Sign
74+
from mathics.eval.inference import get_assumptions_list
7575
from mathics.eval.nevaluator import eval_N
7676

7777
# This tells documentation how to sort this module

mathics/builtin/numbers/algebra.py

Lines changed: 10 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import sympy
1919

20-
from mathics.builtin.inference import evaluate_predicate
2120
from mathics.builtin.options import options_to_rules
2221
from mathics.builtin.scoping import dynamic_scoping
2322
from mathics.core.atoms import Integer, Integer0, Integer1, Number, RationalOneHalf
@@ -63,7 +62,10 @@
6362
SymbolTable,
6463
SymbolTanh,
6564
)
66-
from mathics.eval.numbers.algebra.simplify import default_complexity_function
65+
from mathics.eval.numbers.algebra.simplify import (
66+
default_complexity_function,
67+
eval_Simplify,
68+
)
6769
from mathics.eval.numbers.numbers import cancel, sympy_factor
6870
from mathics.eval.parts import walk_parts
6971
from mathics.eval.patterns import match
@@ -1496,7 +1498,10 @@ def eval_list(self, expr, vars, evaluation):
14961498
# FullSimplify
14971499
class Simplify(Builtin):
14981500
r"""
1499-
<url>:WMA link:
1501+
<url>:SymPy:
1502+
https://docs.sympy.org/latest/modules/simplify
1503+
/simplify.html</url>, <url>
1504+
:WMA:
15001505
https://reference.wolfram.com/language/ref/Simplify.html</url>
15011506
15021507
<dl>
@@ -1605,65 +1610,9 @@ def eval(self, expr, evaluation, options={}):
16051610
{"System`$Assumptions": assumptions},
16061611
evaluation,
16071612
)
1608-
return self.do_apply(expr, evaluation, options)
1609-
1610-
def do_apply(self, expr, evaluation, options={}):
1611-
# Check first if we are dealing with a logic expression...
1612-
if expr in (SymbolTrue, SymbolFalse, SymbolList):
1613-
return expr
1614-
1615-
# ``evaluate_predicate`` tries to reduce expr taking into account
1616-
# the assumptions established in ``$Assumptions``.
1617-
expr = evaluate_predicate(expr, evaluation)
1618-
1619-
# If we get an atom, return it.
1620-
if isinstance(expr, Atom):
1621-
return expr
1622-
1623-
# Now, try to simplify the elements.
1624-
# TODO: Consider to move this step inside ``evaluate_predicate``.
1625-
# Notice that here we want to pass through the full evaluation process
1626-
# to use all the defined rules...
1627-
name = self.get_name()
1628-
symbol_name = Symbol(name)
1629-
elements = [
1630-
Expression(symbol_name, element).evaluate(evaluation)
1631-
for element in expr._elements
1632-
]
1633-
head = Expression(symbol_name, expr.get_head()).evaluate(evaluation)
1634-
expr = Expression(head, *elements)
16351613

1636-
# At this point, we used all the tools available in Mathics.
1637-
# If the expression has a sympy form, try to use it.
1638-
# Now, convert the expression to sympy
1639-
sympy_expr = expr.to_sympy()
1640-
# If the expression cannot be handled by Sympy, just return it.
1641-
if sympy_expr is None:
1642-
return expr
1643-
# Now, try to simplify using sympy
1644-
complexity_function = options.get("System`ComplexityFunction", None)
1645-
if complexity_function is None or complexity_function is SymbolAutomatic:
1646-
1647-
def _default_complexity_function(x):
1648-
return default_complexity_function(from_sympy(x))
1649-
1650-
complexity_function = _default_complexity_function
1651-
else:
1652-
if isinstance(complexity_function, (Expression, Symbol)):
1653-
_complexity_function = complexity_function
1654-
complexity_function = (
1655-
lambda x: Expression(_complexity_function, from_sympy(x))
1656-
.evaluate(evaluation)
1657-
.to_python()
1658-
)
1659-
1660-
# At this point, ``complexity_function`` is a function that takes a
1661-
# sympy expression and returns an integer.
1662-
sympy_result = sympy.simplify(sympy_expr, measure=complexity_function)
1663-
1664-
# and bring it back
1665-
result = from_sympy(sympy_result).evaluate(evaluation)
1666-
return result
1614+
symbol_name = Symbol(self.get_name())
1615+
return eval_Simplify(symbol_name, expr, evaluation, options)
16671616

16681617

16691618
class FullSimplify(Simplify):

mathics/builtin/numeric.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
import sympy
1717

18-
from mathics.builtin.inference import evaluate_predicate
1918
from mathics.core.atoms import (
2019
Complex,
2120
Integer,
@@ -52,6 +51,7 @@
5251
eval_RealSign,
5352
eval_Sign,
5453
)
54+
from mathics.eval.inference import evaluate_predicate
5555
from mathics.eval.nevaluator import eval_NValues
5656

5757

@@ -319,7 +319,10 @@ def eval_N(self, expr, evaluation: Evaluation):
319319

320320
class Piecewise(SympyFunction):
321321
"""
322-
<url>:WMA link:https://reference.wolfram.com/language/ref/Piecewise.html</url>
322+
<url>:SymPy:
323+
https://docs.sympy.org/latest/modules/functions
324+
/elementary.html#piecewise</url>, <url>
325+
:WMA:https://reference.wolfram.com/language/ref/Piecewise.html</url>
323326
324327
<dl>
325328
<dt>'Piecewise[{{expr1, cond1}, ...}]'
Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
Inference Functions
44
"""
55

6-
no_doc = "no doc"
7-
6+
from mathics.core.evaluation import Evaluation
87
from mathics.core.expression import Expression
98
from mathics.core.parser import parse_builtin_rule
109
from mathics.core.parser.util import SystemDefinitions
@@ -14,11 +13,21 @@
1413

1514
# TODO: Extend these rules?
1615

16+
no_doc = "no doc"
17+
18+
19+
def debug_logical_expr(pref, expr, evaluation: Evaluation):
20+
print(
21+
pref, expr
22+
) # expr.format(evaluation,"OutputForm").boxes_to_text(evaluation=evaluation))
23+
24+
25+
def null_debug_logical_expr(pref, expr, evaluation: Evaluation):
26+
return
27+
1728

18-
def debug_logical_expr(pref, expr, evaluation):
19-
pass
20-
# return
21-
# print(pref , expr) #expr.format(evaluation,"OutputForm").boxes_to_text(evaluation=evaluation))
29+
# Tracing can redefine this to provide trace information
30+
DEBUG_LOGICAL_EXPR = null_debug_logical_expr
2231

2332

2433
logical_algebraic_rules_spec = {
@@ -105,7 +114,7 @@ def remove_nots_when_unnecesary(pred, evaluation):
105114
cc = True
106115
while cc:
107116
pred, cc = pred.do_apply_rules(remove_not_rules, evaluation)
108-
debug_logical_expr("-> ", pred, evaluation)
117+
DEBUG_LOGICAL_EXPR("-> ", pred, evaluation)
109118
if pred is SymbolTrue or pred is SymbolFalse:
110119
return pred
111120
return pred
@@ -362,14 +371,14 @@ def evaluate_predicate(pred, evaluation):
362371
*[evaluate_predicate(subp, evaluation) for subp in pred.elements],
363372
)
364373

365-
debug_logical_expr("reducing ", pred, evaluation)
374+
DEBUG_LOGICAL_EXPR("reducing ", pred, evaluation)
366375
ensure_logical_algebraic_rules()
367376
pred = pred.evaluate(evaluation)
368-
debug_logical_expr("-> ", pred, evaluation)
377+
DEBUG_LOGICAL_EXPR("-> ", pred, evaluation)
369378
cc = True
370379
while cc:
371380
pred, cc = pred.do_apply_rules(logical_algebraic_rules, evaluation)
372-
debug_logical_expr("-> ", pred, evaluation)
381+
DEBUG_LOGICAL_EXPR("-> ", pred, evaluation)
373382
if pred is SymbolTrue or pred is SymbolFalse:
374383
return pred
375384

@@ -378,11 +387,11 @@ def evaluate_predicate(pred, evaluation):
378387
return remove_nots_when_unnecesary(pred, evaluation).evaluate(evaluation)
379388

380389
if assumption_rules is not None:
381-
debug_logical_expr(" Now, using the assumptions over ", pred, evaluation)
390+
DEBUG_LOGICAL_EXPR(" Now, using the assumptions over ", pred, evaluation)
382391
changed = True
383392
while changed:
384393
pred, changed = pred.do_apply_rules(assumption_rules, evaluation)
385-
debug_logical_expr(" -> ", pred, evaluation)
394+
DEBUG_LOGICAL_EXPR(" -> ", pred, evaluation)
386395

387396
pred = remove_nots_when_unnecesary(pred, evaluation).evaluate(evaluation)
388397
return pred

mathics/eval/numbers/algebra/simplify.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@
44
Algorithms for simplifying expressions and evaluate complexity.
55
"""
66

7+
from sympy import simplify
8+
79
from mathics.core.atoms import Number
10+
from mathics.core.convert.sympy import from_sympy
811
from mathics.core.expression import Expression
12+
from mathics.core.symbols import Atom, Symbol, SymbolFalse, SymbolList, SymbolTrue
13+
from mathics.core.systemsymbols import SymbolAutomatic
14+
from mathics.eval.inference import evaluate_predicate
915

1016

1117
def default_complexity_function(expr: Expression) -> int:
@@ -24,3 +30,60 @@ def default_complexity_function(expr: Expression) -> int:
2430
)
2531
else:
2632
return 1
33+
34+
35+
def eval_Simplify(symbol_name: Symbol, expr, evaluation, options: dict):
36+
# Check first if we are dealing with a logic expression...
37+
if expr in (SymbolTrue, SymbolFalse, SymbolList):
38+
return expr
39+
40+
# ``evaluate_predicate`` tries to reduce expr taking into account
41+
# the assumptions established in ``$Assumptions``.
42+
expr = evaluate_predicate(expr, evaluation)
43+
44+
# If we get an atom, return it.
45+
if isinstance(expr, Atom):
46+
return expr
47+
48+
# Now, try to simplify the elements.
49+
# TODO: Consider to move this step inside ``evaluate_predicate``.
50+
# Notice that here we want to pass through the full evaluation process
51+
# to use all the defined rules...
52+
elements = [
53+
Expression(symbol_name, element).evaluate(evaluation)
54+
for element in expr._elements
55+
]
56+
head = Expression(symbol_name, expr.get_head()).evaluate(evaluation)
57+
expr = Expression(head, *elements)
58+
59+
# At this point, we used all the tools available in Mathics.
60+
# If the expression has a sympy form, try to use it.
61+
# Now, convert the expression to sympy
62+
sympy_expr = expr.to_sympy()
63+
# If the expression cannot be handled by Sympy, just return it.
64+
if sympy_expr is None:
65+
return expr
66+
# Now, try to simplify using sympy
67+
complexity_function = options.get("System`ComplexityFunction", None)
68+
if complexity_function is None or complexity_function is SymbolAutomatic:
69+
70+
def _default_complexity_function(x):
71+
return default_complexity_function(from_sympy(x))
72+
73+
complexity_function = _default_complexity_function
74+
else:
75+
if isinstance(complexity_function, (Expression, Symbol)):
76+
_complexity_function = complexity_function
77+
complexity_function = (
78+
lambda x: Expression(_complexity_function, from_sympy(x))
79+
.evaluate(evaluation)
80+
.to_python()
81+
)
82+
83+
# At this point, ``complexity_function`` is a function that takes a
84+
# sympy expression and returns an integer.
85+
sympy_result = simplify(sympy_expr, measure=complexity_function)
86+
87+
# and bring it back
88+
result = from_sympy(sympy_result).evaluate(evaluation)
89+
return result

0 commit comments

Comments
 (0)