Source code for normal_forms.jet

import numpy as np
import sympy
import combinatorics
from multiindex import multiindex
import bases


[docs]class jet(object): """Truncated Taylor's series. The jet is represented in both a closed and expanded form. The closed form is ``fun``:math:`=\\sum_{0\\leq deg \\leq k}` ``fun_deg[deg]`` where ``fun_deg[deg]=coeff[deg]*pb[deg]`` is a symbolic representation of the degree ``deg`` term. The expanded form is the list ``fun_deg`` of ``sympy.Matrix(m,1)`` objects where ``coeff`` is a list of ``k+1 numpy.array`` objects with shapes :math:`(m,{n+j-1 \\choose j})` for :math:`0\leq j\leq k`. ``pb`` is a dictionary indexed by degree of ``sympy.Matrix`` objects with ``pb[j]`` representing a basis for homogenous :math:`j^{th}` degree polynomials in the variables :math:`x_0,\ldots,x_{n-1}` of the form :math:`\\begin{pmatrix}x_0^j & x_0^{j-1}x_1 & \\cdots & x_{n-1}^j \\end{pmatrix}^T`. ``coeff[deg][coord,term]`` is the coefficient of the monomial ``pb[deg][term]`` in coordinate ``coord`` of the partial derivative of :math:`f` indexed by the ``term`` th ``normal_forms.multiindex.multiindex(deg,n)``. Parameters ---------- f : callable function that accepts ``n`` arguments and returns tuple of length ``m``, corresponding to mathematical function :math:`f:\\mathbb{R}^n\\rightarrow\\mathbb{R}^m` x : number if ``n==1`` or tuple of length ``n`` if ``n>=1`` center about which jet is expanded k : int maximum degree of jet Attributes ---------- n : int dimension of domain of :math:`f` m : int dimension of codomain of :math:`f` var : list of ``n sympy.symbol`` objects ``x_0``, ``x_1``, ..., ``x_{n-1}`` representing arguments of :math:`f` pb : ``normal_forms.bases.poly_basis`` a basis for polynomials in the variables ``var`` coeff : list of ``k+1 numpy.array`` objects of shape :math:`(m,{n+j-1\\choose j})` for :math:`0\leq j\leq k` jet coefficients indexed as ``coeff[deg][coord,term]`` where :math:`0\leq` ``deg`` :math:`\leq k`, :math:`0\leq` ``coord`` :math:`\leq m`, and :math:`0\leq` ``term`` :math:`<{m-1+deg \\choose deg}`. fun_deg : list of ``k+1 sympy.Matrix(m,1)`` objects symbolic representation of each term in the jet indexed as ``fun_deg[deg]`` for ``deg=0,...,k`` fun : ``sympy.Matrix(m,1)`` symbolic representation of jet fun_lambdified : callable lambdified version of fun """ def __init__(self, f, x, k, f_args=None, var=None, pb=None): """initialize the jet""" self.f = f self.x = x self.k = k if np.array(x).shape == (): n, x = 1, [x] else: n = len(x) # call to f if f_args is None: f_eval = f(*x) else: f_eval = f(*(list(x) + list(f_args))) if np.array(f_eval).shape == (): m = 1 else: # call to f m = len(f_eval) self.m = m self.n = n if var is None: var = sympy.symarray('x', (n, )) if pb is None: pb = bases.poly_basis(var) self.var = var self.pb = pb # number of terms per degree of expanded form n_terms = combinatorics.simplicial_list(n, k) coeff = [np.empty([m, n_terms[deg]]) for deg in range(k + 1)] basis = [sympy.ones(n_terms[deg], 1) for deg in range(k + 1)] # call to f if f_args is None: f_eval = f(*var) else: f_eval = f(*(list(var) + list(f_args))) coeff[0][:, 0] = list(sympy.Matrix([f_eval]).subs(zip(var, x))) for deg in range(1, k + 1): m_idx = multiindex(deg, n) for term in range(n_terms[deg]): # call to f if f_args is None: f_eval = f(*var) else: f_eval = f(*(list(var) + list(f_args))) coeff[deg][:, term] = list( sympy.diff(sympy.Matrix([f_eval]), *m_idx.to_var(var)) .subs(zip(var, x)) / m_idx.factorial()) basis[deg][term] = m_idx.to_polynomial(var, x) m_idx.increment() for deg in range(k + 1): poly = list(sympy.Matrix(coeff[deg]) * basis[deg]) m_idx = multiindex(deg, n) for term in range(n_terms[deg]): for coord in range(m): coeff[deg][coord, term] = poly[coord].coeff(pb[deg][term]) m_idx.increment() self.coeff = coeff self.update_fun()
[docs] def update_fun(self): """Compute symbolic and lambdified versions of the jet from the coefficients.""" # symbolic representation by degree fun_deg = [ sympy.Matrix(self.coeff[deg]) * self.pb[deg] for deg in range(self.k + 1) ] self.fun_deg = fun_deg for deg in range(self.k + 1): self.fun_deg[deg] = sympy.Matrix(self.coeff[deg]) * self.pb[deg] # symbolic representation, sum of elements in fun_deg self.fun = sympy.zeros(self.m, 1) for deg in range(self.k + 1): self.fun += self.fun_deg[deg] self.fun = list(self.fun) if len(self.fun) == 1: self.fun = self.fun[0] # lambdified fun self.fun_lambdified = sympy.lambdify(self.var, self.fun)
def __call__(self, *args): """Evaluate the jet.""" return self.fun_lambdified(*args) def __getitem__(self, deg): """Return symbolic representation of ``deg``th degree jet term.""" res = list(self.fun_deg[deg]) if len(res) == 1: return res[0] else: return res