1use crate::{
8 cartan::OptimizedCartanDecomposer,
9 controlled::make_controlled,
10 error::{QuantRS2Error, QuantRS2Result},
11 gate::{single::*, GateOp},
12 matrix_ops::{DenseMatrix, QuantumMatrix},
13 qubit::QubitId,
14 synthesis::{decompose_single_qubit_zyz, SingleQubitDecomposition},
15};
16use rustc_hash::FxHashMap;
17use scirs2_core::ndarray::{s, Array2};
18use scirs2_core::Complex;
19use std::f64::consts::PI;
20
21#[derive(Debug, Clone)]
23pub struct ShannonDecomposition {
24 pub gates: Vec<Box<dyn GateOp>>,
26 pub cnot_count: usize,
28 pub single_qubit_count: usize,
30 pub depth: usize,
32}
33
34pub struct ShannonDecomposer {
36 tolerance: f64,
38 cache: FxHashMap<u64, ShannonDecomposition>,
40 max_depth: usize,
42}
43
44impl ShannonDecomposer {
45 pub fn new() -> Self {
47 Self {
48 tolerance: 1e-10,
49 cache: FxHashMap::default(),
50 max_depth: 20,
51 }
52 }
53
54 pub fn with_tolerance(tolerance: f64) -> Self {
56 Self {
57 tolerance,
58 cache: FxHashMap::default(),
59 max_depth: 20,
60 }
61 }
62
63 pub fn decompose(
65 &mut self,
66 unitary: &Array2<Complex<f64>>,
67 qubit_ids: &[QubitId],
68 ) -> QuantRS2Result<ShannonDecomposition> {
69 let n = qubit_ids.len();
70 let size = 1 << n;
71
72 if unitary.shape() != [size, size] {
74 return Err(QuantRS2Error::InvalidInput(format!(
75 "Unitary size {} doesn't match {} qubits",
76 unitary.shape()[0],
77 n
78 )));
79 }
80
81 let mat = DenseMatrix::new(unitary.clone())?;
83 if !mat.is_unitary(self.tolerance)? {
84 return Err(QuantRS2Error::InvalidInput(
85 "Matrix is not unitary".to_string(),
86 ));
87 }
88
89 if n == 0 {
91 return Ok(ShannonDecomposition {
92 gates: vec![],
93 cnot_count: 0,
94 single_qubit_count: 0,
95 depth: 0,
96 });
97 }
98
99 if n == 1 {
100 let decomp = decompose_single_qubit_zyz(&unitary.view())?;
102 let gates = self.single_qubit_to_gates(&decomp, qubit_ids[0]);
103 let count = gates.len();
104
105 return Ok(ShannonDecomposition {
106 gates,
107 cnot_count: 0,
108 single_qubit_count: count,
109 depth: count,
110 });
111 }
112
113 if n == 2 {
114 return self.decompose_two_qubit(unitary, qubit_ids);
116 }
117
118 self.decompose_recursive(unitary, qubit_ids, 0)
120 }
121
122 fn decompose_recursive(
124 &mut self,
125 unitary: &Array2<Complex<f64>>,
126 qubit_ids: &[QubitId],
127 depth: usize,
128 ) -> QuantRS2Result<ShannonDecomposition> {
129 if depth > self.max_depth {
130 return Err(QuantRS2Error::InvalidInput(
131 "Maximum recursion depth exceeded".to_string(),
132 ));
133 }
134
135 let n = qubit_ids.len();
136 let half_size = 1 << (n - 1);
137
138 let a = unitary.slice(s![..half_size, ..half_size]).to_owned();
142 let b = unitary.slice(s![..half_size, half_size..]).to_owned();
143 let c = unitary.slice(s![half_size.., ..half_size]).to_owned();
144 let d = unitary.slice(s![half_size.., half_size..]).to_owned();
145
146 let (v, w, u_diag) = self.block_diagonalize(&a, &b, &c, &d)?;
150
151 let mut gates: Vec<Box<dyn GateOp>> = Vec::new();
152 let mut cnot_count = 0;
153 let mut single_qubit_count = 0;
154
155 if !self.is_identity(&w) {
157 let w_decomp = self.decompose_recursive(&w, &qubit_ids[1..], depth + 1)?;
158 gates.extend(w_decomp.gates);
159 cnot_count += w_decomp.cnot_count;
160 single_qubit_count += w_decomp.single_qubit_count;
161 }
162
163 let diag_gates = self.decompose_controlled_diagonal(&u_diag, qubit_ids)?;
165 cnot_count += diag_gates.1;
166 single_qubit_count += diag_gates.2;
167 gates.extend(diag_gates.0);
168
169 if !self.is_identity(&v) {
171 let v_dag = v.mapv(|z| z.conj()).t().to_owned();
172 let v_decomp = self.decompose_recursive(&v_dag, &qubit_ids[1..], depth + 1)?;
173 gates.extend(v_decomp.gates);
174 cnot_count += v_decomp.cnot_count;
175 single_qubit_count += v_decomp.single_qubit_count;
176 }
177
178 let depth = gates.len();
180
181 Ok(ShannonDecomposition {
182 gates,
183 cnot_count,
184 single_qubit_count,
185 depth,
186 })
187 }
188
189 fn block_diagonalize(
202 &self,
203 a: &Array2<Complex<f64>>,
204 b: &Array2<Complex<f64>>,
205 c: &Array2<Complex<f64>>,
206 d: &Array2<Complex<f64>>,
207 ) -> QuantRS2Result<(
208 Array2<Complex<f64>>,
209 Array2<Complex<f64>>,
210 Array2<Complex<f64>>,
211 )> {
212 let size = a.shape()[0];
213
214 let b_norm = b.iter().map(|z| z.norm_sqr()).sum::<f64>().sqrt();
216 let c_norm = c.iter().map(|z| z.norm_sqr()).sum::<f64>().sqrt();
217
218 if b_norm < self.tolerance && c_norm < self.tolerance {
219 let identity = Array2::eye(size);
220 let combined = self.combine_blocks(a, b, c, d);
221 return Ok((identity.clone(), identity, combined));
222 }
223
224 Err(QuantRS2Error::UnsupportedOperation(
226 "quantum Shannon block-diagonalization for matrices with non-zero off-diagonal \
227 blocks requires cosine-sine decomposition and is not yet implemented; \
228 use the multi-qubit KAK decomposer (CSD/block-diagonal paths) instead"
229 .to_string(),
230 ))
231 }
232
233 fn combine_blocks(
235 &self,
236 a: &Array2<Complex<f64>>,
237 b: &Array2<Complex<f64>>,
238 c: &Array2<Complex<f64>>,
239 d: &Array2<Complex<f64>>,
240 ) -> Array2<Complex<f64>> {
241 let size = a.shape()[0];
242 let total_size = 2 * size;
243 let mut result = Array2::zeros((total_size, total_size));
244
245 result.slice_mut(s![..size, ..size]).assign(a);
246 result.slice_mut(s![..size, size..]).assign(b);
247 result.slice_mut(s![size.., ..size]).assign(c);
248 result.slice_mut(s![size.., size..]).assign(d);
249
250 result
251 }
252
253 fn decompose_controlled_diagonal(
255 &self,
256 diagonal: &Array2<Complex<f64>>,
257 qubit_ids: &[QubitId],
258 ) -> QuantRS2Result<(Vec<Box<dyn GateOp>>, usize, usize)> {
259 let mut gates: Vec<Box<dyn GateOp>> = Vec::new();
260 let mut cnot_count = 0;
261 let mut single_qubit_count = 0;
262
263 let n = diagonal.shape()[0];
265 let mut phases = Vec::with_capacity(n);
266
267 for i in 0..n {
268 let phase = diagonal[[i, i]].arg();
269 phases.push(phase);
270 }
271
272 let control = qubit_ids[0];
275
276 for (i, &phase) in phases.iter().enumerate() {
277 if phase.abs() > self.tolerance {
278 if i == 0 {
279 let gate: Box<dyn GateOp> = Box::new(RotationZ {
281 target: control,
282 theta: phase,
283 });
284 gates.push(gate);
285 single_qubit_count += 1;
286 } else {
287 let base_gate = Box::new(RotationZ {
291 target: qubit_ids[1],
292 theta: phase,
293 });
294
295 let controlled = Box::new(make_controlled(vec![control], *base_gate));
296 gates.push(controlled);
297 cnot_count += 2; single_qubit_count += 3; }
300 }
301 }
302
303 Ok((gates, cnot_count, single_qubit_count))
304 }
305
306 fn decompose_two_qubit(
308 &self,
309 unitary: &Array2<Complex<f64>>,
310 qubit_ids: &[QubitId],
311 ) -> QuantRS2Result<ShannonDecomposition> {
312 if self.is_identity(unitary) {
314 return Ok(ShannonDecomposition {
315 gates: vec![],
316 cnot_count: 0,
317 single_qubit_count: 0,
318 depth: 0,
319 });
320 }
321
322 let mut cartan_decomposer = OptimizedCartanDecomposer::new();
324 let cartan_decomp = cartan_decomposer.decompose(unitary)?;
325 let gates = cartan_decomposer.base.to_gates(&cartan_decomp, qubit_ids)?;
326
327 let mut cnot_count = 0;
329 let mut single_qubit_count = 0;
330
331 for gate in &gates {
332 match gate.name() {
333 "CNOT" => cnot_count += 1,
334 _ => single_qubit_count += 1,
335 }
336 }
337
338 let depth = gates.len();
339
340 Ok(ShannonDecomposition {
341 gates,
342 cnot_count,
343 single_qubit_count,
344 depth,
345 })
346 }
347
348 fn single_qubit_to_gates(
350 &self,
351 decomp: &SingleQubitDecomposition,
352 qubit: QubitId,
353 ) -> Vec<Box<dyn GateOp>> {
354 let mut gates = Vec::new();
355
356 if decomp.theta1.abs() > self.tolerance {
358 gates.push(Box::new(RotationZ {
359 target: qubit,
360 theta: decomp.theta1,
361 }) as Box<dyn GateOp>);
362 }
363
364 if decomp.phi.abs() > self.tolerance {
366 gates.push(Box::new(RotationY {
367 target: qubit,
368 theta: decomp.phi,
369 }) as Box<dyn GateOp>);
370 }
371
372 if decomp.theta2.abs() > self.tolerance {
374 gates.push(Box::new(RotationZ {
375 target: qubit,
376 theta: decomp.theta2,
377 }) as Box<dyn GateOp>);
378 }
379
380 gates
383 }
384
385 fn is_identity(&self, matrix: &Array2<Complex<f64>>) -> bool {
387 let n = matrix.shape()[0];
388
389 for i in 0..n {
390 for j in 0..n {
391 let expected = if i == j {
392 Complex::new(1.0, 0.0)
393 } else {
394 Complex::new(0.0, 0.0)
395 };
396 if (matrix[[i, j]] - expected).norm() > self.tolerance {
397 return false;
398 }
399 }
400 }
401
402 true
403 }
404}
405
406pub struct OptimizedShannonDecomposer {
408 base: ShannonDecomposer,
409 peephole: bool,
411 commutation: bool,
413}
414
415impl OptimizedShannonDecomposer {
416 pub fn new() -> Self {
418 Self {
419 base: ShannonDecomposer::new(),
420 peephole: true,
421 commutation: true,
422 }
423 }
424
425 pub fn decompose(
427 &mut self,
428 unitary: &Array2<Complex<f64>>,
429 qubit_ids: &[QubitId],
430 ) -> QuantRS2Result<ShannonDecomposition> {
431 let mut decomp = self.base.decompose(unitary, qubit_ids)?;
433
434 if self.peephole {
435 decomp = self.apply_peephole_optimization(decomp)?;
436 }
437
438 if self.commutation {
439 decomp = self.apply_commutation_optimization(decomp)?;
440 }
441
442 Ok(decomp)
443 }
444
445 fn apply_peephole_optimization(
447 &self,
448 mut decomp: ShannonDecomposition,
449 ) -> QuantRS2Result<ShannonDecomposition> {
450 let mut optimized_gates = Vec::new();
456 let mut i = 0;
457
458 while i < decomp.gates.len() {
459 if i + 1 < decomp.gates.len() {
460 if self.gates_cancel(&decomp.gates[i], &decomp.gates[i + 1]) {
462 i += 2;
464 decomp.cnot_count =
465 decomp
466 .cnot_count
467 .saturating_sub(if decomp.gates[i - 2].name() == "CNOT" {
468 2
469 } else {
470 0
471 });
472 decomp.single_qubit_count = decomp.single_qubit_count.saturating_sub(
473 if decomp.gates[i - 2].name() == "CNOT" {
474 0
475 } else {
476 2
477 },
478 );
479 continue;
480 }
481
482 if let Some(merged) =
484 self.try_merge_rotations(&decomp.gates[i], &decomp.gates[i + 1])
485 {
486 optimized_gates.push(merged);
487 i += 2;
488 decomp.single_qubit_count = decomp.single_qubit_count.saturating_sub(1);
489 continue;
490 }
491 }
492
493 optimized_gates.push(decomp.gates[i].clone());
494 i += 1;
495 }
496
497 decomp.gates = optimized_gates;
498 decomp.depth = decomp.gates.len();
499
500 Ok(decomp)
501 }
502
503 const fn apply_commutation_optimization(
505 &self,
506 decomp: ShannonDecomposition,
507 ) -> QuantRS2Result<ShannonDecomposition> {
508 Ok(decomp)
513 }
514
515 fn gates_cancel(&self, gate1: &Box<dyn GateOp>, gate2: &Box<dyn GateOp>) -> bool {
517 if gate1.name() == gate2.name() && gate1.qubits() == gate2.qubits() {
519 match gate1.name() {
520 "X" | "Y" | "Z" | "H" | "CNOT" | "SWAP" => true,
521 _ => false,
522 }
523 } else {
524 false
525 }
526 }
527
528 fn try_merge_rotations(
530 &self,
531 gate1: &Box<dyn GateOp>,
532 gate2: &Box<dyn GateOp>,
533 ) -> Option<Box<dyn GateOp>> {
534 if gate1.qubits() != gate2.qubits() || gate1.qubits().len() != 1 {
536 return None;
537 }
538
539 let qubit = gate1.qubits()[0];
540
541 match (gate1.name(), gate2.name()) {
542 ("RZ", "RZ") => {
543 let theta1 = gate1.as_any().downcast_ref::<RotationZ>()?.theta;
544 let theta2 = gate2.as_any().downcast_ref::<RotationZ>()?.theta;
545 Some(Box::new(RotationZ {
546 target: qubit,
547 theta: theta1 + theta2,
548 }))
549 }
550 ("RX", "RX") => {
551 let theta1 = gate1.as_any().downcast_ref::<RotationX>()?.theta;
552 let theta2 = gate2.as_any().downcast_ref::<RotationX>()?.theta;
553 Some(Box::new(RotationX {
554 target: qubit,
555 theta: theta1 + theta2,
556 }))
557 }
558 ("RY", "RY") => {
559 let theta1 = gate1.as_any().downcast_ref::<RotationY>()?.theta;
560 let theta2 = gate2.as_any().downcast_ref::<RotationY>()?.theta;
561 Some(Box::new(RotationY {
562 target: qubit,
563 theta: theta1 + theta2,
564 }))
565 }
566 _ => None,
567 }
568 }
569}
570
571pub fn shannon_decompose(
573 unitary: &Array2<Complex<f64>>,
574 qubit_ids: &[QubitId],
575) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
576 let mut decomposer = ShannonDecomposer::new();
577 let decomp = decomposer.decompose(unitary, qubit_ids)?;
578 Ok(decomp.gates)
579}
580
581#[cfg(test)]
582mod tests {
583 use super::*;
584 use scirs2_core::ndarray::Array2;
585 use scirs2_core::Complex;
586
587 #[test]
588 fn test_shannon_single_qubit() {
589 let mut decomposer = ShannonDecomposer::new();
590
591 let h = Array2::from_shape_vec(
593 (2, 2),
594 vec![
595 Complex::new(1.0, 0.0),
596 Complex::new(1.0, 0.0),
597 Complex::new(1.0, 0.0),
598 Complex::new(-1.0, 0.0),
599 ],
600 )
601 .expect("Failed to create Hadamard matrix")
602 / Complex::new(2.0_f64.sqrt(), 0.0);
603
604 let qubit_ids = vec![QubitId(0)];
605 let decomp = decomposer
606 .decompose(&h, &qubit_ids)
607 .expect("Failed to decompose Hadamard gate");
608
609 assert!(decomp.single_qubit_count <= 3);
611 assert_eq!(decomp.cnot_count, 0);
612 }
613
614 #[test]
615 fn test_shannon_two_qubit() {
616 let mut decomposer = ShannonDecomposer::new();
617
618 let cnot = Array2::from_shape_vec(
620 (4, 4),
621 vec![
622 Complex::new(1.0, 0.0),
623 Complex::new(0.0, 0.0),
624 Complex::new(0.0, 0.0),
625 Complex::new(0.0, 0.0),
626 Complex::new(0.0, 0.0),
627 Complex::new(1.0, 0.0),
628 Complex::new(0.0, 0.0),
629 Complex::new(0.0, 0.0),
630 Complex::new(0.0, 0.0),
631 Complex::new(0.0, 0.0),
632 Complex::new(0.0, 0.0),
633 Complex::new(1.0, 0.0),
634 Complex::new(0.0, 0.0),
635 Complex::new(0.0, 0.0),
636 Complex::new(1.0, 0.0),
637 Complex::new(0.0, 0.0),
638 ],
639 )
640 .expect("Failed to create CNOT matrix");
641
642 let qubit_ids = vec![QubitId(0), QubitId(1)];
643 let decomp = decomposer
644 .decompose(&cnot, &qubit_ids)
645 .expect("Failed to decompose CNOT gate");
646
647 assert!(decomp.cnot_count <= 3);
649 }
650
651 #[test]
652 fn test_optimized_decomposer() {
653 let mut decomposer = OptimizedShannonDecomposer::new();
654
655 let identity = Array2::eye(4);
657 let identity_complex = identity.mapv(|x| Complex::new(x, 0.0));
658
659 let qubit_ids = vec![QubitId(0), QubitId(1)];
660 let decomp = decomposer
661 .decompose(&identity_complex, &qubit_ids)
662 .expect("Failed to decompose identity matrix");
663
664 assert_eq!(decomp.gates.len(), 0);
666 }
667
668 #[test]
669 fn test_merge_rz_rotations() {
670 let decomposer = OptimizedShannonDecomposer::new();
671 let qubit = QubitId(0);
672 let g1 = Box::new(RotationZ {
673 target: qubit,
674 theta: 0.3,
675 }) as Box<dyn GateOp>;
676 let g2 = Box::new(RotationZ {
677 target: qubit,
678 theta: 0.4,
679 }) as Box<dyn GateOp>;
680 let merged = decomposer
681 .try_merge_rotations(&g1, &g2)
682 .expect("should merge RZ+RZ");
683 let rz = merged
684 .as_any()
685 .downcast_ref::<RotationZ>()
686 .expect("merged gate must be RotationZ");
687 assert!(
688 (rz.theta - 0.7).abs() < 1e-10,
689 "merged theta should be 0.7, got {}",
690 rz.theta
691 );
692 }
693
694 #[test]
695 fn test_merge_rx_rotations() {
696 let decomposer = OptimizedShannonDecomposer::new();
697 let qubit = QubitId(0);
698 let g1 = Box::new(RotationX {
699 target: qubit,
700 theta: 0.5,
701 }) as Box<dyn GateOp>;
702 let g2 = Box::new(RotationX {
703 target: qubit,
704 theta: 0.3,
705 }) as Box<dyn GateOp>;
706 let merged = decomposer
707 .try_merge_rotations(&g1, &g2)
708 .expect("should merge RX+RX");
709 let rx = merged
710 .as_any()
711 .downcast_ref::<RotationX>()
712 .expect("merged gate must be RotationX");
713 assert!(
714 (rx.theta - 0.8).abs() < 1e-10,
715 "merged theta should be 0.8, got {}",
716 rx.theta
717 );
718 }
719
720 #[test]
724 fn test_block_diagonalize_exact_block_diagonal() {
725 let decomposer = ShannonDecomposer::new();
726 let a = Array2::from_shape_vec(
728 (2, 2),
729 vec![
730 Complex::new(0.0, 0.0),
731 Complex::new(1.0, 0.0),
732 Complex::new(1.0, 0.0),
733 Complex::new(0.0, 0.0),
734 ],
735 )
736 .expect("A");
737 let d = Array2::from_shape_vec(
738 (2, 2),
739 vec![
740 Complex::new(0.0, 0.0),
741 Complex::new(0.0, -1.0),
742 Complex::new(0.0, 1.0),
743 Complex::new(0.0, 0.0),
744 ],
745 )
746 .expect("D");
747 let zero = Array2::<Complex<f64>>::zeros((2, 2));
748
749 let (v, w, u_diag) = decomposer
750 .block_diagonalize(&a, &zero, &zero, &d)
751 .expect("block-diagonal case must succeed");
752
753 let id = Array2::<Complex<f64>>::eye(2);
755 let v_err = v
756 .iter()
757 .zip(id.iter())
758 .map(|(x, y)| (x - y).norm_sqr())
759 .sum::<f64>()
760 .sqrt();
761 let w_err = w
762 .iter()
763 .zip(id.iter())
764 .map(|(x, y)| (x - y).norm_sqr())
765 .sum::<f64>()
766 .sqrt();
767 assert!(v_err < 1e-12 && w_err < 1e-12, "V, W should be identity");
768
769 let original = decomposer.combine_blocks(&a, &zero, &zero, &d);
771 let err = u_diag
772 .iter()
773 .zip(original.iter())
774 .map(|(x, y)| (x - y).norm_sqr())
775 .sum::<f64>()
776 .sqrt();
777 assert!(err < 1e-12, "u_diag must equal diag(A, D), err {err}");
778 }
779
780 #[test]
783 fn test_block_diagonalize_general_honest_error() {
784 let decomposer = ShannonDecomposer::new();
785 let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
786 let a = Array2::from_shape_vec(
789 (2, 2),
790 vec![
791 Complex::new(inv_sqrt2, 0.0),
792 Complex::new(0.0, 0.0),
793 Complex::new(0.0, 0.0),
794 Complex::new(inv_sqrt2, 0.0),
795 ],
796 )
797 .expect("A");
798 let b = a.clone();
799 let c = a.clone();
800 let d = a.mapv(|z| -z);
801
802 let result = decomposer.block_diagonalize(&a, &b, &c, &d);
803 assert!(matches!(
804 result,
805 Err(QuantRS2Error::UnsupportedOperation(_))
806 ));
807 }
808
809 #[test]
810 fn test_no_merge_different_axes() {
811 let decomposer = OptimizedShannonDecomposer::new();
812 let qubit = QubitId(0);
813 let g1 = Box::new(RotationZ {
814 target: qubit,
815 theta: 0.3,
816 }) as Box<dyn GateOp>;
817 let g2 = Box::new(RotationX {
818 target: qubit,
819 theta: 0.4,
820 }) as Box<dyn GateOp>;
821 assert!(
822 decomposer.try_merge_rotations(&g1, &g2).is_none(),
823 "RZ and RX should not merge"
824 );
825 }
826}