From 310c280b7e283483a30eb6e9214f5abe8579be28 Mon Sep 17 00:00:00 2001 From: Dima Fedoriaka Date: Fri, 28 Aug 2026 11:21:49 -0700 Subject: [PATCH 1/6] compact classical control --- source/compiler/qsc_circuit/src/builder.rs | 64 +++++++-- source/compiler/qsc_circuit/src/circuit.rs | 131 +++++++++++++----- .../compiler/qsc_circuit/src/circuit/tests.rs | 4 +- .../qsc_circuit/src/circuit_to_qsharp.rs | 4 +- .../qsc_circuit/src/rir_to_circuit.rs | 106 +++++++++++++- .../tests/logical_stack_trace.rs | 96 ++++++++++++- .../npm/qsharp/src/data-structures/circuit.ts | 20 ++- .../renderer/formatters/gateFormatter.ts | 10 +- .../ux/circuit-vis/renderer/gateRenderData.ts | 2 + .../qsharp/ux/circuit-vis/renderer/process.ts | 21 ++- source/npm/qsharp/ux/qsharp-circuit.css | 6 + 11 files changed, 391 insertions(+), 73 deletions(-) diff --git a/source/compiler/qsc_circuit/src/builder.rs b/source/compiler/qsc_circuit/src/builder.rs index c5c509e4a8a..00c7339e0ce 100644 --- a/source/compiler/qsc_circuit/src/builder.rs +++ b/source/compiler/qsc_circuit/src/builder.rs @@ -7,8 +7,8 @@ pub(crate) mod tests; use crate::{ angle_format::format_angle, circuit::{ - Circuit, ComponentColumn, Ket, Measurement, Metadata, Operation, Qubit, Register, - SourceLocation, Unitary, operation_list_to_grid, + Circuit, ComponentColumn, ControlRegister, Ket, Measurement, Metadata, Operation, Qubit, + Register, SourceLocation, Unitary, operation_list_to_grid, }, operations::QubitParam, }; @@ -86,7 +86,11 @@ impl Tracer for CircuitTracer { self.wire_map_builder.current(), name, is_adjoint, - &GateInputs { targets, controls }, + &GateInputs { + targets, + controls, + classical_controls: &[], + }, display_args, called_at, ); @@ -145,6 +149,7 @@ impl Tracer for CircuitTracer { &GateInputs { targets: &qubit_args, controls: &[], + classical_controls: &[], }, if classical_args.is_empty() { vec![] @@ -1236,6 +1241,7 @@ impl OperationOrGroup { is_adjoint: bool, targets: &[QubitWire], controls: &[QubitWire], + classical_controls: Vec, args: Vec, ) -> Self { Self::new_single(Operation::Unitary(Unitary { @@ -1251,10 +1257,14 @@ impl OperationOrGroup { .collect(), controls: controls .iter() - .map(|q| Register { - qubit: q.0, - result: None, + .map(|q| ControlRegister { + register: Register { + qubit: q.0, + result: None, + }, + inverted: false, }) + .chain(classical_controls) .collect(), is_adjoint, is_conditional: false, @@ -1298,7 +1308,7 @@ impl OperationOrGroup { Operation::Unitary(unitary) => unitary .targets .iter() - .chain(unitary.controls.iter()) + .chain(unitary.controls.iter().map(|control| &control.register)) .filter(|r| r.result.is_none()) .cloned() .collect(), @@ -1334,7 +1344,12 @@ impl OperationOrGroup { Operation::Unitary(unitary) => unitary .controls .iter() - .filter_map(|r| r.result.map(|res| ResultWire(r.qubit, res))) + .filter_map(|control| { + control + .register + .result + .map(|res| ResultWire(control.register.qubit, res)) + }) .collect(), Operation::Measurement(_) | Operation::Ket(_) => vec![], } @@ -1381,7 +1396,7 @@ impl OperationOrGroup { result: Some(result_wire.1), }; control_result_ids_map.push((register.clone(), *result_id)); - control_result_registers.push(register); + control_result_registers.push(ControlRegister::from(register)); } metadata = Some(Metadata { @@ -1400,7 +1415,10 @@ impl OperationOrGroup { gate: String::new(), args: vec![], children: vec![], - targets: control_result_registers.clone(), + targets: control_result_registers + .iter() + .map(|control| control.register.clone()) + .collect(), controls: control_result_registers, is_adjoint: false, metadata, @@ -1624,6 +1642,12 @@ impl OperationListBuilder { pub(crate) struct GateInputs<'a> { pub(crate) targets: &'a [usize], pub(crate) controls: &'a [usize], + pub(crate) classical_controls: &'a [ClassicalControlInput], +} + +pub(crate) struct ClassicalControlInput { + pub(crate) result_id: usize, + pub(crate) inverted: bool, } /// Trait representing a receiver of circuit operations that can accept @@ -1671,8 +1695,26 @@ impl OperationReceiver for OperationListBuilder { .iter() .map(|q| wire_map.qubit_wire(*q)) .collect::>(); + let classical_controls = inputs + .classical_controls + .iter() + .map(|control| { + let result = wire_map.result_wire(control.result_id); + ControlRegister { + register: Register::classical(result.0, result.1), + inverted: control.inverted, + } + }) + .collect(); self.push_op( - OperationOrGroup::new_unitary(name, is_adjoint, &targets, &controls, args), + OperationOrGroup::new_unitary( + name, + is_adjoint, + &targets, + &controls, + classical_controls, + args, + ), call_stack, wire_map, ); diff --git a/source/compiler/qsc_circuit/src/circuit.rs b/source/compiler/qsc_circuit/src/circuit.rs index 3ef9e96ff52..0b06a7f8df0 100644 --- a/source/compiler/qsc_circuit/src/circuit.rs +++ b/source/compiler/qsc_circuit/src/circuit.rs @@ -6,7 +6,10 @@ mod tests; use indenter::indented; use rustc_hash::{FxHashMap, FxHashSet}; -use serde::{Deserialize, Serialize}; +use serde::{ + Deserialize, Serialize, + ser::{SerializeStruct, Serializer}, +}; use std::{ cmp::max, fmt::{Display, Write}, @@ -274,7 +277,7 @@ pub struct Unitary { pub targets: Vec, #[serde(skip_serializing_if = "Vec::is_empty")] #[serde(default)] - pub controls: Vec, + pub controls: Vec, #[serde(rename = "isAdjoint")] #[serde(skip_serializing_if = "Not::not")] #[serde(default)] @@ -287,6 +290,43 @@ pub struct Unitary { pub metadata: Option, } +#[derive(Clone, Deserialize, Debug, Eq, PartialEq)] +pub struct ControlRegister { + #[serde(flatten)] + pub register: Register, + #[serde(default)] + pub inverted: bool, +} + +// Custom serialization emits a plain JavaScript object; deriving it with `flatten` emits a Map. +impl Serialize for ControlRegister { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let field_count = + 1 + usize::from(self.register.result.is_some()) + usize::from(self.inverted); + let mut state = serializer.serialize_struct("ControlRegister", field_count)?; + state.serialize_field("qubit", &self.register.qubit)?; + if let Some(result) = self.register.result { + state.serialize_field("result", &result)?; + } + if self.inverted { + state.serialize_field("inverted", &true)?; + } + state.end() + } +} + +impl From for ControlRegister { + fn from(register: Register) -> Self { + Self { + register, + inverted: false, + } + } +} + /// Representation of a gate that will set the target to a specific state. #[derive(Clone, Serialize, Deserialize, Default, Debug)] pub struct Ket { @@ -842,11 +882,11 @@ impl CircuitDisplay<'_> { ) -> usize { let mut col_width = 0; for op in &col.components { - let target_rows = get_row_indexes(op, register_to_row, true); - let control_rows = get_row_indexes(op, register_to_row, false); + let target_rows = get_target_rows(op, register_to_row); + let control_rows = get_control_rows(op, register_to_row); let mut all_rows = target_rows.clone(); - all_rows.extend(control_rows.iter()); + all_rows.extend(control_rows.iter().map(|(row, _)| row)); all_rows.sort_unstable(); // We'll need to know the entire range of rows for this operation so we can @@ -970,7 +1010,7 @@ fn add_operation_to_rows( operation: &Operation, rows: &mut [Row], targets: &[usize], - controls: &[usize], + controls: &[(usize, bool)], column: usize, begin: usize, end: usize, @@ -986,12 +1026,12 @@ fn add_operation_to_rows( } if operation.is_controlled() || operation.is_measurement() { - for i in controls { + for (i, inverted) in controls { let row = &mut rows[*i]; if matches!(row.wire, Wire::Qubit { .. }) && operation.is_measurement() { row.add_measurement(column, operation.source_location()); } else { - row.add_object(column, "●"); + row.add_object(column, if *inverted { "○" } else { "●" }); } } @@ -1081,34 +1121,15 @@ fn finalize_columns(rows: &[Row]) -> Vec { .collect() } -/// Gets the row indexes for the targets or controls of an operation. -fn get_row_indexes( +/// Gets the row indexes for the targets of an operation. +fn get_target_rows( operation: &Operation, register_to_row: &FxHashMap<(usize, Option), usize>, - is_target: bool, ) -> Vec { let registers = match operation { - Operation::Measurement(m) => { - if is_target { - &m.results - } else { - &m.qubits - } - } - Operation::Unitary(u) => { - if is_target { - &u.targets - } else { - &u.controls - } - } - Operation::Ket(k) => { - if is_target { - &k.targets - } else { - &vec![] - } - } + Operation::Measurement(measurement) => &measurement.results, + Operation::Unitary(unitary) => &unitary.targets, + Operation::Ket(ket) => &ket.targets, }; registers @@ -1120,6 +1141,33 @@ fn get_row_indexes( .collect() } +fn get_control_rows( + operation: &Operation, + register_to_row: &FxHashMap<(usize, Option), usize>, +) -> Vec<(usize, bool)> { + match operation { + Operation::Measurement(measurement) => measurement + .qubits + .iter() + .filter_map(|register| { + register_to_row + .get(&(register.qubit, register.result)) + .map(|row| (*row, false)) + }) + .collect(), + Operation::Unitary(unitary) => unitary + .controls + .iter() + .filter_map(|control| { + register_to_row + .get(&(control.register.qubit, control.register.result)) + .map(|row| (*row, control.inverted)) + }) + .collect(), + Operation::Ket(_) => vec![], + } +} + /// Converts a list of operations into a 2D grid of operations in col-row format. /// Operations will be left-justified as much as possible in the resulting grid. /// Children operations are recursively converted into a grid. @@ -1205,15 +1253,22 @@ fn operation_list_to_grid_base( Operation::Unitary(u) => &u.targets, Operation::Ket(k) => &k.targets, }; - let controls = match &op { - Operation::Measurement(m) => &m.results, - Operation::Unitary(u) => &u.controls, - Operation::Ket(_) => &vec![], - }; let mut all_rows = targets .iter() - .chain(controls.iter()) .map(|r| get_row_for_register(r, &rows)) + .chain(match &op { + Operation::Measurement(measurement) => measurement + .results + .iter() + .map(|register| get_row_for_register(register, &rows)) + .collect::>(), + Operation::Unitary(unitary) => unitary + .controls + .iter() + .map(|control| get_row_for_register(&control.register, &rows)) + .collect(), + Operation::Ket(_) => vec![], + }) .collect::>(); all_rows.sort_unstable(); let (begin, end) = all_rows.split_first().map_or((0, 0), |(first, tail)| { diff --git a/source/compiler/qsc_circuit/src/circuit/tests.rs b/source/compiler/qsc_circuit/src/circuit/tests.rs index 77e19842343..78d4fc369de 100644 --- a/source/compiler/qsc_circuit/src/circuit/tests.rs +++ b/source/compiler/qsc_circuit/src/circuit/tests.rs @@ -75,7 +75,7 @@ fn ctl_unitary(gate: &str, targets: Vec, controls: Vec) -> O gate: gate.to_string(), args: vec![], is_adjoint: false, - controls, + controls: controls.into_iter().map(Into::into).collect(), targets, children: vec![], metadata: None, @@ -106,7 +106,7 @@ fn ctl_unitary_with_children( gate: gate.to_string(), args: vec![], is_adjoint: false, - controls, + controls: controls.into_iter().map(Into::into).collect(), targets, children, metadata: None, diff --git a/source/compiler/qsc_circuit/src/circuit_to_qsharp.rs b/source/compiler/qsc_circuit/src/circuit_to_qsharp.rs index 5256581d583..6fbd53f89a5 100644 --- a/source/compiler/qsc_circuit/src/circuit_to_qsharp.rs +++ b/source/compiler/qsc_circuit/src/circuit_to_qsharp.rs @@ -389,8 +389,8 @@ fn operation_call(unitary: &Unitary, qubits: &FxHashMap) -> Strin .controls .iter() .filter_map(|c| { - if c.result.is_none() { - Some(get_qubit_name(qubits, c.qubit)) + if c.register.result.is_none() { + Some(get_qubit_name(qubits, c.register.qubit)) } else { None } diff --git a/source/compiler/qsc_circuit/src/rir_to_circuit.rs b/source/compiler/qsc_circuit/src/rir_to_circuit.rs index 494760823ef..9b3c7a1083e 100644 --- a/source/compiler/qsc_circuit/src/rir_to_circuit.rs +++ b/source/compiler/qsc_circuit/src/rir_to_circuit.rs @@ -21,9 +21,9 @@ use crate::{ Circuit, Error, TracerConfig, angle_format::format_angle, builder::{ - CallableId, GateInputs, LogicalStack, LogicalStackEntry, LogicalStackEntryLocation, LoopId, - OperationListBuilder, OperationReceiver, PackageOffset, Scope, ScopeStack, SourceLookup, - WireMap, WireMapBuilder, finish_circuit, + CallableId, ClassicalControlInput, GateInputs, LogicalStack, LogicalStackEntry, + LogicalStackEntryLocation, LoopId, OperationListBuilder, OperationReceiver, PackageOffset, + Scope, ScopeStack, SourceLookup, WireMap, WireMapBuilder, finish_circuit, }, rir_to_circuit::control_flow::{StructuredControlFlow, reconstruct_control_flow}, }; @@ -76,6 +76,7 @@ pub fn rir_to_circuit( &mut builder, &structured_control_flow, &[], + &[], &ScopeStack::top(), source_lookup, )?; @@ -91,6 +92,7 @@ pub fn rir_to_circuit( /// Recursively traverses the structured control flow, pushing operations and measurement results /// to the builder as it goes. #[allow(clippy::too_many_arguments)] +#[allow(clippy::too_many_lines)] fn build_operation_list( variable_tracker: &mut VariableTracker, program_rir: &Program, @@ -98,6 +100,7 @@ fn build_operation_list( op_list_builder: &mut impl OperationReceiver, scf: &StructuredControlFlow, control_results: &[usize], + classical_controls: &[ClassicalControlInput], current_stack: &ScopeStack, source_lookup: &impl SourceLookup, ) -> Result<(), Error> { @@ -111,6 +114,7 @@ fn build_operation_list( op_list_builder, item, control_results, + classical_controls, current_stack, source_lookup, )?; @@ -134,6 +138,7 @@ fn build_operation_list( &program_rir.dbg_info, &program_rir.callables, block, + classical_controls, current_stack, source_lookup, )?; @@ -182,6 +187,38 @@ fn build_operation_list( control_results.clone(), ); + // A simple conditional branch containing only single-qubit gates can + // be rendered by attaching its result control directly to each gate. + debug_assert!( + classical_controls.is_empty(), + "nested conditionals cannot inherit compact classical controls" + ); + let simple_control = match expr { + Expr::Bool(BoolExpr::Result(result_id)) => Some((*result_id, false)), + Expr::Bool(BoolExpr::NotResult(result_id)) => Some((*result_id, true)), + _ => None, + }; + let then_is_compact = simple_control.is_some() + && branch_has_only_single_qubit_gates(program_rir, then_br); + let else_is_compact = simple_control.is_some() + && branch_has_only_single_qubit_gates(program_rir, else_br); + let then_controls = simple_control + .filter(|_| then_is_compact) + .map(|(result_id, inverted)| ClassicalControlInput { + result_id, + inverted, + }) + .into_iter() + .collect::>(); + let else_controls = simple_control + .filter(|_| else_is_compact) + .map(|(result_id, inverted)| ClassicalControlInput { + result_id, + inverted: !inverted, + }) + .into_iter() + .collect::>(); + build_operation_list( variable_tracker, program_rir, @@ -189,7 +226,12 @@ fn build_operation_list( op_list_builder, then_br, &control_results, - &new_stack_true, + &then_controls, + if then_is_compact { + current_stack + } else { + &new_stack_true + }, source_lookup, )?; @@ -200,7 +242,12 @@ fn build_operation_list( op_list_builder, else_br, &control_results, - &new_stack_false, + &else_controls, + if else_is_compact { + current_stack + } else { + &new_stack_false + }, source_lookup, )?; } @@ -217,6 +264,7 @@ fn push_operations_in_block( dbg_info: &DbgInfo, callables: &IndexMap, block: &Block, + classical_controls: &[ClassicalControlInput], current_stack: &ScopeStack, source_lookup: &impl SourceLookup, ) -> Result<(), Error> { @@ -246,6 +294,7 @@ fn push_operations_in_block( }, callables.get(*callable_id).expect("callable should exist"), operands, + classical_controls, full_stack, )?; } @@ -254,6 +303,49 @@ fn push_operations_in_block( Ok(()) } +/// Returns whether every operation in `scf` is a single-qubit gate. +fn branch_has_only_single_qubit_gates( + program: &Program, + scf: &StructuredControlFlow, +) -> bool { + match scf { + StructuredControlFlow::Seq(items) => items + .iter() + .all(|item| branch_has_only_single_qubit_gates(program, item)), + StructuredControlFlow::BasicBlock(id) => { + let block = program.blocks.get(*id).expect("block should exist"); + block.0.iter().all(|instruction| { + let Instruction::Call(callable_id, operands, _, _) = instruction else { + return true; + }; + let callable = program + .callables + .get(*callable_id) + .expect("callable should exist"); + let Some(gate_spec) = known_gate_spec(&callable.name) else { + return false; + }; + callable.call_type == CallableType::Regular + && operands + .iter() + .all(|operand| matches!(operand, Operand::Literal(_))) + && gate_spec + .operand_types + .iter() + .filter(|operand| matches!(operand, OperandType::TargetQubit)) + .count() + == 1 + && !gate_spec + .operand_types + .iter() + .any(|operand| matches!(operand, OperandType::TargetResult)) + }) + } + StructuredControlFlow::Return => true, + StructuredControlFlow::If { .. } => false, + } +} + pub(crate) struct DbgLookup<'a> { dbg_info: &'a DbgInfo, } @@ -1077,6 +1169,7 @@ fn trace_call( builder_ctx: &mut BuilderWithRegisterMap, callable: &Callable, operands: &[Operand], + classical_controls: &[ClassicalControlInput], mut stack: LogicalStack, ) -> Result<(), Error> { // Get the signature information for known callables. For custom intrinsics, derive @@ -1119,6 +1212,7 @@ fn trace_call( operands.name, operands.is_adjoint, operands, + classical_controls, stack, )?, callable_type @ (CallableType::Readout | CallableType::OutputRecording) => { @@ -1142,6 +1236,7 @@ fn trace_gate( name: &str, is_adjoint: bool, operands: Operands, + classical_controls: &[ClassicalControlInput], stack: LogicalStack, ) -> Result<(), Error> { let Operands { @@ -1163,6 +1258,7 @@ fn trace_gate( &GateInputs { targets: &target_qubits, controls: &control_qubits, + classical_controls, }, args, stack, diff --git a/source/compiler/qsc_circuit/src/rir_to_circuit/tests/logical_stack_trace.rs b/source/compiler/qsc_circuit/src/rir_to_circuit/tests/logical_stack_trace.rs index 1e8dad2b0b1..f445b4e355a 100644 --- a/source/compiler/qsc_circuit/src/rir_to_circuit/tests/logical_stack_trace.rs +++ b/source/compiler/qsc_circuit/src/rir_to_circuit/tests/logical_stack_trace.rs @@ -66,6 +66,13 @@ impl OperationReceiver for TestOperationReceiver<'_> { .controls .iter() .map(|q| format!("q_{q}")) + .chain(inputs.classical_controls.iter().map(|control| { + format!( + "{}c_{}", + if control.inverted { "!" } else { "" }, + control.result_id + ) + })) .collect::>() .join(", "); @@ -195,6 +202,7 @@ fn check_trace(file: &str, expr: &str, expect: &Expect) { &mut builder, &structured_control_flow, &[], + &[], &ScopeStack::top(), &(&store, &fir_store), ) { @@ -782,7 +790,7 @@ fn nested_callables_and_if() { &expect![[r#" Main@A.qs:2:4 -> Foo@A.qs:7:4 -> H@qsharp-library-source:Std/Intrinsic.qs:205:8 -> gate(H, targets=(q_0), controls=()) Main@A.qs:2:4 -> Foo@A.qs:8:13 -> M@qsharp-library-source:Std/Intrinsic.qs:268:4 -> measure(M, q_0, c_0) - Main@A.qs:2:4 -> Foo@A.qs:9:4[true] -> if: c_0 = |1〉@A.qs:10:8 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_0), controls=()) + Main@A.qs:2:4 -> Foo@A.qs:10:8 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_0), controls=(c_0)) Main@A.qs:3:4 -> ResetAll@qsharp-library-source:Std/Intrinsic.qs:437:4 -> loop: qubits@qsharp-library-source:Std/Intrinsic.qs:437:20[1] -> (1)@qsharp-library-source:Std/Intrinsic.qs:438:8 -> Reset@qsharp-library-source:Std/Intrinsic.qs:426:4 -> reset(q_0) Main@A.qs:3:4 -> ResetAll@qsharp-library-source:Std/Intrinsic.qs:437:4 -> loop: qubits@qsharp-library-source:Std/Intrinsic.qs:437:20[2] -> (2)@qsharp-library-source:Std/Intrinsic.qs:438:8 -> Reset@qsharp-library-source:Std/Intrinsic.qs:426:4 -> reset(q_1) "#]], @@ -810,8 +818,8 @@ fn branch_in_for_loop() { &expect![[r#" Main@A.qs:2:19 -> MResetZ@qsharp-library-source:Std/Measurement.qs:135:4 -> measure(MResetZ, q_0, c_0) Main@A.qs:2:35 -> MResetZ@qsharp-library-source:Std/Measurement.qs:135:4 -> measure(MResetZ, q_1, c_1) - Main@A.qs:4:4 -> loop: 0..1@A.qs:4:18[1] -> (1)@A.qs:5:8[true] -> if: c_0 = |1〉@A.qs:6:12 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_0), controls=()) - Main@A.qs:4:4 -> loop: 0..1@A.qs:4:18[2] -> (2)@A.qs:5:8[true] -> if: c_1 = |1〉@A.qs:6:12 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_0), controls=()) + Main@A.qs:4:4 -> loop: 0..1@A.qs:4:18[1] -> (1)@A.qs:6:12 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_0), controls=(c_0)) + Main@A.qs:4:4 -> loop: 0..1@A.qs:4:18[2] -> (2)@A.qs:6:12 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_0), controls=(c_1)) Main@A.qs:9:4 -> ResetAll@qsharp-library-source:Std/Intrinsic.qs:437:4 -> loop: qubits@qsharp-library-source:Std/Intrinsic.qs:437:20[1] -> (1)@qsharp-library-source:Std/Intrinsic.qs:438:8 -> Reset@qsharp-library-source:Std/Intrinsic.qs:426:4 -> reset(q_0) Main@A.qs:9:4 -> ResetAll@qsharp-library-source:Std/Intrinsic.qs:437:4 -> loop: qubits@qsharp-library-source:Std/Intrinsic.qs:437:20[2] -> (2)@qsharp-library-source:Std/Intrinsic.qs:438:8 -> Reset@qsharp-library-source:Std/Intrinsic.qs:426:4 -> reset(q_1) "#]], @@ -878,8 +886,8 @@ fn nested_conditionals_in_callable() { &expect![[r#" Main@A.qs:3:4 -> NestedConditionalsInCallable@A.qs:8:13 -> MResetZ@qsharp-library-source:Std/Measurement.qs:135:4 -> measure(MResetZ, q_1, c_0) Main@A.qs:3:4 -> NestedConditionalsInCallable@A.qs:9:13 -> MResetZ@qsharp-library-source:Std/Measurement.qs:135:4 -> measure(MResetZ, q_2, c_1) - Main@A.qs:3:4 -> NestedConditionalsInCallable@A.qs:10:4 -> Foo@A.qs:14:4[false] -> if: c_0 = |0〉@A.qs:16:8[true] -> if: c_1 = |1〉@A.qs:17:12 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_0), controls=()) - Main@A.qs:3:4 -> NestedConditionalsInCallable@A.qs:10:4 -> Foo@A.qs:14:4[false] -> if: c_0 = |0〉@A.qs:16:8[false] -> if: c_1 = |0〉@A.qs:19:12 -> Z@qsharp-library-source:Std/Intrinsic.qs:1126:8 -> gate(Z, targets=(q_0), controls=()) + Main@A.qs:3:4 -> NestedConditionalsInCallable@A.qs:10:4 -> Foo@A.qs:14:4[false] -> if: c_0 = |0〉@A.qs:17:12 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_0), controls=(c_1)) + Main@A.qs:3:4 -> NestedConditionalsInCallable@A.qs:10:4 -> Foo@A.qs:14:4[false] -> if: c_0 = |0〉@A.qs:19:12 -> Z@qsharp-library-source:Std/Intrinsic.qs:1126:8 -> gate(Z, targets=(q_0), controls=(!c_1)) Main@A.qs:4:4 -> ResetAll@qsharp-library-source:Std/Intrinsic.qs:437:4 -> loop: qubits@qsharp-library-source:Std/Intrinsic.qs:437:20[1] -> (1)@qsharp-library-source:Std/Intrinsic.qs:438:8 -> Reset@qsharp-library-source:Std/Intrinsic.qs:426:4 -> reset(q_0) Main@A.qs:4:4 -> ResetAll@qsharp-library-source:Std/Intrinsic.qs:437:4 -> loop: qubits@qsharp-library-source:Std/Intrinsic.qs:437:20[2] -> (2)@qsharp-library-source:Std/Intrinsic.qs:438:8 -> Reset@qsharp-library-source:Std/Intrinsic.qs:426:4 -> reset(q_1) Main@A.qs:4:4 -> ResetAll@qsharp-library-source:Std/Intrinsic.qs:437:4 -> loop: qubits@qsharp-library-source:Std/Intrinsic.qs:437:20[3] -> (3)@qsharp-library-source:Std/Intrinsic.qs:438:8 -> Reset@qsharp-library-source:Std/Intrinsic.qs:426:4 -> reset(q_2) @@ -1004,3 +1012,81 @@ fn integer_comparison() { "#]], ); } + +#[test] +fn compact_classical_control() { + check_trace( + indoc! {" + operation Main() : Unit { + use q = Qubit[5]; + H(q[0]); + H(q[1]); + H(q[2]); + let r0 = M(q[0]); + + if (r0 == One) { + X(q[1]); + Rz(0.5, q[2]); + } else { + X(q[3]); + } + + let r1 = M(q[1]); + if (r1 == Zero) { + Z(q[0]); + CZ(q[3], q[4]); + } else { + H(q[3]); + CCNOT(q[2], q[3], q[0]); + } + } + "}, + "A.Main()", + &expect![[r#" + Main@A.qs:2:4 -> H@qsharp-library-source:Std/Intrinsic.qs:205:8 -> gate(H, targets=(q_0), controls=()) + Main@A.qs:3:4 -> H@qsharp-library-source:Std/Intrinsic.qs:205:8 -> gate(H, targets=(q_1), controls=()) + Main@A.qs:4:4 -> H@qsharp-library-source:Std/Intrinsic.qs:205:8 -> gate(H, targets=(q_2), controls=()) + Main@A.qs:5:13 -> M@qsharp-library-source:Std/Intrinsic.qs:268:4 -> measure(M, q_0, c_0) + Main@A.qs:8:8 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_1), controls=(c_0)) + Main@A.qs:9:8 -> Rz@qsharp-library-source:Std/Intrinsic.qs:694:8 -> gate(Rz, targets=(q_2), controls=(c_0)) + Main@A.qs:11:8 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_3), controls=(!c_0)) + Main@A.qs:14:13 -> M@qsharp-library-source:Std/Intrinsic.qs:268:4 -> measure(M, q_1, c_1) + Main@A.qs:16:8 -> Z@qsharp-library-source:Std/Intrinsic.qs:1126:8 -> gate(Z, targets=(q_0), controls=(!c_1)) + Main@A.qs:17:8 -> CZ@qsharp-library-source:Std/Canon.qs:228:8 -> gate(Z, targets=(q_4), controls=(q_3, !c_1)) + Main@A.qs:19:8 -> H@qsharp-library-source:Std/Intrinsic.qs:205:8 -> gate(H, targets=(q_3), controls=(c_1)) + Main@A.qs:20:8 -> CCNOT@qsharp-library-source:Std/Intrinsic.qs:75:8 -> gate(X, targets=(q_0), controls=(q_2, q_3, c_1)) + "#]], + ); +} + +// Test for cases when classically controlled operations should not be rendered in a compact way. +#[test] +fn compact_classical_control_negative_cases() { + check_trace( + indoc! {" + operation Main() : Unit { + use q = Qubit[5]; + H(q[0]); + // Multi-qubit gate. + if (M(q[0]) == One) { + X(q[1]); + Rzz(0.5, q[1], q[2]); + } + // Complex condition. + if (M(q[0]) == One and M(q[1]) == Zero) { + X(q[2]); + } + } + "}, + "A.Main()", + &expect![[r#" + Main@A.qs:2:4 -> H@qsharp-library-source:Std/Intrinsic.qs:205:8 -> gate(H, targets=(q_0), controls=()) + Main@A.qs:4:8 -> M@qsharp-library-source:Std/Intrinsic.qs:268:4 -> measure(M, q_0, c_0) + Main@A.qs:4:4[true] -> if: c_0 = |1〉@A.qs:5:8 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_1), controls=()) + Main@A.qs:4:4[true] -> if: c_0 = |1〉@A.qs:6:8 -> Rzz@qsharp-library-source:Std/Intrinsic.qs:741:8 -> gate(Rzz, targets=(q_1, q_2), controls=()) + Main@A.qs:9:8 -> M@qsharp-library-source:Std/Intrinsic.qs:268:4 -> measure(M, q_0, c_1) + Main@A.qs:9:27[true] -> if: c_1 = |1〉@A.qs:9:27 -> M@qsharp-library-source:Std/Intrinsic.qs:268:4 -> measure(M, q_1, c_2) + Main@A.qs:9:4[true] -> if: f(c_1, c_2)@A.qs:10:8 -> X@qsharp-library-source:Std/Intrinsic.qs:1038:8 -> gate(X, targets=(q_2), controls=()) + "#]], + ); +} diff --git a/source/npm/qsharp/src/data-structures/circuit.ts b/source/npm/qsharp/src/data-structures/circuit.ts index 8a3ca1b1690..8e3897e3557 100644 --- a/source/npm/qsharp/src/data-structures/circuit.ts +++ b/source/npm/qsharp/src/data-structures/circuit.ts @@ -165,6 +165,21 @@ export interface Measurement extends BaseOperation { results: Register[]; } +/** A register that controls whether a unitary operation is applied. */ +export type ControlRegister = Register & { + /** Whether the control is activated when the register value is zero. */ + inverted?: boolean; +}; + +/** Runtime check: is this a valid ControlRegister? */ +export function isControlRegister(obj: any): obj is ControlRegister { + return ( + isRegister(obj) && + ((obj as any).inverted === undefined || + typeof (obj as any).inverted === "boolean") + ); +} + /** * Represents a unitary operation and the registers it acts on. */ @@ -174,7 +189,7 @@ export interface Unitary extends BaseOperation { /** Target registers the gate acts on. */ targets: Register[]; /** Control registers the gate acts on. */ - controls?: Register[]; + controls?: ControlRegister[]; /** Whether gate is an adjoint operation. */ isAdjoint?: boolean; } @@ -209,7 +224,8 @@ export function isOperation(obj: any): obj is Operation { op.targets.every(isRegister) && // controls is optional (op.controls === undefined || - (Array.isArray(op.controls) && op.controls.every(isRegister))) && + (Array.isArray(op.controls) && + op.controls.every(isControlRegister))) && // isAdjoint is optional (op.isAdjoint === undefined || typeof op.isAdjoint === "boolean") ); diff --git a/source/npm/qsharp/ux/circuit-vis/renderer/formatters/gateFormatter.ts b/source/npm/qsharp/ux/circuit-vis/renderer/formatters/gateFormatter.ts index 5910a094efe..936305a014a 100644 --- a/source/npm/qsharp/ux/circuit-vis/renderer/formatters/gateFormatter.ts +++ b/source/npm/qsharp/ux/circuit-vis/renderer/formatters/gateFormatter.ts @@ -596,9 +596,13 @@ const _controlledGate = (renderData: GateRenderData): SVGElement => { throw new Error(`ERROR: Unrecognized gate: ${label} of type ${type}`); } // Get SVGs for control dots - const controlledDotsSvg: SVGElement[] = controlsY.map((y) => - controlDot(x, y, [y]), - ); + const controlledDotsSvg: SVGElement[] = controlsY.map((y, index) => { + const dot = controlDot(x, y, [y]); + if (renderData.controlsInverted?.[index]) { + dot.classList.add("anti-control-dot"); + } + return dot; + }); // Create control lines const maxY: number = Math.max(...controlsY, ...(targetsY as number[])); const minY: number = Math.min(...controlsY, ...(targetsY as number[])); diff --git a/source/npm/qsharp/ux/circuit-vis/renderer/gateRenderData.ts b/source/npm/qsharp/ux/circuit-vis/renderer/gateRenderData.ts index 1bcf4610b14..885a4bcf453 100644 --- a/source/npm/qsharp/ux/circuit-vis/renderer/gateRenderData.ts +++ b/source/npm/qsharp/ux/circuit-vis/renderer/gateRenderData.ts @@ -42,6 +42,8 @@ export interface GateRenderData { x: number; /** Array of y coords of control registers. */ controlsY: number[]; + /** Whether each control is inverted. */ + controlsInverted?: boolean[]; /** Array of y coords of target registers. * For `GateType.Unitary` or `GateType.ControlledUnitary`, this is an array of groups of y * coords, where each group represents a unitary box to be rendered separately. diff --git a/source/npm/qsharp/ux/circuit-vis/renderer/process.ts b/source/npm/qsharp/ux/circuit-vis/renderer/process.ts index 413174c5788..e963bd1c034 100644 --- a/source/npm/qsharp/ux/circuit-vis/renderer/process.ts +++ b/source/npm/qsharp/ux/circuit-vis/renderer/process.ts @@ -102,11 +102,16 @@ const processOperations = ( break; } - // For ops with own classical controls, include those control wires in the body-geometry + // For group ops with own classical controls, include those control wires in the body-geometry // input. `_classicalControls` draws a short L-connector from each control circle to the // body box; for that connector to land on the box (rather than in empty space below the // body), the body must extend down to include the classical control wire's y. - if (op.kind === "unitary" && op.controls) { + if ( + op.kind === "unitary" && + op.controls && + op.children && + op.children.length > 0 + ) { const ownClassicalControls = op.controls.filter( (r) => r.result != null, ); @@ -323,12 +328,13 @@ const _opToRenderData = ( // Classically-controlled operations are encoded as operations whose `controls` are classical // registers (i.e. `Register.result` is set), with IDs provided via `metadata.controlResultIds`. + const hasChildren = children != null && children.length > 0; const hasClassicalControls = op.kind === "unitary" && + hasChildren && ((controls?.some((reg) => reg.result != null) ?? false) || (op.metadata?.controlResultIds?.length ?? 0) > 0); - const hasChildren = children != null && children.length > 0; const expandedAttr = dataAttributes?.["expanded"]; const defaultExpanded = hasClassicalControls && hasChildren; const isExpanded = @@ -337,15 +343,20 @@ const _opToRenderData = ( // Set y coords renderData.controlsY = controls?.map((reg) => _getRegY(reg, registers)) || []; + if (op.kind === "unitary") { + renderData.controlsInverted = op.controls?.map( + (control) => control.inverted ?? false, + ); + } renderData.targetsY = targets.map((reg) => _getRegY(reg, registers)); - // For classically-controlled ops, include the classical-control sub-wires in `targetsY` so the + // For classically-controlled groups, include the classical-control sub-wires in `targetsY` so the // wire span this op claims for layout matches the bounding-box span drawn by `_gateBoundingBox` // (which merges `targetsY` with `controlsY` for its min/max). Without it, a parent group's // `_processChildren` `topY === minTargetY` check fails for nested classically-controlled children // and their `topPadding` doesn't propagate up, causing stacked nested conditionals to render box // tops and labels at the same y. - if (op.kind === "unitary" && op.controls) { + if (hasClassicalControls && op.kind === "unitary" && op.controls) { const ownClassicalControlYs = op.controls .filter((r) => r.result != null) .map((reg) => _getRegY(reg, registers)); diff --git a/source/npm/qsharp/ux/qsharp-circuit.css b/source/npm/qsharp/ux/qsharp-circuit.css index 1299846bf8d..3d502143f6b 100644 --- a/source/npm/qsharp/ux/qsharp-circuit.css +++ b/source/npm/qsharp/ux/qsharp-circuit.css @@ -131,6 +131,12 @@ fill: var(--main-color); } + .anti-control-dot { + fill: var(--main-background); + stroke: var(--main-color); + stroke-width: 2; + } + /* X gate */ .oplus > line, .oplus > circle { From 003c80a3a36ab3256c4a9d166b8b140769b6b715 Mon Sep 17 00:00:00 2001 From: Dima Fedoriaka Date: Fri, 28 Aug 2026 11:37:56 -0700 Subject: [PATCH 2/6] circuit-to-qsharp --- .../qsc_circuit/src/circuit_to_qsharp.rs | 47 +++++++++---- .../src/circuit_to_qsharp/tests.rs | 66 +++++++++++++++++++ .../qsc_circuit/src/rir_to_circuit.rs | 5 +- 3 files changed, 100 insertions(+), 18 deletions(-) diff --git a/source/compiler/qsc_circuit/src/circuit_to_qsharp.rs b/source/compiler/qsc_circuit/src/circuit_to_qsharp.rs index 6fbd53f89a5..4032f5f2c1d 100644 --- a/source/compiler/qsc_circuit/src/circuit_to_qsharp.rs +++ b/source/compiler/qsc_circuit/src/circuit_to_qsharp.rs @@ -143,9 +143,9 @@ fn operation_return_type(circuit: &Circuit) -> &'static str { fn supports_ctl_adj(circuit: &Circuit) -> bool { !circuit.component_grid.iter().any(|col| { - col.components - .iter() - .any(|op| !matches!(op, Operation::Unitary(_))) + col.components.iter().any(|op| { + !matches!(op, Operation::Unitary(unitary) if unitary.controls.iter().all(|control| !control.register.is_classical())) + }) }) } @@ -268,7 +268,28 @@ fn generate_unitary_call( indent: &str, ) -> String { let operation_str = operation_call(unitary, qubits); - format!("{indent}{operation_str};\n") + let classical_controls = unitary + .controls + .iter() + .filter(|control| control.register.result.is_some()) + .collect::>(); + if classical_controls.is_empty() { + format!("{indent}{operation_str};\n") + } else { + let condition = classical_controls + .iter() + .map(|control| { + let register = &control.register; + let result_id = register + .result + .expect("classical control should reference a measurement result"); + let expected_result = if control.inverted { "Zero" } else { "One" }; + format!("c{}_{result_id} == {expected_result}", register.qubit) + }) + .collect::>() + .join(" and "); + format!("{indent}if {condition} {{\n{indent} {operation_str};\n{indent}}}\n") + } } fn generate_ket_call(ket: &Ket, qubits: &FxHashMap, indent: &str) -> String { @@ -343,7 +364,12 @@ fn ket_call(ket: &Ket, qubits: &FxHashMap) -> String { fn operation_call(unitary: &Unitary, qubits: &FxHashMap) -> String { let gate = unitary.gate.as_str(); - let is_controlled = !unitary.controls.is_empty(); + let quantum_controls = unitary + .controls + .iter() + .filter(|control| control.register.result.is_none()) + .collect::>(); + let is_controlled = !quantum_controls.is_empty(); let functors = if is_controlled && unitary.is_adjoint { "Controlled Adjoint " @@ -385,16 +411,9 @@ fn operation_call(unitary: &Unitary, qubits: &FxHashMap) -> Strin args.extend(targets); if is_controlled { - let controls = unitary - .controls + let controls = quantum_controls .iter() - .filter_map(|c| { - if c.register.result.is_none() { - Some(get_qubit_name(qubits, c.register.qubit)) - } else { - None - } - }) + .map(|control| get_qubit_name(qubits, control.register.qubit)) .collect::>() .join(", "); let controls = format!("[{controls}]"); diff --git a/source/compiler/qsc_circuit/src/circuit_to_qsharp/tests.rs b/source/compiler/qsc_circuit/src/circuit_to_qsharp/tests.rs index f52168ffe13..8b87f457903 100644 --- a/source/compiler/qsc_circuit/src/circuit_to_qsharp/tests.rs +++ b/source/compiler/qsc_circuit/src/circuit_to_qsharp/tests.rs @@ -834,3 +834,69 @@ fn circuit_with_ctrl_adj_sqrt_x_gate() { "#]], ); } + +#[test] +fn circuit_with_compact_classical_controls() { + check( + r#" +{ + "componentGrid": [ + { + "components": [ + { + "kind": "measurement", + "gate": "Measure", + "qubits": [{ "qubit": 0 }], + "results": [{ "qubit": 0, "result": 0 }] + } + ] + }, + { + "components": [ + { + "kind": "unitary", + "gate": "X", + "controls": [ + { "qubit": 0, "result": 0 } + ], + "targets": [{ "qubit": 1 }] + } + ] + }, + { + "components": [ + { + "kind": "unitary", + "gate": "Z", + "controls": [ + { "qubit": 0, "result": 0, "inverted": true } + ], + "targets": [{ "qubit": 1 }] + } + ] + } + ], + "qubits": [ + { "id": 0, "numResults": 1 }, + { "id": 1 } + ] +}"#, + &expect![[r#" + /// Expects a qubit register of at least 2 qubits. + operation Test(qs : Qubit[]) : Result { + if Length(qs) < 2 { + fail "Invalid number of qubits. Operation Test expects a qubit register of at least 2 qubits."; + } + let c0_0 = M(qs[0]); + if c0_0 == One { + X(qs[1]); + } + if c0_0 == Zero { + Z(qs[1]); + } + return c0_0; + } + + "#]], + ); +} diff --git a/source/compiler/qsc_circuit/src/rir_to_circuit.rs b/source/compiler/qsc_circuit/src/rir_to_circuit.rs index 9b3c7a1083e..1325a6016a0 100644 --- a/source/compiler/qsc_circuit/src/rir_to_circuit.rs +++ b/source/compiler/qsc_circuit/src/rir_to_circuit.rs @@ -304,10 +304,7 @@ fn push_operations_in_block( } /// Returns whether every operation in `scf` is a single-qubit gate. -fn branch_has_only_single_qubit_gates( - program: &Program, - scf: &StructuredControlFlow, -) -> bool { +fn branch_has_only_single_qubit_gates(program: &Program, scf: &StructuredControlFlow) -> bool { match scf { StructuredControlFlow::Seq(items) => items .iter() From a2bac36e51cb29a57ada76f4e93bfcd91b6f7464 Mon Sep 17 00:00:00 2001 From: Dima Fedoriaka Date: Fri, 28 Aug 2026 11:50:06 -0700 Subject: [PATCH 3/6] update HTML snapshots --- .../conditionals.qs.snapshot.html | 5477 +++++++---------- .../circuits-cases/if-else.qs.snapshot.html | 602 +- 2 files changed, 2386 insertions(+), 3693 deletions(-) diff --git a/source/npm/qsharp/test/circuits-cases/conditionals.qs.snapshot.html b/source/npm/qsharp/test/circuits-cases/conditionals.qs.snapshot.html index cf99d0f54a5..419d0c20674 100644 --- a/source/npm/qsharp/test/circuits-cases/conditionals.qs.snapshot.html +++ b/source/npm/qsharp/test/circuits-cases/conditionals.qs.snapshot.html @@ -2543,15 +2543,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3754,29 +3754,29 @@ @@ -3784,29 +3784,29 @@ @@ -3814,29 +3814,29 @@ @@ -3844,209 +3844,209 @@ @@ -4057,8 +4057,8 @@ class="gate-unitary" x="80" y="46" - width="866" - height="5004" + width="656" + height="4700" fill-opacity="0" stroke-dasharray="8, 8" /> @@ -4066,10 +4066,10 @@ @@ -4081,17 +4081,17 @@ H @@ -4106,105 +4106,54 @@ - - - - - - - c - 0 - - - - - - - conditionals.qs:29:9 X(q); - - - - - - - - + - conditionals.qs:28:5 if (r1 == One) { - - if - : - c - 0 - = |1〉 - + conditionals.qs:29:9 X(q); + + + + + + + - - - - @@ -4213,7 +4162,7 @@ - - + + @@ -4244,17 +4193,17 @@ H @@ -4269,105 +4218,54 @@ - - - - - - - c - 1 - - - - - - - conditionals.qs:40:9 X(q); - - - - - - - - + - conditionals.qs:39:5 if (r1 == Zero) { - - if - : - c - 1 - = |0〉 - + conditionals.qs:40:9 X(q); + + + + + + + - - - - @@ -4376,8 +4274,8 @@ @@ -4387,17 +4285,17 @@ - - + + @@ -4409,17 +4307,17 @@ H @@ -4434,115 +4332,62 @@ - - - - - - - c - 2 - - - - - - - conditionals.qs:49:9 X(q); - - - - - - - - + - - conditionals.qs:48:5 if (r1 == Zero) {} else { - - - if - : - c - 2 - = |1〉 - + conditionals.qs:49:9 X(q); + + + + + + + - - - - conditionals.qs:6:5 ElseBlockOnly(qs[2]); @@ -4550,15 +4395,15 @@ - - + + H @@ -4598,17 +4443,17 @@ H @@ -4623,23 +4468,23 @@ @@ -4652,23 +4497,23 @@ @@ -4681,27 +4526,27 @@ data-expanded="true" > - + - + conditionals.qs:60:9 X(q1); - - - - + + + + @@ -4765,8 +4610,8 @@ conditionals.qs:59:5 if (r1 == r2) { @@ -4786,8 +4631,8 @@ - - + + @@ -4797,8 +4642,8 @@ @@ -4806,15 +4651,15 @@ - - + + H @@ -4854,17 +4699,17 @@ H @@ -4879,23 +4724,23 @@ @@ -4908,23 +4753,23 @@ @@ -4939,8 +4784,8 @@ @@ -4950,17 +4795,17 @@ - - + + @@ -4972,17 +4817,17 @@ H @@ -4997,742 +4842,120 @@ - - - - - - - c - 7 - - - - - - - conditionals.qs:81:9 X(q1); - - - - - - - - + - conditionals.qs:80:5 if r == One { - - if - : - c - 7 - = |1〉 - + conditionals.qs:81:9 X(q1); + + + + + + + - - - - - - - - - - - c - 7 - - - - - - - conditionals.qs:83:9 Y(q1); - - - - - Y - - - - - - - - conditionals.qs:80:5 if r == One { - - if - : - c - 7 - = |0〉 - - - - - - - - - - conditionals.qs:85:14 let r1 = M(q1); - - - - - - - - - - conditionals.qs:9:5 IfElse(qs[7], qs[8]); - - IfElse - - - - - - - - - - - + - conditionals.qs:90:5 H(q0); - - - - - H - - - - - - - - conditionals.qs:91:5 H(q1); + conditionals.qs:83:9 Y(q1); + + - - H - - - - - - - - conditionals.qs:92:14 let r0 = M(q0); - - - - - - - - - - conditionals.qs:93:14 let r1 = M(q1); - - - - - - - - - - - - - - c - 9 - - - - - - - conditionals.qs:95:9 X(q2); - - - - - - - - - - conditionals.qs:94:5 if r0 == One { - - if - : - c - 9 - = |1〉 - - - - - - - - - - - - - - c - 9 - - - - - - - conditionals.qs:97:9 Z(q2); - - - - - Z - - - - - - - - conditionals.qs:94:5 if r0 == One { - - if - : - c - 9 - = |0〉 - - - - - - - - - - - - - - c - 10 - - - - - - - conditionals.qs:100:9 X(q2); - - - - - - - - - - conditionals.qs:99:5 if r1 == One { - - if - : - c - 10 - = |1〉 - - - - - - - - - - - - - - c - 10 - - - - - - - conditionals.qs:102:9 Y(q2); - - - - - Y - - - - - - - - conditionals.qs:99:5 if r1 == One { - - if - : - c - 10 - = |0〉 - + /> + + Y + + + - - - - - + - conditionals.qs:104:14 let r2 = M(q2); + conditionals.qs:85:14 let r1 = M(q1); @@ -5741,53 +4964,51 @@ - - conditionals.qs:10:5 SequentialIfs(qs[9], qs[10], qs[11]); - + conditionals.qs:9:5 IfElse(qs[7], qs[8]); - SequentialIfs + IfElse - - + + - + - + - conditionals.qs:109:5 H(q0); + conditionals.qs:90:5 H(q0); H @@ -5796,24 +5017,24 @@ - + - conditionals.qs:110:5 H(q1); + conditionals.qs:91:5 H(q1); H @@ -5822,414 +5043,397 @@ - + - conditionals.qs:111:14 let r0 = M(q0); + conditionals.qs:92:14 let r0 = M(q0); - + - conditionals.qs:112:14 let r1 = M(q1); + conditionals.qs:93:14 let r1 = M(q1); - - - - - - - c - 12 - - - - - - - - - + + + conditionals.qs:95:9 X(q2); + + + + + + + + + + + + conditionals.qs:97:9 Z(q2); + + + + + - c - - 12 - + Z - - - - + + + + + + conditionals.qs:100:9 X(q2); + + + + + + + + + + + + conditionals.qs:102:9 Y(q2); + + + + + - c - - 13 - + Y + + + + + + conditionals.qs:104:14 let r2 = M(q2); + - - - - conditionals.qs:115:13 X(q2); - - - - - - - - - - conditionals.qs:114:9 if r1 == One { - - if - : - c - - 13 - - = |1〉 - - - - - - - - - - - + + + + + + + conditionals.qs:10:5 SequentialIfs(qs[9], qs[10], qs[11]); + + + SequentialIfs + + + + + + + + + + + + + conditionals.qs:109:5 H(q0); + + + - c - - 12 - + H - - - - + + + + + + conditionals.qs:110:5 H(q1); + + + - c - - 13 - + H + + + + + + conditionals.qs:111:14 let r0 = M(q0); + + + - - - - conditionals.qs:117:13 Y(q2); - - - - - Y - - - - - - - - conditionals.qs:114:9 if r1 == One { - - if - : - c - - 13 - - = |0〉 - - - - - - - + + + - conditionals.qs:113:5 if r0 == One { - - if - : - c - 12 - = |1〉 - + conditionals.qs:112:14 let r1 = M(q1); + + + + + - - - - - + - + + + conditionals.qs:115:13 X(q2); + + + + + + + + + + - conditionals.qs:120:9 Z(q2); + conditionals.qs:117:13 Y(q2); + + - Z + Y @@ -6279,8 +5529,8 @@ conditionals.qs:113:5 if r0 == One { @@ -6288,37 +5538,79 @@ : c 12 - = |0〉 + = |1〉 - - + + + + + conditionals.qs:120:9 Z(q2); + + + + + + + Z + + + + + conditionals.qs:122:14 let r2 = M(q2); @@ -6332,8 +5624,8 @@ @@ -6341,15 +5633,15 @@ - - + + H @@ -6388,23 +5680,23 @@ @@ -6417,27 +5709,27 @@ data-expanded="true" > - + Rx f @@ -6504,8 +5796,8 @@ conditionals.qs:133:5 Rx(theta, q1); @@ -6516,8 +5808,8 @@ - - + + @@ -6526,23 +5818,23 @@ @@ -6557,8 +5849,8 @@ @@ -6568,15 +5860,15 @@ - - + + H @@ -6615,23 +5907,23 @@ @@ -6644,27 +5936,27 @@ data-expanded="true" > - + foo f @@ -6728,8 +6020,8 @@ conditionals.qs:151:5 foo(q, x); @@ -6740,8 +6032,8 @@ - - + + @@ -6751,8 +6043,8 @@ @@ -6762,15 +6054,15 @@ - - + + H @@ -6809,23 +6101,23 @@ @@ -6838,27 +6130,27 @@ data-expanded="true" > - + Rx f @@ -6925,8 +6217,8 @@ conditionals.qs:166:5 Rx(theta, q1); @@ -6937,8 +6229,8 @@ - - + + @@ -6947,23 +6239,23 @@ @@ -6977,8 +6269,8 @@ @@ -6986,15 +6278,15 @@ - - + + H @@ -7033,23 +6325,23 @@ @@ -7062,27 +6354,27 @@ data-expanded="true" > - + conditionals.qs:184:9 X(q1); - - - + + + @@ -7120,8 +6412,8 @@ conditionals.qs:183:5 if cond { @@ -7135,8 +6427,8 @@ - - + + @@ -7145,23 +6437,23 @@ @@ -7175,8 +6467,8 @@ @@ -7184,17 +6476,17 @@ - - + + @@ -7206,8 +6498,8 @@ > conditionals.qs:195:5 X(q); - - - + + + @@ -7250,17 +6542,17 @@ Y @@ -7274,8 +6566,8 @@ conditionals.qs:191:5 Bar(q); @@ -7283,8 +6575,8 @@ - - + + @@ -7292,8 +6584,8 @@ conditionals.qs:200:5 Foo(q); @@ -7301,8 +6593,8 @@ - - + + @@ -7312,17 +6604,17 @@ H @@ -7337,23 +6629,23 @@ @@ -7361,46 +6653,16 @@ - - - - - - c - 22 - - @@ -7412,141 +6674,120 @@ > - - - - - - conditionals.qs:195:5 X(q); - - - - - - - + + + conditionals.qs:195:5 X(q); + + - - conditionals.qs:196:5 Y(q); - - - - - Y - - - - + + + - + + + - conditionals.qs:191:5 Bar(q); - - Bar - + conditionals.qs:196:5 Y(q); + + + + + + + Y + + + - - - - - conditionals.qs:203:9 Foo(q); + conditionals.qs:191:5 Bar(q); - Foo + Bar - - + + - conditionals.qs:202:5 if (M(q1) == One) { + conditionals.qs:203:9 Foo(q); - if - : - c - 22 - = |1〉 + x="424" + y="3049" + class="qs-maintext qs-group-label" + style="pointer-events: all" + > + Foo - - + + @@ -7557,8 +6798,8 @@ @@ -7566,17 +6807,17 @@ - - + + @@ -7591,22 +6832,22 @@ @@ -7624,16 +6865,16 @@ |0⟩ @@ -7652,22 +6893,22 @@ @@ -7685,16 +6926,16 @@ |0⟩ @@ -7711,9 +6952,9 @@ @@ -7726,112 +6967,42 @@ - - - - - - - c - - 23 - - - - - + + + conditionals.qs:212:13 X(q0); + + - - conditionals.qs:212:13 X(q0); - - - - - - + + + - - - - conditionals.qs:211:9 if results[j] == One { - - - if - : - c - - 23 - - = |1〉 - - - - - @@ -7839,7 +7010,7 @@ @@ -7847,8 +7018,8 @@ - - + + - - - - - - - c - - 24 - - - - - + + + conditionals.qs:212:13 X(q0); + + - - conditionals.qs:212:13 X(q0); - - - - - - + + + - - - - conditionals.qs:211:9 if results[j] == One { - - - if - : - c - - 24 - - = |1〉 - - - - - conditionals.qs:210:19 for j in 0..1 { @@ -7980,8 +7081,8 @@ - - + + @@ -7990,7 +7091,7 @@ @@ -7999,8 +7100,8 @@ - - + + H @@ -8082,8 +7183,8 @@ conditionals.qs:217:9 Baz(q0); @@ -8091,8 +7192,8 @@ - - + + @@ -8100,8 +7201,8 @@ conditionals.qs:216:19 for j in 0..1 { @@ -8109,8 +7210,8 @@ - - + + H @@ -8177,8 +7278,8 @@ conditionals.qs:217:9 Baz(q0); @@ -8186,8 +7287,8 @@ - - + + @@ -8195,8 +7296,8 @@ conditionals.qs:216:19 for j in 0..1 { @@ -8204,8 +7305,8 @@ - - + + @@ -8213,8 +7314,8 @@ conditionals.qs:216:5 for j in 0..1 { @@ -8223,8 +7324,8 @@ - - + + @@ -8235,7 +7336,7 @@ @@ -8243,17 +7344,17 @@ - - + + @@ -8267,22 +7368,22 @@ @@ -8298,22 +7399,22 @@ @@ -8330,16 +7431,16 @@ |0⟩ @@ -8358,16 +7459,16 @@ |0⟩ @@ -8384,9 +7485,9 @@ @@ -8399,25 +7500,25 @@ - + - - - - - - - c - - 25 - - - - - - - - - c - - 26 - - - - - + + + conditionals.qs:234:13 X(q); + + - - conditionals.qs:234:13 X(q); - - - - - - + + + - - - - conditionals.qs:233:9 if r1 == One { - - - if - : - c - - 26 - - = |1〉 - - - - - - - - - - - - c - - 25 - - - - - - - - - c - - 26 - - - - - - - - conditionals.qs:236:13 Z(q); - - - - - Z - - - - - - + - - conditionals.qs:233:9 if r1 == One { - - - if - : - c - - 26 - - = |0〉 - + conditionals.qs:236:13 Z(q); + + + + + + + Z + + + - - - - @@ -8712,7 +7619,7 @@ @@ -8726,8 +7633,8 @@ - - + + @@ -8736,7 +7643,7 @@ @@ -8744,8 +7651,8 @@ - - + + @@ -8757,7 +7664,7 @@ @@ -8767,15 +7674,15 @@ - - + + H @@ -8814,23 +7721,23 @@ @@ -8843,27 +7750,27 @@ data-expanded="true" > - + H @@ -8914,23 +7821,23 @@ @@ -8942,8 +7849,8 @@ conditionals.qs:245:5 if r1 == One { @@ -8955,8 +7862,8 @@ - - + + @@ -8967,8 +7874,8 @@ @@ -8976,15 +7883,15 @@ - - + + H @@ -9024,17 +7931,17 @@ H @@ -9052,23 +7959,23 @@ @@ -9081,27 +7988,27 @@ data-expanded="true" > - + @@ -9160,8 +8067,8 @@ @@ -9173,8 +8080,8 @@ - - + + @@ -9186,23 +8093,23 @@ @@ -9215,27 +8122,27 @@ data-expanded="true" > - + @@ -9294,8 +8201,8 @@ @@ -9307,8 +8214,8 @@ - - + + @@ -9319,8 +8226,8 @@ @@ -9330,8 +8237,8 @@ - - + + @@ -9341,17 +8248,17 @@ |0⟩ @@ -9367,17 +8274,17 @@ |0⟩ @@ -9393,17 +8300,17 @@ |0⟩ @@ -9419,17 +8326,17 @@ |0⟩ @@ -9445,17 +8352,17 @@ |0⟩ @@ -9471,17 +8378,17 @@ |0⟩ @@ -9497,17 +8404,17 @@ |0⟩ @@ -9523,17 +8430,17 @@ |0⟩ @@ -9549,17 +8456,17 @@ |0⟩ @@ -9575,17 +8482,17 @@ |0⟩ @@ -9601,17 +8508,17 @@ |0⟩ @@ -9627,17 +8534,17 @@ |0⟩ @@ -9653,17 +8560,17 @@ |0⟩ @@ -9679,17 +8586,17 @@ |0⟩ @@ -9705,17 +8612,17 @@ |0⟩ @@ -9731,17 +8638,17 @@ |0⟩ @@ -9757,17 +8664,17 @@ |0⟩ @@ -9783,17 +8690,17 @@ |0⟩ @@ -9809,17 +8716,17 @@ |0⟩ @@ -9835,17 +8742,17 @@ |0⟩ @@ -9861,17 +8768,17 @@ |0⟩ @@ -9887,17 +8794,17 @@ |0⟩ @@ -9913,17 +8820,17 @@ |0⟩ @@ -9939,17 +8846,17 @@ |0⟩ @@ -9965,17 +8872,17 @@ |0⟩ @@ -9991,17 +8898,17 @@ |0⟩ @@ -10017,17 +8924,17 @@ |0⟩ @@ -10043,17 +8950,17 @@ |0⟩ @@ -10069,17 +8976,17 @@ |0⟩ @@ -10095,17 +9002,17 @@ |0⟩ @@ -10121,17 +9028,17 @@ |0⟩ @@ -10147,17 +9054,17 @@ |0⟩ @@ -10173,17 +9080,17 @@ |0⟩ @@ -10199,17 +9106,17 @@ |0⟩ diff --git a/source/npm/qsharp/test/circuits-cases/if-else.qs.snapshot.html b/source/npm/qsharp/test/circuits-cases/if-else.qs.snapshot.html index b35c9813c44..14d78fa18d6 100644 --- a/source/npm/qsharp/test/circuits-cases/if-else.qs.snapshot.html +++ b/source/npm/qsharp/test/circuits-cases/if-else.qs.snapshot.html @@ -13,9 +13,9 @@ - - + + @@ -117,8 +117,8 @@ class="gate-unitary" x="80" y="46" - width="462" - height="268" + width="268" + height="232" fill-opacity="0" stroke-dasharray="8, 8" /> @@ -173,175 +173,68 @@ - - - - - - - c - 0 - - - - - - - if-else.qs:9:9 X(q1); - - - - - - - - + - if-else.qs:8:5 if r == One { - - if - : - c - 0 - = |1〉 - + if-else.qs:9:9 X(q1); + + + + + + + - - - - - - - - - - - c - 0 - - - - - - - if-else.qs:11:9 Y(q1); - - - - - Y - - - - - - + - if-else.qs:8:5 if r == One { - - if - : - c - 0 - = |0〉 - + if-else.qs:11:9 Y(q1); + + + + + + + Y + + + - - - - @@ -349,23 +242,23 @@ @@ -394,9 +287,9 @@ - - + + @@ -498,8 +391,8 @@ class="gate-unitary" x="80" y="46" - width="462" - height="268" + width="268" + height="232" fill-opacity="0" stroke-dasharray="8, 8" /> @@ -554,175 +447,68 @@ - - - - - - - c - 0 - - - - - - - if-else.qs:9:9 X(q1); - - - - - - - - + - if-else.qs:8:5 if r == One { - - if - : - c - 0 - = |1〉 - + if-else.qs:9:9 X(q1); + + + + + + + - - - - - - - - - - - c - 0 - - - - - - - if-else.qs:11:9 Y(q1); - - - - - Y - - - - - - + - if-else.qs:8:5 if r == One { - - if - : - c - 0 - = |0〉 - + if-else.qs:11:9 Y(q1); + + + + + + + Y + + + - - - - @@ -730,23 +516,23 @@ From 7c92b3edb364a1d2186331b3675f9e4db56b0f2b Mon Sep 17 00:00:00 2001 From: Dima Fedoriaka Date: Fri, 28 Aug 2026 11:53:51 -0700 Subject: [PATCH 4/6] update python tests --- source/qdk_package/tests/test_qasm.py | 8 ++++---- source/qdk_package/tests/test_qsharp.py | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/source/qdk_package/tests/test_qasm.py b/source/qdk_package/tests/test_qasm.py index e68bc0f56a9..df686ba2418 100644 --- a/source/qdk_package/tests/test_qasm.py +++ b/source/qdk_package/tests/test_qasm.py @@ -720,8 +720,8 @@ def test_circuit_from_program_static() -> None: generation_method=CircuitGenerationMethod.Static, ) assert str(c) == dedent("""\ - q_0 ── H ──── M ──── if: c_0 = |1〉 ── - ╘═══════════ ● ════════ + q_0 ── H ──── M ──── X ── + ╘═════ ● ══ """) @@ -866,8 +866,8 @@ def test_circuit_from_callable_static() -> None: generation_method=CircuitGenerationMethod.Static, ) assert str(c) == dedent("""\ - q_0 ── H ──── M ──── if: c_0 = |1〉 ── - ╘═══════════ ● ════════ + q_0 ── H ──── M ──── X ── + ╘═════ ● ══ """) diff --git a/source/qdk_package/tests/test_qsharp.py b/source/qdk_package/tests/test_qsharp.py index 942dc4be788..4b2ffbc6a79 100644 --- a/source/qdk_package/tests/test_qsharp.py +++ b/source/qdk_package/tests/test_qsharp.py @@ -1100,7 +1100,7 @@ def test_circuit_with_static_generation_method() -> None: use q = Qubit(); H(q); let r = M(q); - if r == One { X(q); } + if r == One { X(q); } else { Z(q); } Reset(q); r } @@ -1109,8 +1109,8 @@ def test_circuit_with_static_generation_method() -> None: "Foo()", generation_method=qsharp.CircuitGenerationMethod.Static ) assert str(circuit) == dedent("""\ - q_0 ── H ──── M ──── if: c_0 = |1〉 ──── |0〉 ── - ╘═══════════ ● ═════════════════ + q_0 ── H ──── M ──── X ──── Z ──── |0〉 ── + ╘═════ ● ════ ○ ═══════════ """) @@ -1129,8 +1129,8 @@ def test_circuit_from_qsharp_callable_static() -> None: qdk.code.Foo, generation_method=qsharp.CircuitGenerationMethod.Static ) assert str(circuit) == dedent("""\ - q_0 ── H ──── M ──── if: c_0 = |1〉 ──── |0〉 ── - ╘═══════════ ● ═════════════════ + q_0 ── H ──── M ──── X ──── |0〉 ── + ╘═════ ● ═══════════ """) From 7ba1998affc9a2d2825a78ea8d002b6bfc6369d5 Mon Sep 17 00:00:00 2001 From: Dima Fedoriaka Date: Fri, 28 Aug 2026 12:16:07 -0700 Subject: [PATCH 5/6] update rust tests --- .../interpret/circuit_classical_ctl_tests.rs | 156 +++++------------- 1 file changed, 41 insertions(+), 115 deletions(-) diff --git a/source/compiler/qsc/src/interpret/circuit_classical_ctl_tests.rs b/source/compiler/qsc/src/interpret/circuit_classical_ctl_tests.rs index beda10176a6..d99c97388d9 100644 --- a/source/compiler/qsc/src/interpret/circuit_classical_ctl_tests.rs +++ b/source/compiler/qsc/src/interpret/circuit_classical_ctl_tests.rs @@ -46,12 +46,8 @@ fn result_comparison_to_literal() { ╘═════ [1] Main: - q_0 ─ H@test.qs:2:4 ── M@test.qs:3:13 ──── if: c_0 = |1〉@test.qs:4:4[2] ───── |0〉@test.qs:7:4 ── - ╘═════════════════════════ ● ═════════════════════════════════════ - - [2] if: c_0 = |1〉: - q_0 ─ X@test.qs:5:8 ─ - + q_0 ─ H@test.qs:2:4 ── M@test.qs:3:13 ─── X@test.qs:5:8 ─── |0〉@test.qs:7:4 ── + ╘════════════════ ● ════════════════════════════ "#]] .assert_eq(&circ); } @@ -76,12 +72,8 @@ fn result_comparison_to_literal_zero() { ╘═════ [1] Main: - q_0 ─ H@test.qs:2:4 ── M@test.qs:3:13 ──── if: c_0 = |0〉@test.qs:4:4[2] ───── |0〉@test.qs:7:4 ── - ╘═════════════════════════ ● ═════════════════════════════════════ - - [2] if: c_0 = |0〉: - q_0 ─ X@test.qs:5:8 ─ - + q_0 ─ H@test.qs:2:4 ── M@test.qs:3:13 ─── X@test.qs:5:8 ─── |0〉@test.qs:7:4 ── + ╘════════════════ ○ ════════════════════════════ "#]] .assert_eq(&circ); } @@ -107,12 +99,8 @@ fn else_block_only() { ╘═════ [1] Main: - q_0 ─ H@test.qs:2:4 ── M@test.qs:3:13 ──── if: c_0 = |1〉@test.qs:4:4[2] ───── |0〉@test.qs:8:4 ── - ╘═════════════════════════ ● ═════════════════════════════════════ - - [2] if: c_0 = |1〉: - q_0 ─ X@test.qs:6:8 ─ - + q_0 ─ H@test.qs:2:4 ── M@test.qs:3:13 ─── X@test.qs:6:8 ─── |0〉@test.qs:8:4 ── + ╘════════════════ ● ════════════════════════════ "#]] .assert_eq(&circ); } @@ -215,22 +203,10 @@ fn if_else() { ╘═════ [1] Main: - q_0 ─ H@test.qs:3:4 ── M@test.qs:4:12 ─────────────────────────────────────────────────────────────────────────────────────────── - ╘═════════════════════════ ● ════════════════════════════════ ● ═══════════════════════════════════ - q_1 ────────────────────────────────────── if: c_0 = |1〉@test.qs:5:4[2] ───── if: c_0 = |0〉@test.qs:5:4[3] ──── M@test.qs:10:13 ─ - ╘═════════ - - [2] if: c_0 = |1〉: - q_0 ───────────────── - - q_1 ─ X@test.qs:6:8 ─ - - - [3] if: c_0 = |0〉: - q_0 ───────────────── - - q_1 ─ Y@test.qs:8:8 ─ - + q_0 ─ H@test.qs:3:4 ── M@test.qs:4:12 ─────────────────────────────────────────────────────── + ╘════════════════ ● ══════════════ ○ ══════════════════════════ + q_1 ───────────────────────────────────── X@test.qs:6:8 ── Y@test.qs:8:8 ── M@test.qs:10:13 ─ + ╘═════════ "#]] .assert_eq(&circ); } @@ -270,44 +246,12 @@ fn sequential_ifs() { ╘═════ [1] Main: - q_0 ─ H@test.qs:4:4 ── M@test.qs:6:13 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── - ╘═════════════════════════ ● ════════════════════════════════ ● ═════════════════════════════════════════════════════════════════════════════════════════════════════════ - q_1 ─ H@test.qs:5:4 ── M@test.qs:7:13 ───────────────────┼──────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────── - ╘══════════════════════════╪══════════════════════════════════╪═════════════════════════════════ ● ════════════════════════════════ ● ═══════════════════════════════════ - q_2 ────────────────────────────────────── if: c_0 = |1〉@test.qs:8:4[2] ───── if: c_0 = |0〉@test.qs:8:4[3] ───── if: c_1 = |1〉@test.qs:13:4[4] ──── if: c_1 = |0〉@test.qs:13:4[5] ─── M@test.qs:18:13 ─ - ╘═════════ - - [2] if: c_0 = |1〉: - q_0 ───────────────── - - q_1 ───────────────── - - q_2 ─ X@test.qs:9:8 ─ - - - [3] if: c_0 = |0〉: - q_0 ─────────────────── - - q_1 ─────────────────── - - q_2 ─ Z@test.qs:11:8 ── - - - [4] if: c_1 = |1〉: - q_0 ─────────────────── - - q_1 ─────────────────── - - q_2 ─ X@test.qs:14:8 ── - - - [5] if: c_1 = |0〉: - q_0 ─────────────────── - - q_1 ─────────────────── - - q_2 ─ Y@test.qs:16:8 ── - + q_0 ─ H@test.qs:4:4 ── M@test.qs:6:13 ─────────────────────────────────────────────────────────────────────────────────────────────── + ╘════════════════ ● ═══════════════ ○ ═════════════════════════════════════════════════════════════════ + q_1 ─ H@test.qs:5:4 ── M@test.qs:7:13 ──────────┼─────────────────┼────────────────────────────────────────────────────────────────── + ╘═════════════════╪═════════════════╪═════════════════ ● ════════════════ ○ ═══════════════════════════ + q_2 ───────────────────────────────────── X@test.qs:9:8 ── Z@test.qs:11:8 ─── X@test.qs:14:8 ─── Y@test.qs:16:8 ─── M@test.qs:18:13 ─ + ╘═════════ "#]] .assert_eq(&circ); } @@ -346,43 +290,19 @@ fn nested_ifs() { ╘═════ [1] Main: - q_0 ─ H@test.qs:4:4 ── M@test.qs:6:13 ─────────────────────────────────────────────────────────────────────────────────────────── - ╘═════════════════════════ ● ════════════════════════════════ ● ═══════════════════════════════════ - q_1 ─ H@test.qs:5:4 ── M@test.qs:7:13 ───────────────────┼──────────────────────────────────┼──────────────────────────────────── - ╘══════════════════════════╪══════════════════════════════════╪════════════════════════════════════ - q_2 ────────────────────────────────────── if: c_0 = |1〉@test.qs:8:4[2] ───── if: c_0 = |0〉@test.qs:8:4[3] ──── M@test.qs:17:13 ─ - ╘═════════ + q_0 ─ H@test.qs:4:4 ── M@test.qs:6:13 ─────────────────────────────────────────────────────────────────────────── + ╘═════════════════════════ ● ════════════════════════ ○ ═══════════════════════════ + q_1 ─ H@test.qs:5:4 ── M@test.qs:7:13 ───────────────────┼──────────────────────────┼──────────────────────────── + ╘══════════════════════════╪══════════════════════════╪════════════════════════════ + q_2 ────────────────────────────────────── if: c_0 = |1〉@test.qs:8:4[2] ──── Z@test.qs:15:8 ─── M@test.qs:17:13 ─ + ╘═════════ [2] if: c_0 = |1〉: - q_0 ────────────────────────────────────────────────────────────────────── - ════════════════ ● ════════════════════════════════ ● ════════════════ - q_1 ─────────────────┼──────────────────────────────────┼───────────────── - ════════════════ ● ════════════════════════════════ ● ════════════════ - q_2 ── if: c_1 = |1〉@test.qs:9:8[4] ───── if: c_1 = |0〉@test.qs:9:8[5] ─── - - - [3] if: c_0 = |0〉: - q_0 ─────────────────── - - q_1 ─────────────────── + q_0 ────────────────────────────────────── - q_2 ─ Z@test.qs:15:8 ── - - - [4] if: c_1 = |1〉: - q_0 ─────────────────── - - q_1 ─────────────────── - - q_2 ─ X@test.qs:10:12 ─ - - - [5] if: c_1 = |0〉: - q_0 ─────────────────── - - q_1 ─────────────────── - - q_2 ─ Y@test.qs:12:12 ─ + q_1 ────────────────────────────────────── + ● ○ + q_2 ─ X@test.qs:10:12 ── Y@test.qs:12:12 ─ "#]] .assert_eq(&circ); @@ -579,15 +499,21 @@ fn nested_callables_in_branch() { ╘═════ [1] Main: - q_0 ─ [ [Foo@test.qs:2:4] ── [ [Bar@test.qs:10:4] ─── X@test.qs:13:4 ─── Y@test.qs:14:4 ──── ] ──── ] ───────────────────── if: c_0 = |1〉@test.qs:5:4[2] ─── - │ - q_1 ──── H@test.qs:4:4 ────────────────────────────────────────────────────────────────────────────────── M@test.qs:5:8 ──────────────────┼───────────────── - ╘════════════════════════ ● ════════════════ - - [2] if: c_0 = |1〉: - q_0 ─ [ [Foo@test.qs:6:8] ── [ [Bar@test.qs:10:4] ─── X@test.qs:13:4 ─── Y@test.qs:14:4 ──── ] ──── ] ── - q_1 ──────────────────────────────────────────────────────────────────────────────────────────────────── - + q_0 ─ [ [Foo@test.qs:2:4] ── [ [Bar@test.qs:10:4] ─── X@test.qs:13:4 ─── Y@test.qs:14:4 ──── ] ──── ] ──────────────────── Foo@test.qs:6:8[2] ── + ┆ + q_1 ──── H@test.qs:4:4 ────────────────────────────────────────────────────────────────────────────────── M@test.qs:5:8 ────────────┆─────────── + ╘═══════════════════┆═══════════ + + [2] Foo: + q_0 ─ Bar@test.qs:10:4[3] ─ + ┆ + q_1 ───────────┆─────────── + ╘═══════════ + + [3] Bar: + q_0 ─ X@test.qs:13:4 ─── Y@test.qs:14:4 ── + q_1 ─────────┼──────────────────┼───────── + ● ● "#]] .assert_eq(&circ); } From 3a3bb47f64140934edbac2c9d0af2c1b40be58d3 Mon Sep 17 00:00:00 2001 From: Dima Fedoriaka Date: Fri, 28 Aug 2026 12:32:42 -0700 Subject: [PATCH 6/6] update samples test --- source/samples_test/src/tests/OpenQASM.rs | 2 +- source/samples_test/src/tests/algorithms.rs | 12 ++++++------ source/samples_test/src/tests/getting_started.rs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/samples_test/src/tests/OpenQASM.rs b/source/samples_test/src/tests/OpenQASM.rs index 8ad6341eef5..b2b7f710ca6 100644 --- a/source/samples_test/src/tests/OpenQASM.rs +++ b/source/samples_test/src/tests/OpenQASM.rs @@ -44,6 +44,6 @@ pub const SIMPLE1DISINGORDER1_EXPECT_QIR_ADAPTIVE: Expect = expect!["generated QIR of length 11216"]; pub const TELEPORTATION_EXPECT: Expect = expect!["Zero"]; pub const TELEPORTATION_EXPECT_DEBUG: Expect = expect!["Zero"]; -pub const TELEPORTATION_EXPECT_CIRCUIT: Expect = expect!["generated circuit of length 2086"]; +pub const TELEPORTATION_EXPECT_CIRCUIT: Expect = expect!["generated circuit of length 1394"]; pub const TELEPORTATION_EXPECT_QIR_ADAPTIVE_RIF: Expect = expect!["generated QIR of length 3062"]; pub const TELEPORTATION_EXPECT_QIR_ADAPTIVE: Expect = expect!["generated QIR of length 7274"]; diff --git a/source/samples_test/src/tests/algorithms.rs b/source/samples_test/src/tests/algorithms.rs index 211e45571cc..52f04d431cb 100644 --- a/source/samples_test/src/tests/algorithms.rs +++ b/source/samples_test/src/tests/algorithms.rs @@ -36,7 +36,7 @@ pub const BITFLIPCODE_EXPECT_DEBUG: Expect = expect![[r#" |000⟩: 0.4472+0.0000𝑖 |111⟩: 0.8944+0.0000𝑖 One"#]]; -pub const BITFLIPCODE_EXPECT_CIRCUIT: Expect = expect!["generated circuit of length 8068"]; +pub const BITFLIPCODE_EXPECT_CIRCUIT: Expect = expect!["generated circuit of length 6484"]; pub const BITFLIPCODE_EXPECT_QIR_ADAPTIVE_RIF: Expect = expect!["generated QIR of length 3794"]; pub const BITFLIPCODE_EXPECT_QIR_ADAPTIVE: Expect = expect!["generated QIR of length 5457"]; pub const DEUTSCHJOZSA_EXPECT: Expect = expect!["[true, false, true, false]"]; @@ -65,7 +65,7 @@ pub const DOTPRODUCTVIAPHASEESTIMATION_EXPECT_DEBUG: Expect = expect![[r#" Computed value = 1.0, true value = 0.995974293995239 (16, 4)"#]]; pub const DOTPRODUCTVIAPHASEESTIMATION_EXPECT_CIRCUIT: Expect = - expect!["generated circuit of length 122966"]; + expect!["generated circuit of length 119825"]; pub const DOTPRODUCTVIAPHASEESTIMATION_EXPECT_QIR_ADAPTIVE_RIF: Expect = expect!["generated QIR of length 139362"]; pub const DOTPRODUCTVIAPHASEESTIMATION_EXPECT_QIR_ADAPTIVE: Expect = @@ -151,7 +151,7 @@ pub const PHASEFLIPCODE_EXPECT_DEBUG: Expect = expect![[r#" |110⟩: 0.4743+0.0000𝑖 |111⟩: −0.1581+0.0000𝑖 One"#]]; -pub const PHASEFLIPCODE_EXPECT_CIRCUIT: Expect = expect!["generated circuit of length 9784"]; +pub const PHASEFLIPCODE_EXPECT_CIRCUIT: Expect = expect!["generated circuit of length 8201"]; pub const PHASEFLIPCODE_EXPECT_QIR_ADAPTIVE_RIF: Expect = expect!["generated QIR of length 4734"]; pub const PHASEFLIPCODE_EXPECT_QIR_ADAPTIVE: Expect = expect!["generated QIR of length 7695"]; pub const QRNG_EXPECT: Expect = expect!["7568811972615905454"]; @@ -216,7 +216,7 @@ pub const SIMPLEVQE_EXPECT_QIR_ADAPTIVE: Expect = expect!["compilation error: cannot use a dynamically-sized array"]; pub const SUPERDENSECODING_EXPECT: Expect = expect!["((false, true), (false, true))"]; pub const SUPERDENSECODING_EXPECT_DEBUG: Expect = expect!["((false, true), (false, true))"]; -pub const SUPERDENSECODING_EXPECT_CIRCUIT: Expect = expect!["generated circuit of length 4891"]; +pub const SUPERDENSECODING_EXPECT_CIRCUIT: Expect = expect!["generated circuit of length 4227"]; pub const SUPERDENSECODING_EXPECT_QIR_ADAPTIVE_RIF: Expect = expect!["generated QIR of length 4842"]; pub const SUPERDENSECODING_EXPECT_QIR_ADAPTIVE: Expect = expect!["generated QIR of length 5812"]; @@ -280,13 +280,13 @@ pub const TELEPORTATION_EXPECT_DEBUG: Expect = expect![[r#" |0⟩: 0.7071+0.0000𝑖 |1⟩: −0.7071+0.0000𝑖 [Zero, One, Zero, One]"#]]; -pub const TELEPORTATION_EXPECT_CIRCUIT: Expect = expect!["generated circuit of length 14950"]; +pub const TELEPORTATION_EXPECT_CIRCUIT: Expect = expect!["generated circuit of length 12039"]; pub const TELEPORTATION_EXPECT_QIR_ADAPTIVE_RIF: Expect = expect!["generated QIR of length 8555"]; pub const TELEPORTATION_EXPECT_QIR_ADAPTIVE: Expect = expect!["generated QIR of length 9085"]; pub const THREEQUBITREPETITIONCODE_EXPECT: Expect = expect!["(true, 0)"]; pub const THREEQUBITREPETITIONCODE_EXPECT_DEBUG: Expect = expect!["(true, 0)"]; pub const THREEQUBITREPETITIONCODE_EXPECT_CIRCUIT: Expect = - expect!["generated circuit of length 51383"]; + expect!["generated circuit of length 42388"]; pub const THREEQUBITREPETITIONCODE_EXPECT_QIR_ADAPTIVE_RIF: Expect = expect!["generated QIR of length 18122"]; pub const THREEQUBITREPETITIONCODE_EXPECT_QIR_ADAPTIVE: Expect = diff --git a/source/samples_test/src/tests/getting_started.rs b/source/samples_test/src/tests/getting_started.rs index e2fca6c6b6e..0243ea79760 100644 --- a/source/samples_test/src/tests/getting_started.rs +++ b/source/samples_test/src/tests/getting_started.rs @@ -86,7 +86,7 @@ pub const SIMPLETELEPORTATION_EXPECT_DEBUG: Expect = expect![[r#" |000⟩: 1.0000+0.0000𝑖 Teleportation successful: true. true"#]]; -pub const SIMPLETELEPORTATION_EXPECT_CIRCUIT: Expect = expect!["generated circuit of length 2123"]; +pub const SIMPLETELEPORTATION_EXPECT_CIRCUIT: Expect = expect!["generated circuit of length 1463"]; pub const SIMPLETELEPORTATION_EXPECT_QIR_ADAPTIVE_RIF: Expect = expect!["generated QIR of length 3118"]; pub const SIMPLETELEPORTATION_EXPECT_QIR_ADAPTIVE: Expect = expect!["generated QIR of length 4030"];