title: Improving Performance - Symbolic function applied to each row of numpy array
body: <p>I have a list of input vectors that I need to use as inputs to a list of functions that are generated using SymPy. In the real application, the number of input vectors is ~100k, and there are ~5M sets of symbolic functions. This is currently the bottleneck in my code, so I'm trying to speed things up.</p>
  
  <p>I've already made big improvements by using Sympy's lambdify to create lambda functions based on numpy, but I can't help but think there's a way to vectorize this and get the for loops into numpy/C instead of python.</p>
  
  <p>I initially thought numpy.apply_along_axis() would help, but it still does the looping in python.</p>
  
  <p>This is a simplified version of what I'm doing now:</p>
  
  <pre><code>import time
  import sympy as sp
  import numpy as np
  
  #Input for performance testing
  # sampleSize = 200000
  # inputVector = [1.2, -0.33]
  
  # inputArray = np.array(inputVector*np.ones((sampleSize,1)))
  
  
  #This array would have ~100k rows in actual data set
  inputArray = [[-.333, -.558],
                [-.454, -.367],
                [-.568, -.678]]                    
  
  
  start = time.time()
  
  
  #These are the equations of motion of a mechanical system. Each row represents 
  #a unique arrangement of components. There may be a better way to handle this, 
  #but I haven't understood the system well enough to do so yet.
  
  #This array would have ~5M rows in actual data set
  symEqns = [['(R_1 - 1)/(R_0 - 1)', '0', '-R_1 + 1',     '(R_1 - 1)/(R_0*R_1 - 1)','1'],
             ['R_1/R_0',             '0', '-1/(R_0 - 1)', '(R_1 - 1)/(R_0 - 1)',    '1']]
  
  for eqnSet in symEqns:
    #Create lambda functions 
    lambdaFuncs = []
    for eqn in eqnSet:
      func = sp.lambdify(['R_0', 'R_1'], eqn, 'numpy')
  
      #This is ~5x slower, due to use of pure python vs. numpy ??
      # func = lambda R_0, R_1: eval(eqn)  
  
      lambdaFuncs.append(func)
  
    #Evaluate each lambda func for each input set
  
    # in my actual code, this is a parameter of an object. forgot to store it in my example code
    outputList = []   
    for row in inputArray:
      results = []
      for func in lambdaFuncs:
        results.append(func(*row))
      outputList.append(results)
  
  end = time.time()
  print "\nTotal Time Elapsed: {:d}:{:0&gt;5.2f}".format(int((end-start)/60), (end-start)%60)
  </code></pre>
  
  <p>If it helps, I could also structure the evaluation to calculate each function independently, creating a column of results for each function. Here's an example of the evaluation block in that case (using for loops for illustration, I'd like to get to vectorized evaluation using numpy):</p>
  
  <pre><code>  #Evaluate each lambda func for each input set
    outputList = []
    for func in lambdaFuncs:
      results = []
      for row in inputArray:
        results.append(func(*row))
      outputList.append(results)       
  </code></pre>
  
  <p>[EDIT] For future reference, here's my improved working sample code for this problem. I've tweaked a few things from Oliver's response, mainly allowing for variable length input vectors:</p>
  
  <pre><code>import time
  import sympy as sp
  import numpy as np
  
  # This array would have ~100k rows in actual data set
  input_array = np.array([[-.333, -.558],
                [-.454, -.367],
                [-.568, -.678]])
  
  
  
  #This array would have ~5M rows in actual data set (generated via Sympy linear algebraic solns)
  sym_eqns = [['(R_1 - 1)/(R_0 - 1)', '0', '-R_1 + 1',     '(R_1 - 1)/(R_0*R_1 - 1)','1'],
             ['R_1/R_0',             '0', '-1/(R_0 - 1)', '(R_1 - 1)/(R_0 - 1)',    '1']]
  
  for eqn_set in sym_eqns:
    output_list = []
    for eqn in eqn_set:
      func = sp.lambdify(['R_0', 'R_1'], eqn, 'numpy')
      results = func(*[input_array[:,n] for n in range(input_array.shape[1])])  
      output_list.append(results)     
  </code></pre>