Skip to content

Commit

Permalink
Merge branch 'main' into braiding
Browse files Browse the repository at this point in the history
  • Loading branch information
juli6nne authored Jun 22, 2024
2 parents 1748a01 + cd2452d commit 65a5269
Show file tree
Hide file tree
Showing 20 changed files with 3,224 additions and 287 deletions.
2,744 changes: 2,621 additions & 123 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ name = "anyon_braiding_simulator"
crate-type = ["cdylib"]

[dependencies]
pyo3 = "0.21.1"
pyo3 = { version = "0.21", features = ["extension-module"] }
numpy = "0.21"
ndarray = "0.13"

[dev-dependencies]
maturin = "0.12"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ indent-style = "space"
docstring-code-format = true

[tool.pyright]
include = ["python", "target/wheels", "venv/lib/python3.12/site-packages/"]
include = ["python/anyon_braiding_simulator"]
6 changes: 6 additions & 0 deletions python/anyon_braiding_simulator/Model.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ def __init__(self, model_type: AnyonModel, num_fusion_channels=5) -> None:

self._num_fusion_channels = num_fusion_channels

def get_model_type(self) -> AnyonModel:
"""
Provides the model type
"""
return self.model_type

def get_charges(self) -> set:
"""
Provide the charges that are defined in this model.
Expand Down
123 changes: 123 additions & 0 deletions python/anyon_braiding_simulator/anyon_braiding_simulator.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Standard Library
from typing import List, Optional, Tuple

class IsingTopoCharge:
"""
Options for the topological charge for an Ising Model anyon
"""

Psi: 'IsingTopoCharge'
Vacuum: 'IsingTopoCharge'
Sigma: 'IsingTopoCharge'

def value(self) -> int: ...
def to_string(self) -> str: ...

class FibonacciTopoCharge:
"""
Options for the topological charge for a Fibonacci Model anyon
"""

Tau: 'FibonacciTopoCharge'
Vacuum: 'FibonacciTopoCharge'

class TopoCharge:
"""
Options for the topological charge for an anyon.
Currently only supports Ising and Fibonacci models.
"""

ising: Optional[IsingTopoCharge]
fibonacci: Optional[FibonacciTopoCharge]

def __init__(
self, ising: Optional[IsingTopoCharge] = ..., fibonacci: Optional[FibonacciTopoCharge] = ...
) -> None: ...
@staticmethod
def from_ising(ising: IsingTopoCharge) -> 'TopoCharge': ...
@staticmethod
def from_fibonacci(fibonacci: FibonacciTopoCharge) -> 'TopoCharge': ...
def is_ising(self) -> bool: ...
def is_fibonacci(self) -> bool: ...
def get_ising(self) -> IsingTopoCharge: ...
def get_fibonacci(self) -> FibonacciTopoCharge: ...
def to_string(self) -> str: ...

class Anyon:
"""
In Topological Quantum Computing, anyons are the fundamental quasiparticles
which enable the computation. Anyons have an associated topological charge
given by the model used. This struct represents an anyon with a name,
charge, and position.
"""

name: str
charge: TopoCharge
position: Tuple[float, float]

def __init__(self, name: str, charge: TopoCharge, position: Tuple[float, float]) -> None: ...
def __str__(self) -> str: ...

class AnyonModel:
"""
Different Anyon models that can be used to simulate the system
"""

Ising: 'AnyonModel'
Fibonacci: 'AnyonModel'
Custom: 'AnyonModel'

def __init__(self) -> None: ...

class FusionPair:
"""
Represents a pair of anyons indices that are fused. The indices are derived
from the relative ordering in the list of anyons stored in State.
FusionPair is used to represent the fusion events along the fusion tree.
When two anyons are fused, the resulting anyon carries the lower index. i.e.
the anyon resulting from the fusion (1,2) is later referenced as 1.
"""

anyon_1: int
anyon_2: int

def __init__(self, anyon_1: int, anyon_2: int) -> None: ...
def __str__(self) -> str: ...

class Fusion:
"""
Stores the state of the system and all fusion operations that occur in the
fusion tree. The vector is 2D, where the outer vector represents the time
step and the inner vector represents the fusion operations that occur at
that time step.
"""
def __init__(self, state: State) -> None: ...
def verify_basis(self, basis: Basis) -> bool: ...
def qubit_enc(self, anyon_model: AnyonModel) -> List[FusionPair]: ...
def __str__(self) -> str: ...
def apply_fusion(self, anyon_1: List[int], anyon_2: List[int], anyon_model: AnyonModel) -> List[int]: ...
def verify_fusion_result(self, init_charge: TopoCharge, anyon_model: AnyonModel) -> bool: ...

class State:
"""
Stores the overall state of the system. Use this struct to keep track of any
common information throughout the simulation (e.g. anyons, operations,
statevector).
"""

anyons: List[Anyon]
operations: List[Tuple[int, FusionPair]]

def __init__(self) -> None: ...
def add_anyon(self, anyon: Anyon) -> bool: ...
def add_operation(self, time: int, operation: FusionPair) -> bool: ...

class Basis:
"""
The basis is represented as a vector of tuples (time, FusionPair). In TQC,
the basis is a sequence of fusion operations that occur in the fusion tree,
and a different fusion ordering is a different basis.
"""
def __init__(self, ops: List[Tuple[int, FusionPair]]) -> None: ...
def verify_basis(self, anyons: int) -> bool: ...
31 changes: 24 additions & 7 deletions python/anyon_braiding_simulator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
import subprocess
import sys

from anyon_braiding_simulator import Anyon, AnyonModel, IsingTopoCharge
from anyon_braiding_simulator.anyon_braiding_simulator import (
Anyon,
AnyonModel,
FibonacciTopoCharge,
IsingTopoCharge,
TopoCharge,
)
from Braiding import Braid
from Model import Model
from Simulator import Simulator
Expand All @@ -23,11 +29,22 @@ def anyon(*args):
topological_charge = args[1]
position = ()

topo_charge = {
'psi': IsingTopoCharge.Psi,
'sigma': IsingTopoCharge.Sigma,
'vac': IsingTopoCharge.Vacuum,
}
topo_charge = {}
if sim.get_model().get_model_type() == AnyonModel.Ising:
topo_charge = {
'psi': IsingTopoCharge.Psi,
'sigma': IsingTopoCharge.Sigma,
'vac': IsingTopoCharge.Vacuum,
}
elif sim.get_model().get_model_type() == AnyonModel.Fibonacci:
topo_charge = {
'tau': FibonacciTopoCharge.Tau,
'Vacuum': FibonacciTopoCharge.Vacuum,
}
else:
print('Error: Model not set')
return

try:
topological_charge = topo_charge[args[1].lower()]
except KeyError:
Expand Down Expand Up @@ -67,7 +84,7 @@ def anyon(*args):
print('Error: position must be formatted as {x,y} where x and y are numbers')
return

new_anyon = Anyon(name, topological_charge, position)
new_anyon = Anyon(name, TopoCharge(topological_charge), position)
try:
sim.update_anyons(True, [new_anyon])
if len(args) == 2:
Expand Down
6 changes: 3 additions & 3 deletions python/tests/test_anyon.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import pytest
from anyon_braiding_simulator import Anyon, IsingTopoCharge
from anyon_braiding_simulator.anyon_braiding_simulator import Anyon, IsingTopoCharge, TopoCharge


@pytest.mark.anyon
def test_anyon():
anyon = Anyon('thisisatest', IsingTopoCharge.Psi, (1, 1))
anyon = Anyon('thisisatest', TopoCharge.from_ising(IsingTopoCharge.Psi), (1, 1))
assert anyon.name == 'thisisatest'
assert anyon.charge == IsingTopoCharge.Psi
assert anyon.charge.get_ising() == IsingTopoCharge.Psi
assert anyon.position == (1, 1)
2 changes: 1 addition & 1 deletion python/tests/test_basis.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from anyon_braiding_simulator import Basis, FusionPair
from anyon_braiding_simulator.anyon_braiding_simulator import Basis, FusionPair


@pytest.fixture
Expand Down
38 changes: 30 additions & 8 deletions python/tests/test_fusion.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import pytest
from anyon_braiding_simulator import Anyon, Fusion, FusionPair, IsingTopoCharge, State
from anyon_braiding_simulator.anyon_braiding_simulator import (
Anyon,
AnyonModel,
Fusion,
FusionPair,
IsingTopoCharge,
State,
TopoCharge,
)


@pytest.fixture
def state() -> State:
state = State()
for i in range(6):
state.add_anyon(Anyon(f'{i}', IsingTopoCharge.Sigma, (0, 0)))
state.add_anyon(Anyon(f'{i}', TopoCharge.from_ising(IsingTopoCharge.Sigma), (0, 0)))
return state


Expand All @@ -32,13 +40,13 @@ def test_apply_ising_fusion(state):
vacuum = [0, 1, 0]
sigma = [0, 0, 1]

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)]
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)]

psi_sigma = [1, 0, 1]
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
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


@pytest.mark.fusion
Expand All @@ -52,4 +60,18 @@ 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())) == set(map(str, correct))
assert set(map(str, fusion.qubit_enc(AnyonModel.Ising))) == 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)

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)
2 changes: 1 addition & 1 deletion python/tests/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import numpy as np
import pytest
from anyon_braiding_simulator import AnyonModel
from anyon_braiding_simulator.anyon_braiding_simulator import AnyonModel
from Model import Model


Expand Down
8 changes: 4 additions & 4 deletions python/tests/test_state.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from anyon_braiding_simulator import Anyon, FusionPair, IsingTopoCharge, State
from anyon_braiding_simulator.anyon_braiding_simulator import Anyon, FusionPair, IsingTopoCharge, State, TopoCharge


@pytest.fixture
Expand All @@ -10,20 +10,20 @@ def state() -> State:
@pytest.mark.state
def test_add_anyon(state):
for i in range(100):
anyon = Anyon(f'{i}', IsingTopoCharge.Sigma, (0, 0))
anyon = Anyon(f'{i}', TopoCharge.from_ising(IsingTopoCharge.Sigma), (0, 0))
state.add_anyon(anyon)

for anyon in state.anyons:
assert anyon.name in [f'{i}' for i in range(100)]
assert anyon.charge == IsingTopoCharge.Sigma
assert anyon.charge.get_ising() == IsingTopoCharge.Sigma
assert anyon.position == (0, 0)

assert len(state.anyons) == 100


def test_add_operation(state):
for i in range(101):
anyon = Anyon(f'{i}', IsingTopoCharge.Sigma, (0, 0))
anyon = Anyon(f'{i}', TopoCharge.from_ising(IsingTopoCharge.Sigma), (0, 0))
state.add_anyon(anyon)

assert state.add_operation(1, FusionPair(0, 1))
Expand Down
41 changes: 41 additions & 0 deletions python/tests/test_state_vec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import numpy as np
import pytest
from anyon_braiding_simulator.anyon_braiding_simulator import StateVec


@pytest.mark.state_vec
def test_state_vec_init():
test_arr = np.array([1, 0], dtype=complex)
state = StateVec(1, None)
for i in range(len(state.vec)):
assert state.vec[i] == test_arr[i]

test_arr = np.array([1, 0, 0, 0], dtype=complex)
state = StateVec(2, None)
for i in range(len(state.vec)):
assert state.vec[i] == test_arr[i]


@pytest.mark.state_vec
def test_state_vec():
test_arr = np.array([1, 0], dtype=complex)
state = StateVec(1, test_arr)
for i in range(len(test_arr)):
assert state.vec[i] == test_arr[i]


@pytest.mark.state_vec
def test_state_vec_norm():
test_arr = np.array([1, 0], dtype=complex)
state = StateVec(1, test_arr)
assert np.linalg.norm(state.vec) == 1

test_arr = np.array([1234 + 5j, 6], dtype=complex)
state = StateVec(1, test_arr)
assert np.linalg.norm(state.vec) == 1


@pytest.mark.state_vec
def test_state_vec_str():
state_vec = StateVec(1, np.array([1, 0], dtype=complex))
assert str(state_vec) == '[\n\t1.0 + 0.0i\n\t0.0 + 0.0i\n]'
Loading

0 comments on commit 65a5269

Please sign in to comment.