diff --git a/abp/graphstate.py b/abp/graphstate.py index 1ed614a..703f983 100644 --- a/abp/graphstate.py +++ b/abp/graphstate.py @@ -16,19 +16,25 @@ class GraphState(object): Internally it uses the same dictionary-of-dictionaries data structure as ``networkx``. """ - def __init__(self, nodes=[], deterministic=False): + def __init__(self, nodes=[], deterministic=False, vop="identity"): """ Construct a ``GraphState`` - :param nodes: An iterable of nodes used to construct the graph. + :param nodes: An iterable of nodes used to construct the graph, or an integer -- the number of nodes. :param deterministic: If ``True``, the behaviour of the graph is deterministic up to but not including the choice of measurement outcome. This is slightly less efficient, but useful for testing. If ``False``, the specific graph representation will sometimes be random -- of course, all possible representations still map to the same state vector. + :param vop: The default VOP for new qubits. Setting ``vop="identity"`` initializes qubits in :math:`|+\\rangle`. Setting ``vop="hadamard"`` initializes qubits in :math:`|0\\rangle`. """ - self.adj, self.node = {}, {} - self.add_nodes(nodes) self.deterministic = deterministic + self.adj, self.node = {}, {} + try: + for n in nodes: + self._add_node(n, vop=vop) + except TypeError: + for n in range(nodes): + self._add_node(n, vop=vop) - def add_node(self, node, **kwargs): - """ Add a node. + def _add_node(self, node, **kwargs): + """ Add a node. By default, nodes are initialized with ``vop=``:math:`I`, i.e. they are in the :math:`|+\\rangle` state. :param node: The name of the node, e.g. ``9``, ``start`` :type node: Any hashable type @@ -42,14 +48,23 @@ class GraphState(object): """ assert not node in self.node, "Node {} already exists".format(v) + default = kwargs.get("default", "identity") self.adj[node] = {} - self.node[node] = {"vop": clifford.by_name["hadamard"]} + self.node[node] = {} + kwargs["vop"] = clifford.by_name[str(kwargs.get("vop", "identity"))] #TODO: ugly self.node[node].update(kwargs) - def add_nodes(self, nodes): - """ Add many nodes in one shot. """ - for n in nodes: - self.add_node(n) + def add_qubit(self, name, **kwargs): + """ Add a qubit to the state. + + :param name: The name of the node, e.g. ``9``, ``start``. + :type name: Any hashable type + :param kwargs: Any extra node attributes + + By default, qubits are initialized in the :math:`|0\\rangle` state. Provide the optional ``vop`` argument to set the initial state. + """ + kwargs["vop"] = clifford.by_name[str(kwargs.get("vop", "hadamard"))] #TODO: ugly + self._add_node(name, **kwargs) def act_circuit(self, circuit): """ Run many gates in one call. diff --git a/tests/mock.py b/tests/mock.py index 7e7d0c8..75da5ba 100644 --- a/tests/mock.py +++ b/tests/mock.py @@ -52,7 +52,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, vop="hadamard") def print_stabilizer(self): print self.to_stabilizer() diff --git a/tests/test_fancy.py b/tests/test_fancy.py index a6decb7..dcc6884 100644 --- a/tests/test_fancy.py +++ b/tests/test_fancy.py @@ -15,16 +15,16 @@ def test_json_basic(): def test_tuple_keys(): """ Test that we can use tuple-ish keys """ g = fancy.GraphState() - g.add_node("string") - g.add_node((1, 2, 3)) + g.add_qubit("string") + g.add_qubit((1, 2, 3)) g.add_edge((1, 2, 3), "string") json.dumps(g.to_json(True)) def networkx_test(): """ Test that fancy graph states really behave like networkx graphs """ g = fancy.GraphState() - g.add_node(0, position = xyz(10, 0, 0)) - g.add_node(1, position = xyz(1, 0, 0)) + g.add_qubit(0, position = xyz(10, 0, 0)) + g.add_qubit(1, position = xyz(1, 0, 0)) g.act_hadamard(0) g.act_hadamard(1) g.act_cz(0, 1) diff --git a/tests/test_graphstate.py b/tests/test_graphstate.py index c582e1c..0f6e3b5 100644 --- a/tests/test_graphstate.py +++ b/tests/test_graphstate.py @@ -7,6 +7,14 @@ from tqdm import tqdm REPEATS = 100 DEPTH = 100 +def test_initialization(): + g = GraphState("abc") + assert g.node["a"]["vop"] == clifford.by_name["identity"] + g = GraphState("abc", vop="hadamard") + assert g.node["c"]["vop"] == clifford.by_name["hadamard"] + g = GraphState(5) + assert len(g.node) == 5 + def test_graph_basic(): """ Test that we can construct graphs, delete edges, whatever """ @@ -56,7 +64,7 @@ def test_edgelist(): def test_stress(n=int(1e5)): """ Testing that making a graph of ten thousand qubits takes less than half a second""" import time - g = GraphState(range(n + 1)) + g = GraphState(range(n + 1), vop="hadamard") t = time.clock() for i in xrange(n): g._add_edge(i, i + 1) @@ -65,7 +73,7 @@ def test_stress(n=int(1e5)): def test_cz(): """ Test CZ gate """ - g = GraphState([0, 1]) + g = GraphState([0, 1], vop="hadamard") 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"]) @@ -77,7 +85,7 @@ def test_cz(): def test_local_complementation(): """ Test that local complementation works okay """ pairs = (0, 1), (0, 3), (1, 3), (1, 2), - psi = GraphState(range(4)) + psi = GraphState(range(4), vop="hadamard") psi.act_circuit([(i, "hadamard") for i in psi.node]) psi.act_circuit([(pair, "cz") for pair in pairs]) old_edges = psi.edgelist() diff --git a/tests/test_measurement.py b/tests/test_measurement.py index d50ef65..d6d494f 100644 --- a/tests/test_measurement.py +++ b/tests/test_measurement.py @@ -10,16 +10,16 @@ def test_single_qubit_measurements(): """ Various simple tests of measurements """ # Test that measuring |0> in Z gives 0 - g = GraphState([0]) + g = GraphState([0], vop="hadamard") assert g.measure(0, "pz") == 0, "Measuring |0> in Z gives 0" # Test that measuring |1> in Z gives 1 - g = GraphState([0]) + g = GraphState([0], vop="hadamard") g.act_local_rotation(0, "px") assert g.measure(0, "pz") == 1, "Measuring |1> in Z gives 1" # Test that measuring |+> in X gives 0 - g = GraphState([0]) + g = GraphState([0], vop="hadamard") g.act_local_rotation(0, "hadamard") assert g.measure(0, "px") == 0 assert g.measure(0, "px") == 0, "Measuring |+> in X gives 0" @@ -30,7 +30,7 @@ def test_single_qubit_measurements(): def test_type(): """ Test that the output is always an int """ for r, m, f in it.product(range(24), ("px", "py", "pz"), (0, 1)): - g = GraphState([0]) + g = GraphState([0], vop="hadamard") g.act_local_rotation(0, r) assert str(g.measure(0, m)) in "01" assert str(g.measure(0, m, f)) in "01" @@ -41,7 +41,7 @@ def test_random_outcomes(): """ Testing random behaviour """ ones = 0 for i in range(1000): - g = GraphState([0]) + g = GraphState([0], vop="hadamard") g.act_local_rotation(0, "hadamard") ones += g.measure(0, "pz") assert 400 < ones < 600, "This is a probabilistic test!" @@ -49,13 +49,13 @@ def test_random_outcomes(): def test_projection(): """ Test that projection works correctly """ - g = GraphState([0]) + g = GraphState([0], vop="hadamard") g.act_local_rotation(0, "hadamard") g.measure(0, "pz", 0) assert np.allclose(g.to_state_vector().state, qi.zero) # Now project onto |1> - g = GraphState([0]) + g = GraphState([0], vop="hadamard") g.act_local_rotation(0, "hadamard") g.measure(0, "pz", 1) assert np.allclose(g.to_state_vector().state, qi.one) diff --git a/tests/test_mercedes.py b/tests/test_mercedes.py index 732dae8..3a4e81b 100644 --- a/tests/test_mercedes.py +++ b/tests/test_mercedes.py @@ -3,7 +3,7 @@ from abp.util import xyz from mock import simple_graph def linear_cluster(n): - g = GraphState(range(n)) + g = GraphState(range(n), vop="hadamard") g.act_circuit([(i, "hadamard") for i in range(n)]) g.act_circuit([((i, i+1), "cz") for i in range(n-1)]) return g @@ -23,16 +23,16 @@ def test_single_qubit_measurements(): """ Various simple tests of measurements """ # Test that measuring |0> in Z gives 0 - g = GraphState([0]) + g = GraphState([0], vop="hadamard") assert g.measure_z(0) == 0, "Measuring |0> in Z gives 0" # Test that measuring |1> in Z gives 1 - g = GraphState([0]) + g = GraphState([0], vop="hadamard") g.act_local_rotation(0, "px") assert g.measure_z(0) == 1, "Measuring |1> in Z gives 1" # Test that measuring |+> in X gives 0 - g = GraphState([0]) + g = GraphState([0], vop="hadamard") g.act_local_rotation(0, "hadamard") assert g.measure_x(0) == 0 assert g.measure_x(0) == 0, "Measuring |+> in X gives 0" @@ -44,7 +44,7 @@ def test_single_qubit_measurements(): def test_is_determinate(): """ Test whether asking if an outcome was random or determinate works """ - g = GraphState([0]) + g = GraphState([0], vop="hadamard") assert g.measure_z(0, detail=True)["determinate"] == True assert g.measure_x(0, detail=True)["determinate"] == False diff --git a/tests/test_qi.py b/tests/test_qi.py index 575a75e..6e922e6 100644 --- a/tests/test_qi.py +++ b/tests/test_qi.py @@ -104,8 +104,8 @@ def test_dumbness(): def test_to_state_vector_single_qubit(): """ Test some single-qubit stuff """ g = GraphState() - g.add_node(0) - g.add_node(1) + g.add_qubit(0) + g.add_qubit(1) g.act_local_rotation(0, "hadamard") g.act_local_rotation(1, "hadamard") g.act_cz(0, 1)