class Indexed(Operable): @staticmethod def add_order(): return Symbol.add_order() + .5 @staticmethod def mul_order(): return Symbol.mul_order() + .5 rules = RuleList() def __new__(cls, base, indices, **kwargs): if not hasattr(indices, '__iter__'): indices = [indices] label = fr'{base}_{{{" ".join(map(str,indices))}}}' name = fr'{base.name}_{{{" ".join(map(str,indices))}}}' new_obj = super().__new__(cls, label) if not hasattr(new_obj, 'name'): new_obj.base = base new_obj.name = name new_obj.label = label new_obj.indices = indices new_obj.rules = RuleList() return new_obj def __init__(self, *args, **kwargs): pass def __call__(self, *args): return AppliedIndexedFunction(self, *map(translate, args))
class Neg(Expr): def add_order(self): return self.arg.add_order() + .1 def mul_order(self): return self.arg.mul_order() rules = RuleList( # Rule( # r'negative number', # lambda obj: isinstance(obj.arg, Number), # lambda obj, v: NegNumber(obj.arg.value) # ), Rule(r'double negative', lambda obj: isinstance(obj.arg, Neg), lambda obj, v: obj.arg.arg)) def __new__(cls, arg): obj = super().__new__(cls, arg) if not hasattr(obj, 'arg'): obj.arg = arg return obj def __init__(self, *args, **kwargs): pass def __str__(self): arg = self.arg s = str(arg) if not isinstance(arg, Symbol): if not hasattr(arg, 'base'): s = delim(s) return fr'-{s}'
def __new__(cls, label): name = label label = fr'{cls.__name__}({repr(label)})' new_obj = super().__new__(cls, label) if not hasattr(new_obj, 'name'): new_obj.name = name new_obj.rules = RuleList() return new_obj
def __new__(cls, *args): label = fr'{cls.__name__}({", ".join(map(repr, args))})' obj = super().__new__(cls, label) if not hasattr(obj, 'operator'): obj.operator = cls obj.args = args obj.rules = RuleList() return obj
def __new__(cls, label): _hash = hash(label) if not _hash in _instances.keys(): new_obj = object().__new__(cls) new_obj.label = label new_obj.rules = RuleList() _instances[_hash] = new_obj return new_obj return _instances[_hash]
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 __new__(cls, base, indices, **kwargs): if not hasattr(indices, '__iter__'): indices = [indices] label = fr'{base}_{{{" ".join(map(str,indices))}}}' name = fr'{base.name}_{{{" ".join(map(str,indices))}}}' new_obj = super().__new__(cls, label) if not hasattr(new_obj, 'name'): new_obj.base = base new_obj.name = name new_obj.label = label new_obj.indices = indices new_obj.rules = RuleList() return new_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
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 AppliedFunction(Expr): @staticmethod def add_order(): return Mul.add_order() + 1 @staticmethod def mul_order(): return Pow.mul_order() + 1 rules = RuleList() def __new__(cls, symbol, *args, **kwargs): name = symbol.name obj = super().__new__(cls, symbol.name, *args) if not hasattr(obj, 'name'): obj.operator = symbol obj.args = args obj.name = name return obj def __init__(self, *args, **kwargs): pass def __str__(self): return fr'{self.operator}({", ".join(map(str, self.args))})' def __lshift__(self, arg): def _(obj, v): print(arg.label) print(self.args[0].label) print(obj.args[0].label) string = arg.label.replace(self.args[0].label, obj.args[0].label) print('string', string) return eval( arg.label.replace(self.args[0].label, obj.args[0].label)) rule = Rule(fr'{self} := {arg}', lambda obj: obj.operator == self.operator, lambda obj, v: _(obj, v)) self.__class__.rules.append(rule) return
class Mul(Expr): @staticmethod def add_order(): return Symbol.add_order()+2 @staticmethod def mul_order(): return Symbol.mul_order()+1 rules = RuleList( Rule( r'mul rules', lambda obj: True, lambda obj, v: rules_mul(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 Zero if One in args: return Mul(*[arg for arg in args if not arg == One]) args = sorted( args, key=lambda x: x.mul_order() ) obj = super().__new__(cls, *args ) return obj def __init__(self, *args, **kwargs): pass def __str__(self): s = '' for arg in self.args: if isinstance(arg, (Add, Neg, Mul)): s += delim(str(arg)) else: s += str(arg) return s
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('+-', '-')
class Symbol(Operable): @staticmethod def add_order(): return 0 @staticmethod def mul_order(): return 0 rules = RuleList() def __new__(cls, label): name = label label = fr'{cls.__name__}({repr(label)})' new_obj = super().__new__(cls, label) if not hasattr(new_obj, 'name'): new_obj.name = name new_obj.rules = RuleList() return new_obj def __init__(self, label): pass
class Summation(Expr): @staticmethod def add_order(): return Number.add_order() + 1 @staticmethod def mul_order(): return AppliedFunction.mul_order() + 1 rules = RuleList( Rule('summation rule', lambda obj: True, lambda obj, v: rules_summation(obj, v))) 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 def __init__(self, *args, **kwargs): pass def __str__(self): lims = self.lims arg = self.arg if isinstance(arg, Add): arg = delim(arg) return fr'\sum_{{{lims[0]}={lims[1]}}}^{{{lims[2]}}}{arg}'