Trying to get more sensible defaults for people who like to think about graphstates rather than circuits. Repsponse to issue #4: [Link](https://github.com/peteshadbolt/abp/issues/4)master
@@ -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. | |||
@@ -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() | |||
@@ -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) | |||
@@ -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() | |||
@@ -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) |
@@ -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 | |||
@@ -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) | |||