"""The WaveBlocks Project
This file contains the class which represents a homogeneous Hagedorn wavepacket.
@author: R. Bourquin
@copyright: Copyright (C) 2016 R. Bourquin
@license: Modified BSD License
"""
from numpy import zeros, complexfloating, array, eye, atleast_2d, angle, squeeze
from numpy.linalg import det
from WaveBlocksND.HagedornWavepacketBase import HagedornWavepacketBase
from WaveBlocksND.HyperCubicShape import HyperCubicShape
from WaveBlocksND.ComplexMath import ContinuousSqrt
from WaveBlocksND.GradientHAWPpsi import GradientHAWPpsi
from WaveBlocksND.HagedornBasisEvaluationPsi import HagedornBasisEvaluationPsi
__all__ = ["HagedornWavepacketPsi"]
[docs]class HagedornWavepacketPsi(HagedornWavepacketBase, HagedornBasisEvaluationPsi):
r"""This class represents homogeneous vector valued Hagedorn wavepackets
:math:`\Psi` with :math:`N` components in :math:`D` space dimensions.
"""
[docs] def __init__(self, dimension, ncomponents, eps):
r"""Initialize a new homogeneous Hagedorn wavepacket of the new kind.
:param dimension: The space dimension :math:`D` the packet has.
:param ncomponents: The number :math:`N` of components the packet has.
:param eps: The semi-classical scaling parameter :math:`\varepsilon` of the basis functions.
:return: An instance of :py:class:`HagedornWavepacket`.
"""
self._dimension = dimension
self._number_components = ncomponents
self._eps = eps
# The basis shapes K_i
self._basis_shapes = []
# The coefficients c^i
self._coefficients = []
for d in range(self._number_components):
# Default basis shapes for all components
BS = HyperCubicShape(self._dimension * [1])
self._basis_shapes.append(BS)
# A Gaussian
self._coefficients.append(zeros((BS.get_basis_size(), 1), dtype=complexfloating))
# Cache basis sizes
self._basis_sizes = [bs.get_basis_size() for bs in self._basis_shapes]
# Default parameters of harmonic oscillator eigenstates
q = zeros((self._dimension, 1), dtype=complexfloating)
p = zeros((self._dimension, 1), dtype=complexfloating)
Q = eye(self._dimension, dtype=complexfloating)
P = 1.0j * eye(self._dimension, dtype=complexfloating)
S = zeros((1, 1), dtype=complexfloating)
# The parameter set Pi
self._Pis = [q, p, Q, P, S]
# No inner product set
self._IP = None
# Function for taking continuous roots
self._sqrt = ContinuousSqrt(reference=angle(det(Q)))
[docs] def __str__(self):
r""":return: A string describing the Hagedorn wavepacket :math:`\Psi`.
"""
s = "Homogeneous Hagedorn wavepacket with " + str(self._number_components) + " component(s) in " + str(self._dimension) + " space dimension(s)\n"
return s
def _get_sqrt(self, component):
r"""Compatibility method
"""
return self._sqrt
[docs] def get_description(self):
r"""Return a description of this wavepacket object.
A description is a ``dict`` containing all key-value pairs
necessary to reconstruct the current instance. A description
never contains any data.
"""
d = {}
d["type"] = "HagedornWavepacketPsi"
d["dimension"] = self._dimension
d["ncomponents"] = self._number_components
d["eps"] = self._eps
if self._IP is not None:
d["innerproduct"] = self._IP.get_description()
return d
def clone(self, keepid=False):
# Parameters of this packet
params = self.get_description()
# Create a new Packet
# TODO: Consider using the block factory
other = HagedornWavepacketPsi(params["dimension"],
params["ncomponents"],
params["eps"])
# If we wish to keep the packet ID
if keepid is True:
other.set_id(self.get_id())
# And copy over all (private) data
# Basis shapes are immutable, no issues with sharing same instance
other.set_basis_shapes(self.get_basis_shapes())
other.set_parameters(self.get_parameters())
other.set_coefficients(self.get_coefficients())
# Innerproducts are stateless and finally immutable,
# no issues with sharing same instance
other.set_innerproduct(self.get_innerproduct())
# The complex root cache
other._sqrt = self._sqrt.clone()
return other
[docs] def get_parameters(self, component=None, aslist=False, key=("q", "p", "Q", "P", "S")):
r"""Get the Hagedorn parameter set :math:`\Pi` of the wavepacket :math:`\Psi`.
:param component: Dummy parameter for API compatibility with the inhomogeneous packets.
:param aslist: Return a list of :math:`N` parameter tuples. This is for API compatibility
with inhomogeneous packets.
:return: The Hagedorn parameter set :math:`\Pi = (q, p, Q, P, S)` in this order.
"""
Pilist = []
for k in key:
if k == "q":
Pilist.append(self._Pis[0].copy())
elif k == "p":
Pilist.append(self._Pis[1].copy())
elif k == "Q":
Pilist.append(self._Pis[2].copy())
elif k == "P":
Pilist.append(self._Pis[3].copy())
elif k == "S":
Pilist.append(self._Pis[4].copy())
elif k == "adQ":
Pilist.append(array(self._get_sqrt(component).get(), dtype=complexfloating))
else:
raise KeyError("Invalid parameter key: {}".format(key))
if aslist is True:
return self._number_components * [Pilist]
return Pilist
[docs] def set_parameters(self, Pi, component=None, key=("q", "p", "Q", "P", "S")):
r"""Set the Hagedorn parameters :math:`\Pi` of the wavepacket :math:`\Psi`.
:param Pi: The Hagedorn parameter set :math:`\Pi = (q, p, Q, P, S)` in this order.
:param component: Dummy parameter for API compatibility with the inhomogeneous packets.
"""
D = self._dimension
for k, item in zip(key, Pi):
if k == "q":
self._Pis[0] = atleast_2d(array(item, dtype=complexfloating)).reshape(D, 1)
elif k == "p":
self._Pis[1] = atleast_2d(array(item, dtype=complexfloating)).reshape(D, 1)
elif k == "Q":
self._Pis[2] = atleast_2d(array(item, dtype=complexfloating)).reshape(D, D)
elif k == "P":
self._Pis[3] = atleast_2d(array(item, dtype=complexfloating)).reshape(D, D)
elif k == "S":
self._Pis[4] = atleast_2d(array(item, dtype=complexfloating)).reshape(1, 1)
elif k == "adQ":
self._get_sqrt(component).set(squeeze(item))
else:
raise KeyError("Invalid parameter key: {}".format(key))
[docs] def get_gradient_operator(self):
r"""Return the :py:class:`Gradient` subclass suitable for
computing gradients of this wavepacket.
:return: A :py:class:`GradientHAWP` instance.
"""
return GradientHAWPpsi()