Skip to content

fix(propagator): 🐛 invalidate functionals after update_initial_operator - #225

Open
JoshCudbyAlgo wants to merge 4 commits into
mainfrom
fix/validation-and-propagator-updates
Open

fix(propagator): 🐛 invalidate functionals after update_initial_operator#225
JoshCudbyAlgo wants to merge 4 commits into
mainfrom
fix/validation-and-propagator-updates

Conversation

@JoshCudbyAlgo

@JoshCudbyAlgo JoshCudbyAlgo commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

expectation_value_functional and expectation_value_and_gradient_functional snapshot the initial operator's coefficients. update_initial_operator re-weighted the operator in place without invalidating functionals built beforehand, so a stale functional would silently answer for coefficients the propagator no longer holds. It now bumps a re-weight epoch counter that every functional call checks, raising the same StaleFunctionalGraph error used for a rebuilt graph.

Changes

  • Added initial_operator_epoch_ to MonomialPropagator (cpp/include/monoprop/MonomialPropagator.h), bumped on every update_initial_operator re-weight (cpp/monoprop/detail/monomial_propagator/MonomialPropagatorImpl.h)
  • Added validate_expected_initial_operator, raising StaleFunctionalGraph when the epoch a functional captured no longer matches the propagator's current one (cpp/monoprop/Validation.{h,cpp})
  • Wired the epoch check into make_functional_, so every call through expectation_value_functional/expectation_value_and_gradient_functional validates against it alongside the existing graph-layers check
  • Updated docstrings on MonomialPropagator.expectation_value_functional, expectation_value_and_gradient_functional, and update_initial_operator, plus the MajoranaPropagator/PauliPropagator update_initial_operator overrides, to document that functionals are invalidated by a re-weight
  • Updated docs/content/docs/features/evaluation.mdx to describe functional invalidation on graph mutation or initial-operator update
  • Added update_initial_operator_invalidates_gradient_functional (C++) and test_expectation_value_functional_invalidated_after_initial_operator_update / test_expectation_value_and_gradient_functional_invalidated_after_initial_operator_update (Python) regression tests

Checklist

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

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: ClaudeCode:claude-sonnet-5
  • I used the following tool to generate or modify code: ClaudeCode:claude-sonnet-5

Important

By opening this PR I confirm that I have read CONTRIBUTING.md and I agree to the terms of the Contributor License Agreement.

Warning

If you're contributing on behalf of your employer, contact cla@algorithmiq.fi to arrange a Corporate CLA.

expectation_value_functional and expectation_value_and_gradient_functional
snapshot the initial operator's coefficients. update_initial_operator
re-weighted the operator in place without invalidating functionals built
beforehand, so a stale functional would silently answer for coefficients
the propagator no longer holds. It now bumps a re-weight epoch counter
that every functional call checks, raising the same StaleFunctionalGraph
error used for a rebuilt graph.

Assisted-by: ClaudeCode:claude-sonnet-5
@github-actions

Copy link
Copy Markdown

Docs preview: https://pr-225.monoprop-docs.pages.dev

@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.70%. Comparing base (1bd8dc7) to head (4f66138).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #225   +/-   ##
=======================================
  Coverage   97.70%   97.70%           
=======================================
  Files          14       14           
  Lines         742      742           
  Branches       98       98           
=======================================
  Hits          725      725           
  Misses         12       12           
  Partials        5        5           
Flag Coverage Δ
cpp 97.70% <ø> (ø)

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

with pytest.raises(RuntimeError, match=r"MP object has been modified"):
functional([1.0, 2.0])

def test_expectation_value_functional_invalidated_after_initial_operator_update(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both of the tests you added seem to tests similar behaviour. Do you think you can:

  • parameterize the test?
  • add more cases that will consider also Schrodinger variant?
  • add similar test for PauliPropagator?
    skip parameterization if putting everything under one tests would be complicated, but I'm still finding it valuable

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I second Adam here. Copilot/Claude would know how to do it.

@robertodr robertodr changed the title fix(propagator): 🐛 invalidate functionals after update_initial_operator fix(propagator): 🐛 invalidate functionals after update_initial_operator Aug 13, 2026
@robertodr
robertodr removed the request for review from fpietra August 13, 2026 08:31

@robertodr robertodr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @JoshCudbyAlgo LGTM, but please address Adam's comment.

Using these sentinels in the class always makes me uneasy, because they tend to hide bugs in the long run (e.g., you add one new method that should update the counter, but forget to update it)

I don't have a good idea for a better design, maybe a callback register that schedules/forces a graph update for all these methods when an invalidating action occurs 🤔

@robertodr robertodr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting to "Request changes" so this doesn't get merged before you handle Adam's comments

@sonarqubecloud

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 python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants