Source code for WaveBlocksND.LinearCombinationOfWPs
"""The WaveBlocks Project
This file contains the class which represents linear combinations
of general but compatible wavepackets of any kind.
@author: R. Bourquin
@copyright: Copyright (C) 2013 R. Bourquin
@license: Modified BSD License
"""
from numpy import zeros, ones, complexfloating, atleast_2d, delete, vstack
from WaveBlocksND.LinearCombinationOfWavepackets import LinearCombinationOfWavepackets
__all__ = ["LinearCombinationOfWPs"]
[docs]class LinearCombinationOfWPs(LinearCombinationOfWavepackets):
r"""This class represents linear combinations
of general but compatible wavepackets of any kind.
"""
[docs] def __init__(self, dimension, number_components, number_packets=0):
r"""Initialize a new linear combination of general wavepackets. This
object represents :math:`\Upsilon := \sum_{j=0}^{J-1} c_j \Psi_j`.
All :math:`J` wavepackets :math:`\Psi_j` have the same number :math:`N`
components and are defined in the :math:`D` dimensional space.
:param dimension: The space dimension :math:`D` the packets have.
:param ncomponents: The number :math:`N` of components the packets have.
:return: An instance of :py:class:`LinearCombinationOfWPs`.
"""
self._dimension = dimension
self._number_components = number_components
self._packets = []
self._packet_ids = []
self._number_packets = number_packets
self._coefficients = zeros((number_packets, 1), dtype=complexfloating)
[docs] def __str__(self):
r"""
:return: A string describing the linear combination of general
wavepackets :math:`\Upsilon = \sum_{j=0}^J c_j \Psi_j`.
"""
s = ("Linear combination of "+str(self._number_packets)+" general wavepackets, each with "+str(self._number_components)+" component(s) in "+str(self._dimension)+" space dimension(s)\n")
return s
[docs] def get_description(self):
r"""Return a description of this linear combination 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"] = "LinearCombinationOfWPs"
d["dimension"] = self._dimension
d["ncomponents"] = self._number_components
return d
def clone(self, keepid=False):
# Parameters of this packet
params = self.get_description()
# Create a new linear combination
# TODO: Consider using the block factory
other = LinearCombinationOfWPs(params["dimension"],
params["ncomponents"])
newpackets = [wp.clone(keepid=keepid) for wp in self.get_wavepackets()]
other.add_wavepackets(newpackets, self.get_coefficients())
return other
[docs] def add_wavepacket(self, packet, coefficient=1.0):
r"""Add a new wavepacket to the linear combination.
:param packet: The new wavepacket :math:`\Psi_j` to add.
:type packet: A :py:class:`Wavepacket` subclass instance.
:param coefficient: The corresponding coefficient :math:`c_j`, default is 1.0.
"""
if not packet.get_dimension() == self._dimension:
raise ValueError("Number of dimensions does not match.")
if not packet.get_number_components() == self._number_components:
raise ValueError("Number of components does not match.")
# Note: we do not test that the varepsilon parameter matches.
pid = packet.get_id()
if pid in self._packet_ids:
raise ValueError("Adding packet failed due to duplicate id.")
self._packets.append(packet)
self._number_packets = self._number_packets + 1
self._packet_ids.append(pid)
self._coefficients = vstack([self._coefficients, atleast_2d(coefficient)])
[docs] def add_wavepackets(self, packetlist, coefficients=None):
r"""Add a list of new wavepackets to the linear combination.
:param packetlist: A list of new wavepackets :math:`\{\Psi_j\}`.
:type packetlist: A list of :py:class:`Wavepacket` subclass instances.
:param coefficients: The corresponding coefficient vector :math:`c`, default
is a vector of all 1.0.
"""
if coefficients is not None and len(packetlist) != coefficients.size:
raise ValueError("Differently many packets and coefficients given.")
pids = []
for packet in packetlist:
if not packet.get_dimension() == self._dimension:
raise ValueError("Number of dimensions does not match.")
if not packet.get_number_components() == self._number_components:
raise ValueError("Number of components does not match.")
# Note: we do not test that the varepsilon parameter matches.
pid = packet.get_id()
pids.append(pid)
if pid in self._packet_ids:
raise ValueError("Adding packet failed due to duplicate id.")
if coefficients is None:
coefficients = ones((len(packetlist), 1))
self._packets.extend(packetlist)
self._number_packets = self._number_packets + len(packetlist)
self._packet_ids.extend(pids)
self._coefficients = vstack([self._coefficients, atleast_2d(coefficients).reshape((-1, 1))])
[docs] def remove_wavepacket(self, index):
r"""Remove a wavepacket :math:`\Psi_j` from the linear combination.
:param index: The index :math:`0 \leq j < J` of the packet to remove.
"""
self._packets.pop(index)
self._number_packets = self._number_packets - 1
self._packet_ids.pop(index)
self._coefficients = delete(self._coefficients, index).reshape((-1, 1))
[docs] def get_wavepacket(self, index):
r"""Get the wavepacket :math:`\Psi_j` from the linear combination.
:param index: The index :math:`0 \leq j < J` of the packet to retrieve.
:return: The wavepacket :math:`\Psi_j`.
:type: A :py:class:`Wavepacket` subclass instance.
"""
return self._packets[index]
[docs] def get_wavepackets(self):
r"""Get a list of all wavepackets :math:`\Psi_j` in the linear combination.
:return: A list of all wavepackets :math:`\Psi_j`.
:type: A list of :py:class:`Wavepacket` subclass instances.
"""
return tuple(self._packets)
[docs] def set_wavepackets(self, packetlist):
r"""Set the list :math:`\{\Psi_j\}_j` of new wavepackets.
:param packetlist: A list of new wavepackets :math:`\Psi_j`.
:type packetlist: A list of :py:class:`Wavepacket` subclass instances.
"""
if not len(packetlist) == self._number_packets:
raise ValueError("Wrong number of new packets.")
self._packets = packetlist[:]
[docs] def get_coefficient(self, index):
r"""Get the coefficient :math:`c_j` of the wavepacket :math:`\Psi_j`.
:param index: The index :math:`0 \leq j < J` of the coefficient to retrieve.
:return: The coefficient :math:`c_j`.
"""
return self._coefficients[index]
[docs] def set_coefficient(self, index, coefficient):
r"""Set the coefficient :math:`c_j` of the wavepacket :math:`\Psi_j`.
:param index: The index :math:`0 \leq j < J` of the coefficient to retrieve.
:param coefficient: The coefficient :math:`c_j`.
"""
self._coefficients[index] = coefficient
[docs] def get_coefficients(self):
r"""Get the vector with all coefficients :math:`c_j` of all wavepackets :math:`\Psi_j`.
:return: The vector :math:`c` of all coefficients :math:`c_j`. The vector is of
shape :math:`(J, 1)`.
:type: An :py:class:`ndarray`
"""
return self._coefficients.copy()
[docs] def set_coefficients(self, coefficients):
r"""Update all the coefficients :math:`c` of :math:`\Upsilon`.
:param coefficients: The vector :math:`c`.
:type coefficients: An :py:class:`ndarray`
"""
if not coefficients.size == self._number_packets:
raise ValueError("Wrong number of new coefficients.")
self._coefficients = coefficients.copy().reshape((-1, 1))
[docs] def evaluate_at(self, grid, component=None):
r"""Evaluate the linear combination of wavepackets :math:`\Upsilon` at
the given nodes :math:`\gamma`.
:param grid: The grid :math:`\Gamma` containing the nodes :math:`\gamma`.
:type grid: A class having a ``get_nodes`` method.
:param component: The index :math:`i` of a single component to evaluate.
(Defaults to ``None`` for evaluating all components.)
:return: A list of arrays or a single array containing the values of the
:math:`\Phi_i` at the nodes :math:`\gamma`.
"""
if self._number_packets == 0:
raise ValueError("No packets in the linear combination to evaluate.")
# Split one off to get the result array shape
if self._number_packets > 0:
vals = self._packets[0].evaluate_at(grid, component=component, prefactor=True)
if component is None:
result = [self._coefficients[0] * val for val in vals]
else:
result = self._coefficients[0] * vals
for index, packet in enumerate(self._packets[1:]):
vals = packet.evaluate_at(grid, component=component, prefactor=True)
if component is None:
result = [res + self._coefficients[index + 1] * val for res, val in zip(result, vals)]
else:
result = result + self._coefficients[index + 1] * vals
return result