Source code for WaveBlocksND.BasisTransformationHAWP
r"""The WaveBlocks Project
This file contains the class for basis transformations of Hagedorn wavepackets
between the canonical basis and the basis spanned by the eigenvectors
of the potential.
@author: R. Bourquin
@copyright: Copyright (C) 2012 R. Bourquin
@license: Modified BSD License
"""
from __future__ import absolute_import
from numpy import dot, transpose, conjugate, vsplit
from WaveBlocksND.BasisTransformation import BasisTransformation
__all__ = ["BasisTransformationHAWP"]
[docs]class BasisTransformationHAWP(BasisTransformation):
r"""This class implements basis transformations of Hagedorn wavepackets
:math:`\Psi(x)` between the canonical basis of and the basis :math:`\Lambda(x)`
spanned by the eigenvectors :math:`\nu_i(x)` of the potential :math:`V(x)`.
"""
[docs] def __init__(self, potential, builder=None):
r"""Create a new :py:class:`BasisTransformationHAWP` instance for a
given potential matrix :math:`V(x)`.
:param potential: The potential underlying the basis transformation.
:type potential: A :py:class:`MatrixPotential` instance.
:param builder: An object that can compute this matrix.
:type builder: A :py:class:`Quadrature` subclass instance.
"""
# Keep a reference to the potential
self._potential = potential
# Precompute eigenvectors is case it is necessary
self._potential.calculate_eigenvectors()
if builder is not None:
self.set_matrix_builder(builder)
[docs] def set_matrix_builder(self, builder):
r"""Set the matrix builder. It is responsible for computing the matrix
elements :math:`\langle\phi_i|V_{i,j}|\phi_j\rangle`. This matrix
is used during the basis transformation.
:param builder: An object that can compute this matrix.
:type builder: A :py:class:`Quadrature` subclass instance.
"""
# Keep a reference to the matrix builder
self._builder = builder
[docs] def transform_to_canonical(self, wavepacket):
r"""Transform the wavepacket :math:`\Psi` given
in the eigenbasis to the canonical basis.
Note that this method acts destructively on the given
:py:class:`Wavepacket` instance. If this is not desired,
clone the packet before handing it over to this method.
:param wavepacket: The Hagedorn wavepacket to transform.
:type wavepacket: A :py:class:`Wavepacket` subclass instance.
:return: Another :py:class:`Wavepacket` instance containing the
transformed wavepacket :math:`\Psi^\prime`.
"""
# No transformation for potentials with a single energy level.
# The canonical and eigenbasis are identical here.
if self._potential.get_number_components() == 1:
return
# Basically an ugly hack to overcome some shortcomings of the
# matrix function and of the data layout.
# TODO: Fix and remove
class F():
def __init__(self, potential):
self._potential = potential
self._N = self._potential.get_number_components()
self._values = None
def __call__(self, x, dummy, entry):
# x is given as (D,|QR|) array
if self._values is None:
z = self._potential.evaluate_eigenvectors_at(x)
# returned is a N list of (N,|QR|) arrays
# we need a N**2 list of (|QR|,) arrays
result = []
for nu in z:
result.extend(vsplit(nu, self._N))
self._values = tuple(result)
return self._values[entry[0] * self._N + entry[1]]
f = F(self._potential)
# And now compute the transformation
F = transpose(conjugate(self._builder.build_matrix(wavepacket, operator=f)))
c = wavepacket.get_coefficient_vector()
d = dot(F, c)
wavepacket.set_coefficient_vector(d)
return wavepacket
[docs] def transform_to_eigen(self, wavepacket):
r"""Transform the wavepacket :math:`\Psi^\prime` given
in the canonical basis to the eigenbasis.
Note that this method acts destructively on the given
:py:class:`Wavepacket` instance. If this is not desired,
clone the packet before handing it over to this method.
:param wavepacket: The Hagedorn wavepacket to transform.
:type wavepacket: A :py:class:`Wavepacket` subclass instance.
:return: Another :py:class:`Wavepacket` instance containing the
transformed wavepacket :math:`\Psi`.
"""
# No transformation for potentials with a single energy level.
# The canonical and eigenbasis are identical here.
if self._potential.get_number_components() == 1:
return
# Basically an ugly hack to overcome some shortcomings of the
# matrix function and of the data layout.
# TODO: Fix and remove
class F():
def __init__(self, potential):
self._potential = potential
self._N = self._potential.get_number_components()
self._values = None
def __call__(self, x, dummy, entry):
# x is given as (D,|QR|) array
if self._values is None:
z = self._potential.evaluate_eigenvectors_at(x)
# returned is a N list of (N,|QR|) arrays
# we need a N**2 list of (|QR|,) arrays
result = []
for nu in z:
result.extend(vsplit(nu, self._N))
self._values = tuple(result)
return self._values[entry[0] * self._N + entry[1]]
f = F(self._potential)
# And now compute the transformation
F = self._builder.build_matrix(wavepacket, operator=f)
c = wavepacket.get_coefficient_vector()
d = dot(F, c)
wavepacket.set_coefficient_vector(d)
return wavepacket