|
|
@@ -2,7 +2,6 @@ |
|
|
|
Provides an extremely basic graph structure, based on neighbour lists |
|
|
|
""" |
|
|
|
|
|
|
|
from collections import defaultdict |
|
|
|
import itertools as it |
|
|
|
import clifford |
|
|
|
import json |
|
|
@@ -15,26 +14,34 @@ except ImportError: |
|
|
|
|
|
|
|
class GraphState(object): |
|
|
|
|
|
|
|
def __init__(self): |
|
|
|
self.ngbh = defaultdict(set) |
|
|
|
self.vops = defaultdict(int) |
|
|
|
self.meta = defaultdict(dict) |
|
|
|
def __init__(self, nodes = []): |
|
|
|
self.ngbh = {} |
|
|
|
self.vops = {} |
|
|
|
self.meta = {} |
|
|
|
self.add_nodes(nodes) |
|
|
|
|
|
|
|
def add_node(self, v): |
|
|
|
""" Add a node if it doesn't already exist """ |
|
|
|
if not v in self.ngbh: |
|
|
|
self.ngbh[v] = set() |
|
|
|
self.vops[v] = clifford.by_name["hadamard"] |
|
|
|
self.meta[v] = dict() |
|
|
|
|
|
|
|
def add_nodes(self, nodes): |
|
|
|
""" Add a buncha nodes """ |
|
|
|
for n in nodes: |
|
|
|
self.add_node(n) |
|
|
|
|
|
|
|
def add_edge(self, v1, v2): |
|
|
|
""" Add an edge between two vertices in the self """ |
|
|
|
if not v1 in self.ngbh: |
|
|
|
self.vops[v1] = clifford.by_name["hadamard"] |
|
|
|
if not v2 in self.ngbh: |
|
|
|
self.vops[v2] = clifford.by_name["hadamard"] |
|
|
|
self.ngbh[v1].add(v2) |
|
|
|
self.ngbh[v2].add(v1) |
|
|
|
|
|
|
|
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 """ |
|
|
|
self.ngbh[v1].remove(v2) |
|
|
@@ -70,7 +77,8 @@ class GraphState(object): |
|
|
|
for i, j in it.combinations(self.ngbh[v], 2): |
|
|
|
self.toggle_edge(i, j) |
|
|
|
|
|
|
|
# Update VOPs: TODO check ordering and replace by self.act_local_rotation |
|
|
|
# Update VOPs: TODO check ordering and replace by |
|
|
|
# self.act_local_rotation |
|
|
|
self.vops[v] = clifford.times_table[ |
|
|
|
self.vops[v]][clifford.by_name["sqx"]] |
|
|
|
for i in self.ngbh[v]: |
|
|
@@ -79,7 +87,7 @@ class GraphState(object): |
|
|
|
|
|
|
|
def act_local_rotation(self, a, op): |
|
|
|
""" Act a local rotation """ |
|
|
|
self.vops[a] = clifford.times_table[op,self.vops[a]] |
|
|
|
self.vops[a] = clifford.times_table[op, self.vops[a]] |
|
|
|
|
|
|
|
def act_local_rotation_by_name(self, qubit, name): |
|
|
|
""" Shorthand """ |
|
|
@@ -99,14 +107,14 @@ class GraphState(object): |
|
|
|
if self.ngbh[a] - {b}: |
|
|
|
self.remove_vop(a, b) |
|
|
|
edge = self.has_edge(a, b) |
|
|
|
new_edge, self.vops[a], self.vops[b] = clifford.cz_table[edge, self.vops[a], self.vops[b]] |
|
|
|
new_edge, self.vops[a], self.vops[ |
|
|
|
b] = clifford.cz_table[edge, self.vops[a], self.vops[b]] |
|
|
|
if new_edge != edge: |
|
|
|
self.toggle_edge(a, b) |
|
|
|
|
|
|
|
|
|
|
|
def measure_z(self, node, force = None): |
|
|
|
def measure_z(self, node, force=None): |
|
|
|
""" Measure the graph in the Z-basis """ |
|
|
|
res = force if force else np.random.choice([0,1]) |
|
|
|
res = force if force else np.random.choice([0, 1]) |
|
|
|
|
|
|
|
# Disconnect |
|
|
|
for neighbour in self.ngbh[node]: |
|
|
@@ -123,15 +131,14 @@ class GraphState(object): |
|
|
|
|
|
|
|
return res |
|
|
|
|
|
|
|
|
|
|
|
def measure_x(self, i): |
|
|
|
""" Measure the graph in the X-basis """ |
|
|
|
#TODO |
|
|
|
# TODO |
|
|
|
pass |
|
|
|
|
|
|
|
def measure_y(self, i): |
|
|
|
""" Measure the graph in the Y-basis """ |
|
|
|
#TODO |
|
|
|
# TODO |
|
|
|
pass |
|
|
|
|
|
|
|
def order(self): |
|
|
@@ -140,22 +147,22 @@ class GraphState(object): |
|
|
|
|
|
|
|
def __str__(self): |
|
|
|
""" Represent as a string for quick debugging """ |
|
|
|
vopstr = {key: clifford.get_name(value) for key,value in self.vops.items()} |
|
|
|
nbstr = str(dict(self.ngbh)) |
|
|
|
vopstr = {key: clifford.get_name(value) |
|
|
|
for key, value in self.vops.items()} |
|
|
|
nbstr = str(self.ngbh) |
|
|
|
return "graph:\n vops: {}\n ngbh: {}\n".format(vopstr, nbstr) |
|
|
|
|
|
|
|
def to_json(self): |
|
|
|
""" Convert the graph to JSON form """ |
|
|
|
#ngbh = {key: tuple(value) for key, value in self.ngbh.items()} |
|
|
|
meta = {key: value for key, value in self.meta.items()} |
|
|
|
edge = self.edgelist() |
|
|
|
return json.dumps({"vops": self.vops, "edge": edge, "meta": meta}) |
|
|
|
return json.dumps({"nodes": self.vops, "edges": edge, "meta": meta}) |
|
|
|
|
|
|
|
def to_networkx(self): |
|
|
|
""" Convert the graph to a networkx graph """ |
|
|
|
g = nx.Graph() |
|
|
|
g.edge = {node: {neighbour: {} for neighbour in neighbours} |
|
|
|
for node, neighbours in self.ngbh.items()} |
|
|
|
g.edge = {node: {neighbour: {} for neighbour in neighbours} |
|
|
|
for node, neighbours in self.ngbh.items()} |
|
|
|
g.node = {node: {"vop": vop} for node, vop in self.vops.items()} |
|
|
|
for node, metadata in self.meta.items(): |
|
|
|
g.node[node].update(metadata) |
|
|
@@ -163,28 +170,29 @@ class GraphState(object): |
|
|
|
|
|
|
|
def to_state_vector(self): |
|
|
|
""" Get the full state vector """ |
|
|
|
if not len(self.vops)<10: |
|
|
|
if len(self.vops) > 15: |
|
|
|
raise ValueError("Cannot build state vector: too many qubits") |
|
|
|
output = qi.CircuitModel(len(self.vops)) |
|
|
|
#print output |
|
|
|
state = qi.CircuitModel(len(self.vops)) |
|
|
|
for i in range(len(self.vops)): |
|
|
|
output.act_hadamard(i) |
|
|
|
#print output |
|
|
|
state.act_hadamard(i) |
|
|
|
for i, j in self.edgelist(): |
|
|
|
output.act_cz(i, j) |
|
|
|
#print output |
|
|
|
state.act_cz(i, j) |
|
|
|
for i, u in self.vops.items(): |
|
|
|
output.act_local_rotation(i, clifford.unitaries[u]) |
|
|
|
return output |
|
|
|
state.act_local_rotation(i, clifford.unitaries[u]) |
|
|
|
return state |
|
|
|
|
|
|
|
def layout(self): |
|
|
|
""" Automatically lay out the graph """ |
|
|
|
g = self.to_networkx() |
|
|
|
pos = nx.spring_layout(g, dim=3, scale=10) |
|
|
|
average = lambda axis: sum(p[axis] for p in pos.values())/float(len(pos)) |
|
|
|
average = lambda axis: sum(p[axis] |
|
|
|
for p in pos.values()) / float(len(pos)) |
|
|
|
ax, ay, az = average(0), average(1), average(2) |
|
|
|
for key, (x, y, z) in pos.items(): |
|
|
|
self.meta[key]["pos"] = {"x": round(x-ax, 0), "y": round(y-ay, 0), "z": round(z-az, 0)} |
|
|
|
self.meta[key]["pos"] = { |
|
|
|
"x": round(x - ax, 0), |
|
|
|
"y": round(y - ay, 0), |
|
|
|
"z": round(z - az, 0)} |
|
|
|
|
|
|
|
def to_stabilizer(self): |
|
|
|
""" Get the stabilizer of this graph """ |
|
|
@@ -200,7 +208,7 @@ class GraphState(object): |
|
|
|
output += " I " |
|
|
|
output += "\n" |
|
|
|
return output |
|
|
|
|
|
|
|
|
|
|
|
def adj_list(self): |
|
|
|
""" For comparison with Anders and Briegel's C++ implementation """ |
|
|
|
rows = [] |
|
|
@@ -209,7 +217,5 @@ class GraphState(object): |
|
|
|
vop = clifford.get_name(vop) |
|
|
|
s = "Vertex {}: VOp {}, neighbors {}".format(key, vop, ngbh) |
|
|
|
rows.append(s) |
|
|
|
return " \n".join(rows)+ " \n" |
|
|
|
|
|
|
|
return " \n".join(rows) + " \n" |
|
|
|
|
|
|
|
|