def rules_derivative(obj, v): if isinstance(obj.arg, Indexed): if isinstance(obj.wrt, Indexed): if obj.arg.base == obj.wrt.base: if v: print('kroneckerDelta', obj.arg.indices, obj.wrt.indices) return Mul( *[ KroneckerDelta(ind0, ind1) for ind0, ind1 in zip(obj.arg.indices, obj.wrt.indices)] ) if isinstance(obj.arg, AppliedIndexedFunction): if isinstance(obj.wrt, AppliedIndexedFunction): if obj.arg.base == obj.wrt.base: if v: print('kroneckerDelta+dirac', obj.arg.indices, obj.wrt.indices) return Mul( *[ KroneckerDelta(ind0, ind1) for ind0, ind1 in zip(obj.arg.indices, obj.wrt.indices)], *[ DiracDelta(arg0 - arg1) for arg0, arg1 in zip(obj.arg.args, obj.wrt.args)] ) # if isinstance(obj.arg, Neg): # if v: print('neg deriv') # return Neg( Derivative(obj.arg.arg, obj.wrt) ) # if isinstance(obj.arg, Summation): # if v: print('sum deriv') # return Summation( Derivative(obj.arg.arg, obj.wrt), *obj.arg.lims ) # if isinstance(obj.arg, Add): # if v: print('add deriv') # return Add( *[Derivative(arg, obj.wrt) for arg in obj.arg.args] ) if isinstance(obj.arg, Mul): if v: print('mul deriv') return Add( *[ Mul(*[ jarg if i!=j else Derivative(jarg, obj.wrt) for j, jarg in enumerate(obj.arg.args) ] ) for i, iarg in enumerate(obj.arg.args) ] ) if isinstance(obj.arg, Pow): if v: print('pow deriv') return obj.arg.power * obj.arg.base**(obj.arg.power-1) * Derivative(obj.arg.base, obj.wrt) if isinstance(obj.arg, AppliedFunction): if v: print('func deriv') if isinstance(obj.wrt, AppliedFunction): if v: print('func deriv') if obj.arg.operator == obj.wrt.operator: if v: print('diracDelta deriv') return Mul( *[ DiracDelta( arg0 - arg1 ) for arg0, arg1 in zip(obj.arg.args, obj.wrt.args) ]) return Add(*[ PartialDerivative(obj.arg.operator, i)(*obj.arg.args) * Derivative(arg, obj.wrt) for i, arg in enumerate(obj.arg.args) ]) if v: print('nothing deriv') return obj
class Derivative(Expr): @staticmethod def add_order(): return Mul.add_order()-1 @staticmethod def mul_order(): return AppliedFunction.mul_order()+1 rules = RuleList( Rule( 'd(-...) -> -d(...)', lambda obj: isinstance(obj.arg, Neg), lambda obj, v: Neg( Derivative(obj.arg.arg, obj.wrt) ) ), Rule( 'd(sum(...)) -> sum(d(...))', lambda obj: isinstance(obj.arg, Summation), lambda obj, v: Summation( Derivative(obj.arg.arg, obj.wrt), *obj.arg.lims ) ), Rule( 'd(add(...)) -> add(d(...))', lambda obj: isinstance(obj.arg, Add), lambda obj, v: Add( *[Derivative(arg, obj.wrt) for arg in obj.arg.args] ) ), Rule( 'derivative rule', lambda obj: True, lambda obj, v: rules_derivative(obj, v) ) ) def __new__(cls, *args, **kwargs): arg, wrt, = args if isinstance(wrt, Number): raise Exception('trying to derivate with respect to a number') if isinstance(arg, Number): return Number(0) if arg == wrt: return Number(1) obj = super().__new__(cls, *args) if not hasattr(obj, 'wrt'): obj.arg = arg obj.wrt = wrt return obj def __init__(self, *args, **kwargs): pass def __str__(self): wrt = self.wrt if not isinstance(wrt, Symbol): wrt = delim(wrt) if isinstance(self.arg, Expr): return fr'\frac{{ {{\rm d}} }}{{ {{\rm d}}{self.wrt} }}{delim(self.arg)}' return fr'\frac{{ {{\rm d}}{self.arg} }}{{ {{\rm d}}{self.wrt} }}'
def rules_summation(obj, v): if isinstance(obj.arg, Add): if v: print('add summation') inside_args = [ arg for arg in obj.arg.args if hasindex(arg, obj.lims[0]) ] outside_args = [arg for arg in obj.arg.args if not arg in inside_args] if len(inside_args) == 0: inside_args = [Number(1)] return Add( *[arg * Summation(Number(1), *obj.lims) for arg in outside_args], *[Summation(arg, *obj.lims) for arg in inside_args]) if isinstance(obj.arg, Neg): if v: print('neg summation') return Neg(Summation(obj.arg.arg, *obj.lims)) if isinstance(obj.arg, Mul): if v: print('mul summation') inside_args = [ arg for arg in obj.arg.args if hasindex(arg, obj.lims[0]) ] outside_args = [arg for arg in obj.arg.args if not arg in inside_args] if len(inside_args) == 0: inside_args = [Number(1)] if KroneckerDelta in list(map(lambda x: x.__class__, inside_args)): new_ind = [ ind for delta in inside_args if isinstance(delta, KroneckerDelta) for ind in delta.indices if not ind == obj.lims[0] ][0] return Mul( *outside_args, *[ update_indices(arg, obj.lims[0], new_ind, v) for arg in inside_args if not isinstance(arg, KroneckerDelta) ]) return Mul(*outside_args, Summation(Mul(*[arg for arg in inside_args]), *obj.lims)) if not hasindex(obj.arg, obj.lims[0]): if v: print('no index summation') return obj.arg * (obj.lims[2] - obj.lims[1]) if v: print('nothing summation') return obj
def rules_mul(obj, verbose=False): args = flatten_args(obj) terms = Counter() numeric = 1 for arg in args: if isinstance(arg, Add): return Add(*[ Mul(add_arg, *[ other for other in args if not other == arg ]) for add_arg in arg.args ]) if isinstance(arg, Neg): iarg = arg.args[0] numeric *= -1 if isinstance(iarg, Number): numeric *= iarg.value else: terms.update({iarg:1}) elif isinstance(arg, Number): numeric *= arg.value elif isinstance(arg, Pow): if isinstance(arg.power, Neg): if isinstance(arg.power.arg, Number): terms.update({arg.base: -arg.power.arg.value}) elif isinstance(arg.power, Number): terms.update({arg.base: arg.power.value}) else: terms.update({arg.base: arg.power}) else: terms.update({arg:1}) new_args = [ s**v if v != 1 else s for s, v in terms.items() if v != 0] if numeric == 0: return Zero if len(new_args) == 0: return Number(numeric) if len(args) == 1 and numeric == 1: return new_args[0] if len(args) == 1 and numeric == -1: return Neg(new_args[0]) if numeric == 1: return Mul( *new_args ) if numeric == -1: return Neg(Mul( *new_args )) ret = Mul( Number(numeric), *new_args ) return ret
def mul_order(): return Add.mul_order() - 1
def __rsub__(self, arg): return Add(translate(arg), Neg(self))
def __sub__(self, arg): return Add(self, Neg(translate(arg)))
def __radd__(self, arg): return Add(translate(arg), self)
def __add__(self, arg): return Add(self, translate(arg))