# This code is part of qtealeaves.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
Abstract base class for a term in the model.
"""
from itertools import product
from warnings import warn
from qtealeaves.tooling import QTeaLeavesError
from qtealeaves.tooling.parameterized import _ParameterizedClass
__all__ = ["_ModelTerm", "_ModelTerm1D"]
[docs]
class _ModelTerm(_ParameterizedClass):
"""
Abstract base class for any term in a model.
"""
strength = 1
prefactor = 1
@property
def is_oqs(self):
"""Status flag if term belongs to Hamiltonian or is Lindblad."""
return False
[docs]
@staticmethod
def check_dim(dim):
"""
By default, do not restrict dimensionality. Overwriting
this methods, allows to restrict terms to 1d, 2d, or
3d systems.
**Arguments**
dim : int
Dimensionality of the system, e.g., 3 for a 3-d systems.
"""
return isinstance(dim, int)
[docs]
@staticmethod
def iterate_sites(ll):
"""
Iterate sites; trivial in 1d model.
**Arguments**
ll : list of one int
Number of sites in the chain is stored in the
first entry of the list.
"""
if len(ll) == 1:
yield from range(ll[0])
else:
ranges = [range(ii) for ii in ll]
yield from product(*ranges)
[docs]
def eval_strength(self, params):
"""
Evaluate the strength of the parameter.
**Arguments**
params : dictionary
Contains the simulation parameters.
"""
strength = self.eval_numeric_param(self.strength, params)
if hasattr(strength, "__len__"):
raise QTeaLeavesError("Strength cannot be a list.")
if strength == 0.0:
warn("Adding term with zero-coupling.")
return strength
[docs]
def get_strengths(self):
"""
Returns an iterator over the strenghts of the term.
It is just the strength in most cases, but it can vary
(for example for the KrausTerm)
"""
yield self.strength
[docs]
def get_param_repr(self, param_map):
"""
Get the integer identifier as a string to be written into
the input files for fortran.
**Arguments**
param_map : dict
This dictionary contains the mapping from the parameters
to integer identifiers.
"""
if hasattr(self.strength, "__call__"):
param_repr = str(param_map[repr(self.strength)][0]) + "\n"
elif isinstance(self.strength, str):
param_repr = str(param_map[self.strength][0]) + "\n"
elif self.strength == 1.0:
# Default
param_repr = "-1\n"
else:
raise QTeaLeavesError("Case not allowed.")
return param_repr
[docs]
def get_interactions(self, ll, params, **kwargs):
"""
Iterator returning the terms one-by-one, e.g., to build
a Hamiltonian matrix. Must be overwritten by inheriting
class.
"""
raise NotImplementedError("Must be overwritten.")
[docs]
def get_fortran_str_twobody(self, ll, params, operator_map, param_map):
"""
Get the string representation needed to write
for Fortran. This method works for any two-body interaction.
**Arguments**
ll : int
Number of sites along the dimensions in the system, e.g.,
number of sites for both sides of the rectangle in a 2d system.
params : dictionary
Contains the simulation parameters.
operator_map : OrderedDict
For operator string as dictionary keys, it returns the
corresponding integer IDs.
param_map : dict
This dictionary contains the mapping from the parameters
to integer identifiers.
"""
str_repr = ""
param_repr = self.get_param_repr(param_map)
subterms = []
for elem in self.get_interactions(ll, params):
subterms.append(elem)
if elem[1][0] == elem[1][1]:
raise QTeaLeavesError("Same site ...")
# Number of terms and number of operators (later always 2)
str_repr += "%d\n" % (len(subterms))
str_repr += "%d\n" % (2)
for elem in subterms:
# Two "loop" over the operators
# pylint: disable=no-member
if elem[1][0] < elem[1][1]:
op_id_str_0 = operator_map[(self.operators[0], "l")]
op_id_str_1 = operator_map[(self.operators[1], "r")]
# Convert from python index to fortran index by
# adding offset 1
str_repr += "%d %d\n" % (elem[1][0] + 1, op_id_str_0)
str_repr += "%d %d\n" % (elem[1][1] + 1, op_id_str_1)
else:
op_id_str_0 = operator_map[(self.operators[0], "r")]
op_id_str_1 = operator_map[(self.operators[1], "l")]
# Convert from python index to fortran index by
# adding offset 1
str_repr += "%d %d\n" % (elem[1][1] + 1, op_id_str_1)
str_repr += "%d %d\n" % (elem[1][0] + 1, op_id_str_0)
# pylint: enable=no-member
str_repr += param_repr + " %30.15E\n" % (self.prefactor)
return str_repr
# pylint: disable-next=unused-argument, too-many-arguments
[docs]
def quantum_jump_weight(self, state, operators, quench, time, params, **kwargs):
"""Evaluate the unnormalized weight for a jump with this Lindblad term."""
if self.is_oqs:
raise NotImplementedError("Must be overwritten for Lindblad.")
raise ValueError("Trying to evaluate quantum jump on Hamiltonian term.")
# pylint: disable-next=unused-argument
[docs]
def quantum_jump_apply(self, state, operators, params, rand_generator, **kwargs):
"""Apply jump with this Lindblad."""
if self.is_oqs:
raise NotImplementedError("Must be overwritten for Lindblad.")
raise ValueError("Trying to evaluate quantum jump on Hamiltonian term.")
# pylint: disable-next=abstract-method
[docs]
class _ModelTerm1D(_ModelTerm):
"""
Abstract base class for any term in a 1D model.
"""
[docs]
@staticmethod
def check_dim(dim):
"""
See :func:`_ModelTerm.check_dim`
"""
if dim != 1:
raise QTeaLeavesError("Dimension does not match.")
[docs]
def collect_operators(self):
"""
All the required operators are returned to ensure that they are
written by fortran.
"""
# Take same approach as in 2d/3d system and provide both versions
# of the operator (although there is no mapping like the Hilbert
# curve, occasional problem appeared)
# pylint: disable=no-member
yield self.operators[0], "l"
yield self.operators[0], "r"
yield self.operators[1], "l"
yield self.operators[1], "r"
if self.add_complex_conjg:
# Only works in the b, bdagger or sigma^{+}, sigma^{-}
# scenario
yield self.operators[1], "l"
yield self.operators[0], "r"
# pylint: enable=no-member