feat(propagator): ✨ read individual evolved coefficients by term - #337
Open
JoshCudbyAlgo wants to merge 3 commits into
Open
feat(propagator): ✨ read individual evolved coefficients by term#337JoshCudbyAlgo wants to merge 3 commits into
JoshCudbyAlgo wants to merge 3 commits into
Conversation
`evolved_operator` decodes the whole evolved operator: it enumerates the operator index, materialises a key per surviving term, and builds a term dictionary. `evolved_operator_coefficients` instead probes the index with the terms the caller names, so the decode costs one entry per term *requested* rather than one per term the operator carries. Reading a few amplitudes out of an evolved state (Schrodinger picture) is the motivating case. The graph contraction is unchanged, and still the dominant cost. Contracts: a term the operator does not carry reads back as 0; there is no atol, since the caller named the terms it wants and magnitude filtering would silently zero some of them; the result is positional; and it is rank-local, exactly as `evolved_operator` is. Terms must be canonical monomials, as the constructor's initial_operator keys must be. The engine keys terms by an order-insensitive bitset, so a raw `(1, 0)` would resolve to the row of `(0, 1)` and read back its coefficient without the anticommutation sign -- which a lookup has no coefficient of its own to carry. The Majorana front-end therefore validates raw sequences through `Majorana` rather than normalizing them; use `Majorana.from_unsorted` and apply the sign it returns. Under partitions each partition probes on its own master thread into its own vector, and the merge runs on the facade thread, matching how every other partition fan-out in the file collects its results. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Docs preview: https://pr-337.monoprop-docs.pages.dev |
The comments and docstrings added with the feature were outliers against their neighbours: the header block was 12 lines where the file's median doc block is 1 and its next-longest are 6 and 7, and `evolved_operator_coefficients` was 36 lines against a module median of 4. Cut to the contracts a reader cannot infer, dropping the worked examples and the reasoning that the docs page and the API reference already carry. The `_term_slots` hooks lose their `Raises:` sections -- they are private, so `gen_api_dump.py` never renders them, and the same raises were documented on the public method too. The evaluation page keeps the overview and defers the contract list to the API reference. No behaviour change. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
JoshCudbyAlgo
force-pushed
the
feat/evolved-operator-coefficients
branch
from
September 3, 2026 14:51
2ffb3f9 to
dee3edd
Compare
JoshCudbyAlgo
marked this pull request as ready for review
September 3, 2026 14:52
JoshCudbyAlgo
requested review from
adamglos92,
diagonal-hamiltonian,
fpietra,
ludmilaasb and
robertodr
as code owners
September 3, 2026 14:52
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #337 +/- ##
==========================================
+ Coverage 97.70% 97.72% +0.01%
==========================================
Files 14 14
Lines 742 746 +4
Branches 98 98
==========================================
+ Hits 725 729 +4
Misses 12 12
Partials 5 5
Flags with carried forward coverage won't be shown. Click here to find out more. |
|
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.



🤖 AI text below 🤖
Summary
evolved_operatordecodes the whole evolved operator: it enumerates the index, materialises a key per surviving term, and builds a term dictionary.evolved_operator_coefficientsprobes the index with the terms the caller names instead, so the decode costs one entry per term requested rather than one per term the operator carries. Reading a few amplitudes out of an evolved state (Schrödinger picture) is the motivating case. The graph contraction is unchanged and still dominant — this removes the per-surviving-term tail, not evolution cost.Contracts, all pinned by tests: an absent term reads back as
0; there is deliberately noatol, since the caller named its terms and magnitude filtering would silently zero some of them (threshold the result instead); the result is positional, and a repeated term is answered once per occurrence; the empty term agrees withevolved_operatorin both pictures —core_term()in Heisenberg, an ordinary index row in Schrödinger; and it is rank-local, exactly asevolved_operatoris.Terms must be canonical, as the constructor's
initial_operatorkeys must be. The engine keys terms by an order-insensitive bitset, so a raw(1, 0)would resolve to the row of(0, 1)and read back its coefficient without the anticommutation sign — which a lookup has no coefficient of its own to carry. Raw sequences are therefore validated throughMajoranarather than normalized; useMajorana.from_unsortedand apply the sign it returns.Changes
MonomialPropagator::evolved_operator_coefficients(parameters, terms), built on the operator index's group-prefetchfind_batchrather than afor_eachenumeration. Under partitions each partition probes on its own master thread into its own vector and the merge runs on the facade thread, as every other partition fan-out in the file does.evolved_operator_coefficients(terms, parameters=None), returning a complex NumPy array._term_slotsfront-end hook: Majorana indices forMajoranaPropagator(Majoranaterms, index tuples or index arrays, with non-canonical ones rejected), symplectic slots forPauliPropagator(Pauliterms). Cross-front-end terms raiseTypeError. It is a default rather than an abstract method, so a front-end that does not encode terms still constructs.evolved_operator(atol=0.0), covering both pictures crossed with 1/2/4 partitions. Partitioned Schrödinger is the only configuration where the identity is a hash-partitioned index row, so it is what exercises the empty key against the concurrent merge.test_evolved_operator_coefficients_is_rank_local: the ranks' answers sum to the serial result, plus a check that the split is real — counted against the terms each rank owns rather than its nonzero coefficients, which would drift if an owned term evolved to exactly zero.docs/content/docs/features/evaluation.mdx.Verification
274/274 C++
ctestand 611 Python tests pass locally, andprek run --all-filesis clean over this branch's files. The MPI leg was never executed: the dev environment is a serial build, sotest_evolved_operator_coefficients_is_rank_localis reviewed but unrun and CI's[mpi:on]lanes are its first real run. Branched from7101b33, whichmainhas since moved past, so a rebase before merge would be sensible.Checklist
docs/,CONTRIBUTING.md) if neededCHANGELOG/ release notes updated if applicable — N/A, the repository keeps no CHANGELOGAI/LLM disclosure