feat(ppvm-vihaco): add circuit component crate#173
Open
david-pl wants to merge 2 commits into
Open
Conversation
Introduce the `ppvm-vihaco` crate with just the circuit component slice: - `component.rs` — the `Circuit` component: dispatches `vihaco_circuit_isa::CircuitInstruction`s to a tableau or PauliSum backend, emitting measurement / trace effects (includes the R gate and trace/reset dispatch now that RotXY and tableau expectation/reset are on `main`). - `measurements.rs` — measurement / trace effect + observer types. - `device_info.rs` — `PPVM_MAGIC`, `BackendKind`, `PPVMDeviceInfo`, relocated out of the composite so `component` compiles without the composite machine (which lands in a follow-up PR). - `lib.rs` — module wiring for the component-only slice; the composite-driven helpers (load/run/dump/parse) come with the composite. `chumsky` / `vihaco-parser-core` are transitive through the `vihaco_parser::Parse` derive on `BackendKind`, so they carry a machete ignore. Includes standalone smoke tests for the tableau backend (single-qubit flip and CNOT propagation via the component's instruction dispatch); the fuller integration coverage arrives with the composite's `.sst` fixtures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Contributor
There was a problem hiding this comment.
Pull request overview
Introduces a new workspace crate ppvm-vihaco containing the circuit component layer for PPVM’s vihaco stack: a Circuit component that dispatches vihaco_circuit_isa::CircuitInstruction to one of three execution backends (tableau / PauliSum / lossy PauliSum), emitting measurement and trace outcomes as effects. This PR is scoped to the component slice so it can compile standalone ahead of the composite VM PRs.
Changes:
- Added the new
ppvm-vihacocrate with backend-dispatchingCircuitcomponent and executors. - Added shared device-configuration types (
PPVM_MAGIC,BackendKind,PPVMDeviceInfo) and measurement/trace effect types + observers. - Wired the crate into the Rust workspace (members + lockfile updates).
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/ppvm-vihaco/src/measurements.rs | Adds measurement/trace outcome effect types and observer structs; introduces union effect type for circuit outcomes. |
| crates/ppvm-vihaco/src/lib.rs | New crate root wiring modules + re-exports/prelude. |
| crates/ppvm-vihaco/src/device_info.rs | Defines backend selection + device-info struct used to configure the circuit component. |
| crates/ppvm-vihaco/src/component.rs | Core implementation: backend executors, dispatch logic, effect emission, and smoke tests. |
| crates/ppvm-vihaco/Cargo.toml | Declares new crate dependencies and machete ignores for derive-macro-driven deps. |
| Cargo.toml | Adds crates/ppvm-vihaco to workspace members. |
| Cargo.lock | Records the new crate and its dependency resolution. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+8
to
+14
| /// Measurement results are represented as an integer enum: | ||
| /// 0: state |0> | ||
| /// 1: state |1> | ||
| /// 2: qubit has been lost prior to measurement | ||
| /// In bytecode, this is represented as a u32 integer, which is simpler than | ||
| /// e.g. two boolean values and matches semantics elsewhere | ||
| #[repr(u8)] |
Comment on lines
+584
to
+589
| } else if n_qubits <= 2048 { | ||
| let tab = GeneralizedTableau::new(n_qubits, coefficient_threshold); | ||
| Self::Bits2048(CircuitExecutor { tab }) | ||
| } else { | ||
| panic!("No matching executor for {} qubits", n_qubits); | ||
| } |
Comment on lines
+611
to
+615
| } else if n_qubits <= 2048 { | ||
| seeded!(Bits2048) | ||
| } else { | ||
| panic!("No matching executor for {} qubits", n_qubits); | ||
| } |
Comment on lines
+700
to
+704
| } else if info.n_qubits <= 2048 { | ||
| build!(Bits2048, 256) | ||
| } else { | ||
| panic!("No matching PauliSum executor for {} qubits", info.n_qubits); | ||
| } |
Comment on lines
+786
to
+793
| } else if info.n_qubits <= 2048 { | ||
| build!(Bits2048, 256) | ||
| } else { | ||
| panic!( | ||
| "No matching LossyPauliSum executor for {} qubits", | ||
| info.n_qubits | ||
| ); | ||
| } |
Comment on lines
+960
to
+984
| /// Smoke test: a single-qubit gate dispatches to the tableau backend and | ||
| /// flips the qubit. | ||
| #[test] | ||
| fn tableau_backend_x_flips_qubit() { | ||
| let mut circuit = Circuit::tableau(&info(1)); | ||
| circuit | ||
| .execute_instruction(&CircuitInstruction::X, &CircuitMessage::Qubit(0)) | ||
| .unwrap(); | ||
| assert_eq!(measure(&mut circuit, 0), MeasurementOutcome::One); | ||
| } | ||
|
|
||
| /// Smoke test: a two-qubit gate dispatches correctly — `X(0); CNOT(0, 1)` | ||
| /// leaves both qubits in |1⟩. | ||
| #[test] | ||
| fn tableau_backend_cnot_propagates_flip() { | ||
| let mut circuit = Circuit::tableau(&info(2)); | ||
| circuit | ||
| .execute_instruction(&CircuitInstruction::X, &CircuitMessage::Qubit(0)) | ||
| .unwrap(); | ||
| circuit | ||
| .execute_instruction(&CircuitInstruction::CNOT, &CircuitMessage::TwoQubit(0, 1)) | ||
| .unwrap(); | ||
| assert_eq!(measure(&mut circuit, 0), MeasurementOutcome::One); | ||
| assert_eq!(measure(&mut circuit, 1), MeasurementOutcome::One); | ||
| } |
The size-bucketed circuit constructors panicked when n_qubits exceeded the widest executor bucket. Since n_qubits comes from user-supplied device headers and this backend drives a TUI, a panic would tear down the terminal rather than surface a friendly message. Return eyre::Result from the four constructor pairs (Tableau/PauliSum/LossyPauliSum, plus new_with_seed) so callers can report the error instead of aborting. Add a MAX_QUBITS constant so the ceiling is single-sourced across the bucket checks and error messages. Default stays infallible (0 qubits always fits the smallest bucket). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
david-pl
added a commit
that referenced
this pull request
Jul 8, 2026
…init_inner The circuit constructors now return eyre::Result (merged from #173), so PPVM::init_inner propagates with `?`. It already returns eyre::Result<()> and reports device-header errors, so this just flows the construction error (e.g. n_qubits > MAX_QUBITS) into the same path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Merge order
Part of the #168 split. This PR targets #169 (
david/42.1-vihaco-isa), notmain, and stacks on the core prerequisites:main✅ mergedmain✅ mergedmain(open; hasmainmerged in, so it carries feat(gates): add RotXY in-plane single-qubit rotation (R gate) #170 + feat(tableau): add Pauli expectation/trace and reset_all #172).pyistubs (feat(python-native): add type stubs for the native _core module #171) →main(open; independent of this line)david/42.3-composite) → this PRSummary
Introduces the
ppvm-vihacocrate with just the circuit component slice — the piece that dispatches gates to a backend. The composite machine that drives it (VM, bytecode, syntax, shots) is the next PR.component.rsCircuit— dispatchesCircuitInstructions to a tableau / PauliSum / lossy-PauliSum backend, emitting measurement + trace effects (incl. the R gate and trace/reset, now that #170/#172 are inmain)measurements.rsdevice_info.rsPPVM_MAGIC,BackendKind,PPVMDeviceInfo— relocated out of the composite so the component compiles standalone; the composite PR imports them from herelib.rsload/run/dump/parsehelpers come with the compositeNotes
device_info.rs: the wholePPVM_MAGIC+BackendKind+PPVMDeviceInfocluster moved (the struct can't compile without the other two). In PauliSum printing should properly display small floating points #5,composite.rsimports these instead of defining them.chumsky+vihaco-parser-core— pulled in only via thevihaco_parser::Parsederive onBackendKind(mirrors the isa crate).Testing
cargo test -p ppvm-vihaco— 2 smoke tests (single-qubit flip + CNOT propagation through the component's dispatch). Fuller integration coverage arrives with the composite's.sstfixtures. Full workspace builds; clippy + machete clean.