diff --git a/README.md b/README.md index 12c49c3..b93cc40 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/setup.py b/setup.py index 12d7fb2..9d88985 100755 --- a/setup.py +++ b/setup.py @@ -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", diff --git a/tests/demograph.py b/tests/demograph.py deleted file mode 100644 index 443be90..0000000 --- a/tests/demograph.py +++ /dev/null @@ -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 - - diff --git a/tests/dummy.py b/tests/dummy.py deleted file mode 100644 index cccb072..0000000 --- a/tests/dummy.py +++ /dev/null @@ -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 diff --git a/tests/mock.py b/tests/mock.py index 2963a5b..6bf3dc3 100644 --- a/tests/mock.py +++ b/tests/mock.py @@ -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() - diff --git a/tests/old/test_circuit_model_fails.py b/tests/old/test_circuit_model_fails.py deleted file mode 100644 index e53a055..0000000 --- a/tests/old/test_circuit_model_fails.py +++ /dev/null @@ -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) diff --git a/tests/old/test_clifford_names.py b/tests/old/test_clifford_names.py deleted file mode 100644 index 8d8c2ea..0000000 --- a/tests/old/test_clifford_names.py +++ /dev/null @@ -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" diff --git a/tests/old/test_conjugation.py b/tests/old/test_conjugation.py deleted file mode 100644 index f1d859a..0000000 --- a/tests/old/test_conjugation.py +++ /dev/null @@ -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 - diff --git a/tests/old/test_cphase_table.py b/tests/old/test_cphase_table.py deleted file mode 100644 index d34897f..0000000 --- a/tests/old/test_cphase_table.py +++ /dev/null @@ -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) - - - diff --git a/tests/old/test_get_state_vector.py b/tests/old/test_get_state_vector.py deleted file mode 100644 index aa6daae..0000000 --- a/tests/old/test_get_state_vector.py +++ /dev/null @@ -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) - - diff --git a/tests/old/test_clifford.py b/tests/test_clifford.py similarity index 62% rename from tests/old/test_clifford.py rename to tests/test_clifford.py index 8790667..9a2fe87 100644 --- a/tests/old/test_clifford.py +++ b/tests/test_clifford.py @@ -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) diff --git a/tests/test_graphstate.py b/tests/test_graphstate.py new file mode 100644 index 0000000..382dc9d --- /dev/null +++ b/tests/test_graphstate.py @@ -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 + diff --git a/tests/old/test_qi_circuit_model.py b/tests/test_qi.py similarity index 73% rename from tests/old/test_qi_circuit_model.py rename to tests/test_qi.py index 36f848c..63b836d 100644 --- a/tests/old/test_qi_circuit_model.py +++ b/tests/test_qi.py @@ -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)