Skip to main content

quantrs2_device/
translation.rs

1//! Gate translation for different hardware backends
2//!
3//! This module provides comprehensive gate translation capabilities, converting
4//! gates between different hardware native gate sets and decomposing complex
5//! gates into hardware-supported operations.
6
7use scirs2_core::ndarray::Array2;
8use scirs2_core::Complex64;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use std::f64::consts::PI;
12
13use quantrs2_circuit::builder::Circuit;
14use quantrs2_core::{
15    error::{QuantRS2Error, QuantRS2Result},
16    gate::{multi::*, single::*, GateOp},
17    qubit::QubitId,
18    synthesis::{decompose_single_qubit_zyz, decompose_two_qubit_kak, kak_to_gates},
19};
20
21/// Hardware backend types
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
23pub enum HardwareBackend {
24    /// IBM Quantum devices
25    IBMQuantum,
26    /// Google Quantum devices
27    GoogleSycamore,
28    /// IonQ trapped ion devices
29    IonQ,
30    /// Rigetti quantum devices
31    Rigetti,
32    /// Amazon Braket devices
33    AmazonBraket,
34    /// Azure Quantum devices
35    AzureQuantum,
36    /// Honeywell/Quantinuum devices
37    Honeywell,
38    /// Generic/Custom backend
39    Custom(u32), // Custom ID
40}
41
42/// Native gate set for a hardware backend
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44pub struct NativeGateSet {
45    /// Backend type
46    pub backend: HardwareBackend,
47    /// Single-qubit gates supported
48    pub single_qubit_gates: Vec<String>,
49    /// Two-qubit gates supported
50    pub two_qubit_gates: Vec<String>,
51    /// Multi-qubit gates supported (if any)
52    pub multi_qubit_gates: Vec<String>,
53    /// Whether arbitrary single-qubit rotations are supported
54    pub arbitrary_single_qubit: bool,
55    /// Supported rotation axes for parameterized gates
56    pub rotation_axes: Vec<RotationAxis>,
57    /// Additional backend-specific constraints
58    pub constraints: BackendConstraints,
59}
60
61impl Default for NativeGateSet {
62    fn default() -> Self {
63        Self {
64            backend: HardwareBackend::Custom(0),
65            single_qubit_gates: vec![
66                "H".to_string(),
67                "X".to_string(),
68                "Y".to_string(),
69                "Z".to_string(),
70            ],
71            two_qubit_gates: vec!["CNOT".to_string(), "CZ".to_string()],
72            multi_qubit_gates: Vec::new(),
73            arbitrary_single_qubit: true,
74            rotation_axes: vec![RotationAxis::X, RotationAxis::Y, RotationAxis::Z],
75            constraints: BackendConstraints::default(),
76        }
77    }
78}
79
80/// Rotation axes supported by hardware
81#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
82pub enum RotationAxis {
83    X,
84    Y,
85    Z,
86    XY(f64),               // Rotation in X-Y plane at angle
87    Custom(f64, f64, f64), // Arbitrary axis (x, y, z)
88}
89
90/// Backend-specific constraints
91#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
92pub struct BackendConstraints {
93    /// Maximum circuit depth
94    pub max_depth: Option<usize>,
95    /// Supported angles for rotation gates (None = continuous)
96    pub discrete_angles: Option<Vec<f64>>,
97    /// Whether phase gates are virtual (frame changes)
98    pub virtual_z: bool,
99    /// Coupling constraints (for 2-qubit gates)
100    pub coupling_map: Option<Vec<(usize, usize)>>,
101    /// Gate timing constraints
102    pub timing_constraints: Option<TimingConstraints>,
103}
104
105/// Timing constraints for hardware
106#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
107pub struct TimingConstraints {
108    /// Minimum time between gates on same qubit (ns)
109    pub min_gate_spacing: f64,
110    /// Required alignment for gate start times
111    pub time_alignment: f64,
112    /// Maximum circuit duration (ns)
113    pub max_duration: Option<f64>,
114}
115
116impl Default for TimingConstraints {
117    fn default() -> Self {
118        Self {
119            min_gate_spacing: 0.0,
120            time_alignment: 1.0,
121            max_duration: None,
122        }
123    }
124}
125
126/// Gate translator for converting between gate sets
127pub struct GateTranslator {
128    /// Native gate sets for each backend
129    native_gates: HashMap<HardwareBackend, NativeGateSet>,
130    /// Translation rules
131    translation_rules: HashMap<(HardwareBackend, String), TranslationRule>,
132    /// Decomposition cache
133    decomposition_cache: HashMap<String, Vec<DecomposedGate>>,
134}
135
136/// Translation rule for a specific gate
137#[derive(Debug, Clone)]
138pub struct TranslationRule {
139    /// Gate name to translate
140    pub gate_name: String,
141    /// Translation method
142    pub method: TranslationMethod,
143    /// Expected fidelity after translation
144    pub fidelity: f64,
145    /// Number of native gates after translation
146    pub gate_count: usize,
147}
148
149/// Methods for translating gates
150pub enum TranslationMethod {
151    /// Direct mapping to native gate
152    Direct(String),
153    /// Fixed decomposition
154    FixedDecomposition(Vec<DecomposedGate>),
155    /// Parameterized decomposition
156    ParameterizedDecomposition(Box<dyn Fn(Vec<f64>) -> Vec<DecomposedGate> + Send + Sync>),
157    /// Use synthesis algorithm
158    Synthesis(SynthesisMethod),
159    /// Custom translation function
160    Custom(Box<dyn Fn(&dyn GateOp) -> QuantRS2Result<Vec<DecomposedGate>> + Send + Sync>),
161}
162
163impl std::fmt::Debug for TranslationMethod {
164    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165        match self {
166            Self::Direct(s) => write!(f, "Direct({s})"),
167            Self::FixedDecomposition(gates) => write!(f, "FixedDecomposition({gates:?})"),
168            Self::ParameterizedDecomposition(_) => {
169                write!(f, "ParameterizedDecomposition(<function>)")
170            }
171            Self::Synthesis(method) => write!(f, "Synthesis({method:?})"),
172            Self::Custom(_) => write!(f, "Custom(<function>)"),
173        }
174    }
175}
176
177impl Clone for TranslationMethod {
178    fn clone(&self) -> Self {
179        match self {
180            Self::Direct(s) => Self::Direct(s.clone()),
181            Self::FixedDecomposition(gates) => Self::FixedDecomposition(gates.clone()),
182            Self::ParameterizedDecomposition(_) => {
183                panic!(
184                    "Cannot clone ParameterizedDecomposition - use Arc<TranslationMethod> instead"
185                )
186            }
187            Self::Synthesis(method) => Self::Synthesis(*method),
188            Self::Custom(_) => {
189                panic!("Cannot clone Custom - use Arc<TranslationMethod> instead")
190            }
191        }
192    }
193}
194
195/// Decomposed gate representation
196#[derive(Debug, Clone)]
197pub struct DecomposedGate {
198    /// Gate name in native set
199    pub native_gate: String,
200    /// Target qubits
201    pub qubits: Vec<QubitId>,
202    /// Parameters (if any)
203    pub parameters: Vec<f64>,
204    /// Optional global phase
205    pub global_phase: Option<f64>,
206}
207
208/// Synthesis methods for gate decomposition
209#[derive(Debug, Clone, Copy)]
210pub enum SynthesisMethod {
211    /// Single-qubit synthesis (ZYZ, XYX, etc.)
212    SingleQubitZYZ,
213    SingleQubitXYX,
214    SingleQubitU3,
215    /// Two-qubit synthesis
216    KAKDecomposition,
217    /// Clifford+T synthesis
218    CliffordT,
219    /// Solovay-Kitaev
220    SolovayKitaev,
221}
222
223impl GateTranslator {
224    /// Create a new gate translator
225    pub fn new() -> Self {
226        let mut translator = Self {
227            native_gates: HashMap::new(),
228            translation_rules: HashMap::new(),
229            decomposition_cache: HashMap::new(),
230        };
231
232        // Initialize standard backend gate sets
233        translator.init_ibm_gates();
234        translator.init_google_gates();
235        translator.init_ionq_gates();
236        translator.init_rigetti_gates();
237        translator.init_amazon_gates();
238        translator.init_azure_gates();
239        translator.init_honeywell_gates();
240
241        translator
242    }
243
244    /// Initialize IBM Quantum native gates
245    fn init_ibm_gates(&mut self) {
246        let gate_set = NativeGateSet {
247            backend: HardwareBackend::IBMQuantum,
248            single_qubit_gates: vec![
249                "id".to_string(),
250                "rz".to_string(),
251                "sx".to_string(),
252                "x".to_string(),
253            ],
254            two_qubit_gates: vec!["cx".to_string()],
255            multi_qubit_gates: vec![],
256            arbitrary_single_qubit: false,
257            rotation_axes: vec![RotationAxis::Z],
258            constraints: BackendConstraints {
259                max_depth: None,
260                discrete_angles: None,
261                virtual_z: true,
262                coupling_map: None,
263                timing_constraints: None,
264            },
265        };
266
267        self.native_gates
268            .insert(HardwareBackend::IBMQuantum, gate_set);
269
270        // Add translation rules
271        self.add_ibm_translation_rules();
272    }
273
274    /// Add IBM-specific translation rules
275    fn add_ibm_translation_rules(&mut self) {
276        let backend = HardwareBackend::IBMQuantum;
277
278        // H = RZ(π/2) SX RZ(π/2)
279        self.translation_rules.insert(
280            (backend, "H".to_string()),
281            TranslationRule {
282                gate_name: "H".to_string(),
283                method: TranslationMethod::FixedDecomposition(vec![
284                    DecomposedGate {
285                        native_gate: "rz".to_string(),
286                        qubits: vec![QubitId(0)],
287                        parameters: vec![PI / 2.0],
288                        global_phase: None,
289                    },
290                    DecomposedGate {
291                        native_gate: "sx".to_string(),
292                        qubits: vec![QubitId(0)],
293                        parameters: vec![],
294                        global_phase: None,
295                    },
296                    DecomposedGate {
297                        native_gate: "rz".to_string(),
298                        qubits: vec![QubitId(0)],
299                        parameters: vec![PI / 2.0],
300                        global_phase: None,
301                    },
302                ]),
303                fidelity: 0.9999,
304                gate_count: 3,
305            },
306        );
307
308        // Y = RZ(π) X
309        self.translation_rules.insert(
310            (backend, "Y".to_string()),
311            TranslationRule {
312                gate_name: "Y".to_string(),
313                method: TranslationMethod::FixedDecomposition(vec![
314                    DecomposedGate {
315                        native_gate: "rz".to_string(),
316                        qubits: vec![QubitId(0)],
317                        parameters: vec![PI],
318                        global_phase: None,
319                    },
320                    DecomposedGate {
321                        native_gate: "x".to_string(),
322                        qubits: vec![QubitId(0)],
323                        parameters: vec![],
324                        global_phase: None,
325                    },
326                ]),
327                fidelity: 0.9999,
328                gate_count: 2,
329            },
330        );
331
332        // S = RZ(π/2)
333        self.translation_rules.insert(
334            (backend, "S".to_string()),
335            TranslationRule {
336                gate_name: "S".to_string(),
337                method: TranslationMethod::Direct("rz".to_string()),
338                fidelity: 1.0,
339                gate_count: 1,
340            },
341        );
342
343        // T = RZ(π/4)
344        self.translation_rules.insert(
345            (backend, "T".to_string()),
346            TranslationRule {
347                gate_name: "T".to_string(),
348                method: TranslationMethod::Direct("rz".to_string()),
349                fidelity: 1.0,
350                gate_count: 1,
351            },
352        );
353
354        // RX using RZ and X gates
355        self.translation_rules.insert(
356            (backend, "RX".to_string()),
357            TranslationRule {
358                gate_name: "RX".to_string(),
359                method: TranslationMethod::ParameterizedDecomposition(Box::new(|params| {
360                    let theta = params[0];
361                    vec![
362                        DecomposedGate {
363                            native_gate: "rz".to_string(),
364                            qubits: vec![QubitId(0)],
365                            parameters: vec![PI / 2.0],
366                            global_phase: None,
367                        },
368                        DecomposedGate {
369                            native_gate: "sx".to_string(),
370                            qubits: vec![QubitId(0)],
371                            parameters: vec![],
372                            global_phase: None,
373                        },
374                        DecomposedGate {
375                            native_gate: "rz".to_string(),
376                            qubits: vec![QubitId(0)],
377                            parameters: vec![theta - PI / 2.0],
378                            global_phase: None,
379                        },
380                        DecomposedGate {
381                            native_gate: "sx".to_string(),
382                            qubits: vec![QubitId(0)],
383                            parameters: vec![],
384                            global_phase: None,
385                        },
386                        DecomposedGate {
387                            native_gate: "rz".to_string(),
388                            qubits: vec![QubitId(0)],
389                            parameters: vec![-PI / 2.0],
390                            global_phase: None,
391                        },
392                    ]
393                })),
394                fidelity: 0.9998,
395                gate_count: 5,
396            },
397        );
398
399        // CNOT is called CX in IBM
400        self.translation_rules.insert(
401            (backend, "CNOT".to_string()),
402            TranslationRule {
403                gate_name: "CNOT".to_string(),
404                method: TranslationMethod::Direct("cx".to_string()),
405                fidelity: 1.0,
406                gate_count: 1,
407            },
408        );
409    }
410
411    /// Initialize Google Sycamore native gates
412    fn init_google_gates(&mut self) {
413        let gate_set = NativeGateSet {
414            backend: HardwareBackend::GoogleSycamore,
415            single_qubit_gates: vec![
416                "ph".to_string(),    // Phase gate
417                "x_pow".to_string(), // X^t gate
418                "y_pow".to_string(), // Y^t gate
419                "z_pow".to_string(), // Z^t gate
420            ],
421            two_qubit_gates: vec![
422                "syc".to_string(), // Sycamore gate
423                "sqrt_iswap".to_string(),
424            ],
425            multi_qubit_gates: vec![],
426            arbitrary_single_qubit: true,
427            rotation_axes: vec![RotationAxis::X, RotationAxis::Y, RotationAxis::Z],
428            constraints: BackendConstraints {
429                max_depth: None,
430                discrete_angles: None,
431                virtual_z: false,
432                coupling_map: None,
433                timing_constraints: None,
434            },
435        };
436
437        self.native_gates
438            .insert(HardwareBackend::GoogleSycamore, gate_set);
439        self.add_google_translation_rules();
440    }
441
442    /// Add Google-specific translation rules
443    fn add_google_translation_rules(&mut self) {
444        let backend = HardwareBackend::GoogleSycamore;
445
446        // Direct mappings for powered gates
447        self.translation_rules.insert(
448            (backend, "X".to_string()),
449            TranslationRule {
450                gate_name: "X".to_string(),
451                method: TranslationMethod::FixedDecomposition(vec![DecomposedGate {
452                    native_gate: "x_pow".to_string(),
453                    qubits: vec![QubitId(0)],
454                    parameters: vec![1.0],
455                    global_phase: None,
456                }]),
457                fidelity: 1.0,
458                gate_count: 1,
459            },
460        );
461
462        // CNOT using Sycamore gates
463        self.translation_rules.insert(
464            (backend, "CNOT".to_string()),
465            TranslationRule {
466                gate_name: "CNOT".to_string(),
467                method: TranslationMethod::FixedDecomposition(vec![
468                    // This is a simplified version - actual decomposition is more complex
469                    DecomposedGate {
470                        native_gate: "syc".to_string(),
471                        qubits: vec![QubitId(0), QubitId(1)],
472                        parameters: vec![],
473                        global_phase: None,
474                    },
475                    // Additional single-qubit corrections would go here
476                ]),
477                fidelity: 0.998,
478                gate_count: 3,
479            },
480        );
481    }
482
483    /// Initialize IonQ native gates
484    fn init_ionq_gates(&mut self) {
485        let gate_set = NativeGateSet {
486            backend: HardwareBackend::IonQ,
487            single_qubit_gates: vec!["rx".to_string(), "ry".to_string(), "rz".to_string()],
488            two_qubit_gates: vec![
489                "xx".to_string(), // Mølmer-Sørensen gate
490            ],
491            multi_qubit_gates: vec![],
492            arbitrary_single_qubit: true,
493            rotation_axes: vec![RotationAxis::X, RotationAxis::Y, RotationAxis::Z],
494            constraints: BackendConstraints {
495                max_depth: None,
496                discrete_angles: None,
497                virtual_z: false,
498                coupling_map: None, // All-to-all connectivity
499                timing_constraints: None,
500            },
501        };
502
503        self.native_gates.insert(HardwareBackend::IonQ, gate_set);
504        self.add_ionq_translation_rules();
505    }
506
507    /// Add IonQ-specific translation rules
508    fn add_ionq_translation_rules(&mut self) {
509        let backend = HardwareBackend::IonQ;
510
511        // CNOT using XX gate
512        self.translation_rules.insert(
513            (backend, "CNOT".to_string()),
514            TranslationRule {
515                gate_name: "CNOT".to_string(),
516                method: TranslationMethod::FixedDecomposition(vec![
517                    DecomposedGate {
518                        native_gate: "ry".to_string(),
519                        qubits: vec![QubitId(0)],
520                        parameters: vec![PI / 2.0],
521                        global_phase: None,
522                    },
523                    DecomposedGate {
524                        native_gate: "xx".to_string(),
525                        qubits: vec![QubitId(0), QubitId(1)],
526                        parameters: vec![PI / 2.0],
527                        global_phase: None,
528                    },
529                    DecomposedGate {
530                        native_gate: "rx".to_string(),
531                        qubits: vec![QubitId(0)],
532                        parameters: vec![-PI / 2.0],
533                        global_phase: None,
534                    },
535                    DecomposedGate {
536                        native_gate: "rx".to_string(),
537                        qubits: vec![QubitId(1)],
538                        parameters: vec![-PI / 2.0],
539                        global_phase: None,
540                    },
541                    DecomposedGate {
542                        native_gate: "ry".to_string(),
543                        qubits: vec![QubitId(0)],
544                        parameters: vec![-PI / 2.0],
545                        global_phase: None,
546                    },
547                ]),
548                fidelity: 0.999,
549                gate_count: 5,
550            },
551        );
552    }
553
554    /// Initialize Rigetti native gates
555    fn init_rigetti_gates(&mut self) {
556        let gate_set = NativeGateSet {
557            backend: HardwareBackend::Rigetti,
558            single_qubit_gates: vec!["rx".to_string(), "rz".to_string()],
559            two_qubit_gates: vec![
560                "cz".to_string(),
561                "xy".to_string(), // Parametrized XY gate
562            ],
563            multi_qubit_gates: vec![],
564            arbitrary_single_qubit: true,
565            rotation_axes: vec![RotationAxis::X, RotationAxis::Z],
566            constraints: BackendConstraints {
567                max_depth: None,
568                discrete_angles: None,
569                virtual_z: false,
570                coupling_map: None,
571                timing_constraints: None,
572            },
573        };
574
575        self.native_gates.insert(HardwareBackend::Rigetti, gate_set);
576    }
577
578    /// Initialize Amazon Braket native gates
579    fn init_amazon_gates(&mut self) {
580        // Amazon supports multiple backends, this is a generic set
581        let gate_set = NativeGateSet {
582            backend: HardwareBackend::AmazonBraket,
583            single_qubit_gates: vec![
584                "h".to_string(),
585                "x".to_string(),
586                "y".to_string(),
587                "z".to_string(),
588                "rx".to_string(),
589                "ry".to_string(),
590                "rz".to_string(),
591            ],
592            two_qubit_gates: vec!["cnot".to_string(), "cz".to_string(), "swap".to_string()],
593            multi_qubit_gates: vec!["ccnot".to_string()],
594            arbitrary_single_qubit: true,
595            rotation_axes: vec![RotationAxis::X, RotationAxis::Y, RotationAxis::Z],
596            constraints: BackendConstraints {
597                max_depth: None,
598                discrete_angles: None,
599                virtual_z: false,
600                coupling_map: None,
601                timing_constraints: None,
602            },
603        };
604
605        self.native_gates
606            .insert(HardwareBackend::AmazonBraket, gate_set);
607    }
608
609    /// Initialize Azure Quantum native gates
610    fn init_azure_gates(&mut self) {
611        // Azure supports multiple backends, this is a generic set
612        let gate_set = NativeGateSet {
613            backend: HardwareBackend::AzureQuantum,
614            single_qubit_gates: vec![
615                "h".to_string(),
616                "x".to_string(),
617                "y".to_string(),
618                "z".to_string(),
619                "s".to_string(),
620                "t".to_string(),
621                "rx".to_string(),
622                "ry".to_string(),
623                "rz".to_string(),
624            ],
625            two_qubit_gates: vec!["cnot".to_string(), "cz".to_string()],
626            multi_qubit_gates: vec![],
627            arbitrary_single_qubit: true,
628            rotation_axes: vec![RotationAxis::X, RotationAxis::Y, RotationAxis::Z],
629            constraints: BackendConstraints {
630                max_depth: None,
631                discrete_angles: None,
632                virtual_z: false,
633                coupling_map: None,
634                timing_constraints: None,
635            },
636        };
637
638        self.native_gates
639            .insert(HardwareBackend::AzureQuantum, gate_set);
640    }
641
642    /// Initialize Honeywell/Quantinuum native gates
643    fn init_honeywell_gates(&mut self) {
644        let gate_set = NativeGateSet {
645            backend: HardwareBackend::Honeywell,
646            single_qubit_gates: vec![
647                "u1".to_string(), // Phase gate
648                "u2".to_string(), // Single-qubit gate
649                "u3".to_string(), // General single-qubit gate
650            ],
651            two_qubit_gates: vec![
652                "zz".to_string(), // Native ZZ interaction
653            ],
654            multi_qubit_gates: vec![],
655            arbitrary_single_qubit: true,
656            rotation_axes: vec![RotationAxis::Custom(1.0, 0.0, 0.0)],
657            constraints: BackendConstraints {
658                max_depth: None,
659                discrete_angles: None,
660                virtual_z: false,
661                coupling_map: None, // All-to-all connectivity
662                timing_constraints: None,
663            },
664        };
665
666        self.native_gates
667            .insert(HardwareBackend::Honeywell, gate_set);
668    }
669
670    /// Get native gate set for a backend
671    pub fn get_native_gates(&self, backend: HardwareBackend) -> Option<&NativeGateSet> {
672        self.native_gates.get(&backend)
673    }
674
675    /// Check if a gate is native to a backend
676    pub fn is_native_gate(&self, backend: HardwareBackend, gate_name: &str) -> bool {
677        self.native_gates.get(&backend).map_or(false, |gate_set| {
678            gate_set.single_qubit_gates.contains(&gate_name.to_string())
679                || gate_set.two_qubit_gates.contains(&gate_name.to_string())
680                || gate_set.multi_qubit_gates.contains(&gate_name.to_string())
681        })
682    }
683
684    /// Translate a gate to native gate set
685    pub fn translate_gate(
686        &mut self,
687        gate: &dyn GateOp,
688        backend: HardwareBackend,
689    ) -> QuantRS2Result<Vec<DecomposedGate>> {
690        let gate_name = gate.name();
691
692        // Check if already native
693        if self.is_native_gate(backend, gate_name) {
694            return Ok(vec![DecomposedGate {
695                native_gate: gate_name.to_string(),
696                qubits: gate.qubits(),
697                parameters: self.extract_parameters(gate)?,
698                global_phase: None,
699            }]);
700        }
701
702        // Check cache
703        let cache_key = format!("{gate_name}_{backend:?}");
704        if let Some(cached) = self.decomposition_cache.get(&cache_key) {
705            return Ok(self.remap_qubits(cached.clone(), &gate.qubits()));
706        }
707
708        // Look for translation rule
709        if let Some(rule) = self
710            .translation_rules
711            .get(&(backend, gate_name.to_string()))
712        {
713            let decomposition = match &rule.method {
714                TranslationMethod::Direct(native_name) => {
715                    vec![DecomposedGate {
716                        native_gate: native_name.clone(),
717                        qubits: gate.qubits(),
718                        parameters: self.extract_parameters(gate)?,
719                        global_phase: None,
720                    }]
721                }
722                TranslationMethod::FixedDecomposition(gates) => {
723                    self.remap_qubits(gates.clone(), &gate.qubits())
724                }
725                TranslationMethod::ParameterizedDecomposition(func) => {
726                    let params = self.extract_parameters(gate)?;
727                    let gates = func(params);
728                    self.remap_qubits(gates, &gate.qubits())
729                }
730                TranslationMethod::Synthesis(method) => {
731                    self.synthesize_gate(gate, *method, backend)?
732                }
733                TranslationMethod::Custom(func) => func(gate)?,
734            };
735
736            // Cache the result
737            self.decomposition_cache
738                .insert(cache_key, decomposition.clone());
739            return Ok(decomposition);
740        }
741
742        // Fall back to synthesis based on gate type
743        match gate.num_qubits() {
744            1 => self.synthesize_gate(gate, SynthesisMethod::SingleQubitZYZ, backend),
745            2 => self.synthesize_gate(gate, SynthesisMethod::KAKDecomposition, backend),
746            _ => Err(QuantRS2Error::InvalidInput(format!(
747                "No translation available for {}-qubit gate {}",
748                gate.num_qubits(),
749                gate_name
750            ))),
751        }
752    }
753
754    /// Translate an entire circuit
755    pub fn translate_circuit<const N: usize>(
756        &mut self,
757        circuit: &Circuit<N>,
758        backend: HardwareBackend,
759    ) -> QuantRS2Result<Circuit<N>> {
760        let mut translated = Circuit::<N>::new();
761
762        for gate in circuit.gates() {
763            let decomposed = self.translate_gate(gate.as_ref(), backend)?;
764
765            for dec_gate in decomposed {
766                self.add_decomposed_gate(&mut translated, &dec_gate)?;
767            }
768        }
769
770        Ok(translated)
771    }
772
773    /// Extract parameters from a gate
774    ///
775    /// Returns the rotation angles (or other real parameters) carried by a
776    /// parameterized gate. Common single-qubit rotation gates store an angle
777    /// in `theta`; we recover those by `as_any` downcasts. Non-parameterized
778    /// gates produce an empty `Vec`.
779    fn extract_parameters(&self, gate: &dyn GateOp) -> QuantRS2Result<Vec<f64>> {
780        if !gate.is_parameterized() {
781            return Ok(Vec::new());
782        }
783
784        // Concrete downcasts for the parameterized single-qubit rotations
785        // exported by quantrs2-core. The order matches the gate's natural
786        // matrix representation (single rotation angle).
787        let any = gate.as_any();
788        if let Some(g) = any.downcast_ref::<RotationX>() {
789            return Ok(vec![g.theta]);
790        }
791        if let Some(g) = any.downcast_ref::<RotationY>() {
792            return Ok(vec![g.theta]);
793        }
794        if let Some(g) = any.downcast_ref::<RotationZ>() {
795            return Ok(vec![g.theta]);
796        }
797        if let Some(g) = any.downcast_ref::<CRX>() {
798            return Ok(vec![g.theta]);
799        }
800        if let Some(g) = any.downcast_ref::<CRY>() {
801            return Ok(vec![g.theta]);
802        }
803        if let Some(g) = any.downcast_ref::<CRZ>() {
804            return Ok(vec![g.theta]);
805        }
806
807        // Fallback: gate reports as parameterized but we don't have a known
808        // concrete representation. Return an empty vector rather than fail
809        // so the caller can continue with synthesis on the matrix.
810        Ok(Vec::new())
811    }
812
813    /// Remap qubits in decomposition
814    fn remap_qubits(
815        &self,
816        mut gates: Vec<DecomposedGate>,
817        actual_qubits: &[QubitId],
818    ) -> Vec<DecomposedGate> {
819        for gate in &mut gates {
820            for qubit in &mut gate.qubits {
821                if qubit.id() < actual_qubits.len() as u32 {
822                    *qubit = actual_qubits[qubit.id() as usize];
823                }
824            }
825        }
826        gates
827    }
828
829    /// Convert a concrete native gate produced by the core decomposition
830    /// routines into a [`DecomposedGate`] using its native name and parameters.
831    fn native_gate_to_decomposed(gate: &dyn GateOp) -> QuantRS2Result<DecomposedGate> {
832        let any = gate.as_any();
833        let qubits = gate.qubits();
834
835        // Parameterized single-qubit rotations.
836        if let Some(g) = any.downcast_ref::<RotationX>() {
837            return Ok(DecomposedGate {
838                native_gate: "rx".to_string(),
839                qubits,
840                parameters: vec![g.theta],
841                global_phase: None,
842            });
843        }
844        if let Some(g) = any.downcast_ref::<RotationY>() {
845            return Ok(DecomposedGate {
846                native_gate: "ry".to_string(),
847                qubits,
848                parameters: vec![g.theta],
849                global_phase: None,
850            });
851        }
852        if let Some(g) = any.downcast_ref::<RotationZ>() {
853            return Ok(DecomposedGate {
854                native_gate: "rz".to_string(),
855                qubits,
856                parameters: vec![g.theta],
857                global_phase: None,
858            });
859        }
860
861        // Non-parameterized gates that the KAK lowering may emit.
862        let native_gate = match gate.name() {
863            "CNOT" => "cx",
864            "CZ" => "cz",
865            "H" => "h",
866            "X" => "x",
867            "Y" => "y",
868            "Z" => "z",
869            "S" => "s",
870            "T" => "t",
871            "SWAP" => "swap",
872            other => {
873                return Err(QuantRS2Error::InvalidInput(format!(
874                    "KAK decomposition produced an unsupported native gate: {other}"
875                )));
876            }
877        };
878
879        Ok(DecomposedGate {
880            native_gate: native_gate.to_string(),
881            qubits,
882            parameters: Vec::new(),
883            global_phase: None,
884        })
885    }
886
887    /// Synthesize a gate using specified method
888    fn synthesize_gate(
889        &self,
890        gate: &dyn GateOp,
891        method: SynthesisMethod,
892        backend: HardwareBackend,
893    ) -> QuantRS2Result<Vec<DecomposedGate>> {
894        match method {
895            SynthesisMethod::SingleQubitZYZ => {
896                // Use ZYZ decomposition for single-qubit gates
897                if gate.num_qubits() != 1 {
898                    return Err(QuantRS2Error::InvalidInput(
899                        "ZYZ synthesis only works for single-qubit gates".into(),
900                    ));
901                }
902
903                let matrix_vec = gate.matrix()?;
904                let matrix = Array2::from_shape_vec((2, 2), matrix_vec)
905                    .map_err(|_| QuantRS2Error::InvalidInput("Invalid matrix shape".into()))?;
906                let decomp = decompose_single_qubit_zyz(&matrix.view())?;
907
908                Ok(vec![
909                    DecomposedGate {
910                        native_gate: "rz".to_string(),
911                        qubits: gate.qubits(),
912                        parameters: vec![decomp.theta1],
913                        global_phase: None,
914                    },
915                    DecomposedGate {
916                        native_gate: "ry".to_string(),
917                        qubits: gate.qubits(),
918                        parameters: vec![decomp.phi],
919                        global_phase: None,
920                    },
921                    DecomposedGate {
922                        native_gate: "rz".to_string(),
923                        qubits: gate.qubits(),
924                        parameters: vec![decomp.theta2],
925                        global_phase: Some(decomp.global_phase),
926                    },
927                ])
928            }
929            SynthesisMethod::KAKDecomposition => {
930                // Use KAK (Cartan) decomposition for two-qubit gates.
931                if gate.num_qubits() != 2 {
932                    return Err(QuantRS2Error::InvalidInput(
933                        "KAK decomposition only works for two-qubit gates".into(),
934                    ));
935                }
936
937                let qubits = gate.qubits();
938                if qubits.len() != 2 {
939                    return Err(QuantRS2Error::InvalidInput(
940                        "KAK decomposition requires a gate acting on exactly two qubits".into(),
941                    ));
942                }
943
944                // Obtain the 4x4 unitary of the gate and reshape into a matrix.
945                let matrix_vec = gate.matrix()?;
946                let matrix = Array2::from_shape_vec((4, 4), matrix_vec).map_err(|_| {
947                    QuantRS2Error::InvalidInput(
948                        "Two-qubit gate matrix must be 4x4 for KAK decomposition".into(),
949                    )
950                })?;
951
952                // Run the Cartan (KAK) decomposition and lower it to a native
953                // gate sequence (rz/ry/rx single-qubit rotations + CNOTs).
954                let kak = decompose_two_qubit_kak(&matrix.view())?;
955                let native_gates = kak_to_gates(&kak, qubits[0], qubits[1])?;
956
957                // The first single-qubit layer carries the decomposition's
958                // global phase so that callers can track it.
959                let global_phase = kak.global_phase;
960                let mut decomposed = Vec::with_capacity(native_gates.len());
961                let mut phase_emitted = false;
962                for native in &native_gates {
963                    let mut dg = Self::native_gate_to_decomposed(native.as_ref())?;
964                    if !phase_emitted && global_phase.abs() > 1e-12 {
965                        dg.global_phase = Some(global_phase);
966                        phase_emitted = true;
967                    }
968                    decomposed.push(dg);
969                }
970
971                Ok(decomposed)
972            }
973            _ => Err(QuantRS2Error::InvalidInput(format!(
974                "Synthesis method {method:?} not yet implemented"
975            ))),
976        }
977    }
978
979    /// Add a decomposed gate to circuit
980    fn add_decomposed_gate<const N: usize>(
981        &self,
982        circuit: &mut Circuit<N>,
983        gate: &DecomposedGate,
984    ) -> QuantRS2Result<()> {
985        match gate.native_gate.as_str() {
986            // IBM gates
987            "id" => {} // Identity, do nothing
988            "x" => {
989                let _ = circuit.x(gate.qubits[0]);
990            }
991            "sx" => {
992                let _ = circuit.sx(gate.qubits[0]);
993            }
994            "rz" => {
995                let _ = circuit.rz(gate.qubits[0], gate.parameters[0]);
996            }
997            "cx" => {
998                let _ = circuit.cnot(gate.qubits[0], gate.qubits[1]);
999            }
1000
1001            // IonQ gates
1002            "rx" => {
1003                let _ = circuit.rx(gate.qubits[0], gate.parameters[0]);
1004            }
1005            "ry" => {
1006                let _ = circuit.ry(gate.qubits[0], gate.parameters[0]);
1007            }
1008            "xx" => {
1009                // XX gate would need to be added to circuit builder
1010                // For now, decompose to CNOT
1011                let _ = circuit.cnot(gate.qubits[0], gate.qubits[1]);
1012            }
1013
1014            // Common gates
1015            "h" => {
1016                let _ = circuit.h(gate.qubits[0]);
1017            }
1018            "y" => {
1019                let _ = circuit.y(gate.qubits[0]);
1020            }
1021            "z" => {
1022                let _ = circuit.z(gate.qubits[0]);
1023            }
1024            "s" => {
1025                let _ = circuit.s(gate.qubits[0]);
1026            }
1027            "t" => {
1028                let _ = circuit.t(gate.qubits[0]);
1029            }
1030            "cnot" => {
1031                let _ = circuit.cnot(gate.qubits[0], gate.qubits[1]);
1032            }
1033            "cz" => {
1034                let _ = circuit.cz(gate.qubits[0], gate.qubits[1]);
1035            }
1036            "swap" => {
1037                let _ = circuit.swap(gate.qubits[0], gate.qubits[1]);
1038            }
1039            "ccnot" => {
1040                let _ = circuit.toffoli(gate.qubits[0], gate.qubits[1], gate.qubits[2]);
1041            }
1042
1043            _ => {
1044                return Err(QuantRS2Error::InvalidInput(format!(
1045                    "Unknown native gate: {}",
1046                    gate.native_gate
1047                )));
1048            }
1049        }
1050
1051        Ok(())
1052    }
1053}
1054
1055/// Translation optimizer that minimizes gate count or error
1056pub struct TranslationOptimizer {
1057    /// Gate translator
1058    translator: GateTranslator,
1059    /// Optimization strategy
1060    strategy: OptimizationStrategy,
1061}
1062
1063/// Optimization strategies for translation
1064#[derive(Debug, Clone, Copy)]
1065pub enum OptimizationStrategy {
1066    /// Minimize gate count
1067    MinimizeGateCount,
1068    /// Minimize error (maximize fidelity)
1069    MinimizeError,
1070    /// Minimize circuit depth
1071    MinimizeDepth,
1072    /// Balance between gate count and error
1073    Balanced { weight: f64 },
1074}
1075
1076impl TranslationOptimizer {
1077    /// Create a new translation optimizer
1078    pub fn new(strategy: OptimizationStrategy) -> Self {
1079        Self {
1080            translator: GateTranslator::new(),
1081            strategy,
1082        }
1083    }
1084
1085    /// Find optimal translation for a gate
1086    pub fn optimize_translation(
1087        &mut self,
1088        gate: &dyn GateOp,
1089        backend: HardwareBackend,
1090    ) -> QuantRS2Result<Vec<DecomposedGate>> {
1091        // Try multiple translation methods and pick the best
1092        let candidates = vec![
1093            self.translator.translate_gate(gate, backend)?,
1094            // Could try alternative decompositions here
1095        ];
1096
1097        // Select best based on strategy
1098        let best = match self.strategy {
1099            OptimizationStrategy::MinimizeGateCount => candidates
1100                .into_iter()
1101                .min_by_key(|c| c.len())
1102                .unwrap_or_default(),
1103            OptimizationStrategy::MinimizeError => {
1104                // Would need fidelity information
1105                candidates.into_iter().next().unwrap_or_default()
1106            }
1107            OptimizationStrategy::MinimizeDepth => {
1108                // Would need to calculate depth
1109                candidates.into_iter().next().unwrap_or_default()
1110            }
1111            OptimizationStrategy::Balanced { weight } => {
1112                // Weighted optimization
1113                candidates.into_iter().next().unwrap_or_default()
1114            }
1115        };
1116
1117        Ok(best)
1118    }
1119}
1120
1121/// Validate that a circuit uses only native gates
1122pub fn validate_native_circuit<const N: usize>(
1123    circuit: &Circuit<N>,
1124    backend: HardwareBackend,
1125) -> QuantRS2Result<bool> {
1126    let translator = GateTranslator::new();
1127
1128    for gate in circuit.gates() {
1129        if !translator.is_native_gate(backend, gate.name()) {
1130            return Ok(false);
1131        }
1132    }
1133
1134    Ok(true)
1135}
1136
1137/// Get translation statistics for a circuit
1138pub struct TranslationStats {
1139    /// Original gate count
1140    pub original_gates: usize,
1141    /// Native gate count after translation
1142    pub native_gates: usize,
1143    /// Gate count by type
1144    pub gate_counts: HashMap<String, usize>,
1145    /// Estimated fidelity loss
1146    pub fidelity_loss: f64,
1147    /// Circuit depth change
1148    pub depth_ratio: f64,
1149}
1150
1151impl TranslationStats {
1152    /// Calculate statistics for a translation
1153    pub fn calculate<const N: usize>(
1154        original: &Circuit<N>,
1155        translated: &Circuit<N>,
1156        backend: HardwareBackend,
1157    ) -> Self {
1158        let mut gate_counts = HashMap::new();
1159
1160        for gate in translated.gates() {
1161            *gate_counts.entry(gate.name().to_string()).or_insert(0) += 1;
1162        }
1163
1164        Self {
1165            original_gates: original.gates().len(),
1166            native_gates: translated.gates().len(),
1167            gate_counts,
1168            fidelity_loss: 0.0, // Would need to calculate
1169            depth_ratio: 1.0,   // Would need to calculate
1170        }
1171    }
1172}
1173
1174#[cfg(test)]
1175mod tests {
1176    use super::*;
1177
1178    #[test]
1179    fn test_gate_translator_creation() {
1180        let translator = GateTranslator::new();
1181
1182        // Check that standard backends are initialized
1183        assert!(translator
1184            .get_native_gates(HardwareBackend::IBMQuantum)
1185            .is_some());
1186        assert!(translator
1187            .get_native_gates(HardwareBackend::GoogleSycamore)
1188            .is_some());
1189        assert!(translator.get_native_gates(HardwareBackend::IonQ).is_some());
1190    }
1191
1192    #[test]
1193    fn test_native_gate_check() {
1194        let translator = GateTranslator::new();
1195
1196        // IBM native gates
1197        assert!(translator.is_native_gate(HardwareBackend::IBMQuantum, "rz"));
1198        assert!(translator.is_native_gate(HardwareBackend::IBMQuantum, "sx"));
1199        assert!(translator.is_native_gate(HardwareBackend::IBMQuantum, "cx"));
1200        assert!(!translator.is_native_gate(HardwareBackend::IBMQuantum, "h"));
1201
1202        // IonQ native gates
1203        assert!(translator.is_native_gate(HardwareBackend::IonQ, "rx"));
1204        assert!(translator.is_native_gate(HardwareBackend::IonQ, "xx"));
1205        assert!(!translator.is_native_gate(HardwareBackend::IonQ, "cnot"));
1206    }
1207
1208    #[test]
1209    fn test_hadamard_translation_ibm() {
1210        let mut translator = GateTranslator::new();
1211        let h_gate = Hadamard { target: QubitId(0) };
1212
1213        let decomposed = translator
1214            .translate_gate(&h_gate, HardwareBackend::IBMQuantum)
1215            .expect("Hadamard gate translation should succeed");
1216
1217        // H = RZ(π/2) SX RZ(π/2)
1218        assert_eq!(decomposed.len(), 3);
1219        assert_eq!(decomposed[0].native_gate, "rz");
1220        assert_eq!(decomposed[1].native_gate, "sx");
1221        assert_eq!(decomposed[2].native_gate, "rz");
1222    }
1223
1224    #[test]
1225    fn test_circuit_translation() {
1226        let mut translator = GateTranslator::new();
1227
1228        let mut circuit = Circuit::<2>::new();
1229        let _ = circuit.h(QubitId(0));
1230        let _ = circuit.cnot(QubitId(0), QubitId(1));
1231
1232        let translated = translator
1233            .translate_circuit(&circuit, HardwareBackend::IBMQuantum)
1234            .expect("Circuit translation should succeed");
1235
1236        // Original: H, CNOT
1237        // Translated: RZ, SX, RZ, CX
1238        assert!(translated.gates().len() >= 4);
1239    }
1240
1241    #[test]
1242    fn test_kak_decomposition_produces_real_gates() {
1243        let translator = GateTranslator::new();
1244
1245        // A real, non-trivial two-qubit entangling gate (RXX) must decompose to
1246        // a non-empty native sequence that is fed from the genuine core Cartan
1247        // decomposer — NOT a silently-dropped empty (identity) vector, which was
1248        // the previous fabricated behavior.
1249        let rxx = RXX {
1250            qubit1: QubitId(0),
1251            qubit2: QubitId(1),
1252            theta: std::f64::consts::FRAC_PI_2,
1253        };
1254
1255        let decomposed = translator
1256            .synthesize_gate(
1257                &rxx,
1258                SynthesisMethod::KAKDecomposition,
1259                HardwareBackend::IBMQuantum,
1260            )
1261            .expect("KAK decomposition should succeed for a 2-qubit gate");
1262
1263        assert!(
1264            !decomposed.is_empty(),
1265            "KAK decomposition must not return an empty (identity) sequence for an \
1266             entangling gate"
1267        );
1268
1269        // Every emitted gate must be a real native gate produced by the core
1270        // Cartan lowering (rz/ry/rx single-qubit rotations and cx entanglers).
1271        // NOTE: the exact CNOT count depends on the core Cartan decomposer's
1272        // `cnot_count`; this test asserts the device-side wiring is real and
1273        // honest, not the numerical optimality of the core decomposition.
1274        for g in &decomposed {
1275            assert!(
1276                matches!(
1277                    g.native_gate.as_str(),
1278                    "rx" | "ry" | "rz" | "cx" | "cz" | "h" | "x" | "y" | "z" | "s" | "t" | "swap"
1279                ),
1280                "unexpected native gate from KAK: {}",
1281                g.native_gate
1282            );
1283        }
1284    }
1285
1286    #[test]
1287    fn test_kak_decomposition_rejects_non_two_qubit_gate() {
1288        let translator = GateTranslator::new();
1289        let h = Hadamard { target: QubitId(0) };
1290
1291        // A single-qubit gate must produce an honest error, never Ok(vec![]).
1292        let result = translator.synthesize_gate(
1293            &h,
1294            SynthesisMethod::KAKDecomposition,
1295            HardwareBackend::IBMQuantum,
1296        );
1297        assert!(
1298            matches!(result, Err(QuantRS2Error::InvalidInput(_))),
1299            "expected InvalidInput error for single-qubit gate, got {result:?}"
1300        );
1301    }
1302}