quantrs2_sim/qml/
circuit.rs

1//! Parameterized quantum circuits for machine learning applications.
2//!
3//! This module provides parameterized quantum circuit structures with
4//! hardware-aware optimizations for different quantum computing architectures.
5
6use scirs2_core::ndarray::{Array1, Array2};
7use std::collections::HashMap;
8
9use super::config::HardwareArchitecture;
10use crate::circuit_interfaces::{InterfaceCircuit, InterfaceGateType};
11
12/// Parameterized quantum circuit for machine learning
13#[derive(Debug, Clone)]
14pub struct ParameterizedQuantumCircuit {
15    /// Circuit structure
16    pub circuit: InterfaceCircuit,
17    /// Parameter vector
18    pub parameters: Array1<f64>,
19    /// Parameter names for identification
20    pub parameter_names: Vec<String>,
21    /// Gate-to-parameter mapping
22    pub gate_parameter_map: HashMap<usize, Vec<usize>>,
23    /// Hardware-specific optimizations
24    pub hardware_optimizations: HardwareOptimizations,
25}
26
27/// Hardware-specific optimizations
28#[derive(Debug, Clone)]
29pub struct HardwareOptimizations {
30    /// Connectivity graph
31    pub connectivity_graph: Array2<bool>,
32    /// Gate fidelities
33    pub gate_fidelities: HashMap<String, f64>,
34    /// Decoherence times
35    pub decoherence_times: Array1<f64>,
36    /// Gate times
37    pub gate_times: HashMap<String, f64>,
38    /// Crosstalk matrix
39    pub crosstalk_matrix: Array2<f64>,
40}
41
42impl ParameterizedQuantumCircuit {
43    /// Create a new parameterized quantum circuit
44    #[must_use]
45    pub fn new(
46        circuit: InterfaceCircuit,
47        parameters: Array1<f64>,
48        parameter_names: Vec<String>,
49        hardware_architecture: HardwareArchitecture,
50    ) -> Self {
51        let num_qubits = circuit.num_qubits;
52        let hardware_optimizations =
53            HardwareOptimizations::for_hardware(hardware_architecture, num_qubits);
54
55        Self {
56            circuit,
57            parameters,
58            parameter_names,
59            gate_parameter_map: HashMap::new(),
60            hardware_optimizations,
61        }
62    }
63
64    /// Update circuit parameters
65    pub fn update_parameters(&mut self, new_parameters: Array1<f64>) -> Result<(), String> {
66        if new_parameters.len() != self.parameters.len() {
67            return Err(format!(
68                "Parameter count mismatch: expected {}, got {}",
69                self.parameters.len(),
70                new_parameters.len()
71            ));
72        }
73        self.parameters = new_parameters;
74        Ok(())
75    }
76
77    /// Get parameter at specific index
78    #[must_use]
79    pub fn get_parameter(&self, index: usize) -> Option<f64> {
80        self.parameters.get(index).copied()
81    }
82
83    /// Set parameter at specific index
84    pub fn set_parameter(&mut self, index: usize, value: f64) -> Result<(), String> {
85        if index >= self.parameters.len() {
86            return Err(format!("Parameter index {index} out of bounds"));
87        }
88        self.parameters[index] = value;
89        Ok(())
90    }
91
92    /// Get the number of parameters
93    #[must_use]
94    pub fn num_parameters(&self) -> usize {
95        self.parameters.len()
96    }
97
98    /// Get the number of qubits
99    #[must_use]
100    pub const fn num_qubits(&self) -> usize {
101        self.circuit.num_qubits
102    }
103
104    /// Get circuit depth
105    #[must_use]
106    pub fn depth(&self) -> usize {
107        self.circuit.gates.len()
108    }
109
110    /// Add parameter mapping for a gate
111    pub fn add_parameter_mapping(&mut self, gate_index: usize, parameter_indices: Vec<usize>) {
112        self.gate_parameter_map
113            .insert(gate_index, parameter_indices);
114    }
115
116    /// Get parameter mapping for a gate
117    #[must_use]
118    pub fn get_parameter_mapping(&self, gate_index: usize) -> Option<&Vec<usize>> {
119        self.gate_parameter_map.get(&gate_index)
120    }
121
122    /// Estimate circuit fidelity based on hardware optimizations
123    #[must_use]
124    pub fn estimate_fidelity(&self) -> f64 {
125        let mut total_fidelity = 1.0;
126
127        for gate in &self.circuit.gates {
128            let gate_name = Self::gate_type_to_string(&gate.gate_type);
129            if let Some(&fidelity) = self.hardware_optimizations.gate_fidelities.get(&gate_name) {
130                total_fidelity *= fidelity;
131            } else {
132                // Default fidelity for unknown gates
133                total_fidelity *= 0.99;
134            }
135        }
136
137        total_fidelity
138    }
139
140    /// Estimate total execution time
141    #[must_use]
142    pub fn estimate_execution_time(&self) -> f64 {
143        let mut total_time = 0.0;
144
145        for gate in &self.circuit.gates {
146            let gate_name = Self::gate_type_to_string(&gate.gate_type);
147            if let Some(&time) = self.hardware_optimizations.gate_times.get(&gate_name) {
148                total_time += time;
149            } else {
150                // Default time for unknown gates
151                total_time += 1e-6;
152            }
153        }
154
155        total_time
156    }
157
158    /// Check if two qubits are connected according to hardware topology
159    #[must_use]
160    pub fn are_qubits_connected(&self, qubit1: usize, qubit2: usize) -> bool {
161        if qubit1 >= self.num_qubits() || qubit2 >= self.num_qubits() {
162            return false;
163        }
164        self.hardware_optimizations.connectivity_graph[[qubit1, qubit2]]
165    }
166
167    /// Get decoherence time for a specific qubit
168    #[must_use]
169    pub fn get_decoherence_time(&self, qubit: usize) -> Option<f64> {
170        self.hardware_optimizations
171            .decoherence_times
172            .get(qubit)
173            .copied()
174    }
175
176    /// Clone the circuit with new parameters
177    pub fn with_parameters(&self, parameters: Array1<f64>) -> Result<Self, String> {
178        let mut new_circuit = self.clone();
179        new_circuit.update_parameters(parameters)?;
180        Ok(new_circuit)
181    }
182
183    /// Convert `InterfaceGateType` to string
184    fn gate_type_to_string(gate_type: &InterfaceGateType) -> String {
185        match gate_type {
186            InterfaceGateType::Identity => "I".to_string(),
187            InterfaceGateType::PauliX | InterfaceGateType::X => "X".to_string(),
188            InterfaceGateType::PauliY => "Y".to_string(),
189            InterfaceGateType::PauliZ => "Z".to_string(),
190            InterfaceGateType::Hadamard | InterfaceGateType::H => "H".to_string(),
191            InterfaceGateType::S => "S".to_string(),
192            InterfaceGateType::T => "T".to_string(),
193            InterfaceGateType::Phase(_) => "Phase".to_string(),
194            InterfaceGateType::RX(_) => "RX".to_string(),
195            InterfaceGateType::RY(_) => "RY".to_string(),
196            InterfaceGateType::RZ(_) => "RZ".to_string(),
197            InterfaceGateType::U1(_) => "U1".to_string(),
198            InterfaceGateType::U2(_, _) => "U2".to_string(),
199            InterfaceGateType::U3(_, _, _) => "U3".to_string(),
200            InterfaceGateType::CNOT => "CNOT".to_string(),
201            InterfaceGateType::CZ => "CZ".to_string(),
202            InterfaceGateType::CY => "CY".to_string(),
203            InterfaceGateType::SWAP => "SWAP".to_string(),
204            InterfaceGateType::ISwap => "ISwap".to_string(),
205            InterfaceGateType::CRX(_) => "CRX".to_string(),
206            InterfaceGateType::CRY(_) => "CRY".to_string(),
207            InterfaceGateType::CRZ(_) => "CRZ".to_string(),
208            InterfaceGateType::CPhase(_) => "CPhase".to_string(),
209            InterfaceGateType::Toffoli => "Toffoli".to_string(),
210            InterfaceGateType::Fredkin => "Fredkin".to_string(),
211            InterfaceGateType::MultiControlledX(_) => "MCX".to_string(),
212            InterfaceGateType::MultiControlledZ(_) => "MCZ".to_string(),
213            InterfaceGateType::Custom(name, _) => name.clone(),
214            InterfaceGateType::Measure => "Measure".to_string(),
215            InterfaceGateType::Reset => "Reset".to_string(),
216        }
217    }
218}
219
220impl HardwareOptimizations {
221    /// Create optimizations for specific hardware
222    #[must_use]
223    pub fn for_hardware(architecture: HardwareArchitecture, num_qubits: usize) -> Self {
224        let connectivity_graph = match architecture {
225            HardwareArchitecture::Superconducting => {
226                // Linear connectivity typical of superconducting systems
227                let mut graph = Array2::from_elem((num_qubits, num_qubits), false);
228                for i in 0..num_qubits.saturating_sub(1) {
229                    graph[[i, i + 1]] = true;
230                    graph[[i + 1, i]] = true;
231                }
232                graph
233            }
234            HardwareArchitecture::TrappedIon => {
235                // All-to-all connectivity for trapped ions
236                Array2::from_elem((num_qubits, num_qubits), true)
237            }
238            HardwareArchitecture::Photonic => {
239                // Limited connectivity for photonic systems
240                let mut graph = Array2::from_elem((num_qubits, num_qubits), false);
241                for i in 0..num_qubits {
242                    for j in 0..num_qubits {
243                        if (i as i32 - j as i32).abs() <= 2 {
244                            graph[[i, j]] = true;
245                        }
246                    }
247                }
248                graph
249            }
250            _ => Array2::from_elem((num_qubits, num_qubits), true),
251        };
252
253        let gate_fidelities = match architecture {
254            HardwareArchitecture::Superconducting => {
255                let mut fidelities = HashMap::new();
256                fidelities.insert("X".to_string(), 0.999);
257                fidelities.insert("Y".to_string(), 0.999);
258                fidelities.insert("Z".to_string(), 0.9999);
259                fidelities.insert("H".to_string(), 0.998);
260                fidelities.insert("CNOT".to_string(), 0.995);
261                fidelities.insert("CZ".to_string(), 0.996);
262                fidelities
263            }
264            HardwareArchitecture::TrappedIon => {
265                let mut fidelities = HashMap::new();
266                fidelities.insert("X".to_string(), 0.9999);
267                fidelities.insert("Y".to_string(), 0.9999);
268                fidelities.insert("Z".to_string(), 0.99999);
269                fidelities.insert("H".to_string(), 0.9999);
270                fidelities.insert("CNOT".to_string(), 0.999);
271                fidelities.insert("CZ".to_string(), 0.999);
272                fidelities
273            }
274            _ => {
275                let mut fidelities = HashMap::new();
276                fidelities.insert("X".to_string(), 0.99);
277                fidelities.insert("Y".to_string(), 0.99);
278                fidelities.insert("Z".to_string(), 0.999);
279                fidelities.insert("H".to_string(), 0.99);
280                fidelities.insert("CNOT".to_string(), 0.98);
281                fidelities.insert("CZ".to_string(), 0.98);
282                fidelities
283            }
284        };
285
286        let decoherence_times = match architecture {
287            HardwareArchitecture::Superconducting => {
288                Array1::from_vec(vec![50e-6; num_qubits]) // 50 microseconds
289            }
290            HardwareArchitecture::TrappedIon => {
291                Array1::from_vec(vec![100e-3; num_qubits]) // 100 milliseconds
292            }
293            _ => Array1::from_vec(vec![10e-6; num_qubits]),
294        };
295
296        let gate_times = match architecture {
297            HardwareArchitecture::Superconducting => {
298                let mut times = HashMap::new();
299                times.insert("X".to_string(), 20e-9);
300                times.insert("Y".to_string(), 20e-9);
301                times.insert("Z".to_string(), 0.0);
302                times.insert("H".to_string(), 20e-9);
303                times.insert("CNOT".to_string(), 40e-9);
304                times.insert("CZ".to_string(), 40e-9);
305                times
306            }
307            HardwareArchitecture::TrappedIon => {
308                let mut times = HashMap::new();
309                times.insert("X".to_string(), 10e-6);
310                times.insert("Y".to_string(), 10e-6);
311                times.insert("Z".to_string(), 0.0);
312                times.insert("H".to_string(), 10e-6);
313                times.insert("CNOT".to_string(), 100e-6);
314                times.insert("CZ".to_string(), 100e-6);
315                times
316            }
317            _ => {
318                let mut times = HashMap::new();
319                times.insert("X".to_string(), 1e-6);
320                times.insert("Y".to_string(), 1e-6);
321                times.insert("Z".to_string(), 0.0);
322                times.insert("H".to_string(), 1e-6);
323                times.insert("CNOT".to_string(), 10e-6);
324                times.insert("CZ".to_string(), 10e-6);
325                times
326            }
327        };
328
329        let crosstalk_matrix = Array2::zeros((num_qubits, num_qubits));
330
331        Self {
332            connectivity_graph,
333            gate_fidelities,
334            decoherence_times,
335            gate_times,
336            crosstalk_matrix,
337        }
338    }
339
340    /// Update gate fidelity for a specific gate
341    pub fn set_gate_fidelity(&mut self, gate_name: &str, fidelity: f64) {
342        self.gate_fidelities.insert(gate_name.to_string(), fidelity);
343    }
344
345    /// Update gate time for a specific gate
346    pub fn set_gate_time(&mut self, gate_name: &str, time: f64) {
347        self.gate_times.insert(gate_name.to_string(), time);
348    }
349
350    /// Update decoherence time for a specific qubit
351    pub fn set_decoherence_time(&mut self, qubit: usize, time: f64) {
352        if qubit < self.decoherence_times.len() {
353            self.decoherence_times[qubit] = time;
354        }
355    }
356
357    /// Set connectivity between two qubits
358    pub fn set_connectivity(&mut self, qubit1: usize, qubit2: usize, connected: bool) {
359        if qubit1 < self.connectivity_graph.nrows() && qubit2 < self.connectivity_graph.ncols() {
360            self.connectivity_graph[[qubit1, qubit2]] = connected;
361            self.connectivity_graph[[qubit2, qubit1]] = connected; // Symmetric
362        }
363    }
364
365    /// Get average gate fidelity
366    #[must_use]
367    pub fn average_gate_fidelity(&self) -> f64 {
368        let fidelities: Vec<f64> = self.gate_fidelities.values().copied().collect();
369        if fidelities.is_empty() {
370            1.0
371        } else {
372            fidelities.iter().sum::<f64>() / fidelities.len() as f64
373        }
374    }
375
376    /// Get connectivity degree (number of connections) for a qubit
377    #[must_use]
378    pub fn connectivity_degree(&self, qubit: usize) -> usize {
379        if qubit >= self.connectivity_graph.nrows() {
380            return 0;
381        }
382        self.connectivity_graph
383            .row(qubit)
384            .iter()
385            .map(|&x| usize::from(x))
386            .sum()
387    }
388}