Skip to main content

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 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
19/// Trait for hardware-specific gate implementations
20pub trait HardwareGate: GateOp {
21    /// Get the hardware backend this gate is native to
22    fn backend(&self) -> HardwareBackend;
23
24    /// Get hardware-specific metadata
25    fn metadata(&self) -> HashMap<String, String> {
26        HashMap::new()
27    }
28
29    /// Check if this gate requires calibration
30    fn requires_calibration(&self) -> bool {
31        true
32    }
33
34    /// Get calibration parameters
35    fn calibration_params(&self) -> Vec<String> {
36        vec![]
37    }
38}
39
40/// IBM-specific gate implementations
41pub mod ibm_gates {
42    use super::*;
43    use scirs2_core::Complex64;
44    use std::any::Any;
45
46    /// IBM's SX gate (√X gate)
47    #[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
95/// Google-specific gate implementations
96pub mod google_gates {
97    use super::*;
98    use scirs2_core::Complex64;
99    use std::any::Any;
100    use std::f64::consts::PI;
101
102    /// Google's Sycamore gate
103    #[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            // Sycamore gate matrix
120            // This is a simplified version - actual gate is more complex
121            let fsim_theta = PI / 2.0;
122            let fsim_phi = PI / 6.0;
123
124            // Create fSIM gate matrix
125            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    /// Google's powered gates (X^t, Y^t, Z^t)
173    #[derive(Debug, Clone, Copy)]
174    pub struct PoweredGate {
175        pub target: QubitId,
176        pub axis: char, // 'X', 'Y', or 'Z'
177        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
232/// IonQ-specific gate implementations
233pub mod ionq_gates {
234    use super::*;
235    use scirs2_core::Complex64;
236    use std::any::Any;
237
238    /// IonQ's XX gate (Mølmer-Sørensen gate)
239    #[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
306/// Rigetti-specific gate implementations
307pub mod rigetti_gates {
308    use super::*;
309    use scirs2_core::Complex64;
310    use std::any::Any;
311
312    /// Rigetti's parametrized XY gate
313    #[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
363/// Honeywell-specific gate implementations
364pub mod honeywell_gates {
365    use super::*;
366    use scirs2_core::Complex64;
367    use std::any::Any;
368
369    /// Honeywell's native ZZ interaction
370    #[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    /// Honeywell's U3 gate (general single-qubit rotation)
433    #[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
472/// Decomposition validator
473pub struct DecompositionValidator {
474    /// Tolerance for matrix comparison
475    tolerance: f64,
476}
477
478impl DecompositionValidator {
479    /// Create a new validator
480    pub const fn new(tolerance: f64) -> Self {
481        Self { tolerance }
482    }
483
484    /// Validate that a decomposition is equivalent to original gate.
485    ///
486    /// Reconstructs the product unitary of the decomposed native gates and
487    /// compares it to the original gate's matrix (up to a global phase). Returns
488    /// `true` iff the two unitaries agree within `self.tolerance`.
489    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    /// Calculate the (global-phase-invariant) gate fidelity between the original
499    /// gate and the product of its decomposition.
500    ///
501    /// For a `d`-dimensional Hilbert space the average-gate-fidelity-equivalent
502    /// quantity used here is `|Tr(U_orig† · U_decomp)|² / d²`, which equals 1
503    /// exactly when the two unitaries are identical up to a global phase.
504    ///
505    /// Returns an honest [`QuantRS2Error::UnsupportedOperation`] when the
506    /// decomposition uses a native gate this validator cannot reconstruct a
507    /// matrix for (rather than fabricating a high fidelity).
508    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        // Build a canonical ordering of the qubits the decomposition acts on.
521        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        // Original unitary embedded into the (possibly larger) qubit space.
532        let original_matrix = flat_to_square(&original.matrix()?)?;
533        let original_embedded = embed_unitary(&original_matrix, &original_qubits, &qubit_order)?;
534        // Start from identity and left-multiply the decomposed gates in order.
535        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        // Fidelity = |Tr(U_orig† U_decomp)|² / d².
548        let mut trace = Complex64::new(0.0, 0.0);
549        for row in 0..dim {
550            for col in 0..dim {
551                // (U_orig†)[row][col] = conj(U_orig[col][row]).
552                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        // Numerical guard: clamp into [0, 1].
558        Ok(fidelity.clamp(0.0, 1.0))
559    }
560}
561
562/// Convert a row-major flat unitary into a square `Vec<Complex64>` of the same
563/// row-major layout, validating that the length is a perfect square.
564fn 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
575/// Identity matrix of dimension `dim` in row-major layout.
576fn 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
584/// Row-major dense matrix multiply `a · b` (both `dim×dim`).
585fn 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
602/// Embed a unitary acting on `gate_qubits` into the full Hilbert space spanned
603/// by `qubit_order`, using the framework's little-endian convention (qubit at
604/// position `p` in `qubit_order` is bit `p` of the basis index).
605fn 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    // Map each gate qubit to its bit position in the full index.
623    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        // Decode the sub-index of the columns spanned by the gate qubits.
634        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        // The "rest" bits (qubits the gate does not touch) must be preserved.
641        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            // Build the full row index: copy untouched bits from full_col, set
647            // gate-qubit bits from sub_row.
648            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
663/// Reconstruct the 2x2 / 4x4 unitary of a native gate from its name and
664/// parameters. Returns an honest error for names this function does not yet
665/// know, so callers never silently assume a fabricated identity.
666fn 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            // sqrt(X) = 1/2 [[1+i, 1-i],[1-i, 1+i]].
705            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/// Backend capabilities query
796#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
797pub struct BackendCapabilities {
798    /// Backend identifier
799    pub backend: HardwareBackend,
800    /// Native gate set
801    pub native_gates: NativeGateSet,
802    /// Supported features
803    pub features: BackendFeatures,
804    /// Performance characteristics
805    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/// Backend feature support
820#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
821pub struct BackendFeatures {
822    /// Supports mid-circuit measurements
823    pub mid_circuit_measurement: bool,
824    /// Supports conditional gates
825    pub conditional_gates: bool,
826    /// Supports parametric compilation
827    pub parametric_compilation: bool,
828    /// Supports pulse-level control
829    pub pulse_control: bool,
830    /// Maximum circuit width
831    pub max_qubits: usize,
832    /// Maximum circuit depth
833    pub max_depth: Option<usize>,
834    /// Maximum number of mid-circuit measurements
835    pub max_mid_circuit_measurements: Option<usize>,
836    /// Classical register size (bits)
837    pub classical_register_size: usize,
838    /// Supports real-time feedback
839    pub supports_real_time_feedback: bool,
840    /// Supports parallel execution
841    pub supports_parallel_execution: bool,
842    /// Supports reset operations
843    pub supports_reset: bool,
844    /// Supports barrier operations
845    pub supports_barriers: bool,
846    /// Measurement types supported (Z, X, Y, Pauli, etc.)
847    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/// Backend performance characteristics
871#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
872pub struct BackendPerformance {
873    /// Single-qubit gate time (ns)
874    pub single_qubit_gate_time: f64,
875    /// Two-qubit gate time (ns)
876    pub two_qubit_gate_time: f64,
877    /// Measurement time (ns)
878    pub measurement_time: f64,
879    /// Typical T1 time (μs)
880    pub t1_time: f64,
881    /// Typical T2 time (μs)
882    pub t2_time: f64,
883    /// Single-qubit gate fidelity
884    pub single_qubit_fidelity: f64,
885    /// Two-qubit gate fidelity
886    pub two_qubit_fidelity: f64,
887}
888
889impl Default for BackendPerformance {
890    fn default() -> Self {
891        Self {
892            single_qubit_gate_time: 50.0, // ns
893            two_qubit_gate_time: 500.0,   // ns
894            measurement_time: 1000.0,     // ns
895            t1_time: 100.0,               // μs
896            t2_time: 50.0,                // μs
897            single_qubit_fidelity: 0.999,
898            two_qubit_fidelity: 0.99,
899        }
900    }
901}
902
903/// Query backend capabilities
904pub 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), // One per qubit
934                classical_register_size: 128,
935                supports_real_time_feedback: true,
936                supports_parallel_execution: false, // IBM executes serially
937                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, // All-to-all
973                    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, // Not supported
984                classical_register_size: 0,         // No classical registers
985                supports_real_time_feedback: false,
986                supports_parallel_execution: true, // All-to-all connectivity allows parallelism
987                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, // 10 ms
996                t2_time: 1000.0,  // 1 ms
997                single_qubit_fidelity: 0.9995,
998                two_qubit_fidelity: 0.97,
999            },
1000        },
1001        _ => {
1002            // Default capabilities
1003            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        // Test IBM SX gate
1056        let sx = ibm_gates::SXGate { target: QubitId(0) };
1057        assert_eq!(sx.name(), "sx");
1058        assert_eq!(sx.backend(), HardwareBackend::IBMQuantum);
1059
1060        // Test Google Sycamore gate
1061        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        // Test IonQ XX gate
1069        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}