Source code for qnet.algebra.hilbert_space_algebra

# coding=utf-8
#This file is part of QNET.
#
#    QNET is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#    QNET is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with QNET.  If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2012-2013, Nikolas Tezak
#
###########################################################################

r"""
The Hilbert Space Algebra
=========================

This module defines some simple classes to describe simple and *composite/tensor* (i.e., multiple degree of freedom)
Hilbert spaces of quantum systems.

For more details see :ref:`hilbert_space_algebra`.
"""


from qnet.algebra.abstract_algebra import *

[docs]@six.add_metaclass(ABCMeta) class HilbertSpace(object): """ Basic Hilbert space class from which concrete classes are derived. """
[docs] def tensor(self, other): """ Tensor product between Hilbert spaces :param other: Other Hilbert space :type other: HilbertSpace :return: Tensor product space. :rtype: HilbertSpace """ return ProductSpace.create(self, other)
[docs] def remove(self, other): """ Remove a particular factor from a tensor product space. :param other: Space to remove :type other: HilbertSpace :return: Hilbert space for remaining degrees of freedom. :rtype: HilbertSpace """ return self._remove(other)
@abstractmethod def _remove(self, other): raise NotImplementedError(self.__class__.__name__)
[docs] def intersect(self, other): """ Find the mutual tensor *factors* of two Hilbert spaces. :param other: Other Hilbert space :type other: HilbertSpace """ return self._intersect(other)
@abstractmethod def _intersect(self, other): raise NotImplementedError(self.__class__.__name__)
[docs] def local_factors(self): """ :return: A sequence of LocalSpace objects that tensored together yield this Hilbert space. :rtype: tuple of LocalSpace objects """ return self._local_factors()
@abstractmethod def _local_factors(self): raise NotImplementedError(self.__class__.__name__)
[docs] def is_tensor_factor_of(self, other): """ Test if a space is included within a larger tensor product space. Also ``True`` if ``self == other``. :param other: Other Hilbert space :type other: HilbertSpace :rtype: bool """ return self <= other
[docs] def is_strict_tensor_factor_of(self, other): """ Test if a space is included within a larger tensor product space. Not ``True`` if ``self == other``. :param other: Other Hilbert space :type other: HilbertSpace :rtype: bool """ return self < other
@property def dimension(self): """ :return: The full dimension of the Hilbert space :rtype: int """ #noinspection PyTypeChecker,PyCallByClass,PyArgumentList return BasisRegistry.dimension(self)
[docs] def is_strict_subfactor_of(self, other): """ Test whether a Hilbert space occures as a strict sub-factor in (larger) Hilbert space :type other: HilbertSpace """ return self._is_strict_subfactor_of(other)
@abstractmethod def _is_strict_subfactor_of(self, other): raise NotImplementedError(self.__class__.__name__) def __len__(self): """ :return: The number of LocalSpace factors / degrees of freedom. :rtype: int """ return len(self.local_factors()) def __mul__(self, other): return self.tensor(other) def __div__(self, other): return self.remove(other) __truediv__ = __div__ def __and__(self, other): return self.intersect(other) def __lt__(self, other): return self.is_strict_subfactor_of(other) def __gt__(self, other): return other.is_strict_subfactor_of(self) def __le__(self, other): return self == other or self < other def __ge__(self, other): return self == other or self > other
@singleton class TrivialSpace(HilbertSpace, Expression): """ The 'nullspace', i.e. a one dimensional Hilbert space, which is a factor space of every other Hilbert space. """ def __hash__(self): return hash(self.__class__) def _order_key(self): return "____", # def tensor(self, other): # return other def _all_symbols(self): return set(()) def _remove(self, other): return self def _intersect(self, other): return self def _local_factors(self): return () def _is_strict_subfactor_of(self, other): if other is TrivialSpace: return False return True def __eq__(self, other): return self is other def _tex(self): return r"\mathcal{H}_{\rm null}" @singleton class FullSpace(HilbertSpace, Expression): """ The 'full space', i.e. a Hilbert space, includes any other Hilbert space as a tensor factor. """ def __hash__(self): return hash(self.__class__) def _order_key(self): return "____", # def tensor(self, other): # return self def _all_symbols(self): return set(()) def _remove(self, other): raise AlgebraError() def _local_factors(self): raise AlgebraError() def _intersect(self, other): return other def _is_strict_subfactor_of(self, other): return False def __eq__(self, other): return self is other def _tex(self): return r"\mathcal{H}_{\rm total}"
[docs]@check_signature class LocalSpace(HilbertSpace, Operation): """ Basic class to instantiate a local Hilbert space, i.e., for a single degree of freedom. ``LocalSpace(name, namespace)`` :param name: The identifier of the local space / degree of freedom :type name: str :param namespace: The namespace for the degree of freedom, useful in hierarchical system models. :type namespace: str """ signature = basestring, basestring def _order_key(self): return self.operands def _remove(self, other): if other == self: return TrivialSpace return self def _intersect(self, other): if self in other.local_factors(): return self return TrivialSpace def _local_factors(self): return self, def _is_strict_subfactor_of(self, other): if isinstance(other, ProductSpace) and self in other.operands: #noinspection PyTypeChecker assert len(other.operands) > 1 return True if other is FullSpace: return True return False def _get_dimension(self): return BasisRegistry.dimension(self) def _set_dimension(self, dimension): try: current_basis = list(self.basis) current_length = len(current_basis) if current_length == dimension: return if current_basis != range(current_length): raise ValueError('It appears that the current basis of {} is not simply a range of integer-labelled states: {}'.format(str(self), str(current_basis))) BasisRegistry.set_basis(self, range(dimension)) except BasisNotSetError: BasisRegistry.set_basis(self, range(dimension)) dimension = property(_get_dimension, _set_dimension, doc="The local state space dimension.") @property def basis(self): """ :return: The set of basis states of the local Hilbert space :rtype: sequence of int or str """ #noinspection PyCallByClass,PyArgumentList,PyTypeChecker return BasisRegistry.get_basis(self) def _tex(self): name, namespace = self.operands if namespace: return "{{{}}}_{{{}}}".format(tex(name), tex(namespace)) return "{{{}}}".format(tex(name)) def __str__(self): name, namespace = self.operands if namespace: return "{}_{}".format(name, namespace) return name
# #noinspection PyRedeclaration
[docs]def local_space(name, namespace = "", dimension = None, basis = None): """ Create a LocalSpace with by default empty namespace string. If one also provides a set of basis states, these get stored via the BasisRegistry object. ALternatively, one may provide a dimension such that the states are simply labeled by a range of integers: ``[0, 1, 2, ..., dimension -1]`` :param name: Local space identifier :type name: str or int :param namespace: Local space namespace, see LocalSpace documentation :type namespace: str :param dimension: Dimension of local space (optional) :type dimension: int :param basis: Basis state labels for local space :type basis: sequence of int or sequence of str """ if isinstance(name, int): s = LocalSpace.create(str(name), namespace) else: s = LocalSpace.create(name, namespace) if dimension: if basis: assert len(basis) == dimension else: basis = range(dimension) if basis: #noinspection PyArgumentList,PyTypeChecker BasisRegistry.set_basis(s, basis) return s
[docs]def convert_to_spaces_mtd(dcls, clsmtd, cls, *ops): """ For all operands that are merely of type str or int, substitute LocalSpace objects with corresponding labels: For a string, just itself, for an int, a string version of that int. """ cops = [o if isinstance(o, HilbertSpace) else local_space(o) for o in ops] return clsmtd(cls, *cops)
convert_to_spaces = preprocess_create_with(convert_to_spaces_mtd)
[docs]@assoc @convert_to_spaces @idem @check_signature_assoc @filter_neutral class ProductSpace(HilbertSpace, Operation): """ Tensor product space class for an arbitrary number of local space factors. ``ProductSpace(*factor_spaces)`` :param factor_spaces: The Hilbert spaces to be tensored together. :type factor_spaces: HilbertSpace """ signature = HilbertSpace, neutral_element = TrivialSpace @classmethod def create(cls, *operands): if any(o is FullSpace for o in operands): return FullSpace return cls(*operands) def _remove(self, other): if other is FullSpace: return TrivialSpace if other is TrivialSpace: return self if isinstance(other, ProductSpace): oops = set(other.operands) else: oops = {other} return ProductSpace.create(*sorted(set(self.operands).difference(oops))) def _local_factors(self): return self.operands def _intersect(self, other): if other is FullSpace: return self if other is TrivialSpace: return TrivialSpace if isinstance(other, ProductSpace): other_ops = set(other.operands) else: other_ops = {other} return ProductSpace.create(*sorted(set(self.operands).intersection(other_ops))) def _is_strict_subfactor_of(self, other): if isinstance(other, ProductSpace): return set(self.operands) < set(other.operands) if other is FullSpace: return True return False def _tex(self): return " \otimes ".join(map(tex, self.operands))
[docs]class BasisNotSetError(AlgebraError): """ Is raised when the basis states of a LocalSpace are requested before being defined. :param local_space: :type local_space: """ def __init__(self, local_space): msg = """The basis for the local space {0!s} has not been set yet. Please set the basis states via the command: qnet.algebra.HilbertSpaceAlgebra.BasisRegistry.set_basis({0!r}, basis)""".format(local_space) super(BasisNotSetError, self).__init__(msg)
@singleton class BasisRegistry(object): """ Singleton class to store information about the basis states of all """ registry = {} def set_basis(self, local_space, basis): """ Set the basis states of a local Hilbert space. :param local_space: Local Hilbert space object :type local_space: LocalSpace :param basis: A sequence of state labels :type basis: sequence of int or sequence of str """ previous = self.registry.get(local_space, basis) if basis != previous: print( "Warning: changing basis specification for registered LocalSpace {!s} from {!r} to {!r}".format(local_space, previous, basis)) self.registry[local_space] = basis def get_basis(self, local_space): """ Retrieve the basis states of a local Hilbert space. If no basis states have been set, raise a BasisNotSetError exception. :param local_space: Local Hilbert space object :type local_space: LocalSpace :return: A sequence of state labels :rtype: sequence of int or str :raise: BasisNotSetError """ if local_space in self.registry: return self.registry[local_space] raise BasisNotSetError(local_space) def dimension(self, space): """ Compute the full dimension of a Hilbert space object. :param space: Hilbert space to compute dimension for :type space: HilbertSpace :return: Dimension of space :rtype: int """ if space is TrivialSpace: return 1 if space is FullSpace: return inf dims = [len(self.get_basis(s)) for s in space.local_factors()] return prod(dims)