Anders and Briegel in Python
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

218 líneas
7.4KB

  1. """
  2. This program computes lookup tables and stores them as tables.py and tables.js
  3. # TODO: clifford naming discrepancy
  4. """
  5. import numpy as np
  6. import itertools as it
  7. from functools import reduce
  8. from os.path import dirname, join, split
  9. import json
  10. from . import qi, clifford
  11. DECOMPOSITIONS = (
  12. "xxxx", "xx", "zzxx", "zz", "zxx", "z", "zzz", "xxz", "xzx", "xzxxx", "xzzzx",
  13. "xxxzx", "xzz", "zzx", "xxx", "x", "zzzx", "xxzx", "zx", "zxxx", "xxxz", "xzzz", "xz", "xzxx")
  14. PY_TEMPLATE = """\
  15. import numpy as np
  16. # Define lookup tables
  17. ir2 = 1/np.sqrt(2)
  18. decompositions = {decompositions}
  19. conjugation_table = np.array({conjugation_table}, dtype=int)
  20. times_table = np.array({times_table}, dtype=int)
  21. cz_table = np.array({cz_table}, dtype=int)
  22. by_name = {by_name}
  23. measurement_table = np.array({measurement_table}, dtype=int)
  24. unitaries_real = np.array({unitaries_real}, dtype=complex)
  25. unitaries_imag = np.array({unitaries_imag}, dtype=complex)
  26. # Reconstruct
  27. unitaries = unitaries_real + 1j*unitaries_imag
  28. """
  29. def find_clifford(needle, haystack):
  30. """ Find the index of a given u within a list of unitaries, up to a global phase """
  31. needle = qi.normalize_global_phase(needle)
  32. for i, t in enumerate(haystack):
  33. if np.allclose(t, needle):
  34. return i
  35. raise IndexError
  36. def find_cz(bond, c1, c2, commuters, state_table):
  37. """ Find the output of a CZ operation """
  38. # Figure out the target state
  39. target = qi.cz.dot(state_table[bond, c1, c2])
  40. target = qi.normalize_global_phase(target)
  41. # Choose the sets to search over
  42. s1 = commuters if c1 in commuters else range(24)
  43. s2 = commuters if c2 in commuters else range(24)
  44. # Find a match
  45. for bondp, c1p, c2p in it.product([0, 1], s1, s2):
  46. if np.allclose(target, state_table[bondp, c1p, c2p]):
  47. return bondp, c1p, c2p
  48. # Didn't find anything - this should never happen
  49. raise IndexError
  50. def compose_u(decomposition):
  51. """ Get the unitary representation of a particular decomposition """
  52. matrices = ({"x": qi.msqx, "z": qi.sqz}[c] for c in decomposition)
  53. output = reduce(np.dot, matrices, np.eye(2, dtype=complex))
  54. return qi.normalize_global_phase(output)
  55. def get_unitaries():
  56. """ The Clifford group """
  57. return [compose_u(d) for d in DECOMPOSITIONS]
  58. def get_by_name(unitaries, conjugation_table):
  59. """ Get a lookup table of cliffords by name """
  60. a = {name: find_clifford(u, unitaries)
  61. for name, u in list(qi.by_name.items())}
  62. a.update({key + "_h": conjugation_table[value]
  63. for key, value in list(a.items())})
  64. a.update({clifford.get_name(i): i for i in range(24)})
  65. a.update({i: i for i in range(24)})
  66. return a
  67. def get_conjugation_table(unitaries):
  68. """ Construct the conjugation table """
  69. return np.array([find_clifford(qi.hermitian_conjugate(u), unitaries) for u in unitaries], dtype=int)
  70. def get_times_table(unitaries):
  71. """ Construct the times-table """
  72. return np.array([[find_clifford(u.dot(v), unitaries) for v in unitaries]
  73. for u in unitaries], dtype=int)
  74. def get_state_table(unitaries):
  75. """ Cache a table of state to speed up a little bit """
  76. state_table = np.zeros((2, 24, 24, 4), dtype=complex)
  77. params = list(it.product([0, 1], list(range(24)), list(range(24))))
  78. for bond, i, j in params:
  79. state = qi.bond if bond else qi.nobond
  80. kp = np.kron(unitaries[i], unitaries[j])
  81. state_table[bond, i, j, :] = qi.normalize_global_phase(
  82. np.dot(kp, state).T)
  83. return state_table
  84. def get_measurement_entry(operator, unitary):
  85. """
  86. Any Clifford group unitary will map an operator A in {I, X, Y, Z}
  87. to an operator B in +-{I, X, Y, Z}. This finds that mapping.
  88. """
  89. matrices = ({"x": qi.msqx, "z": qi.sqz}[c]
  90. for c in DECOMPOSITIONS[unitary])
  91. unitary = reduce(np.dot, matrices, np.eye(2, dtype=complex))
  92. operator = qi.operators[operator]
  93. new_operator = reduce(np.dot,
  94. (unitary, operator, qi.hermitian_conjugate(unitary)))
  95. for i, o in enumerate(qi.operators):
  96. if np.allclose(o, new_operator):
  97. return i, 1
  98. elif np.allclose(o, -new_operator):
  99. return i, -1
  100. raise IndexError
  101. def get_measurement_table():
  102. """
  103. Compute a table of transform * operation * transform^dagger
  104. This is pretty unintelligible right now, we should probably compute the phase from unitaries instead
  105. """
  106. measurement_table = np.zeros((4, 24, 2), dtype=int)
  107. for operator, unitary in it.product(list(range(4)), list(range(24))):
  108. measurement_table[operator, unitary] = get_measurement_entry(
  109. operator, unitary)
  110. return measurement_table
  111. def get_commuters(unitaries):
  112. """ Get the indeces of gates which commute with CZ """
  113. commuters = (qi.id, qi.pz, qi.ph, qi.hermitian_conjugate(qi.ph))
  114. return [find_clifford(u, unitaries) for u in commuters]
  115. def get_cz_table(unitaries):
  116. """ Compute the lookup table for the CZ (A&B eq. 9) """
  117. # Get a cached state table and a list of gates which commute with CZ
  118. commuters = get_commuters(unitaries)
  119. state_table = get_state_table(unitaries)
  120. # And now build the CZ table
  121. cz_table = np.zeros((2, 24, 24, 3), dtype=int)
  122. rows = list(
  123. it.product([0, 1], it.combinations_with_replacement(list(range(24)), 2)))
  124. # CZ is symmetric so we only need combinations
  125. for bond, (c1, c2) in rows:
  126. newbond, c1p, c2p = find_cz(
  127. bond, c1, c2, commuters, state_table)
  128. cz_table[bond, c1, c2] = [newbond, c1p, c2p]
  129. cz_table[bond, c2, c1] = [newbond, c2p, c1p]
  130. return cz_table
  131. def get_display_table(unitaries):
  132. """ Used to display VOPs in a human readable style """
  133. for u in unitaries:
  134. c = qi.CircuitModel(1)
  135. c.act_local_rotation(0, u)
  136. state = c.state.round(2)
  137. print("{:.2f}, {:.2f}".format(state[0][0], state[1][0]))
  138. def compute_everything():
  139. """ Compute all lookup tables """
  140. unitaries = get_unitaries()
  141. conjugation_table = get_conjugation_table(unitaries)
  142. return {"decompositions": DECOMPOSITIONS,
  143. "unitaries": unitaries,
  144. "by_name": get_by_name(unitaries, conjugation_table),
  145. "conjugation_table": conjugation_table,
  146. "times_table": get_times_table(unitaries),
  147. "cz_table": get_cz_table(unitaries),
  148. "measurement_table": get_measurement_table()}
  149. def human_readable(data):
  150. """ Format the data """
  151. unitaries = np.array(data["unitaries"])
  152. return {"decompositions": json.dumps(DECOMPOSITIONS),
  153. "unitaries_real": json.dumps(unitaries.real.round(5).tolist()).replace("0.70711", "ir2"),
  154. "unitaries_imag": json.dumps(unitaries.imag.round(5).tolist()).replace("0.70711", "ir2"),
  155. "conjugation_table": json.dumps(data["conjugation_table"].tolist()),
  156. "times_table": json.dumps(data["times_table"].tolist()),
  157. "cz_table": json.dumps(data["cz_table"].tolist()),
  158. "by_name": json.dumps(data["by_name"]),
  159. "measurement_table": json.dumps(data["measurement_table"].tolist())}
  160. def write_python(data):
  161. """ Write the tables to a python module """
  162. path = join(dirname(__file__), "tables.py")
  163. content = PY_TEMPLATE.format(**data)
  164. with open(path, "w") as f:
  165. f.write(content)
  166. if __name__ == '__main__':
  167. get_display_table(get_unitaries())
  168. data = compute_everything()
  169. data = human_readable(data)
  170. write_python(data)