1use crate::{
7 error::{QuantRS2Error, QuantRS2Result},
8 gate::GateOp,
9 qubit::QubitId,
10};
11use ndarray::{Array1, Array2, Array3, Array4};
12use num_complex::Complex64;
13use std::collections::HashMap;
14use std::f64::consts::PI;
15use std::fmt;
16
17type FusionCoeff = Complex64;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub struct AnyonType {
23 pub id: u32,
25 pub label: &'static str,
27}
28
29impl AnyonType {
30 pub const fn new(id: u32, label: &'static str) -> Self {
32 Self { id, label }
33 }
34
35 pub const VACUUM: Self = Self::new(0, "1");
37}
38
39impl fmt::Display for AnyonType {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 write!(f, "{}", self.label)
42 }
43}
44
45pub trait AnyonModel: Send + Sync {
47 fn anyon_types(&self) -> &[AnyonType];
49
50 fn quantum_dimension(&self, anyon: AnyonType) -> f64;
52
53 fn topological_spin(&self, anyon: AnyonType) -> Complex64;
55
56 fn can_fuse(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> bool;
58
59 fn fusion_multiplicity(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> u32;
61
62 fn f_symbol(
64 &self,
65 a: AnyonType,
66 b: AnyonType,
67 c: AnyonType,
68 d: AnyonType,
69 e: AnyonType,
70 f: AnyonType,
71 ) -> FusionCoeff;
72
73 fn r_symbol(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> FusionCoeff;
75
76 fn name(&self) -> &str;
78
79 fn is_modular(&self) -> bool {
81 self.anyon_types()
82 .iter()
83 .all(|&a| self.quantum_dimension(a) > 0.0)
84 }
85
86 fn total_quantum_dimension(&self) -> f64 {
88 self.anyon_types()
89 .iter()
90 .map(|&a| self.quantum_dimension(a).powi(2))
91 .sum::<f64>()
92 .sqrt()
93 }
94}
95
96pub struct FibonacciModel {
98 anyons: Vec<AnyonType>,
99 phi: f64, }
101
102impl FibonacciModel {
103 pub fn new() -> Self {
105 let phi = (1.0 + 5.0_f64.sqrt()) / 2.0;
106 let anyons = vec![
107 AnyonType::new(0, "1"), AnyonType::new(1, "τ"), ];
110
111 Self { anyons, phi }
112 }
113}
114
115impl Default for FibonacciModel {
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121impl AnyonModel for FibonacciModel {
122 fn anyon_types(&self) -> &[AnyonType] {
123 &self.anyons
124 }
125
126 fn quantum_dimension(&self, anyon: AnyonType) -> f64 {
127 match anyon.id {
128 0 => 1.0, 1 => self.phi, _ => 0.0,
131 }
132 }
133
134 fn topological_spin(&self, anyon: AnyonType) -> Complex64 {
135 match anyon.id {
136 0 => Complex64::new(1.0, 0.0), 1 => Complex64::from_polar(1.0, 4.0 * PI / 5.0), _ => Complex64::new(0.0, 0.0),
139 }
140 }
141
142 fn can_fuse(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> bool {
143 self.fusion_multiplicity(a, b, c) > 0
144 }
145
146 fn fusion_multiplicity(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> u32 {
147 match (a.id, b.id, c.id) {
148 (0, x, y) | (x, 0, y) if x == y => 1, (1, 1, 0) => 1, (1, 1, 1) => 1, _ => 0,
152 }
153 }
154
155 fn f_symbol(
156 &self,
157 a: AnyonType,
158 b: AnyonType,
159 c: AnyonType,
160 d: AnyonType,
161 e: AnyonType,
162 f: AnyonType,
163 ) -> FusionCoeff {
164 if a.id == 1 && b.id == 1 && c.id == 1 && d.id == 1 {
167 if e.id == 1 && f.id == 1 {
168 Complex64::new(1.0 / self.phi, 0.0)
170 } else if e.id == 1 && f.id == 0 {
171 Complex64::new(1.0 / self.phi.sqrt(), 0.0)
173 } else if e.id == 0 && f.id == 1 {
174 Complex64::new(1.0 / self.phi.sqrt(), 0.0)
176 } else {
177 Complex64::new(0.0, 0.0)
178 }
179 } else {
180 if self.is_valid_fusion_tree(a, b, c, d, e, f) {
182 Complex64::new(1.0, 0.0)
183 } else {
184 Complex64::new(0.0, 0.0)
185 }
186 }
187 }
188
189 fn r_symbol(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> FusionCoeff {
190 if self.can_fuse(a, b, c) {
192 let theta_a = self.topological_spin(a);
193 let theta_b = self.topological_spin(b);
194 let theta_c = self.topological_spin(c);
195 let r = theta_c / (theta_a * theta_b);
196 Complex64::from_polar(1.0, r.arg())
198 } else {
199 Complex64::new(0.0, 0.0)
200 }
201 }
202
203 fn name(&self) -> &str {
204 "Fibonacci"
205 }
206}
207
208impl FibonacciModel {
209 fn is_valid_fusion_tree(
211 &self,
212 a: AnyonType,
213 b: AnyonType,
214 c: AnyonType,
215 d: AnyonType,
216 e: AnyonType,
217 f: AnyonType,
218 ) -> bool {
219 self.can_fuse(a, b, e)
220 && self.can_fuse(e, c, d)
221 && self.can_fuse(b, c, f)
222 && self.can_fuse(a, f, d)
223 }
224}
225
226pub struct IsingModel {
228 anyons: Vec<AnyonType>,
229}
230
231impl IsingModel {
232 pub fn new() -> Self {
234 let anyons = vec![
235 AnyonType::new(0, "1"), AnyonType::new(1, "σ"), AnyonType::new(2, "ψ"), ];
239
240 Self { anyons }
241 }
242}
243
244impl Default for IsingModel {
245 fn default() -> Self {
246 Self::new()
247 }
248}
249
250impl AnyonModel for IsingModel {
251 fn anyon_types(&self) -> &[AnyonType] {
252 &self.anyons
253 }
254
255 fn quantum_dimension(&self, anyon: AnyonType) -> f64 {
256 match anyon.id {
257 0 => 1.0, 1 => 2.0_f64.sqrt(), 2 => 1.0, _ => 0.0,
261 }
262 }
263
264 fn topological_spin(&self, anyon: AnyonType) -> Complex64 {
265 match anyon.id {
266 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),
270 }
271 }
272
273 fn can_fuse(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> bool {
274 self.fusion_multiplicity(a, b, c) > 0
275 }
276
277 fn fusion_multiplicity(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> u32 {
278 match (a.id, b.id, c.id) {
279 (0, x, y) | (x, 0, y) if x == y => 1,
281 (1, 1, 0) | (1, 1, 2) => 1,
283 (1, 2, 1) | (2, 1, 1) => 1,
285 (2, 2, 0) => 1,
287 _ => 0,
288 }
289 }
290
291 fn f_symbol(
292 &self,
293 a: AnyonType,
294 b: AnyonType,
295 c: AnyonType,
296 d: AnyonType,
297 e: AnyonType,
298 f: AnyonType,
299 ) -> FusionCoeff {
300 if a.id == 1 && b.id == 1 && c.id == 1 && d.id == 1 {
303 match (e.id, f.id) {
304 (0, 0) | (2, 2) => Complex64::new(0.5, 0.0),
305 (0, 2) | (2, 0) => Complex64::new(0.5, 0.0),
306 _ => Complex64::new(0.0, 0.0),
307 }
308 } else if self.is_valid_fusion_tree(a, b, c, d, e, f) {
309 Complex64::new(1.0, 0.0)
310 } else {
311 Complex64::new(0.0, 0.0)
312 }
313 }
314
315 fn r_symbol(&self, a: AnyonType, b: AnyonType, c: AnyonType) -> FusionCoeff {
316 match (a.id, b.id, c.id) {
318 (1, 1, 2) => Complex64::new(-1.0, 0.0),
320 (2, 2, 0) => Complex64::new(-1.0, 0.0),
322 _ => {
324 if self.can_fuse(a, b, c) {
325 let theta_a = self.topological_spin(a);
326 let theta_b = self.topological_spin(b);
327 let theta_c = self.topological_spin(c);
328 theta_c / (theta_a * theta_b)
329 } else {
330 Complex64::new(0.0, 0.0)
331 }
332 }
333 }
334 }
335
336 fn name(&self) -> &str {
337 "Ising"
338 }
339}
340
341impl IsingModel {
342 fn is_valid_fusion_tree(
344 &self,
345 a: AnyonType,
346 b: AnyonType,
347 c: AnyonType,
348 d: AnyonType,
349 e: AnyonType,
350 f: AnyonType,
351 ) -> bool {
352 self.can_fuse(a, b, e)
353 && self.can_fuse(e, c, d)
354 && self.can_fuse(b, c, f)
355 && self.can_fuse(a, f, d)
356 }
357}
358
359#[derive(Debug, Clone)]
361pub struct AnyonWorldline {
362 pub anyon_type: AnyonType,
364 pub start: (f64, f64, f64),
366 pub end: (f64, f64, f64),
368 pub path: Vec<(f64, f64, f64)>,
370}
371
372#[derive(Debug, Clone)]
374pub struct BraidingOperation {
375 pub anyon1: usize,
377 pub anyon2: usize,
379 pub over: bool,
381}
382
383#[derive(Debug, Clone)]
385pub struct FusionTree {
386 pub external: Vec<AnyonType>,
388 pub internal: Vec<AnyonType>,
390 pub structure: Vec<(usize, usize)>,
392}
393
394impl FusionTree {
395 pub fn new(external: Vec<AnyonType>) -> Self {
397 let n = external.len();
398 let internal = if n > 2 {
399 vec![AnyonType::VACUUM; n - 2]
400 } else {
401 vec![]
402 };
403 let structure = if n > 1 {
404 (0..n - 1).map(|i| (i, i + 1)).collect()
405 } else {
406 vec![]
407 };
408
409 Self {
410 external,
411 internal,
412 structure,
413 }
414 }
415
416 pub fn total_charge(&self) -> AnyonType {
418 if self.internal.is_empty() {
419 if self.external.is_empty() {
420 AnyonType::VACUUM
421 } else if self.external.len() == 1 {
422 self.external[0]
423 } else {
424 AnyonType::VACUUM
426 }
427 } else {
428 *self.internal.last().unwrap()
429 }
430 }
431
432 pub fn set_total_charge(&mut self, charge: AnyonType) {
434 if self.external.len() == 2 && self.internal.is_empty() {
435 self.structure = vec![(charge.id as usize, charge.id as usize)];
438 }
439 }
440
441 pub fn get_fusion_outcome(&self) -> Option<AnyonType> {
443 if self.external.len() == 2 && self.internal.is_empty() && !self.structure.is_empty() {
444 let charge_id = self.structure[0].0 as u32;
445 Some(AnyonType::new(
446 charge_id,
447 match charge_id {
448 0 => "1",
449 1 => "σ",
450 2 => "ψ",
451 _ => "τ",
452 },
453 ))
454 } else {
455 None
456 }
457 }
458}
459
460pub struct TopologicalQC {
462 model: Box<dyn AnyonModel>,
464 fusion_trees: Vec<FusionTree>,
466 amplitudes: Array1<Complex64>,
468}
469
470impl TopologicalQC {
471 pub fn new(model: Box<dyn AnyonModel>, anyons: Vec<AnyonType>) -> QuantRS2Result<Self> {
473 let fusion_trees = Self::generate_fusion_trees(&*model, anyons)?;
475 let n = fusion_trees.len();
476
477 if n == 0 {
478 return Err(QuantRS2Error::InvalidInput(
479 "No valid fusion trees for given anyons".to_string(),
480 ));
481 }
482
483 let amplitudes = Array1::from_elem(n, Complex64::new(1.0 / (n as f64).sqrt(), 0.0));
485
486 Ok(Self {
487 model,
488 fusion_trees,
489 amplitudes,
490 })
491 }
492
493 fn generate_fusion_trees(
495 model: &dyn AnyonModel,
496 anyons: Vec<AnyonType>,
497 ) -> QuantRS2Result<Vec<FusionTree>> {
498 if anyons.len() < 2 {
499 return Ok(vec![FusionTree::new(anyons)]);
500 }
501
502 let mut trees = Vec::new();
503
504 if anyons.len() == 2 {
506 let a = anyons[0];
507 let b = anyons[1];
508
509 for c in model.anyon_types() {
511 if model.can_fuse(a, b, *c) {
512 let mut tree = FusionTree::new(anyons.clone());
513 tree.set_total_charge(*c);
514 trees.push(tree);
515 }
516 }
517 } else {
518 trees.push(FusionTree::new(anyons.clone()));
520 }
521
522 if trees.is_empty() {
523 trees.push(FusionTree::new(anyons));
525 }
526
527 Ok(trees)
528 }
529
530 pub fn braid(&mut self, op: &BraidingOperation) -> QuantRS2Result<()> {
532 let braid_matrix = self.compute_braiding_matrix(op)?;
534
535 self.amplitudes = braid_matrix.dot(&self.amplitudes);
537
538 Ok(())
539 }
540
541 fn compute_braiding_matrix(&self, op: &BraidingOperation) -> QuantRS2Result<Array2<Complex64>> {
543 let n = self.fusion_trees.len();
544 let mut matrix = Array2::zeros((n, n));
545
546 for (i, tree) in self.fusion_trees.iter().enumerate() {
548 if op.anyon1 < tree.external.len() && op.anyon2 < tree.external.len() {
549 let a = tree.external[op.anyon1];
550 let b = tree.external[op.anyon2];
551
552 let c = if let Some(charge) = tree.get_fusion_outcome() {
554 charge
555 } else if tree.internal.is_empty() {
556 tree.total_charge()
557 } else {
558 tree.internal[0]
559 };
560
561 let r_symbol = if op.over {
562 self.model.r_symbol(a, b, c)
563 } else {
564 self.model.r_symbol(a, b, c).conj()
565 };
566
567 matrix[(i, i)] = r_symbol;
568 } else {
569 matrix[(i, i)] = Complex64::new(1.0, 0.0);
571 }
572 }
573
574 Ok(matrix)
575 }
576
577 pub fn measure_charge(&self) -> (AnyonType, f64) {
579 let mut charge_probs: HashMap<u32, f64> = HashMap::new();
581
582 for (tree, &) in self.fusion_trees.iter().zip(&self.amplitudes) {
583 let charge = if let Some(c) = tree.get_fusion_outcome() {
584 c
585 } else {
586 tree.total_charge()
587 };
588 *charge_probs.entry(charge.id).or_insert(0.0) += amp.norm_sqr();
589 }
590
591 let (charge_id, prob) = charge_probs
592 .into_iter()
593 .max_by(|(_, p1), (_, p2)| p1.partial_cmp(p2).unwrap())
594 .unwrap_or((0, 0.0));
595
596 let charge = self
597 .model
598 .anyon_types()
599 .iter()
600 .find(|a| a.id == charge_id)
601 .copied()
602 .unwrap_or(AnyonType::VACUUM);
603
604 (charge, prob)
605 }
606}
607
608#[derive(Debug, Clone)]
610pub struct TopologicalGate {
611 pub braids: Vec<BraidingOperation>,
613 pub comp_dim: usize,
615}
616
617impl TopologicalGate {
618 pub fn new(braids: Vec<BraidingOperation>, comp_dim: usize) -> Self {
620 Self { braids, comp_dim }
621 }
622
623 pub fn cnot() -> Self {
625 let braids = vec![
627 BraidingOperation {
628 anyon1: 0,
629 anyon2: 1,
630 over: true,
631 },
632 BraidingOperation {
633 anyon1: 2,
634 anyon2: 3,
635 over: true,
636 },
637 BraidingOperation {
638 anyon1: 1,
639 anyon2: 2,
640 over: false,
641 },
642 ];
643
644 Self::new(braids, 4)
645 }
646
647 pub fn to_matrix(&self, model: &dyn AnyonModel) -> QuantRS2Result<Array2<Complex64>> {
649 Ok(Array2::eye(self.comp_dim))
652 }
653}
654
655pub struct ToricCode {
657 pub size: usize,
659 pub vertex_ops: Vec<Vec<usize>>,
661 pub plaquette_ops: Vec<Vec<usize>>,
663}
664
665impl ToricCode {
666 pub fn new(size: usize) -> Self {
668 let mut vertex_ops = Vec::new();
669 let mut plaquette_ops = Vec::new();
670
671 for i in 0..size {
674 for j in 0..size {
675 let v_op = vec![
677 2 * (i * size + j), 2 * (i * size + j) + 1, ];
680 vertex_ops.push(v_op);
681
682 let p_op = vec![
684 2 * (i * size + j),
685 2 * (i * size + (j + 1) % size),
686 2 * (((i + 1) % size) * size + j),
687 2 * (i * size + j) + 1,
688 ];
689 plaquette_ops.push(p_op);
690 }
691 }
692
693 Self {
694 size,
695 vertex_ops,
696 plaquette_ops,
697 }
698 }
699
700 pub fn num_qubits(&self) -> usize {
702 2 * self.size * self.size
703 }
704
705 pub fn num_logical_qubits(&self) -> usize {
707 2 }
709
710 pub fn create_anyons(&self, vertices: &[usize], plaquettes: &[usize]) -> Vec<AnyonType> {
712 let mut anyons = Vec::new();
713
714 for _ in vertices {
716 anyons.push(AnyonType::new(1, "e"));
717 }
718
719 for _ in plaquettes {
721 anyons.push(AnyonType::new(2, "m"));
722 }
723
724 anyons
725 }
726}
727
728#[cfg(test)]
729mod tests {
730 use super::*;
731
732 #[test]
733 fn test_fibonacci_model() {
734 let model = FibonacciModel::new();
735
736 assert_eq!(model.quantum_dimension(AnyonType::VACUUM), 1.0);
738 assert!((model.quantum_dimension(AnyonType::new(1, "τ")) - 1.618).abs() < 0.001);
739
740 assert_eq!(
742 model.fusion_multiplicity(
743 AnyonType::VACUUM,
744 AnyonType::new(1, "τ"),
745 AnyonType::new(1, "τ")
746 ),
747 1
748 );
749
750 let expected_dim = (1.0 + model.phi.powi(2)).sqrt();
753 assert!((model.total_quantum_dimension() - expected_dim).abs() < 0.001);
754 }
755
756 #[test]
757 fn test_ising_model() {
758 let model = IsingModel::new();
759
760 assert_eq!(model.quantum_dimension(AnyonType::VACUUM), 1.0);
762 assert!((model.quantum_dimension(AnyonType::new(1, "σ")) - 1.414).abs() < 0.001);
763 assert_eq!(model.quantum_dimension(AnyonType::new(2, "ψ")), 1.0);
764
765 assert_eq!(
767 model.fusion_multiplicity(
768 AnyonType::new(1, "σ"),
769 AnyonType::new(1, "σ"),
770 AnyonType::VACUUM
771 ),
772 1
773 );
774 assert_eq!(
775 model.fusion_multiplicity(
776 AnyonType::new(1, "σ"),
777 AnyonType::new(1, "σ"),
778 AnyonType::new(2, "ψ")
779 ),
780 1
781 );
782 }
783
784 #[test]
785 fn test_fusion_tree() {
786 let anyons = vec![
787 AnyonType::new(1, "τ"),
788 AnyonType::new(1, "τ"),
789 AnyonType::new(1, "τ"),
790 ];
791
792 let tree = FusionTree::new(anyons);
793 assert_eq!(tree.external.len(), 3);
794 assert_eq!(tree.internal.len(), 1);
795 }
796
797 #[test]
798 fn test_topological_qc() {
799 let model = Box::new(FibonacciModel::new());
800 let anyons = vec![AnyonType::new(1, "τ"), AnyonType::new(1, "τ")];
801
802 let qc = TopologicalQC::new(model, anyons).unwrap();
803 assert_eq!(qc.fusion_trees.len(), 2);
805
806 let (charge, _prob) = qc.measure_charge();
808 assert!(charge.id == 0 || charge.id == 1); }
810
811 #[test]
812 fn test_toric_code() {
813 let toric = ToricCode::new(4);
814
815 assert_eq!(toric.num_qubits(), 32); assert_eq!(toric.num_logical_qubits(), 2);
817
818 let anyons = toric.create_anyons(&[0, 1], &[2]);
820 assert_eq!(anyons.len(), 3);
821 }
822
823 #[test]
824 fn test_braiding_operation() {
825 let model = Box::new(IsingModel::new());
826 let anyons = vec![AnyonType::new(1, "σ"), AnyonType::new(1, "σ")];
827
828 let mut qc = TopologicalQC::new(model, anyons).unwrap();
829
830 let initial_norm: f64 = qc.amplitudes.iter().map(|a| a.norm_sqr()).sum();
832 assert!(
833 (initial_norm - 1.0).abs() < 1e-10,
834 "Initial state not normalized: {}",
835 initial_norm
836 );
837
838 let braid = BraidingOperation {
840 anyon1: 0,
841 anyon2: 1,
842 over: true,
843 };
844
845 qc.braid(&braid).unwrap();
846
847 let norm: f64 = qc.amplitudes.iter().map(|a| a.norm_sqr()).sum();
849 assert!(
850 (norm - 1.0).abs() < 1e-10,
851 "Final state not normalized: {}",
852 norm
853 );
854 }
855}