Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ Enhancements
* ``ToString`` accepts an optional *form* parameter.
* ``ToExpression`` handles multi-line string input
* The implementation of Streams was redone

* a function `evaluate_predicate` allows for a basic predicate evaluation using `$Assumptions`.

Bug fixes
+++++++++

Expand Down
126 changes: 79 additions & 47 deletions mathics/builtin/arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,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.logic import get_assumptions_list, evaluate_predicate


@lru_cache(maxsize=1024)
Expand Down Expand Up @@ -1307,6 +1310,7 @@ class Arg(_MPMathFunction):
rules = {"Arg[DirectedInfinity[]]": "1",
"Arg[DirectedInfinity[a_]]": "Arg[a]",
}

sympy_name = "arg"
mpmath_name = "arg"

Expand Down Expand Up @@ -2189,15 +2193,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

Expand All @@ -2208,7 +2215,9 @@ 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):
if cond == SymbolTrue:
Expand Down Expand Up @@ -2283,84 +2292,107 @@ class Assuming(Builtin):
"""
<dl>
<dt>'Assuming[$cond$, $expr$]'
<dd>Evaluates $expr$ assuming the conditions $cond$
<dd>Evaluates $expr$ assuming the conditions $cond$.
</dl>
>> $Assumptions = { x > 0 }
= {x > 0}
>> Assuming[y>0, $Assumptions]
= {x > 0, y > 0}
>> Assuming[y>0, ConditionalExpression[y x^2, y>0]]
= x ^ 2 y
>> Assuming[Not[y>0], ConditionalExpression[y x^2, y>0]]
= Undefined
>> ConditionalExpression[y x ^ 2, y > 0]
= 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, asumptions, expr, evaluation):
"Assuming[asumptions_, expr_]"
asumptions = asumptions.evaluate(evaluation)
if asumptions.is_true():
cond = []
elif cond.is_symbol() or not cond.has_form("List", None):
cond = [cond]
elif asumptions.is_symbol() or not asumptions.has_form("List", None):
cond = [asumptions]
else:
cond = cond.leaves
assumptions = evaluation.definitions.get_definition(
"System`$Assumptions", only_if_exists=True
cond = asumptions._leaves
cond = 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):
"""
<dl>
<dt>'ConditionalExpression[$expr$, $cond$]'
<dd>returns $expr$ if $cond$ evaluates to $True$, $Undefined$ if
$cond$ evaluates to $False$.
<dt>'ConditionalExpression[$expr$, $cond$]'
<dd>returns $expr$ if $cond$ evaluates to $True$, $Undefined$ if $cond$ evaluates to $False$.
</dl>

>> 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]
= x ^ 2
>> $Assumptions = True;
"""

sympy_name = "Piecewise"

rules = {
"ConditionalExpression[expr_, True]": "expr",
"ConditionalExpression[expr_, False]": "Undefined",
}

def apply_generic(self, expr, cond, evaluation):
"ConditionalExpression[expr_, cond_]"
cond = cond.evaluate(evaluation)
# What we need here is a way to evaluate
# cond as a predicate, using assumptions.
# Let's delegate this to the And (and Or) symbols...
if not cond.is_atom() and cond._head == SymbolList:
cond = Expression("System`And", *(cond._leaves))
else:
cond = Expression("System`And", cond)
cond = evaluate_predicate(cond, evaluation)
if cond is None:
return
if cond.is_true():
return expr
if cond == SymbolFalse:
return SymbolUndefined
return

def to_sympy(self, expr, **kwargs):
leaves = expr.leaves
if len(leaves) != 2:
return
expr, cond = leaves

sympy_cond = None
if isinstance(cond, Symbol):
if cond == SymbolTrue:
sympy_cond = True
elif cond == SymbolFalse:
sympy_cond = False
if sympy_cond is None:
sympy_cond = cond.to_sympy(**kwargs)
if not (sympy_cond.is_Relational or sympy_cond.is_Boolean):
return

sympy_cases = (
(expr.to_sympy(**kwargs), sympy_cond),
(sympy.Symbol(sympy_symbol_prefix + "System`Undefined"), True),
)
return sympy.Piecewise(*sympy_cases)
21 changes: 16 additions & 5 deletions mathics/builtin/calculus.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,14 +509,17 @@ class Integrate(SympyFunction):

>> Integrate[f'[x], {x, a, b}]
= f[b] - f[a]
>> Integrate[x/Exp[x^2/t], {x, 0, Infinity}]
= Piecewise[{{t / 2, Abs[Arg[t]] < Pi / 2}, {Integrate[x E ^ (-x ^ 2 / t), {x, 0, Infinity}], True}}]
>> 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]
"""
Expand Down Expand Up @@ -561,7 +564,7 @@ def from_sympy(self, sympy_name, leaves):

def apply(self, f, xs, evaluation):
"Integrate[f_, xs__]"

uneval_expr = Expression("Integrate", f, xs.evaluate(evaluation))
f_sympy = f.to_sympy()
if f_sympy is None or isinstance(f_sympy, SympyExpression):
return
Expand Down Expand Up @@ -604,11 +607,19 @@ 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(cases) == 2 and len(result._leaves) == 1:
if (cases[-1]._leaves[1] == SymbolTrue and cases[-1]._leaves[0] == uneval_expr):
result = Expression("ConditionalExpression", *(cases[0]._leaves))
return result


Expand Down
Loading