Anders and Briegel in Python
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

186 lines
6.2KB

  1. # -*- coding: utf-8 -*-
  2. """
  3. This program generates lookup tables and handles the Clifford group
  4. """
  5. import os, json, tempfile, os, sys, json, string
  6. from functools import reduce
  7. import itertools as it
  8. import numpy as np
  9. from tqdm import tqdm
  10. # import argparse
  11. import qi
  12. decompositions = ("xxxx", "xx", "zzxx", "zz", "zxx", "z", "zzz", "xxz",
  13. "xzx", "xzxxx", "xzzzx", "xxxzx", "xzz", "zzx", "xxx", "x",
  14. "zzzx", "xxzx", "zx", "zxxx", "xxxz", "xzzz", "xz", "xzxx")
  15. def get_name(i):
  16. """ Get the human-readable name of this clifford """
  17. return "IXYZ"[i & 0x03] + "ABCDEF"[i / 4]
  18. def find_clifford(needle, haystack):
  19. """ Find the index of a given u within a list of unitaries, up to a global phase """
  20. needle = qi.normalize_global_phase(needle)
  21. for i, t in enumerate(haystack):
  22. if np.allclose(t, needle):
  23. return i
  24. raise IndexError
  25. def find_cz(bond, c1, c2, commuters, state_table):
  26. """ Find the output of a CZ operation """
  27. # Figure out the target state
  28. target = qi.cz.dot(state_table[bond, c1, c2])
  29. target = qi.normalize_global_phase(target)
  30. # Choose the sets to search over
  31. s1 = commuters if c1 in commuters else xrange(24)
  32. s2 = commuters if c2 in commuters else xrange(24)
  33. # Find a match
  34. for bondp, c1p, c2p in it.product([0, 1], s1, s2):
  35. if np.allclose(target, state_table[bondp, c1p, c2p]):
  36. return bondp, c1p, c2p
  37. # Didn't find anything - this should never happen
  38. raise IndexError
  39. def compose_u(decomposition):
  40. """ Get the unitary representation of a particular decomposition """
  41. matrices = ({"x": qi.msqx, "z": qi.sqz}[c] for c in decomposition)
  42. output = reduce(np.dot, matrices, np.eye(2, dtype=complex))
  43. return qi.normalize_global_phase(output)
  44. def get_unitaries():
  45. """ The Clifford group """
  46. return [compose_u(d) for d in decompositions]
  47. def get_by_name(unitaries):
  48. """ Get a lookup table of cliffords by name """
  49. a = {name: find_clifford(u, unitaries)
  50. for name, u in qi.by_name.items()}
  51. a.update({get_name(i): i for i in range(24)})
  52. a.update({i: i for i in range(24)})
  53. return a
  54. def get_conjugation_table(unitaries):
  55. """ Construct the conjugation table """
  56. return np.array([find_clifford(qi.hermitian_conjugate(u), unitaries) for u in unitaries], dtype=int)
  57. def get_times_table(unitaries):
  58. """ Construct the times-table """
  59. return np.array([[find_clifford(u.dot(v), unitaries) for v in unitaries]
  60. for u in tqdm(unitaries, desc="Building times-table")], dtype=int)
  61. def get_state_table(unitaries):
  62. """ Cache a table of state to speed up a little bit """
  63. state_table = np.zeros((2, 24, 24, 4), dtype=complex)
  64. params = list(it.product([0, 1], range(24), range(24)))
  65. for bond, i, j in tqdm(params, desc="Building state table"):
  66. state = qi.bond if bond else qi.nobond
  67. kp = np.kron(unitaries[i], unitaries[j])
  68. state_table[bond, i, j, :] = qi.normalize_global_phase(
  69. np.dot(kp, state).T)
  70. return state_table
  71. def get_commuters(unitaries):
  72. """ Get the indeces of gates which commute with CZ """
  73. commuters = (qi.id, qi.pz, qi.ph, qi.hermitian_conjugate(qi.ph))
  74. return [find_clifford(u, unitaries) for u in commuters]
  75. def get_cz_table(unitaries):
  76. """ Compute the lookup table for the CZ (A&B eq. 9) """
  77. # Get a cached state table and a list of gates which commute with CZ
  78. commuters = get_commuters(unitaries)
  79. print commuters
  80. state_table = get_state_table(unitaries)
  81. # And now build the CZ table
  82. cz_table = np.zeros((2, 24, 24, 3), dtype=int)
  83. rows = list(
  84. it.product([0, 1], it.combinations_with_replacement(range(24), 2)))
  85. # CZ is symmetric so we only need combinations
  86. for bond, (c1, c2) in tqdm(rows, desc="Building CZ table"):
  87. newbond, c1p, c2p = find_cz(
  88. bond, c1, c2, commuters, state_table)
  89. cz_table[bond, c1, c2] = [newbond, c1p, c2p]
  90. cz_table[bond, c2, c1] = [newbond, c2p, c1p]
  91. return cz_table
  92. def write_javascript_tables():
  93. """ Write the tables to javascript files for consumption in the browser """
  94. path = os.path.dirname(sys.argv[0])
  95. path = os.path.split(path)[0]
  96. with open(os.path.join(path, "static/scripts/tables.js"), "w") as f:
  97. f.write("var tables = {\n");
  98. f.write("\tdecompositions : {},\n"\
  99. .format(json.dumps(decompositions)))
  100. f.write("\tconjugation_table : {},\n"\
  101. .format(json.dumps(conjugation_table.tolist())))
  102. f.write("\ttimes_table : {},\n"\
  103. .format(json.dumps(times_table.tolist())))
  104. f.write("\tcz_table : {},\n"\
  105. .format(json.dumps(cz_table.tolist())))
  106. f.write("\tclifford : {}\n"\
  107. .format(json.dumps(by_name)))
  108. f.write("};");
  109. # First try to load tables from cache. If that fails, build them from
  110. # scratch and store in /tmp/
  111. tempdir = tempfile.gettempdir()
  112. temp = lambda filename: os.path.join(tempdir, filename)
  113. try:
  114. if __name__ == "__main__":
  115. raise IOError
  116. # Parse command line args
  117. # parser = argparse.ArgumentParser()
  118. # parser.add_argument("-l", "--legacy", help="Use legacy CZ table", action="store_true", default=False)
  119. # args = parser.parse_args()
  120. legacy_cz = False
  121. unitaries = np.load(temp("unitaries.npy"))
  122. conjugation_table = np.load(temp("conjugation_table.npy"))
  123. times_table = np.load(temp("times_table.npy"))
  124. if legacy_cz:
  125. import anders_cz
  126. cz_table = anders_cz.cz_table
  127. else:
  128. cz_table = np.load(temp("cz_table.npy"))
  129. with open(temp("by_name.json")) as f:
  130. by_name = json.load(f)
  131. except IOError:
  132. # Spend time building the tables
  133. unitaries = get_unitaries()
  134. by_name = get_by_name(unitaries)
  135. conjugation_table = get_conjugation_table(unitaries)
  136. times_table = get_times_table(unitaries)
  137. cz_table = get_cz_table(unitaries)
  138. # Write it all to disk
  139. np.save(temp("unitaries.npy"), unitaries)
  140. np.save(temp("conjugation_table.npy"), conjugation_table)
  141. np.save(temp("times_table.npy"), times_table)
  142. np.save(temp("cz_table.npy"), cz_table)
  143. write_javascript_tables()
  144. with open(temp("by_name.json"), "wb") as f:
  145. json.dump(by_name, f)