|
cppqed-multidiagonal 0.4.0
Multidiagonal sparse operator algebra for C++QED
|
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 | |
| |
| 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. | |
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:
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.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:
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.
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() 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\).| using quantumoperator::MultiDiagonal< RANK >::Diagonal = MultiArray<dcomp,RANK> |
| using quantumoperator::MultiDiagonal< RANK >::Diagonals = std::map<Offsets,Diagonal> |
Single-level map: signed offsets → diagonal data.
| using quantumoperator::MultiDiagonal< RANK >::Offsets = std::array<ptrdiff_t,RANK> |
Signed offsets, one per axis: positive = upper, negative = lower, zero = main diagonal.
std::array<ptrdiff_t,RANK> has lexicographic operator< out of the box — no custom comparator needed.
|
delete |
|
default |
|
inlineexplicit |
Forwards args to the diagonal map. Constrained, so that a non-const lvalue MultiDiagonal reaches the deleted copy constructor rather than this one.
|
inline |
|
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.
|
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.
|
inline |
|
inline |
|
inline |
In-place addition. New diagonals are inserted; existing ones accumulate element-wise.
|
inline |
|
inline |
|
inline |
|
delete |
|
default |
|
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.
|
friend |
Named deep copy.
|
friend |
|
friend |
Tensor (direct) product: concatenates the signed offset arrays, reflecting the tensor product structure exactly. Axes of md1 come first.
|
friend |
|
friend |
|
friend |
Boost.JSON serialization: emits an array of {"offsets": [...], "diagonal": {...}} objects.
|
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\).
|
friend |
\(A+A^\dagger = 2\,\mathrm{Re}A\), which is Hermitian.
| Diagonals quantumoperator::MultiDiagonal< RANK >::diagonals |