def calculate_local_quadratic(self, diagonal_component=None): r""" Calculate the local quadratic approximation :math:`U` of the potential's eigenvalue :math:`\lambda`. :param diagonal_component: Dummy parameter that has no effect here. .. note:: This function is idempotent. """ # Calculation already done at some earlier time? if self.taylor_eigen_s is not None: return self.calculate_eigenvalues() self.calculate_jacobian() self.calculate_hessian() self.taylor_eigen_s = [ (0, self.eigenvalues_s), (1, self.jacobian_s), (2, self.hessian_s) ] # Construct function to evaluate the approximation at point q at the given nodes assert(self.taylor_eigen_n is None) self.taylor_eigen_n = [ (order, sympy.vectorize(0)(sympy.lambdify([self.x], f, "numpy"))) for order, f in self.taylor_eigen_s ]
def __init__(self, expression, variables): r""" Create a new ``MatrixPotential1S`` instance for a given potential matrix :math:`V\left(x\right)`. :param expression: An expression representing the potential. """ #: The variable :math:`x` that represents position space. self.x = variables[0] #: The matrix of the potential :math:`V\left(x\right)`. self.potential = expression # Unpack single matrix entry self.potential = self.potential[0,0] self.exponential = None self.number_components = 1 # prepare the function in every potential matrix cell for numerical evaluation self.potential_n = sympy.vectorize(0)(sympy.lambdify(self.x, self.potential, "numpy")) # Symbolic and numerical eigenvalues and eigenvectors self.eigenvalues_s = None self.eigenvalues_n = None self.eigenvectors_s = None self.eigenvectors_n = None self.taylor_eigen_s = None self.taylor_eigen_n = None self.remainder_eigen_s = None self.remainder_eigen_n = None
def calculate_hessian(self): r""" Calculate the hessian matrix for component :math:`V_{0,0}` of the potential. For potentials which depend only one variable :math:`x`, this equals the second derivative. """ self.hessian_s = sympy.diff(self.potential, self.x, 2) self.hessian_n = sympy.vectorize(0)(sympy.lambdify(self.x, self.hessian_s, "numpy"))
def calculate_hessian(self): r""" Calculate the hessian matrix for each component :math:`V_{i,j}` of the potential. For potentials which depend only one variable :math:`x`, this equals the second derivative. """ self.hessian_s = tuple([ sympy.diff(item, self.x, 2) for item in self.potential ]) self.hessian_n = tuple([ sympy.vectorize(0)(sympy.lambdify(self.x, item, "numpy")) for item in self.hessian_s ])
def __init__(self, expression, variables): r""" Create a new :py:class:`MatrixPotentialMS` instance for a given potential matrix :math:`V\left(x\right)`. :param expression: An expression representing the potential. """ #: The variable :math:`x` that represents position space. self.x = variables[0] #: The matrix of the potential :math:`V\left(x\right)`. self.potential = expression self.number_components = self.potential.shape[0] # prepare the function in every potential matrix cell for numerical evaluation self.potential_n = tuple([ sympy.vectorize(0)(sympy.lambdify(self.x, item, "numpy")) for item in self.potential ]) # {}[chi] -> [(order, function),...] self.taylor_eigen_n = {} # {}[chi] -> [remainder] self.remainder_eigen_s = {} self.remainder_eigen_n = {} # [] -> [remainder] self.remainder_eigen_ih_s = None self.remainder_eigen_ih_n = None
def calculate_eigenvalues(self): r""" Calculate the eigenvalue :math:`\lambda_0\left(x\right)` of the potential :math:`V\left(x\right)`. In the scalar case this is just the matrix entry :math:`V_{0,0}`. .. note:: This function is idempotent and the eigenvalues are memoized for later reuse. """ if self.eigenvalues_s is None: self.eigenvalues_s = self.potential self.eigenvalues_n = sympy.vectorize(0)(sympy.lambdify(self.x, self.potential, "numpy"))
def calculate_eigenvectors(self): r""" Calculate the eigenvector :math:`nu_0\left(x\right)` of the potential :math:`V\left(x\right)`. In the scalar case this is just the value :math:`1`. .. note:: This function is idempotent and the eigenvectors are memoized for later reuse. """ if self.eigenvectors_s is None: self.eigenvectors_s = sympy.Matrix([[1]]) self.eigenvectors_n = sympy.vectorize(0)(sympy.lambdify(self.x, 1, "numpy"))
def evaluate_exponential_at(self, nodes): r""" Evaluate the exponential of the potential matrix :math:`V` at some grid nodes :math:`\gamma`. :param nodes: The grid nodes :math:`\gamma` we want to evaluate the exponential at. :return: The numerical approximation of the matrix exponential at the given grid nodes. """ # Hack for older sympy versions, see recent issue: # http://www.mail-archive.com/[email protected]/msg05137.html lookup = {"I" : 1j} # prepare the function of every potential matrix exponential cell for numerical evaluation self.expfunctions = sympy.vectorize(0)(sympy.lambdify(self.x, self.exponential, (lookup, "numpy"))) return tuple([ numpy.array(self.expfunctions(nodes)) ])
def calculate_local_remainder(self, diagonal_component=None): r""" Calculate the non-quadratic remainder :math:`W` of the quadratic approximation :math:`U` of the potential's eigenvalue :math:`\lambda`. This function is used for the homogeneous case and takes into account the leading component :math:`\chi`. :param diagonal_component: Dummy parameter that has no effect here. .. note:: This function is idempotent. """ # Calculation already done at some earlier time? if self.remainder_eigen_s is not None: return self.calculate_eigenvalues() f = self.eigenvalues_s # point where the taylor series is computed q = sympy.Symbol("q") p = f.subs(self.x, q) j = sympy.diff(f, self.x) j = j.subs(self.x, q) h = sympy.diff(f, self.x, 2) h = h.subs(self.x, q) quadratic = p + j*(self.x-q) + sympy.Rational(1,2)*h*(self.x-q)**2 # Symbolic expression for the taylor expansion remainder term self.remainder_eigen_s = self.potential - quadratic # Construct functions to evaluate the approximation at point q at the given nodes assert(self.remainder_eigen_n is None) self.remainder_eigen_n = sympy.vectorize(1)(sympy.lambdify([q, self.x], self.remainder_eigen_s, "numpy"))