Anders and Briegel in Python
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

221 行
7.6KB

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