quantrs2_device/
backend_traits.rs

1//! Common traits and utilities for hardware backend translation
2//!
3//! This module provides shared functionality for working with different
4//! quantum hardware backends and their gate sets.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::fmt;
9
10use quantrs2_core::{
11    error::{QuantRS2Error, QuantRS2Result},
12    gate::GateOp,
13    qubit::QubitId,
14};
15
16use crate::translation::{DecomposedGate, HardwareBackend, NativeGateSet};
17
18/// Trait for hardware-specific gate implementations
19pub trait HardwareGate: GateOp {
20    /// Get the hardware backend this gate is native to
21    fn backend(&self) -> HardwareBackend;
22
23    /// Get hardware-specific metadata
24    fn metadata(&self) -> HashMap<String, String> {
25        HashMap::new()
26    }
27
28    /// Check if this gate requires calibration
29    fn requires_calibration(&self) -> bool {
30        true
31    }
32
33    /// Get calibration parameters
34    fn calibration_params(&self) -> Vec<String> {
35        vec![]
36    }
37}
38
39/// IBM-specific gate implementations
40pub mod ibm_gates {
41    use super::*;
42    use scirs2_core::Complex64;
43    use std::any::Any;
44
45    /// IBM's SX gate (√X gate)
46    #[derive(Debug, Clone, Copy)]
47    pub struct SXGate {
48        pub target: QubitId,
49    }
50
51    impl GateOp for SXGate {
52        fn name(&self) -> &'static str {
53            "sx"
54        }
55
56        fn qubits(&self) -> Vec<QubitId> {
57            vec![self.target]
58        }
59
60        fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
61            let half = 0.5;
62            let i_half = Complex64::new(0.0, 0.5);
63            Ok(vec![
64                Complex64::new(half, 0.0) + i_half,
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            ])
69        }
70
71        fn as_any(&self) -> &dyn Any {
72            self
73        }
74
75        fn clone_gate(&self) -> Box<dyn GateOp> {
76            Box::new(self.clone())
77        }
78    }
79
80    impl HardwareGate for SXGate {
81        fn backend(&self) -> HardwareBackend {
82            HardwareBackend::IBMQuantum
83        }
84
85        fn metadata(&self) -> HashMap<String, String> {
86            let mut meta = HashMap::new();
87            meta.insert("gate_type".to_string(), "basis".to_string());
88            meta.insert("duration_ns".to_string(), "35.5".to_string());
89            meta
90        }
91    }
92}
93
94/// Google-specific gate implementations
95pub mod google_gates {
96    use super::*;
97    use scirs2_core::Complex64;
98    use std::any::Any;
99    use std::f64::consts::PI;
100
101    /// Google's Sycamore gate
102    #[derive(Debug, Clone, Copy)]
103    pub struct SycamoreGate {
104        pub qubit1: QubitId,
105        pub qubit2: QubitId,
106    }
107
108    impl GateOp for SycamoreGate {
109        fn name(&self) -> &'static str {
110            "syc"
111        }
112
113        fn qubits(&self) -> Vec<QubitId> {
114            vec![self.qubit1, self.qubit2]
115        }
116
117        fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
118            // Sycamore gate matrix
119            // This is a simplified version - actual gate is more complex
120            let fsim_theta = PI / 2.0;
121            let fsim_phi = PI / 6.0;
122
123            // Create fSIM gate matrix
124            let c = fsim_theta.cos();
125            let s = Complex64::new(0.0, -fsim_theta.sin());
126            let phase = Complex64::from_polar(1.0, -fsim_phi);
127
128            Ok(vec![
129                Complex64::new(1.0, 0.0),
130                Complex64::new(0.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(c, 0.0),
135                s,
136                Complex64::new(0.0, 0.0),
137                Complex64::new(0.0, 0.0),
138                s,
139                Complex64::new(c, 0.0),
140                Complex64::new(0.0, 0.0),
141                Complex64::new(0.0, 0.0),
142                Complex64::new(0.0, 0.0),
143                Complex64::new(0.0, 0.0),
144                phase,
145            ])
146        }
147
148        fn as_any(&self) -> &dyn Any {
149            self
150        }
151
152        fn clone_gate(&self) -> Box<dyn GateOp> {
153            Box::new(self.clone())
154        }
155    }
156
157    impl HardwareGate for SycamoreGate {
158        fn backend(&self) -> HardwareBackend {
159            HardwareBackend::GoogleSycamore
160        }
161
162        fn metadata(&self) -> HashMap<String, String> {
163            let mut meta = HashMap::new();
164            meta.insert("gate_type".to_string(), "entangling".to_string());
165            meta.insert("duration_ns".to_string(), "12".to_string());
166            meta.insert("fidelity".to_string(), "0.995".to_string());
167            meta
168        }
169    }
170
171    /// Google's powered gates (X^t, Y^t, Z^t)
172    #[derive(Debug, Clone, Copy)]
173    pub struct PoweredGate {
174        pub target: QubitId,
175        pub axis: char, // 'X', 'Y', or 'Z'
176        pub power: f64,
177    }
178
179    impl GateOp for PoweredGate {
180        fn name(&self) -> &'static str {
181            match self.axis {
182                'X' => "x_pow",
183                'Y' => "y_pow",
184                'Z' => "z_pow",
185                _ => "pow",
186            }
187        }
188
189        fn qubits(&self) -> Vec<QubitId> {
190            vec![self.target]
191        }
192
193        fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
194            let angle = PI * self.power;
195            let cos_half = (angle / 2.0).cos();
196            let sin_half = (angle / 2.0).sin();
197
198            match self.axis {
199                'X' => Ok(vec![
200                    Complex64::new(cos_half, 0.0),
201                    Complex64::new(0.0, -sin_half),
202                    Complex64::new(0.0, -sin_half),
203                    Complex64::new(cos_half, 0.0),
204                ]),
205                'Y' => Ok(vec![
206                    Complex64::new(cos_half, 0.0),
207                    Complex64::new(-sin_half, 0.0),
208                    Complex64::new(sin_half, 0.0),
209                    Complex64::new(cos_half, 0.0),
210                ]),
211                'Z' => Ok(vec![
212                    Complex64::from_polar(1.0, -angle / 2.0),
213                    Complex64::new(0.0, 0.0),
214                    Complex64::new(0.0, 0.0),
215                    Complex64::from_polar(1.0, angle / 2.0),
216                ]),
217                _ => Err(QuantRS2Error::InvalidInput("Invalid axis".to_string())),
218            }
219        }
220
221        fn as_any(&self) -> &dyn Any {
222            self
223        }
224
225        fn clone_gate(&self) -> Box<dyn GateOp> {
226            Box::new(self.clone())
227        }
228    }
229}
230
231/// IonQ-specific gate implementations
232pub mod ionq_gates {
233    use super::*;
234    use scirs2_core::Complex64;
235    use std::any::Any;
236
237    /// IonQ's XX gate (Mølmer-Sørensen gate)
238    #[derive(Debug, Clone, Copy)]
239    pub struct XXGate {
240        pub qubit1: QubitId,
241        pub qubit2: QubitId,
242        pub angle: f64,
243    }
244
245    impl GateOp for XXGate {
246        fn name(&self) -> &'static str {
247            "xx"
248        }
249
250        fn qubits(&self) -> Vec<QubitId> {
251            vec![self.qubit1, self.qubit2]
252        }
253
254        fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
255            let c = self.angle.cos();
256            let s = Complex64::new(0.0, -self.angle.sin());
257
258            Ok(vec![
259                Complex64::new(c, 0.0),
260                Complex64::new(0.0, 0.0),
261                Complex64::new(0.0, 0.0),
262                s,
263                Complex64::new(0.0, 0.0),
264                Complex64::new(c, 0.0),
265                s,
266                Complex64::new(0.0, 0.0),
267                Complex64::new(0.0, 0.0),
268                s,
269                Complex64::new(c, 0.0),
270                Complex64::new(0.0, 0.0),
271                s,
272                Complex64::new(0.0, 0.0),
273                Complex64::new(0.0, 0.0),
274                Complex64::new(c, 0.0),
275            ])
276        }
277
278        fn as_any(&self) -> &dyn Any {
279            self
280        }
281
282        fn clone_gate(&self) -> Box<dyn GateOp> {
283            Box::new(self.clone())
284        }
285    }
286
287    impl HardwareGate for XXGate {
288        fn backend(&self) -> HardwareBackend {
289            HardwareBackend::IonQ
290        }
291
292        fn metadata(&self) -> HashMap<String, String> {
293            let mut meta = HashMap::new();
294            meta.insert("gate_type".to_string(), "ms".to_string());
295            meta.insert("interaction".to_string(), "all-to-all".to_string());
296            meta
297        }
298
299        fn calibration_params(&self) -> Vec<String> {
300            vec!["ms_amplitude".to_string(), "ms_phase".to_string()]
301        }
302    }
303}
304
305/// Rigetti-specific gate implementations
306pub mod rigetti_gates {
307    use super::*;
308    use scirs2_core::Complex64;
309    use std::any::Any;
310
311    /// Rigetti's parametrized XY gate
312    #[derive(Debug, Clone, Copy)]
313    pub struct XYGate {
314        pub qubit1: QubitId,
315        pub qubit2: QubitId,
316        pub angle: f64,
317    }
318
319    impl GateOp for XYGate {
320        fn name(&self) -> &'static str {
321            "xy"
322        }
323
324        fn qubits(&self) -> Vec<QubitId> {
325            vec![self.qubit1, self.qubit2]
326        }
327
328        fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
329            let c = (self.angle / 2.0).cos();
330            let s = Complex64::new(0.0, (self.angle / 2.0).sin());
331
332            Ok(vec![
333                Complex64::new(1.0, 0.0),
334                Complex64::new(0.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(c, 0.0),
339                s,
340                Complex64::new(0.0, 0.0),
341                Complex64::new(0.0, 0.0),
342                s,
343                Complex64::new(c, 0.0),
344                Complex64::new(0.0, 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(1.0, 0.0),
349            ])
350        }
351
352        fn as_any(&self) -> &dyn Any {
353            self
354        }
355
356        fn clone_gate(&self) -> Box<dyn GateOp> {
357            Box::new(self.clone())
358        }
359    }
360}
361
362/// Honeywell-specific gate implementations
363pub mod honeywell_gates {
364    use super::*;
365    use scirs2_core::Complex64;
366    use std::any::Any;
367
368    /// Honeywell's native ZZ interaction
369    #[derive(Debug, Clone, Copy)]
370    pub struct ZZGate {
371        pub qubit1: QubitId,
372        pub qubit2: QubitId,
373        pub angle: f64,
374    }
375
376    impl GateOp for ZZGate {
377        fn name(&self) -> &'static str {
378            "zz"
379        }
380
381        fn qubits(&self) -> Vec<QubitId> {
382            vec![self.qubit1, self.qubit2]
383        }
384
385        fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
386            let phase_p = Complex64::from_polar(1.0, self.angle / 2.0);
387            let phase_m = Complex64::from_polar(1.0, -self.angle / 2.0);
388
389            Ok(vec![
390                phase_m,
391                Complex64::new(0.0, 0.0),
392                Complex64::new(0.0, 0.0),
393                Complex64::new(0.0, 0.0),
394                Complex64::new(0.0, 0.0),
395                phase_p,
396                Complex64::new(0.0, 0.0),
397                Complex64::new(0.0, 0.0),
398                Complex64::new(0.0, 0.0),
399                Complex64::new(0.0, 0.0),
400                phase_p,
401                Complex64::new(0.0, 0.0),
402                Complex64::new(0.0, 0.0),
403                Complex64::new(0.0, 0.0),
404                Complex64::new(0.0, 0.0),
405                phase_m,
406            ])
407        }
408
409        fn as_any(&self) -> &dyn Any {
410            self
411        }
412
413        fn clone_gate(&self) -> Box<dyn GateOp> {
414            Box::new(self.clone())
415        }
416    }
417
418    impl HardwareGate for ZZGate {
419        fn backend(&self) -> HardwareBackend {
420            HardwareBackend::Honeywell
421        }
422
423        fn metadata(&self) -> HashMap<String, String> {
424            let mut meta = HashMap::new();
425            meta.insert("gate_type".to_string(), "native".to_string());
426            meta.insert("fidelity".to_string(), "0.999".to_string());
427            meta
428        }
429    }
430
431    /// Honeywell's U3 gate (general single-qubit rotation)
432    #[derive(Debug, Clone, Copy)]
433    pub struct U3Gate {
434        pub target: QubitId,
435        pub theta: f64,
436        pub phi: f64,
437        pub lambda: f64,
438    }
439
440    impl GateOp for U3Gate {
441        fn name(&self) -> &'static str {
442            "u3"
443        }
444
445        fn qubits(&self) -> Vec<QubitId> {
446            vec![self.target]
447        }
448
449        fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
450            let cos_half = (self.theta / 2.0).cos();
451            let sin_half = (self.theta / 2.0).sin();
452
453            Ok(vec![
454                Complex64::new(cos_half, 0.0),
455                -Complex64::from_polar(sin_half, self.lambda),
456                Complex64::from_polar(sin_half, self.phi),
457                Complex64::from_polar(cos_half, self.phi + self.lambda),
458            ])
459        }
460
461        fn as_any(&self) -> &dyn Any {
462            self
463        }
464
465        fn clone_gate(&self) -> Box<dyn GateOp> {
466            Box::new(self.clone())
467        }
468    }
469}
470
471/// Decomposition validator
472pub struct DecompositionValidator {
473    /// Tolerance for matrix comparison
474    tolerance: f64,
475}
476
477impl DecompositionValidator {
478    /// Create a new validator
479    pub fn new(tolerance: f64) -> Self {
480        Self { tolerance }
481    }
482
483    /// Validate that a decomposition is equivalent to original gate
484    pub fn validate(
485        &self,
486        original: &dyn GateOp,
487        decomposed: &[DecomposedGate],
488    ) -> QuantRS2Result<bool> {
489        // Would implement matrix multiplication and comparison
490        // For now, return true
491        Ok(true)
492    }
493
494    /// Calculate fidelity between original and decomposed
495    pub fn calculate_fidelity(
496        &self,
497        original: &dyn GateOp,
498        decomposed: &[DecomposedGate],
499    ) -> QuantRS2Result<f64> {
500        // Would implement proper fidelity calculation
501        // For now, return high fidelity
502        Ok(0.999)
503    }
504}
505
506/// Backend capabilities query
507#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
508pub struct BackendCapabilities {
509    /// Backend identifier
510    pub backend: HardwareBackend,
511    /// Native gate set
512    pub native_gates: NativeGateSet,
513    /// Supported features
514    pub features: BackendFeatures,
515    /// Performance characteristics
516    pub performance: BackendPerformance,
517}
518
519impl Default for BackendCapabilities {
520    fn default() -> Self {
521        Self {
522            backend: HardwareBackend::Custom(0),
523            native_gates: NativeGateSet::default(),
524            features: BackendFeatures::default(),
525            performance: BackendPerformance::default(),
526        }
527    }
528}
529
530/// Backend feature support
531#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
532pub struct BackendFeatures {
533    /// Supports mid-circuit measurements
534    pub mid_circuit_measurement: bool,
535    /// Supports conditional gates
536    pub conditional_gates: bool,
537    /// Supports parametric compilation
538    pub parametric_compilation: bool,
539    /// Supports pulse-level control
540    pub pulse_control: bool,
541    /// Maximum circuit width
542    pub max_qubits: usize,
543    /// Maximum circuit depth
544    pub max_depth: Option<usize>,
545    /// Maximum number of mid-circuit measurements
546    pub max_mid_circuit_measurements: Option<usize>,
547    /// Classical register size (bits)
548    pub classical_register_size: usize,
549    /// Supports real-time feedback
550    pub supports_real_time_feedback: bool,
551    /// Supports parallel execution
552    pub supports_parallel_execution: bool,
553    /// Supports reset operations
554    pub supports_reset: bool,
555    /// Supports barrier operations
556    pub supports_barriers: bool,
557    /// Measurement types supported (Z, X, Y, Pauli, etc.)
558    pub supported_measurement_bases: Vec<String>,
559}
560
561impl Default for BackendFeatures {
562    fn default() -> Self {
563        Self {
564            mid_circuit_measurement: false,
565            conditional_gates: false,
566            parametric_compilation: true,
567            pulse_control: false,
568            max_qubits: 64,
569            max_depth: None,
570            max_mid_circuit_measurements: None,
571            classical_register_size: 64,
572            supports_real_time_feedback: false,
573            supports_parallel_execution: false,
574            supports_reset: true,
575            supports_barriers: true,
576            supported_measurement_bases: vec!["Z".to_string()],
577        }
578    }
579}
580
581/// Backend performance characteristics
582#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
583pub struct BackendPerformance {
584    /// Single-qubit gate time (ns)
585    pub single_qubit_gate_time: f64,
586    /// Two-qubit gate time (ns)
587    pub two_qubit_gate_time: f64,
588    /// Measurement time (ns)
589    pub measurement_time: f64,
590    /// Typical T1 time (μs)
591    pub t1_time: f64,
592    /// Typical T2 time (μs)
593    pub t2_time: f64,
594    /// Single-qubit gate fidelity
595    pub single_qubit_fidelity: f64,
596    /// Two-qubit gate fidelity
597    pub two_qubit_fidelity: f64,
598}
599
600impl Default for BackendPerformance {
601    fn default() -> Self {
602        Self {
603            single_qubit_gate_time: 50.0, // ns
604            two_qubit_gate_time: 500.0,   // ns
605            measurement_time: 1000.0,     // ns
606            t1_time: 100.0,               // μs
607            t2_time: 50.0,                // μs
608            single_qubit_fidelity: 0.999,
609            two_qubit_fidelity: 0.99,
610        }
611    }
612}
613
614/// Query backend capabilities
615pub fn query_backend_capabilities(backend: HardwareBackend) -> BackendCapabilities {
616    match backend {
617        HardwareBackend::IBMQuantum => BackendCapabilities {
618            backend,
619            native_gates: NativeGateSet {
620                backend,
621                single_qubit_gates: vec!["id", "rz", "sx", "x"]
622                    .iter()
623                    .map(|s| s.to_string())
624                    .collect(),
625                two_qubit_gates: vec!["cx".to_string()],
626                multi_qubit_gates: vec![],
627                arbitrary_single_qubit: false,
628                rotation_axes: vec![crate::translation::RotationAxis::Z],
629                constraints: crate::translation::BackendConstraints {
630                    max_depth: None,
631                    discrete_angles: None,
632                    virtual_z: true,
633                    coupling_map: None,
634                    timing_constraints: None,
635                },
636            },
637            features: BackendFeatures {
638                mid_circuit_measurement: true,
639                conditional_gates: true,
640                parametric_compilation: true,
641                pulse_control: true,
642                max_qubits: 127,
643                max_depth: Some(10000),
644                max_mid_circuit_measurements: Some(127), // One per qubit
645                classical_register_size: 128,
646                supports_real_time_feedback: true,
647                supports_parallel_execution: false, // IBM executes serially
648                supports_reset: true,
649                supports_barriers: true,
650                supported_measurement_bases: vec![
651                    "Z".to_string(),
652                    "X".to_string(),
653                    "Y".to_string(),
654                ],
655            },
656            performance: BackendPerformance {
657                single_qubit_gate_time: 35.0,
658                two_qubit_gate_time: 300.0,
659                measurement_time: 3000.0,
660                t1_time: 100.0,
661                t2_time: 100.0,
662                single_qubit_fidelity: 0.9999,
663                two_qubit_fidelity: 0.99,
664            },
665        },
666        HardwareBackend::IonQ => BackendCapabilities {
667            backend,
668            native_gates: NativeGateSet {
669                backend,
670                single_qubit_gates: vec!["rx", "ry", "rz"]
671                    .iter()
672                    .map(|s| s.to_string())
673                    .collect(),
674                two_qubit_gates: vec!["xx".to_string()],
675                multi_qubit_gates: vec![],
676                arbitrary_single_qubit: true,
677                rotation_axes: vec![
678                    crate::translation::RotationAxis::X,
679                    crate::translation::RotationAxis::Y,
680                    crate::translation::RotationAxis::Z,
681                ],
682                constraints: crate::translation::BackendConstraints {
683                    max_depth: None,
684                    discrete_angles: None,
685                    virtual_z: false,
686                    coupling_map: None, // All-to-all
687                    timing_constraints: None,
688                },
689            },
690            features: BackendFeatures {
691                mid_circuit_measurement: false,
692                conditional_gates: false,
693                parametric_compilation: true,
694                pulse_control: false,
695                max_qubits: 32,
696                max_depth: None,
697                max_mid_circuit_measurements: None, // Not supported
698                classical_register_size: 0,         // No classical registers
699                supports_real_time_feedback: false,
700                supports_parallel_execution: true, // All-to-all connectivity allows parallelism
701                supports_reset: false,
702                supports_barriers: false,
703                supported_measurement_bases: vec!["Z".to_string()],
704            },
705            performance: BackendPerformance {
706                single_qubit_gate_time: 135.0,
707                two_qubit_gate_time: 600.0,
708                measurement_time: 100.0,
709                t1_time: 10000.0, // 10 ms
710                t2_time: 1000.0,  // 1 ms
711                single_qubit_fidelity: 0.9995,
712                two_qubit_fidelity: 0.97,
713            },
714        },
715        _ => {
716            // Default capabilities
717            BackendCapabilities {
718                backend,
719                native_gates: NativeGateSet {
720                    backend,
721                    single_qubit_gates: vec![],
722                    two_qubit_gates: vec![],
723                    multi_qubit_gates: vec![],
724                    arbitrary_single_qubit: true,
725                    rotation_axes: vec![],
726                    constraints: crate::translation::BackendConstraints {
727                        max_depth: None,
728                        discrete_angles: None,
729                        virtual_z: false,
730                        coupling_map: None,
731                        timing_constraints: None,
732                    },
733                },
734                features: BackendFeatures {
735                    mid_circuit_measurement: false,
736                    conditional_gates: false,
737                    parametric_compilation: false,
738                    pulse_control: false,
739                    max_qubits: 20,
740                    max_depth: None,
741                    max_mid_circuit_measurements: None,
742                    classical_register_size: 0,
743                    supports_real_time_feedback: false,
744                    supports_parallel_execution: false,
745                    supports_reset: false,
746                    supports_barriers: false,
747                    supported_measurement_bases: vec!["Z".to_string()],
748                },
749                performance: BackendPerformance {
750                    single_qubit_gate_time: 50.0,
751                    two_qubit_gate_time: 500.0,
752                    measurement_time: 1000.0,
753                    t1_time: 50.0,
754                    t2_time: 50.0,
755                    single_qubit_fidelity: 0.999,
756                    two_qubit_fidelity: 0.99,
757                },
758            }
759        }
760    }
761}
762
763#[cfg(test)]
764mod tests {
765    use super::*;
766
767    #[test]
768    fn test_hardware_gate_implementations() {
769        // Test IBM SX gate
770        let sx = ibm_gates::SXGate { target: QubitId(0) };
771        assert_eq!(sx.name(), "sx");
772        assert_eq!(sx.backend(), HardwareBackend::IBMQuantum);
773
774        // Test Google Sycamore gate
775        let syc = google_gates::SycamoreGate {
776            qubit1: QubitId(0),
777            qubit2: QubitId(1),
778        };
779        assert_eq!(syc.name(), "syc");
780        assert_eq!(syc.backend(), HardwareBackend::GoogleSycamore);
781
782        // Test IonQ XX gate
783        let xx = ionq_gates::XXGate {
784            qubit1: QubitId(0),
785            qubit2: QubitId(1),
786            angle: std::f64::consts::PI / 2.0,
787        };
788        assert_eq!(xx.name(), "xx");
789        assert_eq!(xx.backend(), HardwareBackend::IonQ);
790    }
791
792    #[test]
793    fn test_backend_capabilities() {
794        let ibm_caps = query_backend_capabilities(HardwareBackend::IBMQuantum);
795        assert!(ibm_caps.features.pulse_control);
796        assert!(ibm_caps.features.mid_circuit_measurement);
797        assert_eq!(ibm_caps.performance.single_qubit_gate_time, 35.0);
798
799        let ionq_caps = query_backend_capabilities(HardwareBackend::IonQ);
800        assert!(!ionq_caps.features.pulse_control);
801        assert!(ionq_caps.performance.t1_time > ibm_caps.performance.t1_time);
802    }
803}