diff --git a/CHANGES.rst b/CHANGES.rst
index a4c196c7d5..74d8186e20 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -10,6 +10,7 @@ New builtins
* ``Arg``
* ``Dispatch``
+* ``FullSimplify``
* ``LetterNumber`` #1298. The ``alphabet`` parameter supports only a minimal number of languages.
* ``Nand`` and ``Nor`` logical functions.
* ``Series``, ``O`` and ``SeriesData``
@@ -21,19 +22,23 @@ New builtins
Enhancements
++++++++++++
+* a function `evaluate_predicate` allows for a basic predicate evaluation using `$Assumptions`.
* ``Attributes`` accepts a string parameter.
* ``ColorNegate`` for colors is supported.
* ``D`` and ``Derivative`` improvements.
* ``FileNames`` returns a sorted list (#1250).
* ``FindRoot`` now receives several optional parameters like ``Method`` and ``MaxIterations``.
-* ``FixedPoint`` now supports the ``SameTest`` option.
+* ``FixedPoint`` now supports the ``SameTest`` option.
* ``Prime`` and ``PrimePi`` now accept a list parameter and have the ``NumericFunction`` attribute.
* ``ReplaceRepeated`` and ``FixedPoint`` now supports the ``MaxIteration`` option (#1260).
+* ``Simplify`` performs a more sophisticated set of simplifications.
+* ``Simplify`` accepts a second parameter that temporarily overwrites ``$Assumptions``.
* ``StringTake`` now accepts form containing a list of strings and specification (#1297).
* ``Table`` [*expr*, *n*] is supported.
* ``ToString`` accepts an optional *form* parameter.
* ``ToExpression`` handles multi-line string input
* The implementation of Streams was redone.
+
Bug fixes
diff --git a/mathics/builtin/algebra.py b/mathics/builtin/algebra.py
index 6f8e249a40..eff42843c1 100644
--- a/mathics/builtin/algebra.py
+++ b/mathics/builtin/algebra.py
@@ -3,6 +3,7 @@
Algebraic Manipulation
"""
+
from mathics.version import __version__ # noqa used in loading to check consistency.
from mathics.builtin.base import Builtin
@@ -15,10 +16,13 @@
Number,
Symbol,
SymbolFalse,
+ SymbolList,
SymbolNull,
SymbolTrue,
)
from mathics.core.convert import from_sympy, sympy_symbol_prefix
+from mathics.builtin.scoping import dynamic_scoping
+from mathics.builtin.inference import evaluate_predicate
import sympy
@@ -288,6 +292,8 @@ class Simplify(Builtin):
- 'Simplify[$expr$]'
- simplifies $expr$.
+
- 'Simplify[$expr$, $assump$]'
+
- simplifies $expr$ assuming $assump$ instead of $Assumptions$.
>> Simplify[2*Sin[x]^2 + 2*Cos[x]^2]
@@ -297,6 +303,16 @@ class Simplify(Builtin):
>> Simplify[f[x]]
= f[x]
+ Simplify over conditional expressions uses $Assumptions, or $assump$
+ to evaluate the condition:
+ # TODO: enable this once the logic for conditional expression
+ # be restaured.
+ # >> $Assumptions={a <= 0};
+ # >> Simplify[ConditionalExpression[1, a > 0]]
+ # = Undefined
+ # >> Simplify[ConditionalExpression[1, a > 0], { a > 0 }]
+ # = 1
+
#> Simplify[a*x^2+b*x^2]
= x ^ 2 (a + b)
@@ -308,11 +324,31 @@ class Simplify(Builtin):
rules = {
"Simplify[list_List]": "Simplify /@ list",
"Simplify[rule_Rule]": "Simplify /@ rule",
- "Simplify[eq_Equal]": "Simplify /@ eq",
+ "Simplify[list_List, assum_]": "Simplify[#1, assum]& /@ list",
+ "Simplify[rule_Rule, assum_]": "Simplify[#1, assum]& /@ rule",
+ "Simplify[0^a_, assum_]": "ConditionalExpression[0,Simplify[a>0]]",
+ "Simplify[b_^a_, assum_]": "ConditionalExpression[b,Simplify[{Or[a>0, b!=0]}]]",
}
+ def apply_assuming(self, expr, assumptions, evaluation):
+ "%(name)s[expr_, assumptions_]"
+ assumptions = assumptions.evaluate(evaluation)
+ return dynamic_scoping(
+ lambda ev: self.apply(expr, ev),
+ {"System`$Assumptions": assumptions},
+ evaluation,
+ )
+
def apply(self, expr, evaluation):
- "Simplify[expr_]"
+ "%(name)s[expr_]"
+ # Check first if we are dealing with a logic expression...
+ expr = evaluate_predicate(expr, evaluation)
+ if expr.is_atom():
+ return expr
+ # else, use sympy:
+ leaves = [self.apply(leaf, evaluation) for leaf in expr._leaves]
+ head = self.apply(expr.get_head(), evaluation)
+ expr = Expression(head, *leaves)
sympy_expr = expr.to_sympy()
if sympy_expr is None:
@@ -321,6 +357,31 @@ def apply(self, expr, evaluation):
return from_sympy(sympy_result)
+class FullSimplify(Simplify):
+ """
+
+ - 'FullSimplify[$expr$]'
+
- simplifies $expr$ using an extended set of simplification rules.
+
- 'FullSimplify[$expr$, $assump$]'
+
- simplifies $expr$ assuming $assump$ instead of $Assumptions$.
+
+ TODO: implement the extension. By now, this does the same than Simplify...
+
+ >> FullSimplify[2*Sin[x]^2 + 2*Cos[x]^2]
+ = 2
+
+ """
+
+ rules = {
+ "FullSimplify[list_List]": "FullSimplify /@ list",
+ "FullSimplify[rule_Rule]": "FullSimplify /@ rule",
+ "FullSimplify[eq_Equal]": "FullSimplify /@ eq",
+ "FullSimplify[list_List, assum_]": "FullSimplify[#1, assum]& /@ list",
+ "FullSimplify[rule_Rule, assum_]": "FullSimplify[#1, assum]& /@ rule",
+ "FullSimplify[eq_Equal, assum_]": "FullSimplify[#1, assum]& /@ eq",
+ }
+
+
class Together(Builtin):
"""
@@ -1264,7 +1325,7 @@ def apply(self, expr, form, evaluation):
if expr == Integer0:
return Expression("List")
elif e_null and f_null:
- return Expression("List", Integer0)
+ return Expression(SymbolList, Integer0)
elif e_null and not f_null:
return Expression("List", SymbolNull)
elif f_null:
diff --git a/mathics/builtin/arithmetic.py b/mathics/builtin/arithmetic.py
index 91e2e1d6cb..5f55dc15db 100644
--- a/mathics/builtin/arithmetic.py
+++ b/mathics/builtin/arithmetic.py
@@ -42,13 +42,16 @@
SymbolFalse,
SymbolUndefined,
SymbolSequence,
+ SymbolList,
from_mpmath,
from_python,
)
from mathics.core.numbers import min_prec, dps, SpecialValueError
from mathics.builtin.lists import _IterationFunction
-from mathics.core.convert import from_sympy, SympyExpression
+from mathics.core.convert import from_sympy, SympyExpression, sympy_symbol_prefix
+from mathics.builtin.scoping import dynamic_scoping
+from mathics.builtin.inference import get_assumptions_list, evaluate_predicate
@lru_cache(maxsize=1024)
@@ -741,7 +744,7 @@ def apply(self, items, evaluation):
elif number.sameQ(Integer(-1)) and leaves and leaves[0].has_form("Plus", None):
leaves[0] = Expression(
leaves[0].get_head(),
- *[Expression("Times", Integer(-1), leaf) for leaf in leaves[0].leaves]
+ *[Expression("Times", Integer(-1), leaf) for leaf in leaves[0].leaves],
)
number = None
@@ -1298,12 +1301,26 @@ class Abs(_MPMathFunction):
class Arg(_MPMathFunction):
"""
-
- - 'Arg[$z$]'
-
- returns the argument of a complex value $z$.
-
- >> Arg[-3]
- = Pi
+
+ - 'Arg'[$z$, $method_option$]
+ - returns the argument of a complex value $z$.
+
+
+ - 'Arg'[$z$] is left unevaluated if $z$ is not a numeric quantity.
+
- 'Arg'[$z$] gives the phase angle of $z$ in radians.
+
- The result from 'Arg'[$z$] is always between -Pi and +Pi.
+
- 'Arg'[$z$] has a branch cut discontinuity in the complex $z$ plane running from -Infinity to 0.
+
- 'Arg'[0] is 0.
+
+
+
+ >> Arg[-3]
+ = Pi
+
+ Same as above using sympy's method:
+ >> Arg[-3, Method->"sympy"]
+ = Pi
+
>> Arg[1-I]
= -Pi / 4
@@ -1313,15 +1330,40 @@ class Arg(_MPMathFunction):
= Pi / 4
>> Arg[DirectedInfinity[]]
= 1
+ Arg for 0 is assumed to be 0:
+ >> Arg[0]
+ = 0
"""
- rules = {"Arg[DirectedInfinity[]]": "1",
- "Arg[DirectedInfinity[a_]]": "Arg[a]",
+ rules = {
+ "Arg[0]": "0",
+ "Arg[DirectedInfinity[]]": "1",
+ "Arg[DirectedInfinity[a_]]": "Arg[a]",
}
+
attributes = ("Listable", "NumericFunction")
+ options = {"Method": "Automatic"}
+ numpy_name = "angle" # for later
mpmath_name = "arg"
sympy_name = "arg"
+ def apply(self, z, evaluation, options={}):
+ "%(name)s[z_, OptionsPattern[%(name)s]]"
+ if Expression("PossibleZeroQ", z).evaluate(evaluation) == SymbolTrue:
+ return Integer0
+ preference = self.get_option(options, "Method", evaluation).get_string_value()
+ if preference is None or preference == "Automatic":
+ return super(Arg, self).apply(z, evaluation)
+ elif preference == "mpmath":
+ return _MPMathFunction.apply(self, z, evaluation)
+ elif preference == "sympy":
+ return SympyFunction.apply(self, z)
+ # TODO: add NumpyFunction
+ evaluation.message(
+ "meth", f'Arg Method {preference} not in ("sympy", "mpmath")'
+ )
+ return
+
class Sign(SympyFunction):
"""
@@ -2183,7 +2225,7 @@ class Piecewise(SympyFunction):
## Piecewise({{0, Or[x < 0, x > 0]}}, Indeterminate).
>> Integrate[Piecewise[{{1, x <= 0}, {-1, x > 0}}], x]
- = Piecewise[{{x, x <= 0}, {-x, True}}]
+ = Piecewise[{{x, x <= 0}}, -x]
>> Integrate[Piecewise[{{1, x <= 0}, {-1, x > 0}}], {x, -1, 2}]
= -1
@@ -2205,15 +2247,18 @@ class Piecewise(SympyFunction):
def apply(self, items, evaluation):
"%(name)s[items__]"
- result = self.to_sympy(Expression("Piecewise", *items.get_sequence()))
+ result = self.to_sympy(Expression("Piecewise", *items.get_sequence()),
+ evaluation=evaluation
+ )
if result is None:
return
if not isinstance(result, sympy.Piecewise):
- return from_sympy(result)
+ result = from_sympy(result)
+ return result
def to_sympy(self, expr, **kwargs):
leaves = expr.leaves
-
+ evaluation = kwargs.get("evaluation", None)
if len(leaves) not in (1, 2):
return
@@ -2224,6 +2269,8 @@ def to_sympy(self, expr, **kwargs):
if len(case.leaves) != 2:
return
then, cond = case.leaves
+ if evaluation:
+ cond = evaluate_predicate(cond, evaluation)
sympy_cond = None
if isinstance(cond, Symbol):
@@ -2281,11 +2328,11 @@ def apply(self, expr, evaluation):
class Assumptions(Predefined):
"""
-
- - '$Assumptions'
-
- is the default setting for the Assumptions option used in such
- functions as Simplify, Refine, and Integrate.
-
+
+ - '$Assumptions'
+
- is the default setting for the Assumptions option used in such
+ functions as Simplify, Refine, and Integrate.
+
"""
name = "$Assumptions"
@@ -2299,80 +2346,86 @@ class Assuming(Builtin):
"""
- 'Assuming[$cond$, $expr$]'
-
- Evaluates $expr$ assuming the conditions $cond$
+
- Evaluates $expr$ assuming the conditions $cond$.
>> $Assumptions = { x > 0 }
= {x > 0}
- >> Assuming[y>0, $Assumptions]
- = {x > 0, y > 0}
+ >> Assuming[y>0, ConditionalExpression[y x^2, y>0]//Simplify]
+ = x ^ 2 y
+ >> Assuming[Not[y>0], ConditionalExpression[y x^2, y>0]//Simplify]
+ = Undefined
+ >> ConditionalExpression[y x ^ 2, y > 0]//Simplify
+ = ConditionalExpression[x ^ 2 y, y > 0]
"""
attributes = ("HoldRest",)
- def apply_assuming(self, cond, expr, evaluation):
- "Assuming[cond_, expr_]"
- cond = cond.evaluate(evaluation)
- if cond.is_true():
+ def apply_assuming(self, assumptions, expr, evaluation):
+ "Assuming[assumptions_, expr_]"
+ assumptions = assumptions.evaluate(evaluation)
+ if assumptions.is_true():
cond = []
- elif cond.is_symbol() or not cond.has_form("List", None):
- cond = [cond]
+ elif assumptions.is_symbol() or not assumptions.has_form("List", None):
+ cond = [assumptions]
else:
- cond = cond.leaves
- assumptions = evaluation.definitions.get_definition(
- "System`$Assumptions", only_if_exists=True
+ cond = assumptions._leaves
+ cond = tuple(cond) + get_assumptions_list(evaluation)
+ list_cond = Expression("List", *cond)
+ # TODO: reduce the list of predicates
+ return dynamic_scoping(
+ lambda ev: expr.evaluate(ev), {"System`$Assumptions": list_cond}, evaluation
)
- if assumptions:
- assumptions = assumptions.ownvalues
- if len(assumptions) > 0:
- assumptions = assumptions[0].replace
- else:
- assumptions = None
- if assumptions:
- if assumptions.is_symbol() or not assumptions.has_form("List", None):
- assumptions = [assumptions]
- else:
- assumptions = assumptions.leaves
- cond = assumptions + tuple(cond)
- Expression(
- "Set", Symbol("System`$Assumptions"), Expression("List", *cond)
- ).evaluate(evaluation)
- ret = expr.evaluate(evaluation)
- if assumptions:
- Expression(
- "Set", Symbol("System`$Assumptions"), Expression("List", *assumptions)
- ).evaluate(evaluation)
- else:
- Expression(
- "Set", Symbol("System`$Assumptions"), Expression("List", SymbolTrue)
- ).evaluate(evaluation)
- return ret
-
class ConditionalExpression(Builtin):
"""
- - 'ConditionalExpression[$expr$, $cond$]'
-
- returns $expr$ if $cond$ evaluates to $True$, $Undefined$ if
- $cond$ evaluates to $False$.
+
- 'ConditionalExpression[$expr$, $cond$]'
+
- returns $expr$ if $cond$ evaluates to $True$, $Undefined$ if $cond$ evaluates to $False$.
+ >> ConditionalExpression[x^2, True]
+ = x ^ 2
+
+ >> ConditionalExpression[x^2, False]
+ = Undefined
+
>> f = ConditionalExpression[x^2, x>0]
= ConditionalExpression[x ^ 2, x > 0]
>> f /. x -> 2
= 4
>> f /. x -> -2
= Undefined
+ 'ConditionalExpression' uses assumptions to evaluate the condition:
+ >> $Assumptions = x > 0;
+ >> ConditionalExpression[x ^ 2, x>0]//Simplify
+ = x ^ 2
+ >> $Assumptions = True;
+ # >> ConditionalExpression[ConditionalExpression[s,x>a], xa, x> Integrate[6 x ^ 2 + 3 x ^ 2 - 4 x + 10, x]
- = 10 x - 2 x ^ 2 + 3 x ^ 3
+ = x (10 - 2 x + 3 x ^ 2)
Integrate trigonometric functions:
>> Integrate[Sin[x] ^ 5, x]
@@ -457,7 +459,7 @@ class Integrate(SympyFunction):
Some other integrals:
>> Integrate[1 / (1 - 4 x + x^2), x]
- = -Sqrt[3] Log[-2 + Sqrt[3] + x] / 6 + Sqrt[3] Log[-2 - Sqrt[3] + x] / 6
+ = Sqrt[3] (Log[-2 - Sqrt[3] + x] - Log[-2 + Sqrt[3] + x]) / 6
>> Integrate[4 Sin[x] Cos[x], x]
= 2 Sin[x] ^ 2
@@ -474,6 +476,8 @@ class Integrate(SympyFunction):
= {}
#> Definition[Integrate]
= Attributes[Integrate] = {Protected, ReadProtected}
+ .
+ . Options[Integrate] = {Assumptions -> $Assumptions, GenerateConditions -> Automatic, PrincipalValue -> False}
#> Integrate[Hold[x + x], {x, a, b}]
= Integrate[Hold[x + x], {x, a, b}]
#> Integrate[sin[x], x]
@@ -491,7 +495,7 @@ class Integrate(SympyFunction):
= MachinePrecision
#> Integrate[1/(x^5+1), x]
- = RootSum[625 #1 ^ 4 + 125 #1 ^ 3 + 25 #1 ^ 2 + 5 #1 + 1&, Log[x + 5 #1] #1&] + Log[1 + x] / 5
+ = RootSum[1 + 5 #1 + 25 #1 ^ 2 + 125 #1 ^ 3 + 625 #1 ^ 4&, Log[x + 5 #1] #1&] + Log[1 + x] / 5
#> Integrate[ArcTan(x), x]
= x ^ 2 ArcTan / 2
@@ -509,22 +513,32 @@ class Integrate(SympyFunction):
>> Integrate[f'[x], {x, a, b}]
= f[b] - f[a]
+ >> Integrate[x/Exp[x^2/t], {x, 0, Infinity}]
+ = ConditionalExpression[t / 2, Abs[Arg[t]] < Pi / 2]
+ # This should work after merging the more sophisticated predicate_evaluation routine
+ # be merged...
+ # >> Assuming[Abs[Arg[t]] < Pi / 2, Integrate[x/Exp[x^2/t], {x, 0, Infinity}]]
+ # = t / 2
+ #
"""
# TODO
"""
>> Integrate[Sqrt[Tan[x]], x]
= 1/4 Log[1 + Tan[x] - Sqrt[2] Sqrt[Tan[x]]] Sqrt[2] + 1/2 ArcTan[-1/2 (Sqrt[2] - 2 Sqrt[Tan[x]]) Sqrt[2]] Sqrt[2] + 1/2 ArcTan[1/2 (Sqrt[2] + 2 Sqrt[Tan[x]]) Sqrt[2]] Sqrt[2] - 1/4 Log[1 + Tan[x] + Sqrt[2] Sqrt[Tan[x]]] Sqrt[2]
- #> Integrate[x/Exp[x^2/t], {x, 0, Infinity}]
- = ConditionalExpression[-, Re[t] > 0]
>> Integrate[f'[x], {x, a, b}]
= f[b] - f[a]
"""
-
attributes = ("ReadProtected",)
sympy_name = "Integral"
+ options = {
+ "Assumptions": "$Assumptions",
+ "GenerateConditions": "Automatic",
+ "PrincipalValue": "False",
+ }
+
messages = {
"idiv": "Integral of `1` does not converge on `2`.",
"ilim": "Invalid integration variable or limit(s).",
@@ -559,9 +573,10 @@ def from_sympy(self, sympy_name, leaves):
new_leaves = [leaves[0]] + args
return Expression(self.get_name(), *new_leaves)
- def apply(self, f, xs, evaluation):
- "Integrate[f_, xs__]"
-
+ def apply(self, f, xs, evaluation, options):
+ "Integrate[f_, xs__, OptionsPattern[]]"
+ self.patpow0 = Pattern.create(Expression("Power", Integer0, Expression("Blank")))
+ assuming = options["System`Assumptions"].evaluate(evaluation)
f_sympy = f.to_sympy()
if f_sympy is None or isinstance(f_sympy, SympyExpression):
return
@@ -604,11 +619,51 @@ def apply(self, f, xs, evaluation):
# e.g. NotImplementedError: Result depends on the sign of
# -sign(_Mathics_User_j)*sign(_Mathics_User_w)
return
-
if prec is not None and isinstance(result, sympy.Integral):
# TODO MaxExtaPrecision -> maxn
result = result.evalf(dps(prec))
- result = from_sympy(result)
+ else:
+ result = from_sympy(result)
+ # If the result is defined as a Piecewise expression,
+ # use ConditionalExpression.
+ # This does not work now because the form sympy returns the values
+ if result.get_head_name() == "System`Piecewise":
+ cases = result._leaves[0]._leaves
+ if len(result._leaves) == 1:
+ if cases[-1]._leaves[1].is_true():
+ default = cases[-1]._leaves[0]
+ cases = result._leaves[0]._leaves[:-1]
+ else:
+ default = SymbolUndefined
+ else:
+ cases = result._leaves[0]._leaves
+ default = result._leaves[1]
+ if default.has_form("Integrate", None):
+ if default._leaves[0] == f:
+ default = SymbolUndefined
+
+ simplified_cases = []
+ for case in cases:
+ # TODO: if something like 0^n or 1/expr appears,
+ # put the condition n!=0 or expr!=0 accordingly in the list of
+ # conditions...
+ cond = Expression("Simplify", case._leaves[1], assuming).evaluate(evaluation)
+ resif = Expression("Simplify", case._leaves[0], assuming).evaluate(evaluation)
+ if cond.is_true():
+ return resif
+ if resif.has_form("ConditionalExpression", 2):
+ cond = Expression("And", resif._leaves[1] ,cond)
+ cond = Expression("Simplify", cond, assuming).evaluate(evaluation)
+ resif = resif._leaves[0]
+ simplified_cases.append(Expression(SymbolList, resif, cond))
+ cases = simplified_cases
+ if default == SymbolUndefined and len(cases)==1:
+ cases = cases[0]
+ result = Expression("ConditionalExpression", *(cases._leaves))
+ else:
+ result = Expression(result._head, cases, default)
+ else:
+ result = Expression("Simplify", result, assuming).evaluate(evaluation)
return result
diff --git a/mathics/builtin/datentime.py b/mathics/builtin/datentime.py
index 84800f12f3..823ff02f2d 100644
--- a/mathics/builtin/datentime.py
+++ b/mathics/builtin/datentime.py
@@ -147,7 +147,7 @@ class TimeConstrained(Builtin):
= TimeConstrained[Integrate[Sin[x] ^ 3, x], a]
>> a=1; s
- = -Cos[x] + Cos[x] ^ 3 / 3
+ = Cos[x] (-5 + Cos[2 x]) / 6
Possible issues: for certain time-consuming functions (like simplify)
which are based on sympy or other libraries, it is possible that
diff --git a/mathics/builtin/inference.py b/mathics/builtin/inference.py
new file mode 100644
index 0000000000..5dda95db1c
--- /dev/null
+++ b/mathics/builtin/inference.py
@@ -0,0 +1,57 @@
+from mathics.version import __version__ # noqa used in loading to check consistency.
+from mathics.core.expression import (
+ Expression,
+ Symbol,
+ SymbolTrue,
+ SymbolFalse,
+)
+
+from mathics.core.rules import Rule
+from mathics.core.parser.util import SystemDefinitions
+
+from mathics.core.parser import parse_builtin_rule
+
+# TODO: Extend these rules?
+
+
+
+def get_assumptions_list(evaluation):
+ assumptions = None
+ assumptions_def = evaluation.definitions.get_definition(
+ "System`$Assumptions", only_if_exists=True
+ )
+ if assumptions_def:
+ assumptions = assumptions_def.ownvalues
+ if len(assumptions) > 0:
+ assumptions = assumptions[0].replace
+ if assumptions is None:
+ return None
+
+ if assumptions.is_atom() or not assumptions.has_form("List", None):
+ assumptions = (assumptions,)
+ else:
+ assumptions = assumptions._leaves
+
+ return assumptions
+
+
+
+def evaluate_predicate(pred, evaluation):
+ if pred.has_form(("List", "Sequence"), None):
+ return Expression(pred._head, *[evaluate_predicate(subp, evaluation) for subp in pred._leaves] )
+ assumptions = get_assumptions_list(evaluation)
+
+ assumption_rules = []
+ for assumption in assumptions:
+ true_state = True
+ while assumption.has_form("Not",1):
+ true_state = False
+ assumption = assumption._leaves[0]
+ if true_state:
+ assumption_rules.append(Rule(assumption, SymbolTrue))
+ else:
+ assumption_rules.append(Rule(assumption, SymbolFalse))
+
+ pred, changed = pred.apply_rules(assumption_rules, evaluation)
+ pred = pred.evaluate(evaluation)
+ return pred
diff --git a/mathics/builtin/logic.py b/mathics/builtin/logic.py
index 70be812914..5f3fe5821e 100644
--- a/mathics/builtin/logic.py
+++ b/mathics/builtin/logic.py
@@ -10,6 +10,7 @@
SymbolFalse,
)
+
class Or(BinaryOperator):
"""
@@ -33,6 +34,11 @@ class Or(BinaryOperator):
precedence = 215
attributes = ("Flat", "HoldAll", "OneIdentity")
+ # rules = {
+ # "Or[a_]": "a",
+ # "Or[a_, a_]": "a",
+ # "Or[pred1___, a_, pred2___, a_, pred3___]": "Or[pred1, a, pred2, pred3]",
+ # }
def apply(self, args, evaluation):
"Or[args___]"
@@ -76,6 +82,12 @@ class And(BinaryOperator):
precedence = 215
attributes = ("Flat", "HoldAll", "OneIdentity")
+ # rules = {
+ # "And[a_]": "a",
+ # "And[a_, a_]": "a",
+ # "And[pred1___, a_, pred2___, a_, pred3___]": "And[pred1, a, pred2, pred3]",
+ # }
+
def apply(self, args, evaluation):
"And[args___]"
diff --git a/mathics/builtin/specialfns/erf.py b/mathics/builtin/specialfns/erf.py
index cf6e6dafe1..0b0dbc62f2 100644
--- a/mathics/builtin/specialfns/erf.py
+++ b/mathics/builtin/specialfns/erf.py
@@ -82,7 +82,7 @@ class FresnelC(_MPMathFunction):
## SymPy can't currently simplify this all the way to FresnelC[z].
>> Integrate[Cos[x^2 Pi/2], {x, 0, z}]
- = FresnelC[z] Gamma[1 / 4] / (4 Gamma[5 / 4])
+ = FresnelC[z]
"""
rules = {
@@ -102,7 +102,7 @@ class FresnelS(_MPMathFunction):
## SymPy can't currently simplify this all the way to FresnelS[z].
>> Integrate[Sin[x^2 Pi/2], {x, 0, z}]
- = 3 FresnelS[z] Gamma[3 / 4] / (4 Gamma[7 / 4])
+ = FresnelS[z]
"""
rules = {
diff --git a/mathics/core/expression.py b/mathics/core/expression.py
index 4ccc7e5ddb..a83a1ef268 100644
--- a/mathics/core/expression.py
+++ b/mathics/core/expression.py
@@ -347,6 +347,9 @@ def is_numeric(self) -> bool:
# used by NumericQ and expression ordering
return False
+ def has_form(self, heads, *leaf_counts):
+ return False
+
def flatten(self, head, pattern_only=False, callback=None) -> "BaseExpression":
return self
@@ -559,7 +562,7 @@ def get_option_values(self, evaluation, allow_symbols=False, stop_on_error=True)
def get_rules_list(self):
from mathics.core.rules import Rule
- list_expr = self.flatten(Symbol("List"))
+ list_expr = self.flatten(SymbolList)
list = []
if list_expr.has_form("List", None):
list.extend(list_expr.leaves)
@@ -1514,7 +1517,9 @@ def boxes_to_text(self, **options) -> str:
elif self.has_form("SuperscriptBox", 2):
return "^".join([leaf.boxes_to_text(**options) for leaf in self._leaves])
elif self.has_form("FractionBox", 2):
- return "/".join([" ( " + leaf.boxes_to_text(**options)+ " ) " for leaf in self._leaves])
+ return "/".join(
+ [" ( " + leaf.boxes_to_text(**options) + " ) " for leaf in self._leaves]
+ )
else:
raise BoxError(self, "text")