| @@ -0,0 +1,279 @@ | |||
| """ | |||
| Provides an extremely basic graph structure, based on neighbour lists | |||
| """ | |||
| import itertools as it | |||
| import json | |||
| import qi, clifford, util | |||
| import random | |||
| output = open("debug_pete.txt", "w") | |||
| def debug(x): | |||
| output.write(str(x)+"\n") | |||
| class GraphState(object): | |||
| def __init__(self, nodes=[]): | |||
| self.adj, self.node = {}, {} | |||
| self.add_nodes(nodes) | |||
| def add_node(self, v, **kwargs): | |||
| """ Add a node """ | |||
| assert not v in self.node | |||
| self.adj[v] = {} | |||
| self.node[v] = {"vop": clifford.by_name["hadamard"]} | |||
| self.node[v].update(kwargs) | |||
| def add_nodes(self, nodes): | |||
| """ Add a buncha nodes """ | |||
| for n in nodes: | |||
| self.add_node(n) | |||
| def add_edge(self, v1, v2, data = {}): | |||
| """ Add an edge between two vertices in the self """ | |||
| assert v1 != v2 | |||
| self.adj[v1][v2] = data | |||
| self.adj[v2][v1] = data | |||
| def add_edges(self, edges): | |||
| """ Add a buncha edges """ | |||
| for (v1, v2) in edges: | |||
| self.add_edge(v1, v2) | |||
| def del_edge(self, v1, v2): | |||
| """ Delete an edge between two vertices in the self """ | |||
| debug("deling edge") | |||
| del self.adj[v1][v2] | |||
| del self.adj[v2][v1] | |||
| def has_edge(self, v1, v2): | |||
| """ Test existence of an edge between two vertices in the self """ | |||
| return v2 in self.adj[v1] | |||
| def toggle_edge(self, v1, v2): | |||
| """ Toggle an edge between two vertices in the self """ | |||
| if self.has_edge(v1, v2): | |||
| self.del_edge(v1, v2) | |||
| else: | |||
| self.add_edge(v1, v2) | |||
| def edgelist(self): | |||
| """ Describe a graph as an edgelist """ | |||
| # TODO: inefficient | |||
| edges = set(tuple(sorted((i, n))) | |||
| for i, v in self.adj.items() | |||
| for n in v) | |||
| return tuple(edges) | |||
| def remove_vop(self, a, avoid): | |||
| """ Reduces VOP[a] to the identity """ | |||
| # TODO: sucks! | |||
| others = set(self.adj[a]) - {avoid} | |||
| swap_qubit = others.pop() if others else avoid | |||
| debug("remove_byprod_op called: (v, avoid, vb):") | |||
| self.print_adj_list_line(a) | |||
| self.print_adj_list_line(avoid) | |||
| self.print_adj_list_line(swap_qubit) | |||
| converted = clifford.decompositions[self.node[a]["vop"]] | |||
| converted = "".join("U" if x == "x" else "V" for x in converted) | |||
| debug("using {}".format(converted)) | |||
| for v in reversed(clifford.decompositions[self.node[a]["vop"]]): | |||
| if v == "x": | |||
| self.local_complementation(a, "U ->") | |||
| else: | |||
| self.local_complementation(swap_qubit, "V ->") | |||
| assert self.node[a]["vop"]==0 | |||
| debug("remove_byprod_op: after (v, avoid, vb):") | |||
| self.print_adj_list_line(a) | |||
| self.print_adj_list_line(avoid) | |||
| self.print_adj_list_line(swap_qubit) | |||
| assert self.node[a]["vop"] == 0 | |||
| def local_complementation(self, v, prefix = ""): | |||
| """ As defined in LISTING 1 of Anders & Briegel """ | |||
| debug("{}Inverting about {}".format(prefix, self.get_adj_list_line(v))) | |||
| for i, j in it.combinations(self.adj[v], 2): | |||
| self.toggle_edge(i, j) | |||
| msqx_h = clifford.conjugation_table[clifford.by_name["msqx"]] | |||
| sqz_h = clifford.conjugation_table[clifford.by_name["sqz"]] | |||
| self.node[v]["vop"] = clifford.times_table[self.node[v]["vop"], msqx_h] | |||
| for i in self.adj[v]: | |||
| self.node[i]["vop"] = clifford.times_table[self.node[i]["vop"], sqz_h] | |||
| def act_local_rotation(self, v, op): | |||
| """ Act a local rotation """ | |||
| rotation = clifford.by_name[str(op)] | |||
| self.node[v]["vop"] = clifford.times_table[rotation, self.node[v]["vop"]] | |||
| def act_hadamard(self, qubit): | |||
| """ Shorthand """ | |||
| self.act_local_rotation(qubit, 10) | |||
| def act_cz(self, a, b): | |||
| """ Act a controlled-phase gate on two qubits """ | |||
| debug("before cphase between {} and {}".format(a, b)) | |||
| self.print_adj_list_line(a) | |||
| self.print_adj_list_line(b) | |||
| ci = self.get_connection_info(a, b) | |||
| if ci["non1"]: | |||
| debug("cphase: left vertex has NONs -> putting it to Id") | |||
| self.remove_vop(a, b) | |||
| ci = self.get_connection_info(a, b) | |||
| if ci["non2"]: | |||
| debug("cphase: right vertex has NONs -> putting it to Id") | |||
| self.remove_vop(b, a) | |||
| ci = self.get_connection_info(a, b) | |||
| if ci["non1"] and not clifford.is_diagonal(self.node[a]["vop"]): | |||
| debug("cphase: left one needs treatment again -> putting it to Id") | |||
| self.remove_vop(a, b) | |||
| self.cz_with_table(a, b) | |||
| def get_connection_info(self, a, b): | |||
| if self.has_edge(a, b): | |||
| return {"was_edge": True, | |||
| "non1": len(self.adj.get(a)) > 1, | |||
| "non2": len(self.adj.get(b)) > 1} | |||
| else: | |||
| return {"was_edge": False, | |||
| "non1": len(self.adj.get(a)) > 0, | |||
| "non2": len(self.adj.get(b)) > 0} | |||
| def cz_with_table(self, a, b): | |||
| """ Run the table """ | |||
| debug("cphase_with_table called on:") | |||
| self.print_adj_list_line(a) | |||
| self.print_adj_list_line(b) | |||
| ci = self.get_connection_info(a, b) | |||
| try: | |||
| assert ci["non1"]==False or clifford.is_diagonal(self.node[a]["vop"]) | |||
| assert ci["non2"]==False or clifford.is_diagonal(self.node[b]["vop"]) | |||
| except AssertionError: | |||
| debug(ci) | |||
| debug(self.node[a]["vop"]) | |||
| debug(self.node[b]["vop"]) | |||
| edge = self.has_edge(a, b) | |||
| new_edge, self.node[a]["vop"], self.node[ | |||
| b]["vop"] = clifford.cz_table[edge, self.node[a]["vop"], self.node[b]["vop"]] | |||
| if new_edge != edge: | |||
| self.toggle_edge(a, b) | |||
| debug("cphase_with_table: after") | |||
| self.print_adj_list_line(a) | |||
| self.print_adj_list_line(b) | |||
| ci = self.get_connection_info(a, b) | |||
| assert ci["non1"]==False or clifford.is_diagonal(self.node[a]["vop"]) | |||
| assert ci["non2"]==False or clifford.is_diagonal(self.node[b]["vop"]) | |||
| def measure_z(self, node, force=None): | |||
| """ Measure the graph in the Z-basis """ | |||
| res = force if force!=None else random.choice([0, 1]) | |||
| # Disconnect | |||
| for neighbour in self.adj[node]: | |||
| self.del_edge(node, neighbour) | |||
| if res: | |||
| self.act_local_rotation(neighbour, "pz") | |||
| # Rotate | |||
| if res: | |||
| self.act_local_rotation(node, "px") | |||
| self.act_local_rotation(node, "hadamard") | |||
| else: | |||
| self.act_local_rotation(node, "hadamard") | |||
| return res | |||
| def measure_x(self, i): | |||
| """ Measure the graph in the X-basis """ | |||
| # TODO | |||
| pass | |||
| def measure_y(self, i): | |||
| """ Measure the graph in the Y-basis """ | |||
| # TODO | |||
| pass | |||
| def order(self): | |||
| """ Get the number of qubits """ | |||
| return len(self.node) | |||
| def __str__(self): | |||
| """ Represent as a string for quick debugging """ | |||
| node = {key: clifford.get_name(value["vop"]) | |||
| for key, value in self.node.items()} | |||
| nbstr = str(self.adj) | |||
| return "graph:\n node: {}\n adj: {}\n".format(node, nbstr) | |||
| def to_json(self): | |||
| """ Convert the graph to JSON form """ | |||
| return {"node": self.node, "adj": self.adj} | |||
| def from_json(self, data): | |||
| """ Reconstruct from JSON """ | |||
| self.__init__([]) | |||
| #TODO | |||
| def to_state_vector(self): | |||
| """ Get the full state vector """ | |||
| if len(self.node) > 15: | |||
| raise ValueError("Cannot build state vector: too many qubits") | |||
| state = qi.CircuitModel(len(self.node)) | |||
| for i in range(len(self.node)): | |||
| state.act_hadamard(i) | |||
| for i, j in self.edgelist(): | |||
| state.act_cz(i, j) | |||
| for i, n in self.node.items(): | |||
| state.act_local_rotation(i, clifford.unitaries[n["vop"]]) | |||
| return state | |||
| def to_stabilizer(self): | |||
| """ Get the stabilizer of this graph """ | |||
| output = {a:{} for a in self.node} | |||
| for a, b in it.product(self.node, self.node): | |||
| if a == b: | |||
| output[a][b] = "X" | |||
| elif a in self.adj[b]: | |||
| output[a][b] = "Z" | |||
| else: | |||
| output[a][b] = "I" | |||
| return output | |||
| def adj_list(self): | |||
| """ For comparison with Anders and Briegel's C++ implementation """ | |||
| rows = [] | |||
| for key, node in self.node.items(): | |||
| adj = " ".join(map(str, sorted(self.adj[key]))) | |||
| vop = clifford.get_name(node["vop"]) | |||
| s = "Vertex {}: VOp {}, neighbors {}".format(key, vop, adj) | |||
| rows.append(s) | |||
| return " \n".join(rows) + " \n" | |||
| def get_adj_list_line(self, key): | |||
| """ TODO: delete """ | |||
| node = self.node[key] | |||
| adj = " ".join(map(str, sorted(self.adj[key]))) | |||
| vop = clifford.get_name(node["vop"]) | |||
| s = "Vertex {}: VOp {}, neighbors {}".format(key, vop, adj) | |||
| return s | |||
| def print_adj_list_line(self, key): | |||
| debug(self.get_adj_list_line(key)) | |||
| def __eq__(self, other): | |||
| """ Check equality between graphs """ | |||
| return self.adj == other.adj and self.node == other.node | |||