Commit f559e60
authored
Cleanup (#315)
* Regenerate doc tests and prune stale generated files
* Auto-include CUDA Python packages when toolkit and NVIDIA GPU detected
* Surface CUDA Python packages in pecos setup summary
* Stop pytest from shelling out to cargo, fix shadowed slr_tests module collision
* Sanitize generated doc test paths and fix cmake-setup example
* Introduce VariableState; fix SLR linearity bugs at use-after-unpack sites
* Add AST -> Guppy v1 acceptance tests as xfail spec
* Tighten v1 acceptance tests: strict xfail; defer rejection tests
* Add Guppy-only linearity helper for AST -> Guppy lowering
* Rewrite AST Guppy emitter for v1 linearity
* Clean up legacy AST Guppy tests after v1 emitter rewrite
* Tighten _harness lint: TYPE_CHECKING import, Error suffix on exception
* Black formatting on AST -> Guppy v1 work
* Fix Guppy bool emission for CReg bit assignments
* Add Selene behavioral test harness for AST -> Guppy v1 (Workstream A)
* Workstream B audit runner skeleton (waiting on _force_ast kwarg)
* Add audit-only AST HUGR route
* Expand audit runner with legacy HUGR test corpus (9 cases all pass)
* Harden AST Guppy behavioral coverage
* Add examples/ corpus to audit runner (pass 2)
* Add qeclib corpus + ExpectedFailure XFAIL mechanism to audit runner (pass 3)
* Harden Guppy v1 preflight + inline-CReg inference; lock down pass 4 audit (4 green, 5 XFAIL)
* Cutover: route SlrConverter.hugr() through AST by default; remove _force_ast kwarg
* Cutover: delete legacy gen_codes/guppy/ and dependent tests
* Apply Codex review fixes: no-arg entrypoint wrap + compile diagnostics + stale comments + 3-cycle and cross-register Permute coverage
* Fix Codex review v2 findings: entry wrapper mirrors emitter return shape (explicit Return passthrough + inline CReg result capture); broaden compile diagnostic catch; promote wrapper to shared entry_wrapper module
* Mirror emitter for non-result-CReg Return values; cover nested-control-flow inline Measure in regression tests
* Widen compile-error diagnostics to module-load failures; factor out _load_and_compile_entry helper
* v2 Phase 1: add Print(value, *, tag=None, namespace='result') with SLR class, PrintOp AST node, Guppy result() emission, QASM comment fallback, and 15 Selene-roundtrip behavioral tests
* Phase 1 Print: apply Codex tracer-bullet review (validate derived tags, document Selene tag-mode flip, strengthen loop/multi-Print tests with event-list shape assertions, explicit Print-skip in QIR/Stim/QuantumCircuit, import result from guppylang.std.builtins)
* Phase 1 Print: path-signature validator rejects asymmetric If branches + non-static loops with Print bodies (9 new tests)
* Phase 1 Print: inline-CReg definite-assignment dataflow validator + 8 tests (Codex's Print-before-Measure soundness gap closed)
* Phase 1 Print: tighten inline-CReg validator to bit-level (Codex tracer-bullet finding Q4) + add missing coverage tests (While/Parallel/Repeat(0)/partial-CReg)
* Phase 1 Print: reject whole-CReg Print of inline CReg outright (Codex v2 review Q2 -- prevents silent register shrink); rewrite affected tests at bit-level; add Codex shrink-case regression + direct AST non-static For test
* Phase 1 Print: adversarial tests (cross-codegen byte-identity + Selene shape edge cases + QASM exactly-one-comment assertion)
* Phase 2 deprecation warnings: CReg(result=False) construction-time warning + Main(...) implicit-return warning fired SLR-wide across all codegen entry points (cached per converter, stacklevel attributes to user, walker tightened to skip false-positive no-result Measure and result=False-only targets)
* Land Phase 3a.0-3a.3 iters 1-4: BlockDecl/BlockCall AST + Guppy emitter + 7 Steane Block conversions.
* Phase 3a.3 iter 5a-prep: typed BlockArg sum type for BlockCall arg/out bindings (AllocatorArg + four shape stubs).
* Fix Codex review of BlockArg refactor: flatten path now rejects deferred BlockArg shapes in out_bindings (symmetric with arg_bindings); add 4 lock-in tests across Guppy + flatten paths.
* Phase 3a.3 iter 5b: single-qubit BlockInput type + SingleQubitArg call-site shape end-to-end in Guppy emitter.
* Fix Codex iter-5b review: reject mismatched arg/out bindings for LIVE_PRESERVED inputs (would emit invalid Guppy); add flatten-rejection lock-in for SingleQubitArg + clarify test docstring.
* Phase 3a.3 iter 5c: single-bit BlockInput via array[bool,1] write-back proxy + SingleBitArg call-site shape end-to-end in Guppy emitter.
* Phase 3a.3 iter 5d: QubitBundleArg non-contiguous slot-bundle call-site shape end-to-end in Guppy emitter (pack/unpack across arbitrary allocators, duplicate/bounds/size validation).
* Iter-5d Codex polish: end-to-end Selene lock-in for cross-allocator bundle; pre-consume cross-input qubit-slot alias check; refresh stale _emit_block_call docstring + mismatch message.
* Iter-5d r2 polish: add asymmetric bundle test that genuinely pins unpack order; reword Bell test to not over-claim order-sensitivity; fix stale validated_args tag comment.
* Phase 3a.3 iter 5e.1: unify BlockDecl-body substitution into shared BodyRemap + recursive substitute_stmt (slot/bit-level, reject-on-partial); converter + flatten both delegate to it; 24 synthetic non-identity tests. Byte-identical on the 7 Steane conversions.
* Iter-5e.1 Codex early-fixes: token-boundary permute reject (no substring false-positive); BodyRemap rejects conflicting whole/partial binds at build time; exact prepare-slot-order lock-in + reversed-map case + builder-conflict tests.
* Phase 3a.3 iter 5e.2: SLR var->typed BlockArg detection (single Qubit/Bit, list[Qubit] bundle) + flatten shape promotion + cross-input qubit/bit aliasing guard at both converter and BodyRemap layers; strip review-attribution from code comments.
* Scratch-ancilla S1: ResourceEffect.SCRATCH + converter detection (excluded from out_bindings, kept for O2 alias guard) + R4 Prep->Measure segment-lifecycle validator (every Prep closed by Measure) + 13 lock-in tests.
* Scratch-ancilla S1: reject any ReturnOp in a scratch-bearing block (R4 clause 4, Codex S1 r2 -- scratch slot must not be handed out); drop dead _expr_mentions_allocator; +return-rejected test.
* Scratch-ancilla S2: Guppy emitter lowers SCRATCH as an internal qubit() allocation (no param/arg/return, seeded CONSUMED); fix live_arg_index to index validated_args; reuse pattern now compiles + runs (original blocker dead).
* Scratch-ancilla S2 fixes (Codex r1): whole-program guard rejecting non-scratch use of a scratch-bound outer slot (Prep carve-out for the corpus, reuse still allowed); validate scratch BlockArgs before excluding so malformed bindings fail loudly; 3 lock-in tests.
* Scratch-ancilla S2 R5-completeness (Codex r2): per-scope purity guard (main + every BlockDecl.body) covering PermuteOp + ReturnOp observation paths; 3 lock-in tests.
* Scratch-ancilla S4: convert qeclib Check (a:scratch) -- SynExtractBare reused-ancilla path now compiles to Guppy (original 5e.3 blocker dead); Check Selene records byte-identical (qubits 4->5 for internal alloc, design R2); Steane production lock-in.
* Scratch-ancilla S4: add Color488 serial SynExtractBare production lock-in (Codex S4 non-blocking finding -- design specifies Steane + Color488).
* Scratch-ancilla S5: convert qeclib Check1Flag (a,flag:scratch; Prep(a,flag)->Prep(a),Prep(flag) per O1 option a) -- SynExtractFlagged reused-ancilla+flag path now compiles; Check1Flag Selene records byte-identical (qubits 5->7); SynExtractFlagged + CH-branch lock-ins.
* Scratch-ancilla S5: fix corpus tracked-block table doc drift (Check/Check1Flag ancilla now scratch, was consumed) -- Codex S5 non-blocking finding.
* v1 cutover cleanup: port surface_code_slr_exploration notebook off the deleted IRGuppyGenerator to SlrConverter.guppy(); drop stale deleted-test reference in audit_runner comment.
* Centralize AST visitor dispatch: replace 37 per-node accept() double-dispatch stubs with one BaseVisitor _DISPATCH map; drop vacuous ABC from base nodes; nodes.py now visitor-decoupled; +completeness safety-net test (byte-identity + public API unchanged).
* Visitor dispatch: walk type(node).__mro__ so subclasses of concrete AST nodes resolve to the base node's visit_* (preserves old inherited-accept() semantics, Codex review); scope completeness test to the nodes module + add MRO-subclass regression test.
* Fix two pre-existing qeclib construction bugs (audit XFAIL->accepted, 8->6 XFAIL): PrepProjectZ now preps its list[Qubit] via the Q.pz primitive (dead/broken (QReg,indices) PrepZ removed); Color488 SynExtractBareParallel uses int math.ceil for the range() count instead of the f64 numpy-drop-in pecos_rslib.num.ceil.
* Add Phase 3b S1 codemod (libcst): append explicit Return(<result CRegs>) to implicit-return Main() calls, trailing-comma-preserving so the formatter keeps multi-line shape; validated byte-identical on test_v1_behavioral. Not yet rolled out (harness-first + chunked-black-gated plan pending Codex review).
* Phase 3b S1a: generalize _selene_harness to accept explicit Return (derive measurement_N keys from returned CRegs OR -- pre-3b -- implicit result CRegs; strict on non-CReg returns); codemod now also matches qualified pecos.slr.Main + mirrors its qualifier for Return. No-op for current tree (1129 pass, audit 31/31).
* Phase 3b gate-hardening: implicit-return warning detector now also catches whole-register inline CRegs (Measure(q) > CReg('c',n) stores the CReg itself in cout, not a Bit) -- was under-detecting; suite green (1129), warnings non-fatal.
* Phase 3b S1b: harden implicit-return detector (recursive cout -- whole-register + slice inline CRegs); re-apply per-case audit warning enforcement; migrate implicit-return programs to explicit Return across ast_guppy/QASM-regression/legacy-guppy/unit-slr/test_partial (+scoped block.extend(Return) for qeclib QASM-byte-identity, +Stim/QC helper returns). Audit 31/31; strict gate 1131 pass; QASM byte-identical; ruff+black clean.
* Phase 3b S1c: fix logical_steane_code_program example -- stale pecos.qeclib import -> pecos.slr.qeclib, prog.qasm() -> SlrConverter(prog).qasm() (was doubly-broken/non-runnable), + explicit Return(m_bell,m_out)/(m_reject,m_t,m_out) so it is S3-safe. Now runs end-to-end; wrapped regression test (own copies) still green.
* Phase 3b S2: remove Phase-2 implicit-return warning machinery (slr_converter) + CReg(result=False) warning (vars.py, kwarg kept for S3); revert now-moot per-case audit enforcement to plain hugr() (option 5a); rewrite test_deprecation_warnings into post-S2 no-warning contract tests. gen_codes legacy deprecations untouched. Independently verified: audit 31/31, suite 1121 pass byte-identical, ruff/black clean.
* Phase 3b S4: port qir_bc() to AST path (binding.parse_assembly bitcode + split diagnostics), fix invalid // builder comment so qir() emits parseable LLVM IR, sever legacy gen_qir creg.result (S3 blocker; QIRGenerator symbol kept).
* Phase 3b S4 fix: wrap binding.parse_assembly().as_bitcode() in the diagnostics try so malformed IR raises the wrapped 'Failed to compile QIR to bitcode' error (Codex post-review blocker).
* Phase 3b S3: remove CReg(result=) kwarg + RegisterDecl.is_result field + the v1 implicit-return code path (D-S3-1 b: drop EntryWrapperInfo.result_cregs, explicit-Return-only); no-Return -> main()->None; rewrite test surface to hard contracts; Codex post-review fixes (ISC001 x2 + 4 stale-doc/dead-code).
* Phase 3b S5: M2 converter-time elision of flattened composite block-boundary Returns (provenance flag, not position/count); steane_pz compiles (audit accepted, compile-scope); behavioral lock-in is strict-xfail pinning the orthogonal pre-existing v1 FT-RUS pz() non-codeword defect (tracked post-3b, task #72); EncodingCircuit provenance guard.
* #72: fix _selene_harness measurement-mapping via opt-in named return tags (D-72-B). entry_wrapper gains keyword-only emit_return_result_tags (default False -> production wrapper byte-identical); harness emits result('__pecos_return.<creg>',..) and _shot_records reads tags by name, re-exporting the historical measurement_N shape (immune to internal RUS measurements). Remove the wrong S5 strict-xfail (steane pz now genuinely passes; stim-verified); add internal-measurement regression guard.
* #72 close-out: fail-loud on unexpected Selene result-tag shape in _shot_records (Pickle non-blocking) -- explicit list/int dispatch, TypeError on any other type instead of silent single-bit wrap; behaviour-preserving (1123 passed, audit unchanged).
* #71 Stage A: add qir-qis test-group dep + QIR spec-compliance baseline gate over the audit corpus. Honest non-faked baseline (0 compliant; uniform missing-output_labeling_schema cause; build-failure set pinned so a new qir_bc regression trips it); trips deliberately on Stage-B progress. Codex blocker folded + re-confirmed; Pickle signed off. Test-only, production byte-identical.
* #71 Stage B1: emit required QIR module metadata in _finalize_module (output_labeling_schema=labeled, qir_profiles=adaptive_profile, !llvm.module.flags qir_*_version + dynamic_*/arrays=false). validate_qir 0->27/28; honest two-tier gate (Tier-1 validate, Tier-1b exact entry-attr value pins, Tier-2 qir_to_qis still create_creg until B2; build-fails pinned). Dual post-review: Pickle YES; Codex blocker (presence-only value pin) folded+re-confirmed. QASM/Stim/QC byte-identical; audit/optional_dep unchanged.
* #71 B2a: declare standard QIR classical-model functions (__quantum__qis__mz__body, __quantum__rt__read_result) for B2b to wire. Decls-only, zero behavior/gate change; verified inert (validate/qir_to_qis split, gate, broad 1124, audit all unchanged). Helper methods deferred to B2b (no dead code).
* #75: extend pecos_rslib_llvm with LLVM memory ops to unblock #71 B2 -- LLIRBuilder::{alloca,load,store,zext,trunc,icmp_unsigned} + LLConstant::zero (Array->zeroinitializer, Pointer->null) in crates/pecos-llvm; LLType PartialEq/Eq + manual Hash consistent with inkwell's LLVMTypeRef Eq (inkwell 0.8.0 derives Hash only for IntType); 6 pyo3 #[pymethods] + Constant(ty,value=None)->zero + PyLLValue.type getter + structural __richcmp__/__hash__ on Py*Type (PyAnyType gains IntoPyObject so the getter is returnable). Additive only: end-to-end B2-shaped module via the new ops round-trips binding.parse_assembly + qir_qis.validate_qir + qir_to_qis; #71 gate 1 passed unchanged, slr_tests 368 passed, broad qir/qasm/codegen sweep 1785 passed 0 fail; cargo fmt/clippy clean.
* #75 post-review nit #1 (Pickle): type __richcmp__ ordering ops (Lt/Le/Gt/Ge) now return NotImplemented so Python raises TypeError instead of a silently-wrong False; Eq/Ne unchanged (lltype_richcmp -> Py<PyAny>). Verified i1<i64 raises, eq/hash/dedup intact, #75 e2e + #71 gate + slr_tests 368 + broad sweep 1785 all green; clippy/fmt clean.
* #71 B2 (B2b+B2c) on the #75-extended binding: replace bespoke CReg runtime helpers with the standard M-B2-static model. CReg -> entry-block alloca [N x i1] + zeroinitializer; measure -> __quantum__qis__mz__body + static %Result* + read_result + store (no-result mz; slot=measurement_count-1); BitRef/whole-CReg c.set(int) -> store/per-bit lshr+trunc unpack; BitExpr -> load+zext (i64-canonical); _as_i1/_as_i64 via #75 value.type; record pack load/zext/shl/or -> int_record_output (kept); point-of-use GEP. B2c removes the now-uncalled create_creg/get_creg_bit/set_creg_bit/get_int_from_creg/set_creg_to_int/mz_to_creg_bit decls. #71 structural gate honestly rebaselined: _EXPECTED_QIS_OK 7->27, qis_failed empty, BUILD_FAILED 3->2, qeclib.steane_pz triaged build->validate-fail (B2 makes it build; then hits qir-qis allowlist on PECOS's non-standard __quantum__qis__barrierN__body -- pre-existing barrier gap, out of B2 scope). New tier2_semantic.py: real qir-qis-compiler acceptance + emitted-QIR structural invariants + deterministic AST->Guppy->selene cross-anchor (set_int/zero_init A+B-only; Guppy can't return a set(int) CReg). Executable qir_to_qis->selene differential blocked by LLVM 14<->21 / opaque-vs-typed gap -> tracked separately. slr_tests 368, broad sweep 1785/0-fail, ruff/format clean.
* #71 B2 post-review fold: fix Codex blocker + 2 non-blocking. BLOCKER: >64-bit CReg silently miscompiled (creg_map recorded size but alloca/output only when <=64, so CReg(65) built+validated+qir_to_qis-lowered with storage/output silently dropped) -> _process_declarations now raises NotImplementedError for size>64 (single-i64 pack cap; fail-loud, not a silent fallback) + regression test test_oversize_creg_raises_loud (CReg(64) builds, CReg(65) raises). Non-blocking: register `slow` marker in tests/conftest.py::pytest_configure (invocation-independent; canonical sweep 0 warnings; residual single-file pytest-path warning is a conftest-discovery quirk, cosmetic); tighten tier2_semantic Layer B from read_result-subset-of-mz to per-measurement adjacency (_assert_mz_rr_pairing: each read_result immediately preceded by its own same-slot mz), drop dead _MZ_SLOT/_RR_SLOT. Gates: tier2 PASS (8 A/B + tightened pairing + 2 C), oversize regression passes, #71 gate green, slr_tests 368, broad sweep 1785/0-fail, ruff/format clean. Pickle YES; Codex re-confirm pending (blocker fix + regression test were its exact sign-off condition).
* #71 B2 Codex re-confirm fold (cosmetic): raise the >64-bit CReg NotImplementedError BEFORE writing context.creg_map (fail before any partial state). Behaviour-equivalent (size>64 raises regardless; size<=64 unchanged); Codex minor note from codex-71-stageB2-postreview-reconfirm.md. Codex re-confirm: YES (0 blockers); both reviewers now YES -> B2 dual-signed-off.
* #74: fail loud on the 3 silent AST->QIR miscompiles (B3). VarExpr (was ->constant 0), While (was silent one-pass dropping condition+iteration), Print (was silent drop losing observable output) now raise NotImplementedError with clear messages -- each was valid-QIR-but-wrong-semantics that qir-qis cannot catch. Bounded scope: real While/scalar-SSA/Print->record are explicitly deferred (v1-feature-matrix: real While "too large for first sound emitter"); min bar = fail-fast not silently wrong (same pattern as B2 >64 cap). qir_bc() does not wrap them (only parse_assembly RuntimeError caught) -> surface at QIR-gen. Deliberate #71 gate rebaseline (triaged like steane_pz): only docs.while_loop exercises a B3 site -> _EXPECTED_QIS_OK 27->26, _EXPECTED_BUILD_FAILED 2->3 (NotImplementedError, "does not support While loops"); aligns QIR path with the Guppy path which already rejects While; 0 corpus Print, VarExpr only via for_loopvar_symbolic (build-fails earlier). Fixed enshrined-bug test: test_print.py test_qir_byte_identical asserted the old silent drop -> test_qir_raises_loud_on_print (Stim/QC byte-identical siblings left, documented out-of-scope). 4 fast regression tests in tier2_semantic.py. Gates: 5 fast + #71 gate green, cross-codegen Print 4 passed, slr_tests 368, ruff/format clean; broad sweep in-flight.
* #74 dual-review fold (Codex blocker): fix the 4th silent AST->QIR miscompile -- static `For` body dropped. `_process_for`'s `isinstance(node.start, int)` guard was always false (converter wraps For range bounds in LiteralExpr), so every static-For body was silently dropped: valid QIR, wrong semantics, qir-qis-uncatchable -- and docs.for_static_indexing sat in _EXPECTED_QIS_OK on body-dropped QIR (gate dishonest). Proper fix (static fixed-bound For is v1-supported + small, unlike deferred While/SSA): _static_int_bound resolves LiteralExpr(int) start/stop/step; _process_for unrolls range(start,stop,step) exclusive (matches canonical gen_quantum_circuit); symbolic/non-static bound -> NotImplementedError (never silent-drop). _process_repeat left as-is (RepeatStmt.count SLR-enforced int -- never the same bug; bounded fix to _process_for only). Honest resolution, NO baseline shuffle: docs.for_static_indexing stays in _EXPECTED_QIS_OK (26) but its QIR is now correct (body unrolled) -- gate honest by fixing code not the pin. New regression test_static_for_unrolls_body_not_dropped (3 call sites, not the declare; unrolled QIR still qir-qis-lowered). Gates: 6 fast tests + #71 gate green, broad sweep 1785/0-fail, ruff/format clean. Pickle YES; Codex re-confirm pending (its exact sign-off condition).
* #74 Codex re-confirm fold: satisfy the project formatter gate (the only re-confirm blocker -- semantically the static-For fix was already verified) + non-blocking step==0. Canonical chain is ruff-check --fix -> black 25.9.0 -> blackdoc (NO ruff format; ruff-format was a spurious self-introduced conflict). Collapsed two black-joined split regexes into single literals (behaviour-identical: r"a" r"b" == r"ab") to kill the black<->ISC001 conflict at the source; converged ruff/black (COM812 auto-fixed, black-stable). ruff check + black --check now clean, matching pre-commit. Non-blocking #1 (Codex): For(step=0) now raises a clear QIR NotImplementedError instead of a bare Python ValueError. Behaviour-preserving vs 177ab70c (formatting no-op; regex equiv; step==0 only changes an infinite-loop error message no test exercises). Pickle YES; Codex re-confirm pending (formatting was its sole remaining condition).
* #73 pre-PR hygiene: resolve branch-wide ruff/black debt -> repo fully green. 6 files (4 branch-touched + 2 pre-existing examples/surface, user-chose whole-repo-green). No logic change. migrate_implicit_return.py (libcst CSTTransformer): N802 x2 -> scoped `# noqa: N802` with rationale (libcst dispatches by the exact `leave_<CSTNodeClassName>` name -- API contract, renaming breaks dispatch); ARG002 x2 -> rename unused `original` -> `_original` (libcst calls positionally; no suppression needed). COM812 x7 -> ruff --fix (black-stable). 2x ISC001 from black-joined split strings (_block_flatten error msg, test_ast_visitor assert msg) -> collapsed to single literals at source (behaviour-identical). All 6 black-formatted (line-length 120). Canonical chain ruff-check --fix -> black 25.9.0. Verified: repo-wide `ruff check` All passed + `black --check` clean (760 unchanged); broad sweep 1785 passed / 15 skipped / 0 fail (behaviour-preserving: formatting / equivalent-string / unused-param-rename / comment-only).
* #78: fix the silent AST->QIR-sibling miscompiles in the Stim + QuantumCircuit AST codegens (same class as #74, applied per the explicit-over-implicit / fail-fast rule). _process_for: the isinstance(node.start, int) guard was always false (converter wraps For range bounds in LiteralExpr) -> Stim silently dropped every For body; QuantumCircuit's else *rejected every* static For (even valid For(i,0,3)). Now both resolve LiteralExpr(int) start/stop/step via _static_int_bound and unroll range(start,stop,step) exclusive (matches canonical gen_quantum_circuit); symbolic/non-static bound or step==0 -> clear NotImplementedError, never silent-drop. stim _process_while: was silent one-pass + stray TICK -> NotImplementedError (Stim has no runtime loop; same decision as #74's While; QC While already loud). Print -> fail loud in both (was a deliberately-pinned silent skip): Stim "does not support Print" (fundamental -- no classical-output stream); QuantumCircuit "does not yet support Print" (PECOS owns this format, may be added later). _process_repeat untouched (RepeatStmt.count SLR-enforced int -- never the bug; bounded to _process_for). Flipped test_print.py::TestCrossCodegenPrintEmission test_{stim,quantum_circuit}_byte_identical -> _raises_loud_on_print + docstring (same enshrined-bug-test correction #74 did for test_qir_byte_identical). New regression: TestStim/QuantumCircuitStaticForAndWhile (For unrolls 3x not dropped; While raises). Out of scope (surfaced, not changed): Stim silently skips unsupported gates / has no classical-Assign model -- long-standing, fundamental, broad reliance. Gates: ruff+black canonical clean; slr_tests 372/0-fail; broad 1923/0-fail.
* #77 DONE: real executable QIR->QIS->selene Tier-2 differential (A+B+C -> A+B+C+D). selene_sim (already a PECOS dep) natively executes qir_qis.qir_to_qis's LLVM-21 opaque-pointer QIS via the bundled selene_helios_qis_plugin (+ Helios QIR runtime) -- the long-claimed "blocked on an LLVM 14<->21 bridge" / "bounded post-PR infra (LLVM-21 JIT + runtime shim)" were BOTH wrong; no PECOS LLVM bump, PECOS-Rust stays LLVM-14. tier2_semantic.py Layer D: _qis_exec_records (qir_bc -> qir_to_qis -> selene_sim.build(BitcodeString) -> run_shots(Stim)) + test_tier2_executable_differential. Deterministic representatives execute to EXACT known classical records (empirically pinned; B2 _generate_results records ALL declared CRegs, declaration order, packed LSB-first): set_int [11], zero_init_safety [0,0], multi_creg [2,1], conditional_correction [0]. bell preserves entanglement correlation (every shot record in {0b00,0b11}, verified across seeds 1/2/7/42/123 -- only 0/3, never 1/2; H/CX present). Clean cross-path differential vs the dual-reviewed AST->Guppy->Selene oracle for conditional_correction (set_int/multi_creg excluded -- Guppy wrapper can't return a set(int) CReg, the documented Layer-C limit; zero_init declared!=returned). Slow-marked; slr_tests 372/0-fail; ruff/black canonical clean. Pending #77 dual review.
* #77 dual-review fold (Codex blocker + 2 non-blocking; Pickle YES). BLOCKER: the Bell executable check `all shots in {0,3}` was necessary-not-sufficient -- a dropped-H/dropped-CX/no-op Bell yields all-0 (subset of {0,3}) and would have passed, so the test could not catch the miscompile it guards. Fix: aggregate over fixed seeds (1,2,7,42) and require observed set == {0b00,0b11} (BOTH 00 and 11 must occur -> entanglement actually executed; 1/2 still rejected -> correlation) + single-record well-formedness. Non-blocking: (a) flipped the now-self-contradictory module docstring ("qir_to_qis->Selene blocked by LLVM 14<->21") to the truth -- Layer D selene_helios_qis_plugin runs LLVM-21 QIS, no PECOS LLVM work; framed as a *representative* (not exhaustive) differential; (b) registered the `slow` marker in slr_tests/pytest.ini (was commented under --strict-markers -> PytestUnknownMarkWarning; now clean). Verified: test 1 passed (4 deterministic exact + bell both-00/11-over-4-seeds + cond_corr cross-path), no marker warning, slr_tests 372/0-fail, ruff/black canonical clean. Pickle already YES; Codex re-confirm pending (blocker fix + docstring were its exact sign-off conditions).
* #80: fail loud on the 2 QIR silent-miscompile bugs #79 pre-review surfaced (inline/Return-only CReg lost its record; non-Z Prep basis dropped), re-pin #71 gate from actual _qir_state (3 programs QIS_OK->BUILD_FAILED)
qir.py: _require_creg() helper raises NotImplementedError at the 4 unknown-CReg silent-skip sites (_process_measure, _process_assign x2, _eval_expression BitExpr -- the last a #74-class silent Constant(0)). converter.py::_convert_prep: non-Z Prep basis raises at the shared root so QIR/Stim/QC/QASM all reject it (mirrors the Guppy Z/+Z preflight; Guppy/HUGR route unchanged -- its SLR-preflight runs pre-conversion). test_qir_spec_compliance.py: pins + docstring counts (build-fail 3->6, QIS_OK n->23) + resolved-#77 narrative. tier2_semantic.py: 2 regression tests. Verified: focused 3/0, broad 258/0, ruff/black clean.
* #80 dual-review fold: fix Codex blocker (Return-only inline CReg silently emitted no record -- _process_return now validates returned classical regs, qubit returns not false-rejected) + make the _eval_expression unknown-type default fail loud (same #74/#80 class, Codex non-blocking) + regression test; fix Pickle's surfaced doc-test by rewriting the 3 slr-qeclib.md Prep(q,"X") sites to the v1 Prep;H idiom (generated/ is gitignored). Verified: focused 5/0, doc-tests 22/0, ast_guppy 258/0, ruff/black/blackdoc clean
* #80 Codex re-confirm-1 fold: fix the Return-only inline-CReg name-collision bypass at the root (provenance, not name-guessing). _process_return skipped qubit returns by qubit_map name-membership, but _convert_return had erased QReg/CReg provenance (var.sym flatten), so an inline CReg colliding with a declared QReg name was silently dropped (same silent-output-loss class, public-SLR-reachable). Fix: additive ReturnOp.value_kinds populated from the real QReg/CReg object in _convert_return, preserved through _block_substitution, consumed by qir._process_return; unsound qubit_map name-skip removed. Guppy unaffected (reads .values). Collision regression test added. Verified: focused 5/0 (incl NAME_COLLISION raises, qreg_return builds), broad 258/0, doc-tests 22/0, ruff/black clean
* #81 Stage A: PrepareOp.basis discriminant + 6 dedicated prep classes (PZ/PNZ/PX/PNX/PY/PNY) + converter _PREP_BASIS routing + recast stray-string guard (basis is the gate identity -- any string qarg on any prep gate fails loud, generalizes/supersedes #80 non-Z guard) + block-sub & pretty-print basis preserve + qb exports. Prep retained as PZ alias so the ~223 sites stay green (no churn until Stage C). #71 pins fragment -> 'stray string argument'. Verified: focused 4/0 (#71 gate green -- recast broke no QIS_OK program), broad ast_guppy 258/0, ast_tests 664/0, ruff/black clean
* #81 Stage B: single shared canonical basis->tail map (_prep_tail.py) wired into all 5 codegens (QIR/Stim/QASM/QC/Guppy) -- reset + Clifford tail per pinned 6-row table (PZ id/PNZ X/PX H/PNX H;Z/PY H;S/PNY H;Sdg); non-PZ prepare_all fails loud where the backend no-ops it; PZ byte-identical (zero corpus churn); QC sequences reset/tail as separate ticks; Guppy functional tail. Verified: Stim peek_bloch all-6 PASS, broad ast_guppy 258/0, ast_tests 664/0, ruff/black clean
* #81 Stage C: hard-replace Prep->PZ repo-wide (63 files, all 3 subsystems: SLR gate+call sites + legacy gen_codes op_class + circuits gate-vocab); removed class Prep + _PREP_BASIS Prep entry + qeclib __init__ Prep + dead Guppy non-Z preflight reject + dead _string_args; PhysicalQubit.pz->preps.PZ; converter scratch-lifecycle messages Prep->PZ. Public SLR API break (accepted). Verified: full slr_tests 387 passed + behavioral all-6 PASS + ruff/black clean. The 27 *_qir permutation/measurement failures are PRE-EXISTING (legacy gen_codes QIR comment assertions the AST codegen never emitted; git -S Permutation empty on ast qir.py; #81 introduced 0 new failures) -- tracked task #87, out of #81 scope
* #81 Stage D: docs->dedicated PZ/PX + doc-tests regen (22/0); audit factories _docs_prep_basis_x/_docs_surface_syndrome_block18->dedicated gates, GuppyCodegenError expected_failure deleted; #71 re-pinned from ACTUAL _qir_state (BUILD_FAILED 6->5/QIS_OK 23->24: prep_basis_x->QIS_OK; surface_syndrome_block18 stays BUILD_FAILED on its inline Measure(data)>CReg(final) #80 'was not declared at Main scope' reason NOT prep -- pinned honestly; counts+docstring prose); sim hard-rename Pn{X,Y,Z}->PN{X,Y,Z} 8 files + gate_type.rs comments + removed dead .typos.toml Pn (rslib rebuilt; SIM DISPATCH 13/13 + PZ/PNZ/PX state-correct); fixed 5 genuinely-#81 test-fallout (test_ast_hugr->test_hugr_rejects_stray_prep_basis_string asserting converter NotImplementedError stray-string; 4 converter scratch-lifecycle messages Prep->PZ, re-Prepped verb kept to match deterministic sed-renamed test spec). Verified FULL slr_tests: 391 passed, only #87(14 perm/meas legacy-QIR-comment) + #88(9 #74/#80 scalar-var/oversize fail-loud + SX-rx/parallel/array-unpack) failing -- BOTH proven pre-existing-rel-to-#81 (exception-identity + no-prep; pecos/regression+guppy never subset-verified), #81 net-new=0; #71 green; behavioral all-6 PASS; doc-tests 22/0; ruff/black/cargo-fmt clean
* #81 Stage E: behavioral suite test_prep_gates.py -- 6 dedicated prep gates Stim peek_bloch (+-Z/+-X/+-Y) + 6 AST->Guppy->Selene prep-then-rotate(SZdg/H)-then-measure deterministic + emitted-tail QIR/QASM spot-check per _prep_tail + B1 Codex soundness regression (PX in BlockDecl via BlockCall keeps X-basis through flatten_block_calls in QASM&Guppy + |+> via Selene) + sim PN/alias native-basis behavioral (MZ/MX/MY). Verified: fast 410 passed (only #87/#88 pre-existing, 0 outside, #81 net-new=0) + slow prep_gates 7/7 PASS + ruff/black clean
* #81 post-review fold (round-1): Codex blocker -- direct canonical PNX/PNY keys added to statevec/bindings.py (was asymmetric vs PZ/PX/PY/PNZ; Rust dispatchers had all 6) + test_sim_pn_entry_points now 12 cases exercising all 6 direct (incl PNX-MX-1/PNY-MY-1) + 6 aliases. Pickle-surfaced 6th #81-fallout: git mv regression gold preps.Prep.qasm->preps.PZ.qasm (test_Prep builds qubit.PZ; reset-only content unchanged; tests/pecos/regression 105/0, no other gold fallout). 4 non-blocking folded: autosummary rst Prep->6 gates; emitted-tail asserts reset all 6; stray-string regression covers all 6 prep classes (was PZ-dup+PX); stale Prep prose in converter + pz() docstring. Verified: sim 6-direct+aliases 12/12 state-correct; test_Prep PASS; tests/pecos/regression 105/0; full slr_tests only #87/#88 pre-existing (#81 net-new=0); slow prep_gates 7/7; behavioral all-6 PASS; ruff/black clean
* #87/#88 partial: AST-QIR optional_dependency-lane fixes (honest, pre-existing gen_codes->AST-cutover gaps, not #81)
#87: qir._process_permute now emits the legacy-format `; Permutation:`
comment (whole-reg `a <-> b` / per-element `a[0] -> b[1], ...`),
PermuteOp carries whole_register, _block_substitution preserves it.
Element-granularity was already correct via sources/targets. Net:
test_mixed_permutation_qir + test_multiple_permutations_qir now pass.
test_permutation_with_bell_circuit_qir: measurement regex was stale vs
the committed #76-B2 M-B2-static model (it removed bespoke
@mz_to_creg_bit + legacy 1-arg %Result*-returning mz__body; mz now
lowers to standard 2-arg `call void @__quantum__qis__mz__body(%Qubit*,
%Result*)` + read_result + store) -- updated the regex to the current
correct form (QIR is correct; this is the same stale-format
test-update class as #88, user-authorized).
#88: qir.py PARAMETERIZED 1q+2q gate params resolve LiteralExpr before
float() (was a hard TypeError for parallel/literal params); 6
test_slr_phys tests updated to the #74/#76/#80 fail-loud reality
(>64-bit CReg -> NotImplementedError 'has 75 bits'; whole-CReg scalar
arith/conditions -> NotImplementedError 'classical variable').
NOT fixed (surfaced to user, not auto-guessed): 7 #87 tests hard-assert
#76-B2-REMOVED bespoke APIs (@set_creg_bit/@get_creg_bit/@create_creg/
@set_creg_to_int/creg-xor) -- un-passable without reverting the
dual-reviewed M-B2-static model; test_comprehensive_qir_verification
is a real element-wise-Permute silent miscompile (comment emitted but
qubit remap is a no-op; allocator_offsets keyed by reg name, AST never
ported legacy per-element permutation_map); 2 #88 deferred-class
(test_sx_sxdg = #78-deferred unsupported-gate silent-drop;
test_unique_unpacked_names = real Guppy size-1-unpack bug).
Verified: default lane 403/0 (no regression); behavioral prep all-6
24/0; optional_dependency lane 12->8 failed (3 #87 + 5 #88-side fixed);
ruff/black-25.9.0 clean.
* #87 resolved: real Permute realization in AST QIR (static logical relabel)
Root cause (disproven the 'just a missing comment' premise via the
actual emitted QIR + instrumentation): the AST QIR codegen never
realized Permute at all. The old allocator_offsets swap was dead code
-- offsets are {a:0,b:0} (physical base comes from a parent allocator
the swap never touched), and gate lowering never consulted it. Both
element-wise AND whole-register Permute were silent #74-class
no-op miscompiles (a `; Permutation` comment with gates still on the
original qubits).
Fix (mirrors the WORKING Guppy reference -- _emit_permute via the
linearity tracker's `.permute()` atomic relabel; QIR and the Selene
runtime have no permute intrinsic, confirmed by inspection, so a
compile-time relabel is the only mechanism, same as legacy gen_qir's
permutation_map): QirCodeGenContext.permutation_map maps a logical
(reg,index) -> the (reg,index) whose storage it resolves to.
_process_permute expands whole-register/element refs (qreg_sizes +
creg_map), requires the mapping bijective over the same ref set, emits
the legacy-format `; Permutation:` comment, then composes ATOMICALLY
(snapshot old, map[s]=old.get(t,t)) so a whole-register
sources=(a,b)/targets=(b,a) applies once instead of cancelling. Two
single-point chokepoints consult it: get_qubit_index (every qubit
ref) and _creg_bit_ptr (every classical-bit ref) -- decl-time
pre-population sees the empty map so real qubits still allocate 1:1.
Works uniformly for whole-register + element-wise, QReg + CReg.
All 14 permutation/measurement *_qir tests rewritten as POSITIVE
realized-behavior tests, expected QIR pinned from the actual emitted
output (qubit indices deterministic in declaration order; the
#76-B2-removed bespoke @set_creg_bit/@mz_to_creg_bit/@create_creg
helpers replaced by alloca-buffer stores + the standard 2-arg
@__quantum__qis__mz__body(%Qubit*,%Result*)) -- not weakened, not
xfailed. Added test_whole_register_qreg_permutation_realized_qir
guarding the realizable path.
Verified: optional_dependency lane 41 passed (only the 2 #88-deferred
fail: test_sx_sxdg, test_unique_unpacked_names); default lane 403/0
(no regression); behavioral prep all-6 24/0; ruff/black-25.9.0 clean.
* #88: broad unsupported-gate fail-loud (88A, the class #78 deferred) + Guppy unpack xfail (88B)
#88A: qir._process_gate `if qir_name is None: return` was a silent
#74-class drop (18 gates absent from GATE_TO_QIR:
CH/CRX/CRY/CRZ/CY/F/F4/F4dg/Fdg/SX/SXX/SXXdg/SXdg/SY/SYY/SYYdg/SYdg/
SZZdg) -> valid QIR, wrong semantics, qir-qis-uncatchable. Now raises
NotImplementedError. The "broad reliance" #78 feared did NOT
materialise -- blast radius is tiny: test_sx_sxdg -> raises (SX/SXdg
never had a QIR lowering; legacy gen_qir didn't either) + an honest
#71 re-pin (qeclib.generic_check_xyz / generic_check_1flag_ch use the
"XYZ" Check whose Y branch emits CY (no QIR lowering); they were
dishonestly QIS_OK on the silent drop -> moved _EXPECTED_QIS_OK 24->22
into _EXPECTED_BUILD_FAILED "has no QIR lowering"; docstring "(7)" /
n=22, re-pinned from the actual _qir_state(), never guessed).
#88B: test_unique_unpacked_names investigated -> real Guppy-emitter
bug, NOT shallow: slot-local naming f"{allocator}_{index}"
(guppy.py _local_name + guppy_linearity binding init + entry-unpack
LHS -- 3 sites that must agree) collides when an unpack name (`q_0`)
equals another declared register's name; `q_0,q_1 = q` rebinds the
`q_0` param, then `q_0_0, = q_0` raises UnpackableError. A correct fix
needs a single namespace-wide uniquification authority feeding all 3
sites (linearity state has no register/block-decl names) with
corpus-wide local-name churn -- cross-cutting, out of #87/#88 pre-PR
scope. Per the investigate->fix-if-shallow-else-xfail rule:
@pytest.mark.xfail(strict=True) with full findings, tracked.
Verified: default lane 403/0; optional_dependency lane 42 passed /
3 xfailed / 0 failed; behavioral prep all-6 24/0; ruff/black-25.9.0
clean.
* #79: corpus-wide executable QIR validation (generalises #77 Layer D)
For every audit-corpus program qir_to_qis accepts (the live #71
_qir_state() QIS_OK set, now 22), EXECUTE the lowered QIS
(qir_bc -> qir_qis.qir_to_qis -> selene_sim.build -> run_shots(Stim)
via the #77-proven _qis_exec_records) and assert the EXECUTED
classical records -- the end-to-end proof of the B2 M-B2-static
lowering + #74/#78/#87(Permute)/#88A fail-loud line at corpus scale.
The oracle (the hard part): a _MANIFEST classifies all 22 QIS_OK
programs D/P/X, each derived from first principles by reading the
circuit and CONFIRMED-not-reverse-fitted by execution. D (3) =
exact record; P (8) = a HARD invariant (correlation / fixed bits /
small exact value set) asserted over a fixed seed set, must hold
AND be exercised -- NEVER a statistical/tolerance compare; X (11) =
no classical record or no sound first-principles invariant,
documented per program.
Folds the #79 dual PLAN pre-review: blocker 3 -> n_qubits from the
QIR required_num_qubits entry attr (not max-operand-ref, which is 0
for no-op QReg programs and panics selene); blocker 1 -> a
record-shape contract (an X-with-record program that executes to NO
record fails = output-loss miscompile, not silent X). Blockers 1+2
root causes were resolved upstream by #80 (inline/Return-only CReg
fails loud) + #81 (correct PX); prep cases re-derived under the
correct #81 semantics -- docs.prep_basis_x is PX=|+>, one uniform
Z-measure bit -> X, NOT the pre-review's deterministic [0] (which
assumed the old broken Prep('X')=Z-reset). One first-principles
guess corrected BY execution (the design-mandated confirm step):
legacy.multiple_qregs is NOT c1==c2-correlated (all 4 combos
occur); sound invariant is bit-1==0 per CReg, correlation NOT
claimed. Drift guard: candidate set taken live from _qir_state()
QIS_OK; class histogram pinned D=3/P=8/X=11 so a reclassification
is deliberate.
Slow + optional_dependency lane. Verified: 23 passed (22
executable + drift guard); default lane 403/0 unaffected
(deselected); ruff/black-25.9.0 clean. Pending dual post-review
(reviews/dual-79-corpus-executable-postreview-prompt.md).
* #87 post-review fold (Codex blocker): validate expanded Permute refs BEFORE dict construction
Codex post-review NO -- 1 blocker: _process_permute built `mapping`
via `dict(...).update(zip(src_refs,tgt_refs))` and only THEN checked
`set(mapping) != set(mapping.values())`. A duplicate expanded source
collapses in the dict before the check, so a genuinely non-bijective
public Permute compiled instead of failing loud (silent miscompile,
the exact class this branch fights): `Permute([a[0],a[0]],[b[0],a[0]])`
COMPILED.
Fix (Codex's prescription): accumulate the expanded source/target
refs as LISTS, then validate BEFORE building the dict -- reject (a)
duplicate expanded sources, (b) duplicate expanded targets, (c)
src set != tgt set -- then build the map atomically. Regression
test_non_bijective_permute_fails_loud covers both Codex repro
shapes (distinct non-bijective -> "bijective over the same ref
set"; duplicate-source -> "duplicate source ref"). Pickle already
YES; this was Codex's sole blocker.
Verified: both repros now raise NotImplementedented loud; default
lane 403/0; optional_dependency 43 passed / 3 xfailed / 0 failed
(+1 new regression); behavioral prep all-6 24/0; ruff/black-25.9.0
clean.
* Strip reviewer/persona names from code comments; pin duplicate-target Permute guard
Per user direction: code comments/docstrings must not name
reviewers/agents/personas or leak the review-process workflow.
Branch-wide sweep of changed .py files -- replaced "Codex"/
"Pickle"/"pre-review blocker N"/"re-confirm note" etc. with the
durable issue ref (#80/#79/#71/#88A) + the technical reason. No
behavior change (comments/docstrings only). Also folds the #87
non-blocking symmetry note: test_non_bijective_permute_fails_loud
now also pins the duplicate-TARGET guard (was only duplicate-
source + distinct-non-bijective; the target guard was live but
unpinned).
Verified: default 403/0; optional_dependency 43 passed / 3
xfailed / 0 failed; slow #79 lane 23/23; ruff/black-25.9.0 clean;
branch-wide grep for persona/review-process terms in *.py is
empty.
* #93 (partial): verified QIR lowering for SX/SXdg/SY/SYdg (executable-Clifford)
The user-chosen verified-safe subset of the 18 #88A fail-loud gates.
Methodology (no guessing): extract each gate's authoritative unitary
from the PECOS StateVec simulator, search the EXECUTABLE Clifford
primitive set for a sequence equal up to a global phase, then verify
end-to-end through qir_to_qis -> selene.
Key correctness finding: decomposing to rx/ry/rz is WRONG --
`__quantum__qis__rx__body` is a pinned build/exec failure
(`docs.rotation_rx`) and selene's Stim backend silently no-ops an
rx, so a rotation lowering is itself a silent miscompile (the
behavioral check caught this -- SX;SX gave 0 not 1). Correct
executable-Clifford lowering (verified up-to-phase vs PECOS sim AND
end-to-end): SX=H;S;H, SXdg=H;Sdg;H, SY=H;X, SYdg=H;Z. `_GATE_DECOMP`
+ a pre-fail-loud branch in `_process_gate` (re-emits the primitive
sequence; F-family/CY/CH/CR*/sqrt-2q stay fail-loud pending the
scoped workstream). test_sx_sxdg flipped raises->positive.
test_sqrt_clifford_gates_executable (slow+optional_dependency) pins
the end-to-end identities SX;SX==X / SXdg;SX==I / SY;SY==Y /
SYdg;SY==I + Z-randomness of the single gates.
#71/#79 corpus buckets UNCHANGED (no curated case uses SX/SY; the
CY-bearing generic_check programs stay BUILD_FAILED). Verified:
default 403/0; optional_dependency 43 passed / 3 xfailed / 0 failed;
slow 24 passed (22 #79 + manifest-cover + sqrt_clifford);
ruff/black-25.9.0 clean.
* Fix dead test module: rename tier2_semantic.py -> test_tier2_semantic.py so pytest collects it
Integrity finding: tier2_semantic.py did not match pytest's
test_*.py pattern and there is no python_files override, so it was
only ever imported as a helper -- its tests NEVER ran in any lane.
That silently killed #77's test_tier2_executable_differential
(the QIR->qir_to_qis->selene executable differential) AND a whole
module of #74/#80/#81 fail-loud regression guards
(test_varexpr/while/print/oversize/inline-creg/prep-stray-string
_raises_loud). A "verified" gate that never runs is faked-green.
Proper fix (no python_files hack): git mv to test_tier2_semantic.py
+ update the one real importer (test_qir_corpus_executable.py)
and the stale docstring prose in test_qir_spec_compliance.py.
The revived tests were NOT bit-rotted -- they pass once actually
collected.
Verified: default lane 403 -> 413 (+10 revived fast guards, all
green); optional_dependency 43 passed / 3 xfailed / 0 failed; slow
33 passed / 0 failed (incl. the revived test_tier2_semantic_b2 +
test_tier2_executable_differential + #93 sqrt_clifford);
ruff/black-25.9.0 clean.
* A2: fix the Permute silent-miscompile in the Stim + QuantumCircuit codegens (same class as #87 QIR)
Both AST codegens had the identical pre-#87 broken pattern: a
`allocator_offsets` swap that NEVER reached gate qubit-index
resolution (element-wise refs are keyed by element string, not reg
name -> no-op) and self-cancelled for a whole-register
(a,b)/(b,a) pair. Confirmed: `Permute([a[0],b[0]],[b[0],a[0]])`
then `Y(a[0]); Z(b[0])` emitted `Y 0; Z 2` (Stim) / `Y:{0},Z:{2}`
(QC) instead of the correct `Y 2; Z 0` -- a silent miscompile,
the exact class #87 fixed in QIR (QASM was already correct).
Fix: port the proven #87 `permutation_map` model to both -- a
static logical relabel consulted in `get_qubit` (the single
qubit-ref chokepoint), built by `_process_permute` from expanded
refs with the post-#87-fold validation (dup-source / dup-target /
src-set!=tgt-set all fail loud) and atomic compose. Whole-register
CReg Permute fails loud (Stim/QC have no classical-register
model). Mirrors the Guppy linearity tracker / QIR #87.
Regression: test_permute_realized_quantum_circuit (default lane)
+ test_permute_realized_stim (optional_dependency) pin element +
whole-register realized targeting and the non-bijective fail-loud.
Verified: default 413->414 (+1 QC regression); optional_dependency
44 passed / 3 xfailed / 0 failed (+1 Stim regression); slow 33/0
(no regression); ruff/black-25.9.0 clean. No pre-existing test had
pinned the broken behavior (unlike QIR's 14 #87 tests).
* A: Stim unsupported-gate silent-skip -> fail loud (#78 surfaced-not-changed analogue of #88A)
stim._process_gate `if stim_gate is None: return` silently dropped
12 gates (CH/CRX/CRY/CRZ/F/F4/F4dg/Fdg/RX/RY/RZ/RZZ) -> the
emitted Stim circuit ran with WRONG semantics (a #74-class silent
miscompile, uncatchable downstream). Now raises NotImplementedError
("has no Stim lowering"), same doctrine as #74/#78/#88A. Stim is
Clifford-only so non-Clifford rotations are fundamentally
unrepresentable; the bug was emitting the circuit without them.
Regression test_unsupported_gate_fails_loud added.
QC investigated, NOT changed: it does not silent-skip -- it
name-passes-through (`.get(gate, gate.name)`); the 7 non-param
fallbacks are valid PECOS-sim gates (correct), and parameterized
gates already FAIL LOUD via the QuantumCircuit container's
"requires N angle(s), got 0" ValueError (not a silent miscompile).
No scope creep / no capability regression.
Blast radius ZERO (the #78 "broad reliance" fear did not
materialise in the corpus, same as #88A for QIR): default 414->415
(+1 regression), optional_dependency 44 passed / 3 xfailed / 0
failed, ruff/black-25.9.0 clean.
* Post-review fold (Codex blocker): complete the branch-wide persona/process-name strip from .py comments
Codex post-review NO -- 1 blocker: the 663c0f77 claim (and my
session claim) of a *branch-wide* clean was false; ~30 added .py
comment/docstring lines outside that 5-commit range still named
reviewers/process ("Codex S2 review", "post-review blocker",
"re-confirm bug", "Codex #81 ...", etc.) in 10 files
(guppy.py/converter.py/nodes.py/check_1flag.py/migrate_implicit_
return.py + 5 test modules). Pickle YES (read the claim
narrowly as changed-files-only); Codex's branch-wide reading
matches the durable rule -- fold it.
Stripped every occurrence, replacing with the durable issue ref
+ technical reason (e.g. "#80 re-confirm bug" -> "#80
name-collision bug"; "(Codex S2 review)" -> dropped, surrounding
text already states the rationale; "confirmed Codex 2026-05-16"
-> "confirmed 2026-05-16"). Comments/docstrings ONLY -- zero
behavior change.
Verified: `git diff dev...HEAD -- '*.py'` added lines now match
none of Codex/Pickle/grug/Banana/pre-review/post-review/
re-confirm; default 415/0, optional_dependency 44 passed / 3
xfailed / 0 failed, slow 33/0, ruff/black-25.9.0 clean.
* #93 (cont.): verified QIR lowering for the F/Fdg/F4/F4dg face Cliffords
Same verification-first method as the SX/SY subset: extracted each
unitary from the PECOS StateVec sim, searched the EXECUTABLE
Clifford set for a sequence equal up to a global phase, verified
end-to-end through qir_to_qis -> selene. Results (circuit order):
F=SZdg;H, Fdg=H;SZ, F4=H;SZdg, F4dg=SZ;H.
_GATE_DECOMP entries (incl. the existing SX/SXdg) now use
GateKind.SZ/SZdg rather than the redundant S/Sdg aliases, per the
PECOS convention that S==SZ / Sdg==SZdg (both already map to
"s"/"s__adj" in every codegen -- zero functional change;
re-verified). test_face_clifford_gates_executable pins the
end-to-end identities: F;Fdg / Fdg;F / F4;F4dg / F4dg;F4 == I,
the F|0>=|+> non-no-op discriminator, and F;F;F == I (PECOS F is
the order-3 face Clifford).
#71/#79 corpus buckets unchanged. Verified: default 415/0,
optional_dependency 44 passed / 3 xfailed / 0 failed, slow
face+sqrt 2/2 (full slow unaffected), ruff/black-25.9.0 clean.
Remaining #93: 2q Clifford (CY/SXX/SYY/SXXdg/SYYdg/SZZdg) need a
2q decomposition path; CH (non-Clifford) + CRX/CRY/CRZ
(rotations, no executable primitive) stay fail-loud.
* #95: canonicalize GateKind on SZ/SZdg; remove redundant S/Sdg (user-directed)
PECOS convention: the phase gate S == SZ and Sdg == SZdg. GateKind
carried both as redundant members. Per user directive, removed
GateKind.S/Sdg; the SLR `S`/`Sdg` gate classes now map (converter
GATE_KIND_MAP) to the canonical GateKind.SZ/SZdg. Dropped the now
redundant S/Sdg rows from every codegen map (GATE_TO_QIR/STIM/QC/
QASM/guppy -- SZ/SZdg rows already present), type_checker arity,
gate_properties INVERSE_PAIRS (SZ<->SZdg pair remains), and
_prep_tail PY/PNY tails. _GATE_DECOMP already on SZ/SZdg.
Behavior delta (user-decided -- "keep SZ->rz(pi/2)"): the SLR `S`
gate's QASM lowering changes `s q;` -> `rz(pi/2) q;` (and `sdg` ->
`rz(-pi/2)`), since the canonical kind SZ maps to rz(pi/2) in
qasm.py (the only codegen where S != SZ; physically identical, s
== rz(pi/2) up to global phase). All other codegens unchanged
(SZ == S there). #81 PY/PNY prep-tail QASM accordingly flips
h;s -> h;rz(pi/2); test_prep_gate_emitted_tail[PY,PNY] re-pinned
honestly from the actual emitted output (QIR needles unchanged --
SZ->"s" in QIR).
18 GateKind.S/Sdg refs across 10 source files; 0 residual; no test
referenced GateKind.S/Sdg directly. Verified: default 415/0,
optional_dependency 44 passed / 3 xfailed / 0 failed, slow 34/0,
ruff/black-25.9.0 clean. Pending dual review (repo-wide rename +
deliberate QASM delta).
* test hygiene: dedupe the repeated QC expected-string into a local (behavior-identical; clears the dirty worktree the #93/#95 reviewers flagged)
* #93 cont.: verified QIR lowering for SZZ/SZZdg/SXX/SXXdg/SYY/SYYdg/CY (7 2q Cliffords)
Per ~/Repos/qir-qis/src/lib.rs:59 (ALLOWED_QIS_FNS, 23 functions):
the qir-qis native 2q gate is rzz (parameterized) -- there is NO
zz/szz in the allowlist (the prior `GATE_TO_QIR[SZZ]="zz"` emitted
`__quantum__qis__zz__body` which qir-qis rejects with
"Unsupported QIR QIS function"). Quantinuum supports ZZ-flavor
entanglement natively, just as `rzz`. Fixed:
- Removed the misleading `GateKind.SZZ: "zz"` from `GATE_TO_QIR`
(and `SZZ` from `TWO_QUBIT_GATES`) -- no more misleading
codegen surface.
- Extended `_GATE_DECOMP` to a 2q-capable shape: each step is
(prim_kind, qubit_idx_tuple, params_tuple); `_process_gate`
emits each step with the input gate's targets routed and
constant params threaded.
- Added 7 verified decompositions:
SZZ = RZZ(pi/2)(q0,q1)
SZZdg = RZZ(-pi/2)(q0,q1)
SXX = (H@H); RZZ(pi/2); (H@H)
SXXdg = (H@H); RZZ(-pi/2); (H@H)
SYY = (Sdg@Sdg);(H@H); RZZ(pi/2); (H@H);(S@S)
SYYdg = (Sdg@Sdg);(H@H); RZZ(-pi/2); (H@H);(S@S)
CY = Sdg(t); CX(c,t); S(t)
Each VERIFIED up-to-global-phase against the PECOS StateVec
unitary AND end-to-end via qir_to_qis -> selene (inverse pairs,
CY|10> -> i|11> non-vacuity, SZZ^2 = Z discriminator).
- `test_sqrt_pauli_2q_gates_executable` (slow+optional_dependency)
pins the end-to-end identities.
Methodology correction recorded earlier in memory: my prior "rx is
a silent no-op on the Stim backend" claim was wrong (probe-harness
bug). RX/RY/RZ/RZZ all execute correctly. The truly misleading
surface was only SZZ -> "zz" (now fixed).
#71 honest re-pin: BUILD_FAILED 7 -> 5, QIS_OK 22 -> 24
(generic_check_xyz / generic_check_1flag_ch use CY which now
lowers, so they re-enter QIS_OK). #79 manifest updated: both
classified X (XYZ stabiliser on |0..0> is not an eigenstate ->
single uniformly random syndrome bit; no hard invariant);
histogram pin D=3/P=8/X=11 -> X=13.
Verified: default 415/0, optional_dependency 44 passed / 3
xfailed / 0 failed, slow 28/0 (24 QIS_OK + manifest guard +
sqrt/face/sqrt_pauli_2q behavioral), ruff/black-25.9.0 clean.
Remaining #93 (still fail-loud, no PECOS sim oracle): CH (sim
doesn't support), CRX/CRY/CRZ (parameterized, sim doesn't
support) -- decomposition would require a convention pin without
a verification oracle. Surfaced for separate scope.
* #93 post-review fold (Codex blocker): add phase-sensitive CY interference assertion
Codex post-review NO -- 1 blocker: the committed CY executable
test (X q0; CY; M q1 -> 1; CY|00>; M q1 -> 0) does not
discriminate the CY target-phase decomposition. Mutation probe
(swap CY's `Sdg(t); CX; S(t)` -> `S(t); CX; S(t)`) SURVIVED the
test, violating the prompt's explicit non-vacuity requirement.
Pickle YES (the unitary verification covers it; the e2e is a
secondary guard) -- but the test should still fail under that
mutation per the prompt + branch discipline.
Folded: added the phase-sensitive interference assertion Codex
prescribed -- `H q1; CY(q0, q1); H q1; MZ q1` with q0=|0> must
give 0. Verified: committed PASSes; the mutated S;CX;S CY
decomposition now FAILS the test (the assertion message names
the exact mutation it catches). Mechanism: with q0=|0> the
correct CY is identity, so H;I;H = I -> 0; the mutated S;CX;S
applies S^2 = Z on q1 even when control=0 (since CX is identity
then), giving H;Z;H = X -> 1.
Verified: default 415/0, optional_dependency 44 passed / 3
xfailed / 0 failed, slow sqrt_pauli_2q PASSes, ruff/black clean.
* #93 re-confirm fold (Codex blocker): COM812 trailing comma on the new CY assertion
Codex re-confirm NO -- 1 trivial blocker: the folded CY
interference assertion's multi-line set literal `{\n (0,)\n}`
(black-reformatted) triggers ruff COM812. The assertion was
semantically correct (Codex verified it catches both the
prescribed `S;CX;S` mutation AND the symmetric `Sdg;CX;Sdg`),
but `uv run ruff` (the project version, the authoritative gate
-- distinct from `uvx ruff@latest` which didn't flag it) was
red. Added the trailing comma via `ruff --fix`. Lanes unchanged:
default 415/0, optional_dependency 44/3xf/0, slow sqrt_pauli_2q
1/0; ruff + black-25.9.0 clean.
* #93 cont.: verified 1-CX QIR lowering for CH (non-Clifford -- Quest backend)
* #93 cont.: verified 1-RZZ QIR lowering for CRX/CRY/CRZ + PECOS oracles
* #88B: Guppy slot-local name disambiguation against declared register names
* Cross-codegen: verified Stim decomposition for F/Fdg/F4/F4dg face Cliffords
* Cross-codegen: native CRX/CRY/CRZ in ArbitraryRotationGateable + QC param-threading
* Cross-codegen: CRX/CRY/CRZ in CuStateVec/CudaStateVec/MPS simulator backends
* Cross-codegen post-review fold (Codex blocker): CR* control-superposition phase test
* #94 B1: classical-variable (whole-CReg scalar) QIR lowering via shared i64 pack
* Cross-codegen Guppy Phase A: native SX/SXdg (v/vdg) + RX/RY/RZ/CRZ rotations
* Cross-codegen Guppy Phase B: decompose SY/SYdg/F-family/sqrt-Paulis/CRX/CRY (native zz_phase)
* Post-review folds (Codex non-blockers): Guppy decomposed-rotation missing-angle fails loud + B1 LoopVar comment accuracy
* #97: SLR API -- rotational gates use angle-first RX(theta, q), remove bracket form
* #97 followup: fix stale test_control_flow_qir comment (rx now lowers; build-only is the non-Clifford-on-Stim caveat)
* #97 post-review fold (Codex blocker): fail loud on mis-ordered angle-first calls (SLR __call__ type guard + QIR/QASM arity guards)
* #97 post-review fold: narrow qarg guard to quantum qubit types (Codex reconfirm blocker)
* Add SLR v2 typed-angle API: rotation gates take rad()/turns() over the exposed angle64 dtype
* Make just lint pass: typos false-positive exemptions, black 26.x reformat, awk-portable GHA pinning check
* Harden GHA pinning check: bind the 40-hex SHA to the uses: ref so a comment SHA can't mask an unpinned action (Codex blocker)
* Strip internal planning/issue references and persona names from code comments, keeping the technical reasons
* Strip remaining internal stage labels and string-literal planning references from comments and messages, keeping public-tracker refs
* Strip remaining internal milestone labels from comment and message strings (R4/O1-2/S2-S3/Phase 3a-b/M-B2/iter)
* Strip milestone Phase/iter references from comments and messages, keeping the two-phase BlockCall algorithm labels1 parent fa15304 commit f559e60
227 files changed
Lines changed: 18044 additions & 21943 deletions
File tree
- crates
- pecos-core/src
- pecos-llvm/src
- pecos-simulators
- src
- tests
- docs
- development
- user-guide
- examples
- python_examples
- surface
- python
- pecos-rslib-cuda/src
- pecos-rslib-llvm/src
- pecos-rslib/src
- quantum-pecos
- docs/reference/_autosummary
- src/pecos
- circuit_converters
- circuits
- qec/surface
- simulators
- cuda_statevec
- custatevec
- mps_pytket
- statevec
- slr
- ast
- analysis
- codegen
- optimizations
- validation
- converters
- gen_codes
- guppy
- qeclib
- color488
- syn_extract
- generic
- qubit
- steane
- gates_sq
- gates_tq
- meas
- preps
- syn_extract
- surface/macrolibs/preps
- transforms
- tests
- pecos
- integration/state_sim_tests
- regression/test_qasm
- examples
- pecos/qeclib
- qubit
- steane
- meas
- preps
- qec
- syn_extract
- random_cases
- regression_qasm
- slr
- ast_tests
- analysis
- optimizations
- validation
- unit
- qec
- surface
- selene
- slr_tests
- ast_guppy
- guppy
- pecos
- regression/random_cases
- unit/slr
- slr/pecos/unit/slr
- scripts
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
12 | 11 | | |
13 | 12 | | |
14 | 13 | | |
| |||
42 | 41 | | |
43 | 42 | | |
44 | 43 | | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
148 | 148 | | |
149 | 149 | | |
150 | 150 | | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
151 | 191 | | |
152 | 192 | | |
153 | 193 | | |
| |||
668 | 708 | | |
669 | 709 | | |
670 | 710 | | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
671 | 740 | | |
672 | 741 | | |
673 | 742 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
100 | | - | |
| 100 | + | |
101 | 101 | | |
102 | | - | |
| 102 | + | |
103 | 103 | | |
104 | 104 | | |
105 | | - | |
| 105 | + | |
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
226 | 226 | | |
227 | 227 | | |
228 | 228 | | |
229 | | - | |
| 229 | + | |
230 | 230 | | |
231 | 231 | | |
232 | 232 | | |
| |||
236 | 236 | | |
237 | 237 | | |
238 | 238 | | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
239 | 271 | | |
240 | 272 | | |
241 | 273 | | |
| |||
717 | 749 | | |
718 | 750 | | |
719 | 751 | | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
720 | 852 | | |
721 | 853 | | |
722 | 854 | | |
| |||
766 | 898 | | |
767 | 899 | | |
768 | 900 | | |
| 901 | + | |
| 902 | + | |
| 903 | + | |
| 904 | + | |
| 905 | + | |
| 906 | + | |
| 907 | + | |
| 908 | + | |
| 909 | + | |
| 910 | + | |
| 911 | + | |
| 912 | + | |
| 913 | + | |
| 914 | + | |
769 | 915 | | |
Lines changed: 70 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
254 | 254 | | |
255 | 255 | | |
256 | 256 | | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
257 | 327 | | |
258 | 328 | | |
259 | 329 | | |
| |||
0 commit comments