1use scirs2_core::Complex64;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::fmt;
10
11use quantrs2_core::{
12 error::{QuantRS2Error, QuantRS2Result},
13 gate::GateOp,
14 qubit::QubitId,
15};
16
17use crate::translation::{DecomposedGate, HardwareBackend, NativeGateSet};
18
19pub trait HardwareGate: GateOp {
21 fn backend(&self) -> HardwareBackend;
23
24 fn metadata(&self) -> HashMap<String, String> {
26 HashMap::new()
27 }
28
29 fn requires_calibration(&self) -> bool {
31 true
32 }
33
34 fn calibration_params(&self) -> Vec<String> {
36 vec![]
37 }
38}
39
40pub mod ibm_gates {
42 use super::*;
43 use scirs2_core::Complex64;
44 use std::any::Any;
45
46 #[derive(Debug, Clone, Copy)]
48 pub struct SXGate {
49 pub target: QubitId,
50 }
51
52 impl GateOp for SXGate {
53 fn name(&self) -> &'static str {
54 "sx"
55 }
56
57 fn qubits(&self) -> Vec<QubitId> {
58 vec![self.target]
59 }
60
61 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
62 let half = 0.5;
63 let i_half = Complex64::new(0.0, 0.5);
64 Ok(vec![
65 Complex64::new(half, 0.0) + i_half,
66 Complex64::new(half, 0.0) - i_half,
67 Complex64::new(half, 0.0) - i_half,
68 Complex64::new(half, 0.0) + i_half,
69 ])
70 }
71
72 fn as_any(&self) -> &dyn Any {
73 self
74 }
75
76 fn clone_gate(&self) -> Box<dyn GateOp> {
77 Box::new(*self)
78 }
79 }
80
81 impl HardwareGate for SXGate {
82 fn backend(&self) -> HardwareBackend {
83 HardwareBackend::IBMQuantum
84 }
85
86 fn metadata(&self) -> HashMap<String, String> {
87 let mut meta = HashMap::new();
88 meta.insert("gate_type".to_string(), "basis".to_string());
89 meta.insert("duration_ns".to_string(), "35.5".to_string());
90 meta
91 }
92 }
93}
94
95pub mod google_gates {
97 use super::*;
98 use scirs2_core::Complex64;
99 use std::any::Any;
100 use std::f64::consts::PI;
101
102 #[derive(Debug, Clone, Copy)]
104 pub struct SycamoreGate {
105 pub qubit1: QubitId,
106 pub qubit2: QubitId,
107 }
108
109 impl GateOp for SycamoreGate {
110 fn name(&self) -> &'static str {
111 "syc"
112 }
113
114 fn qubits(&self) -> Vec<QubitId> {
115 vec![self.qubit1, self.qubit2]
116 }
117
118 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
119 let fsim_theta = PI / 2.0;
122 let fsim_phi = PI / 6.0;
123
124 let c = fsim_theta.cos();
126 let s = Complex64::new(0.0, -fsim_theta.sin());
127 let phase = Complex64::from_polar(1.0, -fsim_phi);
128
129 Ok(vec![
130 Complex64::new(1.0, 0.0),
131 Complex64::new(0.0, 0.0),
132 Complex64::new(0.0, 0.0),
133 Complex64::new(0.0, 0.0),
134 Complex64::new(0.0, 0.0),
135 Complex64::new(c, 0.0),
136 s,
137 Complex64::new(0.0, 0.0),
138 Complex64::new(0.0, 0.0),
139 s,
140 Complex64::new(c, 0.0),
141 Complex64::new(0.0, 0.0),
142 Complex64::new(0.0, 0.0),
143 Complex64::new(0.0, 0.0),
144 Complex64::new(0.0, 0.0),
145 phase,
146 ])
147 }
148
149 fn as_any(&self) -> &dyn Any {
150 self
151 }
152
153 fn clone_gate(&self) -> Box<dyn GateOp> {
154 Box::new(*self)
155 }
156 }
157
158 impl HardwareGate for SycamoreGate {
159 fn backend(&self) -> HardwareBackend {
160 HardwareBackend::GoogleSycamore
161 }
162
163 fn metadata(&self) -> HashMap<String, String> {
164 let mut meta = HashMap::new();
165 meta.insert("gate_type".to_string(), "entangling".to_string());
166 meta.insert("duration_ns".to_string(), "12".to_string());
167 meta.insert("fidelity".to_string(), "0.995".to_string());
168 meta
169 }
170 }
171
172 #[derive(Debug, Clone, Copy)]
174 pub struct PoweredGate {
175 pub target: QubitId,
176 pub axis: char, pub power: f64,
178 }
179
180 impl GateOp for PoweredGate {
181 fn name(&self) -> &'static str {
182 match self.axis {
183 'X' => "x_pow",
184 'Y' => "y_pow",
185 'Z' => "z_pow",
186 _ => "pow",
187 }
188 }
189
190 fn qubits(&self) -> Vec<QubitId> {
191 vec![self.target]
192 }
193
194 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
195 let angle = PI * self.power;
196 let cos_half = (angle / 2.0).cos();
197 let sin_half = (angle / 2.0).sin();
198
199 match self.axis {
200 'X' => Ok(vec![
201 Complex64::new(cos_half, 0.0),
202 Complex64::new(0.0, -sin_half),
203 Complex64::new(0.0, -sin_half),
204 Complex64::new(cos_half, 0.0),
205 ]),
206 'Y' => Ok(vec![
207 Complex64::new(cos_half, 0.0),
208 Complex64::new(-sin_half, 0.0),
209 Complex64::new(sin_half, 0.0),
210 Complex64::new(cos_half, 0.0),
211 ]),
212 'Z' => Ok(vec![
213 Complex64::from_polar(1.0, -angle / 2.0),
214 Complex64::new(0.0, 0.0),
215 Complex64::new(0.0, 0.0),
216 Complex64::from_polar(1.0, angle / 2.0),
217 ]),
218 _ => Err(QuantRS2Error::InvalidInput("Invalid axis".to_string())),
219 }
220 }
221
222 fn as_any(&self) -> &dyn Any {
223 self
224 }
225
226 fn clone_gate(&self) -> Box<dyn GateOp> {
227 Box::new(*self)
228 }
229 }
230}
231
232pub mod ionq_gates {
234 use super::*;
235 use scirs2_core::Complex64;
236 use std::any::Any;
237
238 #[derive(Debug, Clone, Copy)]
240 pub struct XXGate {
241 pub qubit1: QubitId,
242 pub qubit2: QubitId,
243 pub angle: f64,
244 }
245
246 impl GateOp for XXGate {
247 fn name(&self) -> &'static str {
248 "xx"
249 }
250
251 fn qubits(&self) -> Vec<QubitId> {
252 vec![self.qubit1, self.qubit2]
253 }
254
255 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
256 let c = self.angle.cos();
257 let s = Complex64::new(0.0, -self.angle.sin());
258
259 Ok(vec![
260 Complex64::new(c, 0.0),
261 Complex64::new(0.0, 0.0),
262 Complex64::new(0.0, 0.0),
263 s,
264 Complex64::new(0.0, 0.0),
265 Complex64::new(c, 0.0),
266 s,
267 Complex64::new(0.0, 0.0),
268 Complex64::new(0.0, 0.0),
269 s,
270 Complex64::new(c, 0.0),
271 Complex64::new(0.0, 0.0),
272 s,
273 Complex64::new(0.0, 0.0),
274 Complex64::new(0.0, 0.0),
275 Complex64::new(c, 0.0),
276 ])
277 }
278
279 fn as_any(&self) -> &dyn Any {
280 self
281 }
282
283 fn clone_gate(&self) -> Box<dyn GateOp> {
284 Box::new(*self)
285 }
286 }
287
288 impl HardwareGate for XXGate {
289 fn backend(&self) -> HardwareBackend {
290 HardwareBackend::IonQ
291 }
292
293 fn metadata(&self) -> HashMap<String, String> {
294 let mut meta = HashMap::new();
295 meta.insert("gate_type".to_string(), "ms".to_string());
296 meta.insert("interaction".to_string(), "all-to-all".to_string());
297 meta
298 }
299
300 fn calibration_params(&self) -> Vec<String> {
301 vec!["ms_amplitude".to_string(), "ms_phase".to_string()]
302 }
303 }
304}
305
306pub mod rigetti_gates {
308 use super::*;
309 use scirs2_core::Complex64;
310 use std::any::Any;
311
312 #[derive(Debug, Clone, Copy)]
314 pub struct XYGate {
315 pub qubit1: QubitId,
316 pub qubit2: QubitId,
317 pub angle: f64,
318 }
319
320 impl GateOp for XYGate {
321 fn name(&self) -> &'static str {
322 "xy"
323 }
324
325 fn qubits(&self) -> Vec<QubitId> {
326 vec![self.qubit1, self.qubit2]
327 }
328
329 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
330 let c = (self.angle / 2.0).cos();
331 let s = Complex64::new(0.0, (self.angle / 2.0).sin());
332
333 Ok(vec![
334 Complex64::new(1.0, 0.0),
335 Complex64::new(0.0, 0.0),
336 Complex64::new(0.0, 0.0),
337 Complex64::new(0.0, 0.0),
338 Complex64::new(0.0, 0.0),
339 Complex64::new(c, 0.0),
340 s,
341 Complex64::new(0.0, 0.0),
342 Complex64::new(0.0, 0.0),
343 s,
344 Complex64::new(c, 0.0),
345 Complex64::new(0.0, 0.0),
346 Complex64::new(0.0, 0.0),
347 Complex64::new(0.0, 0.0),
348 Complex64::new(0.0, 0.0),
349 Complex64::new(1.0, 0.0),
350 ])
351 }
352
353 fn as_any(&self) -> &dyn Any {
354 self
355 }
356
357 fn clone_gate(&self) -> Box<dyn GateOp> {
358 Box::new(*self)
359 }
360 }
361}
362
363pub mod honeywell_gates {
365 use super::*;
366 use scirs2_core::Complex64;
367 use std::any::Any;
368
369 #[derive(Debug, Clone, Copy)]
371 pub struct ZZGate {
372 pub qubit1: QubitId,
373 pub qubit2: QubitId,
374 pub angle: f64,
375 }
376
377 impl GateOp for ZZGate {
378 fn name(&self) -> &'static str {
379 "zz"
380 }
381
382 fn qubits(&self) -> Vec<QubitId> {
383 vec![self.qubit1, self.qubit2]
384 }
385
386 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
387 let phase_p = Complex64::from_polar(1.0, self.angle / 2.0);
388 let phase_m = Complex64::from_polar(1.0, -self.angle / 2.0);
389
390 Ok(vec![
391 phase_m,
392 Complex64::new(0.0, 0.0),
393 Complex64::new(0.0, 0.0),
394 Complex64::new(0.0, 0.0),
395 Complex64::new(0.0, 0.0),
396 phase_p,
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 phase_p,
402 Complex64::new(0.0, 0.0),
403 Complex64::new(0.0, 0.0),
404 Complex64::new(0.0, 0.0),
405 Complex64::new(0.0, 0.0),
406 phase_m,
407 ])
408 }
409
410 fn as_any(&self) -> &dyn Any {
411 self
412 }
413
414 fn clone_gate(&self) -> Box<dyn GateOp> {
415 Box::new(*self)
416 }
417 }
418
419 impl HardwareGate for ZZGate {
420 fn backend(&self) -> HardwareBackend {
421 HardwareBackend::Honeywell
422 }
423
424 fn metadata(&self) -> HashMap<String, String> {
425 let mut meta = HashMap::new();
426 meta.insert("gate_type".to_string(), "native".to_string());
427 meta.insert("fidelity".to_string(), "0.999".to_string());
428 meta
429 }
430 }
431
432 #[derive(Debug, Clone, Copy)]
434 pub struct U3Gate {
435 pub target: QubitId,
436 pub theta: f64,
437 pub phi: f64,
438 pub lambda: f64,
439 }
440
441 impl GateOp for U3Gate {
442 fn name(&self) -> &'static str {
443 "u3"
444 }
445
446 fn qubits(&self) -> Vec<QubitId> {
447 vec![self.target]
448 }
449
450 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
451 let cos_half = (self.theta / 2.0).cos();
452 let sin_half = (self.theta / 2.0).sin();
453
454 Ok(vec![
455 Complex64::new(cos_half, 0.0),
456 -Complex64::from_polar(sin_half, self.lambda),
457 Complex64::from_polar(sin_half, self.phi),
458 Complex64::from_polar(cos_half, self.phi + self.lambda),
459 ])
460 }
461
462 fn as_any(&self) -> &dyn Any {
463 self
464 }
465
466 fn clone_gate(&self) -> Box<dyn GateOp> {
467 Box::new(*self)
468 }
469 }
470}
471
472pub struct DecompositionValidator {
474 tolerance: f64,
476}
477
478impl DecompositionValidator {
479 pub const fn new(tolerance: f64) -> Self {
481 Self { tolerance }
482 }
483
484 pub fn validate(
490 &self,
491 original: &dyn GateOp,
492 decomposed: &[DecomposedGate],
493 ) -> QuantRS2Result<bool> {
494 let fidelity = self.calculate_fidelity(original, decomposed)?;
495 Ok((1.0 - fidelity).abs() <= self.tolerance)
496 }
497
498 pub fn calculate_fidelity(
509 &self,
510 original: &dyn GateOp,
511 decomposed: &[DecomposedGate],
512 ) -> QuantRS2Result<f64> {
513 let original_qubits = original.qubits();
514 let num_qubits = original_qubits.len();
515 if num_qubits == 0 {
516 return Err(QuantRS2Error::InvalidInput(
517 "Original gate acts on zero qubits".to_string(),
518 ));
519 }
520 let mut qubit_order: Vec<QubitId> = original_qubits.clone();
522 for gate in decomposed {
523 for q in &gate.qubits {
524 if !qubit_order.contains(q) {
525 qubit_order.push(*q);
526 }
527 }
528 }
529 let dim = 1usize << qubit_order.len();
530
531 let original_matrix = flat_to_square(&original.matrix()?)?;
533 let original_embedded = embed_unitary(&original_matrix, &original_qubits, &qubit_order)?;
534 let mut product = identity_matrix(dim);
536 for gate in decomposed {
537 let gate_matrix = native_gate_matrix(&gate.native_gate, &gate.parameters)?;
538 let embedded = embed_unitary(&gate_matrix, &gate.qubits, &qubit_order)?;
539 product = matmul(&embedded, &product);
540 }
541 if original_embedded.len() != product.len() {
542 return Err(QuantRS2Error::InvalidInput(
543 "Dimension mismatch between original and decomposed unitaries".to_string(),
544 ));
545 }
546
547 let mut trace = Complex64::new(0.0, 0.0);
549 for row in 0..dim {
550 for col in 0..dim {
551 trace += original_embedded[col * dim + row].conj() * product[row * dim + col];
553 }
554 }
555 let d = dim as f64;
556 let fidelity = trace.norm_sqr() / (d * d);
557 Ok(fidelity.clamp(0.0, 1.0))
559 }
560}
561
562fn flat_to_square(flat: &[Complex64]) -> QuantRS2Result<Vec<Complex64>> {
565 let dim = (flat.len() as f64).sqrt().round() as usize;
566 if dim * dim != flat.len() {
567 return Err(QuantRS2Error::InvalidInput(format!(
568 "Gate matrix length {} is not a perfect square",
569 flat.len()
570 )));
571 }
572 Ok(flat.to_vec())
573}
574
575fn identity_matrix(dim: usize) -> Vec<Complex64> {
577 let mut m = vec![Complex64::new(0.0, 0.0); dim * dim];
578 for i in 0..dim {
579 m[i * dim + i] = Complex64::new(1.0, 0.0);
580 }
581 m
582}
583
584fn matmul(a: &[Complex64], b: &[Complex64]) -> Vec<Complex64> {
586 let dim = (a.len() as f64).sqrt().round() as usize;
587 let mut out = vec![Complex64::new(0.0, 0.0); dim * dim];
588 for row in 0..dim {
589 for k in 0..dim {
590 let a_rk = a[row * dim + k];
591 if a_rk == Complex64::new(0.0, 0.0) {
592 continue;
593 }
594 for col in 0..dim {
595 out[row * dim + col] += a_rk * b[k * dim + col];
596 }
597 }
598 }
599 out
600}
601
602fn embed_unitary(
606 gate_matrix: &[Complex64],
607 gate_qubits: &[QubitId],
608 qubit_order: &[QubitId],
609) -> QuantRS2Result<Vec<Complex64>> {
610 let total = qubit_order.len();
611 let full_dim = 1usize << total;
612 let sub = gate_qubits.len();
613 let sub_dim = 1usize << sub;
614 if gate_matrix.len() != sub_dim * sub_dim {
615 return Err(QuantRS2Error::InvalidInput(format!(
616 "Gate on {} qubits has matrix of length {} (expected {})",
617 sub,
618 gate_matrix.len(),
619 sub_dim * sub_dim
620 )));
621 }
622 let mut positions = Vec::with_capacity(sub);
624 for q in gate_qubits {
625 let pos = qubit_order.iter().position(|p| p == q).ok_or_else(|| {
626 QuantRS2Error::InvalidInput("Gate qubit not in qubit ordering".to_string())
627 })?;
628 positions.push(pos);
629 }
630
631 let mut out = vec![Complex64::new(0.0, 0.0); full_dim * full_dim];
632 for full_col in 0..full_dim {
633 let mut sub_col = 0usize;
635 for (i, &pos) in positions.iter().enumerate() {
636 if full_col & (1usize << pos) != 0 {
637 sub_col |= 1usize << i;
638 }
639 }
640 for sub_row in 0..sub_dim {
642 let amp = gate_matrix[sub_row * sub_dim + sub_col];
643 if amp == Complex64::new(0.0, 0.0) {
644 continue;
645 }
646 let mut full_row = full_col;
649 for (i, &pos) in positions.iter().enumerate() {
650 let bit = 1usize << pos;
651 if sub_row & (1usize << i) != 0 {
652 full_row |= bit;
653 } else {
654 full_row &= !bit;
655 }
656 }
657 out[full_row * full_dim + full_col] = amp;
658 }
659 }
660 Ok(out)
661}
662
663fn native_gate_matrix(name: &str, params: &[f64]) -> QuantRS2Result<Vec<Complex64>> {
667 let frac = std::f64::consts::FRAC_1_SQRT_2;
668 let c = Complex64::new;
669 let upper = name.to_ascii_uppercase();
670 let param = |i: usize| -> QuantRS2Result<f64> {
671 params.get(i).copied().ok_or_else(|| {
672 QuantRS2Error::InvalidInput(format!(
673 "Native gate {upper} requires parameter index {i} but only {} provided",
674 params.len()
675 ))
676 })
677 };
678 match upper.as_str() {
679 "I" | "ID" => Ok(vec![c(1.0, 0.0), c(0.0, 0.0), c(0.0, 0.0), c(1.0, 0.0)]),
680 "X" | "NOT" => Ok(vec![c(0.0, 0.0), c(1.0, 0.0), c(1.0, 0.0), c(0.0, 0.0)]),
681 "Y" => Ok(vec![c(0.0, 0.0), c(0.0, -1.0), c(0.0, 1.0), c(0.0, 0.0)]),
682 "Z" => Ok(vec![c(1.0, 0.0), c(0.0, 0.0), c(0.0, 0.0), c(-1.0, 0.0)]),
683 "H" => Ok(vec![
684 c(frac, 0.0),
685 c(frac, 0.0),
686 c(frac, 0.0),
687 c(-frac, 0.0),
688 ]),
689 "S" => Ok(vec![c(1.0, 0.0), c(0.0, 0.0), c(0.0, 0.0), c(0.0, 1.0)]),
690 "SDG" | "SDAGGER" => Ok(vec![c(1.0, 0.0), c(0.0, 0.0), c(0.0, 0.0), c(0.0, -1.0)]),
691 "T" => Ok(vec![
692 c(1.0, 0.0),
693 c(0.0, 0.0),
694 c(0.0, 0.0),
695 Complex64::from_polar(1.0, std::f64::consts::FRAC_PI_4),
696 ]),
697 "TDG" | "TDAGGER" => Ok(vec![
698 c(1.0, 0.0),
699 c(0.0, 0.0),
700 c(0.0, 0.0),
701 Complex64::from_polar(1.0, -std::f64::consts::FRAC_PI_4),
702 ]),
703 "SX" => {
704 let half = Complex64::new(0.5, 0.5);
706 let half_conj = Complex64::new(0.5, -0.5);
707 Ok(vec![half, half_conj, half_conj, half])
708 }
709 "RX" => {
710 let theta = param(0)?;
711 let cos = (theta / 2.0).cos();
712 let sin = (theta / 2.0).sin();
713 Ok(vec![c(cos, 0.0), c(0.0, -sin), c(0.0, -sin), c(cos, 0.0)])
714 }
715 "RY" => {
716 let theta = param(0)?;
717 let cos = (theta / 2.0).cos();
718 let sin = (theta / 2.0).sin();
719 Ok(vec![c(cos, 0.0), c(-sin, 0.0), c(sin, 0.0), c(cos, 0.0)])
720 }
721 "RZ" => {
722 let theta = param(0)?;
723 Ok(vec![
724 Complex64::from_polar(1.0, -theta / 2.0),
725 c(0.0, 0.0),
726 c(0.0, 0.0),
727 Complex64::from_polar(1.0, theta / 2.0),
728 ])
729 }
730 "P" | "PHASE" | "U1" => {
731 let lambda = param(0)?;
732 Ok(vec![
733 c(1.0, 0.0),
734 c(0.0, 0.0),
735 c(0.0, 0.0),
736 Complex64::from_polar(1.0, lambda),
737 ])
738 }
739 "U" | "U3" => {
740 let theta = param(0)?;
741 let phi = param(1)?;
742 let lambda = param(2)?;
743 let cos = (theta / 2.0).cos();
744 let sin = (theta / 2.0).sin();
745 Ok(vec![
746 c(cos, 0.0),
747 -Complex64::from_polar(sin, lambda),
748 Complex64::from_polar(sin, phi),
749 Complex64::from_polar(cos, phi + lambda),
750 ])
751 }
752 "CNOT" | "CX" => Ok(vec![
753 c(1.0, 0.0),
754 c(0.0, 0.0),
755 c(0.0, 0.0),
756 c(0.0, 0.0),
757 c(0.0, 0.0),
758 c(1.0, 0.0),
759 c(0.0, 0.0),
760 c(0.0, 0.0),
761 c(0.0, 0.0),
762 c(0.0, 0.0),
763 c(0.0, 0.0),
764 c(1.0, 0.0),
765 c(0.0, 0.0),
766 c(0.0, 0.0),
767 c(1.0, 0.0),
768 c(0.0, 0.0),
769 ]),
770 "CZ" => Ok(vec![
771 c(1.0, 0.0),
772 c(0.0, 0.0),
773 c(0.0, 0.0),
774 c(0.0, 0.0),
775 c(0.0, 0.0),
776 c(1.0, 0.0),
777 c(0.0, 0.0),
778 c(0.0, 0.0),
779 c(0.0, 0.0),
780 c(0.0, 0.0),
781 c(1.0, 0.0),
782 c(0.0, 0.0),
783 c(0.0, 0.0),
784 c(0.0, 0.0),
785 c(0.0, 0.0),
786 c(-1.0, 0.0),
787 ]),
788 other => Err(QuantRS2Error::UnsupportedOperation(format!(
789 "DecompositionValidator cannot reconstruct a matrix for native gate '{other}': \
790 add it to native_gate_matrix to validate decompositions that use it"
791 ))),
792 }
793}
794
795#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
797pub struct BackendCapabilities {
798 pub backend: HardwareBackend,
800 pub native_gates: NativeGateSet,
802 pub features: BackendFeatures,
804 pub performance: BackendPerformance,
806}
807
808impl Default for BackendCapabilities {
809 fn default() -> Self {
810 Self {
811 backend: HardwareBackend::Custom(0),
812 native_gates: NativeGateSet::default(),
813 features: BackendFeatures::default(),
814 performance: BackendPerformance::default(),
815 }
816 }
817}
818
819#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
821pub struct BackendFeatures {
822 pub mid_circuit_measurement: bool,
824 pub conditional_gates: bool,
826 pub parametric_compilation: bool,
828 pub pulse_control: bool,
830 pub max_qubits: usize,
832 pub max_depth: Option<usize>,
834 pub max_mid_circuit_measurements: Option<usize>,
836 pub classical_register_size: usize,
838 pub supports_real_time_feedback: bool,
840 pub supports_parallel_execution: bool,
842 pub supports_reset: bool,
844 pub supports_barriers: bool,
846 pub supported_measurement_bases: Vec<String>,
848}
849
850impl Default for BackendFeatures {
851 fn default() -> Self {
852 Self {
853 mid_circuit_measurement: false,
854 conditional_gates: false,
855 parametric_compilation: true,
856 pulse_control: false,
857 max_qubits: 64,
858 max_depth: None,
859 max_mid_circuit_measurements: None,
860 classical_register_size: 64,
861 supports_real_time_feedback: false,
862 supports_parallel_execution: false,
863 supports_reset: true,
864 supports_barriers: true,
865 supported_measurement_bases: vec!["Z".to_string()],
866 }
867 }
868}
869
870#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
872pub struct BackendPerformance {
873 pub single_qubit_gate_time: f64,
875 pub two_qubit_gate_time: f64,
877 pub measurement_time: f64,
879 pub t1_time: f64,
881 pub t2_time: f64,
883 pub single_qubit_fidelity: f64,
885 pub two_qubit_fidelity: f64,
887}
888
889impl Default for BackendPerformance {
890 fn default() -> Self {
891 Self {
892 single_qubit_gate_time: 50.0, two_qubit_gate_time: 500.0, measurement_time: 1000.0, t1_time: 100.0, t2_time: 50.0, single_qubit_fidelity: 0.999,
898 two_qubit_fidelity: 0.99,
899 }
900 }
901}
902
903pub fn query_backend_capabilities(backend: HardwareBackend) -> BackendCapabilities {
905 match backend {
906 HardwareBackend::IBMQuantum => BackendCapabilities {
907 backend,
908 native_gates: NativeGateSet {
909 backend,
910 single_qubit_gates: ["id", "rz", "sx", "x"]
911 .iter()
912 .map(|s| s.to_string())
913 .collect(),
914 two_qubit_gates: vec!["cx".to_string()],
915 multi_qubit_gates: vec![],
916 arbitrary_single_qubit: false,
917 rotation_axes: vec![crate::translation::RotationAxis::Z],
918 constraints: crate::translation::BackendConstraints {
919 max_depth: None,
920 discrete_angles: None,
921 virtual_z: true,
922 coupling_map: None,
923 timing_constraints: None,
924 },
925 },
926 features: BackendFeatures {
927 mid_circuit_measurement: true,
928 conditional_gates: true,
929 parametric_compilation: true,
930 pulse_control: true,
931 max_qubits: 127,
932 max_depth: Some(10000),
933 max_mid_circuit_measurements: Some(127), classical_register_size: 128,
935 supports_real_time_feedback: true,
936 supports_parallel_execution: false, supports_reset: true,
938 supports_barriers: true,
939 supported_measurement_bases: vec![
940 "Z".to_string(),
941 "X".to_string(),
942 "Y".to_string(),
943 ],
944 },
945 performance: BackendPerformance {
946 single_qubit_gate_time: 35.0,
947 two_qubit_gate_time: 300.0,
948 measurement_time: 3000.0,
949 t1_time: 100.0,
950 t2_time: 100.0,
951 single_qubit_fidelity: 0.9999,
952 two_qubit_fidelity: 0.99,
953 },
954 },
955 HardwareBackend::IonQ => BackendCapabilities {
956 backend,
957 native_gates: NativeGateSet {
958 backend,
959 single_qubit_gates: ["rx", "ry", "rz"].iter().map(|s| s.to_string()).collect(),
960 two_qubit_gates: vec!["xx".to_string()],
961 multi_qubit_gates: vec![],
962 arbitrary_single_qubit: true,
963 rotation_axes: vec![
964 crate::translation::RotationAxis::X,
965 crate::translation::RotationAxis::Y,
966 crate::translation::RotationAxis::Z,
967 ],
968 constraints: crate::translation::BackendConstraints {
969 max_depth: None,
970 discrete_angles: None,
971 virtual_z: false,
972 coupling_map: None, timing_constraints: None,
974 },
975 },
976 features: BackendFeatures {
977 mid_circuit_measurement: false,
978 conditional_gates: false,
979 parametric_compilation: true,
980 pulse_control: false,
981 max_qubits: 32,
982 max_depth: None,
983 max_mid_circuit_measurements: None, classical_register_size: 0, supports_real_time_feedback: false,
986 supports_parallel_execution: true, supports_reset: false,
988 supports_barriers: false,
989 supported_measurement_bases: vec!["Z".to_string()],
990 },
991 performance: BackendPerformance {
992 single_qubit_gate_time: 135.0,
993 two_qubit_gate_time: 600.0,
994 measurement_time: 100.0,
995 t1_time: 10000.0, t2_time: 1000.0, single_qubit_fidelity: 0.9995,
998 two_qubit_fidelity: 0.97,
999 },
1000 },
1001 _ => {
1002 BackendCapabilities {
1004 backend,
1005 native_gates: NativeGateSet {
1006 backend,
1007 single_qubit_gates: vec![],
1008 two_qubit_gates: vec![],
1009 multi_qubit_gates: vec![],
1010 arbitrary_single_qubit: true,
1011 rotation_axes: vec![],
1012 constraints: crate::translation::BackendConstraints {
1013 max_depth: None,
1014 discrete_angles: None,
1015 virtual_z: false,
1016 coupling_map: None,
1017 timing_constraints: None,
1018 },
1019 },
1020 features: BackendFeatures {
1021 mid_circuit_measurement: false,
1022 conditional_gates: false,
1023 parametric_compilation: false,
1024 pulse_control: false,
1025 max_qubits: 20,
1026 max_depth: None,
1027 max_mid_circuit_measurements: None,
1028 classical_register_size: 0,
1029 supports_real_time_feedback: false,
1030 supports_parallel_execution: false,
1031 supports_reset: false,
1032 supports_barriers: false,
1033 supported_measurement_bases: vec!["Z".to_string()],
1034 },
1035 performance: BackendPerformance {
1036 single_qubit_gate_time: 50.0,
1037 two_qubit_gate_time: 500.0,
1038 measurement_time: 1000.0,
1039 t1_time: 50.0,
1040 t2_time: 50.0,
1041 single_qubit_fidelity: 0.999,
1042 two_qubit_fidelity: 0.99,
1043 },
1044 }
1045 }
1046 }
1047}
1048
1049#[cfg(test)]
1050mod tests {
1051 use super::*;
1052
1053 #[test]
1054 fn test_hardware_gate_implementations() {
1055 let sx = ibm_gates::SXGate { target: QubitId(0) };
1057 assert_eq!(sx.name(), "sx");
1058 assert_eq!(sx.backend(), HardwareBackend::IBMQuantum);
1059
1060 let syc = google_gates::SycamoreGate {
1062 qubit1: QubitId(0),
1063 qubit2: QubitId(1),
1064 };
1065 assert_eq!(syc.name(), "syc");
1066 assert_eq!(syc.backend(), HardwareBackend::GoogleSycamore);
1067
1068 let xx = ionq_gates::XXGate {
1070 qubit1: QubitId(0),
1071 qubit2: QubitId(1),
1072 angle: std::f64::consts::PI / 2.0,
1073 };
1074 assert_eq!(xx.name(), "xx");
1075 assert_eq!(xx.backend(), HardwareBackend::IonQ);
1076 }
1077
1078 #[test]
1079 fn test_backend_capabilities() {
1080 let ibm_caps = query_backend_capabilities(HardwareBackend::IBMQuantum);
1081 assert!(ibm_caps.features.pulse_control);
1082 assert!(ibm_caps.features.mid_circuit_measurement);
1083 assert_eq!(ibm_caps.performance.single_qubit_gate_time, 35.0);
1084
1085 let ionq_caps = query_backend_capabilities(HardwareBackend::IonQ);
1086 assert!(!ionq_caps.features.pulse_control);
1087 assert!(ionq_caps.performance.t1_time > ibm_caps.performance.t1_time);
1088 }
1089}