class IndexMap(DiGraph): def __init__(self, generators=None, regions=None, transitions=None, debug=False): """ Inputs: generators -- a matix representing mapping of generators in rows onto generators in columns. regions -- a dictionary of lists representing generators per region. transitions -- An adjacency matrix representing the transitions between regions in phase space. debug (optional) = a boolean representing if debug statements should be printed """ self.generators = generators self.regions = regions self.transitions = DiGraph() self.transitions.from_numpy_matrix( transitions ) self.library = UnverifiedLibrary() self.debug = debug # Graph that will store the backend reprsentation DiGraph.__init__(self) # Utility functions for packing and unpacking edge information into an integer self.regioncount = len( self.regions ) self.edge2hash = lambda start, end: start * self.regioncount + end self.hash2edge = lambda hashed: hashed / self.regioncount, #hashed%regioncount # source and sink along one-step paths phase space (maps between regions) for s, t in self.transitions.edges(): # generators supported on each region r2g = ( self.regions[ s ], self.regions[ t ] ) matidx = ( r2g[0][0],r2g[0][-1]+1, r2g[1][0],r2g[1][-1]+1 ) edge = Walk(start=s, end=t, edges=frozenset( [(s,t)] ),#frozenset( [self.edge2hash( s, t )] ), matrix=self.generators[ matidx[2]:matidx[3], matidx[0]:matidx[1] ], length=1 ) if debug: print "Walk from ", s, " --> ", t print " region to generator map:", r2g[0], r2g[1] print " corresponding edge matrix ('color'):\n", edge.matrix print " mat.shape:", edge.matrix.shape print "" if not edge.zero(): self.graph.add_edge( s, t, edge=edge ) self.library.add( edge )
class IndexMapProcessor(object): """ Processes a given index map of generators. """ def __init__(self, indexmap, check_trace=False, debug=False): """ indexmap -- IndexMap object """ self.debug = debug self.check_trace = check_trace self.map = indexmap # Stores all edge sets that need to have one edge cut self.bad_edges = BadLibrary() # Unravel the index map (stored as a graph) into list of 1-step walks. self.edge_walks = {} for start, end, attr in self.map.graph.edges_iter(data=True): self.edge_walks[(start, end)] = attr["edge"] # self.edge_walks = [ attr["edge"] ] # Stores edges that need to be expanded, to search for more edges # init with the 1-step walks above self.todo = deque(self.edge_walks.values()) # Stores all edges that are unverified # again, init with the 1-step walks self.unverified = UnverifiedLibrary(self.edge_walks.values()) def check_walk(self, walk): """ Check whether the matrix product is zero. Returns True if either the matrix product is zero or the trace of a cycle is zero. """ # If matrix product is zero matrix, always add to bad edge # set, so check first if walk.zero(): return True # If we pass the zero matrix condition, and check_trace==True, # then check if we're a cycle and if the trace is zero. if self.check_trace: if walk.cycle() and walk.zero_trace(): return True return False def find_bad_edge_sets(self, maxLength): """ Version of Algorithms 6 in Day, Frongillo, Trevino. maxLength -- maximum length of the paths allowed in edge verifications. Overview of algorithm: --------------------- self.todo -- Holds most recent Walks to be extended and verified. self.unverified -- Holds _all_ unverified Walks. Extended paths from todo are checked against those in unverified to see if a reduction exists. """ if self.debug: maxlen = 1 while self.todo[0].length <= maxLength: # Grab the first walk to extend old = self.todo.popleft() if self.debug: print "Extending:", old print "-------------------" print " matrix", old.matrix if old.length > maxlen: maxlen = old.length print "" print "**** LENGTH ****", maxlen print "" # Now take every edge that comes out from the end of the old walk for succ in self.map.graph.successors_iter(old.end): next_step = self.edge_walks[(old.end, succ)] if self.debug: print "Successor edge:", next_step print " matrix" print next_step.matrix # for next_step in ( append["edge"] for append in self.map[old.end] ): # Create a new walk by appending the new edge new_walk = old + next_step if self.debug: print "--> New walk:" print new_walk print new_walk.matrix # Check if the walk has an edgeset that will already be cut... # If so, ignore it if new_walk.edges in self.bad_edges: continue # Check if the walk is a zero matrix, or is a cycle with zero # trace. If so, add it to the bad edge list. if self.check_walk(new_walk): if self.debug: print "==========================" print "BADS", self.bad_edges.bads print " ** Adding BAD edgeset **" print new_walk.edges print new_walk.matrix print "==========================" self.bad_edges.add(new_walk.edges) continue # Let p = new_walk. If there exists # another walk, q, such that (i) p == s-u and q == s-u # (share same edges), (ii) |q| <= |p| (p is longer # than q), (iii) and q.matrix = a * p.matrix, then we # can ignore new_walk "by induction" if self.unverified.reduction_exists(new_walk, debug=self.debug): continue # We have not decided if this path is bad or good, so it has to # added back into the queue of edges to check self.todo.append(new_walk) self.unverified.add(new_walk) if self.debug: print "\n\n" def cut_bad_edge_sets(self, **kwargs): """ Remove all edges in """ fargs = {bad_edge_sets: self.bad_edges.bad, cut_edges: [], excluded_edges: [], best_entropy: -1, cutoff: 10000} def contruct_symbolic_system(self): """ Construct a symbolic system on phase space from allowable transitions. """ pass