1use scirs2_core::ndarray::{Array1, Array2};
9use scirs2_core::Complex64;
10use serde::{Deserialize, Serialize};
11use std::collections::hash_map::DefaultHasher;
12use std::collections::HashMap;
13use std::hash::{Hash, Hasher};
14use std::sync::{Arc, Mutex};
15
16use crate::error::{Result, SimulatorError};
17use crate::scirs2_integration::SciRS2Backend;
18use crate::sparse::CSRMatrix;
19use crate::statevector::StateVectorSimulator;
20#[cfg(feature = "advanced_math")]
21#[allow(unused_imports)]
22use crate::tensor_network::TensorNetwork;
23
24#[derive(Debug, Clone)]
26pub struct CircuitInterfaceConfig {
27 pub auto_backend_selection: bool,
29 pub enable_optimization: bool,
31 pub max_statevector_qubits: usize,
33 pub max_mps_bond_dim: usize,
35 pub parallel_compilation: bool,
37 pub enable_circuit_cache: bool,
39 pub max_cache_size: usize,
41 pub enable_profiling: bool,
43}
44
45impl Default for CircuitInterfaceConfig {
46 fn default() -> Self {
47 Self {
48 auto_backend_selection: true,
49 enable_optimization: true,
50 max_statevector_qubits: 25,
51 max_mps_bond_dim: 1024,
52 parallel_compilation: true,
53 enable_circuit_cache: true,
54 max_cache_size: 10_000,
55 enable_profiling: true,
56 }
57 }
58}
59
60#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
62pub enum InterfaceGateType {
63 Identity,
65 PauliX,
66 X, PauliY,
68 PauliZ,
69 Hadamard,
70 H, S,
72 T,
73 Phase(f64),
74 RX(f64),
75 RY(f64),
76 RZ(f64),
77 U1(f64),
78 U2(f64, f64),
79 U3(f64, f64, f64),
80 CNOT,
82 CZ,
83 CY,
84 SWAP,
85 ISwap,
86 CRX(f64),
87 CRY(f64),
88 CRZ(f64),
89 CPhase(f64),
90 Toffoli,
92 Fredkin,
93 MultiControlledX(usize), MultiControlledZ(usize),
96 Custom(String, Array2<Complex64>),
98 Measure,
100 Reset,
101}
102
103impl std::hash::Hash for InterfaceGateType {
105 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
106 use std::mem;
107 match self {
108 Self::Identity => 0u8.hash(state),
109 Self::PauliX => 1u8.hash(state),
110 Self::X => 2u8.hash(state),
111 Self::PauliY => 3u8.hash(state),
112 Self::PauliZ => 4u8.hash(state),
113 Self::Hadamard => 5u8.hash(state),
114 Self::H => 6u8.hash(state),
115 Self::S => 7u8.hash(state),
116 Self::T => 8u8.hash(state),
117 Self::Phase(angle) => {
118 9u8.hash(state);
119 angle.to_bits().hash(state);
120 }
121 Self::RX(angle) => {
122 10u8.hash(state);
123 angle.to_bits().hash(state);
124 }
125 Self::RY(angle) => {
126 11u8.hash(state);
127 angle.to_bits().hash(state);
128 }
129 Self::RZ(angle) => {
130 12u8.hash(state);
131 angle.to_bits().hash(state);
132 }
133 Self::U1(angle) => {
134 13u8.hash(state);
135 angle.to_bits().hash(state);
136 }
137 Self::U2(theta, phi) => {
138 14u8.hash(state);
139 theta.to_bits().hash(state);
140 phi.to_bits().hash(state);
141 }
142 Self::U3(theta, phi, lambda) => {
143 15u8.hash(state);
144 theta.to_bits().hash(state);
145 phi.to_bits().hash(state);
146 lambda.to_bits().hash(state);
147 }
148 Self::CNOT => 16u8.hash(state),
149 Self::CZ => 17u8.hash(state),
150 Self::CY => 18u8.hash(state),
151 Self::SWAP => 19u8.hash(state),
152 Self::ISwap => 20u8.hash(state),
153 Self::CRX(angle) => {
154 21u8.hash(state);
155 angle.to_bits().hash(state);
156 }
157 Self::CRY(angle) => {
158 22u8.hash(state);
159 angle.to_bits().hash(state);
160 }
161 Self::CRZ(angle) => {
162 23u8.hash(state);
163 angle.to_bits().hash(state);
164 }
165 Self::CPhase(angle) => {
166 24u8.hash(state);
167 angle.to_bits().hash(state);
168 }
169 Self::Toffoli => 25u8.hash(state),
170 Self::Fredkin => 26u8.hash(state),
171 Self::MultiControlledX(n) => {
172 27u8.hash(state);
173 n.hash(state);
174 }
175 Self::MultiControlledZ(n) => {
176 28u8.hash(state);
177 n.hash(state);
178 }
179 Self::Custom(name, matrix) => {
180 29u8.hash(state);
181 name.hash(state);
182 matrix.shape().hash(state);
184 }
185 Self::Measure => 30u8.hash(state),
186 Self::Reset => 31u8.hash(state),
187 }
188 }
189}
190
191impl Eq for InterfaceGateType {}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct InterfaceGate {
197 pub gate_type: InterfaceGateType,
199 pub qubits: Vec<usize>,
201 pub classical_targets: Vec<usize>,
203 pub position: usize,
205 pub condition: Option<usize>,
207 pub label: Option<String>,
209}
210
211impl InterfaceGate {
212 #[must_use]
214 pub const fn new(gate_type: InterfaceGateType, qubits: Vec<usize>) -> Self {
215 Self {
216 gate_type,
217 qubits,
218 classical_targets: Vec::new(),
219 position: 0,
220 condition: None,
221 label: None,
222 }
223 }
224
225 #[must_use]
227 pub fn measurement(qubit: usize, classical_bit: usize) -> Self {
228 Self {
229 gate_type: InterfaceGateType::Measure,
230 qubits: vec![qubit],
231 classical_targets: vec![classical_bit],
232 position: 0,
233 condition: None,
234 label: None,
235 }
236 }
237
238 #[must_use]
240 pub const fn conditional(mut self, condition: usize) -> Self {
241 self.condition = Some(condition);
242 self
243 }
244
245 #[must_use]
247 pub fn with_label(mut self, label: String) -> Self {
248 self.label = Some(label);
249 self
250 }
251
252 pub fn unitary_matrix(&self) -> Result<Array2<Complex64>> {
254 match &self.gate_type {
255 InterfaceGateType::Identity => Ok(Array2::eye(2)),
256 InterfaceGateType::PauliX => Ok(Array2::from_shape_vec(
257 (2, 2),
258 vec![
259 Complex64::new(0.0, 0.0),
260 Complex64::new(1.0, 0.0),
261 Complex64::new(1.0, 0.0),
262 Complex64::new(0.0, 0.0),
263 ],
264 )
265 .expect("PauliX matrix shape matches data length")),
267 InterfaceGateType::PauliY => Ok(Array2::from_shape_vec(
268 (2, 2),
269 vec![
270 Complex64::new(0.0, 0.0),
271 Complex64::new(0.0, -1.0),
272 Complex64::new(0.0, 1.0),
273 Complex64::new(0.0, 0.0),
274 ],
275 )
276 .expect("PauliY matrix shape matches data length")),
278 InterfaceGateType::PauliZ => Ok(Array2::from_shape_vec(
279 (2, 2),
280 vec![
281 Complex64::new(1.0, 0.0),
282 Complex64::new(0.0, 0.0),
283 Complex64::new(0.0, 0.0),
284 Complex64::new(-1.0, 0.0),
285 ],
286 )
287 .expect("PauliZ matrix shape matches data length")),
289 InterfaceGateType::Hadamard => {
290 let inv_sqrt2 = 1.0 / (2.0_f64).sqrt();
291 Ok(Array2::from_shape_vec(
292 (2, 2),
293 vec![
294 Complex64::new(inv_sqrt2, 0.0),
295 Complex64::new(inv_sqrt2, 0.0),
296 Complex64::new(inv_sqrt2, 0.0),
297 Complex64::new(-inv_sqrt2, 0.0),
298 ],
299 )
300 .expect("Hadamard matrix shape matches data length"))
302 }
303 InterfaceGateType::S => Ok(Array2::from_shape_vec(
304 (2, 2),
305 vec![
306 Complex64::new(1.0, 0.0),
307 Complex64::new(0.0, 0.0),
308 Complex64::new(0.0, 0.0),
309 Complex64::new(0.0, 1.0),
310 ],
311 )
312 .expect("S gate matrix shape matches data length")),
314 InterfaceGateType::T => {
315 let phase = Complex64::new(0.0, std::f64::consts::PI / 4.0).exp();
316 Ok(Array2::from_shape_vec(
317 (2, 2),
318 vec![
319 Complex64::new(1.0, 0.0),
320 Complex64::new(0.0, 0.0),
321 Complex64::new(0.0, 0.0),
322 phase,
323 ],
324 )
325 .expect("T gate matrix shape matches data length"))
327 }
328 InterfaceGateType::Phase(theta) => {
329 let phase = Complex64::new(0.0, *theta).exp();
330 Ok(Array2::from_shape_vec(
331 (2, 2),
332 vec![
333 Complex64::new(1.0, 0.0),
334 Complex64::new(0.0, 0.0),
335 Complex64::new(0.0, 0.0),
336 phase,
337 ],
338 )
339 .expect("Phase gate matrix shape matches data length"))
341 }
342 InterfaceGateType::RX(theta) => {
343 let cos_half = (theta / 2.0).cos();
344 let sin_half = (theta / 2.0).sin();
345 Ok(Array2::from_shape_vec(
346 (2, 2),
347 vec![
348 Complex64::new(cos_half, 0.0),
349 Complex64::new(0.0, -sin_half),
350 Complex64::new(0.0, -sin_half),
351 Complex64::new(cos_half, 0.0),
352 ],
353 )
354 .expect("RX gate matrix shape matches data length"))
356 }
357 InterfaceGateType::RY(theta) => {
358 let cos_half = (theta / 2.0).cos();
359 let sin_half = (theta / 2.0).sin();
360 Ok(Array2::from_shape_vec(
361 (2, 2),
362 vec![
363 Complex64::new(cos_half, 0.0),
364 Complex64::new(-sin_half, 0.0),
365 Complex64::new(sin_half, 0.0),
366 Complex64::new(cos_half, 0.0),
367 ],
368 )
369 .expect("RY gate matrix shape matches data length"))
371 }
372 InterfaceGateType::RZ(theta) => {
373 let exp_neg = Complex64::new(0.0, -theta / 2.0).exp();
374 let exp_pos = Complex64::new(0.0, theta / 2.0).exp();
375 Ok(Array2::from_shape_vec(
376 (2, 2),
377 vec![
378 exp_neg,
379 Complex64::new(0.0, 0.0),
380 Complex64::new(0.0, 0.0),
381 exp_pos,
382 ],
383 )
384 .expect("RZ gate matrix shape matches data length"))
386 }
387 InterfaceGateType::CNOT => Ok(Array2::from_shape_vec(
388 (4, 4),
389 vec![
390 Complex64::new(1.0, 0.0),
391 Complex64::new(0.0, 0.0),
392 Complex64::new(0.0, 0.0),
393 Complex64::new(0.0, 0.0),
394 Complex64::new(0.0, 0.0),
395 Complex64::new(1.0, 0.0),
396 Complex64::new(0.0, 0.0),
397 Complex64::new(0.0, 0.0),
398 Complex64::new(0.0, 0.0),
399 Complex64::new(0.0, 0.0),
400 Complex64::new(0.0, 0.0),
401 Complex64::new(1.0, 0.0),
402 Complex64::new(0.0, 0.0),
403 Complex64::new(0.0, 0.0),
404 Complex64::new(1.0, 0.0),
405 Complex64::new(0.0, 0.0),
406 ],
407 )
408 .expect("CNOT matrix shape matches data length")),
410 InterfaceGateType::CZ => Ok(Array2::from_shape_vec(
411 (4, 4),
412 vec![
413 Complex64::new(1.0, 0.0),
414 Complex64::new(0.0, 0.0),
415 Complex64::new(0.0, 0.0),
416 Complex64::new(0.0, 0.0),
417 Complex64::new(0.0, 0.0),
418 Complex64::new(1.0, 0.0),
419 Complex64::new(0.0, 0.0),
420 Complex64::new(0.0, 0.0),
421 Complex64::new(0.0, 0.0),
422 Complex64::new(0.0, 0.0),
423 Complex64::new(1.0, 0.0),
424 Complex64::new(0.0, 0.0),
425 Complex64::new(0.0, 0.0),
426 Complex64::new(0.0, 0.0),
427 Complex64::new(0.0, 0.0),
428 Complex64::new(-1.0, 0.0),
429 ],
430 )
431 .expect("CZ matrix shape matches data length")),
433 InterfaceGateType::SWAP => Ok(Array2::from_shape_vec(
434 (4, 4),
435 vec![
436 Complex64::new(1.0, 0.0),
437 Complex64::new(0.0, 0.0),
438 Complex64::new(0.0, 0.0),
439 Complex64::new(0.0, 0.0),
440 Complex64::new(0.0, 0.0),
441 Complex64::new(0.0, 0.0),
442 Complex64::new(1.0, 0.0),
443 Complex64::new(0.0, 0.0),
444 Complex64::new(0.0, 0.0),
445 Complex64::new(1.0, 0.0),
446 Complex64::new(0.0, 0.0),
447 Complex64::new(0.0, 0.0),
448 Complex64::new(0.0, 0.0),
449 Complex64::new(0.0, 0.0),
450 Complex64::new(0.0, 0.0),
451 Complex64::new(1.0, 0.0),
452 ],
453 )
454 .expect("SWAP matrix shape matches data length")),
456 InterfaceGateType::MultiControlledZ(num_controls) => {
457 let total_qubits = num_controls + 1;
458 let dim = 1 << total_qubits;
459 let mut matrix = Array2::eye(dim);
460
461 let target_state = (1 << total_qubits) - 1; matrix[(target_state, target_state)] = Complex64::new(-1.0, 0.0);
464
465 Ok(matrix)
466 }
467 InterfaceGateType::MultiControlledX(num_controls) => {
468 let total_qubits = num_controls + 1;
469 let dim = 1 << total_qubits;
470 let mut matrix = Array2::eye(dim);
471
472 let control_pattern = (1 << *num_controls) - 1; let target_bit = 1 << num_controls;
475
476 let state0 = control_pattern; let state1 = control_pattern | target_bit; matrix[(state0, state0)] = Complex64::new(0.0, 0.0);
481 matrix[(state1, state1)] = Complex64::new(0.0, 0.0);
482 matrix[(state0, state1)] = Complex64::new(1.0, 0.0);
483 matrix[(state1, state0)] = Complex64::new(1.0, 0.0);
484
485 Ok(matrix)
486 }
487 InterfaceGateType::CPhase(phase) => {
488 let phase_factor = Complex64::new(0.0, *phase).exp();
489 Ok(Array2::from_shape_vec(
490 (4, 4),
491 vec![
492 Complex64::new(1.0, 0.0),
493 Complex64::new(0.0, 0.0),
494 Complex64::new(0.0, 0.0),
495 Complex64::new(0.0, 0.0),
496 Complex64::new(0.0, 0.0),
497 Complex64::new(1.0, 0.0),
498 Complex64::new(0.0, 0.0),
499 Complex64::new(0.0, 0.0),
500 Complex64::new(0.0, 0.0),
501 Complex64::new(0.0, 0.0),
502 Complex64::new(1.0, 0.0),
503 Complex64::new(0.0, 0.0),
504 Complex64::new(0.0, 0.0),
505 Complex64::new(0.0, 0.0),
506 Complex64::new(0.0, 0.0),
507 phase_factor,
508 ],
509 )
510 .expect("CPhase matrix shape matches data length"))
512 }
513 InterfaceGateType::Custom(_, matrix) => Ok(matrix.clone()),
514 _ => Err(SimulatorError::UnsupportedOperation(format!(
515 "Unitary matrix not available for gate type: {:?}",
516 self.gate_type
517 ))),
518 }
519 }
520
521 #[must_use]
523 pub const fn is_measurement(&self) -> bool {
524 matches!(self.gate_type, InterfaceGateType::Measure)
525 }
526
527 #[must_use]
529 pub const fn is_unitary(&self) -> bool {
530 !matches!(
531 self.gate_type,
532 InterfaceGateType::Measure | InterfaceGateType::Reset
533 )
534 }
535
536 #[must_use]
538 pub fn num_qubits(&self) -> usize {
539 match &self.gate_type {
540 InterfaceGateType::MultiControlledX(n) | InterfaceGateType::MultiControlledZ(n) => {
541 n + 1
542 }
543 _ => self.qubits.len(),
544 }
545 }
546}
547
548#[derive(Debug, Clone, Serialize, Deserialize)]
550pub struct InterfaceCircuit {
551 pub num_qubits: usize,
553 pub num_classical: usize,
555 pub gates: Vec<InterfaceGate>,
557 pub metadata: CircuitMetadata,
559}
560
561#[derive(Debug, Clone, Default, Serialize, Deserialize)]
563pub struct CircuitMetadata {
564 pub name: Option<String>,
566 pub description: Option<String>,
568 #[serde(skip)]
570 pub created_at: Option<std::time::SystemTime>,
571 pub depth: usize,
573 pub two_qubit_gates: usize,
575 pub complexity_score: f64,
577 pub classical_complexity: Option<f64>,
579}
580
581impl InterfaceCircuit {
582 #[must_use]
584 pub fn new(num_qubits: usize, num_classical: usize) -> Self {
585 Self {
586 num_qubits,
587 num_classical,
588 gates: Vec::new(),
589 metadata: CircuitMetadata::default(),
590 }
591 }
592
593 pub fn add_gate(&mut self, mut gate: InterfaceGate) {
595 gate.position = self.gates.len();
596 self.gates.push(gate);
597 self.update_metadata();
598 }
599
600 pub fn add_gates(&mut self, gates: Vec<InterfaceGate>) {
602 for gate in gates {
603 self.add_gate(gate);
604 }
605 }
606
607 fn update_metadata(&mut self) {
609 let depth = self.calculate_depth();
610 let two_qubit_gates = self.gates.iter().filter(|g| g.num_qubits() == 2).count();
611
612 let complexity_score = self.calculate_complexity_score();
613
614 self.metadata.depth = depth;
615 self.metadata.two_qubit_gates = two_qubit_gates;
616 self.metadata.complexity_score = complexity_score;
617 }
618
619 #[must_use]
621 pub fn calculate_depth(&self) -> usize {
622 if self.gates.is_empty() {
623 return 0;
624 }
625
626 let mut qubit_depths = vec![0; self.num_qubits];
627
628 for gate in &self.gates {
629 let valid_qubits: Vec<usize> = gate
631 .qubits
632 .iter()
633 .filter(|&&q| q < self.num_qubits)
634 .copied()
635 .collect();
636
637 if valid_qubits.is_empty() {
638 continue;
639 }
640
641 let max_depth = valid_qubits
642 .iter()
643 .map(|&q| qubit_depths[q])
644 .max()
645 .unwrap_or(0);
646
647 for &qubit in &valid_qubits {
648 qubit_depths[qubit] = max_depth + 1;
649 }
650 }
651
652 qubit_depths.into_iter().max().unwrap_or(0)
653 }
654
655 fn calculate_complexity_score(&self) -> f64 {
657 let mut score = 0.0;
658
659 for gate in &self.gates {
660 let gate_score = match gate.num_qubits() {
661 1 => 1.0,
662 2 => 5.0,
663 3 => 25.0,
664 n => (5.0_f64).powi(n as i32 - 1),
665 };
666 score += gate_score;
667 }
668
669 score
670 }
671
672 pub fn subcircuit(&self, start: usize, end: usize) -> Result<Self> {
674 if start >= end || end > self.gates.len() {
675 return Err(SimulatorError::InvalidInput(
676 "Invalid subcircuit range".to_string(),
677 ));
678 }
679
680 let mut subcircuit = Self::new(self.num_qubits, self.num_classical);
681 subcircuit.gates = self.gates[start..end].to_vec();
682 subcircuit.update_metadata();
683
684 Ok(subcircuit)
685 }
686
687 pub fn optimize(&mut self) -> CircuitOptimizationResult {
689 let original_gates = self.gates.len();
690 let original_depth = self.metadata.depth;
691
692 self.remove_identity_gates();
694 self.cancel_adjacent_gates();
695 self.merge_rotation_gates();
696 self.optimize_cnot_patterns();
697
698 self.update_metadata();
699
700 CircuitOptimizationResult {
701 original_gates,
702 optimized_gates: self.gates.len(),
703 original_depth,
704 optimized_depth: self.metadata.depth,
705 gates_eliminated: original_gates.saturating_sub(self.gates.len()),
706 depth_reduction: original_depth.saturating_sub(self.metadata.depth),
707 }
708 }
709
710 fn remove_identity_gates(&mut self) {
712 self.gates
713 .retain(|gate| !matches!(gate.gate_type, InterfaceGateType::Identity));
714 }
715
716 fn cancel_adjacent_gates(&mut self) {
718 let mut i = 0;
719 while i + 1 < self.gates.len() {
720 if self.gates_cancel(&self.gates[i], &self.gates[i + 1]) {
721 self.gates.remove(i);
722 self.gates.remove(i);
723 if i > 0 {
724 i = i.saturating_sub(1);
725 }
726 } else {
727 i += 1;
728 }
729 }
730 }
731
732 fn gates_cancel(&self, gate1: &InterfaceGate, gate2: &InterfaceGate) -> bool {
734 if gate1.qubits != gate2.qubits {
735 return false;
736 }
737
738 match (&gate1.gate_type, &gate2.gate_type) {
739 (InterfaceGateType::PauliX, InterfaceGateType::PauliX)
740 | (InterfaceGateType::PauliY, InterfaceGateType::PauliY)
741 | (InterfaceGateType::PauliZ, InterfaceGateType::PauliZ)
742 | (InterfaceGateType::Hadamard, InterfaceGateType::Hadamard)
743 | (InterfaceGateType::S, InterfaceGateType::S)
744 | (InterfaceGateType::CNOT, InterfaceGateType::CNOT)
745 | (InterfaceGateType::CZ, InterfaceGateType::CZ)
746 | (InterfaceGateType::SWAP, InterfaceGateType::SWAP) => true,
747 _ => false,
748 }
749 }
750
751 fn merge_rotation_gates(&mut self) {
753 let mut i = 0;
754 while i + 1 < self.gates.len() {
755 if let Some(merged) = self.try_merge_rotations(&self.gates[i], &self.gates[i + 1]) {
756 self.gates[i] = merged;
757 self.gates.remove(i + 1);
758 } else {
759 i += 1;
760 }
761 }
762 }
763
764 fn try_merge_rotations(
766 &self,
767 gate1: &InterfaceGate,
768 gate2: &InterfaceGate,
769 ) -> Option<InterfaceGate> {
770 if gate1.qubits != gate2.qubits {
771 return None;
772 }
773
774 match (&gate1.gate_type, &gate2.gate_type) {
775 (InterfaceGateType::RX(angle1), InterfaceGateType::RX(angle2)) => Some(
776 InterfaceGate::new(InterfaceGateType::RX(angle1 + angle2), gate1.qubits.clone()),
777 ),
778 (InterfaceGateType::RY(angle1), InterfaceGateType::RY(angle2)) => Some(
779 InterfaceGate::new(InterfaceGateType::RY(angle1 + angle2), gate1.qubits.clone()),
780 ),
781 (InterfaceGateType::RZ(angle1), InterfaceGateType::RZ(angle2)) => Some(
782 InterfaceGate::new(InterfaceGateType::RZ(angle1 + angle2), gate1.qubits.clone()),
783 ),
784 _ => None,
785 }
786 }
787
788 fn optimize_cnot_patterns(&mut self) {
790 let mut i = 0;
792 while i + 2 < self.gates.len() {
793 if self.is_cnot_chain(i) {
794 self.optimize_cnot_chain(i);
795 }
796 i += 1;
797 }
798 }
799
800 fn is_cnot_chain(&self, start: usize) -> bool {
802 if start + 2 >= self.gates.len() {
803 return false;
804 }
805
806 for i in start..start + 3 {
807 if !matches!(self.gates[i].gate_type, InterfaceGateType::CNOT) {
808 return false;
809 }
810 }
811
812 true
813 }
814
815 fn optimize_cnot_chain(&mut self, start: usize) {
817 if start + 2 < self.gates.len() {
819 let gate1 = &self.gates[start];
820 let gate2 = &self.gates[start + 1];
821 let gate3 = &self.gates[start + 2];
822
823 if gate1.qubits == gate2.qubits && gate2.qubits == gate3.qubits {
824 self.gates.drain(start + 1..start + 3);
826 }
827 }
828 }
829}
830
831#[derive(Debug, Clone)]
833pub struct CircuitOptimizationResult {
834 pub original_gates: usize,
836 pub optimized_gates: usize,
838 pub original_depth: usize,
840 pub optimized_depth: usize,
842 pub gates_eliminated: usize,
844 pub depth_reduction: usize,
846}
847
848#[derive(Debug, Clone, Copy, PartialEq, Eq)]
850pub enum SimulationBackend {
851 StateVector,
853 MPS,
855 Stabilizer,
857 Sparse,
859 TensorNetwork,
861 Auto,
863}
864
865pub struct CircuitInterface {
867 config: CircuitInterfaceConfig,
869 backend: Option<SciRS2Backend>,
871 circuit_cache: Arc<Mutex<HashMap<u64, CompiledCircuit>>>,
873 stats: CircuitInterfaceStats,
875}
876
877#[derive(Debug, Clone)]
879pub struct CompiledCircuit {
880 pub original: InterfaceCircuit,
882 pub optimized_gates: Vec<InterfaceGate>,
884 pub backend_data: BackendCompiledData,
886 pub metadata: CompilationMetadata,
888}
889
890#[derive(Debug, Clone)]
892pub enum BackendCompiledData {
893 StateVector {
894 unitary_matrices: Vec<Array2<Complex64>>,
895 gate_indices: Vec<Vec<usize>>,
896 },
897 MPS {
898 bond_dimensions: Vec<usize>,
899 truncation_thresholds: Vec<f64>,
900 },
901 Stabilizer {
902 clifford_sequence: Vec<StabilizerOp>,
903 },
904 Sparse {
905 sparse_matrices: Vec<CSRMatrix>,
906 },
907}
908
909#[derive(Debug, Clone)]
911pub enum StabilizerOp {
912 H(usize),
913 S(usize),
914 CNOT(usize, usize),
915 X(usize),
916 Y(usize),
917 Z(usize),
918}
919
920#[derive(Debug, Clone)]
922pub struct CompilationMetadata {
923 pub compilation_time_ms: f64,
925 pub backend: SimulationBackend,
927 pub optimization_passes: Vec<String>,
929 pub estimated_execution_time_ms: f64,
931 pub estimated_memory_bytes: usize,
933}
934
935#[derive(Debug, Clone, Default, Serialize, Deserialize)]
937pub struct CircuitInterfaceStats {
938 pub circuits_compiled: usize,
940 pub total_compilation_time_ms: f64,
942 pub cache_hit_rate: f64,
944 pub backend_selections: HashMap<String, usize>,
946 pub optimization_stats: OptimizationStats,
948}
949
950#[derive(Debug, Clone, Default, Serialize, Deserialize)]
952pub struct OptimizationStats {
953 pub total_gates_eliminated: usize,
955 pub total_depth_reduction: usize,
957 pub average_optimization_ratio: f64,
959}
960
961impl CircuitInterface {
962 pub fn new(config: CircuitInterfaceConfig) -> Result<Self> {
964 Ok(Self {
965 config,
966 backend: None,
967 circuit_cache: Arc::new(Mutex::new(HashMap::new())),
968 stats: CircuitInterfaceStats::default(),
969 })
970 }
971
972 pub fn with_backend(mut self) -> Result<Self> {
974 self.backend = Some(SciRS2Backend::new());
975 Ok(self)
976 }
977
978 pub fn compile_circuit(
980 &mut self,
981 circuit: &InterfaceCircuit,
982 backend: SimulationBackend,
983 ) -> Result<CompiledCircuit> {
984 let start_time = std::time::Instant::now();
985
986 let circuit_hash = self.calculate_circuit_hash(circuit);
988 if self.config.enable_circuit_cache {
989 let cache = self
990 .circuit_cache
991 .lock()
992 .expect("circuit cache lock should not be poisoned");
993 if let Some(compiled) = cache.get(&circuit_hash) {
994 self.stats.cache_hit_rate = self
995 .stats
996 .cache_hit_rate
997 .mul_add(self.stats.circuits_compiled as f64, 1.0)
998 / (self.stats.circuits_compiled + 1) as f64;
999 return Ok(compiled.clone());
1000 }
1001 }
1002
1003 let selected_backend = if backend == SimulationBackend::Auto {
1005 self.select_optimal_backend(circuit)?
1006 } else {
1007 backend
1008 };
1009
1010 let mut optimized_circuit = circuit.clone();
1012 let mut optimization_passes = Vec::new();
1013
1014 if self.config.enable_optimization {
1015 let opt_result = optimized_circuit.optimize();
1016 optimization_passes.push("basic_optimization".to_string());
1017
1018 self.stats.optimization_stats.total_gates_eliminated += opt_result.gates_eliminated;
1019 self.stats.optimization_stats.total_depth_reduction += opt_result.depth_reduction;
1020 }
1021
1022 let backend_data = self.compile_for_backend(&optimized_circuit, selected_backend)?;
1024
1025 let estimated_execution_time_ms =
1027 self.estimate_execution_time(&optimized_circuit, selected_backend);
1028 let estimated_memory_bytes =
1029 self.estimate_memory_requirements(&optimized_circuit, selected_backend);
1030
1031 let compilation_time_ms = start_time.elapsed().as_secs_f64() * 1000.0;
1032
1033 let compiled = CompiledCircuit {
1034 original: circuit.clone(),
1035 optimized_gates: optimized_circuit.gates,
1036 backend_data,
1037 metadata: CompilationMetadata {
1038 compilation_time_ms,
1039 backend: selected_backend,
1040 optimization_passes,
1041 estimated_execution_time_ms,
1042 estimated_memory_bytes,
1043 },
1044 };
1045
1046 if self.config.enable_circuit_cache {
1048 let mut cache = self
1049 .circuit_cache
1050 .lock()
1051 .expect("circuit cache lock should not be poisoned");
1052 if cache.len() >= self.config.max_cache_size {
1053 if let Some(oldest_key) = cache.keys().next().copied() {
1055 cache.remove(&oldest_key);
1056 }
1057 }
1058 cache.insert(circuit_hash, compiled.clone());
1059 }
1060
1061 self.stats.circuits_compiled += 1;
1063 self.stats.total_compilation_time_ms += compilation_time_ms;
1064 *self
1065 .stats
1066 .backend_selections
1067 .entry(format!("{selected_backend:?}"))
1068 .or_insert(0) += 1;
1069
1070 Ok(compiled)
1071 }
1072
1073 pub fn execute_circuit(
1075 &mut self,
1076 compiled: &CompiledCircuit,
1077 initial_state: Option<Array1<Complex64>>,
1078 ) -> Result<CircuitExecutionResult> {
1079 let start_time = std::time::Instant::now();
1080
1081 let result = match compiled.metadata.backend {
1082 SimulationBackend::StateVector => self.execute_statevector(compiled, initial_state)?,
1083 SimulationBackend::MPS => self.execute_mps(compiled, initial_state)?,
1084 SimulationBackend::Stabilizer => self.execute_stabilizer(compiled)?,
1085 SimulationBackend::Sparse => self.execute_sparse(compiled, initial_state)?,
1086 #[cfg(feature = "advanced_math")]
1087 SimulationBackend::TensorNetwork => {
1088 self.execute_tensor_network(compiled, initial_state)?
1089 }
1090 #[cfg(not(feature = "advanced_math"))]
1091 SimulationBackend::TensorNetwork => {
1092 return Err(SimulatorError::UnsupportedOperation(
1093 "Tensor network simulation requires advanced_math feature".to_string(),
1094 ))
1095 }
1096 SimulationBackend::Auto => {
1097 unreachable!("Auto backend should be resolved during compilation")
1098 }
1099 };
1100
1101 let execution_time_ms = start_time.elapsed().as_secs_f64() * 1000.0;
1102
1103 Ok(CircuitExecutionResult {
1104 final_state: result.final_state,
1105 measurement_results: result.measurement_results,
1106 classical_bits: result.classical_bits,
1107 execution_time_ms,
1108 backend_used: compiled.metadata.backend,
1109 memory_used_bytes: result.memory_used_bytes,
1110 })
1111 }
1112
1113 fn select_optimal_backend(&self, circuit: &InterfaceCircuit) -> Result<SimulationBackend> {
1115 let num_qubits = circuit.num_qubits;
1116 let two_qubit_gates = circuit.metadata.two_qubit_gates;
1117 let total_gates = circuit.gates.len();
1118
1119 if self.is_clifford_circuit(circuit) {
1121 return Ok(SimulationBackend::Stabilizer);
1122 }
1123
1124 if num_qubits <= self.config.max_statevector_qubits {
1126 return Ok(SimulationBackend::StateVector);
1127 }
1128
1129 let entanglement_score = two_qubit_gates as f64 / total_gates as f64;
1131 if entanglement_score < 0.3 {
1132 return Ok(SimulationBackend::MPS);
1133 }
1134
1135 let sparsity_score = self.estimate_sparsity(circuit);
1137 if sparsity_score > 0.8 {
1138 return Ok(SimulationBackend::Sparse);
1139 }
1140
1141 if self.has_tensor_network_structure(circuit) {
1143 return Ok(SimulationBackend::TensorNetwork);
1144 }
1145
1146 Ok(SimulationBackend::MPS)
1148 }
1149
1150 fn is_clifford_circuit(&self, circuit: &InterfaceCircuit) -> bool {
1152 circuit.gates.iter().all(|gate| {
1153 matches!(
1154 gate.gate_type,
1155 InterfaceGateType::Identity
1156 | InterfaceGateType::PauliX
1157 | InterfaceGateType::PauliY
1158 | InterfaceGateType::PauliZ
1159 | InterfaceGateType::Hadamard
1160 | InterfaceGateType::S
1161 | InterfaceGateType::CNOT
1162 | InterfaceGateType::CZ
1163 | InterfaceGateType::SWAP
1164 | InterfaceGateType::Measure
1165 | InterfaceGateType::Reset
1166 )
1167 })
1168 }
1169
1170 fn estimate_sparsity(&self, circuit: &InterfaceCircuit) -> f64 {
1172 let single_qubit_gates = circuit.gates.iter().filter(|g| g.num_qubits() == 1).count();
1174
1175 single_qubit_gates as f64 / circuit.gates.len() as f64
1176 }
1177
1178 fn has_tensor_network_structure(&self, circuit: &InterfaceCircuit) -> bool {
1180 let depth = circuit.metadata.depth;
1182 let num_qubits = circuit.num_qubits;
1183
1184 depth > num_qubits && circuit.metadata.complexity_score > 100.0
1186 }
1187
1188 fn compile_for_backend(
1190 &self,
1191 circuit: &InterfaceCircuit,
1192 backend: SimulationBackend,
1193 ) -> Result<BackendCompiledData> {
1194 match backend {
1195 SimulationBackend::StateVector => {
1196 let mut unitary_matrices = Vec::new();
1197 let mut gate_indices = Vec::new();
1198
1199 for gate in &circuit.gates {
1200 if gate.is_unitary() {
1201 unitary_matrices.push(gate.unitary_matrix()?);
1202 gate_indices.push(gate.qubits.clone());
1203 }
1204 }
1205
1206 Ok(BackendCompiledData::StateVector {
1207 unitary_matrices,
1208 gate_indices,
1209 })
1210 }
1211 SimulationBackend::MPS => {
1212 let bond_dimensions = self.calculate_optimal_bond_dimensions(circuit);
1214 let truncation_thresholds = vec![1e-12; circuit.gates.len()];
1215
1216 Ok(BackendCompiledData::MPS {
1217 bond_dimensions,
1218 truncation_thresholds,
1219 })
1220 }
1221 SimulationBackend::Stabilizer => {
1222 let mut clifford_sequence = Vec::new();
1223
1224 for gate in &circuit.gates {
1225 match &gate.gate_type {
1226 InterfaceGateType::Hadamard => {
1227 clifford_sequence.push(StabilizerOp::H(gate.qubits[0]));
1228 }
1229 InterfaceGateType::S => {
1230 clifford_sequence.push(StabilizerOp::S(gate.qubits[0]));
1231 }
1232 InterfaceGateType::PauliX => {
1233 clifford_sequence.push(StabilizerOp::X(gate.qubits[0]));
1234 }
1235 InterfaceGateType::PauliY => {
1236 clifford_sequence.push(StabilizerOp::Y(gate.qubits[0]));
1237 }
1238 InterfaceGateType::PauliZ => {
1239 clifford_sequence.push(StabilizerOp::Z(gate.qubits[0]));
1240 }
1241 InterfaceGateType::CNOT => clifford_sequence
1242 .push(StabilizerOp::CNOT(gate.qubits[0], gate.qubits[1])),
1243 _ => {} }
1245 }
1246
1247 Ok(BackendCompiledData::Stabilizer { clifford_sequence })
1248 }
1249 SimulationBackend::Sparse => {
1250 let sparse_matrices = Vec::new(); Ok(BackendCompiledData::Sparse { sparse_matrices })
1252 }
1253 SimulationBackend::TensorNetwork => {
1254 let mut unitary_matrices = Vec::new();
1256 let mut gate_indices = Vec::new();
1257
1258 for gate in &circuit.gates {
1259 if gate.is_unitary() {
1260 unitary_matrices.push(gate.unitary_matrix()?);
1261 gate_indices.push(gate.qubits.clone());
1262 }
1263 }
1264
1265 Ok(BackendCompiledData::StateVector {
1266 unitary_matrices,
1267 gate_indices,
1268 })
1269 }
1270 SimulationBackend::Auto => unreachable!(),
1271 }
1272 }
1273
1274 fn calculate_optimal_bond_dimensions(&self, circuit: &InterfaceCircuit) -> Vec<usize> {
1276 let base_bond_dim = self.config.max_mps_bond_dim.min(64);
1277 vec![base_bond_dim; circuit.num_qubits - 1]
1278 }
1279
1280 fn execute_statevector(
1282 &self,
1283 compiled: &CompiledCircuit,
1284 initial_state: Option<Array1<Complex64>>,
1285 ) -> Result<BackendExecutionResult> {
1286 let _simulator = StateVectorSimulator::new();
1287
1288 let num_qubits = compiled.original.num_qubits;
1290 let state_size = 1 << num_qubits;
1291
1292 let final_state = initial_state.unwrap_or_else(|| {
1293 let mut state = Array1::zeros(state_size);
1294 state[0] = Complex64::new(1.0, 0.0);
1295 state
1296 });
1297 let memory_used = final_state.len() * std::mem::size_of::<Complex64>();
1298
1299 Ok(BackendExecutionResult {
1300 final_state: Some(final_state),
1301 measurement_results: Vec::new(),
1302 classical_bits: vec![false; compiled.original.num_classical],
1303 memory_used_bytes: memory_used,
1304 })
1305 }
1306
1307 fn execute_mps(
1309 &self,
1310 compiled: &CompiledCircuit,
1311 initial_state: Option<Array1<Complex64>>,
1312 ) -> Result<BackendExecutionResult> {
1313 Ok(BackendExecutionResult {
1315 final_state: None,
1316 measurement_results: Vec::new(),
1317 classical_bits: vec![false; compiled.original.num_classical],
1318 memory_used_bytes: 0,
1319 })
1320 }
1321
1322 fn execute_stabilizer(&self, compiled: &CompiledCircuit) -> Result<BackendExecutionResult> {
1324 Ok(BackendExecutionResult {
1326 final_state: None,
1327 measurement_results: Vec::new(),
1328 classical_bits: vec![false; compiled.original.num_classical],
1329 memory_used_bytes: 0,
1330 })
1331 }
1332
1333 fn execute_sparse(
1335 &self,
1336 compiled: &CompiledCircuit,
1337 initial_state: Option<Array1<Complex64>>,
1338 ) -> Result<BackendExecutionResult> {
1339 Ok(BackendExecutionResult {
1341 final_state: None,
1342 measurement_results: Vec::new(),
1343 classical_bits: vec![false; compiled.original.num_classical],
1344 memory_used_bytes: 0,
1345 })
1346 }
1347
1348 #[cfg(feature = "advanced_math")]
1350 fn execute_tensor_network(
1351 &self,
1352 compiled: &CompiledCircuit,
1353 initial_state: Option<Array1<Complex64>>,
1354 ) -> Result<BackendExecutionResult> {
1355 Ok(BackendExecutionResult {
1357 final_state: None,
1358 measurement_results: Vec::new(),
1359 classical_bits: vec![false; compiled.original.num_classical],
1360 memory_used_bytes: 0,
1361 })
1362 }
1363
1364 #[cfg(not(feature = "advanced_math"))]
1365 fn execute_tensor_network(
1366 &self,
1367 _compiled: &CompiledCircuit,
1368 _initial_state: Option<Array1<Complex64>>,
1369 ) -> Result<BackendExecutionResult> {
1370 Err(SimulatorError::UnsupportedOperation(
1371 "Tensor network simulation requires advanced_math feature".to_string(),
1372 ))
1373 }
1374
1375 fn calculate_circuit_hash(&self, circuit: &InterfaceCircuit) -> u64 {
1377 let mut hasher = DefaultHasher::new();
1378 circuit.num_qubits.hash(&mut hasher);
1379 circuit.num_classical.hash(&mut hasher);
1380
1381 for gate in &circuit.gates {
1382 std::mem::discriminant(&gate.gate_type).hash(&mut hasher);
1384 match &gate.gate_type {
1386 InterfaceGateType::Phase(angle)
1387 | InterfaceGateType::RX(angle)
1388 | InterfaceGateType::RY(angle)
1389 | InterfaceGateType::RZ(angle) => {
1390 angle.to_bits().hash(&mut hasher);
1391 }
1392 _ => {}
1393 }
1394 gate.qubits.hash(&mut hasher);
1395 }
1396
1397 hasher.finish()
1398 }
1399
1400 fn estimate_execution_time(
1402 &self,
1403 circuit: &InterfaceCircuit,
1404 backend: SimulationBackend,
1405 ) -> f64 {
1406 let base_time_per_gate = match backend {
1407 SimulationBackend::StateVector => 0.1, SimulationBackend::MPS => 1.0,
1409 SimulationBackend::Stabilizer => 0.01,
1410 SimulationBackend::Sparse => 0.5,
1411 SimulationBackend::TensorNetwork => 2.0,
1412 SimulationBackend::Auto => 1.0,
1413 };
1414
1415 circuit.gates.len() as f64 * base_time_per_gate * (1.1_f64).powi(circuit.num_qubits as i32)
1416 }
1417
1418 fn estimate_memory_requirements(
1420 &self,
1421 circuit: &InterfaceCircuit,
1422 backend: SimulationBackend,
1423 ) -> usize {
1424 match backend {
1425 SimulationBackend::StateVector => {
1426 (1_usize << circuit.num_qubits) * std::mem::size_of::<Complex64>()
1427 }
1428 SimulationBackend::MPS => {
1429 circuit.num_qubits
1430 * self.config.max_mps_bond_dim
1431 * self.config.max_mps_bond_dim
1432 * std::mem::size_of::<Complex64>()
1433 }
1434 SimulationBackend::Stabilizer => {
1435 circuit.num_qubits * circuit.num_qubits * 2 }
1437 SimulationBackend::Sparse => {
1438 circuit.gates.len() * 1000 * std::mem::size_of::<Complex64>() }
1440 SimulationBackend::TensorNetwork => {
1441 circuit.num_qubits * 64 * std::mem::size_of::<Complex64>() }
1443 SimulationBackend::Auto => 0,
1444 }
1445 }
1446
1447 #[must_use]
1449 pub const fn get_stats(&self) -> &CircuitInterfaceStats {
1450 &self.stats
1451 }
1452
1453 pub fn reset_stats(&mut self) {
1455 self.stats = CircuitInterfaceStats::default();
1456 }
1457}
1458
1459#[derive(Debug)]
1461struct BackendExecutionResult {
1462 final_state: Option<Array1<Complex64>>,
1463 measurement_results: Vec<bool>,
1464 classical_bits: Vec<bool>,
1465 memory_used_bytes: usize,
1466}
1467
1468#[derive(Debug)]
1470pub struct CircuitExecutionResult {
1471 pub final_state: Option<Array1<Complex64>>,
1473 pub measurement_results: Vec<bool>,
1475 pub classical_bits: Vec<bool>,
1477 pub execution_time_ms: f64,
1479 pub backend_used: SimulationBackend,
1481 pub memory_used_bytes: usize,
1483}
1484
1485pub struct CircuitInterfaceUtils;
1487
1488impl CircuitInterfaceUtils {
1489 #[must_use]
1491 pub fn create_test_circuit(circuit_type: &str, num_qubits: usize) -> InterfaceCircuit {
1492 let mut circuit = InterfaceCircuit::new(num_qubits, num_qubits);
1493
1494 match circuit_type {
1495 "ghz" => {
1496 circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![0]));
1498 for i in 1..num_qubits {
1499 circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![0, i]));
1500 }
1501 }
1502 "qft" => {
1503 for i in 0..num_qubits {
1505 circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![i]));
1506 for j in i + 1..num_qubits {
1507 let angle = std::f64::consts::PI / f64::from(1 << (j - i));
1508 circuit.add_gate(InterfaceGate::new(
1509 InterfaceGateType::CRZ(angle),
1510 vec![j, i],
1511 ));
1512 }
1513 }
1514 }
1515 "random" => {
1516 for _ in 0..num_qubits * 5 {
1518 let qubit = fastrand::usize(0..num_qubits);
1519 let gate_type = match fastrand::usize(0..4) {
1520 0 => InterfaceGateType::Hadamard,
1521 1 => InterfaceGateType::RX(fastrand::f64() * 2.0 * std::f64::consts::PI),
1522 2 => InterfaceGateType::RY(fastrand::f64() * 2.0 * std::f64::consts::PI),
1523 _ => InterfaceGateType::RZ(fastrand::f64() * 2.0 * std::f64::consts::PI),
1524 };
1525 circuit.add_gate(InterfaceGate::new(gate_type, vec![qubit]));
1526 }
1527 }
1528 _ => {
1529 for i in 0..num_qubits {
1531 circuit.add_gate(InterfaceGate::new(InterfaceGateType::Identity, vec![i]));
1532 }
1533 }
1534 }
1535
1536 circuit
1537 }
1538
1539 pub fn benchmark_interface(
1541 config: CircuitInterfaceConfig,
1542 ) -> Result<InterfaceBenchmarkResults> {
1543 let mut interface = CircuitInterface::new(config)?;
1544 let mut results = InterfaceBenchmarkResults::default();
1545
1546 let circuit_types = vec!["ghz", "qft", "random"];
1547 let qubit_counts = vec![5, 10, 15, 20];
1548
1549 for circuit_type in circuit_types {
1550 for &num_qubits in &qubit_counts {
1551 let circuit = Self::create_test_circuit(circuit_type, num_qubits);
1552
1553 let start = std::time::Instant::now();
1554 let compiled = interface.compile_circuit(&circuit, SimulationBackend::Auto)?;
1555 let compilation_time = start.elapsed().as_secs_f64() * 1000.0;
1556
1557 let start = std::time::Instant::now();
1558 let _result = interface.execute_circuit(&compiled, None)?;
1559 let execution_time = start.elapsed().as_secs_f64() * 1000.0;
1560
1561 results
1562 .compilation_times
1563 .push((format!("{circuit_type}_{num_qubits}"), compilation_time));
1564 results
1565 .execution_times
1566 .push((format!("{circuit_type}_{num_qubits}"), execution_time));
1567 }
1568 }
1569
1570 results.interface_stats = interface.get_stats().clone();
1571 Ok(results)
1572 }
1573}
1574
1575#[derive(Debug, Clone, Default)]
1577pub struct InterfaceBenchmarkResults {
1578 pub compilation_times: Vec<(String, f64)>,
1580 pub execution_times: Vec<(String, f64)>,
1582 pub interface_stats: CircuitInterfaceStats,
1584}
1585
1586#[cfg(test)]
1587mod tests {
1588 use super::*;
1589 use approx::assert_abs_diff_eq;
1590
1591 #[test]
1592 fn test_interface_gate_creation() {
1593 let gate = InterfaceGate::new(InterfaceGateType::Hadamard, vec![0]);
1594 assert_eq!(gate.qubits, vec![0]);
1595 assert!(gate.is_unitary());
1596 assert!(!gate.is_measurement());
1597 }
1598
1599 #[test]
1600 fn test_measurement_gate() {
1601 let gate = InterfaceGate::measurement(0, 0);
1602 assert!(gate.is_measurement());
1603 assert!(!gate.is_unitary());
1604 assert_eq!(gate.classical_targets, vec![0]);
1605 }
1606
1607 #[test]
1608 fn test_circuit_creation() {
1609 let mut circuit = InterfaceCircuit::new(3, 3);
1610 circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![0]));
1611 circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![0, 1]));
1612
1613 assert_eq!(circuit.gates.len(), 2);
1614 assert_eq!(circuit.calculate_depth(), 2);
1615 }
1616
1617 #[test]
1618 fn test_circuit_optimization() {
1619 let mut circuit = InterfaceCircuit::new(2, 0);
1620 circuit.add_gate(InterfaceGate::new(InterfaceGateType::PauliX, vec![0]));
1621 circuit.add_gate(InterfaceGate::new(InterfaceGateType::PauliX, vec![0])); circuit.add_gate(InterfaceGate::new(InterfaceGateType::Identity, vec![1])); let result = circuit.optimize();
1625 assert_eq!(result.gates_eliminated, 3); }
1627
1628 #[test]
1629 fn test_gate_unitary_matrices() {
1630 let hadamard = InterfaceGate::new(InterfaceGateType::Hadamard, vec![0]);
1631 let matrix = hadamard
1632 .unitary_matrix()
1633 .expect("should get Hadamard matrix");
1634
1635 let inv_sqrt2 = 1.0 / (2.0_f64).sqrt();
1636 assert_abs_diff_eq!(matrix[[0, 0]].re, inv_sqrt2, epsilon = 1e-10);
1637 assert_abs_diff_eq!(matrix[[1, 1]].re, -inv_sqrt2, epsilon = 1e-10);
1638 }
1639
1640 #[test]
1641 fn test_rotation_gate_merging() {
1642 let mut circuit = InterfaceCircuit::new(1, 0);
1643 circuit.add_gate(InterfaceGate::new(InterfaceGateType::RX(0.5), vec![0]));
1644 circuit.add_gate(InterfaceGate::new(InterfaceGateType::RX(0.3), vec![0]));
1645
1646 let _ = circuit.optimize();
1647 assert_eq!(circuit.gates.len(), 1);
1648
1649 if let InterfaceGateType::RX(angle) = &circuit.gates[0].gate_type {
1650 assert_abs_diff_eq!(*angle, 0.8, epsilon = 1e-10);
1651 } else {
1652 panic!("Expected merged RX gate");
1653 }
1654 }
1655
1656 #[test]
1657 fn test_circuit_interface_creation() {
1658 let config = CircuitInterfaceConfig::default();
1659 let _interface =
1660 CircuitInterface::new(config.clone()).expect("should create circuit interface");
1661 assert!(config.auto_backend_selection);
1662 assert!(config.enable_optimization);
1663 assert_eq!(config.max_statevector_qubits, 25);
1664 }
1665
1666 #[test]
1667 fn test_test_circuit_creation() {
1668 let ghz_circuit = CircuitInterfaceUtils::create_test_circuit("ghz", 3);
1669 assert_eq!(ghz_circuit.num_qubits, 3);
1670 assert_eq!(ghz_circuit.gates.len(), 3); let qft_circuit = CircuitInterfaceUtils::create_test_circuit("qft", 3);
1673 assert!(qft_circuit.gates.len() > 3); }
1675
1676 #[test]
1677 fn test_circuit_metadata() {
1678 let circuit = CircuitInterfaceUtils::create_test_circuit("ghz", 4);
1679 assert_eq!(circuit.metadata.depth, 4); assert_eq!(circuit.metadata.two_qubit_gates, 3);
1681 }
1682
1683 #[test]
1684 fn test_clifford_detection() {
1685 let mut circuit = InterfaceCircuit::new(2, 0);
1686 circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![0]));
1687 circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![0, 1]));
1688 circuit.add_gate(InterfaceGate::new(InterfaceGateType::S, vec![1]));
1689
1690 let config = CircuitInterfaceConfig::default();
1691 let interface = CircuitInterface::new(config).expect("should create circuit interface");
1692 assert!(interface.is_clifford_circuit(&circuit));
1693
1694 circuit.add_gate(InterfaceGate::new(InterfaceGateType::T, vec![0]));
1696 assert!(!interface.is_clifford_circuit(&circuit));
1697 }
1698}