1use quantrs2_core::gate::{multi, single, GateOp};
7use quantrs2_core::qubit::QubitId;
8use std::any::Any;
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Copy)]
13pub struct GateCost {
14 pub duration_ns: f64,
16 pub gate_count: u32,
18 pub computational_cost: f64,
20}
21
22impl GateCost {
23 #[must_use]
25 pub const fn new(duration_ns: f64, gate_count: u32, computational_cost: f64) -> Self {
26 Self {
27 duration_ns,
28 gate_count,
29 computational_cost,
30 }
31 }
32
33 #[must_use]
35 pub fn total_cost(&self, time_weight: f64, count_weight: f64, comp_weight: f64) -> f64 {
36 comp_weight.mul_add(
37 self.computational_cost,
38 time_weight.mul_add(self.duration_ns, count_weight * f64::from(self.gate_count)),
39 )
40 }
41}
42
43#[derive(Debug, Clone, Copy)]
45pub struct GateError {
46 pub fidelity: f64,
48 pub error_rate: f64,
50 pub coherent_error: f64,
52}
53
54impl GateError {
55 #[must_use]
57 pub const fn new(fidelity: f64, error_rate: f64, coherent_error: f64) -> Self {
58 Self {
59 fidelity,
60 error_rate,
61 coherent_error,
62 }
63 }
64
65 #[must_use]
67 pub fn total_error(&self) -> f64 {
68 1.0 - self.fidelity + self.error_rate + self.coherent_error
69 }
70}
71
72#[derive(Debug, Clone)]
74pub struct DecompositionRule {
75 pub name: String,
77 pub target_gates: Vec<String>,
79 pub cost: GateCost,
81 pub priority: u32,
83}
84
85#[derive(Debug, Clone)]
87pub struct GateProperties {
88 pub name: String,
90 pub num_qubits: usize,
92 pub is_native: bool,
94 pub cost: GateCost,
96 pub error: GateError,
98 pub decompositions: Vec<DecompositionRule>,
100 pub commutes_with: Vec<String>,
102 pub is_self_inverse: bool,
104 pub is_diagonal: bool,
106 pub is_parameterized: bool,
108}
109
110impl GateProperties {
111 #[must_use]
113 pub fn single_qubit(name: &str) -> Self {
114 match name {
115 "H" => Self {
116 name: name.to_string(),
117 num_qubits: 1,
118 is_native: true,
119 cost: GateCost::new(20.0, 1, 1.0),
120 error: GateError::new(0.9999, 0.0001, 0.00001),
121 decompositions: vec![],
122 commutes_with: vec![],
123 is_self_inverse: true,
124 is_diagonal: false,
125 is_parameterized: false,
126 },
127 "X" | "Y" | "Z" => Self {
128 name: name.to_string(),
129 num_qubits: 1,
130 is_native: true,
131 cost: GateCost::new(20.0, 1, 1.0),
132 error: GateError::new(0.9999, 0.0001, 0.00001),
133 decompositions: vec![],
134 commutes_with: if name == "Z" {
135 vec![
136 "Z".to_string(),
137 "RZ".to_string(),
138 "S".to_string(),
139 "T".to_string(),
140 ]
141 } else {
142 vec![]
143 },
144 is_self_inverse: true,
145 is_diagonal: name == "Z",
146 is_parameterized: false,
147 },
148 "S" => Self {
149 name: name.to_string(),
150 num_qubits: 1,
151 is_native: true,
152 cost: GateCost::new(20.0, 1, 1.0),
153 error: GateError::new(0.9999, 0.0001, 0.00001),
154 decompositions: vec![DecompositionRule {
155 name: "Z-rotation".to_string(),
156 target_gates: vec!["RZ".to_string()],
157 cost: GateCost::new(20.0, 1, 1.0),
158 priority: 1,
159 }],
160 commutes_with: vec![
161 "Z".to_string(),
162 "RZ".to_string(),
163 "S".to_string(),
164 "T".to_string(),
165 ],
166 is_self_inverse: false,
167 is_diagonal: true,
168 is_parameterized: false,
169 },
170 "T" => Self {
171 name: name.to_string(),
172 num_qubits: 1,
173 is_native: true,
174 cost: GateCost::new(20.0, 1, 1.0),
175 error: GateError::new(0.9999, 0.0001, 0.00001),
176 decompositions: vec![DecompositionRule {
177 name: "Z-rotation".to_string(),
178 target_gates: vec!["RZ".to_string()],
179 cost: GateCost::new(20.0, 1, 1.0),
180 priority: 1,
181 }],
182 commutes_with: vec![
183 "Z".to_string(),
184 "RZ".to_string(),
185 "S".to_string(),
186 "T".to_string(),
187 ],
188 is_self_inverse: false,
189 is_diagonal: true,
190 is_parameterized: false,
191 },
192 "RX" | "RY" | "RZ" => Self {
193 name: name.to_string(),
194 num_qubits: 1,
195 is_native: true,
196 cost: GateCost::new(40.0, 1, 1.5),
197 error: GateError::new(0.9998, 0.0002, 0.00002),
198 decompositions: vec![],
199 commutes_with: if name == "RZ" {
200 vec![
201 "Z".to_string(),
202 "RZ".to_string(),
203 "S".to_string(),
204 "T".to_string(),
205 ]
206 } else {
207 vec![]
208 },
209 is_self_inverse: false,
210 is_diagonal: name == "RZ",
211 is_parameterized: true,
212 },
213 _ => Self::default_single_qubit(name),
214 }
215 }
216
217 #[must_use]
219 pub fn two_qubit(name: &str) -> Self {
220 match name {
221 "CNOT" => Self {
222 name: name.to_string(),
223 num_qubits: 2,
224 is_native: true,
225 cost: GateCost::new(300.0, 1, 3.0),
226 error: GateError::new(0.999, 0.001, 0.0001),
227 decompositions: vec![],
228 commutes_with: vec![],
229 is_self_inverse: true,
230 is_diagonal: false,
231 is_parameterized: false,
232 },
233 "CZ" => Self {
234 name: name.to_string(),
235 num_qubits: 2,
236 is_native: true,
237 cost: GateCost::new(200.0, 1, 2.5),
238 error: GateError::new(0.999, 0.001, 0.0001),
239 decompositions: vec![DecompositionRule {
240 name: "H-CNOT-H".to_string(),
241 target_gates: vec!["H".to_string(), "CNOT".to_string()],
242 cost: GateCost::new(340.0, 3, 5.0),
243 priority: 1,
244 }],
245 commutes_with: vec!["CZ".to_string()],
246 is_self_inverse: true,
247 is_diagonal: true,
248 is_parameterized: false,
249 },
250 "SWAP" => Self {
251 name: name.to_string(),
252 num_qubits: 2,
253 is_native: false,
254 cost: GateCost::new(900.0, 3, 9.0),
255 error: GateError::new(0.997, 0.003, 0.0003),
256 decompositions: vec![DecompositionRule {
257 name: "3-CNOT".to_string(),
258 target_gates: vec!["CNOT".to_string()],
259 cost: GateCost::new(900.0, 3, 9.0),
260 priority: 1,
261 }],
262 commutes_with: vec![],
263 is_self_inverse: true,
264 is_diagonal: false,
265 is_parameterized: false,
266 },
267 _ => Self::default_two_qubit(name),
268 }
269 }
270
271 #[must_use]
273 pub fn multi_qubit(name: &str, num_qubits: usize) -> Self {
274 match name {
275 "Toffoli" => Self {
276 name: name.to_string(),
277 num_qubits: 3,
278 is_native: false,
279 cost: GateCost::new(2000.0, 15, 20.0),
280 error: GateError::new(0.99, 0.01, 0.001),
281 decompositions: vec![DecompositionRule {
282 name: "Standard".to_string(),
283 target_gates: vec![
284 "H".to_string(),
285 "CNOT".to_string(),
286 "T".to_string(),
287 "T†".to_string(),
288 ],
289 cost: GateCost::new(2000.0, 15, 20.0),
290 priority: 1,
291 }],
292 commutes_with: vec![],
293 is_self_inverse: true,
294 is_diagonal: false,
295 is_parameterized: false,
296 },
297 _ => Self::default_multi_qubit(name, num_qubits),
298 }
299 }
300
301 fn default_single_qubit(name: &str) -> Self {
302 Self {
303 name: name.to_string(),
304 num_qubits: 1,
305 is_native: false,
306 cost: GateCost::new(50.0, 1, 2.0),
307 error: GateError::new(0.999, 0.001, 0.0001),
308 decompositions: vec![],
309 commutes_with: vec![],
310 is_self_inverse: false,
311 is_diagonal: false,
312 is_parameterized: false,
313 }
314 }
315
316 fn default_two_qubit(name: &str) -> Self {
317 Self {
318 name: name.to_string(),
319 num_qubits: 2,
320 is_native: false,
321 cost: GateCost::new(500.0, 2, 5.0),
322 error: GateError::new(0.995, 0.005, 0.0005),
323 decompositions: vec![],
324 commutes_with: vec![],
325 is_self_inverse: false,
326 is_diagonal: false,
327 is_parameterized: false,
328 }
329 }
330
331 fn default_multi_qubit(name: &str, num_qubits: usize) -> Self {
332 Self {
333 name: name.to_string(),
334 num_qubits,
335 is_native: false,
336 cost: GateCost::new(
337 1000.0 * num_qubits as f64,
338 num_qubits as u32 * 5,
339 10.0 * num_qubits as f64,
340 ),
341 error: GateError::new(
342 0.99 / num_qubits as f64,
343 0.01 * num_qubits as f64,
344 0.001 * num_qubits as f64,
345 ),
346 decompositions: vec![],
347 commutes_with: vec![],
348 is_self_inverse: false,
349 is_diagonal: false,
350 is_parameterized: false,
351 }
352 }
353}
354
355pub struct CommutationTable {
357 table: HashMap<(String, String), bool>,
358}
359
360impl CommutationTable {
361 #[must_use]
363 pub fn new() -> Self {
364 let mut table = HashMap::new();
365
366 table.insert(("X".to_string(), "X".to_string()), true);
368 table.insert(("Y".to_string(), "Y".to_string()), true);
369 table.insert(("Z".to_string(), "Z".to_string()), true);
370 table.insert(("X".to_string(), "Y".to_string()), false);
371 table.insert(("Y".to_string(), "X".to_string()), false);
372 table.insert(("X".to_string(), "Z".to_string()), false);
373 table.insert(("Z".to_string(), "X".to_string()), false);
374 table.insert(("Y".to_string(), "Z".to_string()), false);
375 table.insert(("Z".to_string(), "Y".to_string()), false);
376
377 for diag1 in &["Z", "S", "T", "RZ"] {
379 for diag2 in &["Z", "S", "T", "RZ"] {
380 table.insert(((*diag1).to_string(), (*diag2).to_string()), true);
381 }
382 }
383
384 table.insert(("CNOT".to_string(), "CNOT".to_string()), false); Self { table }
388 }
389
390 #[must_use]
392 pub fn commutes(&self, gate1: &str, gate2: &str) -> bool {
393 if let Some(&result) = self.table.get(&(gate1.to_string(), gate2.to_string())) {
394 result
395 } else if let Some(&result) = self.table.get(&(gate2.to_string(), gate1.to_string())) {
396 result
397 } else {
398 false }
400 }
401
402 pub fn gates_commute(&self, gate1: &dyn GateOp, gate2: &dyn GateOp) -> bool {
404 let qubits1 = gate1.qubits();
405 let qubits2 = gate2.qubits();
406
407 if qubits1.iter().all(|q| !qubits2.contains(q)) {
409 return true;
410 }
411
412 self.commutes(gate1.name(), gate2.name())
414 }
415}
416
417impl Default for CommutationTable {
418 fn default() -> Self {
419 Self::new()
420 }
421}
422
423pub fn get_gate_properties(gate: &dyn GateOp) -> GateProperties {
425 let num_qubits = gate.num_qubits();
426
427 match num_qubits {
428 1 => GateProperties::single_qubit(gate.name()),
429 2 => GateProperties::two_qubit(gate.name()),
430 n => GateProperties::multi_qubit(gate.name(), n),
431 }
432}