Anders and Briegel in Python
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

323 lignes
11KB

  1. """
  2. Provides an extremely basic graph structure, based on key/value pairs
  3. """
  4. import itertools as it
  5. import json
  6. import qi, clifford, util
  7. import random
  8. class GraphState(object):
  9. def __init__(self, nodes=[]):
  10. self.adj, self.node = {}, {}
  11. self.add_nodes(nodes)
  12. def add_node(self, v, **kwargs):
  13. """ Add a node """
  14. assert not v in self.node
  15. self.adj[v] = {}
  16. self.node[v] = {"vop": clifford.by_name["hadamard"]}
  17. self.node[v].update(kwargs)
  18. def add_nodes(self, nodes):
  19. """ Add a buncha nodes """
  20. for n in nodes:
  21. self.add_node(n)
  22. def add_edge(self, v1, v2, data={}):
  23. """ Add an edge between two vertices in the self """
  24. assert v1 != v2
  25. self.adj[v1][v2] = data
  26. self.adj[v2][v1] = data
  27. def add_edges(self, edges):
  28. """ Add a buncha edges """
  29. for (v1, v2) in edges:
  30. self.add_edge(v1, v2)
  31. def del_edge(self, v1, v2):
  32. """ Delete an edge between two vertices in the self """
  33. del self.adj[v1][v2]
  34. del self.adj[v2][v1]
  35. def has_edge(self, v1, v2):
  36. """ Test existence of an edge between two vertices in the self """
  37. return v2 in self.adj[v1]
  38. def toggle_edge(self, v1, v2):
  39. """ Toggle an edge between two vertices in the self """
  40. if self.has_edge(v1, v2):
  41. self.del_edge(v1, v2)
  42. else:
  43. self.add_edge(v1, v2)
  44. def edgelist(self):
  45. """ Describe a graph as an edgelist """
  46. # TODO: inefficient
  47. edges = set(tuple(sorted((i, n)))
  48. for i, v in self.adj.items()
  49. for n in v)
  50. return tuple(edges)
  51. def remove_vop(self, a, avoid):
  52. """ Reduces VOP[a] to the identity """
  53. others = set(self.adj[a]) - {avoid}
  54. #TODO: this is a hack for determinsim. remove
  55. swap_qubit = min(others) if others else avoid
  56. #swap_qubit = others.pop() if others else avoid # TODO: maybe this is the only problematic part
  57. #print "SWAPPING WITH {} (options were {})".format(swap_qubit, tuple(others))
  58. for v in reversed(clifford.decompositions[self.node[a]["vop"]]):
  59. if v == "x":
  60. self.local_complementation(a, "U ->")
  61. else:
  62. self.local_complementation(swap_qubit, "V ->")
  63. def local_complementation(self, v, prefix=""):
  64. """ As defined in LISTING 1 of Anders & Briegel """
  65. for i, j in it.combinations(self.adj[v], 2):
  66. self.toggle_edge(i, j)
  67. self.node[v]["vop"] = clifford.times_table[
  68. self.node[v]["vop"], clifford.by_name["msqx_h"]]
  69. for i in self.adj[v]:
  70. self.node[i]["vop"] = clifford.times_table[
  71. self.node[i]["vop"], clifford.by_name["sqz_h"]]
  72. def act_local_rotation(self, v, op):
  73. """ Act a local rotation """
  74. rotation = clifford.by_name[str(op)]
  75. self.node[v]["vop"] = clifford.times_table[
  76. rotation, self.node[v]["vop"]]
  77. def act_local_rotation2(self, v, op):
  78. """ Act a local rotation """
  79. rotation = clifford.by_name[str(op)]
  80. self.node[v]["vop"] = clifford.times_table[
  81. self.node[v]["vop"], rotation]
  82. def act_hadamard(self, qubit):
  83. """ Shorthand """
  84. self.act_local_rotation(qubit, 10)
  85. def lonely(self, a, b):
  86. """ Is this qubit lonely ? """
  87. return len(self.adj[a]) > (b in self.adj[a])
  88. def act_cz(self, a, b):
  89. """ Act a controlled-phase gate on two qubits """
  90. if self.lonely(a, b):
  91. self.remove_vop(a, b)
  92. if self.lonely(b, a):
  93. self.remove_vop(b, a)
  94. if self.lonely(a, b) and not clifford.is_diagonal(self.node[a]["vop"]):
  95. self.remove_vop(a, b)
  96. edge = self.has_edge(a, b)
  97. va = self.node[a]["vop"]
  98. vb = self.node[b]["vop"]
  99. new_edge, self.node[a]["vop"], self.node[b]["vop"] = \
  100. clifford.cz_table[int(edge), va, vb]
  101. if new_edge != edge:
  102. self.toggle_edge(a, b)
  103. def measure(self, node, basis, force=None):
  104. """ Measure in an arbitrary basis """
  105. basis = clifford.by_name[basis]
  106. ha = clifford.conjugation_table[self.node[node]["vop"]]
  107. #basis, phase = clifford.conjugate(basis, ha)
  108. basis, phase = clifford.conjugate(basis, self.node[node]["vop"])
  109. print "MEASURE"
  110. print "Op: {} Phase: {}".format(basis, phase)
  111. # Flip a coin
  112. result = force if force!=None else random.choice([0, 1])
  113. # Flip the result if we have negative phase
  114. if phase == -1:
  115. result = not result
  116. if basis == clifford.by_name["px"]:
  117. result = self.measure_x(node, result)
  118. elif basis == clifford.by_name["py"]:
  119. result = self.measure_y(node, result)
  120. elif basis == clifford.by_name["pz"]:
  121. result = self.measure_z(node, result)
  122. else:
  123. raise ValueError("You can only measure in {X,Y,Z}")
  124. # Flip the result if we have negative phase
  125. if phase == -1:
  126. result = not result
  127. return result
  128. def toggle_edges(self, a, b):
  129. """ Toggle edges between vertex sets a and b """
  130. # TODO: i'm pretty sure this is just a single-line it.combinations or equiv
  131. done = set()
  132. for i, j in it.product(a, b):
  133. if i != j and not (i, j) in done:
  134. done.add((i, j))
  135. done.add((j, i))
  136. self.toggle_edge(i, j)
  137. def measure_x(self, node, result):
  138. """ Measure the graph in the X-basis """
  139. if len(self.adj[node]) == 0:
  140. print "gXm{},D".format(node),
  141. return 0
  142. print "gXm{},{:d}".format(node, result),
  143. # Pick a vertex
  144. #friend = next(self.adj[node].iterkeys())
  145. # TODO this is enforced determinism for testing purposes
  146. friend = sorted(self.adj[node].keys())[0]
  147. # Update the VOPs. TODO: pretty ugly
  148. if result:
  149. # Do a z on all ngb(vb) \ ngb(v) \ {v}, and some other stuff
  150. self.act_local_rotation(node, "pz")
  151. self.act_local_rotation(friend, "msqy")
  152. for n in set(self.adj[friend]) - set(self.adj[node]) - {node}:
  153. self.act_local_rotation(n, "pz")
  154. else:
  155. # Do a z on all ngb(v) \ ngb(vb) \ {vb}, and sqy on the friend
  156. self.act_local_rotation(friend, "sqy")
  157. for n in set(self.adj[node]) - set(self.adj[friend]) - {friend}:
  158. self.act_local_rotation(n, "pz")
  159. # Toggle the edges. TODO: Yuk. Just awful!
  160. a = set(self.adj[node].keys())
  161. b = set(self.adj[friend].keys())
  162. self.toggle_edges(a, b)
  163. intersection = a & b
  164. for i, j in it.combinations(intersection, 2):
  165. self.toggle_edge(i, j)
  166. for n in a - {friend}:
  167. self.toggle_edge(friend, n)
  168. return result
  169. def measure_y(self, node, result):
  170. """ Measure the graph in the Y-basis """
  171. print "gYm{},{:d}".format(node, result),
  172. # Do some rotations
  173. for neighbour in self.adj[node]:
  174. # NB: should these be hermitian_conjugated?
  175. self.act_local_rotation(neighbour, "sqz" if result else "msqz")
  176. # A sort of local complementation
  177. vngbh = set(self.adj[node]) | {node}
  178. for i, j in it.combinations(vngbh, 2):
  179. self.toggle_edge(i, j)
  180. # lcoS.herm_adjoint() if result else lcoS
  181. # else smiZ
  182. # 5 else 6
  183. print "RESULT is {:d}, op is {} doing {}".format(result, self.node[node]["vop"], 5 if result else 6)
  184. self.act_local_rotation2(node, 5 if result else 6)
  185. print "BECAME ", self.node[node]["vop"]
  186. return result
  187. def measure_z(self, node, result):
  188. """ Measure the graph in the Z-basis """
  189. print "gZm{},{:d}".format(node, result),
  190. # Disconnect
  191. for neighbour in tuple(self.adj[node]):
  192. self.del_edge(node, neighbour)
  193. if result:
  194. self.act_local_rotation(neighbour, "pz")
  195. # Rotate
  196. self.act_local_rotation(node, "hadamard")
  197. if result:
  198. self.act_local_rotation(node, "px")
  199. return result
  200. def order(self):
  201. """ Get the number of qubits """
  202. return len(self.node)
  203. def __str__(self):
  204. """ Represent as a string for quick debugging """
  205. s = ""
  206. for key in sorted(self.node.keys()):
  207. s += "{}: {}\t".format(key, clifford.get_name(self.node[key]["vop"]).replace("YC", "-"))
  208. if self.adj[key]:
  209. s += str(tuple(self.adj[key].keys())).replace(" ", "")
  210. else:
  211. s += "-"
  212. s += "\n"
  213. return s
  214. def to_json(self, stringify=False):
  215. """
  216. Convert the graph to JSON form.
  217. JSON keys must be strings, But sometimes it is useful to have
  218. a JSON-like object whose keys are tuples!
  219. """
  220. if stringify:
  221. node = {str(key): value for key, value in self.node.items()}
  222. adj = {str(key): {str(key): value for key, value in ngbh.items()}
  223. for key, ngbh in self.adj.items()}
  224. return {"node": node, "adj": adj}
  225. else:
  226. return {"node": self.node, "adj": self.adj}
  227. def from_json(self, data):
  228. """ Reconstruct from JSON """
  229. self.__init__([])
  230. # TODO
  231. def to_state_vector(self):
  232. """ Get the full state vector """
  233. if len(self.node) > 15:
  234. raise ValueError("Cannot build state vector: too many qubits")
  235. state = qi.CircuitModel(len(self.node))
  236. for i in range(len(self.node)):
  237. state.act_hadamard(i)
  238. for i, j in self.edgelist():
  239. state.act_cz(i, j)
  240. for i, n in self.node.items():
  241. state.act_local_rotation(i, clifford.unitaries[n["vop"]])
  242. return state
  243. def to_stabilizer(self):
  244. """ Get the stabilizer of this graph """
  245. return
  246. output = {a: {} for a in self.node}
  247. for a, b in it.product(self.node, self.node):
  248. if a == b:
  249. output[a][b] = "X"
  250. elif a in self.adj[b]:
  251. output[a][b] = "Z"
  252. else:
  253. output[a][b] = "I"
  254. # TODO: signs
  255. return output
  256. def __eq__(self, other):
  257. """ Check equality between graphs """
  258. if str(type(other)) == "<class 'anders_briegel.graphsim.GraphRegister'>":
  259. return self.to_json() == other.to_json()
  260. return self.adj == other.adj and self.node == other.node
  261. if __name__ == '__main__':
  262. g = GraphState()
  263. g.add_nodes(range(10))
  264. g.add_edge(0, 5)
  265. g.act_local_rotation(6, 10)
  266. print g
  267. print g.to_state_vector()