Default HybridEngine to the Rust classical interpreter, with parity fixes and #345 engine fail-fast#347
Merged
Merged
Conversation
… and make the no-return measurement index absolute across batches (#345)
…op-level and reconciling stale value-representation expectations
… (signed i(S+1) needs size <= 31), exposed by the new fail-fast on invalid definitions
…uzz iterations, and a QASM-generated-PHIR differential
… foreign_func_calls collector
…all checks in the Rust wrapper
…thon' escape hatch
…conflicting variable redefinitions
…r_validate a clear error on standalone installs
… PyPHIR limitation in the docstring
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.
Fixes #345.
This PR does three things: (1) defaults
HybridEngineto the Rust classical interpreter with full boundary parity to the Python one, (2) applies the #345 engine fail-fast/correctness fixes, and (3) revives a tree of orphaned integration tests found along the way.HybridEngine's default classical interpreter is now the Rust-backedRustPhirClassicalInterpreter, replacing the pure-PythonPhirClassicalInterpreter. It is behaviorally equivalent for valid programs and is differentially validated against the Python interpreter (~3,200 fuzzed programs, a QASM-generated-PHIR differential, and exact exception-type parity tests).Escape hatch: to restore the previous behavior, construct the engine with
HybridEngine(cinterp="python").The Rust interpreter's boundary behavior was brought to parity so the switch is not silent:
phir_validatenow schema-validates on the Rust path (it was previously inert).PyPHIR) raise a clearTypeErrorinstead of a crypticAttributeError.ZeroDivisionError; reading or writing an undefined classical variable →KeyError.KeyErrorrather than silently auto-creating them. This matches the previous Python default, so it does not regress existing production behavior.check_ffcfails fast when a program calls a foreign function the supplied object lacks, or when no foreign object is supplied.run_multisimworks withto_dict-capable foreign objects (e.g.WasmForeignObject): the Rust interpreter now supports detaching its foreign object (foreign_obj = None) for pickling, as the multiprocessing helper requires.phir_validate=True(the default), the Rust interpreter needspecos.typingfromquantum-pecos; standalonepecos_rslibinstalls get a clearImportErrornaming the requirement (or setphir_validate=False).PhirJsonEnginemeasurement writes beyond a register's declared size now fail fast (previously silently masked between declared size and backing width), and conflicting variable redefinitions (same name, different type/size) are rejected.Rust API note:
PecosErrorgains aRuntimeUndefinedVariablevariant — source-breaking for downstream code that exhaustively matches the enum (0.x pre-release).Known deferred divergences that do not affect the default path (documented for follow-up): custom
op_processors that emit block structures, the separatePhirJsonEngineengine's remaining leniency, unreachable Rust-only operators,PyPHIRprogram objects (clearTypeError; use a dict/JSON orcinterp="python"), and residual exception-type differences on malformed-program paths.Engine fixes (#345)
All three were pre-existing on
devand confined to secondary/engine/fallback paths (the primary interpreter surface was already sound).PhirJsonEngine::from_jsonand the exec loop propagatehandle_variable_definitionerrors (?instead oflet _ =), so e.g. a signedsize == 64register errors at construction instead of being swallowed and silently recreated asi32.add_quantum_variableis now idempotent (skip-if-exists) likeadd_classical_variable, so only genuine definition errors propagate (the constructor-pre-define + loop-re-define pattern relied on tolerating the benign re-define).store_measurement_resultreturnsResultand propagatesadd_variable/set_biterrors instead of log-and-continue.measurements_appliedcounter (reset inreset()) keeps the legacymeasurement_Nvars and the no-returnmfallback correct when a shot's measurements split across batches. The primary explicit-return path is unchanged.Three Rust regression tests added.
Revived orphaned integration tests
crates/pecos-phir-json/tests/had ~13 integration test files organised into subdirectories, but with no top-leveltests/*.rsharness and no[[test]]entries — so cargo never compiled or ran them (they'd silently rotted; #339 even edited some assuming they ran). Moved them back to top-level so cargo runs them, and reconciled expectations that had drifted against #339's value/result representations (ExprValue; register results are nowBitVec, notData::U32— same values, new form). Verified every reconciled value against runtime output (no papering-over). One test was reading a signed register viaas_u64(raw bits) instead ofas_i64(signed value) — fixed the test, not the code (as_u64/as_i64are deliberately distinct views and load-bearing across rendering/export/parity). Also cleaned up the pre-existing clippy lints these never-linted files carried.Net: ~41 previously-dormant integration tests now run in CI.
Verification
pecos-phir-json: 91 lib + full integration tests pass, clippy (1.97) and fmt clean. Against the rebuilt extension: 203 PHIR/parity Python tests (incl. exact exception-type + undeclared-variable parity), 1036 pecos-rslib (non-performance), and the engine/hybrid/interp surface all pass. The default switch went through a 2-arm deep-review plus two adversarial re-review rounds; findings and deferred items are recorded in the design/review notes.