# L-24 MCS 507 Mon 17 Oct 2011 : mtaylor.py

# This script exports a class to work with multivariate Taylor series,
# encapsulating SymPy.  For the class to work, expressions better use
# the SymPy functions, imported as "sp", see the test below.

import sympy as sp
from powerenumerator import PowerEnumerator

class mTaylor:

   def __init__(self,expression,variables, \
                parameters,values):
      """
      Evaluates the expression in the
      variables at the values.
      """
      self.vars = sp.var(variables)
      self.pars = sp.var(parameters)
      self.expr = eval(expression)
      self.vals = values
      t = sp.Subs(self.expr,self.vars,values).doit()
      self.terms = [t]
      self.enum = PowerEnumerator(len(variables))
      self.order = self.enum.next()

   def __str__(self):
      """
      Returns the string expression
      of all terms in the series.
      """
      s = 0
      for i in xrange(len(self.terms)):
         s = s + self.terms[i]
      return str(s)

   def next(self):
      """
      Returns the next term of the series.
      """
      ord = self.enum.next()
      drv = self.expr
      prd = sp.Rational(1)
      for k in xrange(len(self.vars)):
         if ord[k] > 0:
            drv = sp.diff(drv,self.vars[k],ord[k])
            prd = prd*(self.vars[k] - self.vals[k])**ord[k]
            prd = prd/sp.factorial(ord[k])
      ev = sp.Subs(drv,self.vars,self.vals).doit()
      t = ev*prd
      self.terms.append(t)
      return t

def main():
   """
   As test we use A*sin(f*x*y)
   where the variables are x and y,
   and the parameters are A and f.
   """
   X = ('x','y')   # variables
   P = ('A','f')   # parameters
   v = (0.34,1.32) # values
   e = "A*sp.sin(f*x*y)"
   t = mTaylor(e,X,P,v)
   m = input('how many terms you want ? ')
   for i in xrange(m):
      print 'current series :', t
      print 'next term :', t.next()

if __name__=="__main__": main()
