From 2498f20b8ffc54a490539514a6eeab7df3c38a7a Mon Sep 17 00:00:00 2001 From: rishi763 <55810666+rishi763@users.noreply.github.com> Date: Sat, 22 Jun 2024 22:20:29 +0000 Subject: [PATCH 1/4] Refractor to move Model into State --- python/tests/test_fusion.py | 37 +++++++++++++++++++++---------------- src/fusion/fusion.rs | 15 +++++++-------- src/fusion/state.rs | 12 +++++++++++- src/lib.rs | 1 - src/model/model.rs | 25 +------------------------ 5 files changed, 40 insertions(+), 50 deletions(-) diff --git a/python/tests/test_fusion.py b/python/tests/test_fusion.py index f6d6320..17d213d 100644 --- a/python/tests/test_fusion.py +++ b/python/tests/test_fusion.py @@ -32,13 +32,13 @@ def test_apply_ising_fusion(state): vacuum = [0, 1, 0] sigma = [0, 0, 1] - assert fusion.apply_fusion(psi, psi, AnyonModel.Ising) == vacuum - assert fusion.apply_fusion(vacuum, vacuum, AnyonModel.Ising) == vacuum - assert fusion.apply_fusion(sigma, sigma, AnyonModel.Ising) == [psi[i] + vacuum[i] for i in range(3)] + assert fusion.apply_fusion(psi, psi) == vacuum + assert fusion.apply_fusion(vacuum, vacuum) == vacuum + assert fusion.apply_fusion(sigma, sigma) == [psi[i] + vacuum[i] for i in range(3)] psi_sigma = [1, 0, 1] - assert fusion.apply_fusion(psi_sigma, psi_sigma, AnyonModel.Ising) == [1, 2, 2] - assert not fusion.apply_fusion(psi_sigma, psi_sigma, AnyonModel.Ising) == [2, 1, 2] # get owned rishi + assert fusion.apply_fusion(psi_sigma, psi_sigma) == [1, 2, 2] + assert not fusion.apply_fusion(psi_sigma, psi_sigma) == [2, 1, 2] # get owned rishi @pytest.mark.fusion @@ -52,30 +52,35 @@ def test_qubit_enc(state): fusion = Fusion(state) correct = [FusionPair(0, 1), FusionPair(2, 4), FusionPair(2, 3)] - assert set(map(str, fusion.qubit_enc(AnyonModel.Ising))) == set(map(str, correct)) + assert set(map(str, fusion.qubit_enc())) == set(map(str, correct)) @pytest.mark.fusion def test_verify_fusion_result(state): fusion = Fusion(state) - assert not fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Sigma), AnyonModel.Ising) - assert fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Vacuum), AnyonModel.Ising) - assert fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Psi), AnyonModel.Ising) + assert not fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Sigma)) + assert fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Vacuum)) + assert fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Psi)) state.add_anyon(Anyon('7', TopoCharge.from_ising(IsingTopoCharge.Sigma), (0, 0))) fusion = Fusion(state) - assert not fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Vacuum), AnyonModel.Ising) - assert not fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Psi), AnyonModel.Ising) - assert fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Sigma), AnyonModel.Ising) + assert not fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Vacuum)) + assert not fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Psi)) + assert fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Sigma)) @pytest.mark.fusion def test_minimum_possible_anyons(state): fusion = Fusion(state) - assert (fusion.minimum_possible_anyons(10, AnyonModel.Ising) == [21,22]) - assert (fusion.minimum_possible_anyons(5, AnyonModel.Ising) == [11,12]) + assert (fusion.minimum_possible_anyons(10) == [21,22]) + assert (fusion.minimum_possible_anyons(5) == [11,12]) + state.set_anyon_model(AnyonModel.Fibonacci) - assert(fusion.minimum_possible_anyons(10, AnyonModel.Fibonacci) == [17,18]); - assert(fusion.minimum_possible_anyons(0, AnyonModel.Fibonacci) == [0,1,2,3]); + fusion = Fusion(state) + + + + assert(fusion.minimum_possible_anyons(10) == [17,18]); + assert(fusion.minimum_possible_anyons(0) == [0,1,2,3]); diff --git a/src/fusion/fusion.rs b/src/fusion/fusion.rs index 9f94dc9..7b1a822 100644 --- a/src/fusion/fusion.rs +++ b/src/fusion/fusion.rs @@ -237,8 +237,8 @@ impl Fusion { Ok(basis.verify_basis(self.state.anyons().len())) } - fn qubit_enc(&self, anyon_model: &AnyonModel) -> PyResult> { - match anyon_model { + fn qubit_enc(&self) -> PyResult> { + match self.state.anyon_model() { AnyonModel::Ising => Ok(self.ising_qubit_enc()), _ => Err(PyValueError::new_err("This model is not supported yet")), } @@ -297,22 +297,21 @@ impl Fusion { &self, anyon_1: [u64; 3], anyon_2: [u64; 3], - anyon_model: &AnyonModel, ) -> PyResult<[u64; 3]> { - match anyon_model { + match self.state.anyon_model() { AnyonModel::Ising => Ok(self.ising_apply_fusion(anyon_1, anyon_2)), _ => Err(PyValueError::new_err("This model is not supported yet")), } } - fn verify_fusion_result(&self, init_charge: TopoCharge, anyon_model: &AnyonModel) -> bool { - match anyon_model { + fn verify_fusion_result(&self, init_charge: TopoCharge) -> bool { + match self.state.anyon_model() { AnyonModel::Ising => self.ising_verify_fusion_result(init_charge.get_ising()), _ => false, } } - fn minimum_possible_anyons(&self, qubits: u32, anyon_model: &AnyonModel) -> PyResult>{ - match anyon_model{ + fn minimum_possible_anyons(&self, qubits: u32) -> PyResult>{ + match self.state.anyon_model() { AnyonModel::Ising => Ok(self.ising_possible_sigmas(qubits)), AnyonModel::Fibonacci => Ok(self.fibonacci_possible_taus(qubits)), _ => Err(PyValueError::new_err("This model is not supported yet")) diff --git a/src/fusion/state.rs b/src/fusion/state.rs index 124ca3f..0edb191 100644 --- a/src/fusion/state.rs +++ b/src/fusion/state.rs @@ -1,4 +1,4 @@ -use crate::{fusion::fusion::FusionPair, model::anyon::Anyon}; +use crate::{fusion::fusion::FusionPair, model::anyon::Anyon, model::model::AnyonModel}; use pyo3::prelude::*; /// The state of the system @@ -12,6 +12,8 @@ pub struct State { anyons: Vec, #[pyo3(get)] operations: Vec<(u32, FusionPair)>, + #[pyo3(get)] + anyon_model: AnyonModel, } /// Internal Methods @@ -24,6 +26,10 @@ impl State { self.operations.clone() } + pub fn anyon_model(&self) -> AnyonModel{ + self.anyon_model.clone() + } + /// Verify the operation /// TODO: Provide better error for panic when no anyons loaded pub fn verify_operation(&self, time: u32, operation: &FusionPair) -> bool { @@ -59,6 +65,7 @@ impl State { State { anyons: Vec::new(), operations: Vec::new(), + anyon_model: AnyonModel::Ising, //Assume model is Ising by default } } @@ -78,4 +85,7 @@ impl State { Ok(true) } + fn set_anyon_model(&mut self, model:AnyonModel){ + self.anyon_model=model; + } } diff --git a/src/lib.rs b/src/lib.rs index ee3f8ef..b6852bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,6 @@ fn anyon_braiding_simulator(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; - m.add_class::()?; m.add_class::()?; m.add_class::()?; diff --git a/src/model/model.rs b/src/model/model.rs index 0d5f3ef..32c12e4 100644 --- a/src/model/model.rs +++ b/src/model/model.rs @@ -4,7 +4,7 @@ use pyo3::prelude::*; /// Different Anyon models that can be used to simulate the system #[pyclass] -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Clone, Debug)] pub enum AnyonModel { Ising, Fibonacci, @@ -18,26 +18,3 @@ impl AnyonModel { AnyonModel::Ising } } - -// Commenting out Model for now because it has no use atm We might port the -// python stuff to rust later, but for now we have no use - -/// The parameters accompanying a model -/// More docs later when we impl stuff from python -#[pyclass] -pub struct Model { - model_type: AnyonModel, - // more fields which we'll impl later -} - -#[pymethods] -impl Model { - #[new] - fn new() -> Self { - // Model { model_type } - Model { - model_type: AnyonModel::Ising, - } - } - -} From 81c35d85fbb67f43715ef06e995e6de8dee8ae79 Mon Sep 17 00:00:00 2001 From: Rishi Gujjar Date: Sat, 29 Jun 2024 15:33:25 -0400 Subject: [PATCH 2/4] Implements fusion methods for Fibonacci Model --- python/tests/test_fusion.py | 111 +++++++++++++++++++-------- src/fusion/fusion.rs | 147 ++++++++++++++++++++++++++---------- 2 files changed, 187 insertions(+), 71 deletions(-) diff --git a/python/tests/test_fusion.py b/python/tests/test_fusion.py index 17d213d..0eac421 100644 --- a/python/tests/test_fusion.py +++ b/python/tests/test_fusion.py @@ -1,32 +1,39 @@ import pytest -from anyon_braiding_simulator import Anyon, AnyonModel, Fusion, FusionPair, IsingTopoCharge, State, TopoCharge +from anyon_braiding_simulator import Anyon, AnyonModel, Fusion, FusionPair, IsingTopoCharge, FibonacciTopoCharge, State, TopoCharge @pytest.fixture -def state() -> State: +def ising_state() -> State: state = State() for i in range(6): state.add_anyon(Anyon(f'{i}', TopoCharge.from_ising(IsingTopoCharge.Sigma), (0, 0))) return state +@pytest.fixture +def fibo_state() -> State: + state = State() + state.set_anyon_model(AnyonModel.Fibonacci) + for i in range(6): + state.add_anyon(Anyon(f'{i}', TopoCharge.from_fibonacci(FibonacciTopoCharge.Tau), (0,0))) + return state @pytest.mark.fusion -def test_str_1(state): - state.add_operation(1, FusionPair(0, 1)) - state.add_operation(1, FusionPair(2, 3)) - state.add_operation(1, FusionPair(4, 5)) - state.add_operation(2, FusionPair(2, 4)) - state.add_operation(3, FusionPair(0, 2)) - - fusion = Fusion(state) +def test_str_1(ising_state): + ising_state.add_operation(1, FusionPair(0, 1)) + ising_state.add_operation(1, FusionPair(2, 3)) + ising_state.add_operation(1, FusionPair(4, 5)) + ising_state.add_operation(2, FusionPair(2, 4)) + ising_state.add_operation(3, FusionPair(0, 2)) + + fusion = Fusion(ising_state) expected = '0 1 2 3 4 5 \n| | | | | | \n|─| |─| |─| \n| |───| \n|───| \n| ' assert str(fusion) == expected @pytest.mark.fusion -def test_apply_ising_fusion(state): - fusion = Fusion(state) +def test_apply_ising_fusion(ising_state): + fusion = Fusion(ising_state) psi = [1, 0, 0] vacuum = [0, 1, 0] @@ -40,47 +47,87 @@ def test_apply_ising_fusion(state): assert fusion.apply_fusion(psi_sigma, psi_sigma) == [1, 2, 2] assert not fusion.apply_fusion(psi_sigma, psi_sigma) == [2, 1, 2] # get owned rishi - @pytest.mark.fusion -def test_qubit_enc(state): - state.add_operation(1, FusionPair(0, 1)) - state.add_operation(1, FusionPair(2, 3)) - state.add_operation(1, FusionPair(4, 5)) - state.add_operation(2, FusionPair(2, 4)) - state.add_operation(3, FusionPair(0, 2)) +def test_apply_fibo_fusion(fibo_state): + fusion = Fusion(fibo_state) - fusion = Fusion(state) + tau = [1,0] + vac = [0,1] + + assert fusion.apply_fusion(tau, tau) == [1,1] + assert fusion.apply_fusion(vac,vac) == vac + assert fusion.apply_fusion(tau, vac) == tau + + tau_vac = [1,1] + + assert fusion.apply_fusion(tau_vac, tau_vac) == [3,2] + +@pytest.mark.fusion +def test_ising_qubit_enc(ising_state): + ising_state.add_operation(1, FusionPair(0, 1)) + ising_state.add_operation(1, FusionPair(2, 3)) + ising_state.add_operation(1, FusionPair(4, 5)) + ising_state.add_operation(2, FusionPair(2, 4)) + ising_state.add_operation(3, FusionPair(0, 2)) + + fusion = Fusion(ising_state) correct = [FusionPair(0, 1), FusionPair(2, 4), FusionPair(2, 3)] assert set(map(str, fusion.qubit_enc())) == set(map(str, correct)) +@pytest.mark.fusion +def test_fibo_qubit_enc(fibo_state): + + fusion = Fusion(fibo_state) + + pass + + @pytest.mark.fusion -def test_verify_fusion_result(state): - fusion = Fusion(state) +def test_ising_verify_fusion_result(ising_state): + fusion = Fusion(ising_state) assert not fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Sigma)) assert fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Vacuum)) assert fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Psi)) - state.add_anyon(Anyon('7', TopoCharge.from_ising(IsingTopoCharge.Sigma), (0, 0))) - fusion = Fusion(state) + ising_state.add_anyon(Anyon('7', TopoCharge.from_ising(IsingTopoCharge.Sigma), (0, 0))) + fusion = Fusion(ising_state) assert not fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Vacuum)) assert not fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Psi)) assert fusion.verify_fusion_result(TopoCharge.from_ising(IsingTopoCharge.Sigma)) @pytest.mark.fusion -def test_minimum_possible_anyons(state): +def test_fibo_verify_fusion_result(fibo_state): + fusion = Fusion(fibo_state) + assert fusion.verify_fusion_result(TopoCharge.from_fibonacci(FibonacciTopoCharge.Tau)) + assert fusion.verify_fusion_result(TopoCharge.from_fibonacci(FibonacciTopoCharge.Vacuum)) + + state = State() + state.set_anyon_model(AnyonModel.Fibonacci) + for i in range(6): + state.add_anyon(Anyon(f'{i}', TopoCharge.from_fibonacci(FibonacciTopoCharge.Vacuum), (0,0))) fusion = Fusion(state) - assert (fusion.minimum_possible_anyons(10) == [21,22]) - assert (fusion.minimum_possible_anyons(5) == [11,12]) + assert not fusion.verify_fusion_result(TopoCharge.from_fibonacci(FibonacciTopoCharge.Tau)) + assert fusion.verify_fusion_result(TopoCharge.from_fibonacci(FibonacciTopoCharge.Vacuum)) - state.set_anyon_model(AnyonModel.Fibonacci) - - fusion = Fusion(state) - assert(fusion.minimum_possible_anyons(10) == [17,18]); - assert(fusion.minimum_possible_anyons(0) == [0,1,2,3]); +@pytest.mark.fusion +def test_ising_minimum_possible_anyons(ising_state): + + fusion = Fusion(ising_state) + + assert (fusion.minimum_possible_anyons(10) == [21,22]) + assert (fusion.minimum_possible_anyons(5) == [11,12]) + +@pytest.mark.fusion +def test_fibo_minimum_possible_anyons(ising_state): + ising_state.set_anyon_model(AnyonModel.Fibonacci) + fusion = Fusion(ising_state) + + assert(fusion.minimum_possible_anyons(10) == [17,18]) + assert(fusion.minimum_possible_anyons(0) == [0,1,2,3]) diff --git a/src/fusion/fusion.rs b/src/fusion/fusion.rs index 7b1a822..9780fc8 100644 --- a/src/fusion/fusion.rs +++ b/src/fusion/fusion.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use crate::fusion::state::State; +use crate::model::anyon::FibonacciTopoCharge; use crate::model::anyon::IsingTopoCharge; use crate::model::anyon::TopoCharge; use crate::model::model::AnyonModel; @@ -58,37 +59,48 @@ pub struct Fusion { impl Fusion { /// Converts from IsingTopoCharge to internal format /// Format is [psi, vacuum, sigma] (so we can use the index as the encode) - pub fn ising_canonical_topo_charge(&self, charge: IsingTopoCharge) -> [u64; 3] { + pub fn ising_canonical_topo_charge(&self, charge: IsingTopoCharge) -> Vec { match charge { - IsingTopoCharge::Psi => [1, 0, 0], - IsingTopoCharge::Vacuum => [0, 1, 0], - IsingTopoCharge::Sigma => [0, 0, 1], + IsingTopoCharge::Psi => vec![1, 0, 0], + IsingTopoCharge::Vacuum => vec![0, 1, 0], + IsingTopoCharge::Sigma => vec![0, 0, 1], } } - /// Creates a qubit encoding from the fusion tree. The encoding is a list of + /// Converts from FibonacciTopoCharge to internal format + /// Format is [tau, vacuum] (so we can use the index as the encode) + pub fn fibonacci_canonical_topo_charge(&self, charge: FibonacciTopoCharge) -> Vec { + match charge { + FibonacciTopoCharge::Tau => vec![1, 0], + FibonacciTopoCharge::Vacuum => vec![0, 1], + } + } + + /// Creates a qubit encoding for the Ising model from the fusion tree. The encoding is a list of /// FusionPairs that represent the anyons that are fused to create the qubit /// encoding. pub fn ising_qubit_enc(&self) -> Vec { - let mut tcs: Vec<[u64; 3]> = self + let mut tcs: Vec> = self .state .anyons() .iter() .map(|a| self.ising_canonical_topo_charge(a.charge().get_ising())) .collect(); - let mut fusion_pair_tc: HashMap = HashMap::new(); + let mut fusion_pair_tc: HashMap> = HashMap::new(); - let mut final_tc: [u64; 3] = [0, 0, 0]; + let mut final_tc: Vec = vec![0, 0, 0]; for (i, op) in self.ops.iter().enumerate() { for (j, fusion_pair) in op.iter().enumerate() { - let tc = - self.ising_apply_fusion(tcs[fusion_pair.anyon_1()], tcs[fusion_pair.anyon_2()]); + let tc = self.ising_apply_fusion( + tcs[fusion_pair.anyon_1()].clone(), + tcs[fusion_pair.anyon_2()].clone(), + ); if i == self.ops.len() - 1 && j == op.len() - 1 { final_tc = tc; break; } - fusion_pair_tc.insert(fusion_pair.clone(), tc); + fusion_pair_tc.insert(fusion_pair.clone(), tc.clone()); tcs[fusion_pair.anyon_1()] = tc; } } @@ -112,8 +124,17 @@ impl Fusion { encoding_fusions } + /// Creates a qubit encoding for the Fibonacci model from the fusion tree. The encoding is a list of + /// FusionPairs that represent the anyons that are fused to create the qubit + /// encoding. + pub fn fibonacci_qubit_enc(&self) -> Vec { + unimplemented!() + } + /// Applies the fusion rules to two anyons and returns the resulting anyon(s). - pub fn ising_apply_fusion(&self, anyon_1: [u64; 3], anyon_2: [u64; 3]) -> [u64; 3] { + pub fn ising_apply_fusion(&self, anyon_1: Vec, anyon_2: Vec) -> Vec { + assert!(anyon_1.len() == 3 && anyon_2.len() == 3); + let add = |a: [u64; 3], b: [u64; 3]| -> [u64; 3] { std::array::from_fn(|i| a[i] + b[i]) }; let arr_scale = |a: [u64; 3], b: u64| -> [u64; 3] { std::array::from_fn(|i| a[i] * b) }; @@ -142,15 +163,45 @@ impl Fusion { } } - output + Vec::from(output) + } + /// Applies the fusion rules to two anyones and returns the resulting anyon(s). + fn fibonacci_apply_fusion(&self, anyon_1: Vec, anyon_2: Vec) -> Vec { + assert!(anyon_1.len() == 2 && anyon_2.len() == 2); + + let add = |a: [u64; 2], b: [u64; 2]| -> [u64; 2] { std::array::from_fn(|i| a[i] + b[i]) }; + let arr_scale = |a: [u64; 2], b: u64| -> [u64; 2] { std::array::from_fn(|i| a[i] * b) }; + + let mut output = [0 as u64; 2]; + + // ising fusion rules + // symmetric matrix which is built from fusion rules of (tau, 1) ^ (tau, 1) + let fusion_rules_mtx: [[[u64; 2]; 2]; 2] = [[[1, 1], [1, 0]], [[1, 0], [0, 1]]]; + + // build the outer product of the two tc vectors + let mut tc_mtx = [[0; 2]; 2]; + for i in 0..2 { + for j in 0..2 { + tc_mtx[i][j] = anyon_1[i] * anyon_2[j]; + } + } + + // mtx multiply fusion rules with tc_mtx + for i in 0..2 { + for j in 0..2 { + output = add(output, arr_scale(fusion_rules_mtx[i][j], tc_mtx[i][j])); + } + } + + Vec::from(output) } /// Checks if an overall fusion result is possible given the state's - /// configuration and an initial topo charge + /// configuration and an initial topo charge under the Ising model /// /// Precondition: Non empty list of anyons pub fn ising_verify_fusion_result(&self, init_charge: IsingTopoCharge) -> bool { - let overall_fusion_result: [u64; 3] = self + let overall_fusion_result: Vec = self .state .anyons() .iter() @@ -166,43 +217,61 @@ impl Fusion { .all(|(a, b)| *b <= 0 || *a > 0) } + /// Checks if an overall fusion result is possible given the state's + /// configuration and an initial topo charge under the Fibonacci model + /// + /// Precondition: Non empty list of anyons + pub fn fibonacci_verify_fusion_result(&self, init_charge: FibonacciTopoCharge) -> bool { + let overall_fusion_result: Vec = self + .state + .anyons() + .iter() + .map(|a| self.fibonacci_canonical_topo_charge(a.charge().get_fibonacci())) + .reduce(|acc, tc| self.fibonacci_apply_fusion(acc, tc)) + .unwrap(); + + // if an element > 0 that means it was our initial charge, so we need to + // check if our final fusion result also has that element > 0 + overall_fusion_result + .iter() + .zip(self.fibonacci_canonical_topo_charge(init_charge).iter()) + .all(|(a, b)| *b <= 0 || *a > 0) + } + /// /// Returns number of sigmas that can be in the initial topological charges of anyons to exactly a certain number of qubits for the Ising model - /// - pub fn ising_possible_sigmas(&self, qubits:u32) -> Vec{ - vec![2*qubits+1, 2*qubits+2] + /// + pub fn ising_possible_sigmas(&self, qubits: u32) -> Vec { + vec![2 * qubits + 1, 2 * qubits + 2] } /// /// Returns number of taus that can be in the initial topological charges of anyons to exactly a certain number of qubits for the Fibonacci model - /// + /// /// Precondition: Requires qubits <=30 - pub fn fibonacci_possible_taus(&self, qubits:u32) -> Vec { - - if qubits ==0{ - return vec![0,1,2,3]; + pub fn fibonacci_possible_taus(&self, qubits: u32) -> Vec { + if qubits == 0 { + return vec![0, 1, 2, 3]; } let mut possible_taus = Vec::new(); let mut n = 1; - // We have that the fibonacci recurrence gives us that tau^n = a + b * tau let mut a = 0; let mut b = 1; - + // Checks that b < 2^(qubits+1) meaning as otherwise fusing to tau would result in too many qubits - while b < 1 << (qubits+1) { - + while b < 1 << (qubits + 1) { // If b >= 2^qubits we have that this will give exactly 'qubits' qubits - if (1 << (qubits)) <= b{ + if (1 << (qubits)) <= b { possible_taus.push(n); } - b=a+b; - a=b-a; + b = a + b; + a = b - a; - n+=1; + n += 1; } // Accounts for the case that we can also fuse to vaccuum possible_taus.push(n); @@ -240,6 +309,7 @@ impl Fusion { fn qubit_enc(&self) -> PyResult> { match self.state.anyon_model() { AnyonModel::Ising => Ok(self.ising_qubit_enc()), + AnyonModel::Fibonacci => Ok(self.fibonacci_qubit_enc()), _ => Err(PyValueError::new_err("This model is not supported yet")), } } @@ -293,13 +363,10 @@ impl Fusion { Ok(format!("{}\n{}\n{}{}", top_level, level_2, body, last_time).to_string()) } - fn apply_fusion( - &self, - anyon_1: [u64; 3], - anyon_2: [u64; 3], - ) -> PyResult<[u64; 3]> { + fn apply_fusion(&self, anyon_1: Vec, anyon_2: Vec) -> PyResult> { match self.state.anyon_model() { AnyonModel::Ising => Ok(self.ising_apply_fusion(anyon_1, anyon_2)), + AnyonModel::Fibonacci => Ok(self.fibonacci_apply_fusion(anyon_1, anyon_2)), _ => Err(PyValueError::new_err("This model is not supported yet")), } } @@ -307,15 +374,17 @@ impl Fusion { fn verify_fusion_result(&self, init_charge: TopoCharge) -> bool { match self.state.anyon_model() { AnyonModel::Ising => self.ising_verify_fusion_result(init_charge.get_ising()), + AnyonModel::Fibonacci => { + self.fibonacci_verify_fusion_result(init_charge.get_fibonacci()) + } _ => false, } } - fn minimum_possible_anyons(&self, qubits: u32) -> PyResult>{ + fn minimum_possible_anyons(&self, qubits: u32) -> PyResult> { match self.state.anyon_model() { AnyonModel::Ising => Ok(self.ising_possible_sigmas(qubits)), AnyonModel::Fibonacci => Ok(self.fibonacci_possible_taus(qubits)), - _ => Err(PyValueError::new_err("This model is not supported yet")) + _ => Err(PyValueError::new_err("This model is not supported yet")), } - } } From 28766176f2ae50673c55dd518795c5baa6a5ed33 Mon Sep 17 00:00:00 2001 From: Rishi Gujjar Date: Sat, 29 Jun 2024 18:11:27 -0400 Subject: [PATCH 3/4] Closes #65 --- python/tests/test_fusion.py | 6 ++++++ src/fusion/fusion.rs | 37 +++++++++++++++++++++++++++++++++++-- src/model/anyon.rs | 13 +++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/python/tests/test_fusion.py b/python/tests/test_fusion.py index 0eac421..ef34c7d 100644 --- a/python/tests/test_fusion.py +++ b/python/tests/test_fusion.py @@ -77,9 +77,15 @@ def test_ising_qubit_enc(ising_state): @pytest.mark.fusion def test_fibo_qubit_enc(fibo_state): + fibo_state.add_operation(1, FusionPair(0, 1)) + fibo_state.add_operation(1, FusionPair(2, 3)) + fibo_state.add_operation(1, FusionPair(4, 5)) + fibo_state.add_operation(2, FusionPair(2, 4)) + fibo_state.add_operation(3, FusionPair(0, 2)) fusion = Fusion(fibo_state) + print(fusion.qubit_enc()) pass diff --git a/src/fusion/fusion.rs b/src/fusion/fusion.rs index 9780fc8..e6a1276 100644 --- a/src/fusion/fusion.rs +++ b/src/fusion/fusion.rs @@ -128,7 +128,40 @@ impl Fusion { /// FusionPairs that represent the anyons that are fused to create the qubit /// encoding. pub fn fibonacci_qubit_enc(&self) -> Vec { - unimplemented!() + let mut tcs: Vec> = self + .state + .anyons() + .iter() + .map(|a| self.fibonacci_canonical_topo_charge(a.charge().get_fibonacci())) + .collect(); + let mut fusion_pair_tc: HashMap> = HashMap::new(); + + + for (i, op) in self.ops.iter().enumerate() { + for (j, fusion_pair) in op.iter().enumerate() { + let tc = self.fibonacci_apply_fusion( + tcs[fusion_pair.anyon_1()].clone(), + tcs[fusion_pair.anyon_2()].clone(), + ); + if i == self.ops.len() - 1 && j == op.len() - 1 { + break; + } + fusion_pair_tc.insert(fusion_pair.clone(), tc.clone()); + tcs[fusion_pair.anyon_1()] = tc; + } + } + + if self.state.anyons().len() == 3{ + return Vec::new(); + } + let mut encoding_fusions: Vec = fusion_pair_tc + .into_iter() + .filter(|(_, tc)| tc[FibonacciTopoCharge::Tau.value()] >0 && tc[FibonacciTopoCharge::Vacuum.value()]>0) + .map(|(fusion_pair, _)| fusion_pair) + .collect(); + encoding_fusions.sort(); + encoding_fusions.pop().unwrap(); + encoding_fusions } /// Applies the fusion rules to two anyons and returns the resulting anyon(s). @@ -166,7 +199,7 @@ impl Fusion { Vec::from(output) } /// Applies the fusion rules to two anyones and returns the resulting anyon(s). - fn fibonacci_apply_fusion(&self, anyon_1: Vec, anyon_2: Vec) -> Vec { + pub fn fibonacci_apply_fusion(&self, anyon_1: Vec, anyon_2: Vec) -> Vec { assert!(anyon_1.len() == 2 && anyon_2.len() == 2); let add = |a: [u64; 2], b: [u64; 2]| -> [u64; 2] { std::array::from_fn(|i| a[i] + b[i]) }; diff --git a/src/model/anyon.rs b/src/model/anyon.rs index 28c02c2..3113be4 100644 --- a/src/model/anyon.rs +++ b/src/model/anyon.rs @@ -107,6 +107,19 @@ impl IsingTopoCharge { } } +impl FibonacciTopoCharge { + pub fn value(&self) -> usize { + *self as usize + } + + pub fn to_string(&self) -> &str { + match self { + FibonacciTopoCharge::Tau => "Tau", + FibonacciTopoCharge::Vacuum => "Vacuum", + } + } +} + #[pyclass] #[derive(Clone, Debug, PartialEq)] /// In Topological Quantum Computing, anyons are the fundamental quasiparticles From c1034dcbb3d8e96fc214163bb4070f40037b4a05 Mon Sep 17 00:00:00 2001 From: Rishi Gujjar Date: Sat, 29 Jun 2024 18:53:12 -0400 Subject: [PATCH 4/4] Fix merge conflicts --- python/anyon_braiding_simulator/Braiding.py | 6 +++--- python/tests/test_braiding.py | 4 ++-- python/tests/test_fusion.py | 1 + src/model/model.rs | 20 ++++++++++++++++++++ src/util/statevec.rs | 2 +- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/python/anyon_braiding_simulator/Braiding.py b/python/anyon_braiding_simulator/Braiding.py index 8f49e3f..197029b 100644 --- a/python/anyon_braiding_simulator/Braiding.py +++ b/python/anyon_braiding_simulator/Braiding.py @@ -74,7 +74,7 @@ def swap_to_qubit(self, time: int, swap_index: int) -> int: index_A, index_B = swap[swap_index] # Iterate through the qubit encoding to find the matching qubit - for qubit_index, fusion_pair in enumerate(self.fusion.qubit_enc(self.model.model_type)): + for qubit_index, fusion_pair in enumerate(self.fusion.qubit_enc()): if {index_A, index_B} == {fusion_pair.anyon_1, fusion_pair.anyon_2}: return qubit_index @@ -112,11 +112,11 @@ def generate_swap_matrix(self, time: int, swap_index: int) -> np.ndarray: return swap_matrix def generate_overall_unitary(self, time: int, swap_index: int) -> np.ndarray: - qubit_encoding = self.fusion.qubit_enc(self.model.model_type) + qubit_encoding = self.fusion.qubit_enc() if qubit_encoding is None: raise ValueError("Fusion qubit encoding returned None") - num_qubits = len(self.fusion.qubit_enc(self.model.model_type)) + num_qubits = len(self.fusion.qubit_enc()) unitary = np.eye(2**num_qubits) # Start with identity matrix of appropriate size for i in range(num_qubits): diff --git a/python/tests/test_braiding.py b/python/tests/test_braiding.py index e4efefd..22597bd 100644 --- a/python/tests/test_braiding.py +++ b/python/tests/test_braiding.py @@ -7,7 +7,7 @@ from Braiding import Braid from Model import Model -from anyon_braiding_simulator import Anyon, AnyonModel, IsingTopoCharge, TopoCharge, State, FusionPair +from anyon_braiding_simulator import Anyon, AnyonModel, IsingTopoCharge, FibonacciTopoCharge, TopoCharge, State, FusionPair @pytest.fixture @@ -159,7 +159,7 @@ def test_qubit_enc(setup_state): correct = [FusionPair(0, 1), FusionPair(2, 4), FusionPair(2, 3)] # Confirm qubit_enc is working as expected - assert set(map(str, braid.fusion.qubit_enc(braid.model.model_type))) == set(map(str, correct)) + assert set(map(str, braid.fusion.qubit_enc())) == set(map(str, correct)) def test_swap_to_qubit(setup_state): braid = setup_state diff --git a/python/tests/test_fusion.py b/python/tests/test_fusion.py index bf60c0b..1cd0da9 100644 --- a/python/tests/test_fusion.py +++ b/python/tests/test_fusion.py @@ -5,6 +5,7 @@ Fusion, FusionPair, IsingTopoCharge, + FibonacciTopoCharge, State, TopoCharge, ) diff --git a/src/model/model.rs b/src/model/model.rs index d583279..07bccbc 100644 --- a/src/model/model.rs +++ b/src/model/model.rs @@ -18,4 +18,24 @@ impl AnyonModel { AnyonModel::Ising } } +// Commenting out Model for now because it has no use atm We might port the +// python stuff to rust later, but for now we have no use +/// The parameters accompanying a model +/// More docs later when we impl stuff from python +#[pyclass] +pub struct Model { + model_type: AnyonModel, + // more fields which we'll impl later +} + +#[pymethods] +impl Model { + #[new] + fn new() -> Self { + // Model { model_type } + Model { + model_type: AnyonModel::Ising, + } + } +} diff --git a/src/util/statevec.rs b/src/util/statevec.rs index e2a195a..065a532 100644 --- a/src/util/statevec.rs +++ b/src/util/statevec.rs @@ -3,7 +3,7 @@ use numpy::{Complex64, PyArray1, PyReadonlyArray1, ToPyArray}; use pyo3::prelude::*; #[pyclass] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] /// State Vector for the system pub struct StateVec { vec: Array1,