title: Function algebra for adding or multiplying several python functions and packaging parameters in a single vector
body: <p>I am trying to construct a more complex function from the sum and product of multiple simple functions. I want to create a class where I can add a new simple function and its operation to construct a complex function then evaluate the complex function. </p>
  
  <p>For example say I have the functions:</p>
  
  <pre class="lang-py prettyprint-override"><code>def f1(a, b, c):
      return 2 * c
  
  def f2(a, b, c, d):
      return c + d
  </code></pre>
  
  <p>And would like to construct something like:</p>
  
  <pre class="lang-py prettyprint-override"><code>f1(0, 0, 1) + f2(0, 0, 1, 0) * f1(0, 0, 2)
  #Out:6
  </code></pre>
  
  <p>But without typing out the whole expression and with the parameters packaged in a single vector so I can optimise them. I have built a class to do it but it is really quite clumsy and doesn't follow BODMAS (the mathematical order of operations,  Bracket, Of, Division, Multiplication, Addition and Subtraction):</p>
  
  <pre class="lang-py prettyprint-override"><code>class FunctionAlgebra:
  
      def __init__(self):
          self.f = []
          self.op = []
          self.hpid = []
          self.hyperparameters = []
          self.name = '0'
  
      def compose(self, f, p, op):
          """
          Compose an expression
          """
          self.hpid = np.concatenate([self.hpid, len(self.f) * np.ones(len(p))])
          self.hyperparameters = np.concatenate([self.hyperparameters, p])
          self.f.append(f)
          self.op.append(op)
          self.name += " {} {}".format(op.__name__, f.__name__)
          self.name = self.name.replace('add', "+").replace("multiply", '*')
  
      def _evalf_(self, a, b, f, args):
          """
          Evaluate a function
          """
          self.cr = f(a, b, *args)
  
      def multiply(self):
          """
          Multiply function result by total result
          """
          self.result *= self.cr
  
      def add(self):
          """
          Add function result to the total result.
          """
          self.result += self.cr
  
      def call(self, a, b, p):
          """
          Call and evaluate the expression
          """
          self.result = 0
          for i in range(len(self.f)):
              args = p[self.hpid == i]
              self._evalf_(a, b, self.f[i], args)
              self.op[i]()
          return self.result
  </code></pre>
  
  <p>Testing on the first example the answer should be 6 but is 12 due to no BODMAS:</p>
  
  <pre class="lang-py prettyprint-override"><code>f = FunctionAlgebra()
  f.compose(f1, [1], f.add)
  f.compose(f2, [1, 0], f.add)
  f.compose(f1, [2], f.multiply)
  f.call(0, 0, f.hyperparameters)
  #Out:12
  </code></pre>
  
  <p>Does anyone know a more pythonic way of adding and multiplying functions together and combining their parameters into a single vector?</p>
  
  <p>Thanks,</p>
  
  <p>Robin</p>