Browse Source

Seperate table generation and usage

master
Pete Shadbolt 8 years ago
parent
commit
8fc33ebb40
5 changed files with 121 additions and 111 deletions
  1. +1
    -0
      .gitignore
  2. +0
    -42
      cz_table.py
  3. +105
    -0
      make_tables.py
  4. +15
    -11
      qi.py
  5. +0
    -58
      tables.py

+ 1
- 0
.gitignore View File

@@ -1,3 +1,4 @@
*.npy
*.cache
*.pkl
*.pdf


+ 0
- 42
cz_table.py View File

@@ -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)


+ 105
- 0
make_tables.py View File

@@ -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)

+ 15
- 11
qi.py View File

@@ -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)

+ 0
- 58
tables.py View File

@@ -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()

Loading…
Cancel
Save