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")