Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions crates/ppvm-stim/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use std::fmt::Debug;

use ppvm_runtime::prelude::*;
use ppvm_tableau::prelude::*;
use stim_parser::ast::{GateName, MeasureName, NoiseName, RawInstruction};
use stim_parser::extended::{Axis, ExtendedInstruction, ExtendedProgram};
use stim_parser::ast::{GateName, MeasureName, NoiseName};
use stim_parser::extended::{Axis, ExtendedInstruction, ExtendedProgram, RawPassthrough};

use crate::prepare::{ExecError, prepare};

Expand Down Expand Up @@ -125,7 +125,7 @@ pub fn execute_prepared<T, I, C>(
{
for instr in instructions {
match instr {
ExtendedInstruction::Raw(RawInstruction::Gate { name, targets, .. }) => match name {
ExtendedInstruction::Raw(RawPassthrough::Gate { name, targets, .. }) => match name {
GateName::Reset | GateName::ResetZ => targets.iter().for_each(|&q| tab.reset(q)),
GateName::X => targets.iter().for_each(|&q| tab.x(q)),
GateName::Y => targets.iter().for_each(|&q| tab.y(q)),
Expand Down Expand Up @@ -193,7 +193,7 @@ pub fn execute_prepared<T, I, C>(
} => targets
.iter()
.for_each(|&q| tab.u3(q, (*theta).into(), (*phi).into(), (*lambda).into())),
ExtendedInstruction::Raw(RawInstruction::Noise {
ExtendedInstruction::Raw(RawPassthrough::Noise {
name,
targets,
args,
Expand Down Expand Up @@ -261,7 +261,7 @@ pub fn execute_prepared<T, I, C>(
tab.correlated_loss_channel(a, b, ps.clone());
}
}
ExtendedInstruction::Raw(RawInstruction::Measure {
ExtendedInstruction::Raw(RawPassthrough::Measure {
name,
args,
targets,
Expand Down Expand Up @@ -307,18 +307,12 @@ pub fn execute_prepared<T, I, C>(
results.push(Some(tab.flip_with_prob(bit, noise)));
}
}
ExtendedInstruction::Raw(RawInstruction::Annotation { .. }) => { /* no-op */ }
ExtendedInstruction::Raw(RawPassthrough::Annotation { .. }) => { /* no-op */ }
ExtendedInstruction::Repeat { count, body, .. } => {
for _ in 0..*count {
execute_prepared(body, tab, results);
}
}
ExtendedInstruction::Raw(RawInstruction::MPad { .. }) => {
unreachable!("MPad is consumed into the extended dialect; never reaches Raw");
}
ExtendedInstruction::Raw(RawInstruction::Repeat { .. }) => {
unreachable!("Repeat is consumed into the extended dialect; never reaches Raw");
}
}
}
}
33 changes: 6 additions & 27 deletions crates/ppvm-stim/src/prepare.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
use stim_parser::ast::{GateName, MeasureName, NoiseName, RawInstruction};
use stim_parser::extended::{ExtendedInstruction, ExtendedProgram};
use stim_parser::ast::{GateName, MeasureName, NoiseName};
use stim_parser::extended::{ExtendedInstruction, ExtendedProgram, RawPassthrough};

#[derive(Debug, thiserror::Error, PartialEq)]
pub enum ExecError {
#[error("unsupported instruction '{name}' at line {line}")]
Unsupported { name: String, line: usize },
/// Raised for `ExtendedInstruction::Raw(_)` values that the extended
/// interpreter would have lowered to typed variants (`MPad`, `Repeat`).
/// `parse_extended` never produces these; only a caller hand-constructing
/// an `ExtendedProgram` can. Reported as a recoverable error rather than
/// a panic.
#[error(
"malformed ExtendedProgram: Raw({kind}) at line {line} should have been lowered to ExtendedInstruction::{kind} by the interpreter"
)]
Malformed { kind: &'static str, line: usize },
#[error("invalid probability {value} for '{name}' at line {line}; expected value in [0, 1]")]
InvalidProbability {
name: String,
Expand All @@ -29,13 +20,13 @@ pub fn prepare(program: &ExtendedProgram) -> Result<(), ExecError> {
fn validate_slice(instructions: &[ExtendedInstruction]) -> Result<(), ExecError> {
for instr in instructions {
match instr {
ExtendedInstruction::Raw(RawInstruction::Gate { name, line, .. }) => {
ExtendedInstruction::Raw(RawPassthrough::Gate { name, line, .. }) => {
check_gate_supported(*name, *line)?;
}
ExtendedInstruction::Raw(RawInstruction::Noise { name, line, .. }) => {
ExtendedInstruction::Raw(RawPassthrough::Noise { name, line, .. }) => {
check_noise_supported(*name, *line)?;
}
ExtendedInstruction::Raw(RawInstruction::Measure {
ExtendedInstruction::Raw(RawPassthrough::Measure {
name, args, line, ..
}) => {
check_measure_supported(*name, *line)?;
Expand All @@ -44,7 +35,7 @@ fn validate_slice(instructions: &[ExtendedInstruction]) -> Result<(), ExecError>
}
}
ExtendedInstruction::Repeat { body, .. } => validate_slice(body)?,
ExtendedInstruction::Raw(RawInstruction::Annotation { .. })
ExtendedInstruction::Raw(RawPassthrough::Annotation { .. })
| ExtendedInstruction::T { .. }
| ExtendedInstruction::TDag { .. }
| ExtendedInstruction::Rotation { .. }
Expand All @@ -56,18 +47,6 @@ fn validate_slice(instructions: &[ExtendedInstruction]) -> Result<(), ExecError>
check_probability(*p, "MPAD", *line)?;
}
}
ExtendedInstruction::Raw(RawInstruction::MPad { line, .. }) => {
return Err(ExecError::Malformed {
kind: "MPad",
line: *line,
});
}
ExtendedInstruction::Raw(RawInstruction::Repeat { line, .. }) => {
return Err(ExecError::Malformed {
kind: "Repeat",
line: *line,
});
}
}
}
Ok(())
Expand Down
61 changes: 59 additions & 2 deletions crates/stim-parser/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use std::fmt;

use crate::ast::{Program, RawInstruction, Tag, TagParam};
use crate::extended::ast::{Axis, ExtendedInstruction, ExtendedProgram};
use crate::extended::ast::{Axis, ExtendedInstruction, ExtendedProgram, RawPassthrough};

const INDENT: &str = " ";

Expand Down Expand Up @@ -122,9 +122,66 @@ fn fmt_raw(i: &RawInstruction, f: &mut fmt::Formatter<'_>, depth: usize) -> fmt:
writeln!(f)
}

fn fmt_raw_passthrough(
i: &RawPassthrough,
f: &mut fmt::Formatter<'_>,
depth: usize,
) -> fmt::Result {
write_indent(f, depth)?;
match i {
RawPassthrough::Gate {
name,
tags,
args,
targets,
..
} => {
f.write_str(name.canonical_name())?;
write_tags(f, tags)?;
write_args(f, args)?;
write_usize_targets(f, targets)?;
}
RawPassthrough::Noise {
name,
tags,
args,
targets,
..
} => {
f.write_str(name.canonical_name())?;
write_tags(f, tags)?;
write_args(f, args)?;
write_usize_targets(f, targets)?;
}
RawPassthrough::Measure {
name,
tags,
args,
targets,
..
} => {
f.write_str(name.canonical_name())?;
write_tags(f, tags)?;
write_args(f, args)?;
write_usize_targets(f, targets)?;
}
RawPassthrough::Annotation {
kind,
args,
targets,
..
} => {
f.write_str(kind.canonical_name())?;
write_args(f, args)?;
write_usize_targets(f, targets)?;
}
}
writeln!(f)
}

fn fmt_ext(i: &ExtendedInstruction, f: &mut fmt::Formatter<'_>, depth: usize) -> fmt::Result {
match i {
ExtendedInstruction::Raw(r) => return fmt_raw(r, f, depth),
ExtendedInstruction::Raw(r) => return fmt_raw_passthrough(r, f, depth),
ExtendedInstruction::Repeat { count, body, .. } => {
write_indent(f, depth)?;
writeln!(f, "REPEAT {count} {{")?;
Expand Down
115 changes: 110 additions & 5 deletions crates/stim-parser/src/extended/ast.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,124 @@
//! Typed AST for Stim with PPVM tag-based extensions promoted to
//! first-class instruction variants.

use crate::ast::{RawInstruction, Tag};
use crate::ast::{AnnotationKind, GateName, MeasureName, NoiseName, RawInstruction, Tag};

#[derive(Debug, Clone, PartialEq)]
pub struct ExtendedProgram {
pub instructions: Vec<ExtendedInstruction>,
}

/// Subset of [`RawInstruction`] that passes through the extended-dialect
/// interpreter unchanged. Excludes `MPad` and `Repeat`, which are always
/// lowered to the typed [`ExtendedInstruction::MPad`] /
/// [`ExtendedInstruction::Repeat`] variants — so by construction
/// `ExtendedInstruction::Raw(_)` can never wrap one of them.
#[derive(Debug, Clone, PartialEq)]
pub enum RawPassthrough {
Gate {
name: GateName,
tags: Vec<Tag>,
args: Vec<f64>,
targets: Vec<usize>,
line: usize,
},
Noise {
name: NoiseName,
tags: Vec<Tag>,
args: Vec<f64>,
targets: Vec<usize>,
line: usize,
},
Measure {
name: MeasureName,
tags: Vec<Tag>,
args: Vec<f64>,
targets: Vec<usize>,
line: usize,
},
Annotation {
kind: AnnotationKind,
args: Vec<f64>,
targets: Vec<usize>,
line: usize,
},
}

impl RawPassthrough {
/// Lift back into [`RawInstruction`]. Used by the printer to share
/// formatting code with the vanilla AST.
pub fn into_raw(self) -> RawInstruction {
match self {
RawPassthrough::Gate {
name,
tags,
args,
targets,
line,
} => RawInstruction::Gate {
name,
tags,
args,
targets,
line,
},
RawPassthrough::Noise {
name,
tags,
args,
targets,
line,
} => RawInstruction::Noise {
name,
tags,
args,
targets,
line,
},
RawPassthrough::Measure {
name,
tags,
args,
targets,
line,
} => RawInstruction::Measure {
name,
tags,
args,
targets,
line,
},
RawPassthrough::Annotation {
kind,
args,
targets,
line,
} => RawInstruction::Annotation {
kind,
args,
targets,
line,
},
}
}

pub fn line(&self) -> usize {
match self {
RawPassthrough::Gate { line, .. }
| RawPassthrough::Noise { line, .. }
| RawPassthrough::Measure { line, .. }
| RawPassthrough::Annotation { line, .. } => *line,
}
}
}

#[derive(Debug, Clone, PartialEq)]
pub enum ExtendedInstruction {
/// Pass-through from vanilla Stim — covers `Gate`, `Noise`, `Measure`,
/// `Annotation`. `MPad` and `Repeat` are NOT in `Raw` because their
/// bits/body shapes diverge between dialects.
Raw(RawInstruction),
/// `Annotation`. `MPad` and `Repeat` get their own typed variants on
/// [`ExtendedInstruction`] because their bits/body shapes diverge
/// between dialects.
Raw(RawPassthrough),

// --- Extended-dialect sugar variants ---
T {
Expand Down Expand Up @@ -81,7 +186,7 @@ fn count_in_slice(instructions: &[ExtendedInstruction], factor: u64) -> usize {
let factor_usize = usize::try_from(factor).unwrap_or(usize::MAX);
for instr in instructions {
match instr {
ExtendedInstruction::Raw(RawInstruction::Measure { targets, .. }) => {
ExtendedInstruction::Raw(RawPassthrough::Measure { targets, .. }) => {
total = total.saturating_add(targets.len().saturating_mul(factor_usize));
}
ExtendedInstruction::MPad { bits, .. } => {
Expand Down
Loading
Loading