Skip to content

feat: post-selected measurement via GeneralizedTableau.project - #293

Open
lkdvos wants to merge 3 commits into
QuEraComputing:mainfrom
lkdvos:issue-239
Open

lkdvos wants to merge 3 commits into
QuEraComputing:mainfrom
lkdvos:issue-239

Conversation

@lkdvos

@lkdvos lkdvos commented Nov 13, 2026

Copy link
Copy Markdown

Closes #239.

Adds GeneralizedTableau.project(addr0, outcome, *, tol=1e-12) -> float: collapses the qubit onto outcome, records it like measure, and returns its probability. Chaining it gives exact bitstring probabilities, P(z) = Π_i P(z_i | z_<i). Rust: GeneralizedTableau::project(addr0, outcome, tol) -> Result<f64, ProjectError>.

Design

  • measure and project share one collapse routine, collapse_with_scratch, which differs only in how the outcome is chosen. The sampling path draws from the RNG exactly as before, so seeded results are unchanged.
  • ProjectError covers a lost qubit and outcomes with probability below tol; in both cases the tableau is left untouched. An invalid tol is a debug_assert! in Rust and a ValueError in Python.
  • tol exists because projecting onto a zero-probability outcome would otherwise panic in normalize or renormalize round-off noise into a valid-looking state.

Tests

crates/ppvm-tableau/tests/project.rs checks probabilities against analytic values, agreement with measure, the chain rule against an independent Z-string-expectation reference, and that rejections leave the state unchanged. test_project.py covers the Python binding.

🤖 Generated with Claude Code

lkdvos and others added 3 commits November 13, 2026 16:14
`project(addr0, outcome, tol)` collapses qubit `addr0` onto `outcome`,
records it like `measure`, and returns its probability (relative to the
current, possibly unnormalized, norm). Chaining it gives exact bitstring
probabilities through the chain rule.

`measure` and `project` share one collapse routine,
`collapse_with_scratch`, parameterized by an `OutcomeChoice` (sample from
the RNG, or force an outcome). The sampling path draws from the RNG
exactly as before, so seeded results are unchanged.

`ProjectError` covers the state-dependent rejections — a lost qubit, or
an outcome with probability below `tol` — and leaves the tableau
untouched. An invalid `tol` is a `debug_assert!`, following the crate's
convention for caller-contract violations.

Refs QuEraComputing#239

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bind the Rust post-selected measurement as
`GeneralizedTableau.project(addr0, outcome, *, tol=1e-12) -> float`.
The wrapper rejects `MeasurementResult.LOST` and invalid `tol` with
`ValueError`; `ProjectError` from Rust maps to `ValueError` as well.

Closes QuEraComputing#239

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 13, 2026 21:51

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👋 Thanks for opening your first pull request against PPVM!

A quick note on contribution terms: by submitting this PR you
agree that your contribution is licensed under the
Apache License 2.0
and that you accept the
PPVM Contributor License Agreement.
Please skim those before a maintainer reviews — opening this PR
counts as your acceptance.

A few things that will speed up review:

  • Read CONTRIBUTING.md
    for the workflow, build commands, and style notes.
  • Run prek run --all-files locally; CI runs the same checks.
  • Use Conventional Commits
    for commit messages.

We'll get to your PR as soon as we can. Thanks for contributing!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Cancellation in the forced-projection probability calculation can incorrectly reject rare outcomes above tolerance in both collapse branches, requiring a numerically stable calculation and a regression test.

1 open issue
What changed in this PR

Adds post-selected Z measurement to GeneralizedTableau in Rust and Python, addressing #239. The API collapses onto a requested outcome and returns its probability, enabling bitstring probability calculations through chained projections.

Changes:

  • Shares collapse logic between sampled measurements and forced projections.
  • Adds Python bindings, type hints, input validation, and usage documentation.
  • Tests probabilities, measurement equivalence, chained projections, and rejection behavior.
File Description
skills/​ppvm-usage/​SKILL.md Documents post-selection usage and API availability.
ppvm-python/​test/​generalized_tableau/​test_project.py Tests Python probabilities, records, and validation.
ppvm-python/​src/​ppvm/​generalized_tableau.py Adds the public Python project method.
ppvm-python/​src/​ppvm/​_core.pyi Declares the native projection signature.
crates/​ppvm-tableau/​tests/​project.rs Tests Rust projection behavior and probability calculations.
crates/​ppvm-tableau/​src/​measure.rs Adds projection errors and shared collapse logic.
crates/​ppvm-python-native/​src/​interface_tableau.rs Exposes projection through PyO3 and maps errors to ValueError.

Review effort: Balanced

// divide by ⟨ψ|ψ⟩ rather than assuming it is 1.
let prob_1 = 0.5 - 0.5 * z_overlap_re / norm_sqr;
let outcome = self.tableau.rng.random::<f64>() < prob_1;
let (outcome, prob) = self.choose_outcome(prob_1, choice)?;

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ Feature ] Post-selected measurement

2 participants