@@ -1,6 +1,6 @@ | |||||
# abp | # abp | ||||
Python port of Anders and Briegel' s [method](https://arxiv.org/abs/quant-ph/0504117) for fast simulation of Clifford circuits. You can read the full documentation [here](https://peteshadbolt.co.uk/abp/). | |||||
Python port of Anders and Briegel' s [method](https://arxiv.org/abs/quant-ph/0504117) for fast simulation of Clifford circuits. You can read the full documentation [here](https://peteshadbolt.co.uk/abp/). | |||||
![demo](examples/demo.gif) | ![demo](examples/demo.gif) | ||||
@@ -6,7 +6,7 @@ STATIC = glob("static/*.*")+glob("static/img/*.*")+glob("static/scripts/*.*") | |||||
setup( | setup( | ||||
name = "abp", | name = "abp", | ||||
version = "0.4.1", | |||||
version = "0.4.2", | |||||
packages = find_packages(), | packages = find_packages(), | ||||
test_suite = "tests", | test_suite = "tests", | ||||
author = "Pete Shadbolt", | author = "Pete Shadbolt", | ||||
@@ -1,13 +0,0 @@ | |||||
from abp import GraphState | |||||
def demograph(): | |||||
""" A graph for testing with """ | |||||
g = GraphState([0,1,2,3,100,200]) | |||||
g.add_edge(0, 1) | |||||
g.add_edge(1, 2) | |||||
g.add_edge(2, 0) | |||||
g.add_edge(0, 3) | |||||
g.add_edge(100, 200) | |||||
return g | |||||
@@ -1,53 +0,0 @@ | |||||
from abp import GraphState, clifford | |||||
from anders_briegel import graphsim | |||||
import numpy as np | |||||
def clean_random_state(N=10): | |||||
""" A state to test on """ | |||||
a = GraphState(range(N)) | |||||
b = graphsim.GraphRegister(N) | |||||
clifford.use_old_cz() | |||||
for i in range(N): | |||||
a.act_hadamard(i) | |||||
b.hadamard(i) | |||||
for i in range(10): | |||||
j, k= np.random.choice(range(N), 2, replace=False) | |||||
a.act_cz(j, k) | |||||
b.cphase(j, k) | |||||
return a, b | |||||
def messy_random_state(N=10): | |||||
a, b = clean_random_state(N) | |||||
for i in range(N): | |||||
a.act_hadamard(i) | |||||
b.hadamard(i) | |||||
for i in range(N): | |||||
j, k= np.random.choice(range(N), 2, replace=False) | |||||
a.act_cz(j, k) | |||||
b.cphase(j, k) | |||||
for i in range(N): | |||||
j = np.random.choice(range(N)) | |||||
k = np.random.choice(range(24)) | |||||
a.act_local_rotation(j, k) | |||||
b.local_op(j, graphsim.LocCliffOp(k)) | |||||
return a, b | |||||
def bell(): | |||||
a = GraphState(range(2)) | |||||
b = graphsim.GraphRegister(2) | |||||
a.act_hadamard(0); a.act_hadamard(1); | |||||
b.hadamard(0); b.hadamard(1); | |||||
a.act_cz(0,1) | |||||
b.cphase(0,1) | |||||
return a, b | |||||
def onequbit(): | |||||
a = GraphState(range(1)) | |||||
b = graphsim.GraphRegister(1) | |||||
return a, b |
@@ -29,8 +29,8 @@ class AndersWrapper(graphsim.GraphRegister): | |||||
def measure(self, qubit, basis, force): | def measure(self, qubit, basis, force): | ||||
basis = clifford.by_name[basis] | basis = clifford.by_name[basis] | ||||
basis = {1: graphsim.lco_X, | |||||
2: graphsim.lco_Y, | |||||
basis = {1: graphsim.lco_X, | |||||
2: graphsim.lco_Y, | |||||
3: graphsim.lco_Z}[clifford.by_name[basis]] | 3: graphsim.lco_Z}[clifford.by_name[basis]] | ||||
super(AndersWrapper, self).measure(qubit, basis, None, force) | super(AndersWrapper, self).measure(qubit, basis, None, force) | ||||
@@ -50,7 +50,7 @@ class ABPWrapper(GraphState): | |||||
""" A wrapper for abp, just to ensure determinism """ | """ A wrapper for abp, just to ensure determinism """ | ||||
def __init__(self, nodes=[]): | def __init__(self, nodes=[]): | ||||
super(ABPWrapper, self).__init__(nodes, deterministic = True) | |||||
super(ABPWrapper, self).__init__(nodes, deterministic=True) | |||||
def random_pair(n): | def random_pair(n): | ||||
@@ -60,13 +60,14 @@ def random_pair(n): | |||||
def random_graph_state(n=10): | def random_graph_state(n=10): | ||||
""" A random Graph state. """ | """ A random Graph state. """ | ||||
czs = [(random_pair(n), "cz") for i in range(n*2)] | |||||
czs = [(random_pair(n), "cz") for i in range(n * 2)] | |||||
for Base in AndersWrapper, ABPWrapper: | for Base in AndersWrapper, ABPWrapper: | ||||
g = Base(range(n)) | g = Base(range(n)) | ||||
g.act_circuit((i, "hadamard") for i in range(n)) | g.act_circuit((i, "hadamard") for i in range(n)) | ||||
g.act_circuit(czs) | g.act_circuit(czs) | ||||
yield g | yield g | ||||
def random_stabilizer_state(n=10): | def random_stabilizer_state(n=10): | ||||
""" Generate a random stabilizer state, without any VOPs """ | """ Generate a random stabilizer state, without any VOPs """ | ||||
rotations = [(i, random.choice(range(24))) for i in range(n)] | rotations = [(i, random.choice(range(24))) for i in range(n)] | ||||
@@ -74,17 +75,20 @@ def random_stabilizer_state(n=10): | |||||
g.act_circuit(rotations) | g.act_circuit(rotations) | ||||
yield g | yield g | ||||
def bell_pair(): | def bell_pair(): | ||||
for Base in AndersWrapper, ABPWrapper: | for Base in AndersWrapper, ABPWrapper: | ||||
g = Base((0, 1)) | g = Base((0, 1)) | ||||
g.act_circuit(((0, "hadamard"), (1, "hadamard"), ((0, 1), "cz"))) | g.act_circuit(((0, "hadamard"), (1, "hadamard"), ((0, 1), "cz"))) | ||||
yield g | yield g | ||||
def onequbit(): | def onequbit(): | ||||
for Base in AndersWrapper, ABPWrapper: | for Base in AndersWrapper, ABPWrapper: | ||||
g = Base((0,)) | g = Base((0,)) | ||||
yield g | yield g | ||||
def named_node_graph(): | def named_node_graph(): | ||||
""" A graph with named nodes""" | """ A graph with named nodes""" | ||||
edges = (0, 1), (1, 2), (2, 0), (0, 3), (100, 200), (200, "named") | edges = (0, 1), (1, 2), (2, 0), (0, 3), (100, 200), (200, "named") | ||||
@@ -93,6 +97,15 @@ def named_node_graph(): | |||||
g.act_circuit((edge, "cz") for edge in edges) | g.act_circuit((edge, "cz") for edge in edges) | ||||
return g | return g | ||||
def simple_graph(): | |||||
""" A simple graph to test with""" | |||||
edges = (0, 1), (1, 2), (2, 0), (0, 3), (100, 200) | |||||
g = ABPWrapper([0, 1, 2, 3, 100, 200]) | |||||
g.act_circuit((i, "hadamard") for i in g.node) | |||||
g.act_circuit((edge, "cz") for edge in edges) | |||||
return g | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
a, b = random_graph_state() | a, b = random_graph_state() | ||||
assert a == b | assert a == b | ||||
@@ -101,4 +114,3 @@ if __name__ == '__main__': | |||||
assert a == b | assert a == b | ||||
print named_node_graph() | print named_node_graph() | ||||
@@ -1,14 +0,0 @@ | |||||
from abp import qi | |||||
def test_dumbness(): | |||||
a = qi.CircuitModel(1) | |||||
b = qi.CircuitModel(1) | |||||
assert a == b | |||||
a.act_local_rotation(0, qi.px) | |||||
assert not (a == b) | |||||
a.act_local_rotation(0, qi.px) | |||||
assert (a == b) |
@@ -1,7 +0,0 @@ | |||||
from abp import clifford | |||||
def test_names(): | |||||
""" Test the naming scheme """ | |||||
for i in range(24): | |||||
clifford.get_name(i) | |||||
assert clifford.get_name(16)=="IE" |
@@ -1,24 +0,0 @@ | |||||
from anders_briegel import graphsim | |||||
from abp import clifford | |||||
import itertools | |||||
#//! replaces op by trans * op * trans^dagger and returns a phase, | |||||
#/*! either +1 or -1 (as RightPhase(0) or RightPhase(2)) */ | |||||
def test_conjugation(): | |||||
""" Test that clifford.conugate() agrees with graphsim.LocCliffOp.conjugate """ | |||||
for operation_index, transform_index in itertools.product(range(4), range(24)): | |||||
transform = graphsim.LocCliffOp(transform_index) | |||||
operation = graphsim.LocCliffOp(operation_index) | |||||
phase = operation.conjugate(transform).ph | |||||
if phase == 1: | |||||
print phase | |||||
phase = [1, 0, -1][phase] | |||||
new_operation = operation.op | |||||
NEW_OPERATION, PHASE = clifford.conjugate(operation_index, transform_index) | |||||
print new_operation, NEW_OPERATION, " ", | |||||
assert new_operation == NEW_OPERATION | |||||
assert PHASE == phase | |||||
@@ -1,27 +0,0 @@ | |||||
import numpy as np | |||||
from abp import clifford, qi, build_tables | |||||
import itertools as it | |||||
def test_cz_table(): | |||||
""" Does the CZ code work good? """ | |||||
state_table = build_tables.get_state_table(clifford.unitaries) | |||||
rows = it.product([0, 1], it.combinations_with_replacement(range(24), 2)) | |||||
for bond, (c1, c2) in rows: | |||||
# Pick the input state | |||||
input_state = state_table[bond, c1, c2] | |||||
# Go and compute the output | |||||
computed_output = np.dot(qi.cz, input_state) | |||||
computed_output = qi.normalize_global_phase(computed_output) | |||||
# Now look up the answer in the table | |||||
bondp, c1p, c2p = clifford.cz_table[bond, c1, c2] | |||||
table_output = state_table[bondp, c1p, c2p] | |||||
assert np.allclose(computed_output, table_output) | |||||
@@ -1,15 +0,0 @@ | |||||
from abp import GraphState | |||||
from abp import qi | |||||
import numpy as np | |||||
def test_single_qubit(): | |||||
""" Test some single-qubit stuff """ | |||||
g = GraphState() | |||||
g.add_node(0) | |||||
g.add_node(1) | |||||
g.act_local_rotation(0, "hadamard") | |||||
g.act_local_rotation(1, "hadamard") | |||||
g.act_cz(0, 1) | |||||
assert np.allclose(g.to_state_vector().state, qi.bond) | |||||
@@ -1,17 +1,18 @@ | |||||
from numpy import * | |||||
import numpy as np | |||||
from tqdm import tqdm | from tqdm import tqdm | ||||
import itertools as it | import itertools as it | ||||
from abp import clifford | from abp import clifford | ||||
from abp import build_tables | from abp import build_tables | ||||
from abp import qi | from abp import qi | ||||
from nose.tools import raises | from nose.tools import raises | ||||
from anders_briegel import graphsim | |||||
def identify_pauli(m): | def identify_pauli(m): | ||||
""" Given a signed Pauli matrix, name it. """ | """ Given a signed Pauli matrix, name it. """ | ||||
for sign in (+1, -1): | for sign in (+1, -1): | ||||
for pauli_label, pauli in zip("xyz", qi.paulis): | for pauli_label, pauli in zip("xyz", qi.paulis): | ||||
if allclose(sign * pauli, m): | |||||
if np.allclose(sign * pauli, m): | |||||
return sign, pauli_label | return sign, pauli_label | ||||
@@ -70,6 +71,45 @@ def test_cz_table_makes_sense(): | |||||
assert all( | assert all( | ||||
clifford.cz_table[0, hadamard, hadamard] == [0, hadamard, hadamard]) | clifford.cz_table[0, hadamard, hadamard] == [0, hadamard, hadamard]) | ||||
def test_commuters(): | def test_commuters(): | ||||
""" Test that commutation is good """ | """ Test that commutation is good """ | ||||
assert len(build_tables.get_commuters(clifford.unitaries)) == 4 | assert len(build_tables.get_commuters(clifford.unitaries)) == 4 | ||||
def test_conjugation(): | |||||
""" Test that clifford.conugate() agrees with graphsim.LocCliffOp.conjugate """ | |||||
for operation_index, transform_index in it.product(range(4), range(24)): | |||||
transform = graphsim.LocCliffOp(transform_index) | |||||
operation = graphsim.LocCliffOp(operation_index) | |||||
phase = operation.conjugate(transform).ph | |||||
phase = [1, 0, -1][phase] | |||||
new_operation = operation.op | |||||
NEW_OPERATION, PHASE = clifford.conjugate( | |||||
operation_index, transform_index) | |||||
assert new_operation == NEW_OPERATION | |||||
assert PHASE == phase | |||||
def test_cz_table(): | |||||
""" Does the CZ code work good? """ | |||||
state_table = build_tables.get_state_table(clifford.unitaries) | |||||
rows = it.product([0, 1], it.combinations_with_replacement(range(24), 2)) | |||||
for bond, (c1, c2) in rows: | |||||
# Pick the input state | |||||
input_state = state_table[bond, c1, c2] | |||||
# Go and compute the output | |||||
computed_output = np.dot(qi.cz, input_state) | |||||
computed_output = qi.normalize_global_phase(computed_output) | |||||
# Now look up the answer in the table | |||||
bondp, c1p, c2p = clifford.cz_table[bond, c1, c2] | |||||
table_output = state_table[bondp, c1p, c2p] | |||||
assert np.allclose(computed_output, table_output) |
@@ -0,0 +1,76 @@ | |||||
from abp import GraphState | |||||
from abp import clifford | |||||
from mock import simple_graph | |||||
import time | |||||
def test_graph_basic(): | |||||
""" Test that we can construct graphs, delete edges, whatever """ | |||||
g = simple_graph() | |||||
assert set(g.adj[0].keys()) == set([1, 2, 3]) | |||||
g._del_edge(0, 1) | |||||
assert set(g.adj[0].keys()) == set([2, 3]) | |||||
assert g.has_edge(1, 2) | |||||
assert not g.has_edge(0, 1) | |||||
def test_local_complementation(): | |||||
""" Test that local complementation works as expected """ | |||||
g = simple_graph() | |||||
g.local_complementation(0) | |||||
assert g.has_edge(0, 1) | |||||
assert g.has_edge(0, 2) | |||||
assert not g.has_edge(1, 2) | |||||
assert g.has_edge(3, 2) | |||||
assert g.has_edge(3, 1) | |||||
# TODO: test VOP conditions | |||||
def test_remove_vop(): | |||||
""" Test that removing VOPs really works """ | |||||
g = simple_graph() | |||||
g.remove_vop(0, 1) | |||||
assert g.node[0]["vop"] == clifford.by_name["identity"] | |||||
g.remove_vop(1, 1) | |||||
assert g.node[1]["vop"] == clifford.by_name["identity"] | |||||
g.remove_vop(2, 1) | |||||
assert g.node[2]["vop"] == clifford.by_name["identity"] | |||||
g.remove_vop(0, 1) | |||||
assert g.node[0]["vop"] == clifford.by_name["identity"] | |||||
def test_edgelist(): | |||||
""" Test making edgelists """ | |||||
g = simple_graph() | |||||
el = g.edgelist() | |||||
assert (0, 3) in el | |||||
assert (0, 2) in el | |||||
assert (100, 200) in el | |||||
def test_stress(n = int(1e5)): | |||||
""" Testing that making a graph of ten thousand qubits takes less than half a second""" | |||||
g = GraphState(range(n+1)) | |||||
t = time.clock() | |||||
for i in xrange(n): | |||||
g._add_edge(i, i + 1) | |||||
assert time.clock() - t < .5 | |||||
def test_cz(): | |||||
""" Test CZ gate """ | |||||
g = GraphState([0, 1]) | |||||
g.act_local_rotation(0, clifford.by_name["hadamard"]) | |||||
g.act_local_rotation(1, clifford.by_name["hadamard"]) | |||||
g.act_local_rotation(1, clifford.by_name["py"]) | |||||
assert not g.has_edge(0, 1) | |||||
g.act_cz(0, 1) | |||||
assert g.has_edge(0, 1) | |||||
def test_stabilizer(): | |||||
""" Test that we can generate stabilizers okay """ | |||||
g = simple_graph() | |||||
stab = g.to_stabilizer() | |||||
#TODO | |||||
@@ -1,11 +1,14 @@ | |||||
import numpy as np | import numpy as np | ||||
from abp import qi | from abp import qi | ||||
from abp import GraphState | |||||
def test_init(): | def test_init(): | ||||
""" Can you initialize some qubits """ | """ Can you initialize some qubits """ | ||||
psi = qi.CircuitModel(5) | psi = qi.CircuitModel(5) | ||||
assert psi.d == 32 | assert psi.d == 32 | ||||
def test_single_qubit_stuff(): | def test_single_qubit_stuff(): | ||||
""" Try some sensible single-qubit things """ | """ Try some sensible single-qubit things """ | ||||
psi = qi.CircuitModel(2) | psi = qi.CircuitModel(2) | ||||
@@ -18,6 +21,7 @@ def test_single_qubit_stuff(): | |||||
psi.act_local_rotation(0, qi.px) | psi.act_local_rotation(0, qi.px) | ||||
assert np.allclose(psi.state[0], -1) | assert np.allclose(psi.state[0], -1) | ||||
def test_further_single_qubit_stuff(): | def test_further_single_qubit_stuff(): | ||||
""" Try some sensible single-qubit things """ | """ Try some sensible single-qubit things """ | ||||
psi = qi.CircuitModel(2) | psi = qi.CircuitModel(2) | ||||
@@ -37,6 +41,7 @@ def test_more_single_qubit_stuff(): | |||||
psi.act_local_rotation(1, qi.px) | psi.act_local_rotation(1, qi.px) | ||||
psi.act_cz(0, 1) | psi.act_cz(0, 1) | ||||
def test_equality(): | def test_equality(): | ||||
""" Test that equality succeeds / fails as desired """ | """ Test that equality succeeds / fails as desired """ | ||||
a = qi.CircuitModel(2) | a = qi.CircuitModel(2) | ||||
@@ -46,17 +51,17 @@ def test_equality(): | |||||
assert a != b | assert a != b | ||||
def test_hadamard(): | def test_hadamard(): | ||||
""" What does CZ do ? """ | """ What does CZ do ? """ | ||||
psi = qi.CircuitModel(3) | psi = qi.CircuitModel(3) | ||||
psi.act_hadamard(0) | psi.act_hadamard(0) | ||||
psi.act_hadamard(1) | psi.act_hadamard(1) | ||||
assert np.allclose(psi.state, np.array([[1,1,1,1,0,0,0,0]]).T/2.) | |||||
assert np.allclose(psi.state, np.array([[1, 1, 1, 1, 0, 0, 0, 0]]).T / 2.) | |||||
psi.act_hadamard(1) | psi.act_hadamard(1) | ||||
psi.act_hadamard(0) | psi.act_hadamard(0) | ||||
psi.act_hadamard(2) | psi.act_hadamard(2) | ||||
assert np.allclose(psi.state, qi.ir2*np.array([[1,0,0,0,1,0,0,0]]).T) | |||||
assert np.allclose( | |||||
psi.state, qi.ir2 * np.array([[1, 0, 0, 0, 1, 0, 0, 0]]).T) | |||||
def test_cz(): | def test_cz(): | ||||
@@ -82,3 +87,27 @@ def test_local_rotation(): | |||||
assert np.allclose(psi.state[0], 1) | assert np.allclose(psi.state[0], 1) | ||||
def test_dumbness(): | |||||
""" Check that I haven't done something really dumb """ | |||||
a = qi.CircuitModel(1) | |||||
b = qi.CircuitModel(1) | |||||
assert a == b | |||||
a.act_local_rotation(0, qi.px) | |||||
assert not (a == b) | |||||
a.act_local_rotation(0, qi.px) | |||||
assert (a == b) | |||||
def test_to_state_vector_single_qubit(): | |||||
""" Test some single-qubit stuff """ | |||||
g = GraphState() | |||||
g.add_node(0) | |||||
g.add_node(1) | |||||
g.act_local_rotation(0, "hadamard") | |||||
g.act_local_rotation(1, "hadamard") | |||||
g.act_cz(0, 1) | |||||
assert np.allclose(g.to_state_vector().state, qi.bond) |