feat(stim-parser)!: require *pi in rotation/U3 tag angles (no bare rotation gates)#163
Open
david-pl wants to merge 4 commits into
Open
feat(stim-parser)!: require *pi in rotation/U3 tag angles (no bare rotation gates)#163david-pl wants to merge 4 commits into
david-pl wants to merge 4 commits into
Conversation
Mirror tsim's parametric-tag convention: rotation and U3 tag angles
must be written in half-turns as `<n>*pi` (e.g. `I[R_Z(theta=0.5*pi)]`),
and a bare number (`I[R_Z(theta=0.5)]`) is now rejected with an
`invalid-tag` diagnostic. tsim's tag parser requires the `*pi` literal
(`core/parse.py`, regex `^\w+=<float>\*pi$`) and treats the coefficient
as half-turns; ppvm now refuses the ambiguous bare form rather than
silently reading it as radians.
Mechanics:
- `TagParam::Named` gains a `had_pi` flag, captured by a new
`pi_expr_flagged` grammar rule (pi_expr is now defined in terms of it).
- `exact_named_params` rejects any rotation/U3 angle written without `*pi`.
- The printer re-emits the half-turn form (`theta={theta/pi}*pi`); since
every parser-produced rotation angle is a `<n>*pi` multiple, theta/pi
recovers the coefficient exactly (verified for the relevant float set),
so print -> parse stays a fixpoint.
Tests across extended/roundtrip/proptest suites move to the `*pi` form;
the proptest AST generator now produces `coeff*pi` angles (the only
parser-producible shape). The ppvm-python fixpoint test is updated too.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The native T / T_DAG arms dropped any tag the same way the gate body is lowered (e.g. `T[foo] 0` parsed and silently lost `[foo]`). A tag has no meaning on the bare mnemonics — the tagged form is S[T] / S_DAG[T] — so reject a non-empty tag list with an `invalid-tag` diagnostic that points at the canonical tagged spelling. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ail) The rotation/U3 printer emitted `theta/PI` directly, so the division's rounding leaked into the output: `theta=0.76*pi` printed as `theta=0.7599999999999999*pi`. Roughly 10% of ordinary decimal angles were affected. `pi_coeff` now returns the shortest decimal `c` whose `c * PI` recovers the stored radians bit-for-bit, falling back to `value / PI` when no exact short form exists. Because acceptance requires exact equality, this both prints cleanly and keeps `parse → print` lossless and the printer a byte-for-byte fixpoint — verified by a new property test over arbitrary decimal coefficients (exact theta recovery + fixpoint + no rounding tail) plus a unit test covering rotation and U3 tags. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-turns The only U3 execution test used phi=lambda=0, so the half-turn scaling of phi/lambda was never exercised end-to-end (flagged in review). Add a test using U3(theta=pi, phi=pi/2, lambda=pi/2) == Y, framed as H·U3·H so the qubit flips to |1> deterministically. The H frame makes the outcome sensitive to phi *and* lambda: dropping or mis-scaling either collapses P(1) to ~0.5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
FYI, @Roger-luo I checked again and these are the semantics as emitted by bloqade and parsed by tsim. |
|
There was a problem hiding this comment.
Pull request overview
This PR tightens stim-parser’s extended-dialect rotation/U3 tag parsing to match tsim semantics: rotation/U3 tag angles must be written in half-turns as "<n>*pi", and the printer re-emits a canonical *pi form that round-trips without floating-point “rounding tail” artifacts. It also tightens parsing of bare T/T_DAG by rejecting (previously ignored) tags.
Changes:
- Require
*piinI[R_X(...)]/I[R_Y(...)]/I[R_Z(...)]/I[U3(...)]tag parameters (bare numbers now produceinvalid-tag). - Track whether
piappeared in named tag parameters (had_pi) and print rotation/U3 angles as the shortest exact<c>*picoefficient to preserve parse→print fixpoints. - Add/adjust tests across
stim-parser,ppvm-stim, and Python API tests to reflect the stricter grammar and canonical printing.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| ppvm-python/test/test_stim_api.py | Updates Python stim API test input to use theta=…*pi syntax. |
| crates/stim-parser/tests/tags.rs | Updates tag parsing test to assert had_pi is captured. |
| crates/stim-parser/tests/roundtrip.rs | Updates the fixed round-trip corpus to the *pi form for rotations/U3. |
| crates/stim-parser/tests/proptest_roundtrip.rs | Updates proptest fragments to *pi and adds property coverage for clean *pi coefficient printing and exact round-trips. |
| crates/stim-parser/tests/proptest_ast.rs | Adjusts AST generators to produce rotation/U3 radians as coefficient * PI (aligned with new tag requirements). |
| crates/stim-parser/tests/extended.rs | Updates extended parsing/lowering tests to expect radians scaled by PI. |
| crates/stim-parser/src/syntax/grammar.rs | Adds pi_expr_flagged and records had_pi for named parameters. |
| crates/stim-parser/src/print/mod.rs | Prints rotation/U3 tags in canonical <c>*pi form and adds pi_coeff recovery to avoid rounding tails. |
| crates/stim-parser/src/pipeline/validate.rs | Updates validation tests to include had_pi in constructed TagParam::Named. |
| crates/stim-parser/src/pipeline/lower.rs | Enforces had_pi for rotation/U3 required params and rejects tags on bare T/T_DAG. |
| crates/stim-parser/src/ast/shared.rs | Extends TagParam::Named with the had_pi flag (syntax provenance). |
| crates/ppvm-stim/tests/executor.rs | Updates U3 test inputs to *pi and adds coverage that exercises nonzero phi/lambda. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
Supersedes #159
This PR replaces #159 (
david/parse-bare-rotations). It keeps only the tag-parsing tightening from that PR and drops the bare rotation-gate support, matching tsim semantics rather than clifft's bare half-turn shorthand.#159 should be closed in favor of this one.
What changes
R_X/R_Y/R_Z/U3gates. The bare clifft-style mnemonics (R_Z(0.5) 0,U3(a,b,c) 0) are not registered, so they are rejected asunknown instruction— the same as onmain. Only tsim's canonical tagged form is accepted.*piis strictly required in rotation/U3 tag angles. Mirroring tsim's parametric-tag convention, angles must be written in half-turns as<n>*pi(e.g.I[R_Z(theta=0.5*pi)]). A bare number (I[R_Z(theta=0.5)]) is now rejected with aninvalid-tagdiagnostic rather than silently read as radians.<c>*piform with the shortest exact coefficient (no0.7599999999999999*pirounding tail), keepingparse → printa byte-for-byte fixpoint.T/T_DAGbare gates now reject tags (T[foo] 0previously parsed and silently dropped the tag) — an orthogonal tightening included here.Behavior
I[R_Z(theta=0.5*pi)] 0I[R_Z(theta=0.5)] 0parameter 'theta' must be written as <n>*pi (half-turns)R_Z(0.5) 0unknown instruction 'R_Z'U3(0.5, 1.0, 1.5) 0unknown instruction 'U3'I[U3(theta=0.3*pi, …)] 0Commits
feat(stim-parser)!: require *pi in rotation/U3 tag anglesfix(stim-parser): reject tags on bare T/T_DAG gatesfix(stim-parser): print shortest exact *pi coefficient (no rounding tail)test(ppvm-stim): U3 with all angles nonzero exercises phi/lambda half-turnsTesting
cargo test -p stim-parser -p ppvm-stim— all green.pytest test/test_stim_api.py— 22 passed (native module rebuilt).🤖 Generated with Claude Code