Source code for spinguin.api.basis
"""
This module provides the Basis class which is assigned as a part of `SpinSystem`
object upon its instantiation. Here is an example of accessing the most
important functionality of the class::
import spinguin as sg # Import the package
spin_system = sg.SpinSystem(["1H"]) # Create an example spin system
spin_system.basis.max_spin_order = 1 # Set the maximum spin order
spin_system.basis.build() # Build the basis set
"""
# Referencing SpinSystem class
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from spinguin.api.spin_system import SpinSystem
# Imports
import numpy as np
import scipy.sparse as sp
import warnings
from spinguin.core.states import state_to_truncated_basis
from spinguin.core.superoperators import sop_to_truncated_basis
from spinguin.core.basis import make_basis, truncate_basis_by_coherence
from spinguin.core.la import isvector
[docs]
class Basis:
"""
Basis class manages the basis set of a spin system. Most importantly, the
basis set contains the information on the truncation of the basis set and is
responsible for building and making changes to the basis set.
"""
# Basis set properties
_basis: np.ndarray = None
_max_spin_order: int = None
_spin_system: SpinSystem = None
def __init__(self, spin_system: SpinSystem):
print("Basis set has been initialized with the following defaults:")
print(f"max_spin_order: {self.max_spin_order}\n")
# Store a reference to the SpinSystem
self._spin_system = spin_system
@property
def dim(self) -> int:
"""Dimension of the basis set."""
return self.basis.shape[0]
@property
def max_spin_order(self) -> int:
"""
Specifies the maximum number of a active spins that are included in the
product operators that constitute the basis set. Must be at least 1 and
not larger than the number of spins in the system.
"""
return self._max_spin_order
@property
def basis(self) -> np.ndarray:
"""
Contains the actual basis set as an array of dimensions (N, M) where
N is the number of states in the basis and M is the number of spins in
the system. The basis set is constructed from Kronecker products of
irreducible spherical tensor operators, which are indexed using integers
starting from 0 with increasing rank `l` and decreasing projection `q`:
- 0 --> T(0, 0)
- 1 --> T(1, 1)
- 2 --> T(1, 0)
- 3 --> T(1, -1) and so on...
"""
return self._basis
@max_spin_order.setter
def max_spin_order(self, max_spin_order):
if max_spin_order < 1:
raise ValueError("Maximum spin order must be at least 1.")
if max_spin_order > self._spin_system.nspins:
raise ValueError("Maximum spin order must not be larger than "
"the number of spins in the system.")
self._max_spin_order = max_spin_order
print(f"Maximum spin order set to: {self.max_spin_order}\n")
[docs]
def build(self):
"""
Builds the basis set for the spin system. Prior to building the basis,
the maximum spin order should be defined. If it is not defined, it is
set equal to the number of spins in the system (may be very slow)!
"""
# If maximum spin order is not specified, raise a warning and set it
# equal to the number of spins
if self.max_spin_order is None:
warnings.warn("Maximum spin order not specified. "
"Defaulting to the number of spins.")
self.max_spin_order = self._spin_system.nspins
# Build the basis
self._basis = make_basis(spins = self._spin_system.spins,
max_spin_order = self.max_spin_order)
[docs]
def truncate_by_coherence(
self,
coherence_orders: list,
*objs: np.ndarray | sp.csc_array
) -> None | np.ndarray | sp.csc_array | tuple[np.ndarray | sp.csc_array]:
"""
Truncates the basis set by retaining only the product operators that
correspond to coherence orders specified in the `coherence_orders` list.
Optionally, superoperators or state vectors can be given as input. These
will be converted to the truncated basis.
Parameters
----------
coherence_orders : list
List of coherence orders to be retained in the basis.
Returns
-------
objs_transformed : ndarray or csc_array or tuple
Superoperators and state vectors transformed into the truncated
basis.
"""
# Truncate the basis and obtain the index map
truncated_basis, index_map = truncate_basis_by_coherence(
basis = self.basis,
coherence_orders = coherence_orders
)
# Update the basis
self._basis = truncated_basis
# Optionally, convert the superoperators and state vectors to the
# truncated basis
if objs:
objs_transformed = []
for obj in objs:
# Consider state vectors
if isvector(obj):
objs_transformed.append(state_to_truncated_basis(
index_map=index_map,
rho=obj))
# Consider superoperators
else:
objs_transformed.append(sop_to_truncated_basis(
index_map=index_map,
sop=obj
))
# Convert to tuple or just single value
if len(objs_transformed) == 1:
objs_transformed = objs_transformed[0]
else:
objs_transformed = tuple(objs_transformed)
return objs_transformed