feat(ppvm-vihaco): add the composite machine and VM#174
Open
david-pl wants to merge 8 commits into
Open
Conversation
Complete the `ppvm-vihaco` crate on top of the circuit component:
- `composite.rs` — the `PPVM` composite: the vihaco machine (CPU + program
loader) driving the circuit component, measurement/trace observers, backend
selection from the device header, resets, and single-instruction stepping.
Imports `BackendKind` / `PPVMDeviceInfo` / `PPVM_MAGIC` from `device_info`
and re-exports them so existing `crate::composite::{…}` paths keep resolving.
- `syntax.rs` — `.sst` header parsing / lowering (`PPVMHeader`, `PPVMResolver`).
- `bytecode.rs` — `.ssb` bytecode emission / loading (magic-tagged).
- `observable.rs` — observable-header parsing for the PauliSum backends.
- `shots.rs` — multi-shot execution (serial + optional rayon parallelism).
- `lib.rs` — full module wiring plus the `load`/`run`/`dump`/`compile`/`parse`
helpers and `PPVMModule` type.
- `Cargo.toml` — adds `vihaco-cpu`, `log`, and the optional `rayon` feature.
Includes the `.sst` integration fixtures (bell, GHZ, function calls, branching,
trotter truncation, loss/trace, rotations) and the composite half of the
PauliSum reset.
Tests: `cargo test -p ppvm-vihaco` — 111 unit + 28 integration; green with and
without `--features rayon`. clippy + machete clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR completes the ppvm-vihaco crate by adding the composite PPVM machine/VM layer on top of the existing circuit component, plus end-to-end tooling (text syntax, bytecode format, observable parsing, and multi-shot execution) and a substantial .sst fixture suite to exercise the public API.
Changes:
- Introduces the
composite::PPVMmachine wiring (vihaco-cpu+ circuit component + observers) with stepping, file/program loading, and REPL-style single-instruction execution. - Adds
.sstparsing/lowering (syntax.rs) and.ssbbytecode framing with magic/version/device-info (bytecode.rs), plus observable-header parsing (observable.rs). - Adds shot runner utilities (
shots.rs) and 28-ish end-to-end.sstfixtures with an integration harness (sst_fixtures.rs).
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/ppvm-vihaco/src/composite.rs | New composite PPVM machine: loader+CPU+circuit, stepping/run, observers, REPL instruction injection, device init. |
| crates/ppvm-vihaco/src/syntax.rs | .sst header + instruction parsing and module resolution (labels/branches/calls, string table lowering). |
| crates/ppvm-vihaco/src/bytecode.rs | .ssb container format read/write for resolved modules (magic/version/device header + strings + code). |
| crates/ppvm-vihaco/src/observable.rs | Parser for device circuit.observable into Pauli-sum terms used by PauliSum backends. |
| crates/ppvm-vihaco/src/shots.rs | Serial/parallel multi-shot execution API and deterministic per-shot seeding. |
| crates/ppvm-vihaco/src/lib.rs | Public module wiring and helper APIs (parse/compile/load/run/dump) plus PPVMModule alias. |
| crates/ppvm-vihaco/Cargo.toml | Adds vihaco-cpu, log, optional rayon feature, and dependency wiring. |
| Cargo.lock | Locks new deps (vihaco-cpu, log, rayon) into the workspace lockfile. |
| crates/ppvm-vihaco/tests/sst_fixtures.rs | Integration harness that runs .sst fixtures (and dump→load round-trips) via public PPVM APIs. |
| crates/ppvm-vihaco/tests/bell.sst | Fixture: Bell prep + measurements. |
| crates/ppvm-vihaco/tests/hello_circuit.sst | Fixture: small gate sequence without measurement. |
| crates/ppvm-vihaco/tests/rotxy.sst | Fixture: circuit.r end-to-end (rotxy) + measurement. |
| crates/ppvm-vihaco/tests/function_call.sst | Fixture: call into helper and return value plumbing (basic). |
| crates/ppvm-vihaco/tests/function_call_ret.sst | Fixture: ret 1-style return-value workflow and branching in caller. |
| crates/ppvm-vihaco/tests/function_call_branch_both.sst | Fixture: tri-state outcome branching (incl. loss) with helper returning an outcome. |
| crates/ppvm-vihaco/tests/branch_on_outcome.sst | Fixture: probabilistic branch steering with invariant check. |
| crates/ppvm-vihaco/tests/branch_on_outcome_x.sst | Fixture: deterministic branch steering. |
| crates/ppvm-vihaco/tests/paulisum_bell_trace.sst | Fixture: PauliSum backend Bell ⟨ZZ⟩ trace via Heisenberg order. |
| crates/ppvm-vihaco/tests/paulisum_ghz_xxx_trace.sst | Fixture: PauliSum backend GHZ ⟨XXX⟩ trace via Heisenberg order. |
| crates/ppvm-vihaco/tests/paulisum_ry_z_trace.sst | Fixture: PauliSum backend non-Clifford RY trace behavior. |
| crates/ppvm-vihaco/tests/paulisum_multi_term_trace.sst | Fixture: PauliSum backend multi-term observable seeding + trace. |
| crates/ppvm-vihaco/tests/paulisum_measure_error.sst | Fixture: PauliSum backend measure rejection behavior. |
| crates/ppvm-vihaco/tests/paulisum_trotter_truncate.sst | Fixture: PauliSum Trotter + explicit truncate between layers. |
| crates/ppvm-vihaco/tests/lossy_paulisum_loss_trace.sst | Fixture: LossyPauliSum loss-channel + trace. |
| crates/ppvm-vihaco/tests/tableau_bell_trace.sst | Fixture: Tableau backend trace against positional Pauli word. |
| crates/ppvm-vihaco/tests/tableau_ghz_xxx_trace.sst | Fixture: Tableau backend GHZ trace. |
| crates/ppvm-vihaco/tests/tableau_ry_z_trace.sst | Fixture: Tableau backend non-Clifford RY then trace. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+93
to
+97
| // Term: coefficient [*] word | bare word. | ||
| let term_with_coeff = coefficient | ||
| .then_ignore(just('*').padded().or_not()) | ||
| .then(pauli_word) | ||
| .map(|(c, w)| (w, c)); |
Comment on lines
+338
to
+350
| /// Append one REPL command's lowered VM ops and run just that block against | ||
| /// the persistent state, advancing the pc through it. NOTE: an out-of-range | ||
| /// qubit index *panics* in the tableau rather than erroring, so callers must | ||
| /// bounds-check qubit operands against `n_qubits` first. | ||
| pub fn execute_single_instruction(&mut self, instrs: &[PPVMInstruction]) -> eyre::Result<()> { | ||
| let start = self.loader.module.code.len() as u32; | ||
| self.loader.module.code.extend_from_slice(instrs); | ||
| *self.loader.pc_mut() = start; | ||
| for _ in 0..instrs.len() { | ||
| self.step_once()?; | ||
| } | ||
| Ok(()) | ||
| } |
Collaborator
Author
Comment on lines
+185
to
+199
| // Don't pre-allocate from an untrusted count; grow as entries are read. | ||
| let string_count = read_u32(r)?; | ||
| let mut strings = Vec::new(); | ||
| for _ in 0..string_count { | ||
| let len = read_u32(r)? as usize; | ||
| let mut bytes = vec![0u8; len]; | ||
| r.read_exact(&mut bytes)?; | ||
| strings.push(String::from_utf8(bytes)?); | ||
| } | ||
|
|
||
| let code_count = read_u32(r)?; | ||
| let mut code = Vec::new(); | ||
| for _ in 0..code_count { | ||
| code.push(PPVMInstruction::from_bytes(r)?); | ||
| } |
Comment on lines
+6
to
10
| [features] | ||
| # Enables shot-level parallelism in `run_shots_parallel`/`run_shots`. | ||
| rayon = ["dep:rayon"] | ||
|
|
||
| [dependencies] |
Comment on lines
+3
to
+5
| // TODO: aspirational — depends on `ret <n>` restoring the caller's PC and | ||
| // leaving the top `n` values on the caller's stack. The runtime doesn't do | ||
| // this today (Frame has no return_pc), so this fixture currently fails. |
…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>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
The documented observable grammar allows `coefficient WS* pauli_word`, but the parser only tolerated whitespace around an explicit `*`, so "0.5 ZZ" failed to parse. Consume optional whitespace before the Pauli word to match the grammar. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previously ppvm-tableau's rayon was force-enabled on every non-wasm build via a target-specific dependency, so downstream could not disable parallelism and the `rayon` feature only gated shot-level parallelism. Fold both into one off-by-default `rayon` feature (dep:rayon + ppvm-tableau/rayon) and drop the forced target override. Consumers opt in for parallelism (ppvm-cli already does); off by default keeps the default build wasm-safe with no CI changes. 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 #173 (
david/42.2-circuit-component) and sits at the top of the vihaco line:main✅ mergedmain✅ mergedmain(open; carries feat(gates): add RotXY in-plane single-qubit rotation (R gate) #170 + feat(tableau): add Pauli expectation/trace and reset_all #172 via amainmerge).pyistubs (feat(python-native): add type stubs for the native _core module #171) →main(open; independent)Summary
Completes the
ppvm-vihacocrate on top of the circuit component (#173) — the composite machine that drives it plus the surrounding VM.composite.rsPPVMcomposite: vihaco machine (CPU + program loader) driving the circuit component, measurement/trace observers, backend selection from the device header, reset, single-instruction steppingsyntax.rs.sstheader parse / lower (PPVMHeader,PPVMResolver)bytecode.rs.ssbbytecode emit / load (magic-tagged)observable.rsshots.rsrayon)lib.rsload/run/dump/compile/parsehelpers +PPVMModuleCargo.tomlvihaco-cpu,log, optionalrayonfeaturetests/*.sst+sst_fixtures.rsdevice_inforelocation resolves herecomposite.rsno longer definesPPVM_MAGIC/BackendKind/PPVMDeviceInfo— it imports them fromdevice_info(introduced in #173) andpub use-re-exports them, so the existingcrate::composite::{…}paths inlib.rs/bytecode.rs/syntax.rskeep resolving with no other edits.The machete-ignore added in #173 is dropped here — the full crate uses
chumsky/vihaco-parser-coredirectly.Testing
cargo test -p ppvm-vihaco— 111 unit + 28 integration tests, green with and without--features rayon. Full workspace builds; clippy + machete clean.