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