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.

217 lines
6.7KB

  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. swap_qubit = others.pop() if others else avoid
  55. for v in reversed(clifford.decompositions[self.node[a]["vop"]]):
  56. if v == "x":
  57. self.local_complementation(a, "U ->")
  58. else:
  59. self.local_complementation(swap_qubit, "V ->")
  60. def local_complementation(self, v, prefix=""):
  61. """ As defined in LISTING 1 of Anders & Briegel """
  62. for i, j in it.combinations(self.adj[v], 2):
  63. self.toggle_edge(i, j)
  64. msqx_h = clifford.conjugation_table[clifford.by_name["msqx"]]
  65. sqz_h = clifford.conjugation_table[clifford.by_name["sqz"]]
  66. self.node[v]["vop"] = clifford.times_table[self.node[v]["vop"], msqx_h]
  67. for i in self.adj[v]:
  68. self.node[i]["vop"] = clifford.times_table[
  69. self.node[i]["vop"], sqz_h]
  70. def act_local_rotation(self, v, op):
  71. """ Act a local rotation """
  72. rotation = clifford.by_name[str(op)]
  73. self.node[v]["vop"] = clifford.times_table[
  74. rotation, self.node[v]["vop"]]
  75. def act_hadamard(self, qubit):
  76. """ Shorthand """
  77. self.act_local_rotation(qubit, 10)
  78. def lonely(self, a, b):
  79. """ Is this qubit lonely ? """
  80. return len(self.adj[a]) > (b in self.adj[a])
  81. def act_cz(self, a, b):
  82. """ Act a controlled-phase gate on two qubits """
  83. if self.lonely(a, b):
  84. self.remove_vop(a, b)
  85. if self.lonely(b, a):
  86. self.remove_vop(b, a)
  87. if self.lonely(a, b) and not clifford.is_diagonal(self.node[a]["vop"]):
  88. self.remove_vop(a, b)
  89. edge = self.has_edge(a, b)
  90. va = self.node[a]["vop"]
  91. vb = self.node[b]["vop"]
  92. new_edge, self.node[a]["vop"], self.node[b]["vop"] = \
  93. clifford.cz_table[edge, va, vb]
  94. if new_edge != edge:
  95. self.toggle_edge(a, b)
  96. def measure(self, node, basis, force=None):
  97. """ Measure in an arbitrary basis """
  98. basis = clifford.by_name[basis]
  99. old_basis = basis
  100. ha = clifford.conjugation_table[self.node[node]["vop"]]
  101. basis, phase = clifford.conjugate(basis, ha)
  102. print basis, phase
  103. assert phase in (-1, 1) # TODO: remove
  104. # TODO: wtf
  105. force = force ^ 0x01 if force != -1 and phase == 0 else force
  106. which = {1: self.measure_x, 2: self.measure_y, 3: self.measure_z}[basis]
  107. res = which(node, force)
  108. res = res if phase == 1 else not res
  109. # TODO: put the asserts from graphsim.cpp into tests
  110. return res
  111. def measure_z(self, node, force=None):
  112. """ Measure the graph in the Z-basis """
  113. res = force if force != None else random.choice([0, 1])
  114. # Disconnect
  115. for neighbour in self.adj[node]:
  116. self.del_edge(node, neighbour)
  117. if res:
  118. self.act_local_rotation(neighbour, "pz")
  119. # Rotate
  120. if res:
  121. self.act_local_rotation(node, "px")
  122. self.act_local_rotation(node, "hadamard")
  123. else:
  124. self.act_local_rotation(node, "hadamard")
  125. return res
  126. def measure_x(self, node, force=None):
  127. """ Measure the graph in the X-basis """
  128. # TODO
  129. pass
  130. def measure_y(self, node, force=None):
  131. """ Measure the graph in the Y-basis """
  132. # TODO
  133. pass
  134. def order(self):
  135. """ Get the number of qubits """
  136. return len(self.node)
  137. def __str__(self):
  138. """ Represent as a string for quick debugging """
  139. node = {key: clifford.get_name(value["vop"])
  140. for key, value in self.node.items()}
  141. nbstr = str(self.adj)
  142. return "graph:\n node: {}\n adj: {}\n".format(node, nbstr)
  143. def to_json(self):
  144. """ Convert the graph to JSON form """
  145. return {"node": self.node, "adj": self.adj}
  146. def from_json(self, data):
  147. """ Reconstruct from JSON """
  148. self.__init__([])
  149. # TODO
  150. def to_state_vector(self):
  151. """ Get the full state vector """
  152. if len(self.node) > 15:
  153. raise ValueError("Cannot build state vector: too many qubits")
  154. state = qi.CircuitModel(len(self.node))
  155. for i in range(len(self.node)):
  156. state.act_hadamard(i)
  157. for i, j in self.edgelist():
  158. state.act_cz(i, j)
  159. for i, n in self.node.items():
  160. state.act_local_rotation(i, clifford.unitaries[n["vop"]])
  161. return state
  162. def to_stabilizer(self):
  163. """ Get the stabilizer of this graph """
  164. output = {a: {} for a in self.node}
  165. for a, b in it.product(self.node, self.node):
  166. if a == b:
  167. output[a][b] = "X"
  168. elif a in self.adj[b]:
  169. output[a][b] = "Z"
  170. else:
  171. output[a][b] = "I"
  172. return output
  173. def __eq__(self, other):
  174. """ Check equality between graphs """
  175. return self.adj == other.adj and self.node == other.node