Linear operators
libcommute’s linear operators implement action of polynomial expressions on state vectors in finitedimensional Hilbert spaces. Linear operator classes are core devices used to build exact diagonalization routines based on libcommute.
The linear operators come in two flavors, simple (loperator
)
and parametric (parametric_loperator
).
Simple linear operator
Simple linear operators are instances of class template loperator
.
Let us assume that expr is a real expression involving only the predefined algebras (fermions, bosons and spins), and hs is a Hilbert space object compatible with expr. Then creating a simple linear operator is as easy as
auto L = libcommute::make_loperator(expr, hs);
Futhermore, let us declare a couple of objects that model the state vectors concept (in this particular case, they are standard vectors)
std::vector<double> psi(hs.dim());
std::vector<double> phi(hs.dim());
The following code fragment shows three (nearly) equivalent ways to act with our operator on a state, \(\phi\rangle = \hat L \psi\rangle\).
// Function call syntax
phi = L(psi);
// Multiplication syntax
phi = L * psi;
// 'Inplace' action syntax
L(psi, phi);
Although all three forms are semantically equivalent, the last one is faster as it eliminates the need for a temporary object to store \(\hat L\psi\rangle\).
Note
An important note must be made about various algebra support in
loperator
. Expressions can dynamically accommodate new algebras via
inheritance from the polymorphic base generator
. Unlike them, linear
operators represent action of a certain set of algebras that is fixed at
compile time. This design decision allows to remove the virtual function call
overhead from the hottest parts of codes, where linear operators are
repeatedly applied to state vectors. Template parameter pack
loperator::AlgebraIDs
(sorted list of integers) determines what
algebras a given linear operator object supports. make_loperator()()
returns operators covering all three predefined algebras. For the sake of
optimization, it may make sense to manually create loperator
objects
with a more restricted set of IDs if some of the predefined algebras will
appear in expressions.

template<typename ScalarType, int... AlgebraIDs>
class loperator Defined in <libcommute/loperator/loperator.hpp>
Linear operator representation of a polynomial expression acting in a finitedimensional Hilbert space.
ScalarType
 coefficient type of the corresponding polynomial expression.AlgebraIDs
 IDs of algebras supported by this object.Constructor

template<typename ...IndexTypes>
loperator(expression<scalar_type, IndexTypes...> const &expr, hilbert_space<IndexTypes...> const &hs) Construct from an expression and a Hilbert space.
Copy/moveconstructors and assignments
Action on a state vector

template<typename StateVector>
StateVector operator()(StateVector const &psi) const 
template<typename StateVector>
StateVector operator*(StateVector const &psi) const Act on a state vector psi and return the result \(\hat L\psi\rangle\).

template<typename SrcStateVector, typename DstStateVector>
void operator()(SrcStateVector &&psi, DstStateVector &&phi) const Act on a state vector psi and write the result into phi, \(\phi\rangle = \hat L \psi\rangle\). This method is faster than the previous two because the result is written directly into phi without making a temporary object.

template<typename ...IndexTypes>

template<typename ScalarType, typename ...IndexTypes>
loperator<ScalarType, fermion, boson, spin> make_loperator(expression<ScalarType, IndexTypes...> const &expr, hilbert_space<IndexTypes...> const &hs) Defined in <libcommute/loperator/loperator.hpp>
A helper factory function that constructs an
loperator
instance. This function is a more convenient equivalent ofloperator
’s constructor.
Parametric linear operator
Parametric linear operators are similar to the simple ones with one important distinction. They represent polynomial expressions with coefficients that support functionlike call syntax. This generalization can be of relevance for expressions that depend on various parameters (hence the name). A rather common example here is when the Hamiltonian of a quantum system explicitly depends on time or some external fields.
When a parametric operator acts on a state, it takes a list of extra arguments. These extra arguments are passed to the callable coefficients of the corresponding expression and their return values are used to eventually form the output state vector. In other words, the extra arguments are effectively ‘substituted’ into the expression.
// Coefficients of 'expr' depend on one real parameter, i.e. its
// ScalarType implements the call operator taking one 'double' argument
// and returning a 'double'.
// Create a parametric linear operator
auto PL = libcommute::make_param_loperator(expr, hs);
// Input and output state vectors
std::vector<double> psi(hs.dim());
std::vector<double> phi(hs.dim());
//
// Substitute 2.0 into the expression and act on 'psi'
//
// Function call syntax
phi = PL(psi, 2.0);
// 'Inplace' action syntax
PL(psi, phi, 2.0);
Note
Omitting the extra arguments will result in the coefficients being called without arguments.
There is also an option to optimize the substitution process by preallocating storage for the coefficient return values.
std::vector<double> evaluated_coeffs;
// Only the first call will cause memory allocation
PL.act_and_store_coeffs(psi, phi, evaluated_coeffs, .0);
PL.act_and_store_coeffs(psi, phi, evaluated_coeffs, 1.0);
PL.act_and_store_coeffs(psi, phi, evaluated_coeffs, 2.0);
PL.act_and_store_coeffs(psi, phi, evaluated_coeffs, 3.0);
Finally, one can permanently transform a parametric operator into the nonparametric form.
// Substitute 4.0 into PL and save the result as a simple linear operator
auto L = PL.at(4.0);
For an extensive example of parametric_loperator
’s use have a look at
“Custom scalar types and parametric_loperator”.

template<typename ScalarType, int... AlgebraIDs>
class parametric_loperator Defined in <libcommute/loperator/loperator.hpp>
Linear operator representation of a polynomial expression acting in a finitedimensional Hilbert space. This class supports parameter substitution upon acting on a state.
Constructor

template<typename ...IndexTypes>
parametric_loperator(expression<scalar_type, IndexTypes...> const &expr, hilbert_space<IndexTypes...> const &hs) Construct from an expression and a Hilbert space.
Copy/moveconstructors and assignments

parametric_loperator(parametric_loperator const&) = default

parametric_loperator(parametric_loperator&&) noexcept = default

parametric_loperator &operator=(parametric_loperator const&) = default

parametric_loperator &operator=(parametric_loperator&&) noexcept = default
Action on a state vector

template<typename StateVector, typename ...CoeffArgs>
StateVector operator()(StateVector const &psi, CoeffArgs&&... args) const Act on a state vector psi and return the result \(\hat L(\text{args}\ldots)\psi\rangle\).

template<typename StateVector, typename ...CoeffArgs>
void operator()(StateVector const &psi, StateVector &phi, CoeffArgs&&... args) const Act on a state vector psi and write the result into phi, \(\phi\rangle = \hat L(\text{args}\ldots) \psi\rangle\). This method is faster than the previous one because the result is written directly into phi without making a temporary object.

template<typename StateVector, typename ...CoeffArgs>
void act_and_store_coeffs(StateVector const &psi, StateVector &phi, std::vector<evaluated_coeff_t<CoeffArgs...>> &evaluated_coeffs, CoeffArgs&&... args) const
Similar to the previous method, but using the external vector evaluated_coeffs to store coefficient values after parameter substitution. When called multiple times, only the first invocation will resize evaluated_coeffs and allocate memory.

template<typename ...IndexTypes>

template<typename ScalarType, typename ...IndexTypes>
parametric_loperator<ScalarType, fermion, boson, spin> make_param_loperator(expression<ScalarType, IndexTypes...> const &expr, hilbert_space<IndexTypes...> const &hs) Defined in <libcommute/loperator/loperator.hpp>
A helper factory function that constructs a
parametric_loperator
instance. This function is a more convenient equivalent ofparametric_loperator
’s constructor.