Source code for hqs_spin_mapper.transformables
"""Module containing the provided transformable objects for the HQS Spin Mapper."""
# Copyright © 2021-2024 HQS Quantum Simulations GmbH. All Rights Reserved.
import sys
import textwrap
import numpy as np
import yaml
from typing import Optional, Union, Tuple, Set, cast
import lattice_builder as lb
import lattice_validator as lv
from hqs_spin_mapper.spin_mapper_protocols import (
Supports_SW_Transformation,
Supports_Spin_Optimization,
Hamiltonian_Matrices,
)
from lattice_builder import Builder
from struqture_py.fermions import FermionHamiltonianSystem
try:
from lattice_functions.Fermions import (
ExpressionSpinful,
ExpressionSpinful_complex,
)
except ImportError as e:
if "mkl" in str(e):
print(
textwrap.dedent(
"""\
===== ERROR: Missing MKL =====
The Intel Math Kernel Library (MKL) could not be found. Please ensure
that the MKL is installed properly.
When using HQStage, run
hqstage envs install-mkl
to install the MKL. In other cases, please consult the manual."""
),
file=sys.stderr,
)
sys.exit(1)
else:
raise
__all__ = [
"Transformable",
"Transformable_Matrices",
"Transformable_Struqture",
"Transformable_LatticeBuilder",
"Transformable_QuantumChemistry",
"SpinFinder",
]
[docs]
class Transformable(Supports_SW_Transformation):
r"""Base Transformable class to avoid code duplication between the transformables."""
[docs]
def __init__(self) -> None:
r"""Construct empty base class (variables specified for linting purposes only!)."""
self._spin_indices: Set[int] = set()
self._system_size = 0
self._site_type = "spinful_fermions"
self._prefactor_cutoff = 1.0
@property
def hamiltonians(self) -> Hamiltonian_Matrices:
r"""Return the matrix descriptions of the terms in the system Hamiltonian.
Returns:
Hamiltonian_Matrices: The stored Hamiltonian matrices
"""
return self._hamiltonians
@hamiltonians.setter
def hamiltonians(self, value: Hamiltonian_Matrices) -> None:
r"""Set the matrix descriptions of the terms in the system Hamiltonian.
Args:
value (Hamiltonian_Matrices): New collection of Hamiltonian matrices
"""
self._hamiltonians = value
# TODO: Modify to use Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]
@property
def rotation_matrix(self) -> np.ndarray:
r"""Return the rotation matrix that transforms the system to the spin-optimized basis.
Returns:
np.ndarray: The stored rotation matrix :math:`U_{iq}`
"""
return self._rotation_matrix
@rotation_matrix.setter
def rotation_matrix(self, value: np.ndarray) -> None:
r"""Set the rotation matrix that transforms the system to the spin-optimized basis.
Args:
value (np.ndarray): Updated rotation matrix :math:`U_{iq}`
"""
self._rotation_matrix = value
@property
def spin_indices(self) -> Set[int]:
r"""Return the set of indices of the spin-like orbitals.
Returns:
Set[int]: The indices of the spin-like orbitals
"""
return self._spin_indices
@spin_indices.setter
def spin_indices(self, value: Set[int]) -> None:
r"""Set the set of indices of the spin-like orbitals.
Args:
value (Set[int]): Updated indices of the spin-like orbitals
"""
self._spin_indices = value
@property
def system_size(self) -> int:
r"""Return the system size i.e. the number of orbitals.
Returns:
int: The combined size of the system
"""
return self._system_size
@property
def site_type(self) -> str:
r"""Return the type of orbitals (e.g. ``spinful_fermions`` or ``spinless_fermions``).
Returns:
str: The type of the system sites
"""
return self._site_type
@property
def prefactor_cutoff(self) -> float:
r"""Return the cutoff for the prefactors of terms to be considered in the transformation.
Returns:
float: The cutoff below which terms are dropped for the transformation
"""
return self._prefactor_cutoff
@prefactor_cutoff.setter
def prefactor_cutoff(self, value: float) -> None:
r"""Set the prefactor cutoff for terms considered in the Schrieffer-Woff transformation.
Args:
value (float): Lower bound for the absolute value of prefactors of terms considered.
"""
self._prefactor_cutoff = value
@property
def generator(self) -> Union[ExpressionSpinful, ExpressionSpinful_complex]:
r"""Return the generator :math:`S` of the Schrieffer-Wolff transformation.
Returns:
Union[ExpressionSpinful, ExpressionSpinfulComplex]:
The generator of the Schrieffer-Wolff transformation
"""
return self._generator
@generator.setter
def generator(self, value: Union[ExpressionSpinful, ExpressionSpinful_complex]) -> None:
r"""Set the generator :math:`S` of the Schrieffer-Wolff transformation.
Args:
value (Union[ExpressionSpinful, ExpressionSpinful_complex]):
Generator of the SW transformation
"""
self._generator = value
@property
def transformed_hamiltonian(self) -> Union[ExpressionSpinful, ExpressionSpinful_complex]:
r"""Return the Schrieffer-Wolff transformed Hamiltonian :math:`\tilde{H}`.
Returns:
Union[ExpressionSpinful, ExpressionSpinful_complex]:
The transformed Hamiltonian
"""
return self._transformed_hamiltonian
@transformed_hamiltonian.setter
def transformed_hamiltonian(
self, value: Union[ExpressionSpinful, ExpressionSpinful_complex]
) -> None:
r"""Set the Schrieffer-Wolff transformed Hamiltonian :math:`\tilde{H}`.
Args:
value (Union[ExpressionSpinful, ExpressionSpinful_complex]):
Schrieffer-Wolff transformed Hamiltonian
"""
self._transformed_hamiltonian = value
[docs]
class Transformable_LatticeBuilder(Transformable):
r"""Transformable system from a LatticeBuilder input."""
[docs]
def __init__(
self,
builder: Union[Builder, str],
rotation_matrix: Optional[np.ndarray] = None,
prefactor_cutoff: float = 1e-6,
spin_indices: Optional[Set[int]] = None,
) -> None:
r"""Construct the transformable system obtained from a LatticeBuilder input.
Args:
builder (Union[Builder, str]): LatticeBuilder input describing the system
rotation_matrix (np.ndarray): Transformation matrix :math:`U_{iq}` to the spin-optimized
basis
prefactor_cutoff (float): Cutoff for the absolute value of the coupling strength of
terms to be considered in the Schrieffer-Wolff transformation.
spin_indices (Set[int]): Set of indices of the dedicated spin-like orbitals.
Raises:
AssertionError: Input is not conforming to lattice validator requirements.
TypeError: The type of the input is incorrect.
"""
# If the input is a filename i.e. str, create a LatticeBuilder instance from it.
def load_from_lattice_builder_input(
lattice_builder_input: str,
) -> Builder:
"""Load LatticeBuilder yaml input file and create a transformable system object from it.
Args:
lattice_builder_input (str): Path to the lattice builder input yaml file
Returns:
Builder: Instance of LatticeBuilder for the input
Raises:
AssertionError: Input is not conforming to lattice validator requirements.
"""
# Open the yaml file containing the lattice builder description of the system
with open(lattice_builder_input, mode="r") as stream:
config = yaml.safe_load(stream)
# Validate the system description
validator = lv.Validator()
is_valid, _config = validator.validateConfiguration(config)
if not is_valid:
print(_config)
raise AssertionError()
# Turn configuration data in lattice builder description of the system.
lattice_builder = lb.Builder(_config)
return lattice_builder
if isinstance(builder, str):
try:
_builder = cast(Builder, load_from_lattice_builder_input(builder))
except AssertionError as lv_error:
raise AssertionError("Not a valid LatticeBuilder description!") from lv_error
# If the input is alread an instance of LatticeBuilder, continue
elif isinstance(builder, Builder):
_builder = cast(Builder, builder)
else:
raise TypeError("The given input has an invalid type!")
self._system_size = int(
np.prod(_builder.size) * np.prod(_builder.cluster_size) * _builder.M_uc
)
if _builder.is_spin_matrix:
(H0_uu, H0_dd, H0_ud) = _builder.getH0()
H0_uu = cast(np.ndarray, H0_uu.toarray())
H0_dd = cast(np.ndarray, H0_dd.toarray())
H0_ud = cast(np.ndarray, H0_ud.toarray())
else:
H0_uu = cast(np.ndarray, _builder.getH0().toarray())
H0_dd = cast(np.ndarray, _builder.getH0().toarray())
H0_ud = np.zeros_like(H0_uu)
HU = np.zeros_like(H0_uu)
HU += _builder.export_U_to_matrix()
# Particle hole symmetry compensation (including the constant energy shift)
H0_uu -= 0.5 * np.diag(np.diag(HU))
H0_dd -= 0.5 * np.diag(np.diag(HU))
self._particle_hole_symmetry_shift = np.real(0.25 * np.trace(HU))
if _builder.is_complex:
self._hamiltonians = Hamiltonian_Matrices(
H0_uu,
H0_dd,
H0_ud,
HU,
Jz=_builder.export_Jz_to_matrix(),
Jp=_builder.export_Jperp_to_matrix(),
Jc=_builder.export_Jcross_to_matrix(),
)
self._transformed_hamiltonian = ExpressionSpinful_complex()
self._generator = ExpressionSpinful_complex()
else:
self._hamiltonians = Hamiltonian_Matrices(
H0_uu.real,
H0_dd.real,
H0_ud.real,
HU.real,
Jz=_builder.export_Jz_to_matrix().real,
Jp=_builder.export_Jperp_to_matrix().real,
Jc=_builder.export_Jcross_to_matrix().real,
)
self._transformed_hamiltonian = ExpressionSpinful()
self._generator = ExpressionSpinful()
self._site_type = _builder.site_type
if spin_indices is None:
self._spin_indices: Set[int] = set()
else:
self._spin_indices = spin_indices
if rotation_matrix is None:
self._rotation_matrix = np.eye(self._system_size)
else:
self._rotation_matrix = rotation_matrix
self._prefactor_cutoff = prefactor_cutoff
@property
def particle_hole_symmetry_shift(self) -> float:
r"""Return the constant energy shift due to the particle-hole symetry of the interaction.
Returns:
float: The the value of the constant energy shift
"""
return self._particle_hole_symmetry_shift
[docs]
class Transformable_Struqture(Transformable):
r"""Transformable system from a LatticeBuilder input."""
[docs]
def __init__(
self,
system: FermionHamiltonianSystem,
prefactor_cutoff: float = 1e-6,
spin_indices: Optional[Set[int]] = None,
) -> None:
r"""Construct the transformable system obtained from a Struqture input.
Args:
system (FermionHamiltonianSystem): Input Hamiltonian as struqture
prefactor_cutoff (float): Cutoff for the absolute value of the coupling strength of
terms to be considered in the Schrieffer-Wolff transformation.
spin_indices (Set[int]): Set of indices of the dedicated spin-like orbitals.
Raises:
ValueError: Struqture incompatible with HQS Spin Mapper
"""
ERROR_MESSAGE = "Invalid structure of the input system."
self._system_size = int(system.current_number_modes() // 2)
# Obtain the quadratic terms, i.e. 1 creator & 1 annihilator
system_H0 = system.separate_into_n_terms((1, 1))[0]
# Obtain the quartic terms, i.e. 2 creators & 2 annihilators
system_HU = system.separate_into_n_terms((2, 2))[0]
H0_uu = np.zeros((self._system_size, self._system_size), dtype=complex)
H0_dd = np.zeros((self._system_size, self._system_size), dtype=complex)
H0_ud = np.zeros((self._system_size, self._system_size), dtype=complex)
for term in system_H0.keys():
i0 = term.creators()[0]
j0 = term.annihilators()[0]
prefactor = system_H0.get(term)
value = prefactor.real.value + 1j * prefactor.imag.value
# Separate into spin-up and spin-down terms
if i0 % 2 == 0 and j0 % 2 == 0:
H0_uu[i0 // 2, j0 // 2] += value
elif i0 % 2 == 1 and j0 % 2 == 1:
H0_dd[i0 // 2, j0 // 2] += value
else:
H0_ud[i0 // 2, j0 // 2] += value / 2
HU = np.zeros(
(self._system_size, self._system_size, self._system_size, self._system_size),
dtype=complex,
)
for term in system_HU.keys():
# Assign the indices of the tensor [ijkl] -> c^+_u c^+_d c_d c_u
iU, jU = tuple(term.creators())
kU, lU = tuple(term.annihilators())
prefactor = system_HU.get(term)
value = prefactor.real.value + 1j * prefactor.imag.value
if iU % 2 == 0:
if jU % 2 != 1:
raise ValueError(ERROR_MESSAGE)
else:
if jU % 2 != 0:
raise ValueError(ERROR_MESSAGE)
iU, jU = jU, iU
value *= -1.0
if lU % 2 == 0:
if kU % 2 != 1:
raise ValueError(ERROR_MESSAGE)
else:
if kU % 2 != 0:
raise ValueError(ERROR_MESSAGE)
kU, lU = lU, kU
value *= -1.0
# Struqture orbitals are spinless -> thus mapping to spinful orbitals necessary
HU[iU // 2, jU // 2, kU // 2, lU // 2] += value
self._hamiltonians = Hamiltonian_Matrices(H0_uu=H0_uu, H0_dd=H0_dd, H0_ud=H0_ud, HU=HU)
self._site_type: str = "spinful_fermions"
if spin_indices is None:
self._spin_indices: Set[int] = set()
else:
self._spin_indices = spin_indices
self._rotation_matrix = np.eye(self._system_size)
self._prefactor_cutoff = prefactor_cutoff
if np.all(np.isreal(H0_uu)):
self._transformed_hamiltonian = ExpressionSpinful()
self._generator = ExpressionSpinful()
else:
self._transformed_hamiltonian = ExpressionSpinful_complex()
self._generator = ExpressionSpinful_complex()
[docs]
class Transformable_Matrices(Transformable):
r"""Transformable system from input matrices for the Hamiltonian."""
[docs]
def __init__(
self,
hamiltonians: Hamiltonian_Matrices,
system_size: Optional[int] = None,
rotation_matrix: Optional[np.ndarray] = None,
prefactor_cutoff: float = 1e-6,
site_type: str = "spinful_fermions",
spin_indices: Optional[Set[int]] = None,
) -> None:
r"""Construct the transformable system obtained from a matrix collection input.
Args:
hamiltonians (Hamiltonian_Matrices): Collection of matrix descriptions of the system
Hamiltonian
system_size (int): Number of orbitals in the system
rotation_matrix (np.ndarray): Transformation matrix :math:`U_{iq}` to the spin-optimized
basis
prefactor_cutoff (float): Cutoff for the absolute value of the coupling strength of
terms to be considered in the Schrieffer-Wolff transformation.
site_type (str): Type of excitations in the orbitals.
spin_indices (Set[int]): Set of indices of the dedicated spin-like orbitals.
"""
if spin_indices is None:
self._spin_indices: Set[int] = set()
else:
self._spin_indices = spin_indices
self._hamiltonians = hamiltonians
if system_size is None:
self._system_size = self._hamiltonians.H0_uu.shape[0]
else:
self._system_size = system_size
if rotation_matrix is None:
self._rotation_matrix = np.eye(self._system_size)
else:
self._rotation_matrix = rotation_matrix
self._prefactor_cutoff = prefactor_cutoff
self._site_type = site_type
if np.all(np.isreal(self._hamiltonians.H0_uu)):
self._transformed_hamiltonian = ExpressionSpinful()
self._generator = ExpressionSpinful()
else:
self._transformed_hamiltonian = ExpressionSpinful_complex()
self._generator = ExpressionSpinful_complex()
[docs]
class Transformable_QuantumChemistry(Transformable, Supports_Spin_Optimization):
r"""Transformable system from an electronic structure calculation input."""
[docs]
def __init__(
self,
hamiltonians: Hamiltonian_Matrices,
rdm1: Union[np.ndarray, Tuple[np.ndarray, ...]],
rdm2: np.ndarray,
system_size: Optional[int] = None,
rotation_matrix: Optional[np.ndarray] = None,
tolerance: float = 1e-1,
prefactor_cutoff: float = 1e-6,
optimization_steps: int = 4,
site_type: str = "spinful_fermions",
spin_indices: Optional[Set[int]] = None,
) -> None:
r"""Construct the transformable system obtained from ab-initio calculation input.
Args:
hamiltonians (Hamiltonian_Matrices): Collection of matrix descriptions of the system
Hamiltonian
rdm1 (Union[np.ndarray, Tuple[np.ndarray, ...]]): Matrix description of the 1-RDM
rdm2 (np.ndarray): Tensor description of the 2-RDM
system_size (int): Number of orbitals in the system
rotation_matrix (np.ndarray): Transformation matrix :math:`U_{iq}` to the spin-optimized
basis
tolerance (float): Parity tolerance :math:`P_i=(-1 + \delta)` for an orbital to be
spin-like
prefactor_cutoff (float): Cutoff for the absolute value of the coupling strength of
terms to be considered in the Schrieffer-Wolff transformation.
optimization_steps (int): Number of optimization loops.
site_type (str): Type of excitations in the orbitals.
spin_indices (Set[int]): Set of indices of the dedicated spin-like orbitals.
Raises:
ValueError: HU does not conform to the requirements needed for basis rotation
ValueError: The 2-RDM does not conform to the requirements needed for spin optimization
"""
if spin_indices is None:
self._spin_indices: Set[int] = set()
else:
self._spin_indices = spin_indices
self._immutable_indices: Set[int] = set()
self._hamiltonians = hamiltonians
# Reordering the two-electron integrals to conform with second quantization standards
# c^+_s c_s c^+_s' c_s' -> c^+_s c^+_s' c_s' c_s
if len(self._hamiltonians.HU.shape) != 4:
raise ValueError(
"The two-particle integrals need to specified as a four index tensor!"
)
self._hamiltonians.HU = np.einsum("ijkl -> iklj", self._hamiltonians.HU)
if np.all(np.isreal(self._hamiltonians.H0_uu)):
self._transformed_hamiltonian = ExpressionSpinful()
self._generator = ExpressionSpinful()
else:
self._transformed_hamiltonian = ExpressionSpinful_complex()
self._generator = ExpressionSpinful_complex()
self._rdm1 = rdm1
self._rdm2 = rdm2
if len(self._rdm2.shape) != 4:
raise ValueError("The 2-RDM needs to specified as a four index tensor!")
# <c^+_s c_s c^+_s' c_s'> -> <c^+_s c^+_s' c_s' c_s>
self._rdm2 = np.einsum("ijkl -> iklj", self._rdm2)
if system_size is None:
self._system_size = self._hamiltonians.H0_uu.shape[0]
else:
self._system_size = system_size
if rotation_matrix is None:
self._rotation_matrix = np.eye(self._system_size)
else:
self._rotation_matrix = rotation_matrix
self._tolerance = tolerance
self._prefactor_cutoff = prefactor_cutoff
self._optimization_steps = optimization_steps
self._site_type = site_type
# EXPERIMENTAL
# self.energy_shift = 0.0
# Warning if a spin-summed 2RDM has been provided
for i in range(self._system_size):
if self._rdm2[i, i, i, i] > 1.0:
raise ValueError("The provided 2-RDM is not suitable for spin optimization!")
@property
def tolerance(self) -> float:
r"""Return the tolerated parity deviation :math:`\delta` in :math:`P=(-1+\delta)` to be
considered a spin-orbital.
Returns:
float: The stored value for the tolerance :maht:`delta` of the parity
"""
return self._tolerance
@property
def optimization_steps(self) -> int:
r"""Return the number of optimization loops.
Returns:
int: The number of optimization loops
"""
return self._optimization_steps
@property
def immutable_indices(self) -> Set[int]:
r"""Return the orbital indices that should remained unaltered by the optimization.
Returns:
Set[int]: The set of immutable indices
"""
return self._immutable_indices
@immutable_indices.setter
def immutable_indices(self, value: Set[int]) -> None:
r"""Set the orbital indices that should remained unaltered by the optimization.
Args:
value (Set[int]): Indices that should remain unaltered
"""
self._immutable_indices = value
@property
def rdm1(self) -> Union[np.ndarray, Tuple[np.ndarray, ...]]:
r"""Return the matrix descriptions of the 1-RDM (:math:`\langle c^{\dagger}_{i\sigma} c_{j\sigma'}\rangle`).
Returns:
Union[np.ndarray, Tuple[np.ndarray, ...]]: The (spin-resolved) 1-RDM(s)
"""
return self._rdm1
@rdm1.setter
def rdm1(self, value: Union[np.ndarray, Tuple[np.ndarray, ...]]) -> None:
r"""Set the matrix descriptions of the 1-RDM (:math:`\langle c^{\dagger}_{i\sigma} c_{j\sigma'}\rangle`).
Args:
value (np.ndarray): Matrix descriptions of the 1-RDM
"""
self._rdm1 = value
@property
def rdm2(self) -> np.ndarray:
r"""Return the tensor of the 2-RDM
(:math:`\langle c^{\dagger}_{i\uparrow} c^{\dagger}_{j\downarrow} c_{k\downarrow} c_{l\uparrow}\rangle`).
Returns:
np.ndarray: Tensor description of the 2-RDM
"""
return self._rdm2
@rdm2.setter
def rdm2(self, value: np.ndarray) -> None:
r"""Set the tensor description of the 2-RDM
(:math:`\langle c^{\dagger}_{i\uparrow} c^{\dagger}_{j\downarrow} c_{k\downarrow} c_{l\uparrow}\rangle`).
Args:
value (np.ndarray): Tensor description of the 2-RDM
"""
self._rdm2 = value
[docs]
class SpinFinder(Supports_Spin_Optimization):
r"""Data object for use in a spin determination calculation."""
[docs]
def __init__(
self,
rdm1: Union[np.ndarray, Tuple[np.ndarray, ...]],
rdm2: np.ndarray,
tolerance: float = 1e-1,
optimization_steps: int = 4,
spin_indices: Optional[Set[int]] = None,
) -> None:
r"""Construct the object ab-initio calculation input.
Args:
rdm1 (Union[np.ndarray, Tuple[np.ndarray, ...]]): Matrix descriptions of the 1-RDM
rdm2 (np.ndarray): Tensor description of the 2-RDM
tolerance (float): Parity tolerance :math:`delta` in :math:`P=(-1 + \delta)` for an
orbital to be spin-like
optimization_steps (int): Number of optimization loops
spin_indices (Set[int]): Set of indices of the dedicated spinful orbitals
Raises:
ValueError: The 2-RDM does not conform to the requirements needed for spin optimization
"""
if spin_indices is None:
self._spin_indices: Set[int] = set()
else:
self._spin_indices = spin_indices
self._immutable_indices: Set[int] = set()
self._rdm1 = rdm1
self._rdm2 = rdm2
if len(self._rdm2.shape) != 4:
raise ValueError("The 2-RDM needs to specified as a four index tensor!")
# <c^+_s c_s c^+_s' c_s'> -> <c^+_s c^+_s' c_s' c_s>
self._rdm2 = np.einsum("ijkl -> iklj", self._rdm2)
self._system_size = self._rdm2.shape[0]
self._rotation_matrix = np.eye(self._system_size)
self._tolerance = tolerance
self._optimization_steps = optimization_steps
# Warning if a spin-summed 2RDM has been provided
for i in range(self._system_size):
if self._rdm2[i, i, i, i] > 1.0:
raise ValueError(
"The provided 2-RDM is not suitable for spin optimization!"
+ " It is probably summed over both spin directions."
)
@property
def rotation_matrix(self) -> np.ndarray:
r"""Return the rotation matrix that transforms the system to the spin-optimized basis.
Returns:
np.ndarray: The stored rotation matrix :math:`U_{iq}`
"""
return self._rotation_matrix
@rotation_matrix.setter
def rotation_matrix(self, value: np.ndarray) -> None:
r"""Set the rotation matrix that transforms the system to the spin-optimized basis.
Args:
value (np.ndarray): Updated rotation matrix :math:`U_{iq}`
"""
self._rotation_matrix = value
@property
def spin_indices(self) -> Set[int]:
r"""Return the set of indices of the spin-like orbitals.
Returns:
Set[int]: The indices of the spin-like orbitals
"""
return self._spin_indices
@spin_indices.setter
def spin_indices(self, value: Set[int]) -> None:
r"""Set the set of indices of the spin-like orbitals.
Args:
value (Set[int]): Updated indices of the spin-like orbitals
"""
self._spin_indices = value
@property
def system_size(self) -> int:
r"""Return the system size i.e. the number of orbitals.
Returns:
int: The combined size of the system
"""
return self._system_size
@property
def tolerance(self) -> float:
r"""Return the tolerated parity deviation :math:`\delta` in :math:`P=(-1+\delta)` to be
considered a spin-orbital.
Returns:
float: The stored parity tolerance
"""
return self._tolerance
@property
def optimization_steps(self) -> int:
r"""Return the number of optimization loops.
Returns:
int: The number of optimization loops
"""
return self._optimization_steps
@property
def immutable_indices(self) -> Set[int]:
r"""Return the orbital indices that should remained unaltered by the optimization.
Returns:
Set[int]: The set of immutable indices
"""
return self._immutable_indices
@immutable_indices.setter
def immutable_indices(self, value: Set[int]) -> None:
r"""Set the orbital indices that should remained unaltered by the optimization.
Args:
value (Set[int]): Indices that should remain unaltered
"""
self._immutable_indices = value
@property
def rdm1(self) -> Union[np.ndarray, Tuple[np.ndarray, ...]]:
r"""Return the matrix/ces of the 1-RDM (:math:`\langle c^{\dagger}_{i\sigma} c_{j\sigma'}\rangle`).
Returns:
Union[np.ndarray, Tuple[np.ndarray, ...]: The matrix descriptions of the 1-RDM
"""
return self._rdm1
@rdm1.setter
def rdm1(self, value: Union[np.ndarray, Tuple[np.ndarray, ...]]) -> None:
r"""Set the matrix description of the 1-RDM (:math:`\langle c^{\dagger}_{i\sigma} c_{j\sigma'}\rangle`).
Args:
value (np.ndarray): Matrix descriptions of the 1-RDM
"""
self._rdm1 = value
@property
def rdm2(self) -> np.ndarray:
r"""Return the tensor description of the 2-RDM
(:math:`\langle c^{\dagger}_{i\uparrow} c^{\dagger}_{j\downarrow} c_{k\downarrow} c_{l\uparrow}\rangle`).
Returns:
np.ndarray: The tensor description of the 2-RDM
"""
return self._rdm2
@rdm2.setter
def rdm2(self, value: np.ndarray) -> None:
r"""Set the tensor of the 2-RDM
(:math:`\langle c^{\dagger}_{i\uparrow} c^{\dagger}_{j\downarrow} c_{k\downarrow} c_{l\uparrow} \rangle`).
Args:
value (np.ndarray): Tensor of the 2-RDM
"""
self._rdm2 = value