1use crate::{FxGraph, Node, Result};
8use scirs2_core::random::thread_rng;
9use scirs2_core::Complex64;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::sync::{Arc, Mutex};
13use torsh_core::error::TorshError;
14
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
17pub enum QuantumBackend {
18 Qiskit { backend_name: String, shots: u32 },
20 Cirq {
22 simulator_type: String,
23 noise_model: Option<String>,
24 },
25 LocalSimulator {
27 num_qubits: u8,
28 precision: QuantumPrecision,
29 },
30 CloudQuantum {
32 provider: CloudProvider,
33 device_name: String,
34 credentials: String,
35 },
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
40pub enum CloudProvider {
41 IBM,
42 Google,
43 Rigetti,
44 IonQ,
45 Honeywell,
46 AWS,
47 Azure,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
52pub enum QuantumPrecision {
53 Single,
54 Double,
55 Arbitrary { bits: u32 },
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
60pub enum QuantumGate {
61 X {
63 qubit: u8,
64 },
65 Y {
66 qubit: u8,
67 },
68 Z {
69 qubit: u8,
70 },
71 H {
72 qubit: u8,
73 },
74 S {
75 qubit: u8,
76 },
77 T {
78 qubit: u8,
79 },
80 RX {
82 qubit: u8,
83 angle: f64,
84 },
85 RY {
86 qubit: u8,
87 angle: f64,
88 },
89 RZ {
90 qubit: u8,
91 angle: f64,
92 },
93 CNOT {
95 control: u8,
96 target: u8,
97 },
98 CZ {
99 control: u8,
100 target: u8,
101 },
102 SWAP {
103 qubit1: u8,
104 qubit2: u8,
105 },
106 Toffoli {
108 control1: u8,
109 control2: u8,
110 target: u8,
111 },
112 Custom {
114 name: String,
115 qubits: Vec<u8>,
116 parameters: Vec<f64>,
117 },
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct QuantumCircuit {
123 pub num_qubits: u8,
124 pub gates: Vec<QuantumGate>,
125 pub measurements: Vec<ClassicalMeasurement>,
126 pub parameters: HashMap<String, f64>,
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct ClassicalMeasurement {
132 pub qubit: u8,
133 pub classical_bit: u8,
134 pub measurement_basis: MeasurementBasis,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
139pub enum MeasurementBasis {
140 Computational,
141 Hadamard,
142 Custom { angles: Vec<f64> },
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct QuantumExecutionResult {
148 pub shots: u32,
149 pub counts: HashMap<String, u32>,
150 pub probabilities: HashMap<String, f64>,
151 pub execution_time: std::time::Duration,
152 pub quantum_volume: Option<f64>,
153 pub fidelity: Option<f64>,
154}
155
156#[derive(Debug, Clone)]
158pub struct HybridWorkflow {
159 pub classical_graph: FxGraph,
160 pub quantum_circuits: Vec<QuantumCircuit>,
161 pub integration_points: Vec<IntegrationPoint>,
162 pub optimization_strategy: HybridOptimizationStrategy,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct IntegrationPoint {
168 pub classical_node: String,
169 pub quantum_circuit_index: usize,
170 pub data_transfer: DataTransferType,
171 pub synchronization: SynchronizationType,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
176pub enum DataTransferType {
177 ParameterUpdate { parameters: Vec<String> },
178 StatePreparation { encoding: StateEncoding },
179 MeasurementFeedback { processing: String },
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
184pub enum StateEncoding {
185 Amplitude,
186 Angle,
187 Binary,
188 Custom { method: String },
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
193pub enum SynchronizationType {
194 Sequential,
195 Parallel,
196 Conditional { condition: String },
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
201pub enum HybridOptimizationStrategy {
202 VQE, QAOA, QGAN, QML, Custom { algorithm: String },
207}
208
209pub struct QuantumComputingBackend {
211 backend: QuantumBackend,
212 circuits: Vec<QuantumCircuit>,
213 execution_history: Arc<Mutex<Vec<QuantumExecutionResult>>>,
214 error_mitigation: ErrorMitigation,
215 #[allow(dead_code)]
216 noise_models: HashMap<String, NoiseModel>,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct ErrorMitigation {
222 pub zero_noise_extrapolation: bool,
223 pub readout_error_mitigation: bool,
224 pub symmetry_verification: bool,
225 pub probabilistic_error_cancellation: bool,
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct NoiseModel {
231 pub depolarizing_error: f64,
232 pub bit_flip_error: f64,
233 pub phase_flip_error: f64,
234 pub thermal_relaxation: Option<ThermalRelaxation>,
235 pub gate_errors: HashMap<String, f64>,
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
240pub struct ThermalRelaxation {
241 pub t1: f64, pub t2: f64, pub temperature: f64,
244}
245
246impl QuantumComputingBackend {
247 pub fn new(backend: QuantumBackend) -> Self {
249 Self {
250 backend,
251 circuits: Vec::new(),
252 execution_history: Arc::new(Mutex::new(Vec::new())),
253 error_mitigation: ErrorMitigation::default(),
254 noise_models: HashMap::new(),
255 }
256 }
257
258 pub fn add_circuit(&mut self, circuit: QuantumCircuit) -> Result<usize> {
260 let circuit_id = self.circuits.len();
261 self.circuits.push(circuit);
262 Ok(circuit_id)
263 }
264
265 pub fn execute_circuit(&self, circuit_id: usize, shots: u32) -> Result<QuantumExecutionResult> {
267 if circuit_id >= self.circuits.len() {
268 return Err(TorshError::IndexError {
269 index: circuit_id,
270 size: self.circuits.len(),
271 });
272 }
273
274 let circuit = &self.circuits[circuit_id];
275 let _start_time = std::time::Instant::now();
276
277 let result = match &self.backend {
279 QuantumBackend::LocalSimulator {
280 num_qubits,
281 precision: _,
282 } => self.simulate_locally(circuit, shots, *num_qubits)?,
283 QuantumBackend::Qiskit {
284 backend_name: _,
285 shots: backend_shots,
286 } => self.execute_qiskit(circuit, shots.min(*backend_shots))?,
287 QuantumBackend::Cirq {
288 simulator_type: _,
289 noise_model: _,
290 } => self.execute_cirq(circuit, shots)?,
291 QuantumBackend::CloudQuantum {
292 provider: _,
293 device_name: _,
294 credentials: _,
295 } => self.execute_cloud(circuit, shots)?,
296 };
297
298 let mut history = self
300 .execution_history
301 .lock()
302 .expect("lock should not be poisoned");
303 history.push(result.clone());
304
305 Ok(result)
306 }
307
308 pub fn create_hybrid_workflow(
310 &self,
311 classical_graph: FxGraph,
312 quantum_circuits: Vec<QuantumCircuit>,
313 strategy: HybridOptimizationStrategy,
314 ) -> Result<HybridWorkflow> {
315 let integration_points =
316 self.analyze_integration_points(&classical_graph, &quantum_circuits)?;
317
318 Ok(HybridWorkflow {
319 classical_graph,
320 quantum_circuits,
321 integration_points,
322 optimization_strategy: strategy,
323 })
324 }
325
326 pub fn optimize_circuits(&mut self) -> Result<()> {
328 let mut optimized_circuits = Vec::new();
329 for circuit in &self.circuits {
330 let mut optimized_circuit = circuit.clone();
331 Self::apply_quantum_optimizations_static(&mut optimized_circuit)?;
332 optimized_circuits.push(optimized_circuit);
333 }
334 self.circuits = optimized_circuits;
335 Ok(())
336 }
337
338 pub fn apply_error_mitigation(&self, result: &mut QuantumExecutionResult) -> Result<()> {
340 if self.error_mitigation.readout_error_mitigation {
341 self.mitigate_readout_errors(result)?;
342 }
343
344 if self.error_mitigation.zero_noise_extrapolation {
345 self.apply_zero_noise_extrapolation(result)?;
346 }
347
348 Ok(())
349 }
350
351 fn simulate_locally(
362 &self,
363 circuit: &QuantumCircuit,
364 shots: u32,
365 num_qubits: u8,
366 ) -> Result<QuantumExecutionResult> {
367 let start_time = std::time::Instant::now();
368
369 if num_qubits == 0 {
370 return Err(TorshError::InvalidArgument(
371 "cannot simulate a circuit with zero qubits".to_string(),
372 ));
373 }
374 const MAX_SIMULATED_QUBITS: u8 = 24;
377 if num_qubits > MAX_SIMULATED_QUBITS {
378 return Err(TorshError::InvalidArgument(format!(
379 "state-vector simulation supports at most {MAX_SIMULATED_QUBITS} qubits, \
380 but the circuit declares {num_qubits} (a dense state vector would need \
381 2^{num_qubits} complex amplitudes)"
382 )));
383 }
384
385 let state = StateVector::from_circuit(circuit, num_qubits)?;
386 let probabilities_per_outcome = state.measurement_distribution();
387
388 let counts = Self::sample_counts(&probabilities_per_outcome, num_qubits, shots);
390
391 let mut probabilities = HashMap::new();
394 if shots > 0 {
395 for (bitstring, count) in &counts {
396 probabilities.insert(bitstring.clone(), *count as f64 / shots as f64);
397 }
398 }
399
400 Ok(QuantumExecutionResult {
401 shots,
402 counts,
403 probabilities,
404 execution_time: start_time.elapsed(),
405 quantum_volume: Some(2.0_f64.powi(num_qubits as i32)),
407 fidelity: Some(1.0),
409 })
410 }
411
412 fn sample_counts(probabilities: &[f64], num_qubits: u8, shots: u32) -> HashMap<String, u32> {
415 let mut counts: HashMap<String, u32> = HashMap::new();
416 if shots == 0 || probabilities.is_empty() {
417 return counts;
418 }
419
420 let width = num_qubits as usize;
421 let mut rng = thread_rng();
422
423 for _ in 0..shots {
424 let sample: f64 = rng.gen_range(0.0..1.0);
425 let mut cumulative = 0.0;
426 let mut chosen = probabilities.len() - 1;
427 for (index, &probability) in probabilities.iter().enumerate() {
428 cumulative += probability;
429 if sample < cumulative {
430 chosen = index;
431 break;
432 }
433 }
434 let bitstring = format!("{chosen:0width$b}");
435 *counts.entry(bitstring).or_insert(0) += 1;
436 }
437
438 counts
439 }
440
441 fn execute_qiskit(
442 &self,
443 _circuit: &QuantumCircuit,
444 _shots: u32,
445 ) -> Result<QuantumExecutionResult> {
446 Err(TorshError::NotImplemented(
452 "Qiskit backend execution requires a Qiskit/IBM Quantum transport that is not \
453 linked into torsh-fx; use QuantumBackend::LocalSimulator for an in-process run"
454 .to_string(),
455 ))
456 }
457
458 fn execute_cirq(
459 &self,
460 _circuit: &QuantumCircuit,
461 _shots: u32,
462 ) -> Result<QuantumExecutionResult> {
463 Err(TorshError::NotImplemented(
466 "Cirq backend execution requires a Cirq transport that is not linked into \
467 torsh-fx; use QuantumBackend::LocalSimulator for an in-process run"
468 .to_string(),
469 ))
470 }
471
472 fn execute_cloud(
473 &self,
474 _circuit: &QuantumCircuit,
475 _shots: u32,
476 ) -> Result<QuantumExecutionResult> {
477 Err(TorshError::NotImplemented(
481 "cloud quantum execution requires a provider client (IBM/AWS Braket/Azure \
482 Quantum/...) that is not linked into torsh-fx; use \
483 QuantumBackend::LocalSimulator for an in-process run"
484 .to_string(),
485 ))
486 }
487
488 fn analyze_integration_points(
489 &self,
490 classical_graph: &FxGraph,
491 _quantum_circuits: &[QuantumCircuit],
492 ) -> Result<Vec<IntegrationPoint>> {
493 let mut integration_points = Vec::new();
494
495 for (node_idx, node) in classical_graph.nodes() {
497 match node {
498 Node::Call(op_name, _) if self.is_quantum_suitable_operation(op_name) => {
499 let integration_point = IntegrationPoint {
500 classical_node: format!("node_{}", node_idx.index()),
501 quantum_circuit_index: 0, data_transfer: DataTransferType::ParameterUpdate {
503 parameters: vec!["theta".to_string(), "phi".to_string()],
504 },
505 synchronization: SynchronizationType::Sequential,
506 };
507 integration_points.push(integration_point);
508 }
509 _ => {}
510 }
511 }
512
513 Ok(integration_points)
514 }
515
516 fn is_quantum_suitable_operation(&self, op_name: &str) -> bool {
517 matches!(
518 op_name,
519 "matmul" | "softmax" | "attention" | "optimization" | "sampling"
520 )
521 }
522
523 fn apply_quantum_optimizations_static(circuit: &mut QuantumCircuit) -> Result<()> {
524 Self::merge_rotation_gates(circuit);
526 Self::cancel_adjacent_gates(circuit);
527 Self::optimize_gate_ordering(circuit);
528 Ok(())
529 }
530
531 fn merge_rotation_gates(circuit: &mut QuantumCircuit) {
532 let mut i = 0;
537 while i + 1 < circuit.gates.len() {
538 let merged = match (&circuit.gates[i], &circuit.gates[i + 1]) {
540 (
541 QuantumGate::RZ {
542 qubit: q1,
543 angle: a1,
544 },
545 QuantumGate::RZ {
546 qubit: q2,
547 angle: a2,
548 },
549 ) if q1 == q2 => Some(QuantumGate::RZ {
550 qubit: *q1,
551 angle: a1 + a2,
552 }),
553 (
554 QuantumGate::RX {
555 qubit: q1,
556 angle: a1,
557 },
558 QuantumGate::RX {
559 qubit: q2,
560 angle: a2,
561 },
562 ) if q1 == q2 => Some(QuantumGate::RX {
563 qubit: *q1,
564 angle: a1 + a2,
565 }),
566 (
567 QuantumGate::RY {
568 qubit: q1,
569 angle: a1,
570 },
571 QuantumGate::RY {
572 qubit: q2,
573 angle: a2,
574 },
575 ) if q1 == q2 => Some(QuantumGate::RY {
576 qubit: *q1,
577 angle: a1 + a2,
578 }),
579 _ => None,
580 };
581
582 if let Some(merged_gate) = merged {
583 circuit.gates[i] = merged_gate;
587 circuit.gates.remove(i + 1);
588 } else {
589 i += 1;
590 }
591 }
592 }
593
594 fn cancel_adjacent_gates(circuit: &mut QuantumCircuit) {
595 let mut i = 0;
599 while i + 1 < circuit.gates.len() {
600 let can_cancel = match (&circuit.gates[i], &circuit.gates.get(i + 1)) {
601 (QuantumGate::X { qubit: q1 }, Some(QuantumGate::X { qubit: q2 })) => q1 == q2,
603 (QuantumGate::Y { qubit: q1 }, Some(QuantumGate::Y { qubit: q2 })) => q1 == q2,
604 (QuantumGate::Z { qubit: q1 }, Some(QuantumGate::Z { qubit: q2 })) => q1 == q2,
605 (QuantumGate::H { qubit: q1 }, Some(QuantumGate::H { qubit: q2 })) => q1 == q2,
606
607 (
609 QuantumGate::CNOT {
610 control: c1,
611 target: t1,
612 },
613 Some(QuantumGate::CNOT {
614 control: c2,
615 target: t2,
616 }),
617 ) => c1 == c2 && t1 == t2,
618
619 (
620 QuantumGate::SWAP {
621 qubit1: q1a,
622 qubit2: q1b,
623 },
624 Some(QuantumGate::SWAP {
625 qubit1: q2a,
626 qubit2: q2b,
627 }),
628 ) => (q1a == q2a && q1b == q2b) || (q1a == q2b && q1b == q2a),
629
630 _ => false,
631 };
632
633 if can_cancel {
634 circuit.gates.remove(i + 1);
636 circuit.gates.remove(i);
637 } else {
639 i += 1;
640 }
641 }
642 }
643
644 fn optimize_gate_ordering(circuit: &mut QuantumCircuit) {
645 let mut qubit_usage: Vec<Vec<usize>> = vec![Vec::new(); circuit.num_qubits as usize];
650
651 for (gate_idx, gate) in circuit.gates.iter().enumerate() {
652 match gate {
653 QuantumGate::X { qubit }
654 | QuantumGate::Y { qubit }
655 | QuantumGate::Z { qubit }
656 | QuantumGate::H { qubit }
657 | QuantumGate::S { qubit }
658 | QuantumGate::T { qubit }
659 | QuantumGate::RX { qubit, .. }
660 | QuantumGate::RY { qubit, .. }
661 | QuantumGate::RZ { qubit, .. } => {
662 qubit_usage[*qubit as usize].push(gate_idx);
663 }
664 QuantumGate::CNOT { control, target } | QuantumGate::CZ { control, target } => {
665 qubit_usage[*control as usize].push(gate_idx);
666 qubit_usage[*target as usize].push(gate_idx);
667 }
668 QuantumGate::SWAP { qubit1, qubit2 } => {
669 qubit_usage[*qubit1 as usize].push(gate_idx);
670 qubit_usage[*qubit2 as usize].push(gate_idx);
671 }
672 QuantumGate::Toffoli {
673 control1,
674 control2,
675 target,
676 } => {
677 qubit_usage[*control1 as usize].push(gate_idx);
678 qubit_usage[*control2 as usize].push(gate_idx);
679 qubit_usage[*target as usize].push(gate_idx);
680 }
681 _ => {}
682 }
683 }
684
685 }
688
689 fn mitigate_readout_errors(&self, result: &mut QuantumExecutionResult) -> Result<()> {
690 if !self.error_mitigation.readout_error_mitigation {
694 return Ok(());
695 }
696
697 let error_rate = 0.02; let total_shots = result.shots;
709 let correction_factor = 1.0 / (1.0 - 2.0 * error_rate);
710
711 for count in result.counts.values_mut() {
712 let corrected = (*count as f64 * correction_factor).round() as u32;
713 *count = corrected.min(total_shots);
714 }
715
716 let new_total: u32 = result.counts.values().sum();
718 if new_total > 0 {
719 for (key, count) in &result.counts {
720 let prob = *count as f64 / new_total as f64;
721 result.probabilities.insert(key.clone(), prob);
722 }
723 }
724
725 Ok(())
726 }
727
728 fn apply_zero_noise_extrapolation(&self, result: &mut QuantumExecutionResult) -> Result<()> {
729 if !self.error_mitigation.zero_noise_extrapolation {
733 return Ok(());
734 }
735
736 let noise_factor = 0.95; for count in result.counts.values_mut() {
747 *count = (*count as f64 * noise_factor).round() as u32;
748 }
749
750 let new_total: u32 = result.counts.values().sum();
752 if new_total > 0 {
753 for (key, count) in &result.counts {
754 let prob = *count as f64 / new_total as f64;
755 result.probabilities.insert(key.clone(), prob);
756 }
757 }
758
759 if let Some(fidelity) = result.fidelity.as_mut() {
761 *fidelity /= noise_factor;
762 *fidelity = fidelity.min(1.0); }
764
765 Ok(())
766 }
767}
768
769struct StateVector {
777 amplitudes: Vec<Complex64>,
778 num_qubits: u8,
779}
780
781impl StateVector {
782 fn from_circuit(circuit: &QuantumCircuit, num_qubits: u8) -> Result<Self> {
785 let dimension = 1usize << num_qubits;
786 let mut amplitudes = vec![Complex64::new(0.0, 0.0); dimension];
787 amplitudes[0] = Complex64::new(1.0, 0.0);
788
789 let mut state = Self {
790 amplitudes,
791 num_qubits,
792 };
793
794 for gate in &circuit.gates {
795 state.apply_gate(gate)?;
796 }
797
798 Ok(state)
799 }
800
801 fn check_qubit(&self, qubit: u8) -> Result<usize> {
803 if qubit >= self.num_qubits {
804 return Err(TorshError::InvalidArgument(format!(
805 "gate references qubit {} but the circuit only has {} qubit(s)",
806 qubit, self.num_qubits
807 )));
808 }
809 Ok(qubit as usize)
810 }
811
812 fn apply_gate(&mut self, gate: &QuantumGate) -> Result<()> {
814 match gate {
815 QuantumGate::X { qubit } => self.apply_single(*qubit, &Self::pauli_x()),
816 QuantumGate::Y { qubit } => self.apply_single(*qubit, &Self::pauli_y()),
817 QuantumGate::Z { qubit } => self.apply_single(*qubit, &Self::pauli_z()),
818 QuantumGate::H { qubit } => self.apply_single(*qubit, &Self::hadamard()),
819 QuantumGate::S { qubit } => self.apply_single(*qubit, &Self::phase_s()),
820 QuantumGate::T { qubit } => self.apply_single(*qubit, &Self::phase_t()),
821 QuantumGate::RX { qubit, angle } => self.apply_single(*qubit, &Self::rx(*angle)),
822 QuantumGate::RY { qubit, angle } => self.apply_single(*qubit, &Self::ry(*angle)),
823 QuantumGate::RZ { qubit, angle } => self.apply_single(*qubit, &Self::rz(*angle)),
824 QuantumGate::CNOT { control, target } => {
825 self.apply_controlled_single(*control, *target, &Self::pauli_x())
826 }
827 QuantumGate::CZ { control, target } => {
828 self.apply_controlled_single(*control, *target, &Self::pauli_z())
829 }
830 QuantumGate::SWAP { qubit1, qubit2 } => self.apply_swap(*qubit1, *qubit2),
831 QuantumGate::Toffoli {
832 control1,
833 control2,
834 target,
835 } => self.apply_toffoli(*control1, *control2, *target),
836 QuantumGate::Custom {
837 name,
838 qubits,
839 parameters,
840 } => Self::apply_custom(name, qubits, parameters),
841 }
842 }
843
844 fn apply_single(&mut self, qubit: u8, matrix: &[[Complex64; 2]; 2]) -> Result<()> {
846 let target = self.check_qubit(qubit)?;
847 let stride = 1usize << target;
848
849 for base in 0..self.amplitudes.len() {
850 if base & stride == 0 {
853 let partner = base | stride;
854 let a0 = self.amplitudes[base];
855 let a1 = self.amplitudes[partner];
856 self.amplitudes[base] = matrix[0][0] * a0 + matrix[0][1] * a1;
857 self.amplitudes[partner] = matrix[1][0] * a0 + matrix[1][1] * a1;
858 }
859 }
860
861 Ok(())
862 }
863
864 fn apply_controlled_single(
867 &mut self,
868 control: u8,
869 target: u8,
870 matrix: &[[Complex64; 2]; 2],
871 ) -> Result<()> {
872 let control_idx = self.check_qubit(control)?;
873 let target_idx = self.check_qubit(target)?;
874 if control_idx == target_idx {
875 return Err(TorshError::InvalidArgument(
876 "controlled gate requires distinct control and target qubits".to_string(),
877 ));
878 }
879
880 let control_mask = 1usize << control_idx;
881 let target_stride = 1usize << target_idx;
882
883 for base in 0..self.amplitudes.len() {
884 if base & control_mask == 0 {
885 continue; }
887 if base & target_stride == 0 {
888 let partner = base | target_stride;
889 let a0 = self.amplitudes[base];
890 let a1 = self.amplitudes[partner];
891 self.amplitudes[base] = matrix[0][0] * a0 + matrix[0][1] * a1;
892 self.amplitudes[partner] = matrix[1][0] * a0 + matrix[1][1] * a1;
893 }
894 }
895
896 Ok(())
897 }
898
899 fn apply_swap(&mut self, qubit1: u8, qubit2: u8) -> Result<()> {
901 let q1 = self.check_qubit(qubit1)?;
902 let q2 = self.check_qubit(qubit2)?;
903 if q1 == q2 {
904 return Ok(());
905 }
906
907 let mask1 = 1usize << q1;
908 let mask2 = 1usize << q2;
909
910 for base in 0..self.amplitudes.len() {
911 let bit1 = base & mask1 != 0;
912 let bit2 = base & mask2 != 0;
913 if bit1 && !bit2 {
915 let partner = (base & !mask1) | mask2;
916 self.amplitudes.swap(base, partner);
917 }
918 }
919
920 Ok(())
921 }
922
923 fn apply_toffoli(&mut self, control1: u8, control2: u8, target: u8) -> Result<()> {
925 let c1 = self.check_qubit(control1)?;
926 let c2 = self.check_qubit(control2)?;
927 let t = self.check_qubit(target)?;
928 if c1 == c2 || c1 == t || c2 == t {
929 return Err(TorshError::InvalidArgument(
930 "Toffoli gate requires three distinct qubits".to_string(),
931 ));
932 }
933
934 let c1_mask = 1usize << c1;
935 let c2_mask = 1usize << c2;
936 let t_stride = 1usize << t;
937
938 for base in 0..self.amplitudes.len() {
939 if base & c1_mask == 0 || base & c2_mask == 0 {
940 continue; }
942 if base & t_stride == 0 {
943 let partner = base | t_stride;
944 self.amplitudes.swap(base, partner);
945 }
946 }
947
948 Ok(())
949 }
950
951 fn apply_custom(name: &str, _qubits: &[u8], _parameters: &[f64]) -> Result<()> {
955 Err(TorshError::NotImplemented(format!(
956 "custom quantum gate '{name}' is not supported by the built-in state-vector \
957 simulator (no unitary definition is available)"
958 )))
959 }
960
961 fn measurement_distribution(&self) -> Vec<f64> {
964 self.amplitudes.iter().map(|a| a.norm_sqr()).collect()
965 }
966
967 fn pauli_x() -> [[Complex64; 2]; 2] {
970 let zero = Complex64::new(0.0, 0.0);
971 let one = Complex64::new(1.0, 0.0);
972 [[zero, one], [one, zero]]
973 }
974
975 fn pauli_y() -> [[Complex64; 2]; 2] {
976 let zero = Complex64::new(0.0, 0.0);
977 let i = Complex64::new(0.0, 1.0);
978 [[zero, -i], [i, zero]]
979 }
980
981 fn pauli_z() -> [[Complex64; 2]; 2] {
982 let zero = Complex64::new(0.0, 0.0);
983 let one = Complex64::new(1.0, 0.0);
984 [[one, zero], [zero, -one]]
985 }
986
987 fn hadamard() -> [[Complex64; 2]; 2] {
988 let inv_sqrt2 = Complex64::new(std::f64::consts::FRAC_1_SQRT_2, 0.0);
989 [[inv_sqrt2, inv_sqrt2], [inv_sqrt2, -inv_sqrt2]]
990 }
991
992 fn phase_s() -> [[Complex64; 2]; 2] {
993 let zero = Complex64::new(0.0, 0.0);
994 let one = Complex64::new(1.0, 0.0);
995 let i = Complex64::new(0.0, 1.0);
996 [[one, zero], [zero, i]]
997 }
998
999 fn phase_t() -> [[Complex64; 2]; 2] {
1000 let zero = Complex64::new(0.0, 0.0);
1001 let one = Complex64::new(1.0, 0.0);
1002 let phase = Complex64::from_polar(1.0, std::f64::consts::FRAC_PI_4);
1004 [[one, zero], [zero, phase]]
1005 }
1006
1007 fn rx(angle: f64) -> [[Complex64; 2]; 2] {
1008 let cos = Complex64::new((angle / 2.0).cos(), 0.0);
1009 let neg_i_sin = Complex64::new(0.0, -(angle / 2.0).sin());
1010 [[cos, neg_i_sin], [neg_i_sin, cos]]
1011 }
1012
1013 fn ry(angle: f64) -> [[Complex64; 2]; 2] {
1014 let cos = Complex64::new((angle / 2.0).cos(), 0.0);
1015 let sin = Complex64::new((angle / 2.0).sin(), 0.0);
1016 [[cos, -sin], [sin, cos]]
1017 }
1018
1019 fn rz(angle: f64) -> [[Complex64; 2]; 2] {
1020 let zero = Complex64::new(0.0, 0.0);
1021 let neg = Complex64::from_polar(1.0, -angle / 2.0);
1022 let pos = Complex64::from_polar(1.0, angle / 2.0);
1023 [[neg, zero], [zero, pos]]
1024 }
1025}
1026
1027impl Default for ErrorMitigation {
1028 fn default() -> Self {
1029 Self {
1030 zero_noise_extrapolation: false,
1031 readout_error_mitigation: true,
1032 symmetry_verification: false,
1033 probabilistic_error_cancellation: false,
1034 }
1035 }
1036}
1037
1038impl QuantumCircuit {
1039 pub fn new(num_qubits: u8) -> Self {
1041 Self {
1042 num_qubits,
1043 gates: Vec::new(),
1044 measurements: Vec::new(),
1045 parameters: HashMap::new(),
1046 }
1047 }
1048
1049 pub fn add_gate(&mut self, gate: QuantumGate) {
1051 self.gates.push(gate);
1052 }
1053
1054 pub fn add_measurement(&mut self, measurement: ClassicalMeasurement) {
1056 self.measurements.push(measurement);
1057 }
1058
1059 pub fn set_parameter(&mut self, name: String, value: f64) {
1061 self.parameters.insert(name, value);
1062 }
1063
1064 pub fn depth(&self) -> usize {
1066 self.gates.len()
1068 }
1069
1070 pub fn gate_counts(&self) -> HashMap<String, usize> {
1072 let mut counts = HashMap::new();
1073 for gate in &self.gates {
1074 let gate_type = match gate {
1075 QuantumGate::X { .. } => "X",
1076 QuantumGate::Y { .. } => "Y",
1077 QuantumGate::Z { .. } => "Z",
1078 QuantumGate::H { .. } => "H",
1079 QuantumGate::S { .. } => "S",
1080 QuantumGate::T { .. } => "T",
1081 QuantumGate::RX { .. } => "RX",
1082 QuantumGate::RY { .. } => "RY",
1083 QuantumGate::RZ { .. } => "RZ",
1084 QuantumGate::CNOT { .. } => "CNOT",
1085 QuantumGate::CZ { .. } => "CZ",
1086 QuantumGate::SWAP { .. } => "SWAP",
1087 QuantumGate::Toffoli { .. } => "Toffoli",
1088 QuantumGate::Custom { name, .. } => name,
1089 };
1090 *counts.entry(gate_type.to_string()).or_insert(0) += 1;
1091 }
1092 counts
1093 }
1094}
1095
1096pub fn create_local_quantum_backend(num_qubits: u8) -> QuantumComputingBackend {
1100 QuantumComputingBackend::new(QuantumBackend::LocalSimulator {
1101 num_qubits,
1102 precision: QuantumPrecision::Double,
1103 })
1104}
1105
1106pub fn create_qiskit_backend(backend_name: String, shots: u32) -> QuantumComputingBackend {
1108 QuantumComputingBackend::new(QuantumBackend::Qiskit {
1109 backend_name,
1110 shots,
1111 })
1112}
1113
1114pub fn create_vqe_circuit(num_qubits: u8, depth: usize) -> QuantumCircuit {
1116 let mut circuit = QuantumCircuit::new(num_qubits);
1117
1118 for layer in 0..depth {
1120 for qubit in 0..num_qubits {
1122 circuit.add_gate(QuantumGate::RY {
1123 qubit,
1124 angle: std::f64::consts::PI / 4.0, });
1126 }
1127
1128 for qubit in 0..num_qubits - 1 {
1130 circuit.add_gate(QuantumGate::CNOT {
1131 control: qubit,
1132 target: qubit + 1,
1133 });
1134 }
1135
1136 circuit.set_parameter(format!("theta_{}", layer), 0.0);
1138 }
1139
1140 for qubit in 0..num_qubits {
1142 circuit.add_measurement(ClassicalMeasurement {
1143 qubit,
1144 classical_bit: qubit,
1145 measurement_basis: MeasurementBasis::Computational,
1146 });
1147 }
1148
1149 circuit
1150}
1151
1152pub fn create_qaoa_circuit(num_qubits: u8, p: usize) -> QuantumCircuit {
1154 let mut circuit = QuantumCircuit::new(num_qubits);
1155
1156 for qubit in 0..num_qubits {
1158 circuit.add_gate(QuantumGate::H { qubit });
1159 }
1160
1161 for layer in 0..p {
1163 for qubit in 0..num_qubits - 1 {
1165 circuit.add_gate(QuantumGate::CNOT {
1166 control: qubit,
1167 target: qubit + 1,
1168 });
1169 circuit.add_gate(QuantumGate::RZ {
1170 qubit: qubit + 1,
1171 angle: 1.0, });
1173 circuit.add_gate(QuantumGate::CNOT {
1174 control: qubit,
1175 target: qubit + 1,
1176 });
1177 }
1178
1179 for qubit in 0..num_qubits {
1181 circuit.add_gate(QuantumGate::RX {
1182 qubit,
1183 angle: 1.0, });
1185 }
1186
1187 circuit.set_parameter(format!("gamma_{}", layer), 1.0);
1188 circuit.set_parameter(format!("beta_{}", layer), 1.0);
1189 }
1190
1191 for qubit in 0..num_qubits {
1193 circuit.add_measurement(ClassicalMeasurement {
1194 qubit,
1195 classical_bit: qubit,
1196 measurement_basis: MeasurementBasis::Computational,
1197 });
1198 }
1199
1200 circuit
1201}
1202
1203pub fn integrate_quantum_computing(
1205 graph: FxGraph,
1206 quantum_backend: &mut QuantumComputingBackend,
1207 strategy: HybridOptimizationStrategy,
1208) -> Result<HybridWorkflow> {
1209 let circuits = match strategy {
1211 HybridOptimizationStrategy::VQE => {
1212 vec![create_vqe_circuit(4, 3)]
1213 }
1214 HybridOptimizationStrategy::QAOA => {
1215 vec![create_qaoa_circuit(4, 2)]
1216 }
1217 HybridOptimizationStrategy::QML => {
1218 vec![create_vqe_circuit(6, 2)] }
1220 _ => {
1221 vec![QuantumCircuit::new(2)] }
1223 };
1224
1225 for circuit in &circuits {
1227 quantum_backend.add_circuit(circuit.clone())?;
1228 }
1229
1230 quantum_backend.create_hybrid_workflow(graph, circuits, strategy)
1232}
1233
1234#[cfg(test)]
1235mod tests {
1236 use super::*;
1237
1238 #[test]
1239 fn test_quantum_backend_creation() {
1240 let backend = create_local_quantum_backend(4);
1241 assert_eq!(backend.circuits.len(), 0);
1242 }
1243
1244 #[test]
1245 fn test_quantum_circuit_creation() {
1246 let mut circuit = QuantumCircuit::new(2);
1247 circuit.add_gate(QuantumGate::H { qubit: 0 });
1248 circuit.add_gate(QuantumGate::CNOT {
1249 control: 0,
1250 target: 1,
1251 });
1252
1253 assert_eq!(circuit.num_qubits, 2);
1254 assert_eq!(circuit.gates.len(), 2);
1255 assert_eq!(circuit.depth(), 2);
1256 }
1257
1258 #[test]
1259 fn test_circuit_execution() {
1260 let mut backend = create_local_quantum_backend(2);
1261 let circuit = QuantumCircuit::new(2);
1262 let circuit_id = backend.add_circuit(circuit).unwrap();
1263
1264 let result = backend.execute_circuit(circuit_id, 1000).unwrap();
1265 assert_eq!(result.shots, 1000);
1266 assert!(!result.counts.is_empty());
1267 }
1268
1269 #[test]
1270 fn test_vqe_circuit_creation() {
1271 let circuit = create_vqe_circuit(4, 2);
1272 assert_eq!(circuit.num_qubits, 4);
1273 assert!(!circuit.gates.is_empty());
1274 assert!(!circuit.parameters.is_empty());
1275 }
1276
1277 #[test]
1278 fn test_qaoa_circuit_creation() {
1279 let circuit = create_qaoa_circuit(3, 2);
1280 assert_eq!(circuit.num_qubits, 3);
1281 assert!(!circuit.gates.is_empty());
1282 assert!(circuit.parameters.contains_key("gamma_0"));
1283 assert!(circuit.parameters.contains_key("beta_0"));
1284 }
1285
1286 #[test]
1287 fn test_gate_counting() {
1288 let mut circuit = QuantumCircuit::new(2);
1289 circuit.add_gate(QuantumGate::H { qubit: 0 });
1290 circuit.add_gate(QuantumGate::H { qubit: 1 });
1291 circuit.add_gate(QuantumGate::CNOT {
1292 control: 0,
1293 target: 1,
1294 });
1295
1296 let counts = circuit.gate_counts();
1297 assert_eq!(counts.get("H"), Some(&2));
1298 assert_eq!(counts.get("CNOT"), Some(&1));
1299 }
1300
1301 #[test]
1302 fn test_hybrid_workflow_creation() {
1303 let graph = FxGraph::new();
1304 let backend = create_local_quantum_backend(4);
1305
1306 let workflow = backend.create_hybrid_workflow(
1307 graph,
1308 vec![create_vqe_circuit(4, 2)],
1309 HybridOptimizationStrategy::VQE,
1310 );
1311
1312 assert!(workflow.is_ok());
1313 }
1314
1315 #[test]
1316 fn test_cloud_providers() {
1317 let providers = vec![
1318 CloudProvider::IBM,
1319 CloudProvider::Google,
1320 CloudProvider::Rigetti,
1321 CloudProvider::IonQ,
1322 CloudProvider::Honeywell,
1323 CloudProvider::AWS,
1324 CloudProvider::Azure,
1325 ];
1326
1327 assert_eq!(providers.len(), 7);
1328 }
1329
1330 #[test]
1331 fn test_error_mitigation() {
1332 let backend = create_local_quantum_backend(2);
1333 let mut result = QuantumExecutionResult {
1334 shots: 1000,
1335 counts: HashMap::new(),
1336 probabilities: HashMap::new(),
1337 execution_time: std::time::Duration::from_millis(100),
1338 quantum_volume: Some(4.0),
1339 fidelity: Some(0.95),
1340 };
1341
1342 assert!(backend.apply_error_mitigation(&mut result).is_ok());
1343 }
1344
1345 #[test]
1346 fn test_state_vector_x_gate_flips_qubit() {
1347 let mut circuit = QuantumCircuit::new(1);
1349 circuit.add_gate(QuantumGate::X { qubit: 0 });
1350 let state = StateVector::from_circuit(&circuit, 1).expect("simulation should succeed");
1351 let dist = state.measurement_distribution();
1352 assert!((dist[0]).abs() < 1e-12, "|0> probability should be ~0");
1353 assert!(
1354 (dist[1] - 1.0).abs() < 1e-12,
1355 "|1> probability should be ~1"
1356 );
1357 }
1358
1359 #[test]
1360 fn test_state_vector_hadamard_uniform_superposition() {
1361 let mut circuit = QuantumCircuit::new(1);
1363 circuit.add_gate(QuantumGate::H { qubit: 0 });
1364 let state = StateVector::from_circuit(&circuit, 1).expect("simulation should succeed");
1365 let dist = state.measurement_distribution();
1366 assert!((dist[0] - 0.5).abs() < 1e-12);
1367 assert!((dist[1] - 0.5).abs() < 1e-12);
1368 }
1369
1370 #[test]
1371 fn test_state_vector_bell_state() {
1372 let mut circuit = QuantumCircuit::new(2);
1376 circuit.add_gate(QuantumGate::H { qubit: 0 });
1377 circuit.add_gate(QuantumGate::CNOT {
1378 control: 0,
1379 target: 1,
1380 });
1381 let state = StateVector::from_circuit(&circuit, 2).expect("simulation should succeed");
1382 let dist = state.measurement_distribution();
1383 assert!((dist[0b00] - 0.5).abs() < 1e-12);
1384 assert!((dist[0b11] - 0.5).abs() < 1e-12);
1385 assert!(dist[0b01].abs() < 1e-12);
1386 assert!(dist[0b10].abs() < 1e-12);
1387
1388 let total: f64 = dist.iter().sum();
1390 assert!((total - 1.0).abs() < 1e-12);
1391 }
1392
1393 #[test]
1394 fn test_state_vector_swap() {
1395 let mut circuit = QuantumCircuit::new(2);
1397 circuit.add_gate(QuantumGate::X { qubit: 0 });
1398 circuit.add_gate(QuantumGate::SWAP {
1399 qubit1: 0,
1400 qubit2: 1,
1401 });
1402 let state = StateVector::from_circuit(&circuit, 2).expect("simulation should succeed");
1403 let dist = state.measurement_distribution();
1404 assert!((dist[0b10] - 1.0).abs() < 1e-12);
1406 }
1407
1408 #[test]
1409 fn test_state_vector_toffoli() {
1410 let mut circuit = QuantumCircuit::new(3);
1412 circuit.add_gate(QuantumGate::X { qubit: 0 });
1413 circuit.add_gate(QuantumGate::X { qubit: 1 });
1414 circuit.add_gate(QuantumGate::Toffoli {
1415 control1: 0,
1416 control2: 1,
1417 target: 2,
1418 });
1419 let state = StateVector::from_circuit(&circuit, 3).expect("simulation should succeed");
1420 let dist = state.measurement_distribution();
1421 assert!((dist[0b111] - 1.0).abs() < 1e-12);
1423 }
1424
1425 #[test]
1426 fn test_state_vector_rotation_norm_preserved() {
1427 let mut circuit = QuantumCircuit::new(1);
1429 circuit.add_gate(QuantumGate::RY {
1430 qubit: 0,
1431 angle: 0.73,
1432 });
1433 let state = StateVector::from_circuit(&circuit, 1).expect("simulation should succeed");
1434 let total: f64 = state.measurement_distribution().iter().sum();
1435 assert!((total - 1.0).abs() < 1e-12);
1436 }
1437
1438 #[test]
1439 fn test_simulate_locally_sampling_matches_distribution() {
1440 let backend = create_local_quantum_backend(2);
1443 let mut circuit = QuantumCircuit::new(2);
1444 circuit.add_gate(QuantumGate::H { qubit: 0 });
1445 circuit.add_gate(QuantumGate::CNOT {
1446 control: 0,
1447 target: 1,
1448 });
1449 let result = backend
1450 .simulate_locally(&circuit, 4000, 2)
1451 .expect("simulation should succeed");
1452
1453 assert_eq!(result.shots, 4000);
1454 assert_eq!(result.fidelity, Some(1.0));
1455 assert!(!result.counts.contains_key("01"));
1457 assert!(!result.counts.contains_key("10"));
1458
1459 let prob_sum: f64 = result.probabilities.values().sum();
1461 assert!((prob_sum - 1.0).abs() < 1e-9);
1462
1463 let p00 = result.probabilities.get("00").copied().unwrap_or(0.0);
1465 let p11 = result.probabilities.get("11").copied().unwrap_or(0.0);
1466 assert!(p00 > 0.4 && p00 < 0.6, "p00 = {p00}");
1467 assert!(p11 > 0.4 && p11 < 0.6, "p11 = {p11}");
1468 }
1469
1470 #[test]
1471 fn test_custom_gate_is_rejected() {
1472 let mut circuit = QuantumCircuit::new(1);
1474 circuit.add_gate(QuantumGate::Custom {
1475 name: "mystery".to_string(),
1476 qubits: vec![0],
1477 parameters: vec![],
1478 });
1479 let result = StateVector::from_circuit(&circuit, 1);
1480 assert!(result.is_err());
1481 }
1482
1483 #[test]
1484 fn test_out_of_range_qubit_is_rejected() {
1485 let mut circuit = QuantumCircuit::new(1);
1486 circuit.add_gate(QuantumGate::X { qubit: 3 });
1487 let result = StateVector::from_circuit(&circuit, 1);
1488 assert!(result.is_err());
1489 }
1490
1491 #[test]
1492 fn test_merge_rotation_gates_combines_angles() {
1493 let mut circuit = QuantumCircuit::new(1);
1496 circuit.add_gate(QuantumGate::RZ {
1497 qubit: 0,
1498 angle: 0.3,
1499 });
1500 circuit.add_gate(QuantumGate::RZ {
1501 qubit: 0,
1502 angle: 0.4,
1503 });
1504 QuantumComputingBackend::merge_rotation_gates(&mut circuit);
1505 assert_eq!(circuit.gates.len(), 1);
1506 match &circuit.gates[0] {
1507 QuantumGate::RZ { qubit, angle } => {
1508 assert_eq!(*qubit, 0);
1509 assert!((angle - 0.7).abs() < 1e-12);
1510 }
1511 other => panic!("expected merged RZ gate, got {other:?}"),
1512 }
1513 }
1514
1515 #[test]
1516 fn test_external_backends_return_honest_errors() {
1517 let circuit = QuantumCircuit::new(2);
1520
1521 let mut qiskit = create_qiskit_backend("aer".to_string(), 1024);
1522 let circuit_id = qiskit.add_circuit(circuit.clone()).expect("add circuit");
1523 assert!(qiskit.execute_circuit(circuit_id, 1024).is_err());
1524
1525 let mut cirq = QuantumComputingBackend::new(QuantumBackend::Cirq {
1526 simulator_type: "density_matrix".to_string(),
1527 noise_model: None,
1528 });
1529 let cirq_id = cirq.add_circuit(circuit.clone()).expect("add circuit");
1530 assert!(cirq.execute_circuit(cirq_id, 1024).is_err());
1531
1532 let mut cloud = QuantumComputingBackend::new(QuantumBackend::CloudQuantum {
1533 provider: CloudProvider::IBM,
1534 device_name: "ibmq".to_string(),
1535 credentials: "token".to_string(),
1536 });
1537 let cloud_id = cloud.add_circuit(circuit).expect("add circuit");
1538 assert!(cloud.execute_circuit(cloud_id, 1024).is_err());
1539 }
1540}