diff --git a/CHANGES.rst b/CHANGES.rst
index 1fa8edc47c..89fa72afed 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -4,13 +4,6 @@ CHANGES
2.1.1
-----
-
-New builtins
-++++++++++++
-
-* ``Dispatch``
-
-
Enhancements
++++++++++++
@@ -24,10 +17,16 @@ Enhancements
Bug fixes
+++++++++
-
* ``SetTagDelayed`` does not evaluate now the rhs before assignment.
* ``$InstallationDirectory`` starts out Unprotected
+* Improvings in the implementation of ``D`` and ``Derivative``.
+
+New builtins
+++++++++++++
+* ``Dispatch``
+* ``Series``, ``O`` and ``SeriesData``
+
2.1.0
-----
diff --git a/mathics/builtin/arithmetic.py b/mathics/builtin/arithmetic.py
index e481b487dc..9cc5cec991 100644
--- a/mathics/builtin/arithmetic.py
+++ b/mathics/builtin/arithmetic.py
@@ -33,19 +33,20 @@
Symbol,
SymbolComplexInfinity,
SymbolDirectedInfinity,
- SymbolFalse,
SymbolInfinity,
SymbolN,
SymbolNull,
- SymbolSequence,
SymbolTrue,
+ SymbolFalse,
+ SymbolUndefined,
+ SymbolSequence,
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
@lru_cache(maxsize=1024)
@@ -675,7 +676,9 @@ def apply(self, items, evaluation):
Expression("Plus", item.leaves[1], leaves[-1].leaves[1]),
)
elif (
- leaves and item.has_form("Power", 2) and item.leaves[0].sameQ(leaves[-1])
+ leaves
+ and item.has_form("Power", 2)
+ and item.leaves[0].sameQ(leaves[-1])
):
leaves[-1] = Expression(
"Power", leaves[-1], Expression("Plus", item.leaves[1], Integer(1))
@@ -1135,12 +1138,16 @@ class DirectedInfinity(SympyFunction):
}
def to_sympy(self, expr, **kwargs):
- if len(expr.leaves) == 1:
+ if len(expr._leaves) == 1:
dir = expr.leaves[0].get_int_value()
if dir == 1:
return sympy.oo
elif dir == -1:
return -sympy.oo
+ else:
+ return sympy.Mul((expr._leaves[0].to_sympy()), sympy.zoo)
+ else:
+ return sympy.zoo
class Re(SympyFunction):
@@ -1833,6 +1840,9 @@ class Gamma(_MPMathMultiFunction):
rules = {
"Gamma[z_, x0_, x1_]": "Gamma[z, x0] - Gamma[z, x1]",
"Gamma[1 + z_]": "z!",
+ "Derivative[1][Gamma]": "(Gamma[#1]*PolyGamma[0, #1])&",
+ "Derivative[1, 0][Gamma]": "(Gamma[#1, #2]*Log[#2] + MeijerG[{{}, {1, 1}}, {{0, 0, #1}, {}}, #2])&",
+ "Derivative[0, 1][Gamma]": "(-(#2^(-1 + #1)/E^#2))&",
}
def get_sympy_names(self):
@@ -1862,6 +1872,8 @@ class Pochhammer(SympyFunction):
rules = {
"Pochhammer[a_, n_]": "Gamma[a + n] / Gamma[a]",
+ "Derivative[1,0][Pochhammer]": "(Pochhammer[#1, #2]*(-PolyGamma[0, #1] + PolyGamma[0, #1 + #2]))&",
+ "Derivative[0,1][Pochhammer]": "(Pochhammer[#1, #2]*PolyGamma[0, #1 + #2])&",
}
@@ -2205,3 +2217,131 @@ def apply(self, expr, evaluation):
elif expr == SymbolFalse:
return Integer(0)
return None
+
+
+class Assumptions(Predefined):
+ """
+
+ - '$Assumptions'
+
- is the default setting for the Assumptions option used in such
+ functions as Simplify, Refine, and Integrate.
+
+ """
+
+ name = "$Assumptions"
+ attributes = ("Unprotected",)
+ rules = {
+ "$Assumptions": "True",
+ }
+
+
+class Assuming(Builtin):
+ """
+
+ - 'Assuming[$cond$, $expr$]'
+
- Evaluates $expr$ assuming the conditions $cond$
+
+ >> $Assumptions = { x > 0 }
+ = {x > 0}
+ >> Assuming[y>0, $Assumptions]
+ = {x > 0, y > 0}
+ """
+
+ attributes = ("HoldRest",)
+
+ def apply_assuming(self, cond, expr, evaluation):
+ "Assuming[cond_, expr_]"
+ cond = cond.evaluate(evaluation)
+ if cond.is_true():
+ cond = []
+ elif cond.is_symbol() or not cond.has_form("List", None):
+ cond = [cond]
+ else:
+ cond = cond.leaves
+ assumptions = evaluation.definitions.get_definition(
+ "System`$Assumptions", only_if_exists=True
+ )
+
+ 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(SympyFunction):
+ """
+
+ - 'ConditionalExpression[$expr$, $cond$]'
+
- returns $expr$ if $cond$ evaluates to $True$, $Undefined$ if
+ $cond$ evaluates to $False$.
+
+
+ >> f = ConditionalExpression[x^2, x>0]
+ = ConditionalExpression[x ^ 2, x > 0]
+ >> f /. x -> 2
+ = 4
+ >> f /. x -> -2
+ = Undefined
+ """
+
+ 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)
+ 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)
diff --git a/mathics/builtin/assignment.py b/mathics/builtin/assignment.py
index 110c027225..4b2ccdad71 100644
--- a/mathics/builtin/assignment.py
+++ b/mathics/builtin/assignment.py
@@ -366,19 +366,34 @@ def assign_elementary(self, lhs, rhs, evaluation, tags=None, upset=False):
evaluation.message(lhs_name, "precset", lhs, rhs)
return False
+ # To Handle `OptionValue` in `Condition`
+ rulopc = Rule(
+ Expression(
+ "OptionValue",
+ Expression("Pattern", Symbol("$cond$"), Expression("Blank")),
+ ),
+ Expression("OptionValue", lhs.get_head(), Symbol("$cond$")),
+ )
+
rhs_name = rhs.get_head_name()
while rhs_name == "System`Condition":
if len(rhs.leaves) != 2:
evaluation.message_args("Condition", len(rhs.leaves), 2)
return False
else:
- lhs = Expression("Condition", lhs, rhs.leaves[1])
+ lhs = Expression(
+ "Condition", lhs, rhs.leaves[1].apply_rules([rulopc], evaluation)[0]
+ )
rhs = rhs.leaves[0]
rhs_name = rhs.get_head_name()
# Now, let's add the conditions on the LHS
if condition:
- lhs = Expression("Condition", lhs, condition.leaves[1])
+ lhs = Expression(
+ "Condition",
+ lhs,
+ condition.leaves[1].apply_rules([rulopc], evaluation)[0],
+ )
rule = Rule(lhs, rhs)
count = 0
diff --git a/mathics/builtin/calculus.py b/mathics/builtin/calculus.py
index b340c8fa4d..df8936af20 100644
--- a/mathics/builtin/calculus.py
+++ b/mathics/builtin/calculus.py
@@ -10,6 +10,7 @@
Expression,
Integer,
Number,
+ Rational,
SymbolTrue,
SymbolFalse,
SymbolList,
@@ -25,6 +26,14 @@
import sympy
+SymbolPlus = Symbol("Plus")
+SymbolTimes = Symbol("Times")
+SymbolPower = Symbol("Power")
+IntegerZero = Integer(0)
+IntegerOne = Integer(1)
+IntegerMinusOne = Integer(-1)
+
+
class D(SympyFunction):
"""
@@ -117,23 +126,24 @@ class D(SympyFunction):
}
rules = {
- "D[f_ + g_, x_?NotListQ]": "D[f, x] + D[g, x]",
- "D[f_ * g_, x_?NotListQ]": "D[f, x] * g + f * D[g, x]",
- "D[f_ ^ r_, x_?NotListQ] /; FreeQ[r, x]": "r * f ^ (r-1) * D[f, x]",
- "D[E ^ f_, x_?NotListQ]": "E ^ f * D[f, x]",
- "D[f_ ^ g_, x_?NotListQ]": "D[E ^ (Log[f] * g), x]",
- "D[f_, x_?NotListQ] /; FreeQ[f, x]": "0",
- # 'D[f_[g_], x_?NotListQ]': (
- # 'Module[{t}, D[f[t], t] /. t -> g] * D[g, x]',
- # 'D[f_[g_], x_?NotListQ]': 'D[f[g], g] * D[g, x]',
- "D[f_[left___, x_, right___], x_?NotListQ] /; FreeQ[{left, right}, x]": "Derivative[Sequence @@ UnitVector["
- " Length[{left, x, right}], Length[{left, x}]]][f][left, x, right]",
- # 'D[f_[args___], x_?NotListQ]':
- # 'Plus @@ MapIndexed[(D[f[Sequence@@ReplacePart[{args}, #2->t]], t] '
- # '/. t->#) * D[#, x]&, {args}]',
+ # Basic rules (implemented in apply):
+ # "D[f_ + g_, x_?NotListQ]": "D[f, x] + D[g, x]",
+ # "D[f_ * g_, x_?NotListQ]": "D[f, x] * g + f * D[g, x]",
+ # "D[f_ ^ r_, x_?NotListQ] /; FreeQ[r, x]": "r * f ^ (r-1) * D[f, x]",
+ # "D[E ^ f_, x_?NotListQ]": "E ^ f * D[f, x]",
+ # "D[f_ ^ g_, x_?NotListQ]": "D[E ^ (Log[f] * g), x]",
+ # Hacky: better implement them in apply
+ # "D[f_, x_?NotListQ] /; FreeQ[f, x]": "0",
+ # "D[f_[left___, x_, right___], x_?NotListQ] /; FreeQ[{left, right}, x]":
+ # "Derivative[Sequence @@ UnitVector["
+ # " Length[{left, x, right}], Length[{left, x}]]][f][left, x, right]",
+ # 'D[f_[args___], x_?NotListQ]':
+ # 'Plus @@ MapIndexed[(D[f[Sequence@@ReplacePart[{args}, #2->t]], t] '
+ # '/. t->#) * D[#, x]&, {args}]',
"D[{items___}, x_?NotListQ]": (
"Function[{System`Private`item}, D[System`Private`item, x]]" " /@ {items}"
),
+ # Handling iterated and vectorized derivative variables
"D[f_, {list_List}]": "D[f, #]& /@ list",
"D[f_, {list_List, n_Integer?Positive}]": (
"D[f, Sequence @@ ConstantArray[{list}, n]]"
@@ -147,54 +157,137 @@ class D(SympyFunction):
def apply(self, f, x, evaluation):
"D[f_, x_?NotListQ]"
- if f == x:
- return Integer(1)
- elif not f.is_atom() and len(f.leaves) == 1 and f.leaves[0] == x:
- return Expression(
- Expression(Expression("Derivative", Integer(1)), f.head), x
- )
- elif not f.is_atom() and len(f.leaves) == 1:
- g = f.leaves[0]
- return Expression(
- "Times",
- Expression("D", Expression(f.head, g), g),
- Expression("D", g, x),
- )
- elif not f.is_atom() and len(f.leaves) > 1:
+ x_pattern = Pattern.create(x)
+ if f.is_free(x_pattern, evaluation):
+ return IntegerZero
+ elif f == x:
+ return IntegerOne
+ elif f.is_atom(): # Shouldn't happen
+ 1 / 0
+ return
+ # So, this is not an atom...
+
+ head = f.get_head()
+ if head == SymbolPlus:
+ terms = [
+ Expression("D", term, x)
+ for term in f.leaves
+ if not term.is_free(x_pattern, evaluation)
+ ]
+ if len(terms) == 0:
+ return IntegerZero
+ return Expression(SymbolPlus, *terms)
+ elif head == SymbolTimes:
+ terms = []
+ for i, factor in enumerate(f.leaves):
+ if factor.is_free(x_pattern, evaluation):
+ continue
+ factors = [leaf for j, leaf in enumerate(f.leaves) if j != i]
+ factors.append(Expression("D", factor, x))
+ terms.append(Expression(SymbolTimes, *factors))
+ if len(terms) != 0:
+ return Expression(SymbolPlus, *terms)
+ else:
+ return IntegerZero
+ elif head == SymbolPower and len(f.leaves) == 2:
+ base, exp = f.leaves
+ terms = []
+ if not base.is_free(x_pattern, evaluation):
+ terms.append(
+ Expression(
+ SymbolTimes,
+ exp,
+ Expression(
+ SymbolPower,
+ base,
+ Expression(SymbolPlus, exp, IntegerMinusOne),
+ ),
+ Expression("D", base, x),
+ )
+ )
+ if not exp.is_free(x_pattern, evaluation):
+ if base.is_atom() and base.get_name() == "System`E":
+ terms.append(Expression(SymbolTimes, f, Expression("D", exp, x)))
+ else:
+ terms.append(
+ Expression(
+ SymbolTimes,
+ f,
+ Expression("Log", base),
+ Expression("D", exp, x),
+ )
+ )
+ if len(terms) == 0:
+ return IntegerZero
+ elif len(terms) == 1:
+ return terms[0]
+ else:
+ return Expression(SymbolPlus, *terms)
+ elif len(f.leaves) == 1:
+ if f.leaves[0] == x:
+ return Expression(
+ Expression(Expression("Derivative", Integer(1)), f.head), x
+ )
+ else:
+ g = f.leaves[0]
+ return Expression(
+ SymbolTimes,
+ Expression("D", Expression(f.head, g), g),
+ Expression("D", g, x),
+ )
+ else: # many leaves
+ if len(terms) == 0:
+ return IntegerZero
+ elif len(terms) == 1:
+ return terms[0]
+ else:
+ return Expression(SymbolPlus, *terms)
+ elif len(f.leaves) == 1:
+ if f.leaves[0] == x:
+ return Expression(
+ Expression(Expression("Derivative", Integer(1)), f.head), x
+ )
+ else:
+ g = f.leaves[0]
+ return Expression(
+ SymbolTimes,
+ Expression("D", Expression(f.head, g), g),
+ Expression("D", g, x),
+ )
+ else: # many leaves
def summand(leaf, index):
if leaf.sameQ(x):
result = Expression(
Expression(
- Expression(
- "Derivative",
- *(
- [Integer(0)] * (index)
- + [Integer(1)]
- + [Integer(0)] * (len(f.leaves) - index - 1)
- )
- ),
- f.head,
+ "Derivative",
+ *(
+ [IntegerZero] * (index)
+ + [IntegerOne]
+ + [IntegerZero] * (len(f.leaves) - index - 1)
+ )
),
- *f.leaves
- )
+ f.head,
+ ),
+ *f.leaves
+ )
+ if leaf.same(x):
+ return result
else:
- result = Expression("D", f, leaf)
- return Expression("Times", result, Expression("D", leaf, x))
-
- x_pattern = Pattern.create(x)
- result = Expression(
- "Plus",
- *[
- summand(leaf, index)
- for index, leaf in enumerate(f.leaves)
- if not leaf.is_free(x_pattern, evaluation)
- ]
- )
- if len(result.leaves) == 1:
- return result.leaves[0]
+ return Expression("Times", result, Expression("D", leaf, x))
+
+ result = [
+ summand(leaf, index)
+ for index, leaf in enumerate(f.leaves)
+ if not leaf.is_free(x_pattern, evaluation)
+ ]
+
+ if len(result) == 1:
+ return result[0]
+ elif len(result) == 0:
+ return IntegerZero
else:
- return result
+ return Expression("Plus", *result)
def apply_wrong(self, expr, x, other, evaluation):
"D[expr_, {x_, other___}]"
@@ -273,9 +366,12 @@ class Derivative(PostfixOperator, SympyFunction):
r' RowBox[{"(", Sequence @@ Riffle[{n}, ","], ")"}]]]]'
),
"MakeBoxes[Derivative[n:1|2][f_], form:OutputForm]": """RowBox[{MakeBoxes[f, form], If[n==1, "'", "''"]}]""",
+ # The following rules should be applied in the apply method, instead of relying on the pattern matching
+ # mechanism.
"Derivative[0...][f_]": "f",
"Derivative[n__Integer][Derivative[m__Integer][f_]] /; Length[{m}] "
"== Length[{n}]": "Derivative[Sequence @@ ({n} + {m})][f]",
+ # This would require at least some comments...
"""Derivative[n__Integer][f_Symbol] /; Module[{t=Sequence@@Slot/@Range[Length[{n}]], result, nothing, ft=f[t]},
If[Head[ft] === f
&& FreeQ[Join[UpValues[f], DownValues[f], SubValues[f]], Derivative|D]
@@ -1151,3 +1247,113 @@ def sub(evaluation):
evaluation.message("FindRoot", "maxiter")
return Expression(SymbolList, Expression(SymbolRule, x, x0))
+
+
+class O(Builtin):
+ """
+
+ - 'O[$x$]^n'
+
- Represents a term of order $x^n$.
+
- O[x]^n is generated to represent omitted higher‐order terms in power series.
+
+
+ >> Series[1/(1-x),{x,0,2}]
+ = 1 + x + x ^ 2 + O[x] ^ 3
+
+ """
+
+ pass
+
+
+class Series(Builtin):
+ """
+
+ - 'Series[$f$, {$x$, $x0$, $n$}]'
+
- Represents the series expansion around '$x$=$x0$' up to order $n$.
+
+
+ >> Series[Exp[x],{x,0,2}]
+ = 1 + x + 1 / 2 x ^ 2 + O[x] ^ 3
+ >> Series[Exp[x^2],{x,0,2}]
+ = 1 + x ^ 2 + O[x] ^ 3
+
+ """
+
+ def apply_series(self, f, x, x0, n, evaluation):
+ """Series[f_, {x_Symbol, x0_, n_Integer}]"""
+ # TODO:
+ # - Asymptotic series
+ # - Series of compositions
+ vars = {
+ x.get_name(): x0,
+ }
+
+ data = [f.replace_vars(vars)]
+ df = f
+ for i in range(n.get_int_value()):
+ df = Expression("D", df, x).evaluate(evaluation)
+ newcoeff = df.replace_vars(vars)
+ factorial = Expression("Factorial", Integer(i + 1))
+ newcoeff = Expression(
+ SymbolTimes,
+ Expression(SymbolPower, factorial, IntegerMinusOne),
+ newcoeff,
+ ).evaluate(evaluation)
+ data.append(newcoeff)
+ data = Expression(SymbolList, *data).evaluate(evaluation)
+ return Expression("SeriesData", x, x0, data, IntegerZero, n, IntegerOne)
+
+
+class SeriesData(Builtin):
+ """
+
+ TODO:
+ - Implement sum, product and composition of series
+ """
+
+ def apply_makeboxes(self, x, x0, data, nmin, nmax, den, form, evaluation):
+ """MakeBoxes[SeriesData[x_, x0_, data_List, nmin_Integer, nmax_Integer, den_Integer], form_Symbol]"""
+
+ form = form.get_name()
+ if x0.is_zero:
+ variable = x
+ else:
+ variable = Expression(
+ SymbolPlus, x, Expression(SymbolTimes, IntegerMinusOne, x0)
+ )
+ den = den.get_int_value()
+ nmin = nmin.get_int_value()
+ nmax = nmax.get_int_value() + 1
+ if den != 1:
+ powers = [Rational(i, den) for i in range(nmin, nmax)]
+ powers = powers + [Rational(nmax, den)]
+ else:
+ powers = [Integer(i) for i in range(nmin, nmax)]
+ powers = powers + [Integer(nmax)]
+
+ expansion = []
+ for i, leaf in enumerate(data.leaves):
+ if leaf.is_numeric() and leaf.is_zero:
+ continue
+ if powers[i].is_zero:
+ expansion.append(leaf)
+ continue
+ if powers[i] == IntegerOne:
+ if leaf == IntegerOne:
+ term = variable
+ else:
+ term = Expression(SymbolTimes, leaf, variable)
+ else:
+ if leaf == IntegerOne:
+ term = Expression(SymbolPower, variable, powers[i])
+ else:
+ term = Expression(
+ SymbolTimes, leaf, Expression(SymbolPower, variable, powers[i])
+ )
+ expansion.append(term)
+ expansion = expansion + [
+ Expression(SymbolPower, Expression("O", variable), powers[-1])
+ ]
+ # expansion = [ex.format(form) for ex in expansion]
+ expansion = Expression(SymbolPlus, *expansion)
+ return expansion.format(evaluation, form)
diff --git a/mathics/builtin/specialfunctions.py b/mathics/builtin/specialfunctions.py
index 8a2327f183..52a421245a 100644
--- a/mathics/builtin/specialfunctions.py
+++ b/mathics/builtin/specialfunctions.py
@@ -233,8 +233,8 @@ class BesselJ(_Bessel):
#> BesselJ[2.5, 1]
= 0.0494968
- ## >> D[BesselJ[n, z], z]
- ## = BesselJ[n - 1, z] / 2 - BesselJ[n + 1, z] / 2
+ >> D[BesselJ[n, z], z]
+ = -BesselJ[1 + n, z] / 2 + BesselJ[-1 + n, z] / 2
#> BesselJ[0., 0.]
= 1.
@@ -243,12 +243,14 @@ class BesselJ(_Bessel):
= -Graphics-
"""
- # TODO: Sympy Backend is not as powerful as Mathmeatica
+ # TODO: Sympy Backend is not as powerful as Mathematica
"""
>> BesselJ[1/2, x]
= Sqrt[2 / Pi] Sin[x] / Sqrt[x]
"""
-
+ rules = {
+ "Derivative[0,1][BesselJ]": "(BesselJ[#1- 1, #2] / 2 - BesselJ[#1 + 1, #2] / 2)&",
+ }
attributes = ("Listable", "NumericFunction", "Protected")
sympy_name = "besselj"
@@ -278,6 +280,9 @@ class BesselY(_Bessel):
>> BesselY[0, 0]
= -Infinity
"""
+ rules = {
+ "Derivative[0,1][BesselY]": "(BesselY[-1 + #1, #2] / 2 - BesselY[1 + #1, #2] / 2)&",
+ }
attributes = ("Listable", "NumericFunction", "Protected")
@@ -299,6 +304,10 @@ class BesselI(_Bessel):
= -Graphics-
"""
+ rules = {
+ "Derivative[0, 1][BesselI]": "((BesselI[-1 + #1, #2] + BesselI[1 + #1, #2])/2)&",
+ }
+
attributes = ("Listable", "NumericFunction", "Protected")
sympy_name = "besseli"
@@ -320,6 +329,9 @@ class BesselK(_Bessel):
"""
attributes = ("Listable", "NumericFunction", "Protected")
+ rules = {
+ "Derivative[0, 1][BesselK]": "((-BesselK[-1 + #1, #2] - BesselK[1 + #1, #2])/2)&",
+ }
sympy_name = "besselk"
mpmath_name = "besselk"
@@ -341,6 +353,10 @@ class HankelH1(_Bessel):
= 0.185286 + 0.367112 I
"""
+ rules = {
+ "Derivative[0, 1][HankelH1]": "((HankelH1[-1 + #1, #2] - HankelH1[1 + #1, #2])/2)&",
+ }
+
sympy_name = "hankel1"
mpmath_name = "hankel1"
@@ -356,6 +372,10 @@ class HankelH2(_Bessel):
= 0.185286 - 0.367112 I
"""
+ rules = {
+ "Derivative[0, 1][HankelH2]": "((HankelH2[-1 + #1, #2] - HankelH2[1 + #1, #2])/2)&",
+ }
+
sympy_name = "hankel2"
mpmath_name = "hankel2"
@@ -408,6 +428,10 @@ class AiryAiPrime(_MPMathFunction):
= -0.224911
"""
+ rules = {
+ "Derivative[1][AiryAiPrime]": "(#1 AiryAi[#1])&",
+ }
+
sympy_name = "airyaiprime"
mpmath_name = ""
@@ -438,7 +462,6 @@ class AiryBi(_MPMathFunction):
sympy_name = "airybi"
mpmath_name = "airybi"
-
rules = {
"Derivative[1][AiryBi]": "AiryBiPrime",
}
@@ -463,6 +486,9 @@ class AiryBiPrime(_MPMathFunction):
sympy_name = "airybiprime"
mpmath_name = ""
+ rules = {
+ "Derivative[1][AiryBiPrime]": "(#1 AiryBi[#1])&",
+ }
def get_mpmath_function(self, args):
return lambda x: mpmath.airybi(x, derivative=1)
@@ -495,6 +521,7 @@ class KelvinBer(_Bessel):
rules = {
"KelvinBer[z_]": "KelvinBer[0, z]",
+ "Derivative[1][KelvinBer]": "((2*KelvinBei[1, #1] + 2*KelvinBer[1, #1])/(2*Sqrt[2]))&",
}
sympy_name = ""
@@ -525,6 +552,7 @@ class KelvinBei(_Bessel):
rules = {
"KelvinBei[z_]": "KelvinBei[0, z]",
+ "Derivative[1][KelvinBei]": "((2*KelvinBei[1, #1] - 2*KelvinBer[1, #1])/(2*Sqrt[2]))&",
}
sympy_name = ""
@@ -858,7 +886,11 @@ class LegendreP(_MPMathFunction):
= -3 x Sqrt[1 - x^2]
"""
- rules = {"LegendreP[n_, x_]": "LegendreP[n, 0, x]"}
+ rules = {
+ "LegendreP[n_, x_]": "LegendreP[n, 0, x]",
+ "Derivative[0,1][LegendreP]": "(((-1 - #1)*x*LegendreP[#1, #2] + (1 + #1)*LegendreP[1 + #1, #2])/(-1 + #2^2))&",
+ "Derivative[0,0,1][LegendreP]": "((LegendreP[1 + #1, #2, #3]*(1 + #1 - #2) + LegendreP[#1, #2, #3]*(-1 - #1)*#3)/(-1 + #3^2))&",
+ }
nargs = 3
sympy_name = "legendre"
@@ -896,7 +928,11 @@ class LegendreQ(_MPMathFunction):
= -3 x / 2 - 3 x ^ 2 Log[1 - x] / 4 + 3 x ^ 2 Log[1 + x] / 4 - Log[1 + x] / 4 + Log[1 - x] / 4
"""
- rules = {"LegendreQ[n_, x_]": "LegendreQ[n, 0, x]"}
+ rules = {
+ "LegendreQ[n_, x_]": "LegendreQ[n, 0, x]",
+ "Derivative[0,1][LegendreQ]": "((LegendreQ[1 + #1, #2]*(1 + #1) + LegendreQ[#1, #2]*(-1 - #1)*#2)/(-1 + #2^2))&",
+ "Derivative[0,0,1][LegendreQ]": "((LegendreQ[1 + #1, #2, #3]*(1 + #1 - #2) + LegendreQ[#1, #2, #3]*(-1 - #1)*#3)/(-1 + #3^2))&",
+ }
nargs = 3
sympy_name = ""
@@ -948,6 +984,10 @@ class SphericalHarmonicY(_MPMathFunction):
nargs = 4
sympy_name = "Ynm"
mpmath_name = "spherharm"
+ rules = {
+ "Derivative[0,0,1,0][SphericalHarmonicY]": "(Cot[#3]*#2*SphericalHarmonicY[#1, #2, #3, #4] + (Sqrt[Gamma[1 + #1 - #2]]*Sqrt[Gamma[2 + #1 + #2]]*SphericalHarmonicY[#1, 1 + #2, #3, #4])/(E^(I*#4)*Sqrt[Gamma[#1 - #2]]*Sqrt[Gamma[1 + #1 + #2]]))&",
+ "Derivative[0,0,0,1][SphericalHarmonicY]": "(I*#2*SphericalHarmonicY[#1, #2, #3, #4])&",
+ }
def prepare_mathics(self, sympy_expr):
return sympy_expr.expand(func=True).simplify()
@@ -1137,6 +1177,9 @@ class FresnelS(_MPMathFunction):
= 3 FresnelS[z] Gamma[3 / 4] / (4 Gamma[7 / 4])
"""
+ rules = {
+ "Derivative[1][FresnelS]": "Sin[(Pi*#1^2)/2]&",
+ }
mpmath_name = "fresnels"
@@ -1155,4 +1198,7 @@ class FresnelC(_MPMathFunction):
= FresnelC[z] Gamma[1 / 4] / (4 Gamma[5 / 4])
"""
+ rules = {
+ "Derivative[1][FresnelC]": "Cos[(Pi*#1^2)/2]&",
+ }
mpmath_name = "fresnelc"
diff --git a/mathics/core/expression.py b/mathics/core/expression.py
index 80fbc9b1bf..54e2968c9b 100644
--- a/mathics/core/expression.py
+++ b/mathics/core/expression.py
@@ -2027,7 +2027,6 @@ def __getnewargs__(self):
SymbolComplexInfinity = Symbol("ComplexInfinity")
SymbolDirectedInfinity = Symbol("DirectedInfinity")
SymbolFailed = Symbol("$Failed")
-SymbolFalse = Symbol("False")
SymbolInfinity = Symbol("Infinity")
SymbolList = Symbol("List")
SymbolMakeBoxes = Symbol("MakeBoxes")
@@ -2036,6 +2035,8 @@ def __getnewargs__(self):
SymbolRule = Symbol("Rule")
SymbolSequence = Symbol("Sequence")
SymbolTrue = Symbol("True")
+SymbolFalse = Symbol("False")
+SymbolUndefined = Symbol("Undefined")
SymbolAborted = Symbol("$Aborted")
SymbolInfinity = Symbol("Infinity")
SymbolList = Symbol("List")