def rules_add(obj, verbose=False): args = flatten_args(obj) terms = Counter() numeric = 0 for arg in args: if isinstance(arg, Neg): arg0 = arg.args[0] if isinstance(arg0, Number): numeric += -arg0.value else: terms.update({Mul(arg0): -1}) elif isinstance(arg, Number): numeric += arg.value elif isinstance(arg, Mul): arg0 = arg.args[0] if isinstance(arg0, Neg): if isinstance(arg0.args[0], Number): terms.update({Mul(*arg.args[1:]): -arg0.args[0].value}) elif isinstance(arg0, Number): terms.update({Mul(*arg.args[1:]): arg0.value}) else: terms.update({arg: 1}) else: terms.update({arg: 1}) new_args = [v * s if v != 1 else s for s, v in terms.items() if v != 0] if len(new_args) == 0: return Number(numeric) if len(new_args) == 1 and numeric == 0: return new_args[0] if numeric == 0: return Add(*new_args) return Add(*new_args, Number(numeric))
def __new__(cls, arg, **kwargs): if arg == Number(0): return Number(1) symbol = Symbol(r'{\delta}') obj = super().__new__(cls, symbol, arg) if not hasattr(obj, 'arg'): obj.arg = arg return obj
def translate(arg): if isinstance(arg, Operable): return arg if isinstance(arg, (int, float)): if arg < 0: return Neg(Number(abs(arg))) return Number(arg) if isinstance(arg, str): return Symbol(arg) raise Exception('translate not implemented for', arg.__class__)
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 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
class DiracDelta(AppliedFunction): rules = RuleList( Rule( 'delta dirac zero', lambda obj: obj.arg == Number(0), lambda obj, v: Number(1) ) ) def __new__(cls, arg, **kwargs): if arg == Number(0): return Number(1) symbol = Symbol(r'{\delta}') obj = super().__new__(cls, symbol, arg) if not hasattr(obj, 'arg'): obj.arg = arg return obj
def __str__(self): base = self.base if not isinstance(base, (Symbol, Indexed)): base = delim(base) power = self.power if isinstance(power, Neg): if power == -Number(1): return fr'\frac{{1}}{{{base}}}' return fr'\frac{{1}}{{{base}^{{{power.arg}}} }}' return fr'{base}^{{ {power} }}'
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 __new__(cls, arg, *lims, **kwargs): lims = list(map(translate, lims)) if len(lims) < 2: lims = (lims[0], -Infinity, Infinity) if len(lims) < 3: lims = (lims[0], lims[1], Infinity) if arg == Number(1): return lims[2] - lims[1] obj = super().__new__(cls, arg, *lims) if not hasattr(obj, 'name'): obj.arg = arg obj.lims = lims return obj
class Pow(Expr): @staticmethod def add_order(): return Mul.add_order() - 1 @staticmethod def mul_order(): return Add.mul_order() - 1 rules = RuleList( Rule('(x!=0)^0 -> 1', lambda obj: obj.base == Number(0) and not obj.power == Number(0), lambda obj, v: Number(1)), Rule('0^{x!=0} -> 0', lambda obj: obj.power == Number(0) and not obj.base == Number(0), lambda obj, v: Number(0)), Rule('x^1 -> x', lambda obj: obj.power == Number(1), lambda obj, v: obj.base), Rule('1^x -> 1', lambda obj: obj.base == Number(1), lambda obj, v: Number(1)), Rule('(x^a)^b -> x^{a*b}', lambda obj: isinstance(obj.base, Pow), lambda obj, v: obj.base.base**(obj.base.power * obj.power)), ) def __new__(cls, *args, **kwargs): obj = super().__new__(cls, *args) if not hasattr(obj, 'base'): obj.base = args[0] obj.power = args[1] return obj def __init__(self, *args, **kwargs): pass def __str__(self): base = self.base if not isinstance(base, (Symbol, Indexed)): base = delim(base) power = self.power if isinstance(power, Neg): if power == -Number(1): return fr'\frac{{1}}{{{base}}}' return fr'\frac{{1}}{{{base}^{{{power.arg}}} }}' return fr'{base}^{{ {power} }}'
class KroneckerDelta(Indexed): @staticmethod def add_order(): return Mul.add_order() - 1 @staticmethod def mul_order(): return AppliedFunction.mul_order() + 2 rules = RuleList( Rule('KroneckerDelta rule', lambda obj: obj.indices[0] == obj.indices[1], lambda obj, v: Number(1))) def __new__(cls, ind0, ind1, **kwargs): if ind0 == ind1: return Number(1) symbol = Symbol(r'{\delta}') obj = super().__new__(cls, symbol, [ind0, ind1]) return obj
class Add(Expr): @staticmethod def add_order(): return Symbol.add_order() - 1 @staticmethod def mul_order(): return Symbol.mul_order() + 2 rules = RuleList( Rule(r'flatten args', lambda obj: any(areinstance(obj.args, Add)), lambda obj, v: Add(*flatten_args(obj))), Rule( r'add numbers', lambda obj: any(areinstance(obj.args, Number)), lambda obj, v: Add(Number(sum(getnumbervalues( obj))), *[arg for arg in obj.args if not isnumber(arg)])), Rule(r'add rules', lambda obj: True, lambda obj, v: rules_add(obj, v)), ) def __new__(cls, *args, **kwargs): if len(args) == 0: return Zero if len(args) == 1: return args[0] if Zero in args: return Add(*[arg for arg in args if not arg == Zero]) args = sorted(args, key=lambda x: x.add_order()) obj = super().__new__(cls, *args) return obj def __init__(self, *args, **kwargs): pass def __str__(self): s = '+'.join([ str(arg) if not isinstance(arg, Add) else delim(str(arg)) for arg in self.args ]) return s.replace('+-', '-')
def __new__(cls, ind0, ind1, **kwargs): if ind0 == ind1: return Number(1) symbol = Symbol(r'{\delta}') obj = super().__new__(cls, symbol, [ind0, ind1]) return obj
def add_order(): return Number.add_order() + 1
def rules_diracdelta(obj, v): if obj.arg == Number(0): return Number(1) return obj