| @@ -1,6 +1,6 @@ | |||||
| import time, atexit, json | import time, atexit, json | ||||
| import sys | import sys | ||||
| import networkx | |||||
| import networkx as nx | |||||
| import numpy as np | import numpy as np | ||||
| import websocket | import websocket | ||||
| from socket import error as socket_error | from socket import error as socket_error | ||||
| @@ -8,7 +8,7 @@ import graphstate | |||||
| import clifford | import clifford | ||||
| import util | import util | ||||
| class GraphState(graphstate.GraphState, networkx.Graph): | |||||
| class GraphState(graphstate.GraphState, nx.Graph): | |||||
| def __init__(self, *args, **kwargs): | def __init__(self, *args, **kwargs): | ||||
| graphstate.GraphState.__init__(self, *args, **kwargs) | graphstate.GraphState.__init__(self, *args, **kwargs) | ||||
| self.connect_to_server() | self.connect_to_server() | ||||
| @@ -50,7 +50,7 @@ class GraphState(graphstate.GraphState, networkx.Graph): | |||||
| def layout(self): | def layout(self): | ||||
| """ Automatically lay out the graph """ | """ Automatically lay out the graph """ | ||||
| pos = networkx.spring_layout(self, dim=3, scale=np.sqrt(self.order())) | |||||
| pos = nx.spring_layout(self, dim=3, scale=np.sqrt(self.order())) | |||||
| middle = np.average(pos.values(), axis=0) | middle = np.average(pos.values(), axis=0) | ||||
| pos = {key: value - middle for key, value in pos.items()} | pos = {key: value - middle for key, value in pos.items()} | ||||
| for key, (x, y, z) in pos.items(): | for key, (x, y, z) in pos.items(): | ||||
| @@ -16,10 +16,10 @@ class GraphState(object): | |||||
| Internally it uses the same dictionary-of-dictionaries data structure as ``networkx``. | Internally it uses the same dictionary-of-dictionaries data structure as ``networkx``. | ||||
| """ | """ | ||||
| def __init__(self, nodes=[], deterministic=False, vop="identity"): | |||||
| def __init__(self, data=(), deterministic=False, vop="identity"): | |||||
| """ Construct a ``GraphState`` | """ Construct a ``GraphState`` | ||||
| :param nodes: An iterable of nodes used to construct the graph, or an integer -- the number of nodes. | |||||
| :param data: An iterable of nodes used to construct the graph, or an integer -- the number of nodes, or a ``nx.Graph``. | |||||
| :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 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`. | :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`. | ||||
| """ | """ | ||||
| @@ -27,11 +27,20 @@ class GraphState(object): | |||||
| self.deterministic = deterministic | self.deterministic = deterministic | ||||
| self.adj, self.node = {}, {} | self.adj, self.node = {}, {} | ||||
| try: | try: | ||||
| for n in nodes: | |||||
| self._add_node(n, vop=vop) | |||||
| except TypeError: | |||||
| for n in range(nodes): | |||||
| self._add_node(n, vop=vop) | |||||
| # Cloning from a networkx graph | |||||
| self.adj = data.adj.copy() | |||||
| self.node = data.node.copy() | |||||
| for key, value in self.node.items(): | |||||
| self.node[key]["vop"] = data.node[key].get("vop", clifford.by_name["identity"]) | |||||
| except AttributeError: | |||||
| try: | |||||
| # Provided with a list of node names? | |||||
| for n in data: | |||||
| self._add_node(n, vop=vop) | |||||
| except TypeError: | |||||
| # Provided with an integer? | |||||
| for n in range(data): | |||||
| self._add_node(n, vop=vop) | |||||
| def _add_node(self, node, **kwargs): | 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. | """ Add a node. By default, nodes are initialized with ``vop=``:math:`I`, i.e. they are in the :math:`|+\\rangle` state. | ||||
| @@ -0,0 +1,61 @@ | |||||
| """ | |||||
| This is a sketch of a consistent language for defining resource states and lattices. | |||||
| """ | |||||
| import networkx as nx | |||||
| from abp.fancy import GraphState | |||||
| def union(*graphs): | |||||
| """ Assumes that all graphs are completely independent and uniquely labelled """ | |||||
| output = nx.Graph() | |||||
| output.node = dict(i for g in graphs for i in g.node.items()) | |||||
| output.adj = dict(i for g in graphs for i in g.adj.items()) | |||||
| return output | |||||
| def relabel(g, label): | |||||
| """ Shorthand relabel """ | |||||
| return nx.relabel_nodes(g, lambda x: (label, x)) | |||||
| def fuse(psi, na, nb): | |||||
| """ Deterministic fusion for testing purposes """ | |||||
| neighbors_a, neighbors_b = psi.neighbors(na), psi.neighbors(nb) | |||||
| new_edges = ((i, j) for i in neighbors_a for j in neighbors_b if i != j) | |||||
| psi.add_edges_from(new_edges) | |||||
| psi.remove_nodes_from((na, nb)) | |||||
| return psi | |||||
| def ghz(label): | |||||
| """ A 3-GHZ state """ | |||||
| psi = nx.Graph(((0, 1), (1, 2))) | |||||
| return relabel(psi, label) | |||||
| def microcluster(label): | |||||
| """ A microcluster """ | |||||
| psi = union(ghz(0), ghz(1), ghz(2)) | |||||
| psi = fuse(psi, (0, 1), (1, 0)) | |||||
| psi = fuse(psi, (1, 2), (2, 1)) | |||||
| return relabel(psi, label) | |||||
| def unit_cell(label): | |||||
| """ A simple ring-like unit cell """ | |||||
| psi = union(microcluster(0), microcluster(1), microcluster(2), microcluster(3)) | |||||
| psi = fuse(psi, (0, (0, 2)), (1, (2, 2))) | |||||
| psi = fuse(psi, (1, (0, 2)), (2, (2, 2))) | |||||
| psi = fuse(psi, (2, (0, 2)), (3, (2, 2))) | |||||
| psi = fuse(psi, (3, (0, 2)), (0, (2, 2))) | |||||
| return relabel(psi, label) | |||||
| def position(node): | |||||
| print node | |||||
| return {} | |||||
| def annotate(g, f): | |||||
| """ Annotate a graph """ | |||||
| for node in g.nodes(): | |||||
| g.node[node].update(f(node)) | |||||
| if __name__ == '__main__': | |||||
| psi = union(unit_cell((0, 0)), unit_cell((2, 0))) | |||||
| annotate(psi, position) | |||||
| g = GraphState(psi) | |||||
| @@ -5,6 +5,7 @@ from abp import clifford | |||||
| from abp.util import xyz | from abp.util import xyz | ||||
| from mock import simple_graph | from mock import simple_graph | ||||
| def test_json_basic(): | def test_json_basic(): | ||||
| """ Test that we can export to JSON """ | """ Test that we can export to JSON """ | ||||
| g = simple_graph() | g = simple_graph() | ||||
| @@ -12,6 +13,7 @@ def test_json_basic(): | |||||
| assert "adj" in js | assert "adj" in js | ||||
| assert "node" in js | assert "node" in js | ||||
| def test_tuple_keys(): | def test_tuple_keys(): | ||||
| """ Test that we can use tuple-ish keys """ | """ Test that we can use tuple-ish keys """ | ||||
| g = fancy.GraphState() | g = fancy.GraphState() | ||||
| @@ -20,17 +22,24 @@ def test_tuple_keys(): | |||||
| g.add_edge((1, 2, 3), "string") | g.add_edge((1, 2, 3), "string") | ||||
| json.dumps(g.to_json(True)) | json.dumps(g.to_json(True)) | ||||
| def networkx_test(): | def networkx_test(): | ||||
| """ Test that fancy graph states really behave like networkx graphs """ | """ Test that fancy graph states really behave like networkx graphs """ | ||||
| g = fancy.GraphState() | g = fancy.GraphState() | ||||
| g.add_qubit(0, position = xyz(10, 0, 0)) | |||||
| g.add_qubit(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(0) | ||||
| g.act_hadamard(1) | g.act_hadamard(1) | ||||
| g.act_cz(0, 1) | g.act_cz(0, 1) | ||||
| g.copy() | g.copy() | ||||
| # TODO: more tests here! | |||||
| def test_from_nx(): | |||||
| """ Test that making graphs from networkx objects goes smoothly """ | |||||
| g = nx.random_geometric_graph(100, 2) | |||||
| psi = fancy.GraphState(g) | |||||
| assert psi.node[0]["vop"] == 0 | |||||
| assert len(psi.edges()) > 0 | |||||
| psi.measure(0, "px", detail=True) | |||||
| psi = fancy.GraphState(nx.Graph(((0, 1),))) | |||||
| @@ -3,6 +3,7 @@ import mock | |||||
| import random | import random | ||||
| import numpy as np | import numpy as np | ||||
| from tqdm import tqdm | from tqdm import tqdm | ||||
| import networkx as nx | |||||
| REPEATS = 100 | REPEATS = 100 | ||||
| DEPTH = 100 | DEPTH = 100 | ||||
| @@ -121,3 +122,12 @@ def test_stabilizer_state_multiqubit(n=6): | |||||
| b = mock.circuit_to_state(mock.CircuitModelWrapper, n, circuit) | b = mock.circuit_to_state(mock.CircuitModelWrapper, n, circuit) | ||||
| assert a.to_state_vector() == b | assert a.to_state_vector() == b | ||||
| def test_from_nx(): | |||||
| """ Creating from a networkx graph """ | |||||
| g = nx.random_geometric_graph(100, 2) | |||||
| psi = GraphState(g) | |||||
| assert len(psi.node) == 100 | |||||
| psi = GraphState(nx.Graph(((0, 1),))) | |||||