Repository navigation
Releases: WignerQuantumOptics/cppqed-multidiagonal
Release list
v0.4.1 — application performance: two loop shapes
No breaking changes. Applying a MultiDiagonal is faster, and its speed depends far less on where the arrays lie in memory. Results agree with v0.4.0 up to rounding: within a row, the order of summation differs.
Performance: the application loop was rewritten in three steps. Addresses are computed once per diagonal, and a loop nest generated at compile time runs in memory order, axis 0 innermost (#1). The complex multiply-add is done in real arithmetic, so the default build is as fast as with -fcx-limited-range, and an axis 0 of up to four elements (a qubit) is unrolled at compile time (#23). For long axes, operands larger than cache and strided views, the diagonals contributing to a row are summed before one store, so the state is swept once instead of once per diagonal (#1, #25). On the reference machine (i7-7700, GCC 16, Eigen 5.0.1), application is now 2.7–3.6× faster than Eigen's sparse product in cache at rank 1 and with the mode on axis 0, 1.4–2.2× with a qubit on axis 0, and 1.4–2.5× out of cache except with a qubit on axis 0 (0.6–0.9×). At v0.4.0, multi-axis application was 3.4–4.5× slower than Eigen. Where the array placement still matters, in small views, the shape used is faster at any placement than the alternative (#25).
New API: applyByDiagonals and applyByRows, the two loop shapes that operator() chooses between from the view's strides and extents. They are public, so that tests and benchmarks can force either.
Benchmarks: kernels.cc gains case F, where the two shapes cross over, and case G, the placement scan of #25. The performance page (benchmarks/performance.md) records every figure as a median over five processes on a quiet reference machine, together with what remains: a qubit on axis 0 of a state larger than cache, and contiguous slices of power-of-two length out of cache (#1). CI builds the benchmarks without -march=native, which failed with Clang 18 on runners with AVX10.
v0.4.0 — twoITimesImagPartOf, contiguous-view checks, documentation sweep
Breaking: twoTimesImagPartOf is renamed to twoITimesImagPartOf. It returns A − A† = 2i·Im A, which is anti-Hermitian, not 2·Im A as the old name said. The value is unchanged; it is exactly H/i of a coherent drive H = i(ηa† − η*a). The old name is removed rather than deprecated, so existing calls fail to compile instead of silently changing meaning (#22). hermitianConjugateSelf now takes a view, like twoTimesRealPartOfSelf; pass an owning array as ma.mutableView() (#8).
Fixes: both in-place matrix functions index the flat storage, so in debug builds they now reject a sub-block or slice view with std::invalid_argument (checkContiguous, #8). A diagonal of zero extent (|o| = D, e.g. a at cutoff 1) on a non-leading axis made application index out of bounds; it is now skipped as a structural zero. The variadic MultiDiagonal constructor is constrained, so a non-const lvalue reaches the deleted copy constructor. sqrAbs is std::norm (re² + im²) instead of the square of the rounded abs. noInit, zeroInit and the slicing placeholder _ are inline variables, removing a formal ODR violation (#12).
Documentation: application accumulates Aψ and applies no factor 1/i (C++QED stores A = H/i), and the interaction picture belongs to the consumer, not to a derived type (#7). Storage is first-index-fastest while incrementMultiIndex iterates last-index-fastest; the stride docstring said row-major (#13). The README's operator formula had the offset direction reversed and implied a single tensor product; (a†)²a² is a main diagonal; a Performance section states that multi-axis application is 2–2.7× slower than Eigen sparse (#1); integration is via FetchContent. The outdated mainpage.md is gone: the README is the Doxygen main page, read through a sed filter for its math (#6).
v0.3.0 — headers under include/multidiagonal/
Breaking: the headers moved from include/ to include/multidiagonal/ and install there, so consumers write #include <multidiagonal/MultiDiagonal.h>. The flat layout made the library's Traits.h collide with C++QED's own; C++QED now builds against the library (#2).
Fixes: the const overloads of vectorize and matricize failed to compile on first use, because they built a mutable Eigen::Map from a const pointer, a regression in v0.2.0 (#3). The installed package exported the target as cppqed::cppqed-multidiagonal instead of the documented cppqed::multidiagonal, so find_package consumers following the README failed (#21). Clang -Wunused-lambda-capture and -Wignored-qualifiers warnings in MultiArray.h (#10).
Tests: Hermitian conjugation of complex operators against the dense adjoint (deleting the value conjugation used to pass every test), rank-3 application and composition with colliding offsets, the complex directProduct combiner, isEqual on unequal arrays, strides and offset after a serialization round trip; the constructJC benchmark validates before timing (#4).
CI: warnings as errors on GCC and Clang, an ASan+UBSan job, and a downstream consumer job that uses the library via find_package after install and via FetchContent at the pushed commit; runners pinned to ubuntu-24.04 and actions to commit SHAs (#10).