quantrs2_device/
ibm_dynamic.rs

1//! IBM Dynamic Circuits Executor
2//!
3//! This module provides execution capabilities for dynamic circuits on IBM backends:
4//! - Mid-circuit measurement with classical feedback
5//! - Switch-case statements based on measurement results
6//! - Classical arithmetic and logical operations
7//! - Timing validation and capability checking
8//!
9//! ## Example
10//!
11//! ```rust,ignore
12//! use quantrs2_device::ibm_dynamic::{IBMDynamicExecutor, DynamicCircuitBuilder};
13//!
14//! // Build a dynamic circuit with mid-circuit measurement
15//! let mut builder = DynamicCircuitBuilder::new(2);
16//! builder.h(0);
17//! builder.measure(0, 0);
18//! builder.if_then("c[0] == 1", |b| {
19//!     b.x(1);
20//!     Ok(())
21//! })?;
22//! builder.measure(1, 1);
23//!
24//! // Execute on IBM backend
25//! let executor = IBMDynamicExecutor::new(client, "ibm_brisbane")?;
26//! let result = executor.submit_dynamic_circuit(&builder.build()?).await?;
27//! ```
28
29use std::collections::HashMap;
30use std::sync::Arc;
31
32use crate::ibm::IBMQuantumClient;
33use crate::qasm3::{Qasm3Builder, Qasm3Circuit, Qasm3Statement};
34use crate::{DeviceError, DeviceResult};
35
36/// Dynamic circuit capabilities of an IBM backend
37#[derive(Debug, Clone)]
38pub struct DynamicCapabilities {
39    /// Backend supports classical feedback
40    pub supports_classical_feedback: bool,
41    /// Maximum classical computation latency in microseconds
42    pub max_classical_latency_us: u64,
43    /// Backend supports switch-case statements
44    pub supports_switch_case: bool,
45    /// Maximum depth for dynamic circuits
46    pub max_dynamic_depth: usize,
47    /// Supported classical operations
48    pub supported_operations: Vec<ClassicalOperation>,
49    /// Maximum number of mid-circuit measurements
50    pub max_mid_circuit_measurements: usize,
51    /// Supports real-time classical computation
52    pub supports_realtime_classical: bool,
53}
54
55impl Default for DynamicCapabilities {
56    fn default() -> Self {
57        Self {
58            supports_classical_feedback: true,
59            max_classical_latency_us: 1000, // 1ms
60            supports_switch_case: true,
61            max_dynamic_depth: 100,
62            supported_operations: vec![
63                ClassicalOperation::And,
64                ClassicalOperation::Or,
65                ClassicalOperation::Xor,
66                ClassicalOperation::Not,
67                ClassicalOperation::Equal,
68                ClassicalOperation::NotEqual,
69            ],
70            max_mid_circuit_measurements: 50,
71            supports_realtime_classical: false,
72        }
73    }
74}
75
76/// Supported classical operations
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum ClassicalOperation {
79    /// Bitwise AND
80    And,
81    /// Bitwise OR
82    Or,
83    /// Bitwise XOR
84    Xor,
85    /// Bitwise NOT
86    Not,
87    /// Addition
88    Add,
89    /// Subtraction
90    Sub,
91    /// Multiplication
92    Mul,
93    /// Division
94    Div,
95    /// Modulo
96    Mod,
97    /// Equality comparison
98    Equal,
99    /// Inequality comparison
100    NotEqual,
101    /// Less than
102    LessThan,
103    /// Greater than
104    GreaterThan,
105    /// Less than or equal
106    LessEqual,
107    /// Greater than or equal
108    GreaterEqual,
109}
110
111/// Configuration for dynamic circuit execution
112#[derive(Debug, Clone)]
113pub struct DynamicExecutionConfig {
114    /// Number of shots
115    pub shots: usize,
116    /// Enable timing validation
117    pub validate_timing: bool,
118    /// Maximum execution time in seconds
119    pub max_execution_time: u64,
120    /// Enable optimization for dynamic circuits
121    pub optimize_dynamic: bool,
122    /// Classical computation timeout in microseconds
123    pub classical_timeout_us: u64,
124}
125
126impl Default for DynamicExecutionConfig {
127    fn default() -> Self {
128        Self {
129            shots: 4096,
130            validate_timing: true,
131            max_execution_time: 300,
132            optimize_dynamic: true,
133            classical_timeout_us: 1000,
134        }
135    }
136}
137
138/// Timing validation result
139#[derive(Debug, Clone)]
140pub struct TimingValidation {
141    /// Whether timing constraints are satisfied
142    pub is_valid: bool,
143    /// Total circuit depth
144    pub total_depth: usize,
145    /// Number of mid-circuit measurements
146    pub mid_circuit_measurements: usize,
147    /// Estimated classical processing time in microseconds
148    pub estimated_classical_time_us: u64,
149    /// Warnings about timing
150    pub warnings: Vec<String>,
151    /// Errors that would prevent execution
152    pub errors: Vec<String>,
153}
154
155/// Result from dynamic circuit execution
156#[derive(Debug, Clone)]
157pub struct DynamicExecutionResult {
158    /// Measurement counts
159    pub counts: HashMap<String, usize>,
160    /// Per-shot measurement results (if available)
161    pub memory: Option<Vec<String>>,
162    /// Execution metadata
163    pub metadata: DynamicExecutionMetadata,
164}
165
166/// Metadata from dynamic circuit execution
167#[derive(Debug, Clone)]
168pub struct DynamicExecutionMetadata {
169    /// Job ID
170    pub job_id: String,
171    /// Backend used
172    pub backend: String,
173    /// Number of shots executed
174    pub shots: usize,
175    /// Execution time in seconds
176    pub execution_time: f64,
177    /// Number of mid-circuit measurements
178    pub mid_circuit_measurements: usize,
179    /// Number of classical operations
180    pub classical_operations: usize,
181}
182
183/// IBM Dynamic Circuit Executor
184#[cfg(feature = "ibm")]
185pub struct IBMDynamicExecutor {
186    /// IBM Quantum client
187    client: Arc<IBMQuantumClient>,
188    /// Backend name
189    backend: String,
190    /// Execution configuration
191    config: DynamicExecutionConfig,
192    /// Cached capabilities
193    capabilities: Option<DynamicCapabilities>,
194}
195
196#[cfg(not(feature = "ibm"))]
197pub struct IBMDynamicExecutor {
198    /// Backend name
199    backend: String,
200    /// Execution configuration
201    config: DynamicExecutionConfig,
202}
203
204#[cfg(feature = "ibm")]
205impl IBMDynamicExecutor {
206    /// Create a new dynamic circuit executor
207    pub fn new(client: IBMQuantumClient, backend: &str) -> DeviceResult<Self> {
208        Ok(Self {
209            client: Arc::new(client),
210            backend: backend.to_string(),
211            config: DynamicExecutionConfig::default(),
212            capabilities: None,
213        })
214    }
215
216    /// Create with custom configuration
217    pub fn with_config(
218        client: IBMQuantumClient,
219        backend: &str,
220        config: DynamicExecutionConfig,
221    ) -> DeviceResult<Self> {
222        Ok(Self {
223            client: Arc::new(client),
224            backend: backend.to_string(),
225            config,
226            capabilities: None,
227        })
228    }
229
230    /// Get dynamic circuit capabilities for the backend
231    pub async fn get_capabilities(&mut self) -> DeviceResult<&DynamicCapabilities> {
232        if self.capabilities.is_none() {
233            // In a real implementation, this would query the backend
234            // For now, return default capabilities
235            self.capabilities = Some(DynamicCapabilities::default());
236        }
237        Ok(self
238            .capabilities
239            .as_ref()
240            .expect("capabilities should be set"))
241    }
242
243    /// Validate timing constraints for a dynamic circuit
244    pub fn validate_timing(&self, circuit: &Qasm3Circuit) -> TimingValidation {
245        let mut mid_circuit_measurements = 0;
246        let mut classical_operations = 0;
247        let mut warnings = Vec::new();
248        let mut errors = Vec::new();
249
250        // Count dynamic operations
251        for stmt in &circuit.statements {
252            self.count_dynamic_ops(
253                stmt,
254                &mut mid_circuit_measurements,
255                &mut classical_operations,
256            );
257        }
258
259        // Check against capabilities
260        let default_caps = DynamicCapabilities::default();
261        let capabilities = self.capabilities.as_ref().unwrap_or(&default_caps);
262
263        if mid_circuit_measurements > capabilities.max_mid_circuit_measurements {
264            errors.push(format!(
265                "Too many mid-circuit measurements: {} > {}",
266                mid_circuit_measurements, capabilities.max_mid_circuit_measurements
267            ));
268        }
269
270        let estimated_classical_time_us = (classical_operations as u64) * 100; // ~100us per operation
271
272        if estimated_classical_time_us > capabilities.max_classical_latency_us {
273            warnings.push(format!(
274                "Estimated classical processing time ({} us) exceeds recommended limit ({} us)",
275                estimated_classical_time_us, capabilities.max_classical_latency_us
276            ));
277        }
278
279        TimingValidation {
280            is_valid: errors.is_empty(),
281            total_depth: circuit.statements.len(),
282            mid_circuit_measurements,
283            estimated_classical_time_us,
284            warnings,
285            errors,
286        }
287    }
288
289    /// Count dynamic operations in a statement
290    fn count_dynamic_ops(
291        &self,
292        stmt: &Qasm3Statement,
293        measurements: &mut usize,
294        classical_ops: &mut usize,
295    ) {
296        match stmt {
297            Qasm3Statement::Measure { .. } => {
298                *measurements += 1;
299            }
300            Qasm3Statement::If {
301                then_body,
302                else_body,
303                ..
304            } => {
305                *classical_ops += 1;
306                for s in then_body {
307                    self.count_dynamic_ops(s, measurements, classical_ops);
308                }
309                if let Some(else_stmts) = else_body {
310                    for s in else_stmts {
311                        self.count_dynamic_ops(s, measurements, classical_ops);
312                    }
313                }
314            }
315            Qasm3Statement::Switch {
316                cases,
317                default_case,
318                ..
319            } => {
320                *classical_ops += 1;
321                for (_, body) in cases {
322                    for s in body {
323                        self.count_dynamic_ops(s, measurements, classical_ops);
324                    }
325                }
326                if let Some(default_body) = default_case {
327                    for s in default_body {
328                        self.count_dynamic_ops(s, measurements, classical_ops);
329                    }
330                }
331            }
332            Qasm3Statement::Assignment { .. } => {
333                *classical_ops += 1;
334            }
335            Qasm3Statement::While { body, .. } | Qasm3Statement::For { body, .. } => {
336                *classical_ops += 1;
337                for s in body {
338                    self.count_dynamic_ops(s, measurements, classical_ops);
339                }
340            }
341            _ => {}
342        }
343    }
344
345    /// Submit a dynamic circuit for execution
346    pub async fn submit_dynamic_circuit(
347        &self,
348        circuit: &Qasm3Circuit,
349    ) -> DeviceResult<DynamicExecutionResult> {
350        // Validate timing if enabled
351        if self.config.validate_timing {
352            let validation = self.validate_timing(circuit);
353            if !validation.is_valid {
354                return Err(DeviceError::InvalidInput(format!(
355                    "Dynamic circuit validation failed: {:?}",
356                    validation.errors
357                )));
358            }
359        }
360
361        // Convert circuit to QASM string
362        let qasm = circuit.to_string();
363
364        // Submit to IBM Runtime
365        let config = crate::ibm::IBMCircuitConfig {
366            name: "dynamic_circuit".to_string(),
367            qasm,
368            shots: self.config.shots,
369            optimization_level: Some(1),
370            initial_layout: None,
371        };
372
373        let job_id = self.client.submit_circuit(&self.backend, config).await?;
374        let result = self
375            .client
376            .wait_for_job(&job_id, Some(self.config.max_execution_time))
377            .await?;
378
379        // Count operations for metadata
380        let mut mid_circuit_measurements = 0;
381        let mut classical_operations = 0;
382        for stmt in &circuit.statements {
383            self.count_dynamic_ops(
384                stmt,
385                &mut mid_circuit_measurements,
386                &mut classical_operations,
387            );
388        }
389
390        Ok(DynamicExecutionResult {
391            counts: result.counts,
392            memory: None, // Would be populated from actual result
393            metadata: DynamicExecutionMetadata {
394                job_id,
395                backend: self.backend.clone(),
396                shots: self.config.shots,
397                execution_time: 0.0, // Would be from actual result
398                mid_circuit_measurements,
399                classical_operations,
400            },
401        })
402    }
403
404    /// Execute a pre-built QASM 3.0 string
405    pub async fn execute_qasm(
406        &self,
407        qasm: &str,
408        shots: usize,
409    ) -> DeviceResult<DynamicExecutionResult> {
410        let config = crate::ibm::IBMCircuitConfig {
411            name: "dynamic_qasm".to_string(),
412            qasm: qasm.to_string(),
413            shots,
414            optimization_level: Some(1),
415            initial_layout: None,
416        };
417
418        let job_id = self.client.submit_circuit(&self.backend, config).await?;
419        let result = self
420            .client
421            .wait_for_job(&job_id, Some(self.config.max_execution_time))
422            .await?;
423
424        Ok(DynamicExecutionResult {
425            counts: result.counts,
426            memory: None,
427            metadata: DynamicExecutionMetadata {
428                job_id,
429                backend: self.backend.clone(),
430                shots,
431                execution_time: 0.0,
432                mid_circuit_measurements: 0, // Unknown without parsing
433                classical_operations: 0,
434            },
435        })
436    }
437}
438
439#[cfg(not(feature = "ibm"))]
440impl IBMDynamicExecutor {
441    pub fn new(_client: IBMQuantumClient, backend: &str) -> DeviceResult<Self> {
442        Ok(Self {
443            backend: backend.to_string(),
444            config: DynamicExecutionConfig::default(),
445        })
446    }
447
448    pub async fn submit_dynamic_circuit(
449        &self,
450        _circuit: &Qasm3Circuit,
451    ) -> DeviceResult<DynamicExecutionResult> {
452        Err(DeviceError::UnsupportedDevice(
453            "IBM Runtime support not enabled".to_string(),
454        ))
455    }
456
457    pub fn validate_timing(&self, _circuit: &Qasm3Circuit) -> TimingValidation {
458        TimingValidation {
459            is_valid: false,
460            total_depth: 0,
461            mid_circuit_measurements: 0,
462            estimated_classical_time_us: 0,
463            warnings: vec![],
464            errors: vec!["IBM support not enabled".to_string()],
465        }
466    }
467}
468
469/// Builder for dynamic circuits with mid-circuit measurement
470pub struct DynamicCircuitBuilder {
471    inner: Qasm3Builder,
472    mid_circuit_measurements: usize,
473}
474
475impl DynamicCircuitBuilder {
476    /// Create a new dynamic circuit builder
477    pub fn new(num_qubits: usize) -> Self {
478        Self {
479            inner: Qasm3Builder::new(num_qubits),
480            mid_circuit_measurements: 0,
481        }
482    }
483
484    /// Create with specified number of classical bits
485    pub fn with_bits(mut self, num_bits: usize) -> Self {
486        self.inner = self.inner.with_bits(num_bits);
487        self
488    }
489
490    /// Add a Hadamard gate
491    pub fn h(&mut self, qubit: usize) -> DeviceResult<&mut Self> {
492        self.inner.gate("h", &[qubit])?;
493        Ok(self)
494    }
495
496    /// Add a Pauli-X gate
497    pub fn x(&mut self, qubit: usize) -> DeviceResult<&mut Self> {
498        self.inner.gate("x", &[qubit])?;
499        Ok(self)
500    }
501
502    /// Add a Pauli-Y gate
503    pub fn y(&mut self, qubit: usize) -> DeviceResult<&mut Self> {
504        self.inner.gate("y", &[qubit])?;
505        Ok(self)
506    }
507
508    /// Add a Pauli-Z gate
509    pub fn z(&mut self, qubit: usize) -> DeviceResult<&mut Self> {
510        self.inner.gate("z", &[qubit])?;
511        Ok(self)
512    }
513
514    /// Add a CNOT gate
515    pub fn cx(&mut self, control: usize, target: usize) -> DeviceResult<&mut Self> {
516        self.inner.ctrl_gate("x", control, target, &[])?;
517        Ok(self)
518    }
519
520    /// Add a rotation around X axis
521    pub fn rx(&mut self, qubit: usize, angle: f64) -> DeviceResult<&mut Self> {
522        self.inner.gate_with_params("rx", &[angle], &[qubit])?;
523        Ok(self)
524    }
525
526    /// Add a rotation around Y axis
527    pub fn ry(&mut self, qubit: usize, angle: f64) -> DeviceResult<&mut Self> {
528        self.inner.gate_with_params("ry", &[angle], &[qubit])?;
529        Ok(self)
530    }
531
532    /// Add a rotation around Z axis
533    pub fn rz(&mut self, qubit: usize, angle: f64) -> DeviceResult<&mut Self> {
534        self.inner.gate_with_params("rz", &[angle], &[qubit])?;
535        Ok(self)
536    }
537
538    /// Add a measurement (mid-circuit or final)
539    pub fn measure(&mut self, qubit: usize, bit: usize) -> DeviceResult<&mut Self> {
540        self.inner.measure(qubit, bit)?;
541        self.mid_circuit_measurements += 1;
542        Ok(self)
543    }
544
545    /// Add measurements for all qubits
546    pub fn measure_all(&mut self) -> DeviceResult<&mut Self> {
547        self.inner.measure_all()?;
548        Ok(self)
549    }
550
551    /// Add a reset operation
552    pub fn reset(&mut self, qubit: usize) -> DeviceResult<&mut Self> {
553        self.inner.reset(qubit)?;
554        Ok(self)
555    }
556
557    /// Add a barrier
558    pub fn barrier(&mut self, qubits: &[usize]) -> DeviceResult<&mut Self> {
559        self.inner.barrier(qubits)?;
560        Ok(self)
561    }
562
563    /// Add a conditional operation based on classical bit value
564    ///
565    /// # Example
566    /// ```ignore
567    /// builder.if_then("c[0] == 1", |b| {
568    ///     b.x(1);
569    ///     Ok(())
570    /// })?;
571    /// ```
572    pub fn if_then<F>(&mut self, condition: &str, body: F) -> DeviceResult<&mut Self>
573    where
574        F: FnOnce(&mut DynamicCircuitBuilder) -> DeviceResult<()>,
575    {
576        // Create a temporary builder for the body
577        let mut temp_builder = DynamicCircuitBuilder {
578            inner: Qasm3Builder {
579                circuit: crate::qasm3::Qasm3Circuit::new(),
580                num_qubits: self.inner.num_qubits,
581                num_bits: self.inner.num_bits,
582            },
583            mid_circuit_measurements: 0,
584        };
585        temp_builder.inner.circuit.statements.clear();
586
587        body(&mut temp_builder)?;
588
589        self.inner.circuit.add_statement(Qasm3Statement::If {
590            condition: condition.to_string(),
591            then_body: temp_builder.inner.circuit.statements,
592            else_body: None,
593        });
594
595        self.mid_circuit_measurements += temp_builder.mid_circuit_measurements;
596
597        Ok(self)
598    }
599
600    /// Add a conditional operation with else branch
601    pub fn if_then_else<F, G>(
602        &mut self,
603        condition: &str,
604        then_body: F,
605        else_body: G,
606    ) -> DeviceResult<&mut Self>
607    where
608        F: FnOnce(&mut DynamicCircuitBuilder) -> DeviceResult<()>,
609        G: FnOnce(&mut DynamicCircuitBuilder) -> DeviceResult<()>,
610    {
611        let mut then_builder = DynamicCircuitBuilder {
612            inner: Qasm3Builder {
613                circuit: crate::qasm3::Qasm3Circuit::new(),
614                num_qubits: self.inner.num_qubits,
615                num_bits: self.inner.num_bits,
616            },
617            mid_circuit_measurements: 0,
618        };
619        then_builder.inner.circuit.statements.clear();
620        then_body(&mut then_builder)?;
621
622        let mut else_builder = DynamicCircuitBuilder {
623            inner: Qasm3Builder {
624                circuit: crate::qasm3::Qasm3Circuit::new(),
625                num_qubits: self.inner.num_qubits,
626                num_bits: self.inner.num_bits,
627            },
628            mid_circuit_measurements: 0,
629        };
630        else_builder.inner.circuit.statements.clear();
631        else_body(&mut else_builder)?;
632
633        self.inner.circuit.add_statement(Qasm3Statement::If {
634            condition: condition.to_string(),
635            then_body: then_builder.inner.circuit.statements,
636            else_body: Some(else_builder.inner.circuit.statements),
637        });
638
639        self.mid_circuit_measurements += then_builder.mid_circuit_measurements;
640        self.mid_circuit_measurements += else_builder.mid_circuit_measurements;
641
642        Ok(self)
643    }
644
645    /// Add a switch-case statement
646    ///
647    /// # Example
648    /// ```ignore
649    /// builder.switch("c", |sw| {
650    ///     sw.case(&[0], |b| { b.x(0)?; Ok(()) })?;
651    ///     sw.case(&[1], |b| { b.y(0)?; Ok(()) })?;
652    ///     sw.default(|b| { b.z(0)?; Ok(()) })?;
653    ///     Ok(())
654    /// })?;
655    /// ```
656    pub fn switch<F>(&mut self, expression: &str, case_builder: F) -> DeviceResult<&mut Self>
657    where
658        F: FnOnce(&mut DynamicSwitchBuilder) -> DeviceResult<()>,
659    {
660        let mut switch_builder =
661            DynamicSwitchBuilder::new(self.inner.num_qubits, self.inner.num_bits);
662        case_builder(&mut switch_builder)?;
663
664        self.inner.circuit.add_statement(Qasm3Statement::Switch {
665            expression: expression.to_string(),
666            cases: switch_builder.cases,
667            default_case: switch_builder.default_case,
668        });
669
670        self.mid_circuit_measurements += switch_builder.mid_circuit_measurements;
671
672        Ok(self)
673    }
674
675    /// Add a classical assignment
676    pub fn assign(&mut self, target: &str, value: &str) -> &mut Self {
677        self.inner.assign(target, value);
678        self
679    }
680
681    /// Add a comment
682    pub fn comment(&mut self, text: &str) -> &mut Self {
683        self.inner.comment(text);
684        self
685    }
686
687    /// Get number of mid-circuit measurements
688    pub fn num_mid_circuit_measurements(&self) -> usize {
689        self.mid_circuit_measurements
690    }
691
692    /// Build the dynamic circuit
693    pub fn build(self) -> DeviceResult<Qasm3Circuit> {
694        self.inner.build()
695    }
696}
697
698/// Builder for switch-case statements in dynamic circuits
699pub struct DynamicSwitchBuilder {
700    num_qubits: usize,
701    num_bits: usize,
702    cases: Vec<(Vec<i64>, Vec<Qasm3Statement>)>,
703    default_case: Option<Vec<Qasm3Statement>>,
704    mid_circuit_measurements: usize,
705}
706
707impl DynamicSwitchBuilder {
708    fn new(num_qubits: usize, num_bits: usize) -> Self {
709        Self {
710            num_qubits,
711            num_bits,
712            cases: Vec::new(),
713            default_case: None,
714            mid_circuit_measurements: 0,
715        }
716    }
717
718    /// Add a case to the switch statement
719    pub fn case<F>(&mut self, values: &[i64], body: F) -> DeviceResult<&mut Self>
720    where
721        F: FnOnce(&mut DynamicCircuitBuilder) -> DeviceResult<()>,
722    {
723        let mut case_builder = DynamicCircuitBuilder {
724            inner: Qasm3Builder {
725                circuit: crate::qasm3::Qasm3Circuit::new(),
726                num_qubits: self.num_qubits,
727                num_bits: self.num_bits,
728            },
729            mid_circuit_measurements: 0,
730        };
731        case_builder.inner.circuit.statements.clear();
732
733        body(&mut case_builder)?;
734
735        self.cases
736            .push((values.to_vec(), case_builder.inner.circuit.statements));
737        self.mid_circuit_measurements += case_builder.mid_circuit_measurements;
738
739        Ok(self)
740    }
741
742    /// Add a default case
743    pub fn default<F>(&mut self, body: F) -> DeviceResult<&mut Self>
744    where
745        F: FnOnce(&mut DynamicCircuitBuilder) -> DeviceResult<()>,
746    {
747        let mut default_builder = DynamicCircuitBuilder {
748            inner: Qasm3Builder {
749                circuit: crate::qasm3::Qasm3Circuit::new(),
750                num_qubits: self.num_qubits,
751                num_bits: self.num_bits,
752            },
753            mid_circuit_measurements: 0,
754        };
755        default_builder.inner.circuit.statements.clear();
756
757        body(&mut default_builder)?;
758
759        self.default_case = Some(default_builder.inner.circuit.statements);
760        self.mid_circuit_measurements += default_builder.mid_circuit_measurements;
761
762        Ok(self)
763    }
764}
765
766#[cfg(test)]
767mod tests {
768    use super::*;
769
770    #[test]
771    fn test_dynamic_capabilities_default() {
772        let caps = DynamicCapabilities::default();
773        assert!(caps.supports_classical_feedback);
774        assert!(caps.supports_switch_case);
775        assert_eq!(caps.max_mid_circuit_measurements, 50);
776    }
777
778    #[test]
779    fn test_dynamic_execution_config_default() {
780        let config = DynamicExecutionConfig::default();
781        assert_eq!(config.shots, 4096);
782        assert!(config.validate_timing);
783    }
784
785    #[test]
786    fn test_dynamic_circuit_builder() {
787        let mut builder = DynamicCircuitBuilder::new(2);
788        builder.h(0).unwrap();
789        builder.cx(0, 1).unwrap();
790        builder.measure(0, 0).unwrap();
791        builder.measure(1, 1).unwrap();
792
793        let circuit = builder.build().unwrap();
794        let qasm = circuit.to_string();
795
796        assert!(qasm.contains("h q[0]"));
797        assert!(qasm.contains("ctrl @"));
798    }
799
800    #[test]
801    fn test_dynamic_circuit_with_if() {
802        let mut builder = DynamicCircuitBuilder::new(2);
803        builder.h(0).unwrap();
804        builder.measure(0, 0).unwrap();
805        builder
806            .if_then("c[0] == 1", |b| {
807                b.x(1)?;
808                Ok(())
809            })
810            .unwrap();
811
812        let circuit = builder.build().unwrap();
813        let qasm = circuit.to_string();
814
815        assert!(qasm.contains("if (c[0] == 1)"));
816        assert!(qasm.contains("x q[1]"));
817    }
818
819    #[test]
820    fn test_dynamic_circuit_with_switch() {
821        let mut builder = DynamicCircuitBuilder::new(2);
822        builder.h(0).unwrap();
823        builder.measure(0, 0).unwrap();
824        builder
825            .switch("c[0]", |sw| {
826                sw.case(&[0], |b| {
827                    b.x(1)?;
828                    Ok(())
829                })?;
830                sw.case(&[1], |b| {
831                    b.y(1)?;
832                    Ok(())
833                })?;
834                Ok(())
835            })
836            .unwrap();
837
838        let circuit = builder.build().unwrap();
839        let qasm = circuit.to_string();
840
841        assert!(qasm.contains("switch (c[0])"));
842        assert!(qasm.contains("case 0"));
843        assert!(qasm.contains("case 1"));
844    }
845
846    #[test]
847    fn test_classical_operations() {
848        assert_eq!(ClassicalOperation::And, ClassicalOperation::And);
849        assert_ne!(ClassicalOperation::And, ClassicalOperation::Or);
850    }
851
852    #[test]
853    fn test_timing_validation_structure() {
854        let validation = TimingValidation {
855            is_valid: true,
856            total_depth: 10,
857            mid_circuit_measurements: 2,
858            estimated_classical_time_us: 200,
859            warnings: vec![],
860            errors: vec![],
861        };
862
863        assert!(validation.is_valid);
864        assert_eq!(validation.mid_circuit_measurements, 2);
865    }
866}