| @@ -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/). | |||||
|  |  | ||||
| @@ -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) | |||||