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.

162 lines
5.2KB

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. """
  4. This program generates lookup tables
  5. """
  6. import os, json
  7. from functools import reduce
  8. import itertools as it
  9. import qi
  10. import numpy as np
  11. import tempfile
  12. from tqdm import tqdm
  13. decompositions = ("xxxx", "xx", "zzxx", "zz", "zxx", "z", "zzz", "xxz",
  14. "xzx", "xzxxx", "xzzzx", "xxxzx", "xzz", "zzx", "xxx", "x",
  15. "zzzx", "xxzx", "zx", "zxxx", "xxxz", "xzzz", "xz", "xzxx")
  16. #{"UUUU", "UU", "VVUU", "VV",
  17. #"VUU", "V", "VVV", "UUV",
  18. #"UVU", "UVUUU", "UVVVU", "UUUVU",
  19. #"UVV", "VVU", "UUU", "U",
  20. #"VVVU", "UUVU", "VU", "VUUU",
  21. #"UUUV", "UVVV", "UV", "UVUU"};
  22. #string LocCliffOp::get_name (void) const
  23. #{
  24. #static const char* paulinames[] = {"I", "X", "Y", "Z"};
  25. #return string (paulinames[op & 0x03]) + (char) ('A' + op / 4);
  26. #}
  27. ab_names = {0: "IA"}
  28. def find_clifford(needle, haystack):
  29. """ Find the index of a given u within a list of unitaries, up to a global phase """
  30. needle = normalize_global_phase(needle)
  31. for i, t in enumerate(haystack):
  32. if np.allclose(t, needle):
  33. return i
  34. raise IndexError
  35. def normalize_global_phase(m):
  36. """ Normalize the global phase of a matrix """
  37. v = (x for x in m.flatten() if np.abs(x) > 0.001).next()
  38. phase = np.arctan2(v.imag, v.real) % np.pi
  39. rot = np.exp(-1j * phase)
  40. return rot * m if rot * v > 0 else -rot * m
  41. def find_cz(bond, c1, c2, commuters, state_table):
  42. """ Find the output of a CZ operation """
  43. # Figure out the target state
  44. target = qi.cz.dot(state_table[bond, c1, c2])
  45. target = normalize_global_phase(target)
  46. # Choose the sets to search over
  47. s1 = commuters if c1 in commuters else xrange(24)
  48. s2 = commuters if c2 in commuters else xrange(24)
  49. # Find a match
  50. for bond, c1p, c2p in it.product([0, 1], s1, s2):
  51. if np.allclose(target, state_table[bond, c1p, c2p]):
  52. return bond, c1p, c2p
  53. # Didn't find anything - this should never happen
  54. raise IndexError
  55. def compose_u(decomposition):
  56. """ Get the unitary representation of a particular decomposition """
  57. matrices = ({"x": qi.sqx, "z": qi.msqz}[c] for c in decomposition)
  58. output = reduce(np.dot, matrices, np.eye(2, dtype=complex))
  59. return normalize_global_phase(output)
  60. def get_unitaries():
  61. """ The Clifford group """
  62. return [compose_u(d) for d in decompositions]
  63. def get_by_name(unitaries):
  64. """ Get a lookup table of cliffords by name """
  65. return {name: find_clifford(u, unitaries)
  66. for name, u in qi.by_name.items()}
  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 tqdm(unitaries, desc="Building times-table")], 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], range(24), range(24)))
  78. for bond, i, j in tqdm(params, desc="Building state table"):
  79. state = qi.bond if bond else qi.nobond
  80. kp = np.kron(unitaries[i], unitaries[j])
  81. state_table[bond, i, j, :] = normalize_global_phase(
  82. np.dot(kp, state).T)
  83. return state_table
  84. def get_cz_table(unitaries):
  85. """ Compute the lookup table for the CZ (A&B eq. 9) """
  86. # This is the set of Cliffords which commute with CZ
  87. commuters = (qi.id, qi.px, qi.pz, qi.ph, qi.hermitian_conjugate(qi.ph))
  88. commuters = [find_clifford(u, unitaries) for u in commuters]
  89. # Get a cached state table
  90. state_table = get_state_table(unitaries)
  91. # And now build the CZ table
  92. cz_table = np.zeros((2, 24, 24, 3), dtype=int)
  93. rows = list(it.product([0, 1], it.combinations_with_replacement(range(24), 2)))
  94. # CZ is symmetric so we only need combinations
  95. for bond, (c1, c2) in tqdm(rows, desc="Building CZ table"):
  96. newbond, c1p, c2p = find_cz(bond, c1, c2, commuters, state_table)
  97. cz_table[bond, c1, c2] = [newbond, c1p, c2p]
  98. cz_table[bond, c2, c1] = [newbond, c2p, c1p]
  99. return cz_table
  100. # First try to load tables from cache. If that fails, build them from
  101. # scratch and store
  102. os.chdir(tempfile.gettempdir())
  103. try:
  104. unitaries = np.load("unitaries.npy")
  105. conjugation_table = np.load("conjugation_table.npy")
  106. times_table = np.load("times_table.npy")
  107. cz_table = np.load("cz_table.npy")
  108. with open("by_name.json") as f:
  109. by_name = json.load(f)
  110. print "Loaded tables from cache"
  111. except IOError:
  112. # Spend time building the tables
  113. unitaries = get_unitaries()
  114. by_name = get_by_name(unitaries)
  115. conjugation_table = get_conjugation_table(unitaries)
  116. times_table = get_times_table(unitaries)
  117. cz_table = get_cz_table(unitaries)
  118. # Write it all to disk
  119. np.save("unitaries.npy", unitaries)
  120. np.save("conjugation_table.npy", conjugation_table)
  121. np.save("times_table.npy", times_table)
  122. np.save("cz_table.npy", cz_table)
  123. with open("by_name.json", "wb") as f:
  124. json.dump(by_name, f)