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