1use scirs2_core::ndarray::{Array1, Array2};
7use std::collections::HashMap;
8
9use super::config::HardwareArchitecture;
10use crate::circuit_interfaces::{InterfaceCircuit, InterfaceGateType};
11
12#[derive(Debug, Clone)]
14pub struct ParameterizedQuantumCircuit {
15 pub circuit: InterfaceCircuit,
17 pub parameters: Array1<f64>,
19 pub parameter_names: Vec<String>,
21 pub gate_parameter_map: HashMap<usize, Vec<usize>>,
23 pub hardware_optimizations: HardwareOptimizations,
25}
26
27#[derive(Debug, Clone)]
29pub struct HardwareOptimizations {
30 pub connectivity_graph: Array2<bool>,
32 pub gate_fidelities: HashMap<String, f64>,
34 pub decoherence_times: Array1<f64>,
36 pub gate_times: HashMap<String, f64>,
38 pub crosstalk_matrix: Array2<f64>,
40}
41
42impl ParameterizedQuantumCircuit {
43 #[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 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 #[must_use]
79 pub fn get_parameter(&self, index: usize) -> Option<f64> {
80 self.parameters.get(index).copied()
81 }
82
83 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 #[must_use]
94 pub fn num_parameters(&self) -> usize {
95 self.parameters.len()
96 }
97
98 #[must_use]
100 pub const fn num_qubits(&self) -> usize {
101 self.circuit.num_qubits
102 }
103
104 #[must_use]
106 pub fn depth(&self) -> usize {
107 self.circuit.gates.len()
108 }
109
110 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 #[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 #[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 total_fidelity *= 0.99;
134 }
135 }
136
137 total_fidelity
138 }
139
140 #[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 total_time += 1e-6;
152 }
153 }
154
155 total_time
156 }
157
158 #[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 #[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 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 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 #[must_use]
223 pub fn for_hardware(architecture: HardwareArchitecture, num_qubits: usize) -> Self {
224 let connectivity_graph = match architecture {
225 HardwareArchitecture::Superconducting => {
226 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 Array2::from_elem((num_qubits, num_qubits), true)
237 }
238 HardwareArchitecture::Photonic => {
239 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]) }
290 HardwareArchitecture::TrappedIon => {
291 Array1::from_vec(vec![100e-3; num_qubits]) }
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 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 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 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 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; }
363 }
364
365 #[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 #[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}