| @@ -27,16 +27,21 @@ 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): | |||||
| """ 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") | @cache_to_disk("tables.pkl") | ||||
| 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()} | |||||
| get_name = {v:k for k, v in by_name.items()} | |||||
| conjugation_table = [find_up_to_phase(u.H)[0] | conjugation_table = [find_up_to_phase(u.H)[0] | ||||
| 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 conjugation_table, times_table | |||||
| return by_name, get_name, conjugation_table, times_table | |||||
| # Various useful tables | # Various useful tables | ||||
| decompositions = ("xxxx", "xx", "zzxx", "zz", "zxx", "z", "zzz", "xxz", | 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") | "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] | ||||
| 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__': | 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 itertools as it | ||||
| import clifford | 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): | def local_complementation(g, vops, v): | ||||
| """ As defined in LISTING 1 of Anders & Briegel """ | """ As defined in LISTING 1 of Anders & Briegel """ | ||||
| @@ -10,20 +23,11 @@ def local_complementation(g, vops, v): | |||||
| toggle_edge(g, i, j) | toggle_edge(g, i, j) | ||||
| # Update VOPs | # 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]: | 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__': | if __name__ == '__main__': | ||||
| g, vops = graph() | g, vops = graph() | ||||
| @@ -31,7 +35,8 @@ if __name__ == '__main__': | |||||
| add_edge(g, 1, 2) | add_edge(g, 1, 2) | ||||
| add_edge(g, 0, 2) | add_edge(g, 0, 2) | ||||
| add_edge(g, 0, 3) | 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) | 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 | from collections import defaultdict | ||||
| import clifford | |||||
| def graph(): | def graph(): | ||||
| """ Generate a graph with Hadamards on each qubit """ | """ 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): | def add_edge(graph, v1, v2): | ||||
| """ Add an edge between two vertices in the graph """ | """ Add an edge between two vertices in the graph """ | ||||
| @@ -32,7 +35,7 @@ def toggle_edge(graph, v1, v2): | |||||
| def edgelist(g): | def edgelist(g): | ||||
| """ Describe a graph as an edgelist """ | """ Describe a graph as an edgelist """ | ||||
| edges = frozenset(frozenset((i, n)) | edges = frozenset(frozenset((i, n)) | ||||
| for i, v in enumerate(g.values()) | |||||
| for i, v in enumerate(g) | |||||
| for n in v) | for n in v) | ||||
| return [tuple(e) for e in edges] | return [tuple(e) for e in edges] | ||||
| @@ -22,3 +22,7 @@ msqz = sqrtm(-1j * pz) | |||||
| sqx = sqrtm(1j * px) | sqx = sqrtm(1j * px) | ||||
| msqx = sqrtm(-1j * px) | msqx = sqrtm(-1j * px) | ||||
| paulis = (px, py, pz) | 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 | import clifford as lc | ||||
| from numpy import * | from numpy import * | ||||
| from scipy.linalg import sqrtm | from scipy.linalg import sqrtm | ||||
| from qi import * | |||||
| import qi | |||||
| from tqdm import tqdm | from tqdm import tqdm | ||||
| import itertools as it | import itertools as it | ||||
| @@ -9,7 +9,7 @@ import itertools as it | |||||
| def identify_pauli(m): | def identify_pauli(m): | ||||
| """ Given a signed Pauli matrix, name it. """ | """ Given a signed Pauli matrix, name it. """ | ||||
| for sign in (+1, -1): | 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): | if allclose(sign * pauli, m): | ||||
| return sign, pauli_label | return sign, pauli_label | ||||
| @@ -22,7 +22,7 @@ def _test_find_up_to_phase(): | |||||
| def get_action(u): | def get_action(u): | ||||
| """ What does this unitary operator do to the Paulis? """ | """ 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): | def format_action(action): | ||||
| @@ -37,8 +37,7 @@ def test_we_have_24_matrices(): | |||||
| def test_we_have_all_useful_gates(): | def test_we_have_all_useful_gates(): | ||||
| """ Check that all the interesting gates are included up to a global phase """ | """ 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) | lc.find_up_to_phase(u) | ||||
| @@ -17,3 +17,9 @@ def test_local_complementation(): | |||||
| assert has_edge(g, 3, 1) | assert has_edge(g, 3, 1) | ||||
| # TODO: test VOP conditions | # 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 | import networkx as nx | ||||
| from matplotlib import pyplot as plt | from matplotlib import pyplot as plt | ||||
| from graph import * | from graph import * | ||||
| import clifford | |||||
| import numpy as np | |||||
| VOP_COLORS = ["red", "green", "blue", "orange", "yellow", "purple", "black", "white"] | 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 """ | """ Draw a graph with networkx layout """ | ||||
| plt.clf() | plt.clf() | ||||
| g = nx.from_edgelist(edgelist(graph)) | 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] | 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="white", node_size=ns) | ||||
| nx.draw_networkx_nodes(g, pos, node_color=colors, node_size=ns, alpha=.4) | 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_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.axis('off') | ||||
| plt.savefig(filename) | plt.savefig(filename) | ||||
| return pos | |||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||
| g, vops = graph() | g, vops = graph() | ||||