State vectors
StateVector
concept
Let us say we want to make type SV
a libcommute-compatible state
vector type so that linear operators can act on instances
of SV
. For this we have to make SV
model the StateVector
concept.
In a nutshell, a StateVector
type is a one-dimensional array of numbers
(quantum amplitudes) allowing integer indexing and implementing a certain
interface. Elements of the array do not have to be stored contiguously.
Acceptable index values must be at least 64-bit wide unsigned integers since
libcommute uses the following type for basis state indexing.
-
using sv_index_type = std::uint64_t
Defined in <libcommute/loperator/state_vector.hpp>
Type of basis state indices.
The table below shows the interface (a set of free functions and a
metafunction) that needs be implemented for an object sv of type
SV
.
libcommute provides an implementation of the StateVector
concept for
std::vector
(see <libcommute/loperator/state_vector.hpp>).
Function/metafunction |
Description |
Implementation for std::vector<T> |
---|---|---|
element_type<SV>::type |
Type of the elements. |
T |
get_element(sv, n) |
Return the n-th element of sv. |
sv[n] |
update_add_element(sv, n, value) |
Add a value of some type |
sv[n] += value or sv[n] = sv[n] + value The compound-assignment from type |
set_zeros(sv) |
Fill sv with zeros. |
std::fill(sv.begin(), sv.end(), zero). The zero value is created by make_const(0) as described in “Custom scalar types”. |
zeros_like(sv) |
Return an object of the same type and size as sv but filled with zeros. |
Creates a new object as std::vector<T>(sv.size(), zero). |
foreach(sv, f) |
Apply a function-like object f to all basis state index/non-zero element pairs (n, a) in sv. |
In a for-loop, calls f(n, a) for all non-zero elements a as detected by is_zero() (see “Custom scalar types”). |
Inclusion of <libcommute/loperator/state_vector_eigen3.hpp> makes some
Eigen 3 types (column vectors,
vector blocks,
column-like matrix blocks and one-dimensional Eigen::Map views)
compatible with the StateVector
concept as well.
Sparse state vector
sparse_state_vector
is a state vector that saves memory by storing only
the non-zero elements. It is essentially a wrapper around
std::unordered_map
modelling the StateVector
concept. Here, we show
only the part of its interface not covered by StateVector
.
-
template<typename ScalarType>
class sparse_state_vector State vector with a sparse storage of elements (quantum amplitudes). ScalarType is the type of the elements.
-
sparse_state_vector() = delete
-
sparse_state_vector(sv_index_type size)
Construct a zero (empty) sparse vector with a given size – dimension of the corresponding Hilbert space.
-
sv_index_type size() const
Size (dimension) of the vector.
-
ScalarType &operator[](sv_index_type n)
Access the n-th element. If it is zero (missing from the storage), then a new value-initialized element will be inserted and a reference to it will be returned.
Warning
Improper use of this method may result in zero elements being stored in the unordered map. Only the non-zero values should be assigned to the references returned by it.
-
sv_index_type n_nonzeros() const
Get the number of non-zero (stored) elements.
-
void prune()
Remove all zero elements (as defined by scalar_traits<ScalarType>::is_zero()) from the unordered map.
-
template<typename UnaryPredicate>
void prune(UnaryPredicate &&p) Remove unordered map elements (amplitudes) for which predicate p returns
true
.
-
sparse_state_vector() = delete
Mapped basis view
mapped_basis_view
is another utility type modelling the StateVector
concept. It is a view of a state vector, which translates basis state
index arguments of get_element()
and update_add_element()
according to a predefined map sv_index_type
-> sv_index_type
.
The element access functions throw std::out_of_range
if their index
argument is missing from the map.
mapped_basis_view
can be used in situations
where a linear operator acts in a small subspace of
a full Hilbert space, and it is desirable to store vector components only within
that subspace. Such a situation naturally emerges when working with
invariant subspaces of operators.
-
template<typename StateVector, bool Ref = true>
class mapped_basis_view View of a
StateVector
object that translates basis state indices according to a certain mapping.StateVector
- type of the underlying state vector object. Defining a read-only view (such that prohibits update_add_element() operations) requires using aconst
-qualified type here. For example, one can useStateVector = std::vector<double>
for a read-write view, andStateVector = const std::vector<double>
for a read-only view.Ref
- by default,mapped_basis_view
stores a reference to the underlying state vector. Setting this option tofalse
will result in a copy being created and stored instead. This feature can be useful when the underlying type is already a view-like object similar toEigen::Map
.
The mapped basis views should always be constructed by means of a special
factory class basis_mapper
and its methods
basis_mapper:: make_view()
/basis_mapper::make_const_view()
.
-
class basis_mapper
Factory class for
mapped_basis_view
.Constructors
-
basis_mapper(std::vector<sv_index_type> const &basis_state_indices)
Build a mapping from a list of basis states basis_state_indices to their positions within the list.
std::vector<sv_index_type> basis_indices{3, 5, 6}; basis_mapper mapper(basis_indices); // Views created by 'mapper' will translate basis state indices // according to // 0 -> std::out_of_range // 1 -> std::out_of_range // 2 -> std::out_of_range // 3 -> 0 // 4 -> std::out_of_range // 5 -> 1 // 6 -> 2 // 7 -> std::out_of_range // ...
-
template<typename HSType, typename LOpScalarType, int... LOpAlgebraIDs>
basis_mapper(loperator<LOpScalarType, LOpAlgebraIDs...> const &O, HSType const &hs) Build a mapping from a set of all basis states contributing to \(\hat O|0\rangle\).
Operator O acts in the Hilbert space hs. \(|0\rangle\) is the basis state with index 0 (‘vacuum’ state in the case of fermions and bosons). Mapped values are assigned continuously starting from 0 without any specific order.
-
template<typename HSType, typename LOpScalarType, int... LOpAlgebraIDs>
basis_mapper(std::vector<loperator<LOpScalarType, LOpAlgebraIDs...>> const &O_list, HSType const &hs, unsigned int N) Given a list of operators \(\{\hat O_1, \hat O_2, \hat O_3, \ldots, \hat O_M\}\), build a mapping from all basis states contributing to all states \(\hat O_1^{n_1} \hat O_2^{n_2} \ldots \hat O_M^{n_M} |0\rangle\), where \(n_m \geq 0\) and \(\sum_{m=1}^M n_M = N\).
Operators in O_list act in the Hilbert space hs. \(|0\rangle\) is the basis state with index 0 (‘vacuum’ state in the case of fermions and bosons). Mapped values are assigned continuously starting from 0 without any specific order.
This constructor is useful to create a mapping from a fixed-particle-number subspace of a fermionic/bosonic Hilbert space.
mapped_basis_view
factory functions-
template<typename StateVector>
mapped_basis_view<StateVector> make_view(StateVector &&sv) const -
template<typename StateVector>
mapped_basis_view<StateVector const> make_const_view(StateVector &&sv) const Make a read/write or constant view of sv. Constant views will not be accepted by
update_add_element()
. If sv is not an lvalue reference, the resulting view will hold a copy of sv.Warning
To reduce memory footprint,
mapped_basis_view
objects store a reference to the basis index map owned by their parentbasis_mapper
object. For this reason, the views should never outlive the mapper.
Other methods
-
sv_index_type size() const
Number of elements in the index map.
-
std::unordered_map<sv_index_type, sv_index_type> const &map() const
Direct access to the underlying index map.
-
std::unordered_map<sv_index_type, sv_index_type> inverse_map() const
Build and return an inverse index map. Depending on map’s size, building the inverse can be an expensive operation. Calling this method on a non-invertible map is undefined behavior.
-
basis_mapper(std::vector<sv_index_type> const &basis_state_indices)
N-fermion sector views
There are two more specialized flavours of the basis mapping views called
\(N\)-fermion sector views and \(N\)-fermion multisector views. They
can come in handy when working with particle-number preserving models with
fermions. If a model involves \(M\) fermionic degrees of freedom, then
storing a basis state index map for mapped_basis_view
requires
exponentially much memory, \(O(2^M)\).
It is possible to alleviate the memory consumption problem by employing a
(somewhat slower) algorithm that ranks bit patterns in the binary representation
of a basis state index. A computed rank is then used to index into the
\(N\)-fermion (multi)sector.
-
template<typename StateVector, bool Ref = true, typename RankingAlgorithm = combination_ranking>
class n_fermion_sector_view Defined in <libcommute/loperator/n_fermion_sector_view.hpp>
View of a
StateVector
object that translates basis state indices from a full Hilbert space to its subspace (sector) with a fixed total occupation of fermionic degrees of freedom \(N\). The full Hilbert space does not have to be purely fermionic.StateVector
- type of the underlying state vector object. Defining a read-only view (such that prohibits update_add_element() operations) requires using aconst
-qualified type here. For example, one can useStateVector = std::vector<double>
for a read-write view, andStateVector = const std::vector<double>
for a read-only view.Ref
- by default,n_fermion_sector_view
stores a reference to the underlying state vector. Setting this option tofalse
will result in a copy being created and stored instead. This feature can be useful when the underlying type is already a view-like object similar toEigen::Map
.RankingAlgorithm
- one of the types implementing bit pattern ranking.-
template<typename SV, typename HSType>
n_fermion_sector_view(SV &&sv, HSType const &hs, unsigned int N) Construct a view of the state vector sv, defined in the N-fermion sector of the full Hilbert space hs.
-
sv_index_type map_index(sv_index_type index) const
Translate a basis state index from the full Hilbert space to the sector.
-
template<typename SV, typename HSType>
-
template<typename HSType>
struct sector_descriptor Description of an \(N\)-fermion sector defined over a subset of fermionic degrees of freedom.
HSType
- type of the full Hilbert space this sector belongs to.-
std::set<typename HSType::index_types> indices
Set of indices corresponding to the relevant fermionic degrees of freedom.
-
unsigned int N
Total occupation of the sector.
-
std::set<typename HSType::index_types> indices
-
template<typename StateVector, bool Ref = true, typename RankingAlgorithm = combination_ranking>
class n_fermion_multisector_view Defined in <libcommute/loperator/n_fermion_sector_view.hpp>
View of a
StateVector
object that translates basis state indices from a full Hilbert space to an \(N\)-fermion multisector. A multisector is a set of all basis states, which have \(N_1\) particles within a subset of fermionic modes \(\{S_1\}\), \(N_2\) particles within another subset \(\{S_2\}\) and so on. There can be any number of individual pairs \((\{S_i\}, N_i)\) (sectors contributing to the multisector) as long as all subsets \(\{S_i\}\) are disjoint. The full Hilbert space does not have to be purely fermionic.It is advised to use
n_fermion_sector_view
instead, if there is only one contributing sector that also spans all fermionic degrees of freedom.StateVector
- type of the underlying state vector object. Defining a read-only view (such that prohibits update_add_element() operations) requires using aconst
-qualified type here. For example, one can useStateVector = std::vector<double>
for a read-write view, andStateVector = const std::vector<double>
for a read-only view.Ref
- by default,n_fermion_multisector_view
stores a reference to the underlying state vector. Setting this option tofalse
will result in a copy being created and stored instead. This feature can be useful when the underlying type is already a view-like object similar toEigen::Map
.RankingAlgorithm
- one of the types implementing bit pattern ranking.-
template<typename SV, typename HSType>
n_fermion_multisector_view(SV &&sv, HSType const &hs, std::vector<sector_descriptor<HSType>> const §ors) Construct a view of the state vector sv, defined in the \(N\)-fermion multisector of the full Hilbert space hs. The multisector is defined via a list of contributing sectors (list of \((\{S_i\}, N_i)\) pairs).
-
sv_index_type map_index(sv_index_type index) const
Translate a basis state index from the full Hilbert space to the multisector.
-
template<typename SV, typename HSType>
Besides n_fermion_sector_view
and n_fermion_multisector_view
,
<libcommute/loperator/n_fermion_sector_view.hpp> defines a few supplemental
utility functions that help working with (multi)sectors.
-
template<typename StateVector, typename HSType>
auto make_nfs_view(StateVector &&sv, HSType const &hs, unsigned int N) -
template<typename StateVector, typename HSType>
auto make_const_nfs_view(StateVector &&sv, HSType const &hs, unsigned int N) Make and return a read/write or constant N-fermion sector view of sv within the full Hilbert space hs. If sv is not an lvalue reference, the resulting view will hold a copy of sv. A returned view uses
combination_ranking
as its bit pattern ranking algorithm.
-
template<typename StateVector, typename HSType>
auto make_nfms_view(StateVector &&sv, HSType const &hs, std::vector<sector_descriptor<HSType>> const §ors) -
template<typename StateVector, typename HSType>
auto make_const_nfms_view(StateVector &&sv, HSType const &hs, std::vector<sector_descriptor<HSType>> const §ors) Make and return a read/write or constant \(N\)-fermion multisector view of sv within the full Hilbert space hs. The multisector is defined via a list of contributing sectors (list of \((\{S_i\}, N_i)\) pairs). If sv is not an lvalue reference, the resulting view will hold a copy of sv. A returned view uses
combination_ranking
as its bit pattern ranking algorithm.
-
template<typename HSType>
sv_index_type n_fermion_sector_size(HSType const &hs, unsigned int N) Size of the N-fermion sector within the full Hilbert space hs.
-
template<typename HSType>
sv_index_type n_fermion_multisector_size(HSType const &hs, std::vector<sector_descriptor<HSType>> const §ors) Size of the \(N\)-fermion multisector within the full Hilbert space hs. The multisector is defined via a list of contributing sectors (list of \((\{S_i\}, N_i)\) pairs).
-
template<typename HSType>
std::vector<sv_index_type> n_fermion_sector_basis_states(HSType const &hs, unsigned int N) Build and return a list of basis state indices forming the N-fermion sector within the full Hilbert space hs. The order of the indices in the list is consistent with the results of
n_fermion_sector_view::map_index()
.auto basis_states = n_fermion_sector_basis_states(hs, N); auto view = n_fermion_sector_view(st, hs, N); for(sv_index_type n = 0; n < basis_states.size(); ++n) { view.map_index(basis_states[n]) == n; // true for all n }
-
template<typename HSType>
std::vector<sv_index_type> n_fermion_multisector_basis_states(HSType const &hs, std::vector<sector_descriptor<HSType>> const §ors) Build and return a list of basis state indices forming an \(N\)-fermion multisector within the full Hilbert space hs. The multisector is defined via a list of contributing sectors (list of \((\{S_i\}, N_i)\) pairs). The order of the indices in the list is consistent with the results of
n_fermion_multisector_view::map_index()
.auto basis_states = n_fermion_multisector_basis_states(hs, sectors); auto view = n_fermion_multisector_view(st, hs, sectors); for(sv_index_type n = 0; n < basis_states.size(); ++n) { view.map_index(basis_states[n]) == n; // true for all n }
The following classes implement the three ranking algorithms described in [WH22]. They precompute and store a certain amount of data in order to speed up calculations.
-
class combination_ranking
Defined in <libcommute/loperator/n_fermion_sector_view.hpp>
The ranking algorithm based on the combinatorial number system. The \(N\)-fermion (multi)sector view types use this algorithm by default. The storage space required by this class scales as \(O(M \min(N, M - N))\), where \(M\) is the total number of the fermionic degrees of freedom.
-
class combination_ranking
-
template<unsigned int R>
class staggered_ranking Defined in <libcommute/loperator/n_fermion_sector_view.hpp>
The improved combinatorial ranking with staggered lookup and a chunk size of R bits. The storage space required by this class scales as \(O\left(2^R (M-R+2)(\frac{M}{2R}+1)\right)\), where \(M\) is the total number of the fermionic degrees of freedom.
-
template<unsigned int R>
“Trie-based ranking of quantum many-body states”, M. Wallerberger and K. Held, Phys. Rev. Research 4, 033238 (2022), https://doi.org/10.1103/PhysRevResearch.4.033238