| @@ -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 | |||||