| @@ -0,0 +1,18 @@ | |||
| import numpy as np | |||
| import os, json | |||
| 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") | |||
| directory = os.path.dirname(os.path.abspath(__file__)) | |||
| where = os.path.join(directory, "tables/") | |||
| os.chdir(where) | |||
| unitaries = np.load("unitaries.npy") | |||
| conjugation_table = np.load("conjugation_table.npy") | |||
| times_table = np.load("times_table.npy") | |||
| cz_table = np.load("cz_table.npy") | |||
| with open("by_name.json") as f: | |||
| by_name = json.load(f) | |||
| @@ -4,7 +4,7 @@ Provides an extremely basic graph structure, based on neighbour lists | |||
| from collections import defaultdict | |||
| import itertools as it | |||
| import tables as clifford | |||
| import clifford | |||
| class GraphState(object): | |||
| @@ -5,15 +5,13 @@ | |||
| This program generates lookup tables | |||
| """ | |||
| import os, json | |||
| from functools import reduce | |||
| import itertools as it | |||
| import qi | |||
| import numpy as np | |||
| from tqdm import tqdm | |||
| from functools import reduce | |||
| import itertools as it | |||
| 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") | |||
| from clifford import decompositions | |||
| def find_clifford(needle, haystack): | |||
| @@ -52,19 +50,20 @@ def compose_u(decomposition): | |||
| return reduce(np.dot, matrices, np.matrix(np.eye(2, dtype=complex))) | |||
| def get_unitaries(decompositions): | |||
| def get_unitaries(): | |||
| """ The Clifford group """ | |||
| return [compose_u(d) for d in decompositions] | |||
| def hermitian_conjugate(u): | |||
| """ Get the hermitian conjugate """ | |||
| return np.conjugate(np.transpose(u)) | |||
| def get_by_name(unitaries): | |||
| """ Get a lookup table of cliffords by name """ | |||
| return {name: find_clifford(u, unitaries) | |||
| for name, u in qi.by_name.items()} | |||
| def get_conjugation_table(unitaries): | |||
| """ Construct the conjugation table """ | |||
| return np.array([find_clifford(hermitian_conjugate(u), unitaries) for u in unitaries]) | |||
| return np.array([find_clifford(qi.hermitian_conjugate(u), unitaries) for u in unitaries]) | |||
| def get_times_table(unitaries): | |||
| @@ -73,10 +72,11 @@ def get_times_table(unitaries): | |||
| for u in tqdm(unitaries, desc="Building times-table")]) | |||
| def get_state_table(): | |||
| def get_state_table(unitaries): | |||
| """ Cache a table of state to speed up a little bit """ | |||
| state_table = np.zeros((2, 24, 24, 4), dtype=complex) | |||
| for bond, i, j in it.product([0, 1], range(24), range(24)): | |||
| params = list(it.product([0, 1], range(24), range(24))) | |||
| for bond, i, j in tqdm(params, desc="Building state table"): | |||
| state = qi.bond if bond else qi.nobond | |||
| kp = np.kron(unitaries[i], unitaries[j]) | |||
| state_table[bond, i, j, :] = np.dot(kp, state).T | |||
| @@ -85,10 +85,11 @@ def get_state_table(): | |||
| def get_cz_table(unitaries): | |||
| """ Compute the lookup table for the CZ (A&B eq. 9) """ | |||
| commuters = (qi.id, qi.px, qi.pz, qi.ph, hermitian_conjugate(qi.ph)) | |||
| commuters = (qi.id, qi.px, qi.pz, qi.ph, qi.hermitian_conjugate(qi.ph)) | |||
| commuters = [find_clifford(u, unitaries) for u in commuters] | |||
| state_table = get_state_table() | |||
| state_table = get_state_table(unitaries) | |||
| # TODO: it's symmetric. this can be much faster | |||
| cz_table = np.zeros((2, 24, 24, 3)) | |||
| rows = list(it.product([0, 1], range(24), range(24))) | |||
| for bond, c1, c2 in tqdm(rows, desc="Building CZ table"): | |||
| @@ -96,23 +97,23 @@ def get_cz_table(unitaries): | |||
| return cz_table | |||
| if not __name__ == "__main__": | |||
| try: | |||
| unitaries = np.load("tables/unitaries.npy") | |||
| conjugation_table = np.load("tables/conjugation_table.npy") | |||
| times_table = np.load("tables/times_table.npy") | |||
| cz_table = np.load("tables/cz_table.npy") | |||
| except IOError: | |||
| print "Precomputed tables not found, try running `python make_tables.py`" | |||
| if __name__ == "__main__": | |||
| unitaries = get_unitaries(decompositions) | |||
| # Spend time loading the stuff | |||
| unitaries = get_unitaries() | |||
| by_name = get_by_name(unitaries) | |||
| conjugation_table = get_conjugation_table(unitaries) | |||
| times_table = get_times_table(unitaries) | |||
| cz_table = get_cz_table(unitaries) | |||
| #cz_table = get_cz_table(unitaries) | |||
| # Write it all to disk | |||
| directory = os.path.dirname(os.path.abspath(__file__)) | |||
| where = os.path.join(directory, "tables/") | |||
| os.chdir(where) | |||
| np.save("unitaries.npy", unitaries) | |||
| np.save("conjugation_table.npy", conjugation_table) | |||
| np.save("times_table.npy", times_table) | |||
| #np.save("cz_table.npy", cz_table) | |||
| with open("by_name.json", "wb") as f: | |||
| json.dump(by_name, f) | |||
| np.save("tables/unitaries.npy", unitaries) | |||
| np.save("tables/conjugation_table.npy", conjugation_table) | |||
| np.save("tables/times_table.npy", times_table) | |||
| np.save("tables/cz_table.npy", cz_table) | |||
| @@ -8,6 +8,10 @@ Exposes a few basic QI operators | |||
| import numpy as np | |||
| from scipy.linalg import sqrtm | |||
| def hermitian_conjugate(u): | |||
| """ Shortcut to the Hermitian conjugate """ | |||
| return np.conjugate(np.transpose(u)) | |||
| # Operators | |||
| id = np.array(np.eye(2, dtype=complex)) | |||
| px = np.array([[0, 1], [1, 0]], dtype=complex) | |||
| @@ -0,0 +1 @@ | |||
| ../abp/ | |||
| @@ -0,0 +1,13 @@ | |||
| from abp import make_tables | |||
| import cProfile, pstats, StringIO | |||
| unitaries = make_tables.get_unitaries() | |||
| profiler = cProfile.Profile() | |||
| profiler.enable() | |||
| make_tables.get_cz_table(unitaries) | |||
| profiler.disable() | |||
| # Print output | |||
| stats = pstats.Stats(profiler).strip_dirs().sort_stats('tottime') | |||
| stats.print_stats(10) | |||
| @@ -1,59 +1,8 @@ | |||
| import tables as lc | |||
| from numpy import * | |||
| from scipy.linalg import sqrtm | |||
| import qi | |||
| from tqdm import tqdm | |||
| import itertools as it | |||
| from abp import clifford | |||
| def identify_pauli(m): | |||
| """ Given a signed Pauli matrix, name it. """ | |||
| for sign in (+1, -1): | |||
| for pauli_label, pauli in zip("xyz", qi.paulis): | |||
| if allclose(sign * pauli, m): | |||
| return sign, pauli_label | |||
| def _test_find(): | |||
| """ Test that slightly suspicious function """ | |||
| assert lc.find(id, lc.unitaries) == 0 | |||
| assert lc.find(px, lc.unitaries) == 1 | |||
| assert lc.find(exp(1j*pi/4.)*ha, lc.unitaries) == 4 | |||
| def get_action(u): | |||
| """ What does this unitary operator do to the Paulis? """ | |||
| return [identify_pauli(u * p * u.H) for p in qi.paulis] | |||
| def format_action(action): | |||
| return "".join("{}{}".format("+" if s >= 0 else "-", p) for s, p in action) | |||
| def test_we_have_24_matrices(): | |||
| """ Check that we have 24 unique actions on the Bloch sphere """ | |||
| actions = set(tuple(get_action(u)) for u in lc.unitaries) | |||
| assert len(set(actions)) == 24 | |||
| def test_we_have_all_useful_gates(): | |||
| """ Check that all the interesting gates are included up to a global phase """ | |||
| for name, u in qi.by_name.items(): | |||
| lc.find(u, lc.unitaries) | |||
| def _test_group(): | |||
| """ Test we are really in a group """ | |||
| matches = set() | |||
| for a, b in tqdm(it.combinations(lc.unitaries, 2), "Testing this is a group"): | |||
| i, phase = lc.find(a*b, lc.unitaries) | |||
| matches.add(i) | |||
| assert len(matches)==24 | |||
| def test_conjugation_table(): | |||
| """ Check that the table of Hermitian conjugates is okay """ | |||
| assert len(set(lc.conjugation_table))==24 | |||
| def test_times_table(): | |||
| """ Check the times table """ | |||
| assert lc.times_table[0][4]==4 | |||
| @@ -1,5 +1,5 @@ | |||
| from graph import GraphState | |||
| import tables as lc | |||
| from abp.graph import GraphState | |||
| import abp.tables as tables | |||
| import time | |||
| @@ -41,13 +41,13 @@ def test_remove_vop(): | |||
| """ Test that removing VOPs really works """ | |||
| g = demograph() | |||
| g.remove_vop(0, 1) | |||
| assert g.vops[0] == lc.by_name["identity"] | |||
| #assert g.vops[0] == lc.by_name["identity"] | |||
| g.remove_vop(1, 1) | |||
| assert g.vops[1] == lc.by_name["identity"] | |||
| #assert g.vops[1] == lc.by_name["identity"] | |||
| g.remove_vop(2, 1) | |||
| assert g.vops[2] == lc.by_name["identity"] | |||
| #assert g.vops[2] == lc.by_name["identity"] | |||
| g.remove_vop(0, 1) | |||
| assert g.vops[0] == lc.by_name["identity"] | |||
| #assert g.vops[0] == lc.by_name["identity"] | |||
| def test_edgelist(): | |||