At least we have infinite-size graphs nowmaster
| @@ -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() | |||||
| @@ -4,68 +4,75 @@ Provides an extremely basic graph structure, based on neighbour lists | |||||
| from collections import defaultdict | from collections import defaultdict | ||||
| import itertools as it | 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"]] | |||||
| @@ -9,7 +9,21 @@ import numpy as np | |||||
| from tqdm import tqdm | from tqdm import tqdm | ||||
| import qi | import qi | ||||
| from functools import reduce | 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 | # TODO: make this more efficient / shorter | ||||
| @@ -27,12 +41,13 @@ def compose_u(decomposition): | |||||
| us = (elements[c] for c in decomposition) | us = (elements[c] for c in decomposition) | ||||
| return np.matrix(reduce(np.dot, us), dtype=complex) | return np.matrix(reduce(np.dot, us), dtype=complex) | ||||
| def name_of(vop): | def name_of(vop): | ||||
| """ Get the formatted name of a VOP """ | """ Get the formatted name of a VOP """ | ||||
| return "%s" % get_name[vop] if vop in get_name else "VOP%d" % 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(): | def construct_tables(): | ||||
| """ Constructs / caches multiplication and conjugation tables """ | """ Constructs / caches multiplication and conjugation tables """ | ||||
| by_name = {name: find_up_to_phase(u)[0] for name, u in qi.by_name.items()} | 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)] | for i, u in enumerate(unitaries)] | ||||
| times_table = [[find_up_to_phase(u * v)[0] for v in unitaries] | times_table = [[find_up_to_phase(u * v)[0] for v in unitaries] | ||||
| for u in tqdm(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", | decompositions = ("xxxx", "xx", "zzxx", "zz", "zxx", "z", "zzz", "xxz", | ||||
| "xzx", "xzxxx", "xzzzx", "xxxzx", "xzz", "zzx", "xxx", "x", | "xzx", "xzxxx", "xzzzx", "xxxzx", "xzz", "zzx", "xxx", "x", | ||||
| "zzzx", "xxzx", "zx", "zxxx", "xxxz", "xzzz", "xz", "xzxx") | "zzzx", "xxzx", "zx", "zxxx", "xxxz", "xzzz", "xz", "xzxx") | ||||
| elements = {"x": qi.sqx, "z": qi.msqz} | elements = {"x": qi.sqx, "z": qi.msqz} | ||||
| unitaries = [compose_u(d) for d in decompositions] | 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() | |||||
| @@ -1,4 +1,4 @@ | |||||
| import clifford as lc | |||||
| import tables as lc | |||||
| from numpy import * | from numpy import * | ||||
| from scipy.linalg import sqrtm | from scipy.linalg import sqrtm | ||||
| import qi | import qi | ||||
| @@ -1,51 +1,52 @@ | |||||
| from graph import * | |||||
| from graph import GraphState | |||||
| import tables as lc | |||||
| def test_graph(): | 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 (1,2) in el | ||||
| assert not (0,1) in el | assert not (0,1) in el | ||||
| assert len(el)==2 | 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(): | def test_local_complementation(): | ||||
| """ Test that local complementation works as expected """ | """ 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 | # TODO: test VOP conditions | ||||
| def test_remove_vop(): | def test_remove_vop(): | ||||
| """ Test that removing VOPs really works """ | """ 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"] | |||||
| @@ -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) | |||||
| @@ -1,14 +1,7 @@ | |||||
| """ | """ | ||||
| Useful but messy crap | 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): | def cache_to_disk(file_name): | ||||
| """ A decorator to cache the output of a function to disk """ | """ A decorator to cache the output of a function to disk """ | ||||
| @@ -25,21 +18,3 @@ def cache_to_disk(file_name): | |||||
| return wrap | 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 | |||||
| @@ -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 | |||||