1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
23pub enum HardwareBackend {
24 IBMQuantum,
26 GoogleSycamore,
28 IonQ,
30 Rigetti,
32 AmazonBraket,
34 AzureQuantum,
36 Honeywell,
38 Custom(u32), }
41
42#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44pub struct NativeGateSet {
45 pub backend: HardwareBackend,
47 pub single_qubit_gates: Vec<String>,
49 pub two_qubit_gates: Vec<String>,
51 pub multi_qubit_gates: Vec<String>,
53 pub arbitrary_single_qubit: bool,
55 pub rotation_axes: Vec<RotationAxis>,
57 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#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
82pub enum RotationAxis {
83 X,
84 Y,
85 Z,
86 XY(f64), Custom(f64, f64, f64), }
89
90#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
92pub struct BackendConstraints {
93 pub max_depth: Option<usize>,
95 pub discrete_angles: Option<Vec<f64>>,
97 pub virtual_z: bool,
99 pub coupling_map: Option<Vec<(usize, usize)>>,
101 pub timing_constraints: Option<TimingConstraints>,
103}
104
105#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
107pub struct TimingConstraints {
108 pub min_gate_spacing: f64,
110 pub time_alignment: f64,
112 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
126pub struct GateTranslator {
128 native_gates: HashMap<HardwareBackend, NativeGateSet>,
130 translation_rules: HashMap<(HardwareBackend, String), TranslationRule>,
132 decomposition_cache: HashMap<String, Vec<DecomposedGate>>,
134}
135
136#[derive(Debug, Clone)]
138pub struct TranslationRule {
139 pub gate_name: String,
141 pub method: TranslationMethod,
143 pub fidelity: f64,
145 pub gate_count: usize,
147}
148
149pub enum TranslationMethod {
151 Direct(String),
153 FixedDecomposition(Vec<DecomposedGate>),
155 ParameterizedDecomposition(Box<dyn Fn(Vec<f64>) -> Vec<DecomposedGate> + Send + Sync>),
157 Synthesis(SynthesisMethod),
159 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#[derive(Debug, Clone)]
197pub struct DecomposedGate {
198 pub native_gate: String,
200 pub qubits: Vec<QubitId>,
202 pub parameters: Vec<f64>,
204 pub global_phase: Option<f64>,
206}
207
208#[derive(Debug, Clone, Copy)]
210pub enum SynthesisMethod {
211 SingleQubitZYZ,
213 SingleQubitXYX,
214 SingleQubitU3,
215 KAKDecomposition,
217 CliffordT,
219 SolovayKitaev,
221}
222
223impl GateTranslator {
224 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 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 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 self.add_ibm_translation_rules();
272 }
273
274 fn add_ibm_translation_rules(&mut self) {
276 let backend = HardwareBackend::IBMQuantum;
277
278 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 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 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 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 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 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 fn init_google_gates(&mut self) {
413 let gate_set = NativeGateSet {
414 backend: HardwareBackend::GoogleSycamore,
415 single_qubit_gates: vec![
416 "ph".to_string(), "x_pow".to_string(), "y_pow".to_string(), "z_pow".to_string(), ],
421 two_qubit_gates: vec![
422 "syc".to_string(), "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 fn add_google_translation_rules(&mut self) {
444 let backend = HardwareBackend::GoogleSycamore;
445
446 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 self.translation_rules.insert(
464 (backend, "CNOT".to_string()),
465 TranslationRule {
466 gate_name: "CNOT".to_string(),
467 method: TranslationMethod::FixedDecomposition(vec![
468 DecomposedGate {
470 native_gate: "syc".to_string(),
471 qubits: vec![QubitId(0), QubitId(1)],
472 parameters: vec![],
473 global_phase: None,
474 },
475 ]),
477 fidelity: 0.998,
478 gate_count: 3,
479 },
480 );
481 }
482
483 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(), ],
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, timing_constraints: None,
500 },
501 };
502
503 self.native_gates.insert(HardwareBackend::IonQ, gate_set);
504 self.add_ionq_translation_rules();
505 }
506
507 fn add_ionq_translation_rules(&mut self) {
509 let backend = HardwareBackend::IonQ;
510
511 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 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(), ],
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 fn init_amazon_gates(&mut self) {
580 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 fn init_azure_gates(&mut self) {
611 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 fn init_honeywell_gates(&mut self) {
644 let gate_set = NativeGateSet {
645 backend: HardwareBackend::Honeywell,
646 single_qubit_gates: vec![
647 "u1".to_string(), "u2".to_string(), "u3".to_string(), ],
651 two_qubit_gates: vec![
652 "zz".to_string(), ],
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, timing_constraints: None,
663 },
664 };
665
666 self.native_gates
667 .insert(HardwareBackend::Honeywell, gate_set);
668 }
669
670 pub fn get_native_gates(&self, backend: HardwareBackend) -> Option<&NativeGateSet> {
672 self.native_gates.get(&backend)
673 }
674
675 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 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 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 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 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 self.decomposition_cache
738 .insert(cache_key, decomposition.clone());
739 return Ok(decomposition);
740 }
741
742 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 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 fn extract_parameters(&self, gate: &dyn GateOp) -> QuantRS2Result<Vec<f64>> {
780 if !gate.is_parameterized() {
781 return Ok(Vec::new());
782 }
783
784 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 Ok(Vec::new())
811 }
812
813 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 fn native_gate_to_decomposed(gate: &dyn GateOp) -> QuantRS2Result<DecomposedGate> {
832 let any = gate.as_any();
833 let qubits = gate.qubits();
834
835 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 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 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 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 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 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 let kak = decompose_two_qubit_kak(&matrix.view())?;
955 let native_gates = kak_to_gates(&kak, qubits[0], qubits[1])?;
956
957 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 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 "id" => {} "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 "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 let _ = circuit.cnot(gate.qubits[0], gate.qubits[1]);
1012 }
1013
1014 "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
1055pub struct TranslationOptimizer {
1057 translator: GateTranslator,
1059 strategy: OptimizationStrategy,
1061}
1062
1063#[derive(Debug, Clone, Copy)]
1065pub enum OptimizationStrategy {
1066 MinimizeGateCount,
1068 MinimizeError,
1070 MinimizeDepth,
1072 Balanced { weight: f64 },
1074}
1075
1076impl TranslationOptimizer {
1077 pub fn new(strategy: OptimizationStrategy) -> Self {
1079 Self {
1080 translator: GateTranslator::new(),
1081 strategy,
1082 }
1083 }
1084
1085 pub fn optimize_translation(
1087 &mut self,
1088 gate: &dyn GateOp,
1089 backend: HardwareBackend,
1090 ) -> QuantRS2Result<Vec<DecomposedGate>> {
1091 let candidates = vec![
1093 self.translator.translate_gate(gate, backend)?,
1094 ];
1096
1097 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 candidates.into_iter().next().unwrap_or_default()
1106 }
1107 OptimizationStrategy::MinimizeDepth => {
1108 candidates.into_iter().next().unwrap_or_default()
1110 }
1111 OptimizationStrategy::Balanced { weight } => {
1112 candidates.into_iter().next().unwrap_or_default()
1114 }
1115 };
1116
1117 Ok(best)
1118 }
1119}
1120
1121pub 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
1137pub struct TranslationStats {
1139 pub original_gates: usize,
1141 pub native_gates: usize,
1143 pub gate_counts: HashMap<String, usize>,
1145 pub fidelity_loss: f64,
1147 pub depth_ratio: f64,
1149}
1150
1151impl TranslationStats {
1152 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, depth_ratio: 1.0, }
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 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 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 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 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 assert!(translated.gates().len() >= 4);
1239 }
1240
1241 #[test]
1242 fn test_kak_decomposition_produces_real_gates() {
1243 let translator = GateTranslator::new();
1244
1245 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 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 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}