Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
04e100f
Add Rust PhirClassicalInterpreter as drop-in replacement for Python v…
ciaranra Apr 7, 2026
8a728bf
Add FFCall bridge, Angle64 name resolver, typed results, cvar_export …
ciaranra Apr 7, 2026
915d7ee
Fix signed integer binary string formatting to match Python behavior
ciaranra Apr 7, 2026
c77c4dc
Fix multi-assign eval order, optional size, multi-qubit arg format, s…
ciaranra Apr 7, 2026
3e590fa
Add unsigned size masking and optional size inference from data type
ciaranra Apr 7, 2026
8726b48
Add cinterp='rust' option to HybridEngine, fix dtype name mapping
ciaranra Apr 7, 2026
95b759a
Add passthrough iterator for inner interpreter, cache Python class lo…
ciaranra Apr 7, 2026
29741aa
Add run_phir_sim for full-Rust execution path (3.6x speedup), auto-de…
ciaranra Apr 7, 2026
5054614
Remove Result cop requirement, fix bit string widths, filter internal…
ciaranra Apr 7, 2026
bccc742
Add WASM and depolarizing noise support to run_phir_sim full Rust path
ciaranra Apr 7, 2026
2179ce0
Fix cross-engine test to compare values not Data types
ciaranra Apr 7, 2026
188472d
Support Rust and Python noise models in run_phir_sim via build_noise_…
ciaranra Apr 7, 2026
760f9a7
Add ArgItem::UInteger for u64 max values, fix unsigned ResultValue
ciaranra Apr 7, 2026
f61938f
Replace TypedValue storage with BitUInt-based BitValue in Environment
ciaranra Apr 7, 2026
4751c53
Fix BitValue sign interpretation to use type width, disable auto fast…
ciaranra Apr 7, 2026
c1579e9
Make Rust classical interpreter the default for HybridEngine
ciaranra Apr 7, 2026
6314e52
Add pickle support for multiprocessing compatibility
ciaranra Apr 7, 2026
0e25a5b
Add foreign_obj getter for protocol parity
ciaranra Apr 7, 2026
0eb8dd1
Fix signed type storage to use type width (matching Python), improve …
ciaranra Apr 7, 2026
90c45fa
Fix expression evaluation to constrain results to operand type width
ciaranra Apr 7, 2026
66dbf7e
Untrack known-issues docs
ciaranra Apr 7, 2026
70ef0fc
Track suspected Python PhirClassicalInterpreter bugs found during Rus…
ciaranra Apr 7, 2026
bcdbf76
Remove stale Rust known-issues gitignore entry (issues resolved)
ciaranra Apr 7, 2026
7e2e7eb
Refine Python suspected bugs: remove fixed/non-bugs, add confidence l…
ciaranra Apr 8, 2026
b17a41d
Note relationship between suspected Python bugs and PECOS#213
ciaranra Apr 8, 2026
f00f906
Fix PECOS dtype overflow (related to #213): truncate instead of rejec…
ciaranra Apr 8, 2026
54e8f1d
Mark Python dtype overflow bugs as fixed in suspected bugs doc
ciaranra Apr 8, 2026
8239ffd
Fix ScalarI64 arithmetic overflow panic: use wrapping_add/sub/mul
ciaranra Apr 8, 2026
bf6eaef
Add Rust/Python parity test suite with fuzz testing (60 tests)
ciaranra Apr 8, 2026
f3a6b91
Fix u64 dtype constructor to accept negative values (wrap to unsigned)
ciaranra Apr 8, 2026
38fb56f
Refactor ExpressionEvaluator to use BitUInt at 64-bit evaluation width
ciaranra Apr 8, 2026
a37134e
Use BitUInt arithmetic directly in expression evaluator for arbitrary…
ciaranra Apr 8, 2026
e5bc8df
Store all types at type width, mask to size on assignment; fix Qubits…
ciaranra Apr 8, 2026
5be1feb
Fix Python eval_expr to evaluate at Python int width, mask all types …
ciaranra Apr 8, 2026
d0820ea
Add gitignore entry for unstaged QASM-to-PHIR test plan
ciaranra Apr 8, 2026
cd2d9b4
Add QASM-to-PHIR-JSON converter, validation tests, and classical edge…
ciaranra Apr 8, 2026
e8aab90
Merge remote-tracking branch 'origin/dev' into worktree-rust-phir-cla…
ciaranra Apr 8, 2026
c8c48ed
Default to Python classical interpreter, remove disabled full-Rust path
ciaranra Apr 9, 2026
bc98e4f
Fix signed arithmetic, remove dead code, tighten tests
ciaranra Apr 9, 2026
0e342d0
Guard against negative shift amounts, add i64::MIN / -1 regression test
ciaranra Apr 9, 2026
0d88bfc
Merge remote-tracking branch 'origin/dev' into worktree-rust-phir-cla…
ciaranra Apr 9, 2026
3c5f080
Remove TypedValue, qasm-to-phir-test-plan.md, and gitignore entry
ciaranra Apr 9, 2026
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
22 changes: 17 additions & 5 deletions crates/pecos-phir-json/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,14 +604,26 @@ mod tests {
"shot counts should match"
);

// Compare shared register values between engines
// Compare shared register values between engines using binary string representation
// (different engines may store values as BitVec vs U32, but the logical values should match)
let mut compared = 0;
for (i, (s1, s2)) in shots1.shots.iter().zip(shots2.shots.iter()).enumerate() {
for (name, val1) in &s1.data {
if let Some(val2) = s2.data.get(name) {
for name in s1.data.keys() {
if name.starts_with('_') {
continue; // Skip metadata keys like _width_*
}
if let (Some(str1), Some(str2)) = (
s1.register_to_binary_string(name),
s2.register_to_binary_string(name),
) {
// Compare values by stripping leading zeros (engines may use different widths)
let v1 = str1.trim_start_matches('0');
let v2 = str2.trim_start_matches('0');
let v1 = if v1.is_empty() { "0" } else { v1 };
let v2 = if v2.is_empty() { "0" } else { v2 };
assert_eq!(
val1, val2,
"shot {i}: register '{name}' differs between engines"
v1, v2,
"shot {i}: register '{name}' differs between engines (str1={str1}, str2={str2})"
);
compared += 1;
}
Expand Down
18 changes: 2 additions & 16 deletions crates/pecos-phir-json/src/v0_1.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
pub mod ast;
pub mod classical_interpreter;
pub mod engine;
pub mod foreign_objects;
pub mod name_resolver;
pub mod operations;
pub mod phir_converter;
pub mod wasm_foreign_object;
Expand Down Expand Up @@ -48,22 +50,6 @@ impl PhirImplementation for V0_1 {
)));
}

// Validate that at least one Result command exists
let has_result_command = program.ops.iter().any(|op| {
if let ast::Operation::ClassicalOp { cop, .. } = op {
cop == "Result"
} else {
false
}
});

if !has_result_command {
return Err(PecosError::Input(
"Invalid PHIR-JSON program structure: Program must contain at least one Result command to specify outputs"
.to_string(),
));
}

Ok(program)
}

Expand Down
28 changes: 26 additions & 2 deletions crates/pecos-phir-json/src/v0_1/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::f64::consts::PI;
pub struct PHIRProgram {
pub format: String,
pub version: String,
#[serde(default)]
pub metadata: BTreeMap<String, serde_json::Value>,
pub ops: Vec<Operation>,
}
Expand All @@ -20,7 +21,9 @@ pub enum Operation {
data: String,
data_type: String,
variable: String,
size: usize,
/// Size in bits. Optional -- if omitted, inferred from `data_type`.
#[serde(default)]
size: Option<usize>,
},
/// Quantum operation (gates, measurements)
QuantumOp {
Expand Down Expand Up @@ -78,6 +81,11 @@ pub enum Operation {
#[serde(default)]
metadata: Option<BTreeMap<String, serde_json::Value>>,
},
/// Data export (`cvar_export`) -- specifies which variables to export
DataExport {
data: String,
variables: Vec<String>,
},
/// Comment
Comment {
#[serde(rename = "//")]
Expand All @@ -103,8 +111,10 @@ pub enum ArgItem {
Indexed((String, usize)),
/// Simple argument (entire register)
Simple(String),
/// Integer literal
/// Integer literal (signed, covers most cases)
Integer(i64),
/// Unsigned integer literal (for values > `i64::MAX`, e.g. `u64::MAX`)
UInteger(u64),
/// Expression (for nested expressions)
Expression(Box<Expression>),
}
Expand All @@ -124,6 +134,20 @@ pub enum Expression {
// Constants for internal register naming
pub const MEASUREMENT_PREFIX: &str = "measurement_";

/// Infer variable size from data type when not explicitly provided.
///
/// For types like "i32", "u64", extracts the bit width from the name.
/// For "qubits", returns 0 (size must be explicit).
#[must_use]
pub fn infer_size(data_type: &str, explicit_size: Option<usize>) -> usize {
if let Some(s) = explicit_size {
return s;
}
// Try to extract bit width from type name (e.g., "i32" -> 32, "u64" -> 64)
let digits: String = data_type.chars().filter(char::is_ascii_digit).collect();
digits.parse().unwrap_or(0)
}

/// Custom deserializer to convert angles to radians
fn deserialize_angles_to_radians<'de, D>(deserializer: D) -> Result<Option<Vec<f64>>, D::Error>
where
Expand Down
13 changes: 10 additions & 3 deletions crates/pecos-phir-json/src/v0_1/block_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,12 @@ impl BlockExecutor {
size,
} => {
debug!("Processing variable definition: {data_type} {variable}");
self.processor
.handle_variable_definition(data, data_type, variable, *size)?;
self.processor.handle_variable_definition(
data,
data_type,
variable,
crate::v0_1::ast::infer_size(data_type, *size),
)?;
}
Operation::QuantumOp {
qop, angles, args, ..
Expand Down Expand Up @@ -219,6 +223,9 @@ impl BlockExecutor {
debug!("Skipping comment: {comment}");
// Comments are no-ops
}
Operation::DataExport { .. } => {
// Data exports are no-ops during execution
}
}

Ok(())
Expand Down Expand Up @@ -831,7 +838,7 @@ mod tests {
data: "cvar_define".to_string(),
data_type: "i32".to_string(),
variable: "x".to_string(),
size: 32,
size: Some(32),
},
Operation::ClassicalOp {
cop: "=".to_string(),
Expand Down
Loading
Loading