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},
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>> {
775 Ok(vec![])
778 }
779
780 fn remap_qubits(
782 &self,
783 mut gates: Vec<DecomposedGate>,
784 actual_qubits: &[QubitId],
785 ) -> Vec<DecomposedGate> {
786 for gate in &mut gates {
787 for qubit in &mut gate.qubits {
788 if qubit.id() < actual_qubits.len() as u32 {
789 *qubit = actual_qubits[qubit.id() as usize];
790 }
791 }
792 }
793 gates
794 }
795
796 fn synthesize_gate(
798 &self,
799 gate: &dyn GateOp,
800 method: SynthesisMethod,
801 backend: HardwareBackend,
802 ) -> QuantRS2Result<Vec<DecomposedGate>> {
803 match method {
804 SynthesisMethod::SingleQubitZYZ => {
805 if gate.num_qubits() != 1 {
807 return Err(QuantRS2Error::InvalidInput(
808 "ZYZ synthesis only works for single-qubit gates".into(),
809 ));
810 }
811
812 let matrix_vec = gate.matrix()?;
813 let matrix = Array2::from_shape_vec((2, 2), matrix_vec)
814 .map_err(|_| QuantRS2Error::InvalidInput("Invalid matrix shape".into()))?;
815 let decomp = decompose_single_qubit_zyz(&matrix.view())?;
816
817 Ok(vec![
818 DecomposedGate {
819 native_gate: "rz".to_string(),
820 qubits: gate.qubits(),
821 parameters: vec![decomp.theta1],
822 global_phase: None,
823 },
824 DecomposedGate {
825 native_gate: "ry".to_string(),
826 qubits: gate.qubits(),
827 parameters: vec![decomp.phi],
828 global_phase: None,
829 },
830 DecomposedGate {
831 native_gate: "rz".to_string(),
832 qubits: gate.qubits(),
833 parameters: vec![decomp.theta2],
834 global_phase: Some(decomp.global_phase),
835 },
836 ])
837 }
838 SynthesisMethod::KAKDecomposition => {
839 if gate.num_qubits() != 2 {
841 return Err(QuantRS2Error::InvalidInput(
842 "KAK decomposition only works for two-qubit gates".into(),
843 ));
844 }
845
846 Ok(vec![])
849 }
850 _ => Err(QuantRS2Error::InvalidInput(format!(
851 "Synthesis method {method:?} not yet implemented"
852 ))),
853 }
854 }
855
856 fn add_decomposed_gate<const N: usize>(
858 &self,
859 circuit: &mut Circuit<N>,
860 gate: &DecomposedGate,
861 ) -> QuantRS2Result<()> {
862 match gate.native_gate.as_str() {
863 "id" => {} "x" => {
866 let _ = circuit.x(gate.qubits[0]);
867 }
868 "sx" => {
869 let _ = circuit.sx(gate.qubits[0]);
870 }
871 "rz" => {
872 let _ = circuit.rz(gate.qubits[0], gate.parameters[0]);
873 }
874 "cx" => {
875 let _ = circuit.cnot(gate.qubits[0], gate.qubits[1]);
876 }
877
878 "rx" => {
880 let _ = circuit.rx(gate.qubits[0], gate.parameters[0]);
881 }
882 "ry" => {
883 let _ = circuit.ry(gate.qubits[0], gate.parameters[0]);
884 }
885 "xx" => {
886 let _ = circuit.cnot(gate.qubits[0], gate.qubits[1]);
889 }
890
891 "h" => {
893 let _ = circuit.h(gate.qubits[0]);
894 }
895 "y" => {
896 let _ = circuit.y(gate.qubits[0]);
897 }
898 "z" => {
899 let _ = circuit.z(gate.qubits[0]);
900 }
901 "s" => {
902 let _ = circuit.s(gate.qubits[0]);
903 }
904 "t" => {
905 let _ = circuit.t(gate.qubits[0]);
906 }
907 "cnot" => {
908 let _ = circuit.cnot(gate.qubits[0], gate.qubits[1]);
909 }
910 "cz" => {
911 let _ = circuit.cz(gate.qubits[0], gate.qubits[1]);
912 }
913 "swap" => {
914 let _ = circuit.swap(gate.qubits[0], gate.qubits[1]);
915 }
916 "ccnot" => {
917 let _ = circuit.toffoli(gate.qubits[0], gate.qubits[1], gate.qubits[2]);
918 }
919
920 _ => {
921 return Err(QuantRS2Error::InvalidInput(format!(
922 "Unknown native gate: {}",
923 gate.native_gate
924 )));
925 }
926 }
927
928 Ok(())
929 }
930}
931
932pub struct TranslationOptimizer {
934 translator: GateTranslator,
936 strategy: OptimizationStrategy,
938}
939
940#[derive(Debug, Clone, Copy)]
942pub enum OptimizationStrategy {
943 MinimizeGateCount,
945 MinimizeError,
947 MinimizeDepth,
949 Balanced { weight: f64 },
951}
952
953impl TranslationOptimizer {
954 pub fn new(strategy: OptimizationStrategy) -> Self {
956 Self {
957 translator: GateTranslator::new(),
958 strategy,
959 }
960 }
961
962 pub fn optimize_translation(
964 &mut self,
965 gate: &dyn GateOp,
966 backend: HardwareBackend,
967 ) -> QuantRS2Result<Vec<DecomposedGate>> {
968 let candidates = vec![
970 self.translator.translate_gate(gate, backend)?,
971 ];
973
974 let best = match self.strategy {
976 OptimizationStrategy::MinimizeGateCount => candidates
977 .into_iter()
978 .min_by_key(|c| c.len())
979 .unwrap_or_default(),
980 OptimizationStrategy::MinimizeError => {
981 candidates.into_iter().next().unwrap_or_default()
983 }
984 OptimizationStrategy::MinimizeDepth => {
985 candidates.into_iter().next().unwrap_or_default()
987 }
988 OptimizationStrategy::Balanced { weight } => {
989 candidates.into_iter().next().unwrap_or_default()
991 }
992 };
993
994 Ok(best)
995 }
996}
997
998pub fn validate_native_circuit<const N: usize>(
1000 circuit: &Circuit<N>,
1001 backend: HardwareBackend,
1002) -> QuantRS2Result<bool> {
1003 let translator = GateTranslator::new();
1004
1005 for gate in circuit.gates() {
1006 if !translator.is_native_gate(backend, gate.name()) {
1007 return Ok(false);
1008 }
1009 }
1010
1011 Ok(true)
1012}
1013
1014pub struct TranslationStats {
1016 pub original_gates: usize,
1018 pub native_gates: usize,
1020 pub gate_counts: HashMap<String, usize>,
1022 pub fidelity_loss: f64,
1024 pub depth_ratio: f64,
1026}
1027
1028impl TranslationStats {
1029 pub fn calculate<const N: usize>(
1031 original: &Circuit<N>,
1032 translated: &Circuit<N>,
1033 backend: HardwareBackend,
1034 ) -> Self {
1035 let mut gate_counts = HashMap::new();
1036
1037 for gate in translated.gates() {
1038 *gate_counts.entry(gate.name().to_string()).or_insert(0) += 1;
1039 }
1040
1041 Self {
1042 original_gates: original.gates().len(),
1043 native_gates: translated.gates().len(),
1044 gate_counts,
1045 fidelity_loss: 0.0, depth_ratio: 1.0, }
1048 }
1049}
1050
1051#[cfg(test)]
1052mod tests {
1053 use super::*;
1054
1055 #[test]
1056 fn test_gate_translator_creation() {
1057 let translator = GateTranslator::new();
1058
1059 assert!(translator
1061 .get_native_gates(HardwareBackend::IBMQuantum)
1062 .is_some());
1063 assert!(translator
1064 .get_native_gates(HardwareBackend::GoogleSycamore)
1065 .is_some());
1066 assert!(translator.get_native_gates(HardwareBackend::IonQ).is_some());
1067 }
1068
1069 #[test]
1070 fn test_native_gate_check() {
1071 let translator = GateTranslator::new();
1072
1073 assert!(translator.is_native_gate(HardwareBackend::IBMQuantum, "rz"));
1075 assert!(translator.is_native_gate(HardwareBackend::IBMQuantum, "sx"));
1076 assert!(translator.is_native_gate(HardwareBackend::IBMQuantum, "cx"));
1077 assert!(!translator.is_native_gate(HardwareBackend::IBMQuantum, "h"));
1078
1079 assert!(translator.is_native_gate(HardwareBackend::IonQ, "rx"));
1081 assert!(translator.is_native_gate(HardwareBackend::IonQ, "xx"));
1082 assert!(!translator.is_native_gate(HardwareBackend::IonQ, "cnot"));
1083 }
1084
1085 #[test]
1086 fn test_hadamard_translation_ibm() {
1087 let mut translator = GateTranslator::new();
1088 let h_gate = Hadamard { target: QubitId(0) };
1089
1090 let decomposed = translator
1091 .translate_gate(&h_gate, HardwareBackend::IBMQuantum)
1092 .expect("Hadamard gate translation should succeed");
1093
1094 assert_eq!(decomposed.len(), 3);
1096 assert_eq!(decomposed[0].native_gate, "rz");
1097 assert_eq!(decomposed[1].native_gate, "sx");
1098 assert_eq!(decomposed[2].native_gate, "rz");
1099 }
1100
1101 #[test]
1102 fn test_circuit_translation() {
1103 let mut translator = GateTranslator::new();
1104
1105 let mut circuit = Circuit::<2>::new();
1106 let _ = circuit.h(QubitId(0));
1107 let _ = circuit.cnot(QubitId(0), QubitId(1));
1108
1109 let translated = translator
1110 .translate_circuit(&circuit, HardwareBackend::IBMQuantum)
1111 .expect("Circuit translation should succeed");
1112
1113 assert!(translated.gates().len() >= 4);
1116 }
1117}