@@ -1,6 +1,6 @@ | |||
# 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) | |||
@@ -6,7 +6,7 @@ STATIC = glob("static/*.*")+glob("static/img/*.*")+glob("static/scripts/*.*") | |||
setup( | |||
name = "abp", | |||
version = "0.4.1", | |||
version = "0.4.2", | |||
packages = find_packages(), | |||
test_suite = "tests", | |||
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): | |||
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]] | |||
super(AndersWrapper, self).measure(qubit, basis, None, force) | |||
@@ -50,7 +50,7 @@ class ABPWrapper(GraphState): | |||
""" A wrapper for abp, just to ensure determinism """ | |||
def __init__(self, nodes=[]): | |||
super(ABPWrapper, self).__init__(nodes, deterministic = True) | |||
super(ABPWrapper, self).__init__(nodes, deterministic=True) | |||
def random_pair(n): | |||
@@ -60,13 +60,14 @@ def random_pair(n): | |||
def random_graph_state(n=10): | |||
""" 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: | |||
g = Base(range(n)) | |||
g.act_circuit((i, "hadamard") for i in range(n)) | |||
g.act_circuit(czs) | |||
yield g | |||
def random_stabilizer_state(n=10): | |||
""" Generate a random stabilizer state, without any VOPs """ | |||
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) | |||
yield g | |||
def bell_pair(): | |||
for Base in AndersWrapper, ABPWrapper: | |||
g = Base((0, 1)) | |||
g.act_circuit(((0, "hadamard"), (1, "hadamard"), ((0, 1), "cz"))) | |||
yield g | |||
def onequbit(): | |||
for Base in AndersWrapper, ABPWrapper: | |||
g = Base((0,)) | |||
yield g | |||
def named_node_graph(): | |||
""" A graph with named nodes""" | |||
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) | |||
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__': | |||
a, b = random_graph_state() | |||
assert a == b | |||
@@ -101,4 +114,3 @@ if __name__ == '__main__': | |||
assert a == b | |||
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 | |||
import itertools as it | |||
from abp import clifford | |||
from abp import build_tables | |||
from abp import qi | |||
from nose.tools import raises | |||
from anders_briegel import graphsim | |||
def identify_pauli(m): | |||
""" Given a signed Pauli matrix, name it. """ | |||
for sign in (+1, -1): | |||
for pauli_label, pauli in zip("xyz", qi.paulis): | |||
if allclose(sign * pauli, m): | |||
if np.allclose(sign * pauli, m): | |||
return sign, pauli_label | |||
@@ -70,6 +71,45 @@ def test_cz_table_makes_sense(): | |||
assert all( | |||
clifford.cz_table[0, hadamard, hadamard] == [0, hadamard, hadamard]) | |||
def test_commuters(): | |||
""" Test that commutation is good """ | |||
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 | |||
from abp import qi | |||
from abp import GraphState | |||
def test_init(): | |||
""" Can you initialize some qubits """ | |||
psi = qi.CircuitModel(5) | |||
assert psi.d == 32 | |||
def test_single_qubit_stuff(): | |||
""" Try some sensible single-qubit things """ | |||
psi = qi.CircuitModel(2) | |||
@@ -18,6 +21,7 @@ def test_single_qubit_stuff(): | |||
psi.act_local_rotation(0, qi.px) | |||
assert np.allclose(psi.state[0], -1) | |||
def test_further_single_qubit_stuff(): | |||
""" Try some sensible single-qubit things """ | |||
psi = qi.CircuitModel(2) | |||
@@ -37,6 +41,7 @@ def test_more_single_qubit_stuff(): | |||
psi.act_local_rotation(1, qi.px) | |||
psi.act_cz(0, 1) | |||
def test_equality(): | |||
""" Test that equality succeeds / fails as desired """ | |||
a = qi.CircuitModel(2) | |||
@@ -46,17 +51,17 @@ def test_equality(): | |||
assert a != b | |||
def test_hadamard(): | |||
""" What does CZ do ? """ | |||
psi = qi.CircuitModel(3) | |||
psi.act_hadamard(0) | |||
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(0) | |||
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(): | |||
@@ -82,3 +87,27 @@ def test_local_rotation(): | |||
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) |