def product(items): res = 1 for i in items: res = res * i return res
class Node:
def __init__(self, name, value=0): self.name = name self.value = value
def __add__(self, other): return wrapper_opt("add", self, other)
def __mul__(self, other): return wrapper_opt("mul", self, other)
def __truediv__(self, other): return wrapper_opt("div", self, other)
def __pow__(self, other): return wrapper_opt("pow", self, other)
def __sub__(self, other): return wrapper_opt("add", self, wrapper_opt("mul", Constant(-1), other))
def __radd__(self, other): return wrapper_opt("add", self, other, r=True)
def __rmul__(self, other): return wrapper_opt("mul", self, other, r=True)
def __rtruediv__(self, other): return wrapper_opt("div", self, other, r=True)
def __eq__(self, other): return self.name == other.name
def __str__(self): return str(self.name)
__repr__ = __str__
class Constant(Node):
def __init__(self, value): super().__init__(value, value)
def calculate(self): return self.value
@staticmethod def derivative(variable): return 0
class Variable(Node):
def calculate(self): return self.value
def derivative(self, variable): return 1 if variable.name == self.name else 0
class Operator(Node):
def __init__(self, inputs, name): super().__init__(name) self.inputs = inputs self.name = f"Opt {name} of {inputs}"
def __str__(self): opt2str = {"Add": "+", "Power": "^", "Multiply": "*", "Divide": "/"} return opt2str[self.name.split(" ")[1]].join(map(str, self.inputs))
class Add(Operator):
def __init__(self, inputs): super().__init__(inputs, name="Add")
def calculate(self): return sum(inp.calculate() for inp in self.inputs)
def derivative(self, variable): return sum(inp.derivative(variable) for inp in self.inputs)
class Multiply(Operator):
def __init__(self, inputs): super().__init__(inputs, name="Multiply")
def calculate(self): return product(inp.calculate() for inp in self.inputs)
def derivative(self, variable): return sum( inp.derivative(variable) * product( other_inp.calculate() for other_inp in self.inputs if other_inp != inp ) for inp in self.inputs )
class Divide(Operator):
def __init__(self, inputs): super().__init__(inputs, name="Divide")
def calculate(self): a, b = [inp.calculate() for inp in self.inputs] return a / b
def derivative(self, variable): a, b = [inp.calculate() for inp in self.inputs] da, db = [inp.derivative(variable) for inp in self.inputs] return (da * b - db * a) / (b ** 2)
class Power(Operator):
def __init__(self, inputs): super().__init__(inputs, name="Power")
def calculate(self): _x, n = self.inputs n = n.value return _x.calculate() ** n
def derivative(self, variable): _x, n = self.inputs n = n.value return n * (_x.calculate() ** (n - 1)) * _x.derivative(variable)
def wrapper_opt(opt, self, other, r=False): opt2class = {"add": Add, "mul": Multiply, "pow": Power, "div": Divide} if not isinstance(other, Node): other = Constant(other) inputs = [other, self] if r else [self, other] node = opt2class[opt](inputs=inputs) return node
|