diff --git a/cz_table.py b/cz_table.py deleted file mode 100644 index 882c8c9..0000000 --- a/cz_table.py +++ /dev/null @@ -1,14 +0,0 @@ -""" This part computes the CZ table """ - -from util import cache_to_disk -import qi -import numpy as np -from tqdm import tqdm -import clifford - -#@cache_to_disk("cz_table.pkl") -def construct_table(): - print "awd" - return "awd" - -cz_table = construct_table() diff --git a/graph.py b/graph.py index f0800dd..3b4aaeb 100644 --- a/graph.py +++ b/graph.py @@ -4,68 +4,75 @@ Provides an extremely basic graph structure, based on neighbour lists from collections import defaultdict import itertools as it -import clifford - -def graph(): - """ Generate a graph with Hadamards on each qubit """ - #return defaultdict(set), defaultdict(lambda: clifford.by_name["hadamard"]) - return [set() for i in range(100)], [clifford.by_name["hadamard"] for i in range(100)] - - -def add_edge(graph, v1, v2): - """ Add an edge between two vertices in the graph """ - graph[v1].add(v2) - graph[v2].add(v1) - -def del_edge(graph, v1, v2): - """ Delete an edge between two vertices in the graph """ - graph[v1].remove(v2) - graph[v2].remove(v1) - -def has_edge(graph, v1, v2): - """ Test existence of an edge between two vertices in the graph """ - return v2 in graph[v1] - -def toggle_edge(graph, v1, v2): - """ Toggle an edge between two vertices in the graph """ - if has_edge(graph, v1, v2): - del_edge(graph, v1, v2) - else: - add_edge(graph, v1, v2) - -def edgelist(g): - """ Describe a graph as an edgelist """ - edges = frozenset(frozenset((i, n)) - for i, v in enumerate(g) - for n in v) - return [tuple(e) for e in edges] - -def cphase(g, vops, a, b): - """ Act a controlled-phase gate on two qubits """ - if g[a]-{b}: remove_vop(g, vops, a, b) - if g[b]-{a}: remove_vop(g, vops, b, a) - if g[a]-{b}: remove_vop(g, vops, a, b) - edge = has_edge(g, a, b) - new_edge, vops[a], vops[b] = cphase_table[edge, vops[a], vops[b]] - if new_edge != edge: - toggle_edge(g, a, b) - - -def remove_vop(g, vops, a, avoid): - """ Reduces VOP[a] to the identity, avoiding (if possible) the use of vertex b as a swapping partner """ - others = g[a] - {avoid} - swap_qubit = others.pop() if others else avoid - for v in reversed(clifford.decompositions[vops[a]]): - local_complementation(g, vops, a if v == "x" else swap_qubit) - - -def local_complementation(g, vops, v): - """ As defined in LISTING 1 of Anders & Briegel """ - for i, j in it.combinations(g[v], 2): - toggle_edge(g, i, j) - - # Update VOPs - vops[v] = clifford.times_table[vops[v]][clifford.by_name["sqx"]] - for i in g[v]: - vops[i] = clifford.times_table[vops[i]][clifford.by_name["msqz"]] +import tables as clifford + + +class GraphState(object): + def __init__(self): + self.ngbh = defaultdict(set) + self.vops = defaultdict(int) + + + def add_edge(self, v1, v2): + """ Add an edge between two vertices in the self """ + if not v1 in self.ngbh: self.vops[v1] = clifford.by_name["hadamard"] + if not v2 in self.ngbh: self.vops[v2] = clifford.by_name["hadamard"] + self.ngbh[v1].add(v2) + self.ngbh[v2].add(v1) + + + def del_edge(self, v1, v2): + """ Delete an edge between two vertices in the self """ + self.ngbh[v1].remove(v2) + self.ngbh[v2].remove(v1) + + + def has_edge(self, v1, v2): + """ Test existence of an edge between two vertices in the self """ + return v2 in self.ngbh[v1] + + + def toggle_edge(self, v1, v2): + """ Toggle an edge between two vertices in the self """ + if self.has_edge(v1, v2): + self.del_edge(v1, v2) + else: + self.add_edge(v1, v2) + + def edgelist(self): + """ Describe a graph as an edgelist """ + edges = frozenset(frozenset((i, n)) + for i, v in enumerate(self.ngbh.values()) + for n in v) + return [tuple(e) for e in edges] + + def remove_vop(self, a, avoid): + """ Reduces VOP[a] to the identity, avoiding (if possible) the use of vertex b as a swapping partner """ + others = self.ngbh[a] - {avoid} + swap_qubit = others.pop() if others else avoid + for v in reversed(clifford.decompositions[self.vops[a]]): + self.local_complementation(a if v == "x" else swap_qubit) + + def cphase(self, a, b): + """ Act a controlled-phase gate on two qubits """ + if self.ngbh[a] - {b}: + self.remove_vop(a, b) + if self.ngbh[b] - {a}: + self.remove_vop(b, a) + if self.ngbh[a] - {b}: + self.remove_vop(a, b) + edge = self.has_edge(a, b) + new_edge, vops[a], vops[b] = cphase_table[edge, vops[a], vops[b]] + if new_edge != edge: + self.toggle_edge(a, b) + + def local_complementation(self, v): + """ As defined in LISTING 1 of Anders & Briegel """ + for i, j in it.combinations(self.ngbh[v], 2): + self.toggle_edge(i, j) + + # Update VOPs + self.vops[v] = clifford.times_table[self.vops[v]][clifford.by_name["sqx"]] + for i in self.ngbh[v]: + self.vops[i] = clifford.times_table[self.vops[i]][clifford.by_name["msqz"]] diff --git a/clifford.py b/tables.py similarity index 71% rename from clifford.py rename to tables.py index ef3ed8a..9dc8008 100644 --- a/clifford.py +++ b/tables.py @@ -9,7 +9,21 @@ import numpy as np from tqdm import tqdm import qi from functools import reduce -from util import cache_to_disk +import cPickle + +def cache_to_disk(file_name): + """ A decorator to cache the output of a function to disk """ + def wrap(func): + def modified(*args, **kwargs): + try: + output = cPickle.load(open(file_name, "r")) + except (IOError, ValueError): + output = func(*args, **kwargs) + with open(file_name, "w") as f: + cPickle.dump(output, f) + return output + return modified + return wrap # TODO: make this more efficient / shorter @@ -27,12 +41,13 @@ def compose_u(decomposition): us = (elements[c] for c in decomposition) return np.matrix(reduce(np.dot, us), dtype=complex) + def name_of(vop): """ Get the formatted name of a VOP """ return "%s" % get_name[vop] if vop in get_name else "VOP%d" % vop -@cache_to_disk("clifford_tables.pkl") +@cache_to_disk("tables.cache") def construct_tables(): """ Constructs / caches multiplication and conjugation tables """ by_name = {name: find_up_to_phase(u)[0] for name, u in qi.by_name.items()} @@ -41,13 +56,14 @@ def construct_tables(): for i, u in enumerate(unitaries)] times_table = [[find_up_to_phase(u * v)[0] for v in unitaries] for u in tqdm(unitaries)] - return by_name, get_name, conjugation_table, times_table + cz_table = None + return by_name, get_name, conjugation_table, times_table, cz_table + -# Various useful tables decompositions = ("xxxx", "xx", "zzxx", "zz", "zxx", "z", "zzz", "xxz", "xzx", "xzxxx", "xzzzx", "xxxzx", "xzz", "zzx", "xxx", "x", "zzzx", "xxzx", "zx", "zxxx", "xxxz", "xzzz", "xz", "xzxx") elements = {"x": qi.sqx, "z": qi.msqz} unitaries = [compose_u(d) for d in decompositions] -by_name, get_name, conjugation_table, times_table = construct_tables() +by_name, get_name, conjugation_table, times_table, cz_table = construct_tables() diff --git a/tests/test_clifford.py b/tests/test_clifford.py index cfc16f8..5df3ec5 100644 --- a/tests/test_clifford.py +++ b/tests/test_clifford.py @@ -1,4 +1,4 @@ -import clifford as lc +import tables as lc from numpy import * from scipy.linalg import sqrtm import qi diff --git a/tests/test_graph.py b/tests/test_graph.py index 0ebc23a..d6eeacb 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -1,51 +1,52 @@ -from graph import * +from graph import GraphState +import tables as lc def test_graph(): - g, v = graph() - add_edge(g, 0,1) - add_edge(g, 1,2) - add_edge(g, 2,0) - assert g[0]==set([1,2]) + g = GraphState() + g.add_edge(0,1) + g.add_edge(1,2) + g.add_edge(2,0) + assert g.ngbh[0]==set([1,2]) - del_edge(g, 0,1) - assert g[0]==set([2]) - el = edgelist(g) + g.del_edge(0,1) + assert g.ngbh[0]==set([2]) + el = g.edgelist() assert (1,2) in el assert not (0,1) in el assert len(el)==2 - assert has_edge(g, 1,2) - assert not has_edge(g, 0,1) + 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, vops = graph() - add_edge(g, 0, 1) - add_edge(g, 0, 2) - add_edge(g, 1, 2) - add_edge(g, 0, 3) - local_complementation(g, vops, 0) - assert has_edge(g, 0, 1) - assert has_edge(g, 0, 2) - assert not has_edge(g, 1, 2) - assert has_edge(g, 3, 2) - assert has_edge(g, 3, 1) + g = GraphState() + g.add_edge(0,1) + g.add_edge(1,2) + g.add_edge(2,0) + g.add_edge(0,3) + 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, vops = graph() - add_edge(g, 0, 1) - add_edge(g, 0, 2) - add_edge(g, 1, 2) - add_edge(g, 0, 3) - remove_vop(g, vops, 0, 1) - assert vops[0] == clifford.by_name["identity"] - remove_vop(g, vops, 1, 1) - assert vops[1] == clifford.by_name["identity"] - remove_vop(g, vops, 2, 1) - assert vops[2] == clifford.by_name["identity"] - remove_vop(g, vops, 0, 1) - assert vops[0] == clifford.by_name["identity"] + g = GraphState() + g.add_edge(0,1) + g.add_edge(1,2) + g.add_edge(2,0) + g.add_edge(0,3) + g.remove_vop(0, 1) + assert g.vops[0] == lc.by_name["identity"] + g.remove_vop(1, 1) + assert g.vops[1] == lc.by_name["identity"] + g.remove_vop(2, 1) + assert g.vops[2] == lc.by_name["identity"] + g.remove_vop(0, 1) + assert g.vops[0] == lc.by_name["identity"] diff --git a/tests/test_viz.py b/tests/test_viz.py new file mode 100644 index 0000000..e15d8f5 --- /dev/null +++ b/tests/test_viz.py @@ -0,0 +1,11 @@ +from graph import GraphState +import viz + +def test_viz(): + g = GraphState() + g.add_edge(0,1) + g.add_edge(1,2) + g.add_edge(2,0) + g.add_edge(0,3) + print g.vops + viz.draw(g) diff --git a/util.py b/util.py index 810fb53..0e66ebf 100644 --- a/util.py +++ b/util.py @@ -1,14 +1,7 @@ """ Useful but messy crap """ -import cPickle -import networkx as nx -from matplotlib import pyplot as plt -from graph import * -import clifford -import numpy as np -VOP_COLORS = ["red", "green", "blue", "orange", "yellow", "purple", "black", "white"] def cache_to_disk(file_name): """ A decorator to cache the output of a function to disk """ @@ -25,21 +18,3 @@ def cache_to_disk(file_name): return wrap -def draw(graph, vops, filename="out.pdf", pos=None, ns=500): - """ Draw a graph with networkx layout """ - plt.clf() - g = nx.from_edgelist(edgelist(graph)) - pos = nx.spring_layout(g) if pos==None else pos - colors = [VOP_COLORS[vop % len(VOP_COLORS)] for vop in vops] - nx.draw_networkx_nodes(g, pos, node_color="white", node_size=ns) - nx.draw_networkx_nodes(g, pos, node_color=colors, node_size=ns, alpha=.4) - nx.draw_networkx_edges(g, pos, edge_color="gray") - nx.draw_networkx_labels(g, pos, font_family="FreeSans") - - labels = {i: clifford.name_of(vops[i]) for i in g.nodes()} - pos = {k: v + np.array([0, -.1]) for k, v in pos.items()} - nx.draw_networkx_labels(g, pos, labels, font_family="FreeSans") - plt.axis('off') - plt.savefig(filename) - return pos - diff --git a/viz.py b/viz.py new file mode 100644 index 0000000..fe1d632 --- /dev/null +++ b/viz.py @@ -0,0 +1,25 @@ +import networkx as nx +from matplotlib import pyplot as plt +import tables +import numpy as np +from graph import GraphState + +VOP_COLORS = ["red", "green", "blue", "orange", "yellow", "purple", "black", "white"] + +def draw(state, filename="out.pdf", pos=None, ns=500): + """ Draw a graph with networkx layout """ + plt.clf() + graph = nx.from_edgelist(state.edgelist()) + pos = nx.spring_layout(graph) if pos==None else pos + colors = [VOP_COLORS[vop % len(VOP_COLORS)] for vop in state.vops.values()] + nx.draw_networkx_nodes(graph, pos, node_color="white", node_size=ns) + nx.draw_networkx_nodes(graph, pos, node_color=colors, node_size=ns, alpha=.4) + nx.draw_networkx_edges(graph, pos, edge_color="gray") + nx.draw_networkx_labels(graph, pos, font_family="FreeSans") + + labels = {i: tables.name_of(v) for i, v in state.vops.items()} + pos = {k: v + np.array([0, -.1]) for k, v in pos.items()} + nx.draw_networkx_labels(graph, pos, labels, font_family="FreeSans") + plt.axis('off') + plt.savefig(filename) + return pos