cppqed-multidiagonal 0.4.0
Multidiagonal sparse operator algebra for C++QED
Loading...
Searching...
No Matches
quantumoperator::MultiDiagonal< RANK > Struct Template Reference

A sparse matrix storing an arbitrary set of diagonals, closed under composition, direct product, and Hermitian conjugation. More...

#include <multidiagonal/MultiDiagonal.h>

Public Types

using Dimensions = Extents< RANK >
 
using Offsets = std::array< ptrdiff_t, RANK >
 Signed offsets, one per axis: positive = upper, negative = lower, zero = main diagonal.
 
using Diagonal = MultiArray< dcomp, RANK >
 
using Diagonals = std::map< Offsets, Diagonal >
 Single-level map: signed offsets → diagonal data.
 

Public Member Functions

 MultiDiagonal (const MultiDiagonal &)=delete
 
MultiDiagonal & operator= (const MultiDiagonal &)=delete
 
 MultiDiagonal (MultiDiagonal &&)=default
 
MultiDiagonal & operator= (MultiDiagonal &&)=default
 
 MultiDiagonal (auto &&... args)
 Forwards args to the diagonal map. Constrained, so that a non-const lvalue MultiDiagonal reaches the deleted copy constructor rather than this one.
 
void operator() (double, MultiArrayConstView< dcomp, RANK > psi, MultiArrayView< dcomp, RANK > dpsidt) const
 Accumulates the operator's action on psi into dpsidt: dpsidt(k+o⁻) += diag(k) * psi(k+o⁺) for each diagonal and each entry k, with no factor \(1/i\) (see the operator conventions above). Per axis, rows run in \([o^-,D-o^+)\) and columns in \([o^+,D-o^-)\).
 

Public Attributes

Diagonals diagonals
 

Friends

MultiDiagonal copy (const MultiDiagonal &md)
 Named deep copy.
 
template<size_t RANK2>
auto operator* (const MultiDiagonal< RANK > &md1, const MultiDiagonal< RANK2 > &md2)
 Tensor (direct) product: concatenates the signed offset arrays, reflecting the tensor product structure exactly. Axes of md1 come first.
 

Hermitian conjugation

MultiDiagonal & hermitianConjugate ()
 In-place Hermitian conjugation: negates all offsets and conjugates all diagonal elements. No reindexing of the stored data is needed — see the storage convention in the class documentation.
 
MultiDiagonal & dagger ()
 
MultiDiagonal hermitianConjugateOf (const MultiDiagonal &md)
 
MultiDiagonal twoTimesRealPartOf (const MultiDiagonal &md)
 \(A+A^\dagger = 2\,\mathrm{Re}A\), which is Hermitian.
 
MultiDiagonal twoITimesImagPartOf (const MultiDiagonal &md)
 \(A-A^\dagger = 2i\,\mathrm{Im}A\), which is anti-Hermitian.
 

Algebra

Note
Boost.Operator cannot be used since MultiDiagonal is not copyable.
MultiDiagonal operator- () const
 
MultiDiagonal operator+ () const
 
MultiDiagonal & operator+= (const MultiDiagonal &md)
 In-place addition. New diagonals are inserted; existing ones accumulate element-wise.
 
MultiDiagonal & operator-= (const MultiDiagonal &md)
 
MultiDiagonal & operator*= (scalar auto d)
 
MultiDiagonal & operator/= (scalar auto d)
 
auto operator+ (const MultiDiagonal &md1, const MultiDiagonal &md2)
 
auto operator- (const MultiDiagonal &md1, const MultiDiagonal &md2)
 
auto operator* (scalar auto v, const MultiDiagonal &md)
 
auto operator/ (const MultiDiagonal &md, scalar auto v)
 
Dimensions calculateAndCheckDimensions (const MultiDiagonal &md)
 Derives and validates the Hilbert space dimensions from the invariant \(\text{extent}_i + |o_i| = D_i\). Returns Dimensions{} for an empty operator. Throws if any diagonal is inconsistent.
 
void tag_invoke (const json::value_from_tag &, json::value &jv, const MultiDiagonal &md)
 Boost.JSON serialization: emits an array of {"offsets": [...], "diagonal": {...}} objects.
 

Detailed Description

template<size_t RANK>
struct quantumoperator::MultiDiagonal< RANK >

A sparse matrix storing an arbitrary set of diagonals, closed under composition, direct product, and Hermitian conjugation.

MultiDiagonal<RANK> supersedes Tridiagonal<RANK> from v2, which stored exactly three diagonals per axis — at positions $-K$, $0$, $+K$ for a single shared offset $K$ — and was not closed under composition: the product of two tridiagonal operators is in general pentadiagonal, and with different offsets not tridiagonal at all. MultiDiagonal lifts all these restrictions:

  • Any number of diagonals at arbitrary offsets per axis.
  • operator| (composition) produces new diagonals at all pairwise offset sums — the algebra is closed.
  • operator* (direct product) concatenates offset arrays, reflecting tensor product structure exactly.

Representation

Each diagonal is keyed by a signed offset per axis: positive = upper, negative = lower, zero = main. The offset counts the quanta a diagonal moves along each axis, and the algebra reduces to arithmetic on these counts:

  • composition: \(\text{offset}_{A|B} = \text{offset}_A + \text{offset}_B\) (per axis)
  • Hermitian conjugation: negation of all offsets
  • direct product: concatenation of offset arrays

Storage convention. The diagonal at signed offset \(o\) (per axis, dimension \(D\)) has extent \(D-|o|\), and entry \(k\in[0,D-|o|)\) holds the matrix element

\[ d[k] = A_{k+o^-,\;k+o^+}, \qquad o^\pm = \max(\pm o,0). \]

This min(row,col)-anchored indexing is symmetric under transposition, so Hermitian conjugation negates the key and conjugates the values without reindexing. The invariant \(\text{extent}_i + |o_i| = D_i\) holds for every diagonal and is enforced by calculateAndCheckDimensions; entries that are structurally zero (created e.g. by composition) are stored explicitly as zeros to preserve it. A diagonal with \(|o_i|=D_i\) on some axis has no entries (e.g. \(a\) for a cutoff of 1); it is a structural zero, and application skips it.

Time dependence

MultiDiagonal is time-independent and carries no frequencies. Interaction-picture time dependence — diagonal elements of the form \(\alpha_{m,n} e^{\delta_{m,n} t}\) with complex \(\delta\) — belongs to the consumer: in C++QED the trajectory owns the interaction picture, and where phase tabulation is needed, the physics layer keeps a separate (coefficients, frequencies) pair of MultiDiagonals. This is a deliberate design boundary.

Operator conventions

  • operator() accumulates \(A\psi\) into dpsidt for the stored operator \(A\), with no factor \(1/i\). C++QED's Hamiltonians add \(H/i\cdot\psi\), so they store \(A=H/i\).
  • The stored operator need not be Hermitian: in QJMC and Master equation contexts \(H\) is the full effective Hamiltonian \(H_\text{eff} = H_\text{phys} - \frac{i}{2}\sum_k J_k^\dagger J_k\).

Member Typedef Documentation

◆ Diagonal

◆ Diagonals

template<size_t RANK>
using quantumoperator::MultiDiagonal< RANK >::Diagonals = std::map<Offsets,Diagonal>

Single-level map: signed offsets → diagonal data.

◆ Dimensions

template<size_t RANK>
using quantumoperator::MultiDiagonal< RANK >::Dimensions = Extents<RANK>

◆ Offsets

template<size_t RANK>
using quantumoperator::MultiDiagonal< RANK >::Offsets = std::array<ptrdiff_t,RANK>

Signed offsets, one per axis: positive = upper, negative = lower, zero = main diagonal.

Note
std::array<ptrdiff_t,RANK> has lexicographic operator< out of the box — no custom comparator needed.

Constructor & Destructor Documentation

◆ MultiDiagonal() [1/3]

template<size_t RANK>
quantumoperator::MultiDiagonal< RANK >::MultiDiagonal ( const MultiDiagonal< RANK > &  )
delete

◆ MultiDiagonal() [2/3]

template<size_t RANK>
quantumoperator::MultiDiagonal< RANK >::MultiDiagonal ( MultiDiagonal< RANK > &&  )
default

◆ MultiDiagonal() [3/3]

template<size_t RANK>
quantumoperator::MultiDiagonal< RANK >::MultiDiagonal ( auto &&...  args)
inlineexplicit

Forwards args to the diagonal map. Constrained, so that a non-const lvalue MultiDiagonal reaches the deleted copy constructor rather than this one.

Member Function Documentation

◆ dagger()

template<size_t RANK>
MultiDiagonal & quantumoperator::MultiDiagonal< RANK >::dagger ( )
inline

◆ hermitianConjugate()

template<size_t RANK>
MultiDiagonal & quantumoperator::MultiDiagonal< RANK >::hermitianConjugate ( )
inline

In-place Hermitian conjugation: negates all offsets and conjugates all diagonal elements. No reindexing of the stored data is needed — see the storage convention in the class documentation.

◆ operator()()

template<size_t RANK>
void quantumoperator::MultiDiagonal< RANK >::operator() ( double  ,
MultiArrayConstView< dcomp, RANK >  psi,
MultiArrayView< dcomp, RANK >  dpsidt 
) const
inline

Accumulates the operator's action on psi into dpsidt: dpsidt(k+o⁻) += diag(k) * psi(k+o⁺) for each diagonal and each entry k, with no factor \(1/i\) (see the operator conventions above). Per axis, rows run in \([o^-,D-o^+)\) and columns in \([o^+,D-o^-)\).

The views may be strided and offset, e.g. slices of a larger state, each with its own strides. **psi and dpsidt must not overlap.**

Per diagonal, the addresses of the first row and column are computed once. A loop nest generated at compile time then runs over the axes with axis 0 innermost, i.e. in memory order for both the diagonal and the state. The outer axes only add strides to pointers. The innermost loop multiplies with textbookProduct, so that it vectorizes also without -fcx-limited-range, and has a separate branch for unit stride. An axis 0 of up to four elements (e.g. a qubit) is unrolled at compile time instead, and the loop over axis 1 becomes the innermost one.

See also
Performance of MultiDiagonal for what this loop costs and why.

◆ operator*=()

template<size_t RANK>
MultiDiagonal & quantumoperator::MultiDiagonal< RANK >::operator*= ( scalar auto  d)
inline

◆ operator+()

template<size_t RANK>
MultiDiagonal quantumoperator::MultiDiagonal< RANK >::operator+ ( ) const
inline

◆ operator+=()

template<size_t RANK>
MultiDiagonal & quantumoperator::MultiDiagonal< RANK >::operator+= ( const MultiDiagonal< RANK > &  md)
inline

In-place addition. New diagonals are inserted; existing ones accumulate element-wise.

◆ operator-()

template<size_t RANK>
MultiDiagonal quantumoperator::MultiDiagonal< RANK >::operator- ( ) const
inline

◆ operator-=()

template<size_t RANK>
MultiDiagonal & quantumoperator::MultiDiagonal< RANK >::operator-= ( const MultiDiagonal< RANK > &  md)
inline

◆ operator/=()

template<size_t RANK>
MultiDiagonal & quantumoperator::MultiDiagonal< RANK >::operator/= ( scalar auto  d)
inline

◆ operator=() [1/2]

template<size_t RANK>
MultiDiagonal & quantumoperator::MultiDiagonal< RANK >::operator= ( const MultiDiagonal< RANK > &  )
delete

◆ operator=() [2/2]

template<size_t RANK>
MultiDiagonal & quantumoperator::MultiDiagonal< RANK >::operator= ( MultiDiagonal< RANK > &&  )
default

Friends And Related Symbol Documentation

◆ calculateAndCheckDimensions

template<size_t RANK>
Dimensions calculateAndCheckDimensions ( const MultiDiagonal< RANK > &  md)
friend

Derives and validates the Hilbert space dimensions from the invariant \(\text{extent}_i + |o_i| = D_i\). Returns Dimensions{} for an empty operator. Throws if any diagonal is inconsistent.

◆ copy

template<size_t RANK>
MultiDiagonal copy ( const MultiDiagonal< RANK > &  md)
friend

Named deep copy.

◆ hermitianConjugateOf

template<size_t RANK>
MultiDiagonal hermitianConjugateOf ( const MultiDiagonal< RANK > &  md)
friend

◆ operator* [1/2]

template<size_t RANK>
template<size_t RANK2>
auto operator* ( const MultiDiagonal< RANK > &  md1,
const MultiDiagonal< RANK2 > &  md2 
)
friend

Tensor (direct) product: concatenates the signed offset arrays, reflecting the tensor product structure exactly. Axes of md1 come first.

◆ operator* [2/2]

template<size_t RANK>
auto operator* ( scalar auto  v,
const MultiDiagonal< RANK > &  md 
)
friend

◆ operator+

template<size_t RANK>
auto operator+ ( const MultiDiagonal< RANK > &  md1,
const MultiDiagonal< RANK > &  md2 
)
friend

◆ operator-

template<size_t RANK>
auto operator- ( const MultiDiagonal< RANK > &  md1,
const MultiDiagonal< RANK > &  md2 
)
friend

◆ operator/

template<size_t RANK>
auto operator/ ( const MultiDiagonal< RANK > &  md,
scalar auto  v 
)
friend

◆ tag_invoke

template<size_t RANK>
void tag_invoke ( const json::value_from_tag &  ,
json::value &  jv,
const MultiDiagonal< RANK > &  md 
)
friend

Boost.JSON serialization: emits an array of {"offsets": [...], "diagonal": {...}} objects.

◆ twoITimesImagPartOf

template<size_t RANK>
MultiDiagonal twoITimesImagPartOf ( const MultiDiagonal< RANK > &  md)
friend

\(A-A^\dagger = 2i\,\mathrm{Im}A\), which is anti-Hermitian.

This is \(H/i\) for \(H=i(A-A^\dagger)\), e.g. a coherent drive \(H=i(\eta a^\dagger-\eta^*a)\) with \(A=\eta a^\dagger\).

◆ twoTimesRealPartOf

template<size_t RANK>
MultiDiagonal twoTimesRealPartOf ( const MultiDiagonal< RANK > &  md)
friend

\(A+A^\dagger = 2\,\mathrm{Re}A\), which is Hermitian.

Member Data Documentation

◆ diagonals

template<size_t RANK>
Diagonals quantumoperator::MultiDiagonal< RANK >::diagonals

The documentation for this struct was generated from the following file: