Skip to content

feat(ppvm-vihaco): add the composite machine and VM#174

Open
david-pl wants to merge 8 commits into
david/42.2-circuit-componentfrom
david/42.3-composite
Open

feat(ppvm-vihaco): add the composite machine and VM#174
david-pl wants to merge 8 commits into
david/42.2-circuit-componentfrom
david/42.3-composite

Conversation

@david-pl

@david-pl david-pl commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

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:

  1. RotXY / R gate (feat(gates): add RotXY in-plane single-qubit rotation (R gate) #170) → main ✅ merged
  2. tableau expectation/reset (feat(tableau): add Pauli expectation/trace and reset_all #172) → main ✅ merged
  3. vihaco-circuit-isa (feat(vihaco-circuit-isa): add standalone circuit ISA crate #169) → main (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 a main merge)
  4. native .pyi stubs (feat(python-native): add type stubs for the native _core module #171) → main (open; independent)
  5. circuit component (feat(ppvm-vihaco): add circuit component crate #173) → feat(vihaco-circuit-isa): add standalone circuit ISA crate #169
  6. composite machine + VM (this PR)feat(ppvm-vihaco): add circuit component crate #173
  7. CLI + TUI (Add a composable ratatui TUI to ppvm-cli #166) → this PR

Summary

Completes the ppvm-vihaco crate on top of the circuit component (#173) — the composite machine that drives it plus the surrounding VM.

File What
composite.rs the PPVM composite: vihaco machine (CPU + program loader) driving the circuit component, measurement/trace observers, backend selection from the device header, reset, single-instruction stepping
syntax.rs .sst header parse / lower (PPVMHeader, PPVMResolver)
bytecode.rs .ssb bytecode emit / load (magic-tagged)
observable.rs observable-header parsing for the PauliSum backends
shots.rs multi-shot execution (serial + optional rayon)
lib.rs full module wiring + load/run/dump/compile/parse helpers + PPVMModule
Cargo.toml adds vihaco-cpu, log, optional rayon feature
tests/*.sst + sst_fixtures.rs 28 integration fixtures

device_info relocation resolves here

composite.rs no longer defines PPVM_MAGIC / BackendKind / PPVMDeviceInfo — it imports them from device_info (introduced in #173) and pub use-re-exports them, so the existing crate::composite::{…} paths in lib.rs / bytecode.rs / syntax.rs keep resolving with no other edits.

The machete-ignore added in #173 is dropped here — the full crate uses chumsky / vihaco-parser-core directly.

Testing

cargo test -p ppvm-vihaco111 unit + 28 integration tests, green with and without --features rayon. Full workspace builds; clippy + machete clean.

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>
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://QuEraComputing.github.io/ppvm/pr-preview/pr-174/

Built to branch gh-pages at 2026-07-08 13:37 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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::PPVM machine wiring (vihaco-cpu + circuit component + observers) with stepping, file/program loading, and REPL-style single-instruction execution.
  • Adds .sst parsing/lowering (syntax.rs) and .ssb bytecode 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 .sst fixtures 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 thread crates/ppvm-vihaco/src/observable.rs Outdated
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 thread crates/ppvm-vihaco/src/shots.rs
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(())
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#166 already has a fix for this and it will be merged afterwards, so this is not a concern here so long as we merge #166 soon after this PR.

Comment thread crates/ppvm-vihaco/src/composite.rs Outdated
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.
david-pl and others added 7 commits July 8, 2026 14:38
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants