Anders and Briegel in Python
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

237 linhas
7.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. msqx_h = clifford.conjugation_table[clifford.by_name["msqx"]]
  66. sqz_h = clifford.conjugation_table[clifford.by_name["sqz"]]
  67. self.node[v]["vop"] = clifford.times_table[self.node[v]["vop"], msqx_h]
  68. for i in self.adj[v]:
  69. self.node[i]["vop"] = clifford.times_table[
  70. self.node[i]["vop"], sqz_h]
  71. def act_local_rotation(self, v, op):
  72. """ Act a local rotation """
  73. rotation = clifford.by_name[str(op)]
  74. self.node[v]["vop"] = clifford.times_table[
  75. rotation, self.node[v]["vop"]]
  76. def act_hadamard(self, qubit):
  77. """ Shorthand """
  78. self.act_local_rotation(qubit, 10)
  79. def lonely(self, a, b):
  80. """ Is this qubit lonely ? """
  81. return len(self.adj[a]) > (b in self.adj[a])
  82. def act_cz(self, a, b):
  83. """ Act a controlled-phase gate on two qubits """
  84. if self.lonely(a, b):
  85. self.remove_vop(a, b)
  86. if self.lonely(b, a):
  87. self.remove_vop(b, a)
  88. if self.lonely(a, b) and not clifford.is_diagonal(self.node[a]["vop"]):
  89. self.remove_vop(a, b)
  90. edge = self.has_edge(a, b)
  91. va = self.node[a]["vop"]
  92. vb = self.node[b]["vop"]
  93. new_edge, self.node[a]["vop"], self.node[b]["vop"] = \
  94. clifford.cz_table[edge, va, vb]
  95. if new_edge != edge:
  96. self.toggle_edge(a, b)
  97. def measure(self, node, basis, force=None):
  98. """ Measure in an arbitrary basis """
  99. basis = clifford.by_name[basis]
  100. old_basis = basis
  101. ha = clifford.conjugation_table[self.node[node]["vop"]]
  102. basis, phase = clifford.conjugate(basis, ha)
  103. print basis, phase
  104. assert phase in (-1, 1) # TODO: remove
  105. # TODO: wtf
  106. force = force ^ 0x01 if force != -1 and phase == 0 else force
  107. which = {1: self.measure_x, 2: self.measure_y, 3: self.measure_z}[basis]
  108. res = which(node, force)
  109. res = res if phase == 1 else not res
  110. # TODO: put the asserts from graphsim.cpp into tests
  111. return res
  112. def measure_y(self, node, force=None):
  113. """ Measure the graph in the Y-basis """
  114. result = force if force != None else random.choice([0, 1])
  115. # Do some rotations
  116. rotation = clifford.conjugate("sqz" if result else "msqz")
  117. for neighbour in self.adj[node]:
  118. self.act_local_rotation(neighbour, rotation)
  119. # A sort of local complementation
  120. vngbh = set(self.adj[node]) + {node}
  121. for i, j in it.combinations(vngbh, 2):
  122. self.toggle_edge(i, j)
  123. if result:
  124. self.act_local_rotation(node, "msqz")
  125. else:
  126. self.act_local_rotation(node, clifford.conjugate("msqz"))
  127. return result
  128. def measure_z(self, node, force=None):
  129. """ Measure the graph in the Z-basis """
  130. result = force if force != None else random.choice([0, 1])
  131. # Disconnect
  132. for neighbour in self.adj[node]:
  133. self.del_edge(node, neighbour)
  134. if result:
  135. self.act_local_rotation(neighbour, "pz")
  136. # Rotate
  137. if result:
  138. self.act_local_rotation(node, "px")
  139. self.act_local_rotation(node, "hadamard")
  140. return result
  141. def order(self):
  142. """ Get the number of qubits """
  143. return len(self.node)
  144. def __str__(self):
  145. """ Represent as a string for quick debugging """
  146. node = {key: clifford.get_name(value["vop"])
  147. for key, value in self.node.items()}
  148. nbstr = str(self.adj)
  149. return "graph:\n node: {}\n adj: {}\n".format(node, nbstr)
  150. def to_json(self, stringify = False):
  151. """
  152. Convert the graph to JSON form.
  153. JSON keys must be strings, But sometimes it is useful to have
  154. a JSON-like object whose keys are tuples!
  155. """
  156. if stringify:
  157. node = {str(key):value for key, value in self.node.items()}
  158. adj = {str(key): {str(key):value for key, value in ngbh.items()}
  159. for key, ngbh in self.adj.items()}
  160. return {"node": node, "adj": adj}
  161. else:
  162. return {"node": self.node, "adj": self.adj}
  163. def from_json(self, data):
  164. """ Reconstruct from JSON """
  165. self.__init__([])
  166. # TODO
  167. def to_state_vector(self):
  168. """ Get the full state vector """
  169. if len(self.node) > 15:
  170. raise ValueError("Cannot build state vector: too many qubits")
  171. state = qi.CircuitModel(len(self.node))
  172. for i in range(len(self.node)):
  173. state.act_hadamard(i)
  174. for i, j in self.edgelist():
  175. state.act_cz(i, j)
  176. for i, n in self.node.items():
  177. state.act_local_rotation(i, clifford.unitaries[n["vop"]])
  178. return state
  179. def to_stabilizer(self):
  180. """ Get the stabilizer of this graph """
  181. output = {a: {} for a in self.node}
  182. for a, b in it.product(self.node, self.node):
  183. if a == b:
  184. output[a][b] = "X"
  185. elif a in self.adj[b]:
  186. output[a][b] = "Z"
  187. else:
  188. output[a][b] = "I"
  189. return output
  190. def __eq__(self, other):
  191. """ Check equality between graphs """
  192. return self.adj == other.adj and self.node == other.node