1use crate::error::{QuantRS2Error, QuantRS2Result};
7use scirs2_core::ndarray::{Array1, Array2};
8use scirs2_core::Complex64;
9use std::collections::HashMap;
10use std::f64::consts::PI;
11use std::fmt;
12
13type FusionCoeff = Complex64;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub struct AnyonType {
19 pub id: u32,
21 pub label: &'static str,
23}
24
25impl AnyonType {
26 pub const fn new(id: u32, label: &'static str) -> Self {
28 Self { id, label }
29 }
30
31 pub const VACUUM: Self = Self::new(0, "1");
33}
34
35impl fmt::Display for AnyonType {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 write!(f, "{}", self.label)
38 }
39}
40
41pub trait AnyonModel: Send + Sync {
43 fn anyon_types(&self) -> &[AnyonType];
45
46 fn quantum_dimension(&self, anyon: AnyonType) -> f64;
48
49 fn topological_spin(&self, anyon: AnyonType) -> Complex64;
51
52 fn can_fuse(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> bool;
54
55 fn fusion_multiplicity(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> u32;
57
58 fn f_symbol(
60 &self,
61 a: AnyonType,
62 b: AnyonType,
63 c: AnyonType,
64 d: AnyonType,
65 e: AnyonType,
66 f: AnyonType,
67 ) -> FusionCoeff;
68
69 fn r_symbol(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> FusionCoeff;
71
72 fn name(&self) -> &str;
74
75 fn is_modular(&self) -> bool {
77 self.anyon_types()
78 .iter()
79 .all(|&a| self.quantum_dimension(a) > 0.0)
80 }
81
82 fn total_quantum_dimension(&self) -> f64 {
84 self.anyon_types()
85 .iter()
86 .map(|&a| self.quantum_dimension(a).powi(2))
87 .sum::<f64>()
88 .sqrt()
89 }
90}
91
92pub struct FibonacciModel {
94 anyons: Vec<AnyonType>,
95 phi: f64, }
97
98impl FibonacciModel {
99 pub fn new() -> Self {
101 let phi = f64::midpoint(1.0, 5.0_f64.sqrt());
102 let anyons = vec![
103 AnyonType::new(0, "1"), AnyonType::new(1, "τ"), ];
106
107 Self { anyons, phi }
108 }
109}
110
111impl Default for FibonacciModel {
112 fn default() -> Self {
113 Self::new()
114 }
115}
116
117impl AnyonModel for FibonacciModel {
118 fn anyon_types(&self) -> &[AnyonType] {
119 &self.anyons
120 }
121
122 fn quantum_dimension(&self, anyon: AnyonType) -> f64 {
123 match anyon.id {
124 0 => 1.0, 1 => self.phi, _ => 0.0,
127 }
128 }
129
130 fn topological_spin(&self, anyon: AnyonType) -> Complex64 {
131 match anyon.id {
132 0 => Complex64::new(1.0, 0.0), 1 => Complex64::from_polar(1.0, 4.0 * PI / 5.0), _ => Complex64::new(0.0, 0.0),
135 }
136 }
137
138 fn can_fuse(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> bool {
139 self.fusion_multiplicity(a, b, c) > 0
140 }
141
142 fn fusion_multiplicity(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> u32 {
143 match (a.id, b.id, c.id) {
144 (0, x, y) | (x, 0, y) if x == y => 1, (1, 1, 0 | 1) => 1, _ => 0,
147 }
148 }
149
150 fn f_symbol(
151 &self,
152 a: AnyonType,
153 b: AnyonType,
154 c: AnyonType,
155 d: AnyonType,
156 e: AnyonType,
157 f: AnyonType,
158 ) -> FusionCoeff {
159 if a.id == 1 && b.id == 1 && c.id == 1 && d.id == 1 {
162 if e.id == 1 && f.id == 1 {
163 Complex64::new(1.0 / self.phi, 0.0)
165 } else if e.id == 1 && f.id == 0 {
166 Complex64::new(1.0 / self.phi.sqrt(), 0.0)
168 } else if e.id == 0 && f.id == 1 {
169 Complex64::new(1.0 / self.phi.sqrt(), 0.0)
171 } else {
172 Complex64::new(0.0, 0.0)
173 }
174 } else {
175 if self.is_valid_fusion_tree(a, b, c, d, e, f) {
177 Complex64::new(1.0, 0.0)
178 } else {
179 Complex64::new(0.0, 0.0)
180 }
181 }
182 }
183
184 fn r_symbol(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> FusionCoeff {
185 if self.can_fuse(a, b, c) {
187 let theta_a = self.topological_spin(a);
188 let theta_b = self.topological_spin(b);
189 let theta_c = self.topological_spin(c);
190 let r = theta_c / (theta_a * theta_b);
191 Complex64::from_polar(1.0, r.arg())
193 } else {
194 Complex64::new(0.0, 0.0)
195 }
196 }
197
198 fn name(&self) -> &'static str {
199 "Fibonacci"
200 }
201}
202
203impl FibonacciModel {
204 fn is_valid_fusion_tree(
206 &self,
207 a: AnyonType,
208 b: AnyonType,
209 c: AnyonType,
210 d: AnyonType,
211 e: AnyonType,
212 f: AnyonType,
213 ) -> bool {
214 self.can_fuse(a, b, e)
215 && self.can_fuse(e, c, d)
216 && self.can_fuse(b, c, f)
217 && self.can_fuse(a, f, d)
218 }
219}
220
221pub struct IsingModel {
223 anyons: Vec<AnyonType>,
224}
225
226impl IsingModel {
227 pub fn new() -> Self {
229 let anyons = vec![
230 AnyonType::new(0, "1"), AnyonType::new(1, "σ"), AnyonType::new(2, "ψ"), ];
234
235 Self { anyons }
236 }
237}
238
239impl Default for IsingModel {
240 fn default() -> Self {
241 Self::new()
242 }
243}
244
245impl AnyonModel for IsingModel {
246 fn anyon_types(&self) -> &[AnyonType] {
247 &self.anyons
248 }
249
250 fn quantum_dimension(&self, anyon: AnyonType) -> f64 {
251 match anyon.id {
252 0 | 2 => 1.0, 1 => 2.0_f64.sqrt(), _ => 0.0,
255 }
256 }
257
258 fn topological_spin(&self, anyon: AnyonType) -> Complex64 {
259 match anyon.id {
260 0 => Complex64::new(1.0, 0.0), 1 => Complex64::from_polar(1.0, PI / 8.0), 2 => Complex64::new(-1.0, 0.0), _ => Complex64::new(0.0, 0.0),
264 }
265 }
266
267 fn can_fuse(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> bool {
268 self.fusion_multiplicity(a, b, c) > 0
269 }
270
271 fn fusion_multiplicity(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> u32 {
272 match (a.id, b.id, c.id) {
273 (0, x, y) | (x, 0, y) if x == y => 1,
275 (1, 1, 0 | 2) | (1, 2, 1) | (2, 1, 1) | (2, 2, 0) => 1,
277 _ => 0,
278 }
279 }
280
281 fn f_symbol(
282 &self,
283 a: AnyonType,
284 b: AnyonType,
285 c: AnyonType,
286 d: AnyonType,
287 e: AnyonType,
288 f: AnyonType,
289 ) -> FusionCoeff {
290 if a.id == 1 && b.id == 1 && c.id == 1 && d.id == 1 {
293 match (e.id, f.id) {
294 (0 | 2, 0 | 2) => Complex64::new(0.5, 0.0),
295 _ => Complex64::new(0.0, 0.0),
296 }
297 } else if self.is_valid_fusion_tree(a, b, c, d, e, f) {
298 Complex64::new(1.0, 0.0)
299 } else {
300 Complex64::new(0.0, 0.0)
301 }
302 }
303
304 fn r_symbol(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> FusionCoeff {
305 match (a.id, b.id, c.id) {
307 (1, 1, 2) | (2, 2, 0) => Complex64::new(-1.0, 0.0),
309 _ => {
311 if self.can_fuse(a, b, c) {
312 let theta_a = self.topological_spin(a);
313 let theta_b = self.topological_spin(b);
314 let theta_c = self.topological_spin(c);
315 theta_c / (theta_a * theta_b)
316 } else {
317 Complex64::new(0.0, 0.0)
318 }
319 }
320 }
321 }
322
323 fn name(&self) -> &'static str {
324 "Ising"
325 }
326}
327
328impl IsingModel {
329 fn is_valid_fusion_tree(
331 &self,
332 a: AnyonType,
333 b: AnyonType,
334 c: AnyonType,
335 d: AnyonType,
336 e: AnyonType,
337 f: AnyonType,
338 ) -> bool {
339 self.can_fuse(a, b, e)
340 && self.can_fuse(e, c, d)
341 && self.can_fuse(b, c, f)
342 && self.can_fuse(a, f, d)
343 }
344}
345
346#[derive(Debug, Clone)]
348pub struct AnyonWorldline {
349 pub anyon_type: AnyonType,
351 pub start: (f64, f64, f64),
353 pub end: (f64, f64, f64),
355 pub path: Vec<(f64, f64, f64)>,
357}
358
359#[derive(Debug, Clone)]
361pub struct BraidingOperation {
362 pub anyon1: usize,
364 pub anyon2: usize,
366 pub over: bool,
368}
369
370#[derive(Debug, Clone)]
372pub struct FusionTree {
373 pub external: Vec<AnyonType>,
375 pub internal: Vec<AnyonType>,
377 pub structure: Vec<(usize, usize)>,
379}
380
381impl FusionTree {
382 pub fn new(external: Vec<AnyonType>) -> Self {
384 let n = external.len();
385 let internal = if n > 2 {
386 vec![AnyonType::VACUUM; n - 2]
387 } else {
388 vec![]
389 };
390 let structure = if n > 1 {
391 (0..n - 1).map(|i| (i, i + 1)).collect()
392 } else {
393 vec![]
394 };
395
396 Self {
397 external,
398 internal,
399 structure,
400 }
401 }
402
403 pub fn total_charge(&self) -> AnyonType {
405 if self.internal.is_empty() {
406 if self.external.is_empty() {
407 AnyonType::VACUUM
408 } else if self.external.len() == 1 {
409 self.external[0]
410 } else {
411 AnyonType::VACUUM
413 }
414 } else {
415 self.internal.last().copied().unwrap_or(AnyonType::VACUUM)
417 }
418 }
419
420 pub fn set_total_charge(&mut self, charge: AnyonType) {
422 if self.external.len() == 2 && self.internal.is_empty() {
423 self.structure = vec![(charge.id as usize, charge.id as usize)];
426 }
427 }
428
429 pub fn get_fusion_outcome(&self) -> Option<AnyonType> {
431 if self.external.len() == 2 && self.internal.is_empty() && !self.structure.is_empty() {
432 let charge_id = self.structure[0].0 as u32;
433 Some(AnyonType::new(
434 charge_id,
435 match charge_id {
436 0 => "1",
437 1 => "σ",
438 2 => "ψ",
439 _ => "τ",
440 },
441 ))
442 } else {
443 None
444 }
445 }
446}
447
448pub struct TopologicalQC {
450 model: Box<dyn AnyonModel>,
452 fusion_trees: Vec<FusionTree>,
454 amplitudes: Array1<Complex64>,
456}
457
458impl TopologicalQC {
459 pub fn new(model: Box<dyn AnyonModel>, anyons: Vec<AnyonType>) -> QuantRS2Result<Self> {
461 let fusion_trees = Self::generate_fusion_trees(&*model, anyons)?;
463 let n = fusion_trees.len();
464
465 if n == 0 {
466 return Err(QuantRS2Error::InvalidInput(
467 "No valid fusion trees for given anyons".to_string(),
468 ));
469 }
470
471 let amplitudes = Array1::from_elem(n, Complex64::new(1.0 / (n as f64).sqrt(), 0.0));
473
474 Ok(Self {
475 model,
476 fusion_trees,
477 amplitudes,
478 })
479 }
480
481 fn generate_fusion_trees(
483 model: &dyn AnyonModel,
484 anyons: Vec<AnyonType>,
485 ) -> QuantRS2Result<Vec<FusionTree>> {
486 if anyons.len() < 2 {
487 return Ok(vec![FusionTree::new(anyons)]);
488 }
489
490 let mut trees = Vec::new();
491
492 if anyons.len() == 2 {
494 let a = anyons[0];
495 let b = anyons[1];
496
497 for c in model.anyon_types() {
499 if model.can_fuse(a, b, *c) {
500 let mut tree = FusionTree::new(anyons.clone());
501 tree.set_total_charge(*c);
502 trees.push(tree);
503 }
504 }
505 } else {
506 trees.push(FusionTree::new(anyons.clone()));
508 }
509
510 if trees.is_empty() {
511 trees.push(FusionTree::new(anyons));
513 }
514
515 Ok(trees)
516 }
517
518 pub fn braid(&mut self, op: &BraidingOperation) -> QuantRS2Result<()> {
520 let braid_matrix = self.compute_braiding_matrix(op)?;
522
523 self.amplitudes = braid_matrix.dot(&self.amplitudes);
525
526 Ok(())
527 }
528
529 fn compute_braiding_matrix(&self, op: &BraidingOperation) -> QuantRS2Result<Array2<Complex64>> {
531 let n = self.fusion_trees.len();
532 let mut matrix = Array2::zeros((n, n));
533
534 for (i, tree) in self.fusion_trees.iter().enumerate() {
536 if op.anyon1 < tree.external.len() && op.anyon2 < tree.external.len() {
537 let a = tree.external[op.anyon1];
538 let b = tree.external[op.anyon2];
539
540 let c = if let Some(charge) = tree.get_fusion_outcome() {
542 charge
543 } else if tree.internal.is_empty() {
544 tree.total_charge()
545 } else {
546 tree.internal[0]
547 };
548
549 let r_symbol = if op.over {
550 self.model.r_symbol(a, b, c)
551 } else {
552 self.model.r_symbol(a, b, c).conj()
553 };
554
555 matrix[(i, i)] = r_symbol;
556 } else {
557 matrix[(i, i)] = Complex64::new(1.0, 0.0);
559 }
560 }
561
562 Ok(matrix)
563 }
564
565 pub fn measure_charge(&self) -> (AnyonType, f64) {
567 let mut charge_probs: HashMap<u32, f64> = HashMap::new();
569
570 for (tree, &) in self.fusion_trees.iter().zip(&self.amplitudes) {
571 let charge = if let Some(c) = tree.get_fusion_outcome() {
572 c
573 } else {
574 tree.total_charge()
575 };
576 *charge_probs.entry(charge.id).or_insert(0.0) += amp.norm_sqr();
577 }
578
579 let (charge_id, prob) = charge_probs
580 .into_iter()
581 .max_by(|(_, p1), (_, p2)| p1.partial_cmp(p2).unwrap_or(std::cmp::Ordering::Equal))
582 .unwrap_or((0, 0.0));
583
584 let charge = self
585 .model
586 .anyon_types()
587 .iter()
588 .find(|a| a.id == charge_id)
589 .copied()
590 .unwrap_or(AnyonType::VACUUM);
591
592 (charge, prob)
593 }
594}
595
596#[derive(Debug, Clone)]
598pub struct TopologicalGate {
599 pub braids: Vec<BraidingOperation>,
601 pub comp_dim: usize,
603}
604
605impl TopologicalGate {
606 pub const fn new(braids: Vec<BraidingOperation>, comp_dim: usize) -> Self {
608 Self { braids, comp_dim }
609 }
610
611 pub fn cnot() -> Self {
613 let braids = vec![
615 BraidingOperation {
616 anyon1: 0,
617 anyon2: 1,
618 over: true,
619 },
620 BraidingOperation {
621 anyon1: 2,
622 anyon2: 3,
623 over: true,
624 },
625 BraidingOperation {
626 anyon1: 1,
627 anyon2: 2,
628 over: false,
629 },
630 ];
631
632 Self::new(braids, 4)
633 }
634
635 pub fn to_matrix(&self, _model: &dyn AnyonModel) -> QuantRS2Result<Array2<Complex64>> {
637 Ok(Array2::eye(self.comp_dim))
640 }
641}
642
643pub struct ToricCode {
645 pub size: usize,
647 pub vertex_ops: Vec<Vec<usize>>,
649 pub plaquette_ops: Vec<Vec<usize>>,
651}
652
653impl ToricCode {
654 pub fn new(size: usize) -> Self {
656 let mut vertex_ops = Vec::new();
657 let mut plaquette_ops = Vec::new();
658
659 for i in 0..size {
662 for j in 0..size {
663 let v_op = vec![
665 2 * (i * size + j), 2 * (i * size + j) + 1, ];
668 vertex_ops.push(v_op);
669
670 let p_op = vec![
672 2 * (i * size + j),
673 2 * (i * size + (j + 1) % size),
674 2 * (((i + 1) % size) * size + j),
675 2 * (i * size + j) + 1,
676 ];
677 plaquette_ops.push(p_op);
678 }
679 }
680
681 Self {
682 size,
683 vertex_ops,
684 plaquette_ops,
685 }
686 }
687
688 pub const fn num_qubits(&self) -> usize {
690 2 * self.size * self.size
691 }
692
693 pub const fn num_logical_qubits(&self) -> usize {
695 2 }
697
698 pub fn create_anyons(&self, vertices: &[usize], plaquettes: &[usize]) -> Vec<AnyonType> {
700 let mut anyons = Vec::new();
701
702 for _ in vertices {
704 anyons.push(AnyonType::new(1, "e"));
705 }
706
707 for _ in plaquettes {
709 anyons.push(AnyonType::new(2, "m"));
710 }
711
712 anyons
713 }
714}
715
716#[cfg(test)]
717mod tests {
718 use super::*;
719
720 #[test]
721 fn test_fibonacci_model() {
722 let model = FibonacciModel::new();
723
724 assert_eq!(model.quantum_dimension(AnyonType::VACUUM), 1.0);
726 assert!((model.quantum_dimension(AnyonType::new(1, "τ")) - 1.618).abs() < 0.001);
727
728 assert_eq!(
730 model.fusion_multiplicity(
731 AnyonType::VACUUM,
732 AnyonType::new(1, "τ"),
733 AnyonType::new(1, "τ")
734 ),
735 1
736 );
737
738 let expected_dim = (1.0 + model.phi.powi(2)).sqrt();
741 assert!((model.total_quantum_dimension() - expected_dim).abs() < 0.001);
742 }
743
744 #[test]
745 fn test_ising_model() {
746 let model = IsingModel::new();
747
748 assert_eq!(model.quantum_dimension(AnyonType::VACUUM), 1.0);
750 assert!((model.quantum_dimension(AnyonType::new(1, "σ")) - 1.414).abs() < 0.001);
751 assert_eq!(model.quantum_dimension(AnyonType::new(2, "ψ")), 1.0);
752
753 assert_eq!(
755 model.fusion_multiplicity(
756 AnyonType::new(1, "σ"),
757 AnyonType::new(1, "σ"),
758 AnyonType::VACUUM
759 ),
760 1
761 );
762 assert_eq!(
763 model.fusion_multiplicity(
764 AnyonType::new(1, "σ"),
765 AnyonType::new(1, "σ"),
766 AnyonType::new(2, "ψ")
767 ),
768 1
769 );
770 }
771
772 #[test]
773 fn test_fusion_tree() {
774 let anyons = vec![
775 AnyonType::new(1, "τ"),
776 AnyonType::new(1, "τ"),
777 AnyonType::new(1, "τ"),
778 ];
779
780 let tree = FusionTree::new(anyons);
781 assert_eq!(tree.external.len(), 3);
782 assert_eq!(tree.internal.len(), 1);
783 }
784
785 #[test]
786 fn test_topological_qc() {
787 let model = Box::new(FibonacciModel::new());
788 let anyons = vec![AnyonType::new(1, "τ"), AnyonType::new(1, "τ")];
789
790 let qc = TopologicalQC::new(model, anyons).expect("Failed to create TopologicalQC");
791 assert_eq!(qc.fusion_trees.len(), 2);
793
794 let (charge, _prob) = qc.measure_charge();
796 assert!(charge.id == 0 || charge.id == 1); }
798
799 #[test]
800 fn test_toric_code() {
801 let toric = ToricCode::new(4);
802
803 assert_eq!(toric.num_qubits(), 32); assert_eq!(toric.num_logical_qubits(), 2);
805
806 let anyons = toric.create_anyons(&[0, 1], &[2]);
808 assert_eq!(anyons.len(), 3);
809 }
810
811 #[test]
812 fn test_braiding_operation() {
813 let model = Box::new(IsingModel::new());
814 let anyons = vec![AnyonType::new(1, "σ"), AnyonType::new(1, "σ")];
815
816 let mut qc = TopologicalQC::new(model, anyons).expect("Failed to create TopologicalQC");
817
818 let initial_norm: f64 = qc.amplitudes.iter().map(|a| a.norm_sqr()).sum();
820 assert!(
821 (initial_norm - 1.0).abs() < 1e-10,
822 "Initial state not normalized: {}",
823 initial_norm
824 );
825
826 let braid = BraidingOperation {
828 anyon1: 0,
829 anyon2: 1,
830 over: true,
831 };
832
833 qc.braid(&braid)
834 .expect("Failed to apply braiding operation");
835
836 let norm: f64 = qc.amplitudes.iter().map(|a| a.norm_sqr()).sum();
838 assert!(
839 (norm - 1.0).abs() < 1e-10,
840 "Final state not normalized: {}",
841 norm
842 );
843 }
844}