cqlib_core/circuit/circuit_classical.rs
1// This code is part of Cqlib.
2//
3// (C) Copyright China Telecom Quantum Group 2026
4//
5// This code is licensed under the Apache License, Version 2.0. You may
6// obtain a copy of this license in the LICENSE.txt file in the root directory
7// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8//
9// Any modifications or derivative works of this code must retain this
10// copyright notice, and modified files need to carry a notice indicating
11// that they have been altered from the originals.
12
13#![allow(rustdoc::private_doc_tests)]
14
15//! Circuit builder support for runtime classical data and structured control flow.
16//!
17//! This module is the circuit-level API layer for dynamic circuits. It connects
18//! three lower-level concepts:
19//!
20//! - [`ClassicalValue`]: immutable runtime results produced by operations such
21//! as measurement. Values are SSA-like: once produced, they are never
22//! overwritten.
23//! - [`Measurement`]: self-contained receipt returned by measurement builders.
24//! It contains both the immutable [`ClassicalValue`] and the measured qubits
25//! in result bit order.
26//! - [`ClassicalVar`]: mutable runtime storage slots allocated by a circuit.
27//! Variables are used when a classical result must be stored, reused, or
28//! updated across loop iterations.
29//! - [`ClassicalExpr`]: typed, side-effect-free expressions that read values
30//! and variables. Control-flow operations consume expressions; they do not
31//! read mutable storage directly.
32//!
33//! The public API intentionally keeps measurement, storage, and control flow as
34//! separate concepts:
35//!
36//! - [`Circuit::measure`] and [`Circuit::measure_bits`] append measurement
37//! operations and return [`Measurement`] handles.
38//! - [`Circuit::store`] copies an expression result into mutable
39//! [`ClassicalVar`] storage.
40//! - Structured control-flow operations consume [`ClassicalExpr`] values.
41//!
42//! This separation allows a measurement result to drive control flow directly
43//! without allocating mutable storage. A variable is needed only when a value
44//! must be overwritten, carried across loop iterations, or retained as named
45//! runtime state.
46//!
47//! # Measurement model
48//!
49//! Every measurement remains an operation in the circuit schedule. Its qubits,
50//! position, result type, and immutable result handle are therefore preserved
51//! by the circuit IR. The returned [`Measurement`] identifies that specific
52//! measurement operation and exposes the measured qubit order; measuring the
53//! same qubit again creates a different value.
54//!
55//! [`Circuit::measure`] produces a [`ClassicalType::Bit`].
56//! [`Circuit::measure_bits`] produces a [`ClassicalType::BitVec`] whose width is
57//! the number of measured qubits. For a multi-qubit measurement, input order is
58//! bit-vector order: the first qubit maps to bit index `0`, the least-significant
59//! bit.
60//!
61//! Use [`Measurement::value`] or [`Measurement::expr`] when the IR value is
62//! needed for control flow or storage. Use [`Measurement::qubits`] when a state
63//! sampler needs to know which qubits to sample and how to order the result.
64//!
65//! Measurement results are runtime values, not compile-time booleans. Convert a
66//! measured `Bit` explicitly with [`ClassicalExpr::bit_to_bool`] before using it
67//! as an `if` or `while` condition.
68//!
69//! The circuit IR records measurements and their data-flow uses, but it does not
70//! define a result presentation policy. Selecting result fields, grouping them
71//! into user-visible registers, and formatting bit strings belong to the
72//! execution/result layer rather than these builder APIs.
73//!
74//! # Measurement value used directly by `if`
75//!
76//! ```rust
77//! use cqlib_core::circuit::{
78//! Circuit, CircuitError, ClassicalExpr, Qubit,
79//! };
80//!
81//! fn build() -> Result<Circuit, CircuitError> {
82//! let mut circuit = Circuit::new(2);
83//! let q0 = Qubit::new(0);
84//! let q1 = Qubit::new(1);
85//!
86//! let measured = circuit.measure(q0)?;
87//! let condition = ClassicalExpr::bit_to_bool(measured.expr())?;
88//!
89//! circuit.if_(condition, |body| {
90//! body.x(q1)?;
91//! Ok(())
92//! })?;
93//!
94//! Ok(circuit)
95//! }
96//! ```
97//!
98//! Expected circuit shape:
99//!
100//! ```text
101//! measure_bit q[0] -> v0: Bit
102//! if bit_to_bool(v0) {
103//! x q[1]
104//! }
105//! ```
106//!
107//! The measurement value is immutable and belongs to this circuit. It can be
108//! read by any later expression for which it is available, but it cannot be
109//! overwritten.
110//!
111//! # Multi-qubit measurement and bit order
112//!
113//! ```rust
114//! use cqlib_core::circuit::{Circuit, CircuitError, Qubit};
115//!
116//! fn build() -> Result<Circuit, CircuitError> {
117//! let mut circuit = Circuit::new(3);
118//! let measured = circuit.measure_bits([
119//! Qubit::new(2),
120//! Qubit::new(0),
121//! Qubit::new(1),
122//! ])?;
123//!
124//! assert_eq!(measured.ty().width(), 3);
125//! Ok(circuit)
126//! }
127//! ```
128//!
129//! The result layout is:
130//!
131//! ```text
132//! result[0] = measure(q[2]) // least-significant bit
133//! result[1] = measure(q[0])
134//! result[2] = measure(q[1]) // most-significant bit
135//! ```
136//!
137//! # Measuring into mutable storage
138//!
139//! [`Circuit::measure_into`] and [`Circuit::measure_bits_into`] are convenience
140//! operations. They append the same measurement as [`Circuit::measure`] or
141//! [`Circuit::measure_bits`], then append a [`Circuit::store`] into the supplied
142//! variable. They still return the same [`Measurement`] handle.
143//!
144//! ```rust
145//! use cqlib_core::circuit::{
146//! Circuit, CircuitError, ClassicalType, Qubit,
147//! };
148//!
149//! fn build() -> Result<Circuit, CircuitError> {
150//! let mut circuit = Circuit::new(2);
151//! let latest = circuit.var(ClassicalType::bit_vec(2).unwrap());
152//!
153//! let measured = circuit.measure_bits_into(
154//! [Qubit::new(0), Qubit::new(1)],
155//! latest,
156//! )?;
157//!
158//! assert_eq!(measured.ty(), latest.ty());
159//! Ok(circuit)
160//! }
161//! ```
162//!
163//! Conceptually, this appends:
164//!
165//! ```text
166//! measure_bits q[0], q[1] -> v0: BitVec(2)
167//! store s0 <- v0
168//! ```
169//!
170//! The measurement handle's immutable value `v0` always denotes this
171//! measurement occurrence. The variable `s0` denotes mutable storage and may be
172//! overwritten later, which makes it suitable for loop-carried or reusable
173//! runtime state. Measuring into a variable does not by itself declare how an
174//! executor presents results.
175//!
176//! # Storing reusable classical state
177//!
178//! Use [`Circuit::var`] and [`Circuit::store`] when a classical expression must
179//! be preserved or updated later.
180//!
181//! ```rust
182//! use cqlib_core::circuit::{
183//! Circuit, CircuitError, ClassicalExpr, ClassicalType, Qubit,
184//! };
185//!
186//! fn build() -> Result<Circuit, CircuitError> {
187//! let q0 = Qubit::new(0);
188//! let q1 = Qubit::new(1);
189//! let mut circuit = Circuit::from_qubits(vec![q0, q1])?;
190//! circuit.h(q0)?;
191//!
192//! let flag = circuit.var(ClassicalType::Bool);
193//! let measured = circuit.measure(q0)?;
194//! let condition = ClassicalExpr::bit_to_bool(measured.expr())?;
195//! circuit.store(flag, condition)?;
196//!
197//! circuit.if_(flag.expr(), |body| {
198//! body.z(q1)?;
199//! Ok(())
200//! })?;
201//!
202//! Ok(circuit)
203//! }
204//! ```
205//!
206//! Expected circuit shape:
207//!
208//! ```text
209//! var s0: Bool
210//! measure_bit q[0] -> v0: Bit
211//! store s0 <- bit_to_bool(v0)
212//! if s0 {
213//! z q[1]
214//! }
215//! ```
216//!
217//! # Loop-carried classical state
218//!
219//! A loop condition should read a variable when the condition is updated inside
220//! the loop body.
221//!
222//! ```rust,no_run
223//! use cqlib_core::circuit::{
224//! Circuit, CircuitError, ClassicalExpr, ClassicalType, Qubit,
225//! };
226//!
227//! fn build() -> Result<Circuit, CircuitError> {
228//! let mut circuit = Circuit::new(1);
229//! let q0 = Qubit::new(0);
230//!
231//! let keep_running = circuit.var(ClassicalType::Bool);
232//! circuit.store(keep_running, ClassicalExpr::bool_literal(true))?;
233//!
234//! circuit.while_(keep_running.expr(), |body| {
235//! let measured = body.measure(q0)?;
236//! let next = ClassicalExpr::bit_to_bool(measured.expr())?;
237//! body.store(keep_running, next)?;
238//! Ok(())
239//! })?;
240//!
241//! Ok(circuit)
242//! }
243//! ```
244//!
245//! Expected circuit shape:
246//!
247//! ```text
248//! var s0: Bool
249//! store s0 <- true
250//! while s0 {
251//! measure_bit q[0] -> v0: Bit
252//! store s0 <- bit_to_bool(v0)
253//! }
254//! ```
255//!
256//! # `if_else`
257//!
258//! ```rust,no_run
259//! use cqlib_core::circuit::{
260//! Circuit, CircuitError, ClassicalExpr, Qubit,
261//! };
262//!
263//! fn build() -> Result<Circuit, CircuitError> {
264//! let mut circuit = Circuit::new(2);
265//! let q0 = Qubit::new(0);
266//! let q1 = Qubit::new(1);
267//! let measured = circuit.measure(q0)?;
268//! let condition = ClassicalExpr::bit_to_bool(measured.expr())?;
269//!
270//! circuit.if_else(
271//! condition,
272//! |then_body| {
273//! then_body.x(q1)?;
274//! Ok(())
275//! },
276//! |else_body| {
277//! else_body.z(q1)?;
278//! Ok(())
279//! },
280//! )?;
281//!
282//! Ok(circuit)
283//! }
284//! ```
285//!
286//! Expected circuit shape:
287//!
288//! ```text
289//! measure_bit q[0] -> v0: Bit
290//! if bit_to_bool(v0) {
291//! x q[1]
292//! } else {
293//! z q[1]
294//! }
295//! ```
296//!
297//! # Unsigned `for` loop
298//!
299//! `for_uint` models a runtime unsigned range loop. The loop variable is a
300//! mutable [`ClassicalVar`], and the closure receives its read expression.
301//!
302//! ```rust,no_run
303//! use cqlib_core::circuit::{
304//! Circuit, CircuitError, ClassicalExpr, ClassicalType, Qubit,
305//! };
306//!
307//! fn build() -> Result<Circuit, CircuitError> {
308//! let mut circuit = Circuit::new(1);
309//! let q0 = Qubit::new(0);
310//! let i = circuit.var(ClassicalType::uint(8).unwrap());
311//!
312//! circuit.for_uint(
313//! i,
314//! ClassicalExpr::uint_literal(8, 0)?,
315//! ClassicalExpr::uint_literal(8, 4)?,
316//! ClassicalExpr::uint_literal(8, 1)?,
317//! |body, _i_expr| {
318//! body.h(q0)?;
319//! Ok(())
320//! },
321//! )?;
322//!
323//! Ok(circuit)
324//! }
325//! ```
326//!
327//! Expected circuit shape:
328//!
329//! ```text
330//! var s0: UInt(8)
331//! for s0 in 0_u8..4_u8 step 1_u8 {
332//! h q[0]
333//! }
334//! ```
335//!
336//! # `switch`
337//!
338//! [`SwitchBuilder`] is the only builder object kept in this module. It is a
339//! temporary case collector for [`Circuit::switch`]; it does not represent a
340//! general scoped circuit.
341//!
342//! ```rust,no_run
343//! use cqlib_core::circuit::{
344//! Circuit, CircuitError, ClassicalExpr, ClassicalType, Qubit,
345//! };
346//!
347//! fn build() -> Result<Circuit, CircuitError> {
348//! let mut circuit = Circuit::new(2);
349//! let state = circuit.var(ClassicalType::uint(2).unwrap());
350//! circuit.store(state, ClassicalExpr::uint_literal(2, 1)?)?;
351//!
352//! circuit.switch(state.expr(), |case| {
353//! case.value(0, |body| {
354//! body.x(Qubit::new(0))?;
355//! Ok(())
356//! })?;
357//! case.value(1, |body| {
358//! body.h(Qubit::new(1))?;
359//! Ok(())
360//! })?;
361//! case.value(2, |body| {
362//! body.h(Qubit::new(1))?;
363//! Ok(())
364//! })?;
365//! case.default(|body| {
366//! body.z(Qubit::new(0))?;
367//! Ok(())
368//! })?;
369//! Ok(())
370//! })?;
371//!
372//! Ok(circuit)
373//! }
374//! ```
375//!
376//! Expected circuit shape:
377//!
378//! ```text
379//! var s0: UInt(2)
380//! store s0 <- 1_u2
381//! switch s0 {
382//! case 0:
383//! x q[0]
384//! case 1:
385//! h q[1]
386//! case 2:
387//! h q[1]
388//! default:
389//! z q[0]
390//! }
391//! ```
392//!
393//! Each case stores exactly one integer label and one body. Sharing body
394//! construction across multiple labels should be done by the caller if needed,
395//! rather than by adding another builder abstraction here.
396//!
397//! # Internal validation
398//!
399//! `Circuit` keeps compact circuit-local type tables for classical variables
400//! and values. These tables are not user-facing program objects; they validate
401//! that expression handles belong to the circuit that consumes them and that
402//! their static types match. The `control_scope_stack` is also builder-only
403//! state: it validates that `break` and `continue` appear only in legal
404//! structured-control bodies.
405
406use crate::circuit::bit::Qubit;
407use crate::circuit::circuit_impl::Circuit;
408use crate::circuit::classical_expr::ClassicalExpr;
409use crate::circuit::control_flow::{
410 ClassicalControlOp, ControlBody, ForOp, IfOp, SwitchCase, SwitchOp, WhileOp,
411};
412use crate::circuit::error::CircuitError;
413use crate::circuit::gate::instruction::Instruction;
414use crate::circuit::{
415 CircuitParam, ClassicalDataOp, ClassicalType, ClassicalValue, ClassicalVar, Measurement,
416};
417use std::sync::Arc;
418
419#[derive(Debug, Clone, Copy, PartialEq, Eq)]
420pub(super) enum ControlScopeKind {
421 Loop,
422 Switch,
423}
424
425/// Scope kind used by foreign-language bindings while building callback bodies.
426#[doc(hidden)]
427#[derive(Debug, Clone, Copy, PartialEq, Eq)]
428pub enum ExternalControlScope {
429 Branch,
430 Loop,
431 Switch,
432}
433
434/// Temporary builder for the ordered cases of a structured `switch` operation.
435pub struct SwitchBuilder<'a> {
436 circuit: &'a mut Circuit,
437 cases: Vec<SwitchCase>,
438 default: Option<ControlBody>,
439}
440
441impl SwitchBuilder<'_> {
442 /// Adds one exact-value case.
443 pub fn value<F>(&mut self, value: u128, body: F) -> Result<(), CircuitError>
444 where
445 F: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
446 {
447 let body = self
448 .circuit
449 .build_control_body(Some(ControlScopeKind::Switch), body)?;
450 self.cases.push(SwitchCase::new(value, body));
451 Ok(())
452 }
453
454 /// Adds the optional default case.
455 pub fn default<F>(&mut self, body: F) -> Result<(), CircuitError>
456 where
457 F: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
458 {
459 if self.default.is_some() {
460 return Err(CircuitError::InvalidOperation(
461 "switch default case is already defined".to_string(),
462 ));
463 }
464 self.default = Some(
465 self.circuit
466 .build_control_body(Some(ControlScopeKind::Switch), body)?,
467 );
468 Ok(())
469 }
470}
471
472/// Restores appended builder state and the previous phase, not edits to existing entries.
473#[derive(Debug, Clone)]
474pub(super) struct CircuitCheckpoint {
475 data_len: usize,
476 qubit_len: usize,
477 parameter_len: usize,
478 symbol_len: usize,
479 classical_var_len: usize,
480 classical_value_len: usize,
481 control_scope_len: usize,
482 global_phase: CircuitParam,
483}
484
485/// Opaque rollback token for callback-driven control-flow construction.
486#[doc(hidden)]
487pub struct ControlBodyTransaction {
488 checkpoint: CircuitCheckpoint,
489}
490
491#[derive(Debug, Clone, Copy)]
492struct ControlValidationContext {
493 break_allowed: bool,
494 continue_allowed: bool,
495}
496
497impl ControlValidationContext {
498 fn from_scope_stack(scopes: &[ControlScopeKind]) -> Self {
499 Self {
500 break_allowed: scopes
501 .iter()
502 .rev()
503 .any(|scope| matches!(scope, ControlScopeKind::Loop | ControlScopeKind::Switch)),
504 continue_allowed: scopes
505 .iter()
506 .rev()
507 .any(|scope| matches!(scope, ControlScopeKind::Loop)),
508 }
509 }
510
511 fn enter_loop(self) -> Self {
512 Self {
513 break_allowed: true,
514 continue_allowed: true,
515 }
516 }
517
518 fn enter_switch(self) -> Self {
519 Self {
520 break_allowed: true,
521 continue_allowed: self.continue_allowed,
522 }
523 }
524}
525
526impl Circuit {
527 /// Allocates a mutable runtime classical variable.
528 pub fn var(&mut self, ty: ClassicalType) -> ClassicalVar {
529 self.allocate_classical_var(ty)
530 }
531
532 fn allocate_classical_var(&mut self, ty: ClassicalType) -> ClassicalVar {
533 let id = self.classical_vars.len() as u32;
534 self.classical_vars.push(ty);
535 ClassicalVar::new(self.circuit_id, id, ty)
536 }
537
538 fn allocate_classical_value(&mut self, ty: ClassicalType) -> ClassicalValue {
539 let id = self.classical_values.len() as u32;
540 self.classical_values.push(ty);
541 ClassicalValue::new(self.circuit_id, id, ty)
542 }
543
544 pub(super) fn checkpoint(&self) -> CircuitCheckpoint {
545 CircuitCheckpoint {
546 data_len: self.data.len(),
547 qubit_len: self.qubits.len(),
548 parameter_len: self.parameters.len(),
549 symbol_len: self.symbols.len(),
550 classical_var_len: self.classical_vars.len(),
551 classical_value_len: self.classical_values.len(),
552 control_scope_len: self.control_scope_stack.len(),
553 global_phase: self.global_phase.clone(),
554 }
555 }
556
557 pub(super) fn rollback_to(&mut self, checkpoint: CircuitCheckpoint) {
558 Arc::make_mut(&mut self.data).truncate(checkpoint.data_len);
559 self.qubits.truncate(checkpoint.qubit_len);
560 self.parameters.truncate(checkpoint.parameter_len);
561 self.global_phase = checkpoint.global_phase;
562 self.symbols.truncate(checkpoint.symbol_len);
563 self.classical_vars.truncate(checkpoint.classical_var_len);
564 self.classical_values
565 .truncate(checkpoint.classical_value_len);
566 self.control_scope_stack
567 .truncate(checkpoint.control_scope_len);
568 }
569
570 #[doc(hidden)]
571 pub fn validate_classical_var(&self, var: ClassicalVar) -> Result<(), CircuitError> {
572 if var.circuit_id() != self.circuit_id {
573 return Err(CircuitError::ForeignClassicalHandle {
574 kind: "classical variable",
575 index: var.index(),
576 });
577 }
578 match self.classical_vars.get(var.id() as usize) {
579 Some(ty) if *ty == var.ty() => Ok(()),
580 Some(ty) => Err(CircuitError::InvalidOperation(format!(
581 "classical variable {} has type {:?}, got {:?}",
582 var.id(),
583 ty,
584 var.ty()
585 ))),
586 None => Err(CircuitError::InvalidOperation(format!(
587 "classical variable {} is not allocated by this circuit",
588 var.id()
589 ))),
590 }
591 }
592
593 pub(super) fn validate_classical_value(
594 &self,
595 value: ClassicalValue,
596 ) -> Result<(), CircuitError> {
597 if value.circuit_id() != self.circuit_id {
598 return Err(CircuitError::ForeignClassicalHandle {
599 kind: "classical value",
600 index: value.index(),
601 });
602 }
603 match self.classical_values.get(value.index() as usize) {
604 Some(ty) if *ty == value.ty() => Ok(()),
605 Some(ty) => Err(CircuitError::InvalidOperation(format!(
606 "classical value {} has type {:?}, got {:?}",
607 value.index(),
608 ty,
609 value.ty()
610 ))),
611 None => Err(CircuitError::InvalidOperation(format!(
612 "classical value {} is not produced by this circuit",
613 value.index()
614 ))),
615 }
616 }
617
618 #[doc(hidden)]
619 pub fn validate_classical_expr(&self, expr: &ClassicalExpr) -> Result<(), CircuitError> {
620 for var in expr.vars() {
621 self.validate_classical_var(var)?;
622 }
623 for value in expr.values() {
624 self.validate_classical_value(value)?;
625 }
626 Ok(())
627 }
628
629 fn validate_control_body_in(
630 &self,
631 body: &ControlBody,
632 context: ControlValidationContext,
633 ) -> Result<(), CircuitError> {
634 for operation in body.operations() {
635 for qubit in &operation.qubits {
636 if !self.qubits.contains(qubit) {
637 return Err(CircuitError::QubitNotFound(qubit.id()));
638 }
639 }
640 if let Instruction::ClassicalControl(op) = &operation.instruction {
641 self.validate_control_op_in(op, context)?;
642 }
643 if let Instruction::ClassicalData(op) = &operation.instruction {
644 self.validate_classical_data_op(op, operation.qubits.len())?;
645 }
646 }
647 Ok(())
648 }
649
650 pub(super) fn validate_classical_data_op(
651 &self,
652 op: &ClassicalDataOp,
653 qubit_count: usize,
654 ) -> Result<(), CircuitError> {
655 match op {
656 ClassicalDataOp::Store { target, value } => {
657 self.validate_classical_var(*target)?;
658 if qubit_count != 0 {
659 return Err(CircuitError::QubitCountMismatch {
660 expected: 0,
661 actual: qubit_count,
662 });
663 }
664 self.validate_classical_expr(value)?;
665 if value.ty() != target.ty() {
666 return Err(CircuitError::InvalidOperation(format!(
667 "store target type {:?} does not match value type {:?}",
668 target.ty(),
669 value.ty()
670 )));
671 }
672 }
673 ClassicalDataOp::MeasureBit { result } => {
674 self.validate_classical_value(*result)?;
675 if qubit_count != 1 {
676 return Err(CircuitError::QubitCountMismatch {
677 expected: 1,
678 actual: qubit_count,
679 });
680 }
681 if result.ty() != ClassicalType::Bit {
682 return Err(CircuitError::InvalidOperation(format!(
683 "single-qubit measurement result must be Bit, got {:?}",
684 result.ty()
685 )));
686 }
687 }
688 ClassicalDataOp::MeasureBits { result } => {
689 self.validate_classical_value(*result)?;
690 let expected = match result.ty() {
691 ClassicalType::BitVec(width) => width.get() as usize,
692 ty => {
693 return Err(CircuitError::InvalidOperation(format!(
694 "multi-qubit measurement result must be BitVec, got {ty:?}"
695 )));
696 }
697 };
698 if qubit_count != expected {
699 return Err(CircuitError::QubitCountMismatch {
700 expected,
701 actual: qubit_count,
702 });
703 }
704 }
705 }
706 Ok(())
707 }
708
709 pub(super) fn validate_control_op(&self, op: &ClassicalControlOp) -> Result<(), CircuitError> {
710 self.validate_control_op_in(
711 op,
712 ControlValidationContext::from_scope_stack(&self.control_scope_stack),
713 )
714 }
715
716 fn validate_control_op_in(
717 &self,
718 op: &ClassicalControlOp,
719 context: ControlValidationContext,
720 ) -> Result<(), CircuitError> {
721 match op {
722 ClassicalControlOp::If(op) => {
723 self.validate_classical_expr(op.condition())?;
724 self.validate_control_body_in(op.then_body(), context)?;
725 if let Some(body) = op.else_body() {
726 self.validate_control_body_in(body, context)?;
727 }
728 }
729 ClassicalControlOp::While(op) => {
730 self.validate_classical_expr(op.condition())?;
731 self.validate_control_body_in(op.body(), context.enter_loop())?;
732 }
733 ClassicalControlOp::For(op) => {
734 self.validate_classical_var(op.var())?;
735 self.validate_classical_expr(op.start())?;
736 self.validate_classical_expr(op.stop())?;
737 self.validate_classical_expr(op.step())?;
738 self.validate_control_body_in(op.body(), context.enter_loop())?;
739 }
740 ClassicalControlOp::Switch(op) => {
741 self.validate_classical_expr(op.target())?;
742 let body_context = context.enter_switch();
743 for case in op.cases() {
744 self.validate_control_body_in(case.body(), body_context)?;
745 }
746 if let Some(body) = op.default() {
747 self.validate_control_body_in(body, body_context)?;
748 }
749 }
750 ClassicalControlOp::Break => {
751 if !context.break_allowed {
752 return Err(CircuitError::InvalidOperation(
753 "break can only be used inside a loop or switch body".to_string(),
754 ));
755 }
756 }
757 ClassicalControlOp::Continue => {
758 if !context.continue_allowed {
759 return Err(CircuitError::InvalidOperation(
760 "continue can only be used inside a loop body".to_string(),
761 ));
762 }
763 }
764 }
765 Ok(())
766 }
767
768 fn build_control_body<F>(
769 &mut self,
770 scope: Option<ControlScopeKind>,
771 body: F,
772 ) -> Result<ControlBody, CircuitError>
773 where
774 F: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
775 {
776 let checkpoint = self.checkpoint();
777
778 if let Some(scope) = scope {
779 self.control_scope_stack.push(scope);
780 }
781 let result = body(self);
782 self.control_scope_stack
783 .truncate(checkpoint.control_scope_len);
784
785 match result {
786 Ok(()) => Ok(ControlBody::new(
787 Arc::make_mut(&mut self.data).split_off(checkpoint.data_len),
788 )),
789 Err(error) => {
790 self.rollback_to(checkpoint);
791 Err(error)
792 }
793 }
794 }
795
796 /// Starts an externally driven control-flow construction transaction.
797 #[doc(hidden)]
798 pub fn begin_control_body_transaction(&self) -> ControlBodyTransaction {
799 ControlBodyTransaction {
800 checkpoint: self.checkpoint(),
801 }
802 }
803
804 /// Enters one callback body within an external construction transaction.
805 #[doc(hidden)]
806 pub fn enter_external_control_body(
807 &mut self,
808 transaction: &ControlBodyTransaction,
809 scope: ExternalControlScope,
810 ) {
811 self.control_scope_stack
812 .truncate(transaction.checkpoint.control_scope_len);
813 match scope {
814 ExternalControlScope::Branch => {}
815 ExternalControlScope::Loop => self.control_scope_stack.push(ControlScopeKind::Loop),
816 ExternalControlScope::Switch => self.control_scope_stack.push(ControlScopeKind::Switch),
817 }
818 }
819
820 /// Captures operations appended by the current callback body.
821 #[doc(hidden)]
822 pub fn finish_external_control_body(
823 &mut self,
824 transaction: &ControlBodyTransaction,
825 ) -> ControlBody {
826 self.control_scope_stack
827 .truncate(transaction.checkpoint.control_scope_len);
828 ControlBody::new(Arc::make_mut(&mut self.data).split_off(transaction.checkpoint.data_len))
829 }
830
831 /// Commits state allocated while constructing callback bodies.
832 #[doc(hidden)]
833 pub fn commit_control_body_transaction(&mut self, transaction: ControlBodyTransaction) {
834 self.control_scope_stack
835 .truncate(transaction.checkpoint.control_scope_len);
836 }
837
838 /// Rolls back all state allocated while constructing callback bodies.
839 #[doc(hidden)]
840 pub fn rollback_control_body_transaction(&mut self, transaction: ControlBodyTransaction) {
841 self.rollback_to(transaction.checkpoint);
842 }
843
844 /// Stores a runtime classical expression into a mutable classical variable.
845 pub fn store(
846 &mut self,
847 target: ClassicalVar,
848 value: ClassicalExpr,
849 ) -> Result<(), CircuitError> {
850 self.validate_classical_var(target)?;
851 self.validate_classical_expr(&value)?;
852 self.append(
853 Instruction::ClassicalData(ClassicalDataOp::Store { target, value }),
854 std::iter::empty::<Qubit>(),
855 std::iter::empty(),
856 None,
857 )
858 }
859
860 /// Measures one qubit and produces a self-contained measurement handle.
861 ///
862 /// The returned [`Measurement`] contains the immutable [`ClassicalValue`]
863 /// for this measurement and the measured qubit. The value may be read by
864 /// later classical expressions or structured control flow through
865 /// [`Measurement::expr`].
866 /// This method does not write mutable classical storage; use
867 /// [`Circuit::measure_into`] when the result must also be stored in a
868 /// [`ClassicalVar`].
869 ///
870 /// If appending the measurement fails, the allocated classical value is
871 /// rolled back and the circuit is left unchanged.
872 pub fn measure(&mut self, qubit: Qubit) -> Result<Measurement, CircuitError> {
873 let checkpoint = self.checkpoint();
874 let result = self.allocate_classical_value(ClassicalType::Bit);
875 if let Err(error) = self.append(
876 Instruction::ClassicalData(ClassicalDataOp::MeasureBit { result }),
877 [qubit],
878 std::iter::empty(),
879 None,
880 ) {
881 self.rollback_to(checkpoint);
882 return Err(error);
883 }
884 Ok(Measurement::new(result, smallvec::smallvec![qubit]))
885 }
886
887 /// Measures qubits and produces a self-contained `BitVec` measurement handle.
888 ///
889 /// The result width equals the number of qubits. Input order defines bit
890 /// order: the first qubit maps to bit index `0`, the least-significant bit.
891 /// The returned [`Measurement`] keeps that order and contains the immutable
892 /// [`ClassicalValue`] that may be read by later classical expressions or
893 /// structured control flow.
894 ///
895 /// # Errors
896 ///
897 /// Returns [`CircuitError::InvalidOperation`] when `qubits` is empty. Any
898 /// error while appending the operation rolls back the allocated classical
899 /// value and leaves the circuit unchanged.
900 pub fn measure_bits<I>(&mut self, qubits: I) -> Result<Measurement, CircuitError>
901 where
902 I: IntoIterator<Item = Qubit>,
903 {
904 let qubits: smallvec::SmallVec<[Qubit; 3]> = qubits.into_iter().collect();
905 let ty = ClassicalType::bit_vec(qubits.len() as u32).ok_or_else(|| {
906 CircuitError::InvalidOperation(
907 "multi-qubit measurement requires at least one qubit".to_string(),
908 )
909 })?;
910 let checkpoint = self.checkpoint();
911 let result = self.allocate_classical_value(ty);
912 if let Err(error) = self.append(
913 Instruction::ClassicalData(ClassicalDataOp::MeasureBits { result }),
914 qubits.iter().copied(),
915 std::iter::empty(),
916 None,
917 ) {
918 self.rollback_to(checkpoint);
919 return Err(error);
920 }
921 Ok(Measurement::new(result, qubits))
922 }
923
924 /// Measures one qubit and stores the result into `target`.
925 ///
926 /// This is a convenience operation equivalent to calling
927 /// [`Circuit::measure`] followed by [`Circuit::store`]. It returns the
928 /// self-contained measurement handle in addition to updating `target`, so
929 /// later expressions may read either the original value or the mutable
930 /// variable.
931 ///
932 /// `target` must be a [`ClassicalType::Bit`] variable owned by this circuit.
933 /// The measurement and store are appended atomically: if either operation
934 /// fails, both operations and the allocated measurement value are rolled
935 /// back.
936 pub fn measure_into(
937 &mut self,
938 qubit: Qubit,
939 target: ClassicalVar,
940 ) -> Result<Measurement, CircuitError> {
941 self.validate_classical_var(target)?;
942 let checkpoint = self.checkpoint();
943 let result = self.measure(qubit)?;
944 if let Err(error) = self.store(target, result.expr()) {
945 self.rollback_to(checkpoint);
946 return Err(error);
947 }
948 Ok(result)
949 }
950
951 /// Measures qubits and stores the resulting `BitVec` into `target`.
952 ///
953 /// This is a convenience operation equivalent to calling
954 /// [`Circuit::measure_bits`] followed by [`Circuit::store`]. The returned
955 /// [`Measurement`] contains the immutable measurement result; `target` is
956 /// the mutable copy written by the generated store operation.
957 ///
958 /// `target` must be a [`ClassicalType::BitVec`] variable owned by this
959 /// circuit, and its width must equal the number of measured qubits. Input
960 /// order defines bit order: the first qubit maps to bit index `0`, the
961 /// least-significant bit. The measurement and store are appended atomically
962 /// and are both rolled back if either operation fails.
963 pub fn measure_bits_into<I>(
964 &mut self,
965 qubits: I,
966 target: ClassicalVar,
967 ) -> Result<Measurement, CircuitError>
968 where
969 I: IntoIterator<Item = Qubit>,
970 {
971 self.validate_classical_var(target)?;
972 let qubits: smallvec::SmallVec<[Qubit; 3]> = qubits.into_iter().collect();
973
974 let checkpoint = self.checkpoint();
975 let result = self.measure_bits(qubits)?;
976 if let Err(error) = self.store(target, result.expr()) {
977 self.rollback_to(checkpoint);
978 return Err(error);
979 }
980 Ok(result)
981 }
982
983 /// Appends a structured `if` operation controlled by a boolean classical expression.
984 pub fn if_<F>(&mut self, condition: ClassicalExpr, then_body: F) -> Result<(), CircuitError>
985 where
986 F: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
987 {
988 self.validate_classical_expr(&condition)?;
989 let checkpoint = self.checkpoint();
990
991 let result = (|| {
992 let then_body = self.build_control_body(None, then_body)?;
993 let op = IfOp::new(condition, then_body, None)?;
994 self.append_control(ClassicalControlOp::If(op))
995 })();
996
997 if result.is_err() {
998 self.rollback_to(checkpoint);
999 }
1000 result
1001 }
1002
1003 /// Appends a structured `if`/`else` operation controlled by a boolean classical expression.
1004 pub fn if_else<T, E>(
1005 &mut self,
1006 condition: ClassicalExpr,
1007 then_body: T,
1008 else_body: E,
1009 ) -> Result<(), CircuitError>
1010 where
1011 T: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
1012 E: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
1013 {
1014 self.validate_classical_expr(&condition)?;
1015 let checkpoint = self.checkpoint();
1016
1017 let result = (|| {
1018 let then_body = self.build_control_body(None, then_body)?;
1019 let else_body = self.build_control_body(None, else_body)?;
1020 let op = IfOp::new(condition, then_body, Some(else_body))?;
1021 self.append_control(ClassicalControlOp::If(op))
1022 })();
1023
1024 if result.is_err() {
1025 self.rollback_to(checkpoint);
1026 }
1027 result
1028 }
1029
1030 /// Appends a structured `while` loop controlled by a boolean classical expression.
1031 pub fn while_<F>(&mut self, condition: ClassicalExpr, body: F) -> Result<(), CircuitError>
1032 where
1033 F: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
1034 {
1035 self.validate_classical_expr(&condition)?;
1036 let checkpoint = self.checkpoint();
1037
1038 let result = (|| {
1039 let body = self.build_control_body(Some(ControlScopeKind::Loop), body)?;
1040 let op = WhileOp::new(condition, body)?;
1041 self.append_control(ClassicalControlOp::While(op))
1042 })();
1043
1044 if result.is_err() {
1045 self.rollback_to(checkpoint);
1046 }
1047 result
1048 }
1049
1050 /// Appends an unsigned runtime range loop with half-open `[start, stop)` semantics.
1051 pub fn for_uint<F>(
1052 &mut self,
1053 var: ClassicalVar,
1054 start: ClassicalExpr,
1055 stop: ClassicalExpr,
1056 step: ClassicalExpr,
1057 body: F,
1058 ) -> Result<(), CircuitError>
1059 where
1060 F: FnOnce(&mut Circuit, ClassicalExpr) -> Result<(), CircuitError>,
1061 {
1062 self.validate_classical_var(var)?;
1063 self.validate_classical_expr(&start)?;
1064 self.validate_classical_expr(&stop)?;
1065 self.validate_classical_expr(&step)?;
1066 let checkpoint = self.checkpoint();
1067 let loop_expr = var.expr();
1068
1069 let result = (|| {
1070 let body = self
1071 .build_control_body(Some(ControlScopeKind::Loop), |scope| body(scope, loop_expr))?;
1072 let op = ForOp::new(var, start, stop, step, body)?;
1073 self.append_control(ClassicalControlOp::For(op))
1074 })();
1075
1076 if result.is_err() {
1077 self.rollback_to(checkpoint);
1078 }
1079 result
1080 }
1081
1082 /// Appends a structured exact-value `switch` operation over a `UInt` expression.
1083 pub fn switch<F>(&mut self, target: ClassicalExpr, build: F) -> Result<(), CircuitError>
1084 where
1085 F: FnOnce(&mut SwitchBuilder<'_>) -> Result<(), CircuitError>,
1086 {
1087 self.validate_classical_expr(&target)?;
1088 let checkpoint = self.checkpoint();
1089
1090 let result = (|| {
1091 let mut builder = SwitchBuilder {
1092 circuit: self,
1093 cases: Vec::new(),
1094 default: None,
1095 };
1096 build(&mut builder)?;
1097 let op = SwitchOp::new(target, builder.cases, builder.default)?;
1098 builder
1099 .circuit
1100 .append_control(ClassicalControlOp::Switch(op))
1101 })();
1102
1103 if result.is_err() {
1104 self.rollback_to(checkpoint);
1105 }
1106 result
1107 }
1108
1109 /// Appends a raw structured classical-control operation.
1110 pub fn append_control(&mut self, op: ClassicalControlOp) -> Result<(), CircuitError> {
1111 let qubits: Vec<Qubit> = op.used_qubits().into_iter().collect();
1112 self.append(
1113 Instruction::ClassicalControl(op),
1114 qubits,
1115 std::iter::empty(),
1116 None,
1117 )
1118 }
1119
1120 /// Appends a `break` to the nearest enclosing loop or switch body.
1121 pub fn break_loop(&mut self) -> Result<(), CircuitError> {
1122 self.append_control(ClassicalControlOp::Break)
1123 }
1124
1125 /// Appends a `continue` to the nearest enclosing loop body.
1126 pub fn continue_loop(&mut self) -> Result<(), CircuitError> {
1127 self.append_control(ClassicalControlOp::Continue)
1128 }
1129}