Skip to main content

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}