@@ -27,16 +27,21 @@ 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 get_name[vop].title() if vop in get_name else "VOP%d" % vop | |||
@cache_to_disk("tables.pkl") | |||
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()} | |||
get_name = {v:k for k, v in by_name.items()} | |||
conjugation_table = [find_up_to_phase(u.H)[0] | |||
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 conjugation_table, times_table | |||
return by_name, get_name, conjugation_table, times_table | |||
# Various useful tables | |||
decompositions = ("xxxx", "xx", "zzxx", "zz", "zxx", "z", "zzz", "xxz", | |||
@@ -44,15 +49,10 @@ decompositions = ("xxxx", "xx", "zzxx", "zz", "zxx", "z", "zzz", "xxz", | |||
"zzzx", "xxzx", "zx", "zxxx", "xxxz", "xzzz", "xz", "xzxx") | |||
elements = {"x": qi.sqx, "z": qi.msqz} | |||
unitaries = [compose_u(d) for d in decompositions] | |||
conjugation_table, times_table = construct_tables() | |||
# TODO: generate these global names automatically via search | |||
sqx = 15 | |||
msqz = 5 | |||
by_name, get_name, conjugation_table, times_table = construct_tables() | |||
if __name__ == '__main__': | |||
print find_up_to_phase(qi.sqx) | |||
print find_up_to_phase(qi.msqz) | |||
print by_name | |||
print get_name | |||
@@ -3,6 +3,19 @@ import viz | |||
import itertools as it | |||
import clifford | |||
#def cphase(a, b): | |||
#""" Act a controlled-phase gate on two qubits """ | |||
#if g | |||
def remove_vop(g, vops, a, b): | |||
""" Reduces VOP[a] to the identity, avoiding (if possible) the use of vertex b as a swapping partner """ | |||
free_neighbours = g[a] - {b} | |||
c = b if len(free_neighbours) == 0 else free_neighbours.pop() | |||
d = clifford.decompositions[a] | |||
for v in reversed(d): | |||
target = a if v == clifford.by_name["msqx"] else b | |||
local_complementation(g, vops, target) | |||
def local_complementation(g, vops, v): | |||
""" As defined in LISTING 1 of Anders & Briegel """ | |||
@@ -10,20 +23,11 @@ def local_complementation(g, vops, v): | |||
toggle_edge(g, i, j) | |||
# Update VOPs | |||
vops[v] = clifford.times_table[vops[v]][clifford.sqx] | |||
vops[v] = clifford.times_table[vops[v]][clifford.by_name["sqx"]] | |||
for i in g[v]: | |||
vops[i] = clifford.times_table[vops[i]][clifford.msqz] | |||
vops[i] = clifford.times_table[vops[i]][clifford.by_name["msqz"]] | |||
def remove_vop(g, vops, a, b): | |||
""" Reduces VOP[a] to the identity, avoiding (if possible) the use of vertex b as a swapping partner """ | |||
free_neighbours = g[a] - {b} | |||
c = b if len(free_neighbours) == 0 else free_neighbours.pop() | |||
d = clifford.decompositions[a] | |||
for v in reversed(d): | |||
target = a if v == clifford.sqx else b | |||
local_complementation(g, vops, target) | |||
if __name__ == '__main__': | |||
g, vops = graph() | |||
@@ -31,7 +35,8 @@ if __name__ == '__main__': | |||
add_edge(g, 1, 2) | |||
add_edge(g, 0, 2) | |||
add_edge(g, 0, 3) | |||
add_edge(g, 6, 7) | |||
viz.draw(g, vops, "out.pdf") | |||
pos = viz.draw(g, vops, "out.pdf") | |||
local_complementation(g, vops, 0) | |||
viz.draw(g, vops, "out2.pdf") | |||
viz.draw(g, vops, "out2.pdf", pos) |
@@ -3,10 +3,13 @@ Provides an extremely basic graph structure, based on neighbour lists | |||
""" | |||
from collections import defaultdict | |||
import clifford | |||
def graph(): | |||
""" Generate a graph with Hadamards on each qubit """ | |||
return defaultdict(set), defaultdict(int) | |||
#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 """ | |||
@@ -32,7 +35,7 @@ def toggle_edge(graph, v1, v2): | |||
def edgelist(g): | |||
""" Describe a graph as an edgelist """ | |||
edges = frozenset(frozenset((i, n)) | |||
for i, v in enumerate(g.values()) | |||
for i, v in enumerate(g) | |||
for n in v) | |||
return [tuple(e) for e in edges] | |||
@@ -22,3 +22,7 @@ msqz = sqrtm(-1j * pz) | |||
sqx = sqrtm(1j * px) | |||
msqx = sqrtm(-1j * px) | |||
paulis = (px, py, pz) | |||
common_us = id, px, py, pz, ha, ph, sqz, msqz, sqy, msqy, sqx, msqx | |||
names = "identity", "px", "py", "pz", "hadamard", "phase", "sqz", "msqz", "sqy", "msqy", "sqx", "msqx" | |||
by_name = dict(zip(names, common_us)) |
@@ -1,7 +1,7 @@ | |||
import clifford as lc | |||
from numpy import * | |||
from scipy.linalg import sqrtm | |||
from qi import * | |||
import qi | |||
from tqdm import tqdm | |||
import itertools as it | |||
@@ -9,7 +9,7 @@ import itertools as it | |||
def identify_pauli(m): | |||
""" Given a signed Pauli matrix, name it. """ | |||
for sign in (+1, -1): | |||
for pauli_label, pauli in zip("xyz", paulis): | |||
for pauli_label, pauli in zip("xyz", qi.paulis): | |||
if allclose(sign * pauli, m): | |||
return sign, pauli_label | |||
@@ -22,7 +22,7 @@ def _test_find_up_to_phase(): | |||
def get_action(u): | |||
""" What does this unitary operator do to the Paulis? """ | |||
return [identify_pauli(u * p * u.H) for p in paulis] | |||
return [identify_pauli(u * p * u.H) for p in qi.paulis] | |||
def format_action(action): | |||
@@ -37,8 +37,7 @@ def test_we_have_24_matrices(): | |||
def test_we_have_all_useful_gates(): | |||
""" Check that all the interesting gates are included up to a global phase """ | |||
common_us = id, px, py, pz, ha, ph, sqz, msqz, sqy, msqy, sqx, msqx | |||
for u in common_us: | |||
for name, u in qi.by_name.items(): | |||
lc.find_up_to_phase(u) | |||
@@ -17,3 +17,9 @@ def test_local_complementation(): | |||
assert has_edge(g, 3, 1) | |||
# TODO: test VOP conditions | |||
def test_remove_vop(): | |||
""" Test that removing VOPs really works """ | |||
pass | |||
@@ -5,21 +5,28 @@ Utility function for plotting graphs nicely | |||
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 draw(graph, vops, filename="out.pdf", ns=500): | |||
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) | |||
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) | |||
nx.draw_networkx_edges(g, pos) | |||
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) | |||
plt.axis('off') | |||
plt.savefig(filename) | |||
return pos | |||
if __name__ == '__main__': | |||
g, vops = graph() | |||