1use crate::optimization::cost_model::CostModel;
4use crate::optimization::gate_properties::get_gate_properties;
5use quantrs2_core::error::{QuantRS2Error, QuantRS2Result};
6use quantrs2_core::gate::{
7 multi,
8 single::{self},
9 GateOp,
10};
11use quantrs2_core::qubit::QubitId;
12use std::collections::{HashMap, HashSet};
13
14use super::rewriting_passes::parallelize_gates;
15use super::OptimizationPass;
16
17pub struct DecompositionOptimization {
19 target_gate_set: HashSet<String>,
20 prefer_native: bool,
21}
22
23impl DecompositionOptimization {
24 #[must_use]
25 pub const fn new(target_gate_set: HashSet<String>, prefer_native: bool) -> Self {
26 Self {
27 target_gate_set,
28 prefer_native,
29 }
30 }
31
32 #[must_use]
33 pub fn for_hardware(hardware: &str) -> Self {
34 let target_gate_set = match hardware {
35 "ibm" => vec!["X", "Y", "Z", "H", "S", "T", "RZ", "CNOT", "CZ"]
36 .into_iter()
37 .map(std::string::ToString::to_string)
38 .collect(),
39 "google" => vec!["X", "Y", "Z", "H", "RZ", "CZ", "SQRT_X"]
40 .into_iter()
41 .map(std::string::ToString::to_string)
42 .collect(),
43 _ => vec!["X", "Y", "Z", "H", "S", "T", "RZ", "RX", "RY", "CNOT"]
44 .into_iter()
45 .map(std::string::ToString::to_string)
46 .collect(),
47 };
48
49 Self {
50 target_gate_set,
51 prefer_native: true,
52 }
53 }
54}
55
56impl OptimizationPass for DecompositionOptimization {
57 fn name(&self) -> &'static str {
58 "Decomposition Optimization"
59 }
60
61 fn apply_to_gates(
62 &self,
63 gates: Vec<Box<dyn GateOp>>,
64 cost_model: &dyn CostModel,
65 ) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
66 let mut optimized_gates = Vec::with_capacity(gates.len() * 2);
67
68 for gate in gates {
69 let gate_name = gate.name();
70 let gate_qubits = gate.qubits();
71
72 if self.should_decompose(&gate, cost_model) {
74 match gate_name {
76 "Toffoli" => {
77 if gate_qubits.len() == 3 {
78 self.decompose_toffoli(&gate_qubits, &mut optimized_gates)?;
80 } else {
81 optimized_gates.push(gate);
82 }
83 }
84 "Fredkin" | "CSWAP" => {
85 if gate_qubits.len() == 3 {
86 self.decompose_fredkin(&gate_qubits, &mut optimized_gates)?;
88 } else {
89 optimized_gates.push(gate);
90 }
91 }
92 "SWAP" => {
93 if self.target_gate_set.contains("CNOT") && gate_qubits.len() == 2 {
94 self.decompose_swap(&gate_qubits, &mut optimized_gates)?;
96 } else {
97 optimized_gates.push(gate);
98 }
99 }
100 "CRX" | "CRY" | "CRZ" => {
101 if !self.target_gate_set.contains(gate_name) && gate_qubits.len() == 2 {
103 self.decompose_controlled_rotation(&gate, &mut optimized_gates)?;
104 } else {
105 optimized_gates.push(gate);
106 }
107 }
108 _ => {
109 optimized_gates.push(gate);
111 }
112 }
113 } else {
114 optimized_gates.push(gate);
115 }
116 }
117
118 Ok(optimized_gates)
119 }
120}
121
122impl DecompositionOptimization {
123 fn should_decompose(&self, gate: &Box<dyn GateOp>, _cost_model: &dyn CostModel) -> bool {
124 let gate_name = gate.name();
125
126 if self.target_gate_set.contains(gate_name) {
128 false
129 } else {
130 matches!(
132 gate_name,
133 "Toffoli" | "Fredkin" | "CSWAP" | "SWAP" | "CRX" | "CRY" | "CRZ"
134 )
135 }
136 }
137
138 fn decompose_toffoli(
139 &self,
140 qubits: &[QubitId],
141 gates: &mut Vec<Box<dyn GateOp>>,
142 ) -> QuantRS2Result<()> {
143 if qubits.len() != 3 {
144 return Err(QuantRS2Error::InvalidInput(
145 "Toffoli gate requires exactly 3 qubits".to_string(),
146 ));
147 }
148
149 let c1 = qubits[0];
150 let c2 = qubits[1];
151 let target = qubits[2];
152
153 use quantrs2_core::gate::{
155 multi::CNOT,
156 single::{Hadamard, TDagger, T},
157 };
158
159 gates.push(Box::new(Hadamard { target }));
160 gates.push(Box::new(CNOT {
161 control: c2,
162 target,
163 }));
164 gates.push(Box::new(TDagger { target }));
165 gates.push(Box::new(CNOT {
166 control: c1,
167 target,
168 }));
169 gates.push(Box::new(T { target }));
170 gates.push(Box::new(CNOT {
171 control: c2,
172 target,
173 }));
174 gates.push(Box::new(TDagger { target }));
175 gates.push(Box::new(CNOT {
176 control: c1,
177 target,
178 }));
179 gates.push(Box::new(T { target: c2 }));
180 gates.push(Box::new(T { target }));
181 gates.push(Box::new(CNOT {
182 control: c1,
183 target: c2,
184 }));
185 gates.push(Box::new(Hadamard { target }));
186 gates.push(Box::new(T { target: c1 }));
187 gates.push(Box::new(TDagger { target: c2 }));
188 gates.push(Box::new(CNOT {
189 control: c1,
190 target: c2,
191 }));
192
193 Ok(())
194 }
195
196 fn decompose_fredkin(
197 &self,
198 qubits: &[QubitId],
199 gates: &mut Vec<Box<dyn GateOp>>,
200 ) -> QuantRS2Result<()> {
201 if qubits.len() != 3 {
202 return Err(QuantRS2Error::InvalidInput(
203 "Fredkin gate requires exactly 3 qubits".to_string(),
204 ));
205 }
206
207 let control = qubits[0];
208 let target1 = qubits[1];
209 let target2 = qubits[2];
210
211 use quantrs2_core::gate::multi::CNOT;
213
214 gates.push(Box::new(CNOT {
215 control: target2,
216 target: target1,
217 }));
218 gates.push(Box::new(CNOT {
219 control,
220 target: target1,
221 }));
222 gates.push(Box::new(CNOT {
223 control: target1,
224 target: target2,
225 }));
226 gates.push(Box::new(CNOT {
227 control,
228 target: target1,
229 }));
230 gates.push(Box::new(CNOT {
231 control: target2,
232 target: target1,
233 }));
234
235 Ok(())
236 }
237
238 fn decompose_swap(
239 &self,
240 qubits: &[QubitId],
241 gates: &mut Vec<Box<dyn GateOp>>,
242 ) -> QuantRS2Result<()> {
243 if qubits.len() != 2 {
244 return Err(QuantRS2Error::InvalidInput(
245 "SWAP gate requires exactly 2 qubits".to_string(),
246 ));
247 }
248
249 let q1 = qubits[0];
250 let q2 = qubits[1];
251
252 use quantrs2_core::gate::multi::CNOT;
254
255 gates.push(Box::new(CNOT {
256 control: q1,
257 target: q2,
258 }));
259 gates.push(Box::new(CNOT {
260 control: q2,
261 target: q1,
262 }));
263 gates.push(Box::new(CNOT {
264 control: q1,
265 target: q2,
266 }));
267
268 Ok(())
269 }
270
271 fn decompose_controlled_rotation(
272 &self,
273 gate: &Box<dyn GateOp>,
274 gates: &mut Vec<Box<dyn GateOp>>,
275 ) -> QuantRS2Result<()> {
276 let qubits = gate.qubits();
277 if qubits.len() != 2 {
278 return Err(QuantRS2Error::InvalidInput(
279 "Controlled rotation requires exactly 2 qubits".to_string(),
280 ));
281 }
282
283 let control = qubits[0];
284 let target = qubits[1];
285
286 use quantrs2_core::gate::{
287 multi::CNOT,
288 single::{RotationX, RotationY, RotationZ},
289 };
290
291 match gate.name() {
292 "CRX" => {
293 gates.push(Box::new(RotationX {
294 target,
295 theta: std::f64::consts::PI / 4.0,
296 }));
297 gates.push(Box::new(CNOT { control, target }));
298 gates.push(Box::new(RotationX {
299 target,
300 theta: -std::f64::consts::PI / 4.0,
301 }));
302 gates.push(Box::new(CNOT { control, target }));
303 }
304 "CRY" => {
305 gates.push(Box::new(RotationY {
306 target,
307 theta: std::f64::consts::PI / 4.0,
308 }));
309 gates.push(Box::new(CNOT { control, target }));
310 gates.push(Box::new(RotationY {
311 target,
312 theta: -std::f64::consts::PI / 4.0,
313 }));
314 gates.push(Box::new(CNOT { control, target }));
315 }
316 "CRZ" => {
317 gates.push(Box::new(RotationZ {
318 target,
319 theta: std::f64::consts::PI / 4.0,
320 }));
321 gates.push(Box::new(CNOT { control, target }));
322 gates.push(Box::new(RotationZ {
323 target,
324 theta: -std::f64::consts::PI / 4.0,
325 }));
326 gates.push(Box::new(CNOT { control, target }));
327 }
328 _ => {
329 return Err(QuantRS2Error::UnsupportedOperation(format!(
330 "Unknown controlled rotation gate: {}",
331 gate.name()
332 )));
333 }
334 }
335
336 Ok(())
337 }
338}
339
340pub struct CostBasedOptimization {
342 optimization_target: CostTarget,
343 max_iterations: usize,
344}
345
346#[derive(Debug, Clone, Copy)]
347pub enum CostTarget {
348 GateCount,
349 CircuitDepth,
350 TotalError,
351 ExecutionTime,
352 Balanced,
353}
354
355impl CostBasedOptimization {
356 #[must_use]
357 pub const fn new(target: CostTarget, max_iterations: usize) -> Self {
358 Self {
359 optimization_target: target,
360 max_iterations,
361 }
362 }
363}
364
365impl OptimizationPass for CostBasedOptimization {
366 fn name(&self) -> &'static str {
367 "Cost-Based Optimization"
368 }
369
370 fn apply_to_gates(
371 &self,
372 gates: Vec<Box<dyn GateOp>>,
373 cost_model: &dyn CostModel,
374 ) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
375 let mut best_gates = gates.clone();
376 let mut best_cost = self.calculate_cost(&best_gates, cost_model);
377
378 for iteration in 0..self.max_iterations {
379 let candidate_gates = self.generate_candidate_solution(&best_gates, iteration)?;
380 let candidate_cost = self.calculate_cost(&candidate_gates, cost_model);
381
382 if candidate_cost < best_cost {
383 best_gates = candidate_gates;
384 best_cost = candidate_cost;
385 }
386 }
387
388 Ok(best_gates)
389 }
390}
391
392impl CostBasedOptimization {
393 fn calculate_cost(&self, gates: &[Box<dyn GateOp>], cost_model: &dyn CostModel) -> f64 {
394 match self.optimization_target {
395 CostTarget::GateCount => gates.len() as f64,
396 CostTarget::CircuitDepth => self.calculate_depth(gates) as f64,
397 CostTarget::TotalError => self.calculate_total_error(gates),
398 CostTarget::ExecutionTime => self.calculate_execution_time(gates),
399 CostTarget::Balanced => {
400 let gate_count = gates.len() as f64;
401 let depth = self.calculate_depth(gates) as f64;
402 let error = self.calculate_total_error(gates);
403 let time = self.calculate_execution_time(gates);
404
405 (0.2 * error).mul_add(1000.0, 0.3f64.mul_add(gate_count, 0.3 * depth))
406 + 0.2 * time / 1000.0
407 }
408 }
409 }
410
411 fn generate_candidate_solution(
412 &self,
413 gates: &[Box<dyn GateOp>],
414 iteration: usize,
415 ) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
416 let mut candidate = gates.to_vec();
417
418 match self.optimization_target {
419 CostTarget::GateCount => {
420 self.cancel_inverse_gates(&mut candidate);
421 }
422 CostTarget::CircuitDepth => {
423 self.parallelize_gates_impl(&mut candidate);
424 }
425 CostTarget::TotalError => {
426 self.reduce_error_gates(&candidate)?;
427 }
428 CostTarget::ExecutionTime => {
429 self.optimize_for_speed(&candidate)?;
430 }
431 CostTarget::Balanced => match iteration % 4 {
432 0 => self.cancel_inverse_gates(&mut candidate),
433 1 => self.parallelize_gates_impl(&mut candidate),
434 2 => self.reduce_error_gates(&candidate)?,
435 3 => self.optimize_for_speed(&candidate)?,
436 _ => unreachable!(),
437 },
438 }
439
440 Ok(candidate)
441 }
442
443 fn calculate_depth(&self, gates: &[Box<dyn GateOp>]) -> usize {
444 let mut qubit_depths = std::collections::HashMap::new();
445 let mut max_depth = 0;
446
447 for gate in gates {
448 let gate_qubits = gate.qubits();
449 let gate_start_depth = gate_qubits
450 .iter()
451 .map(|q| qubit_depths.get(&q.id()).copied().unwrap_or(0))
452 .max()
453 .unwrap_or(0);
454
455 let gate_end_depth = gate_start_depth + 1;
456
457 for qubit in gate_qubits {
458 qubit_depths.insert(qubit.id(), gate_end_depth);
459 }
460
461 max_depth = max_depth.max(gate_end_depth);
462 }
463
464 max_depth
465 }
466
467 fn calculate_total_error(&self, gates: &[Box<dyn GateOp>]) -> f64 {
468 gates
469 .iter()
470 .map(|gate| self.estimate_gate_error(gate.name()))
471 .sum()
472 }
473
474 fn calculate_execution_time(&self, gates: &[Box<dyn GateOp>]) -> f64 {
475 gates
476 .iter()
477 .map(|gate| self.estimate_gate_time(gate.name()))
478 .sum()
479 }
480
481 fn estimate_gate_error(&self, gate_name: &str) -> f64 {
482 match gate_name {
483 "H" | "X" | "Y" | "Z" | "S" | "T" => 0.0001,
484 "RX" | "RY" | "RZ" => 0.0005,
485 "CNOT" | "CX" | "CZ" | "CY" => 0.01,
486 "SWAP" | "CRX" | "CRY" | "CRZ" => 0.015,
487 "Toffoli" | "Fredkin" => 0.05,
488 _ => 0.01,
489 }
490 }
491
492 fn estimate_gate_time(&self, gate_name: &str) -> f64 {
493 match gate_name {
494 "H" | "X" | "Y" | "Z" | "S" | "T" | "RX" | "RY" | "RZ" => 50.0,
495 "CNOT" | "CX" | "CZ" | "CY" | "SWAP" | "CRX" | "CRY" | "CRZ" => 200.0,
496 "Toffoli" | "Fredkin" => 500.0,
497 _ => 100.0,
498 }
499 }
500
501 fn cancel_inverse_gates(&self, gates: &mut Vec<Box<dyn GateOp>>) {
502 let mut i = 0;
503 while i + 1 < gates.len() {
504 if self.are_inverse_gates(&gates[i], &gates[i + 1]) {
505 gates.remove(i + 1);
506 gates.remove(i);
507 i = i.saturating_sub(1);
508 } else {
509 i += 1;
510 }
511 }
512 }
513
514 fn are_inverse_gates(&self, gate1: &Box<dyn GateOp>, gate2: &Box<dyn GateOp>) -> bool {
515 if gate1.qubits() != gate2.qubits() {
516 return false;
517 }
518
519 match (gate1.name(), gate2.name()) {
520 ("H", "H") | ("X", "X") | ("Y", "Y") | ("Z", "Z") => true,
521 ("S", "SDG") | ("SDG", "S") => true,
522 ("T", "TDG") | ("TDG", "T") => true,
523 ("CNOT", "CNOT") | ("CX", "CX") => true,
524 _ => false,
525 }
526 }
527
528 fn parallelize_gates_impl(&self, gates: &mut Vec<Box<dyn GateOp>>) {
529 let owned = std::mem::take(gates);
530 *gates = parallelize_gates(owned);
531 }
532
533 fn reduce_error_gates(&self, gates: &[Box<dyn GateOp>]) -> QuantRS2Result<()> {
534 for i in 0..gates.len() {
535 if gates[i].name() == "Toffoli" {
536 }
538 }
539 Ok(())
540 }
541
542 fn optimize_for_speed(&self, gates: &[Box<dyn GateOp>]) -> QuantRS2Result<()> {
543 for i in 0..gates.len() {
544 if gates[i].name() == "Toffoli" {
545 }
547 }
548 Ok(())
549 }
550}
551
552pub struct TwoQubitOptimization {
554 use_kak_decomposition: bool,
555 optimize_cnots: bool,
556}
557
558impl TwoQubitOptimization {
559 #[must_use]
560 pub const fn new(use_kak_decomposition: bool, optimize_cnots: bool) -> Self {
561 Self {
562 use_kak_decomposition,
563 optimize_cnots,
564 }
565 }
566}
567
568impl TwoQubitOptimization {
569 fn qubit_ids(gate: &dyn GateOp) -> HashSet<u32> {
571 gate.qubits().into_iter().map(|q| q.id()).collect()
572 }
573
574 fn range_touches(gates: &[Box<dyn GateOp>], from: usize, to: usize, qid: u32) -> bool {
576 if from >= to {
577 return false;
578 }
579 gates[from..to]
580 .iter()
581 .any(|g| g.qubits().iter().any(|q| q.id() == qid))
582 }
583
584 fn next_on_pair(
586 gates: &[Box<dyn GateOp>],
587 start: usize,
588 skip: &[bool],
589 qa: u32,
590 qb: u32,
591 ) -> Option<usize> {
592 for k in start..gates.len() {
593 if skip[k] {
594 continue;
595 }
596 let ids = Self::qubit_ids(gates[k].as_ref());
597 if ids.contains(&qa) || ids.contains(&qb) {
598 return Some(k);
599 }
600 }
601 None
602 }
603
604 fn sweep(gates: Vec<Box<dyn GateOp>>) -> (Vec<Box<dyn GateOp>>, bool) {
606 let n = gates.len();
607 let mut skip = vec![false; n];
608 let mut swap_at: HashMap<usize, multi::SWAP> = HashMap::new();
609 let mut cnot_at: HashMap<usize, multi::CNOT> = HashMap::new();
610
611 for i in 0..n {
613 if skip[i] {
614 continue;
615 }
616 let ci = match gates[i].as_any().downcast_ref::<multi::CNOT>() {
617 Some(c) => *c,
618 None => continue,
619 };
620 let (qa, qb) = (ci.control.id(), ci.target.id());
621 if let Some(j) = Self::next_on_pair(&gates, i + 1, &skip, qa, qb) {
622 if let Some(cj) = gates[j].as_any().downcast_ref::<multi::CNOT>() {
623 if cj.control.id() == qa && cj.target.id() == qb {
624 skip[i] = true;
625 skip[j] = true;
626 }
627 }
628 }
629 }
630
631 for i in 0..n {
633 if skip[i] {
634 continue;
635 }
636 let c0 = match gates[i].as_any().downcast_ref::<multi::CNOT>() {
637 Some(c) => *c,
638 None => continue,
639 };
640 let (qa, qb) = (c0.control.id(), c0.target.id());
641 let j1 = match Self::next_on_pair(&gates, i + 1, &skip, qa, qb) {
642 Some(k) => k,
643 None => continue,
644 };
645 match gates[j1].as_any().downcast_ref::<multi::CNOT>() {
646 Some(c) if c.control.id() == qb && c.target.id() == qa => {}
647 _ => continue,
648 }
649 let j2 = match Self::next_on_pair(&gates, j1 + 1, &skip, qa, qb) {
650 Some(k) => k,
651 None => continue,
652 };
653 if let Some(c2) = gates[j2].as_any().downcast_ref::<multi::CNOT>() {
654 if c2.control.id() == qa && c2.target.id() == qb {
655 skip[i] = true;
656 skip[j1] = true;
657 skip[j2] = true;
658 swap_at.insert(
659 i,
660 multi::SWAP {
661 qubit1: c0.control,
662 qubit2: c0.target,
663 },
664 );
665 }
666 }
667 }
668
669 for i in 0..n {
671 if skip[i] {
672 continue;
673 }
674 let h1 = match gates[i].as_any().downcast_ref::<single::Hadamard>() {
675 Some(h) => *h,
676 None => continue,
677 };
678 let qb = h1.target.id();
679 let j = match Self::next_on_pair(&gates, i + 1, &skip, qb, qb) {
680 Some(k) => k,
681 None => continue,
682 };
683 let cz = match gates[j].as_any().downcast_ref::<multi::CZ>() {
684 Some(c) if c.control.id() == qb || c.target.id() == qb => *c,
685 _ => continue,
686 };
687 let qa = if cz.control.id() == qb {
688 cz.target.id()
689 } else {
690 cz.control.id()
691 };
692 let k = match Self::next_on_pair(&gates, j + 1, &skip, qb, qb) {
693 Some(k) => k,
694 None => continue,
695 };
696 match gates[k].as_any().downcast_ref::<single::Hadamard>() {
697 Some(h) if h.target.id() == qb => {}
698 _ => continue,
699 }
700 if Self::range_touches(&gates, i + 1, j, qa)
701 || Self::range_touches(&gates, j + 1, k, qa)
702 {
703 continue;
704 }
705 let (ctrl, tgt) = if cz.control.id() == qa {
706 (cz.control, cz.target)
707 } else {
708 (cz.target, cz.control)
709 };
710 skip[i] = true;
711 skip[j] = true;
712 skip[k] = true;
713 cnot_at.insert(
714 i,
715 multi::CNOT {
716 control: ctrl,
717 target: tgt,
718 },
719 );
720 }
721
722 let any_change = skip.iter().any(|&s| s);
723 if !any_change {
724 return (gates, false);
725 }
726
727 let mut result: Vec<Box<dyn GateOp>> = Vec::with_capacity(n);
728 for idx in 0..n {
729 if !skip[idx] {
730 result.push(gates[idx].clone());
731 } else if let Some(&swap) = swap_at.get(&idx) {
732 result.push(Box::new(swap));
733 } else if let Some(&cnot) = cnot_at.get(&idx) {
734 result.push(Box::new(cnot));
735 }
736 }
738 (result, true)
739 }
740
741 fn optimize_two_qubit(gates: Vec<Box<dyn GateOp>>) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
743 let mut work = gates;
744 loop {
745 let (next, changed) = Self::sweep(work);
746 work = next;
747 if !changed {
748 break;
749 }
750 }
751 Ok(work)
752 }
753}
754
755impl OptimizationPass for TwoQubitOptimization {
756 fn name(&self) -> &'static str {
757 "Two-Qubit Optimization"
758 }
759
760 fn apply_to_gates(
761 &self,
762 gates: Vec<Box<dyn GateOp>>,
763 _cost_model: &dyn CostModel,
764 ) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
765 if !self.optimize_cnots {
766 return Ok(gates);
767 }
768 Self::optimize_two_qubit(gates)
769 }
770}