# Derivative calculator simplifier using python

I am newbie in the field of programming, but I have tried to make relatively "huge" project. My "app" would derivate a mathematical expression, after that simplify the output expression.

How could I improve the expression simplify function to further simplify the output expression?

I know about simpy, I want to implement the simplifier myself using python.

from abc import ABC, abstractmethod
import numpy as np
import math

class derivateSymb(ABC):
@abstractmethod
def df(self, var): pass

@abstractmethod
def compute(self): pass

class Const(derivateSymb):
def __init__(self, value): self.value = value
def df(self, var): return Const(0)
def compute(self): return self.value
def __repr__(self): return str(self.value)

class Var(derivateSymb):
def __init__(self, name, value=None): self.name, self.value = name, value
def df(self, var):  return Const(1) if self == var else Const(0)
def compute(self):
if self.value is None:
raise ValueError('unassigned variable')
return self.value
def __repr__(self): return f'{self.name}'

def __init__(self, x, y): self.x, self.y = x, y
def df(self, var): return Add(self.x.df(var), self.y.df(var))
def compute(self): return self.x.compute() + self.y.compute()
def __repr__(self): return f'({self.x} + {self.y})'

class Sub(derivateSymb):
def __init__(self, x, y): self.x, self.y = x, y
def df(self, var): return Sub(self.x.df(var), self.y.df(var))
def compute(self): return self.x.compute() - self.y.compute()
def __repr__(self): return f'({self.x} - {self.y})'

class Mult(derivateSymb):
def __init__(self, x, y): self.x, self.y = x, y
def df(self, var): return Add( Mult(self.x.df(var), self.y), Mult(self.x, self.y.df(var)) )
def compute(self): return self.x.compute() * self.y.compute()
def __repr__(self): return f'({self.x} * {self.y})'

class Div(derivateSymb):
def __init__(self, x, y): self.x, self.y = x, y
def df(self, var): return Div( Sub( Mult(self.x.df(var), self.y), Mult(self.x, self.y.df(var)) ), Mult(self.y, self.y) )
def compute(self): return self.x.compute() / self.y.compute()
def __repr__(self): return f'({self.x} / {self.y})'

class Pow(derivateSymb):
def __init__(self, base, power): self.base, self.power = base, power
def df(self, var): return Mult( Mult( Const(self.power), Pow(self.base, self.power-1) ), self.base.df(var) )
def compute(self): return math.pow(self.base.compute(), self.power)
def __repr__(self): return f'({self.base}^{self.power})'

class Sin(derivateSymb):
def __init__(self, x): self.x = x
def df(self, var): return Mult( Cos(self.x), self.x.df(var) )
def compute(self): return math.sin(self.x.compute())
def __repr__(self): return f'(sin{self.x})'

class Cos(derivateSymb):
def __init__(self, x): self.x = x
def df(self, var): return Mult( Const(-1), Mult( Cos(self.x), self.x.df(var) ) )
def compute(self): return math.cos(self.x.compute())
def __repr__(self): return f'(cos{self.x})'

class Nodes:
@staticmethod
def _to_symbolic(x):
if not isinstance(x, derivateSymb): return Const(x)
else: return x
def __sub__(self, other): return ErgonomicSub(self, self._to_symbolic(other))
def __mul__(self, other): return ErgonomicMul(self, self._to_symbolic(other))
def __truediv__(self, other): return ErgonomicDiv(self, self._to_symbolic(other))
def __neg__(self): return ErgonomicMul(Const(-1), self)

class ErgonomicVar(Var, Nodes): pass
class ErgonomicSub(Sub, Nodes): pass
class ErgonomicMul(Mult, Nodes): pass
class ErgonomicDiv(Div, Nodes): pass

def simplify(node):
elif isinstance(node, Sub): return simplifySub(node)
elif isinstance(node, Mult): return simplifyMult(node)
else: return node

x, y = simplify(node.x), simplify(node.y)
x_const, y_const = isinstance(x, Const), isinstance(y, Const)
if x_const and y_const: return Const(x.value + y.value)
elif x_const and x.value == 0: return y
elif y_const and y.value == 0: return x

def simplifySub(node):
x, y = simplify(node.x), simplify(node.y)
x_const, y_const = isinstance(x, Const), isinstance(y, Const)
if x_const and y_const: return Const(x.value - y.value)
elif x_const and x.value == 0: return y
elif y_const and y.value == 0: return x
else: return Sub(x, y)

def simplifyMult(node):
x, y = simplify(node.x), simplify(node.y)
x_const, y_const = isinstance(x, Const), isinstance(y, Const)
if x_const and y_const: return Const(x.value * y.value)
elif x_const and x.value == 0: return Const(0)
elif x_const and x.value == 1: return y
elif y_const and y.value == 0: return Const(0)
elif y_const and y.value == 1: return x
else: return Mult(x, y)

x = ErgonomicVar('x', 3)
y = ErgonomicVar('y', 3)
z = Pow(x*3+y*y*y, 3)
#z = Sin(x*3+y*y)
print(z)
print(z.compute())
print(simplify(z.df(x)))
print(z.df(x).compute())

• What is ABC supposed to be? And do you have any import statements that are not included in this code sample? Sep 20, 2022 at 11:43
• @AaronMeese I have just included the import statements. Sep 20, 2022 at 11:48
• (taking passes, eh?) Dec 18, 2022 at 10:54

Firstly I recommend using some form of standard Python formatter, so your code is standardized and will allow others to more easily assist you. In my answer I use the black formatter, but you can choose from alternatives.

I would recommend is adding support for division, exponents, sin, and cosin, since you already have the classes for them:

def simplify(node):
elif isinstance(node, Sub):
return simplifySub(node)
elif isinstance(node, Mult):
return simplifyMult(node)
elif isinstance(node, Div):
return simplifyDiv(node)
elif isinstance(node, Pow):
return simplifyPow(node)
elif isinstance(node, Sin):
return simplifySin(node)
else:
return node

x, y = simplify(node.x), simplify(node.y)
x_const, y_const = isinstance(x, Const), isinstance(y, Const)
if x_const and y_const:
return Const(x.value + y.value)
elif x_const and x.value == 0:
return y
elif y_const and y.value == 0:
return x
else:

def simplifySub(node):
x, y = simplify(node.x), simplify(node.y)
x_const, y_const = isinstance(x, Const), isinstance(y, Const)
if x_const and y_const:
return Const(x.value - y.value)
elif x_const and x.value == 0:
return y
elif y_const and y.value == 0:
return x
else:
return Sub(x, y)

def simplifyMult(node):
x, y = simplify(node.x), simplify(node.y)
x_const, y_const = isinstance(x, Const), isinstance(y, Const)
if x_const and y_const:
return Const(x.value * y.value)
elif x_const and x.value == 0:
return Const(0)
elif x_const and x.value == 1:
return y
elif y_const and y.value == 0:
return Const(0)
elif y_const and y.value == 1:
return x
else:
return Mult(x, y)

def simplifyDiv(node):
x, y = simplify(node.x), simplify(node.y)
x_const, y_const = isinstance(x, Const), isinstance(y, Const)
if x_const and y_const:
return Const(x.value / y.value)
elif x_const and x.value == 0:
return Const(0)
elif y_const and y.value == 1:
return x
else:
return Div(x, y)

def simplifyPow(node):
x, y = simplify(node.base), simplify(node.power)
x_const, y_const = isinstance(x, Const), isinstance(y, Const)
if x_const and y_const:
return Const(math.pow(x.value, y.value))
elif y_const and y.value == 0:
return Const(1)
elif y_const and y.value == 1:
return x
else:
return Pow(x, y)

def simplifySin(node):
x = simplify(node.x)
x_const = isinstance(x, Const)
if x_const:
return Const(math.sin(x.value))
else:
return Sin(x)

def simplifyCos(node):
x = simplify(node.x)
x_const = isinstance(x, Const)
if x_const:
return Const(math.cos(x.value))
else:
return Cos(x)


This can be complemented by adding __pow__ to the Nodes class:

def __pow__(self, other):
return ErgonomicPow(self, self._to_symbolic(other))


And by adding in your empty ErgonomicPow class:

class ErgonomicPow(Pow, Nodes):
pass

• Does the code follow PEP8? That would be one thing to review. Welcome to the Code Review Community. While this might be a good answer on stackoverflow, it is not a good answer on Code Review. A good answer on Code Review contains at least one insightful observation about the code. Alternate code only solutions are considered poor answers and may be down voted or deleted by the community. Please read How do I write a good answer. Sep 20, 2022 at 13:43
• Answer has been edited to conform with your suggestions (I think) @pacmaninbw Thanks for the feedback, I'm very new to this forum! Sep 20, 2022 at 19:26