| @@ -1,3 +1,4 @@ | |||||
| *.npy | |||||
| *.cache | *.cache | ||||
| *.pkl | *.pkl | ||||
| @@ -1,42 +0,0 @@ | |||||
| import qi | |||||
| import numpy as np | |||||
| import tables | |||||
| from tqdm import tqdm | |||||
| import itertools as it | |||||
| def get_krontable(): | |||||
| table = np.zeros((24,24,4,4), dtype=complex) | |||||
| for i, j in it.product(range(24), range(24)): | |||||
| u1 = tables.unitaries[i] | |||||
| u2 = tables.unitaries[j] | |||||
| table[i, j, :, :] = np.kron(u1, u2) | |||||
| return table | |||||
| def find(bond, c1, c2, z, krontable): | |||||
| # Figure out the target state | |||||
| state = qi.bond if bond else qi.nobond | |||||
| target = qi.cz * krontable[c1, c2] * state | |||||
| # Choose the sets to search over | |||||
| s1 = z if c1 in z else xrange(24) | |||||
| s2 = z if c2 in z else xrange(24) | |||||
| for bond, c1p, c2p in it.product([0,1], s1, s2): | |||||
| state = qi.bond if bond else qi.nobond | |||||
| trial = krontable[c1p, c2p] * state | |||||
| for phase in range(8): | |||||
| if np.allclose(target, np.exp(1j * phase * np.pi / 4.) * trial): | |||||
| return bond, c1p, c2p | |||||
| raise IndexError | |||||
| z = [tables.find(u, tables.unitaries) for u in qi.id, qi.px, qi.pz, qi.ph, qi.ph.H] | |||||
| krontable = get_krontable() | |||||
| cz_table = np.zeros((2, 24, 24, 3)) | |||||
| for bond, c1, c2 in tqdm(list(it.product([0,1], range(24), range(24)))): | |||||
| cz_table[bond, c1, c2] = find(bond, c1, c2, z, krontable) | |||||
| np.save("cz_table.npy", cz_table) | |||||
| @@ -0,0 +1,105 @@ | |||||
| #!/usr/bin/python | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | |||||
| This program generates lookup tables | |||||
| """ | |||||
| 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") | |||||
| def find_clifford(needle, haystack): | |||||
| """ Find the index of a given u within a list of unitaries, up to a global phase """ | |||||
| for i, t in enumerate(haystack): | |||||
| for phase in range(8): | |||||
| if np.allclose(t, np.exp(1j * phase * np.pi / 4.) * needle): | |||||
| return i | |||||
| raise IndexError | |||||
| def find_cz(bond, c1, c2, z, krontable): | |||||
| """ Find the output of a CZ operation """ | |||||
| # Figure out the target state | |||||
| state = qi.bond if bond else qi.nobond | |||||
| target = qi.cz.dot(krontable[c1, c2].dot(state)) | |||||
| # Choose the sets to search over | |||||
| s1 = z if c1 in z else xrange(24) | |||||
| s2 = z if c2 in z else xrange(24) | |||||
| # Find a match | |||||
| for bond, c1p, c2p in it.product([0, 1], s1, s2): | |||||
| state = qi.bond if bond else qi.nobond | |||||
| trial = krontable[c1p, c2p].dot(state) | |||||
| for phase in range(8): | |||||
| if np.allclose(target, np.exp(1j * phase * np.pi / 4.) * trial): | |||||
| return bond, c1p, c2p | |||||
| # Didn't find anything - this should never happen | |||||
| raise IndexError | |||||
| def compose_u(decomposition): | |||||
| """ Get the unitary representation of a particular decomposition """ | |||||
| matrices = ({"x": qi.sqx, "z": qi.msqz}[c] for c in decomposition) | |||||
| return reduce(np.dot, matrices, np.matrix(np.eye(2, dtype=complex))) | |||||
| def get_unitaries(decompositions): | |||||
| """ 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_conjugation_table(unitaries): | |||||
| """ Construct the conjugation table """ | |||||
| return np.array([find_clifford(hermitian_conjugate(u), unitaries) for u in unitaries]) | |||||
| def get_times_table(unitaries): | |||||
| """ Construct the times-table """ | |||||
| return np.array([[find_clifford(u.dot(v), unitaries) for v in unitaries] | |||||
| for u in tqdm(unitaries)]) | |||||
| def get_krontable(): | |||||
| """ Cache a table of Kronecker products to speed up a little bit """ | |||||
| krontable = np.zeros((24, 24, 4, 4), dtype=complex) | |||||
| for i, j in it.product(range(24), range(24)): | |||||
| krontable[i, j,:,:] = np.kron(unitaries[i], unitaries[j]) | |||||
| return krontable | |||||
| def get_cz_table(unitaries): | |||||
| """ Compute the lookup table for the CZ (A&B eq. 9) """ | |||||
| z = (qi.id, qi.px, qi.pz, qi.ph, hermitian_conjugate(qi.ph)) | |||||
| z = [find_clifford(u, unitaries) for u in z] | |||||
| krontable = get_krontable() | |||||
| cz_table = np.zeros((2, 24, 24, 3)) | |||||
| for bond, c1, c2 in tqdm(list(it.product([0, 1], range(24), range(24)))): | |||||
| cz_table[bond, c1, c2] = find_cz(bond, c1, c2, z, krontable) | |||||
| return cz_table | |||||
| if __name__ == '__main__': | |||||
| unitaries = get_unitaries(DECOMPOSITIONS) | |||||
| conjugation_table = get_conjugation_table(unitaries) | |||||
| times_table = get_times_table(unitaries) | |||||
| cz_table = get_cz_table(unitaries) | |||||
| np.save("tables/unitaries.npy", unitaries) | |||||
| np.save("tables/conjugation_table.npy", conjugation_table) | |||||
| np.save("tables/times_table.npy", times_table) | |||||
| np.save("cz_table.npy", cz_table) | |||||
| @@ -8,28 +8,32 @@ Exposes a few basic QI operators | |||||
| import numpy as np | import numpy as np | ||||
| from scipy.linalg import sqrtm | from scipy.linalg import sqrtm | ||||
| # Operators | |||||
| id = np.matrix(np.eye(2, dtype=complex)) | id = np.matrix(np.eye(2, dtype=complex)) | ||||
| px = np.matrix([[0, 1], [1, 0]], dtype=complex) | px = np.matrix([[0, 1], [1, 0]], dtype=complex) | ||||
| py = np.matrix([[0, -1j], [1j, 0]], dtype=complex) | py = np.matrix([[0, -1j], [1j, 0]], dtype=complex) | ||||
| pz = np.matrix([[1, 0], [0, -1]], dtype=complex) | pz = np.matrix([[1, 0], [0, -1]], dtype=complex) | ||||
| ha = np.matrix([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2) | ha = np.matrix([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2) | ||||
| ph = np.matrix([[1, 0], [0, 1j]], dtype=complex) | ph = np.matrix([[1, 0], [0, 1j]], dtype=complex) | ||||
| sqy = sqrtm(1j * py) | |||||
| msqy = np.matrix(sqrtm(-1j * py)) | |||||
| sqz = np.matrix(sqrtm(1j * pz)) | |||||
| msqz = np.matrix(sqrtm(-1j * pz)) | |||||
| sqx = np.matrix(sqrtm(1j * px)) | |||||
| msqx = np.matrix(sqrtm(-1j * px)) | |||||
| paulis = (px, py, pz) | |||||
| # CZ gate | |||||
| cz = np.matrix(np.eye(4), dtype=complex) | cz = np.matrix(np.eye(4), dtype=complex) | ||||
| cz[3,3]=-1 | cz[3,3]=-1 | ||||
| # States | |||||
| plus = np.matrix([[1],[1]], dtype=complex) / np.sqrt(2) | plus = np.matrix([[1],[1]], dtype=complex) / np.sqrt(2) | ||||
| bond = cz * np.kron(plus, plus) | |||||
| nobond = np.kron(plus, plus) | |||||
| sqy = sqrtm(1j * py) | |||||
| msqy = sqrtm(-1j * py) | |||||
| sqz = sqrtm(1j * pz) | |||||
| msqz = sqrtm(-1j * pz) | |||||
| sqx = sqrtm(1j * px) | |||||
| msqx = sqrtm(-1j * px) | |||||
| paulis = (px, py, pz) | |||||
| # Labelling stuff | |||||
| common_us = id, px, py, pz, ha, ph, sqz, msqz, sqy, msqy, sqx, msqx | 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" | names = "identity", "px", "py", "pz", "hadamard", "phase", "sqz", "msqz", "sqy", "msqy", "sqx", "msqx" | ||||
| by_name = dict(zip(names, common_us)) | by_name = dict(zip(names, common_us)) | ||||
| bond = cz * np.kron(plus, plus) | |||||
| nobond = np.kron(plus, plus) | |||||
| @@ -1,58 +0,0 @@ | |||||
| """ | |||||
| Enumerates the 24 elements of the local Clifford group, providing multiplication and conjugation tables | |||||
| permutations = (id, ha, ph, ha*ph, ha*ph*ha, ha*ph*ha*ph) | |||||
| signs = (id, px, py, pz) | |||||
| unitaries = [p*s for p in permutations for s in signs] | |||||
| """ | |||||
| import numpy as np | |||||
| from tqdm import tqdm | |||||
| import os | |||||
| from functools import reduce | |||||
| import cPickle | |||||
| import qi | |||||
| # TODO: make this more efficient / shorter | |||||
| def find(needle, haystack): | |||||
| """ Find the index of a given u within a list of unitaries, up to a global phase """ | |||||
| for i, t in enumerate(haystack): | |||||
| for phase in range(8): | |||||
| if np.allclose(t, np.exp(1j * phase * np.pi / 4.) * needle): | |||||
| return i | |||||
| raise IndexError | |||||
| def compose_u(decomposition): | |||||
| """ Get the unitary representation of a particular decomposition """ | |||||
| us = ({"x": qi.sqx, "z": qi.msqz}[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 | |||||
| def construct_tables(filename="tables.cache"): | |||||
| """ Constructs / caches multiplication and conjugation tables """ | |||||
| if os.path.exists(filename): | |||||
| return cPickle.load(open(filename, "r")) | |||||
| by_name = {name: find(u, unitaries) for name, u in qi.by_name.items()} | |||||
| get_name = {v:k for k, v in by_name.items()} | |||||
| conjugation_table = [find(u.H, unitaries) | |||||
| for i, u in enumerate(unitaries)] | |||||
| times_table = [[find(u * v, unitaries) for v in unitaries] | |||||
| for u in tqdm(unitaries)] | |||||
| cz_table = None | |||||
| output = by_name, get_name, conjugation_table, times_table, cz_table | |||||
| with open(filename, "w") as f: | |||||
| cPickle.dump(output, f) | |||||
| return output | |||||
| 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") | |||||
| unitaries = [compose_u(d) for d in decompositions] | |||||
| by_name, get_name, conjugation_table, times_table, cz_table = construct_tables() | |||||