Skip to content

feat(propagator): ✨ read individual evolved coefficients by term - #337

Open
JoshCudbyAlgo wants to merge 3 commits into
mainfrom
feat/evolved-operator-coefficients
Open

feat(propagator): ✨ read individual evolved coefficients by term#337
JoshCudbyAlgo wants to merge 3 commits into
mainfrom
feat/evolved-operator-coefficients

Conversation

@JoshCudbyAlgo

@JoshCudbyAlgo JoshCudbyAlgo commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

🤖 AI text below 🤖

Summary

evolved_operator decodes the whole evolved operator: it enumerates the index, materialises a key per surviving term, and builds a term dictionary. evolved_operator_coefficients probes 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 no atol, 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 with evolved_operator in both pictures — core_term() in Heisenberg, an ordinary index row in Schrödinger; and it is rank-local, exactly as evolved_operator is.

Terms must be canonical, 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. Raw sequences are therefore validated through Majorana rather than normalized; use Majorana.from_unsorted and apply the sign it returns.

Changes

  • MonomialPropagator::evolved_operator_coefficients(parameters, terms), built on the operator index's group-prefetch find_batch rather than a for_each enumeration. 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.
  • Bound as evolved_operator_coefficients(terms, parameters=None), returning a complex NumPy array.
  • A _term_slots front-end hook: Majorana indices for MajoranaPropagator (Majorana terms, index tuples or index arrays, with non-canonical ones rejected), symplectic slots for PauliPropagator (Pauli terms). Cross-front-end terms raise TypeError. It is a default rather than an abstract method, so a front-end that does not encode terms still constructs.
  • 11 C++ and 17 Python cases oracled against 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.
  • A "Reading individual terms" section in docs/content/docs/features/evaluation.mdx.

Verification

274/274 C++ ctest and 611 Python tests pass locally, and prek run --all-files is clean over this branch's files. The MPI leg was never executed: the dev environment is a serial build, so test_evolved_operator_coefficients_is_rank_local is reviewed but unrun and CI's [mpi:on] lanes are its first real run. Branched from 7101b33, which main has since moved past, so a rebase before merge would be sensible.

Checklist

  • Tests added or updated to cover the changes
  • Documentation updated (docstrings, docs/, CONTRIBUTING.md) if needed
  • CHANGELOG / release notes updated if applicable — N/A, the repository keeps no CHANGELOG

AI/LLM disclosure

  • I did not use LLM tooling, or used it only privately for ideation
  • I used the following tool to help write this PR description: Claude Code (Claude Opus 5)
  • I used the following tool to generate or modify code: Claude Code (Claude Opus 5)

`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>
@github-actions github-actions Bot added documentation Improvements or additions to documentation python cpp labels Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

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
JoshCudbyAlgo force-pushed the feat/evolved-operator-coefficients branch from 2ffb3f9 to dee3edd Compare September 3, 2026 14:51
@JoshCudbyAlgo
JoshCudbyAlgo marked this pull request as ready for review September 3, 2026 14:52
@JoshCudbyAlgo JoshCudbyAlgo added enhancement New feature or request help wanted Extra attention is needed labels Sep 3, 2026
@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.72%. Comparing base (d531dc6) to head (656c7b0).
✅ All tests successful. No failed tests found.

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              
Flag Coverage Δ
cpp 97.72% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

@sonarqubecloud

sonarqubecloud Bot commented Sep 3, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cpp documentation Improvements or additions to documentation enhancement New feature or request help wanted Extra attention is needed python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant