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.

clifford.py 4.8KB

8 years ago
8 years ago
8 years ago
8 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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. from . 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. def find_clifford(needle, haystack):
  17. """ Find the index of a given u within a list of unitaries, up to a global phase """
  18. needle = normalize_global_phase(needle)
  19. for i, t in enumerate(haystack):
  20. if np.allclose(t, needle):
  21. return i
  22. raise IndexError
  23. def normalize_global_phase(m):
  24. """ Normalize the global phase of a matrix """
  25. v = (x for x in m.flatten() if np.abs(x) > 0.001).next()
  26. phase = np.arctan2(v.imag, v.real) % np.pi
  27. rot = np.exp(-1j * phase)
  28. return rot * m if rot * v > 0 else -rot * m
  29. def find_cz(bond, c1, c2, commuters, state_table):
  30. """ Find the output of a CZ operation """
  31. # Figure out the target state
  32. target = qi.cz.dot(state_table[bond, c1, c2])
  33. target = normalize_global_phase(target)
  34. # Choose the sets to search over
  35. s1 = commuters if c1 in commuters else xrange(24)
  36. s2 = commuters if c2 in commuters else xrange(24)
  37. # Find a match
  38. for bond, c1p, c2p in it.product([0, 1], s1, s2):
  39. if np.allclose(target, state_table[bond, c1p, c2p]):
  40. return bond, c1p, c2p
  41. # Didn't find anything - this should never happen
  42. raise IndexError
  43. def compose_u(decomposition):
  44. """ Get the unitary representation of a particular decomposition """
  45. matrices = ({"x": qi.sqx, "z": qi.msqz}[c] for c in decomposition)
  46. output = reduce(np.dot, matrices, np.eye(2, dtype=complex))
  47. return normalize_global_phase(output)
  48. def get_unitaries():
  49. """ The Clifford group """
  50. return [compose_u(d) for d in decompositions]
  51. def get_by_name(unitaries):
  52. """ Get a lookup table of cliffords by name """
  53. return {name: find_clifford(u, unitaries)
  54. for name, u in qi.by_name.items()}
  55. def get_conjugation_table(unitaries):
  56. """ Construct the conjugation table """
  57. return np.array([find_clifford(qi.hermitian_conjugate(u), unitaries) for u in unitaries])
  58. def get_times_table(unitaries):
  59. """ Construct the times-table """
  60. return np.array([[find_clifford(u.dot(v), unitaries) for v in unitaries]
  61. for u in tqdm(unitaries, desc="Building times-table")])
  62. def get_state_table(unitaries):
  63. """ Cache a table of state to speed up a little bit """
  64. state_table = np.zeros((2, 24, 24, 4), dtype=complex)
  65. params = list(it.product([0, 1], range(24), range(24)))
  66. for bond, i, j in tqdm(params, desc="Building state table"):
  67. state = qi.bond if bond else qi.nobond
  68. kp = np.kron(unitaries[i], unitaries[j])
  69. state_table[bond, i, j, :] = normalize_global_phase(
  70. np.dot(kp, state).T)
  71. return state_table
  72. def get_cz_table(unitaries):
  73. """ Compute the lookup table for the CZ (A&B eq. 9) """
  74. # This is the set of Cliffords which commute with CZ
  75. commuters = (qi.id, qi.px, qi.pz, qi.ph, qi.hermitian_conjugate(qi.ph))
  76. commuters = [find_clifford(u, unitaries) for u in commuters]
  77. # Get a cached state table
  78. state_table = get_state_table(unitaries)
  79. # And now build the CZ table
  80. cz_table = np.zeros((2, 24, 24, 3))
  81. rows = list(it.product([0, 1], it.combinations(range(24), 2)))
  82. # CZ is symmetric so we only need combinations
  83. for bond, (c1, c2) in tqdm(rows, desc="Building CZ table"):
  84. newbond, c1p, c2p = find_cz(bond, c1, c2, commuters, state_table)
  85. cz_table[bond, c1, c2] = [newbond, c1p, c2p]
  86. cz_table[bond, c2, c1] = [newbond, c2p, c1p]
  87. return cz_table
  88. # First try to load tables from cache. If that fails, build them from
  89. # scratch and store
  90. os.chdir(tempfile.gettempdir())
  91. try:
  92. unitaries = np.load("unitaries.npy")
  93. conjugation_table = np.load("conjugation_table.npy")
  94. times_table = np.load("times_table.npy")
  95. cz_table = np.load("cz_table.npy")
  96. with open("by_name.json") as f:
  97. by_name = json.load(f)
  98. print "Loaded tables from cache"
  99. except IOError:
  100. # Spend time building the tables
  101. unitaries = get_unitaries()
  102. by_name = get_by_name(unitaries)
  103. conjugation_table = get_conjugation_table(unitaries)
  104. times_table = get_times_table(unitaries)
  105. cz_table = get_cz_table(unitaries)
  106. # Write it all to disk
  107. np.save("unitaries.npy", unitaries)
  108. np.save("conjugation_table.npy", conjugation_table)
  109. np.save("times_table.npy", times_table)
  110. np.save("cz_table.npy", cz_table)
  111. with open("by_name.json", "wb") as f:
  112. json.dump(by_name, f)