Repository navigation
Conversation
`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>
There was a problem hiding this comment.
👋 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-fileslocally; 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!
There was a problem hiding this comment.
🟡 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)?; |

Closes #239.
Adds
GeneralizedTableau.project(addr0, outcome, *, tol=1e-12) -> float: collapses the qubit ontooutcome, records it likemeasure, 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
measureandprojectshare 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.ProjectErrorcovers a lost qubit and outcomes with probability belowtol; in both cases the tableau is left untouched. An invalidtolis adebug_assert!in Rust and aValueErrorin Python.tolexists because projecting onto a zero-probability outcome would otherwise panic innormalizeor renormalize round-off noise into a valid-looking state.Tests
crates/ppvm-tableau/tests/project.rschecks probabilities against analytic values, agreement withmeasure, the chain rule against an independent Z-string-expectation reference, and that rejections leave the state unchanged.test_project.pycovers the Python binding.🤖 Generated with Claude Code