From 8fc33ebb402208f64719efba868e19990996d4f6 Mon Sep 17 00:00:00 2001 From: Pete Shadbolt Date: Fri, 6 May 2016 00:28:16 +0100 Subject: [PATCH] Seperate table generation and usage --- .gitignore | 1 + cz_table.py | 42 -------------------- make_tables.py | 105 +++++++++++++++++++++++++++++++++++++++++++++++++ qi.py | 26 ++++++------ tables.py | 58 --------------------------- 5 files changed, 121 insertions(+), 111 deletions(-) delete mode 100644 cz_table.py create mode 100644 make_tables.py delete mode 100644 tables.py diff --git a/.gitignore b/.gitignore index adff77f..d22e22f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.npy *.cache *.pkl *.pdf diff --git a/cz_table.py b/cz_table.py deleted file mode 100644 index 0135e51..0000000 --- a/cz_table.py +++ /dev/null @@ -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) - diff --git a/make_tables.py b/make_tables.py new file mode 100644 index 0000000..07558cd --- /dev/null +++ b/make_tables.py @@ -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) diff --git a/qi.py b/qi.py index fb35bf6..c57c963 100644 --- a/qi.py +++ b/qi.py @@ -8,28 +8,32 @@ Exposes a few basic QI operators import numpy as np from scipy.linalg import sqrtm +# Operators id = np.matrix(np.eye(2, dtype=complex)) px = np.matrix([[0, 1], [1, 0]], dtype=complex) py = np.matrix([[0, -1j], [1j, 0]], dtype=complex) pz = np.matrix([[1, 0], [0, -1]], dtype=complex) ha = np.matrix([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2) 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[3,3]=-1 +# States 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 names = "identity", "px", "py", "pz", "hadamard", "phase", "sqz", "msqz", "sqy", "msqy", "sqx", "msqx" by_name = dict(zip(names, common_us)) - -bond = cz * np.kron(plus, plus) -nobond = np.kron(plus, plus) diff --git a/tables.py b/tables.py deleted file mode 100644 index a956f25..0000000 --- a/tables.py +++ /dev/null @@ -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()