1use super::{
7 Anyon, FusionRuleSet, NonAbelianAnyonType, TopologicalCharge, TopologicalError,
8 TopologicalResult,
9};
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct FusionTreeNode {
16 pub input_charges: Vec<TopologicalCharge>,
18 pub output_charge: TopologicalCharge,
20 pub channel: String,
22 pub children: Vec<Self>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct FusionTree {
29 pub root: FusionTreeNode,
31 pub anyon_count: usize,
33 pub final_charge: TopologicalCharge,
35}
36
37impl FusionTree {
38 pub fn new(
40 input_charges: Vec<TopologicalCharge>,
41 fusion_rules: &FusionRuleSet,
42 ) -> TopologicalResult<Self> {
43 if input_charges.is_empty() {
44 return Err(TopologicalError::FusionFailed(
45 "Cannot create fusion tree with no input charges".to_string(),
46 ));
47 }
48
49 let anyon_count = input_charges.len();
50 let root = Self::build_fusion_tree_recursive(input_charges, fusion_rules)?;
51 let final_charge = root.output_charge.clone();
52
53 Ok(Self {
54 root,
55 anyon_count,
56 final_charge,
57 })
58 }
59
60 fn build_fusion_tree_recursive(
62 charges: Vec<TopologicalCharge>,
63 fusion_rules: &FusionRuleSet,
64 ) -> TopologicalResult<FusionTreeNode> {
65 if charges.len() == 1 {
66 return Ok(FusionTreeNode {
68 input_charges: charges.clone(),
69 output_charge: charges[0].clone(),
70 channel: "identity".to_string(),
71 children: Vec::new(),
72 });
73 }
74
75 if charges.len() == 2 {
76 let fusion_key = (charges[0].label.clone(), charges[1].label.clone());
78 let fusion_products = fusion_rules.rules.get(&fusion_key).ok_or_else(|| {
79 TopologicalError::FusionFailed(format!("No fusion rule found for {fusion_key:?}"))
80 })?;
81
82 let output_charge = TopologicalCharge {
84 label: fusion_products[0].clone(),
85 quantum_dimension: "1".to_string(), scaling_dimension: 0.0,
87 };
88
89 return Ok(FusionTreeNode {
90 input_charges: charges,
91 output_charge,
92 channel: fusion_products[0].clone(),
93 children: Vec::new(),
94 });
95 }
96
97 let first_two = vec![charges[0].clone(), charges[1].clone()];
100 let remaining = charges[2..].to_vec();
101
102 let left_child = Self::build_fusion_tree_recursive(first_two, fusion_rules)?;
103 let intermediate_charge = left_child.output_charge.clone();
104
105 let mut new_charges = vec![intermediate_charge];
106 new_charges.extend(remaining);
107
108 let right_child = Self::build_fusion_tree_recursive(new_charges, fusion_rules)?;
109 let final_charge = right_child.output_charge.clone();
110
111 Ok(FusionTreeNode {
112 input_charges: charges,
113 output_charge: final_charge,
114 channel: "composite".to_string(),
115 children: vec![left_child, right_child],
116 })
117 }
118
119 pub fn fusion_multiplicity(&self) -> usize {
121 self.calculate_multiplicity_recursive(&self.root)
122 }
123
124 fn calculate_multiplicity_recursive(&self, node: &FusionTreeNode) -> usize {
126 if node.children.is_empty() {
127 return 1;
128 }
129
130 node.children
131 .iter()
132 .map(|child| self.calculate_multiplicity_recursive(child))
133 .product()
134 }
135}
136
137pub struct FSymbolCalculator {
139 anyon_type: NonAbelianAnyonType,
140 fusion_rules: FusionRuleSet,
141}
142
143impl FSymbolCalculator {
144 pub const fn new(anyon_type: NonAbelianAnyonType, fusion_rules: FusionRuleSet) -> Self {
146 Self {
147 anyon_type,
148 fusion_rules,
149 }
150 }
151
152 pub fn calculate_f_symbol(
155 &self,
156 a: &TopologicalCharge,
157 b: &TopologicalCharge,
158 c: &TopologicalCharge,
159 d: &TopologicalCharge,
160 e: &TopologicalCharge,
161 f: &TopologicalCharge,
162 ) -> TopologicalResult<f64> {
163 match self.anyon_type {
164 NonAbelianAnyonType::Fibonacci => self.fibonacci_f_symbol(a, b, c, d, e, f),
165 NonAbelianAnyonType::Ising => self.ising_f_symbol(a, b, c, d, e, f),
166 _ => {
167 Ok(1.0)
169 }
170 }
171 }
172
173 fn fibonacci_f_symbol(
175 &self,
176 a: &TopologicalCharge,
177 b: &TopologicalCharge,
178 c: &TopologicalCharge,
179 d: &TopologicalCharge,
180 e: &TopologicalCharge,
181 f: &TopologicalCharge,
182 ) -> TopologicalResult<f64> {
183 let phi = f64::midpoint(1.0, 5.0_f64.sqrt()); let phi_inv = 1.0 / phi;
185
186 match (
188 a.label.as_str(),
189 b.label.as_str(),
190 c.label.as_str(),
191 d.label.as_str(),
192 e.label.as_str(),
193 f.label.as_str(),
194 ) {
195 ("τ", "τ", "τ", "τ", "τ", "τ") => Ok(phi_inv),
196 ("τ", "τ", "τ", "I", "τ", "τ") => Ok(-phi_inv),
197 ("τ", "τ", "τ", "τ", "I", "I") => Ok(phi_inv.sqrt()),
198 _ => Ok(1.0), }
201 }
202
203 fn ising_f_symbol(
205 &self,
206 a: &TopologicalCharge,
207 b: &TopologicalCharge,
208 c: &TopologicalCharge,
209 d: &TopologicalCharge,
210 e: &TopologicalCharge,
211 f: &TopologicalCharge,
212 ) -> TopologicalResult<f64> {
213 match (
215 a.label.as_str(),
216 b.label.as_str(),
217 c.label.as_str(),
218 d.label.as_str(),
219 e.label.as_str(),
220 f.label.as_str(),
221 ) {
222 ("σ", "σ", "σ", "σ", "I", "I") | ("σ", "σ", "σ", "σ", "ψ", "ψ") => {
223 Ok(1.0 / 2.0_f64.sqrt())
224 }
225 ("σ", "σ", "ψ", "I", "σ", "σ") => Ok(-1.0),
226 _ => Ok(1.0), }
229 }
230}
231
232pub struct FusionSpaceCalculator {
234 anyon_type: NonAbelianAnyonType,
235 fusion_rules: FusionRuleSet,
236}
237
238impl FusionSpaceCalculator {
239 pub const fn new(anyon_type: NonAbelianAnyonType, fusion_rules: FusionRuleSet) -> Self {
241 Self {
242 anyon_type,
243 fusion_rules,
244 }
245 }
246
247 pub fn fusion_space_dimension(
249 &self,
250 input_charges: &[TopologicalCharge],
251 output_charge: &TopologicalCharge,
252 ) -> TopologicalResult<usize> {
253 if input_charges.is_empty() {
254 return Ok(0);
255 }
256
257 if input_charges.len() == 1 {
258 return Ok(usize::from(input_charges[0].label == output_charge.label));
260 }
261
262 let trees = self.enumerate_fusion_trees(input_charges, output_charge)?;
264 Ok(trees.len())
265 }
266
267 fn enumerate_fusion_trees(
269 &self,
270 input_charges: &[TopologicalCharge],
271 output_charge: &TopologicalCharge,
272 ) -> TopologicalResult<Vec<FusionTree>> {
273 let mut trees = Vec::new();
274
275 if input_charges.len() == 2 {
277 let fusion_key = (
278 input_charges[0].label.clone(),
279 input_charges[1].label.clone(),
280 );
281 if let Some(fusion_products) = self.fusion_rules.rules.get(&fusion_key) {
282 for product in fusion_products {
283 if product == &output_charge.label {
284 let tree_result =
286 self.create_specific_fusion_tree(input_charges, output_charge);
287 if let Ok(tree) = tree_result {
288 trees.push(tree);
289 }
290 }
291 }
292 }
293 } else {
294 if let Ok(tree) = FusionTree::new(input_charges.to_vec(), &self.fusion_rules) {
296 if tree.final_charge.label == output_charge.label {
297 trees.push(tree);
298 }
299 }
300 }
301
302 Ok(trees)
303 }
304
305 fn create_specific_fusion_tree(
307 &self,
308 input_charges: &[TopologicalCharge],
309 output_charge: &TopologicalCharge,
310 ) -> TopologicalResult<FusionTree> {
311 if input_charges.len() != 2 {
312 return Err(TopologicalError::FusionFailed(
313 "create_specific_fusion_tree only supports two input charges".to_string(),
314 ));
315 }
316
317 let root = FusionTreeNode {
318 input_charges: input_charges.to_vec(),
319 output_charge: output_charge.clone(),
320 channel: output_charge.label.clone(),
321 children: Vec::new(),
322 };
323
324 Ok(FusionTree {
325 root,
326 anyon_count: 2,
327 final_charge: output_charge.clone(),
328 })
329 }
330
331 pub fn fusion_coefficient(
333 &self,
334 charge1: &TopologicalCharge,
335 charge2: &TopologicalCharge,
336 product: &TopologicalCharge,
337 ) -> usize {
338 let fusion_key = (charge1.label.clone(), charge2.label.clone());
339 self.fusion_rules
340 .rules
341 .get(&fusion_key)
342 .map_or(0, |products| {
343 products.iter().filter(|&p| p == &product.label).count()
344 })
345 }
346}
347
348pub struct FusionOperationExecutor {
350 anyon_type: NonAbelianAnyonType,
351 fusion_rules: FusionRuleSet,
352 f_calculator: FSymbolCalculator,
353 operation_history: Vec<FusionOperation>,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct FusionOperation {
359 pub operation_id: usize,
361 pub input_anyons: Vec<usize>,
363 pub output_anyon: usize,
365 pub fusion_channel: String,
367 pub timestamp: f64,
369 pub f_symbol: f64,
371}
372
373impl FusionOperationExecutor {
374 pub fn new(anyon_type: NonAbelianAnyonType, fusion_rules: FusionRuleSet) -> Self {
376 let f_calculator = FSymbolCalculator::new(anyon_type.clone(), fusion_rules.clone());
377
378 Self {
379 anyon_type,
380 fusion_rules,
381 f_calculator,
382 operation_history: Vec::new(),
383 }
384 }
385
386 pub fn execute_binary_fusion(
388 &mut self,
389 anyon1: &Anyon,
390 anyon2: &Anyon,
391 preferred_channel: Option<&str>,
392 ) -> TopologicalResult<Anyon> {
393 let fusion_key = (anyon1.charge.label.clone(), anyon2.charge.label.clone());
395 let fusion_products = self.fusion_rules.rules.get(&fusion_key).ok_or_else(|| {
396 TopologicalError::FusionFailed(format!("No fusion rule found for {fusion_key:?}"))
397 })?;
398
399 let selected_channel = if let Some(channel) = preferred_channel {
401 if fusion_products.contains(&channel.to_string()) {
402 channel.to_string()
403 } else {
404 return Err(TopologicalError::FusionFailed(format!(
405 "Requested fusion channel {channel} not available"
406 )));
407 }
408 } else {
409 fusion_products[0].clone()
411 };
412
413 let output_charge = TopologicalCharge {
415 label: selected_channel.clone(),
416 quantum_dimension: "1".to_string(), scaling_dimension: 0.0, };
419
420 let output_anyon = Anyon {
421 anyon_id: anyon1.anyon_id.max(anyon2.anyon_id) + 1, charge: output_charge,
423 position: (
424 f64::midpoint(anyon1.position.0, anyon2.position.0),
425 f64::midpoint(anyon1.position.1, anyon2.position.1),
426 ),
427 is_qubit_part: false,
428 qubit_id: None,
429 creation_time: anyon1.creation_time.max(anyon2.creation_time),
430 };
431
432 let f_symbol = 1.0; let operation = FusionOperation {
437 operation_id: self.operation_history.len(),
438 input_anyons: vec![anyon1.anyon_id, anyon2.anyon_id],
439 output_anyon: output_anyon.anyon_id,
440 fusion_channel: selected_channel,
441 timestamp: 0.0, f_symbol,
443 };
444
445 self.operation_history.push(operation);
446 Ok(output_anyon)
447 }
448
449 pub fn execute_multi_fusion(
451 &mut self,
452 anyons: &[Anyon],
453 fusion_tree: &FusionTree,
454 ) -> TopologicalResult<Anyon> {
455 if anyons.len() != fusion_tree.anyon_count {
456 return Err(TopologicalError::FusionFailed(
457 "Number of anyons doesn't match fusion tree".to_string(),
458 ));
459 }
460
461 self.execute_fusion_node(&fusion_tree.root, anyons)
463 }
464
465 fn execute_fusion_node(
467 &mut self,
468 node: &FusionTreeNode,
469 anyons: &[Anyon],
470 ) -> TopologicalResult<Anyon> {
471 if node.children.is_empty() {
472 if anyons.len() == 1 {
474 Ok(anyons[0].clone())
475 } else {
476 Err(TopologicalError::FusionFailed(
477 "Leaf node with multiple anyons".to_string(),
478 ))
479 }
480 } else if node.children.len() == 2 {
481 let left_result = self.execute_fusion_node(&node.children[0], &anyons[0..1])?;
483 let right_result = self.execute_fusion_node(&node.children[1], &anyons[1..])?;
484
485 self.execute_binary_fusion(&left_result, &right_result, Some(&node.channel))
486 } else {
487 Err(TopologicalError::FusionFailed(
488 "Invalid fusion tree structure".to_string(),
489 ))
490 }
491 }
492
493 pub fn get_operation_history(&self) -> &[FusionOperation] {
495 &self.operation_history
496 }
497
498 pub fn calculate_fusion_probability(&self, operations: &[usize]) -> f64 {
500 operations
501 .iter()
502 .filter_map(|&op_id| self.operation_history.get(op_id))
503 .map(|op| op.f_symbol.abs().powi(2))
504 .product()
505 }
506}
507
508#[cfg(test)]
509mod tests {
510 use super::*;
511
512 #[test]
513 fn test_fusion_tree_creation() {
514 let charges = vec![
515 TopologicalCharge::fibonacci_tau(),
516 TopologicalCharge::fibonacci_tau(),
517 ];
518 let fusion_rules = FusionRuleSet::fibonacci();
519
520 let tree = FusionTree::new(charges, &fusion_rules)
521 .expect("FusionTree creation should succeed with valid charges");
522 assert_eq!(tree.anyon_count, 2);
523 }
524
525 #[test]
526 fn test_f_symbol_calculator() {
527 let fusion_rules = FusionRuleSet::fibonacci();
528 let calculator = FSymbolCalculator::new(NonAbelianAnyonType::Fibonacci, fusion_rules);
529
530 let tau = TopologicalCharge::fibonacci_tau();
531 let identity = TopologicalCharge::identity();
532
533 let f_symbol = calculator
534 .calculate_f_symbol(&tau, &tau, &tau, &tau, &tau, &tau)
535 .expect("F-symbol calculation should succeed for Fibonacci anyons");
536
537 assert!(f_symbol > 0.0);
538 }
539
540 #[test]
541 fn test_fusion_space_dimension() {
542 let fusion_rules = FusionRuleSet::fibonacci();
543 let calculator = FusionSpaceCalculator::new(NonAbelianAnyonType::Fibonacci, fusion_rules);
544
545 let tau = TopologicalCharge::fibonacci_tau();
546 let charges = vec![tau.clone(), tau.clone()];
547
548 let dimension = calculator
549 .fusion_space_dimension(&charges, &tau)
550 .expect("Fusion space dimension calculation should succeed");
551 assert!(dimension > 0);
552 }
553
554 #[test]
555 fn test_fusion_operation_executor() {
556 let fusion_rules = FusionRuleSet::fibonacci();
557 let mut executor =
558 FusionOperationExecutor::new(NonAbelianAnyonType::Fibonacci, fusion_rules);
559
560 let anyon1 = Anyon {
561 anyon_id: 0,
562 charge: TopologicalCharge::fibonacci_tau(),
563 position: (0.0, 0.0),
564 is_qubit_part: false,
565 qubit_id: None,
566 creation_time: 0.0,
567 };
568
569 let anyon2 = Anyon {
570 anyon_id: 1,
571 charge: TopologicalCharge::fibonacci_tau(),
572 position: (1.0, 0.0),
573 is_qubit_part: false,
574 qubit_id: None,
575 creation_time: 0.0,
576 };
577
578 let result = executor.execute_binary_fusion(&anyon1, &anyon2, None);
579 assert!(result.is_ok());
580 assert_eq!(executor.get_operation_history().len(), 1);
581 }
582}