|  |  | @@ -6,6 +6,10 @@ from collections import defaultdict | 
		
	
		
			
			|  |  |  | import itertools as it | 
		
	
		
			
			|  |  |  | import clifford | 
		
	
		
			
			|  |  |  | import json | 
		
	
		
			
			|  |  |  | try: | 
		
	
		
			
			|  |  |  | import networkx as nx | 
		
	
		
			
			|  |  |  | except ImportError: | 
		
	
		
			
			|  |  |  | print "Could not import networkx: layout will not work" | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | class GraphState(object): | 
		
	
	
		
			
				|  |  | @@ -13,6 +17,7 @@ class GraphState(object): | 
		
	
		
			
			|  |  |  | def __init__(self): | 
		
	
		
			
			|  |  |  | self.ngbh = defaultdict(set) | 
		
	
		
			
			|  |  |  | self.vops = defaultdict(int) | 
		
	
		
			
			|  |  |  | self.meta = defaultdict(dict) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def add_vertex(self, v): | 
		
	
		
			
			|  |  |  | """ Add a vertex if it doesn't already exist """ | 
		
	
	
		
			
				|  |  | @@ -84,7 +89,6 @@ class GraphState(object): | 
		
	
		
			
			|  |  |  | if self.ngbh[a] - {b}: | 
		
	
		
			
			|  |  |  | self.remove_vop(a, b) | 
		
	
		
			
			|  |  |  | edge = self.has_edge(a, b) | 
		
	
		
			
			|  |  |  | # TODO put this in a new function for diff hook | 
		
	
		
			
			|  |  |  | 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) | 
		
	
	
		
			
				|  |  | @@ -97,22 +101,24 @@ class GraphState(object): | 
		
	
		
			
			|  |  |  | def to_json(self): | 
		
	
		
			
			|  |  |  | """ Convert the graph to JSON form """ | 
		
	
		
			
			|  |  |  | ngbh = {key: tuple(value) for key, value in self.ngbh.items()} | 
		
	
		
			
			|  |  |  | return json.dumps({"vops": self.vops, "ngbh": ngbh}) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | class DiffedGraphState(GraphState): | 
		
	
		
			
			|  |  |  | """ Just like a graph state, but tracks changes for rendering purposes """ | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def __init__(self): | 
		
	
		
			
			|  |  |  | GraphState.__init__(self) | 
		
	
		
			
			|  |  |  | self.diff = [] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def add_vertex(self, v): | 
		
	
		
			
			|  |  |  | GraphState.add_vertex(self, v) | 
		
	
		
			
			|  |  |  | self.diff.append("add_vertex {}".format(v)) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def add_edge(self, v1, v2): | 
		
	
		
			
			|  |  |  | GraphState.add_edge(self, v1, v2) | 
		
	
		
			
			|  |  |  | self.diff.append("add_edge {} {}".format(v1, v2)) | 
		
	
		
			
			|  |  |  | meta = {key: value for key, value in self.meta.items()} | 
		
	
		
			
			|  |  |  | return json.dumps({"vops": self.vops, "ngbh": ngbh, "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.node = {node: {"vop": vop} for node, vop in self.vops.items()} | 
		
	
		
			
			|  |  |  | for node, metadata in self.meta.items(): | 
		
	
		
			
			|  |  |  | g.node[node].update(metadata) | 
		
	
		
			
			|  |  |  | return g | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def layout(self): | 
		
	
		
			
			|  |  |  | """ Automatically lay out the graph """ | 
		
	
		
			
			|  |  |  | g = self.to_networkx() | 
		
	
		
			
			|  |  |  | pos = nx.spring_layout(g, dim=3, scale=10) | 
		
	
		
			
			|  |  |  | for key, (x, y, z) in pos.items(): | 
		
	
		
			
			|  |  |  | self.meta[key]["pos"] = {"x": round(x, 0), "y": round(y, 0), "z": round(z, 0)} | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  |  |