1use std::collections::HashMap;
30use std::sync::Arc;
31
32use crate::ibm::IBMQuantumClient;
33use crate::qasm3::{Qasm3Builder, Qasm3Circuit, Qasm3Statement};
34use crate::{DeviceError, DeviceResult};
35
36#[derive(Debug, Clone)]
38pub struct DynamicCapabilities {
39 pub supports_classical_feedback: bool,
41 pub max_classical_latency_us: u64,
43 pub supports_switch_case: bool,
45 pub max_dynamic_depth: usize,
47 pub supported_operations: Vec<ClassicalOperation>,
49 pub max_mid_circuit_measurements: usize,
51 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, 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum ClassicalOperation {
79 And,
81 Or,
83 Xor,
85 Not,
87 Add,
89 Sub,
91 Mul,
93 Div,
95 Mod,
97 Equal,
99 NotEqual,
101 LessThan,
103 GreaterThan,
105 LessEqual,
107 GreaterEqual,
109}
110
111#[derive(Debug, Clone)]
113pub struct DynamicExecutionConfig {
114 pub shots: usize,
116 pub validate_timing: bool,
118 pub max_execution_time: u64,
120 pub optimize_dynamic: bool,
122 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#[derive(Debug, Clone)]
140pub struct TimingValidation {
141 pub is_valid: bool,
143 pub total_depth: usize,
145 pub mid_circuit_measurements: usize,
147 pub estimated_classical_time_us: u64,
149 pub warnings: Vec<String>,
151 pub errors: Vec<String>,
153}
154
155#[derive(Debug, Clone)]
157pub struct DynamicExecutionResult {
158 pub counts: HashMap<String, usize>,
160 pub memory: Option<Vec<String>>,
162 pub metadata: DynamicExecutionMetadata,
164}
165
166#[derive(Debug, Clone)]
168pub struct DynamicExecutionMetadata {
169 pub job_id: String,
171 pub backend: String,
173 pub shots: usize,
175 pub execution_time: f64,
177 pub mid_circuit_measurements: usize,
179 pub classical_operations: usize,
181}
182
183#[cfg(feature = "ibm")]
185pub struct IBMDynamicExecutor {
186 client: Arc<IBMQuantumClient>,
188 backend: String,
190 config: DynamicExecutionConfig,
192 capabilities: Option<DynamicCapabilities>,
194}
195
196#[cfg(not(feature = "ibm"))]
197pub struct IBMDynamicExecutor {
198 backend: String,
200 config: DynamicExecutionConfig,
202}
203
204#[cfg(feature = "ibm")]
205impl IBMDynamicExecutor {
206 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 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 pub async fn get_capabilities(&mut self) -> DeviceResult<&DynamicCapabilities> {
232 if self.capabilities.is_none() {
233 self.capabilities = Some(DynamicCapabilities::default());
236 }
237 Ok(self
238 .capabilities
239 .as_ref()
240 .expect("capabilities should be set"))
241 }
242
243 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 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 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; 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 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 pub async fn submit_dynamic_circuit(
347 &self,
348 circuit: &Qasm3Circuit,
349 ) -> DeviceResult<DynamicExecutionResult> {
350 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 let qasm = circuit.to_string();
363
364 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 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, metadata: DynamicExecutionMetadata {
394 job_id,
395 backend: self.backend.clone(),
396 shots: self.config.shots,
397 execution_time: 0.0, mid_circuit_measurements,
399 classical_operations,
400 },
401 })
402 }
403
404 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, 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
469pub struct DynamicCircuitBuilder {
471 inner: Qasm3Builder,
472 mid_circuit_measurements: usize,
473}
474
475impl DynamicCircuitBuilder {
476 pub fn new(num_qubits: usize) -> Self {
478 Self {
479 inner: Qasm3Builder::new(num_qubits),
480 mid_circuit_measurements: 0,
481 }
482 }
483
484 pub fn with_bits(mut self, num_bits: usize) -> Self {
486 self.inner = self.inner.with_bits(num_bits);
487 self
488 }
489
490 pub fn h(&mut self, qubit: usize) -> DeviceResult<&mut Self> {
492 self.inner.gate("h", &[qubit])?;
493 Ok(self)
494 }
495
496 pub fn x(&mut self, qubit: usize) -> DeviceResult<&mut Self> {
498 self.inner.gate("x", &[qubit])?;
499 Ok(self)
500 }
501
502 pub fn y(&mut self, qubit: usize) -> DeviceResult<&mut Self> {
504 self.inner.gate("y", &[qubit])?;
505 Ok(self)
506 }
507
508 pub fn z(&mut self, qubit: usize) -> DeviceResult<&mut Self> {
510 self.inner.gate("z", &[qubit])?;
511 Ok(self)
512 }
513
514 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 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 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 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 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 pub fn measure_all(&mut self) -> DeviceResult<&mut Self> {
547 self.inner.measure_all()?;
548 Ok(self)
549 }
550
551 pub fn reset(&mut self, qubit: usize) -> DeviceResult<&mut Self> {
553 self.inner.reset(qubit)?;
554 Ok(self)
555 }
556
557 pub fn barrier(&mut self, qubits: &[usize]) -> DeviceResult<&mut Self> {
559 self.inner.barrier(qubits)?;
560 Ok(self)
561 }
562
563 pub fn if_then<F>(&mut self, condition: &str, body: F) -> DeviceResult<&mut Self>
573 where
574 F: FnOnce(&mut DynamicCircuitBuilder) -> DeviceResult<()>,
575 {
576 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 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 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 pub fn assign(&mut self, target: &str, value: &str) -> &mut Self {
677 self.inner.assign(target, value);
678 self
679 }
680
681 pub fn comment(&mut self, text: &str) -> &mut Self {
683 self.inner.comment(text);
684 self
685 }
686
687 pub fn num_mid_circuit_measurements(&self) -> usize {
689 self.mid_circuit_measurements
690 }
691
692 pub fn build(self) -> DeviceResult<Qasm3Circuit> {
694 self.inner.build()
695 }
696}
697
698pub 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 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 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}