Browse Source

Returned to a shitty OO representation

At least we have infinite-size graphs now
master
Pete Shadbolt 8 years ago
parent
commit
32b417cb66
8 changed files with 165 additions and 144 deletions
  1. +0
    -14
      cz_table.py
  2. +71
    -64
      graph.py
  3. +21
    -5
      tables.py
  4. +1
    -1
      tests/test_clifford.py
  5. +36
    -35
      tests/test_graph.py
  6. +11
    -0
      tests/test_viz.py
  7. +0
    -25
      util.py
  8. +25
    -0
      viz.py

+ 0
- 14
cz_table.py View File

@@ -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()

+ 71
- 64
graph.py View File

@@ -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"]]


clifford.py → tables.py View File

@@ -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()


+ 1
- 1
tests/test_clifford.py View File

@@ -1,4 +1,4 @@
import clifford as lc
import tables as lc
from numpy import *
from scipy.linalg import sqrtm
import qi


+ 36
- 35
tests/test_graph.py View File

@@ -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"]


+ 11
- 0
tests/test_viz.py View File

@@ -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)

+ 0
- 25
util.py View File

@@ -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


+ 25
- 0
viz.py View File

@@ -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

Loading…
Cancel
Save