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.

268 lignes
8.6KB

  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. from util import ABPJsonEncoder
  9. class GraphState(object):
  10. def __init__(self, nodes=[]):
  11. self.adj, self.node = {}, {}
  12. self.add_nodes(nodes)
  13. def add_node(self, v, **kwargs):
  14. """ Add a node """
  15. assert not v in self.node
  16. self.adj[v] = {}
  17. self.node[v] = {"vop": clifford.by_name["hadamard"]}
  18. self.node[v].update(kwargs)
  19. def add_nodes(self, nodes):
  20. """ Add a buncha nodes """
  21. for n in nodes:
  22. self.add_node(n)
  23. def add_edge(self, v1, v2, data={}):
  24. """ Add an edge between two vertices in the self """
  25. assert v1 != v2
  26. self.adj[v1][v2] = data
  27. self.adj[v2][v1] = data
  28. def add_edges(self, edges):
  29. """ Add a buncha edges """
  30. for (v1, v2) in edges:
  31. self.add_edge(v1, v2)
  32. def del_edge(self, v1, v2):
  33. """ Delete an edge between two vertices in the self """
  34. del self.adj[v1][v2]
  35. del self.adj[v2][v1]
  36. def has_edge(self, v1, v2):
  37. """ Test existence of an edge between two vertices in the self """
  38. return v2 in self.adj[v1]
  39. def toggle_edge(self, v1, v2):
  40. """ Toggle an edge between two vertices in the self """
  41. if self.has_edge(v1, v2):
  42. self.del_edge(v1, v2)
  43. else:
  44. self.add_edge(v1, v2)
  45. def edgelist(self):
  46. """ Describe a graph as an edgelist """
  47. # TODO: inefficient
  48. edges = set(tuple(sorted((i, n)))
  49. for i, v in self.adj.items()
  50. for n in v)
  51. return tuple(edges)
  52. def remove_vop(self, a, avoid):
  53. """ Reduces VOP[a] to the identity """
  54. others = set(self.adj[a]) - {avoid}
  55. swap_qubit = others.pop() if others else avoid
  56. for v in reversed(clifford.decompositions[self.node[a]["vop"]]):
  57. if v == "x":
  58. self.local_complementation(a, "U ->")
  59. else:
  60. self.local_complementation(swap_qubit, "V ->")
  61. def local_complementation(self, v, prefix=""):
  62. """ As defined in LISTING 1 of Anders & Briegel """
  63. for i, j in it.combinations(self.adj[v], 2):
  64. self.toggle_edge(i, j)
  65. self.node[v]["vop"] = clifford.times_table[self.node[v]["vop"], clifford.by_name["msqx_h"]]
  66. for i in self.adj[v]:
  67. self.node[i]["vop"] = clifford.times_table[
  68. self.node[i]["vop"], clifford.by_name["sqz_h"]]
  69. def act_local_rotation(self, v, op):
  70. """ Act a local rotation """
  71. rotation = clifford.by_name[str(op)]
  72. self.node[v]["vop"] = clifford.times_table[
  73. rotation, self.node[v]["vop"]]
  74. def act_hadamard(self, qubit):
  75. """ Shorthand """
  76. self.act_local_rotation(qubit, 10)
  77. def lonely(self, a, b):
  78. """ Is this qubit lonely ? """
  79. return len(self.adj[a]) > (b in self.adj[a])
  80. def act_cz(self, a, b):
  81. """ Act a controlled-phase gate on two qubits """
  82. if self.lonely(a, b):
  83. self.remove_vop(a, b)
  84. if self.lonely(b, a):
  85. self.remove_vop(b, a)
  86. if self.lonely(a, b) and not clifford.is_diagonal(self.node[a]["vop"]):
  87. self.remove_vop(a, b)
  88. edge = self.has_edge(a, b)
  89. va = self.node[a]["vop"]
  90. vb = self.node[b]["vop"]
  91. new_edge, self.node[a]["vop"], self.node[b]["vop"] = \
  92. clifford.cz_table[edge, va, vb]
  93. if new_edge != edge:
  94. self.toggle_edge(a, b)
  95. def measure(self, node, basis, force=None):
  96. """ Measure in an arbitrary basis """
  97. basis = clifford.by_name[basis]
  98. old_basis = basis
  99. ha = clifford.conjugation_table[self.node[node]["vop"]]
  100. basis, phase = clifford.conjugate(basis, ha)
  101. print basis, phase
  102. assert phase in (-1, 1) # TODO: remove
  103. # TODO: wtf
  104. force = force ^ 0x01 if force != -1 and phase == 0 else force
  105. which = {1: self.measure_x, 2: self.measure_y, 3: self.measure_z}[basis]
  106. res = which(node, force)
  107. res = res if phase == 1 else not res
  108. # TODO: put the asserts from graphsim.cpp into tests
  109. return res
  110. def measure_x(self, node, force=None):
  111. """ Measure the graph in the X-basis """
  112. if len(self.adj[node]) == 0:
  113. return 0
  114. # Flip a coin
  115. result = force if force != None else random.choice([0, 1])
  116. # Pick a vertex
  117. friend = next(self.adj[node].iterkeys())
  118. if not result:
  119. # Do a z on all ngb(v) \ ngb(vb) \ {vb}, and sqy on the friend
  120. self.act_local_rotation(friend, "sqy")
  121. for n in set(self.adj[node]) - set(self.adj(friend)) - {friend}:
  122. self.act_local_rotation(n, "pz")
  123. else:
  124. # Do a z on all ngb(vb) \ ngb(v) \ {v}, and some other stuff
  125. self.act_local_rotation(node, "pz")
  126. self.act_local_rotation(friend, "msqy")
  127. for n in set(self.adj[friend]) - set(self.adj(node)) - {node}:
  128. self.act_local_rotation(n, "pz")
  129. # TODO: the really nasty bit
  130. def measure_y(self, node, force=None):
  131. """ Measure the graph in the Y-basis """
  132. # Flip a coin
  133. result = force if force != None else random.choice([0, 1])
  134. # Do some rotations
  135. for neighbour in self.adj[node]:
  136. # NB: should these be hermitian_conjugated?
  137. self.act_local_rotation(neighbour, "sqz" if result else "msqz")
  138. # A sort of local complementation
  139. vngbh = set(self.adj[node]) | {node}
  140. for i, j in it.combinations(vngbh, 2):
  141. self.toggle_edge(i, j)
  142. self.act_local_rotation(node, "msqz" if result else "msqz_h")
  143. return result
  144. def measure_z(self, node, force=None):
  145. """ Measure the graph in the Z-basis """
  146. # Flip a coin
  147. result = force if force != None else random.choice([0, 1])
  148. # Disconnect
  149. for neighbour in self.adj[node]:
  150. self.del_edge(node, neighbour)
  151. if result:
  152. self.act_local_rotation(neighbour, "pz")
  153. # Rotate
  154. if result:
  155. self.act_local_rotation(node, "px")
  156. self.act_local_rotation(node, "hadamard")
  157. return result
  158. def toggle_edge(a, b):
  159. """ Toggle edges between vertex sets a and b """
  160. done = {}
  161. for i, j in it.product(a, b):
  162. if i==j and not (i, j) in done:
  163. done.add((i, j))
  164. self.toggle_edge(i, j)
  165. def order(self):
  166. """ Get the number of qubits """
  167. return len(self.node)
  168. def __str__(self):
  169. """ Represent as a string for quick debugging """
  170. node = {key: clifford.get_name(value["vop"])
  171. for key, value in self.node.items()}
  172. nbstr = str(self.adj)
  173. return "graph:\n node: {}\n adj: {}\n".format(node, nbstr)
  174. def to_json(self, stringify = False):
  175. """
  176. Convert the graph to JSON form.
  177. JSON keys must be strings, But sometimes it is useful to have
  178. a JSON-like object whose keys are tuples!
  179. """
  180. if stringify:
  181. node = {str(key):value for key, value in self.node.items()}
  182. adj = {str(key): {str(key):value for key, value in ngbh.items()}
  183. for key, ngbh in self.adj.items()}
  184. return {"node": node, "adj": adj}
  185. else:
  186. return {"node": self.node, "adj": self.adj}
  187. def from_json(self, data):
  188. """ Reconstruct from JSON """
  189. self.__init__([])
  190. # TODO
  191. def to_state_vector(self):
  192. """ Get the full state vector """
  193. if len(self.node) > 15:
  194. raise ValueError("Cannot build state vector: too many qubits")
  195. state = qi.CircuitModel(len(self.node))
  196. for i in range(len(self.node)):
  197. state.act_hadamard(i)
  198. for i, j in self.edgelist():
  199. state.act_cz(i, j)
  200. for i, n in self.node.items():
  201. state.act_local_rotation(i, clifford.unitaries[n["vop"]])
  202. return state
  203. def to_stabilizer(self):
  204. """ Get the stabilizer of this graph """
  205. output = {a: {} for a in self.node}
  206. for a, b in it.product(self.node, self.node):
  207. if a == b:
  208. output[a][b] = "X"
  209. elif a in self.adj[b]:
  210. output[a][b] = "Z"
  211. else:
  212. output[a][b] = "I"
  213. return output
  214. def __eq__(self, other):
  215. """ Check equality between graphs """
  216. return self.adj == other.adj and self.node == other.node