Kossakowski-form dissipator + dissipative pc_step_orbit_rep validation - #179
Closed
AlexSchuckert wants to merge 50 commits into
Closed
AlexSchuckert wants to merge 50 commits into
AlexSchuckert wants to merge 50 commits into
Conversation
|
…daptive Lindbladian evolution Direct Heisenberg-picture evolution O ← exp(dt·L*)O on an adaptively truncated Pauli-string basis: exact in dt within the working basis, no Trotter splitting. LindbladSpec precompiles Hermitian-Pauli (fast diagonal) and general complex Pauli-sum jumps (σ±, amplitude damping, precomputed L†L); pc_step does two-hop leakage enrichment + predictor/ corrector matrix-free exponential via quspin-expm (MIT), with the truncation policy in one PcStepConfig (max_basis rank cap, admit_basis displacement bound, drop_tol prune, tau_add admission filter). Python: ppvm.Lindbladian with numpy hot path + string convenience API, mimalloc global allocator (~50% peak-RSS cut on adaptive paths), two jupytext demos, tests against dense-Liouvillian / closed-form / eig references. Split 2/4 of the CTPP work; the momentum-sector (orbit-rep) evolution follows in split 3. Full history: branch continuous-time-pauli-propagation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two consumers of the translation-symmetry primitive:
Trotter path: PauliSum.symmetry_merge (k=0) and PauliSum.momentum_merge
(k≠0 carried as a real pair, character-weighted fold, |G|-rescaled to
the summing projector so merging after every step is idempotent), with
TranslationGroup and canonicalize_basis_arr{,_complex} /
check_momentum_sector_arr exposed to Python. Inputs validated at the
boundary (ValueError, not panics).
CTPP path: pc_step_orbit_rep — per-step evolution entirely in
orbit-representative form (complex coefficients, phase-aware action,
cached-CSC expm; basis ~|G|× smaller than full-basis evolution,
persisting through every step), with the same PcStepConfig truncation
policy incl. displacement admission.
Split 3/4 of the CTPP work; full history on branch
continuous-time-pauli-propagation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The complete experiment ledgers behind the CTPP design decisions: truncation-scheme comparisons (cap/valve vs displacement vs thresholds), the momentum-vs-real-space study, exact-reference methodology, the L=21/L=41 application exhibits, and every kept or discarded attempt with its measured numbers. Scan drivers and ED reference generators included as text; plots and .npz exact-reference data (~2.5 MB) live on the archive branch continuous-time-pauli-propagation. Split 4/4 of the CTPP work. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rep validation
D*(O) = Σ_nm K_nm (A_n†OA_m − ½{A_n†A_m,O}) from an operator list +
Hermitian PSD pair matrix — exactly equivalent to the eigenmode-jump
representation but O(N²) instead of O(N³) per string (measured
3.4×/8.9×/17.4× per pc_step at N=10/20/30 on the superradiance chain;
criterion bench included). One JumpKind::KossakowskiPair per nonzero
K_nm with a precompiled sandwich table and a one-sided ±½[K·A_n†A_m, p]
commutator fast path; all step/leakage/generator paths inherit it via
compute_action_terms. Python: Lindbladian(..., kossakowski=(ops, K))
with Hermiticity/PSD validation (raises, no silent clipping).
Dissipative pc_step_orbit_rep validated correct out of the box (the
1/|G| projection convention makes the phase-aware action exact for any
equivariant generator, incl. the non-unital Z→I flow): orbit vs
projected-full ≤1e-12, R(t) vs excitation-cascade ED <1e-4, identity
bookkeeping vs closed form, truncated-regime sanity. Physics regression:
N=6 superradiance chain vs exact cascade <1e-4 (vs the fig11 stored
reference: 1.1e-05).
Squashed from archive/kossakowski-dissipator (full history there).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AlexSchuckert
force-pushed
the
kossakowski-dissipator
branch
from
July 16, 2026 12:24
a3ba18f to
d3a8ffa
Compare
AlexSchuckert
changed the base branch from
continuous-time-pauli-propagation
to
split/3-symmetric-evolution
July 16, 2026 12:24
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # crates/ppvm-python-native/src/lindblad.rs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ord) — cargo machete Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The split/3 merge conflicted on the Lindbladian __init__ signature and the markers were committed; resolved by combining the kossakowski kwarg with the tightened jump_terms typing. Full gauntlet green (ty, ruff check+format, 250 tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bitvec implements BitStore for u64 only on 64-bit targets, so the [u64; 2] word storage broke the wasm32 workspace build. The chunk type is now target-gated: two u64 chunks on 64-bit targets (bit-identical to before), four u32 chunks elsewhere — 128 qubits either way. The chunk loops (anti_commutes / pauli_mul) were already width-agnostic. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # crates/ppvm-lindblad/src/lib.rs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AlexSchuckert
force-pushed
the
kossakowski-dissipator
branch
from
July 16, 2026 15:24
ce6e79d to
4992d1e
Compare
Adds the molecular ZULF-NMR dipolar workload (2-local rank-2 tensor ops, block-diagonal K over 5 spatial harmonics, dense high-weight basis strings) as a criterion bench and a fast pc_step_timed profiling example — the A/B harness for the drug-kossakowski autotune campaign. This path stresses the both-sided sandwich, unlike the single-term sigma-minus superradiance bench. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Group the precompiled sandwich table by the left word P_a so P_a.p is computed once per distinct P_a and reused across its P_b partners, instead of one pauli_mul pair per (P_a,P_b) term-pair. No-op for single-term ops (the superradiance sigma-minus case, why it2 in the 07-16 ledger found it noise-level), but for the multi-term 2-local tensors of dipolar relaxation (~4 terms/op) it cuts the both-sided sandwich from 32 to 20 pauli_mul. Same-session A/B on the drug_profile harness (n=10, B=1500, 5 steps): 7039 -> 5765 ms/step = 18% (1.22x). Equivalence tests unchanged (kossakowski_pair_matches_eigenmode_jumps green; 3-spin ZULF dense-vs-koss action 3.3e-15, trajectory 3.2e-14). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The action keeps only the real part, and P_i.p.Q_j / Q_j.p.P_i produce the same Pauli word with conjugate phase, so a Hermitian-conjugate pair collapses to one upper-triangle (n<=m) entry using only the (n,m) products: - both-sided sandwich: double and take the real part; - both-sided anticommutator: dd = 2*Re(K_nm A_n#A_m) (Hermitian sum); - one-sided: +-1/2 [F, p] with F = -2i*Im(K_nm A_n#A_m) (anti-Hermitian difference of the folded conjugate one-sided contributions). Diagonal pairs keep the original single-pair handling. Halves the pair count (P^2 -> P(P+1)/2), and the off-diagonal real-part accumulation drops the complex arithmetic and per-pair imaginary-cancellation bookkeeping. General for any Hermitian PSD K (not just real). Same-session A/B on drug_profile (n=10, B=1500): 5765 -> 2281 ms/step = 2.53x (cumulative from baseline 7039 -> 2281 = 3.08x). kossakowski_pair_matches_eigenmode_jumps and the full lindblad suite green; 3-spin ZULF action 3.3e-15, gemcitabine (N=10) action 1.5e-15 vs dense jumps. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Records the drug-workload Kossakowski campaign (baseline 7039 -> it1 5765 -> it2 2281 ms/step = 3.08x cumulative; 6.0x / 1.3x-RAM representation win over dense jumps on gemcitabine N=10). Introduces a SandwichGroups type alias and type-complexity allows on the bench/example geometry helpers to keep clippy at zero warnings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cargo fmt --all on the bench, profiler example, and the it2 dissipator construction (the pre-commit cargo-fmt hook was red). No logic change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
david-pl
force-pushed
the
split/3-symmetric-evolution
branch
from
September 2, 2026 07:16
36cb95d to
070bc75
Compare
Collaborator
|
Superseded by the split starting with #180 |
david-pl
added a commit
that referenced
this pull request
Sep 28, 2026
Adds the general GKSL form
`D*(O) = Σ_nm K_nm (A_n† O A_m − ½{A_n† A_m, O})` to `ppvm-lindblad`,
alongside the existing jump-operator form, with Python bindings, tests
and benchmarks.
Replaces #179, which was stacked on the pre-split #178 and could not be
reopened usefully: its branch had never been rebased, so its diff
against
the split stack was 160 files and reverted work that #180–#182 had since
landed. This branch is the same feature ported onto `split/3` and
cleaned
up; the original commits are preserved on `kossakowski-dissipator`.
Stacked on #182 — **review after that one settles**, see the note below.
## What's here
| Commit | |
|---|---|
| `7b52cf6b` | core dissipator: `kossakowski.rs`, `PairShape`,
`add_kossakowski` |
| `e6266b52` | superradiance + dipolar-relaxation benchmarks, profiling
example |
| `dc8c6234` | `Lindbladian(..., kossakowski=(ops, K))` Python surface |
| `b52d22cc` | dense-reference and orbit-representative tests |
Conjugate pairs `(n,m)`/`(m,n)` are folded into a single upper-triangle
entry — both sandwiches produce the same output words with conjugate
phase and the action keeps only the real part — halving the pair count.
`PairShape` distinguishes the diagonal entry from a folded off-diagonal
one and carries the term list only the folded case needs, so the
off-diagonal-only path is unreachable on a diagonal pair.
Against the equivalent eigenmode-jump decomposition on a superradiant
chain, the pair-matrix path costs 27.7 ms vs 103 ms at n=10 and 96 ms vs
1.71 s at n=30: the per-string action scales with the number of nonzero
`K_nm` entries instead of carrying an extra factor of `M`.
## Differences from #179
Ported into the `spec`/`basis`/`step`/`algebra` layout `split/2` and
`split/3` introduced, rather than appended to `lib.rs`:
- New `kossakowski.rs` module. `spec.rs` grows 65 lines, not 520.
- `add_kossakowski` (147 lines) split into `validate_k` / `parse_ops` /
`compile_pair` / `compile`; the ~90-line action arm became
`Pair::accumulate` + the one-sided and both-sided halves.
- `PairShape` enum replaces an `off_diag` bool that drove three separate
branches in the hot path. Benchmarked both shapes: the deltas
(+2.4% / −1.3% / +2.2%, n30 p=0.17) are smaller than the drift on the
`eigenmode_*` control benchmarks (+3.4% to +6.6%) in the same run, so
this is noise, not a regression.
- **7 clippy `allow`s removed, 0 added.** `split/3` had none in
`ppvm-lindblad`; three `needless_range_loop` became iterator loops and
four `type_complexity` became two named aliases.
- `precompute_ldagger_l` generalized to `precompute_adag_b(a, b)`, moved
to `algebra.rs` beside `PauliTerm` and a named `COEFF_DROP_TOL` (was a
`1e-14` literal in five places).
- Dropped a dead `let _ = i;`, and row-length errors now report which
row.
- `support_mask` lifted out of the pair loop, where it was being rebuilt
per iteration.
- Deleted the `k_ops` field — `LindbladSpec` state that never outlived
the constructor.
- Did not port the original's `:func:`/`:meth:` docstrings; `split/3`
had already converted those to backticks per AGENTS.md.
## One thing to check
`test_orbit_dissipative.py` failed on arrival. `d8313e0e` on `split/3`
("orbit-rep evolution on stabilized orbits") made rep coefficients
member
coefficients on *every* orbit, where stabilized orbits were previously
divided by `|G|`; the tests encoded the old behaviour and `c_I` came out
exactly 6× the asserted value on a 6-site ring.
I rewrote the reconstruction as `Σ |orbit(rep)| · c_rep` and updated the
identity-bookkeeping expectation to `coeff_I`. The new values match the
closed form exactly. **If `d8313e0e` was not meant to change that
convention, the bug is there and not in these tests** — worth confirming
while reviewing #182.
## Verification
- `cargo test -p ppvm-lindblad` — 8 passed
- `pytest test/lindblad/` — 35 passed, 1 skipped
- `cargo clippy -p ppvm-lindblad --all-targets` — zero warnings
- pre-commit green on all four commits
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Alexander Schuckert <schuckertalexander@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #178 (base:
continuous-time-pauli-propagation).What this adds
Kossakowski-form dissipators:
D*(O) = Σ_nm K_nm (A_n† O A_m − ½{A_n†A_m, O})specified directly by an operator list + Hermitian PSD pair matrix — the natural form for collective decay (every emitter pair shares a channel viaΓ_nmfrom the free-space Green's function). Mathematically identical to eigendecomposingKinto N dense collective jumps, but the per-string action costs one term-pair product per nonzeroK_nminstead of carrying an extra factor of N: O(N²) instead of O(N³).JumpKind::KossakowskiPairper nonzeroK_nm(precompiled sandwich table +A_n†A_mlincomb, per-pair support masks), plugged intocompute_action_terms— soaction/leakage/generator/pc_step/pc_step_orbit_repall inherit it. Includes a one-sided fast path: strings touching only one of a pair's two sites collapse to±½[K·A_n†A_m, p](4 Pauli products instead of 12; the dominant case for local strings under dense K).Lindbladian(..., kossakowski=(ops, K)), validating Hermiticity and positive semidefiniteness (raises — no silent clipping). Coexists withjump_terms.Dissipative
pc_step_orbit_repvalidation: the orbit path applies jump/Kossakowski terms correctly with no code change. Under the 1/|G| projection convention ofcanonicalize_basis_arr_complex(c_rep = coeff_word/|Stab|), the phase-aware action is exact for any translation-equivariant generator, including transitions between orbits of different sizes (the non-unital Z→I flow). Corollary (documented + tested): a translation-invariant {I,Z} observable reads out as|G|·Σ c_rep.Numbers
Measured per-
pc_stepwall (criterion, B=4096, superradiance chain d=0.1λ₀), eigenmode jumps vs Kossakowski:(pre-optimization Kossakowski walls; two further kept optimizations take another ~40% off — full history in
docs/autotune/2026-07-16-kossakowski/).Validation
🤖 Generated with Claude Code