def main(): """ Main event dispatcher. Returns True if cycle should be reported, False or None otherwise. Appends recursive search of contracted graph to action stack. """ if degree_two: return handle_degree_two() # At this point, we are going to branch, and if the time is really # exponential in the size of the remaining graph we can afford some # more expensive pruning steps first. # If graph is Hamiltonian it must be biconnected if not isBiconnected(G): return None # If graph is Hamiltonian, unforced edges must have a perfect matching. # We jump-start the matching algorithm with our previously computed # matching (or as much of it as fits the current graph) since that is # likely to be near-perfect. unforced = dict([(v, {}) for v in G]) for v in G: for w in G[v]: if w not in forced_in_current[v]: unforced[v][w] = True for v in previous_matching.keys(): if v not in unforced or previous_matching[v] not in unforced[v]: del previous_matching[v] M = matching(unforced, previous_matching) previous_matching.clear() previous_matching.update(M) if len(M) != len(G): return None # Here with a degree three graph in which the forced edges # form a matching. Pick an unforced edge adjacent to a # forced one, if possible, else pick any unforced edge, # and branch on the chosen edge. if forced_vertices: v = arbitrary_item(forced_vertices) else: v = arbitrary_item(G) w = [x for x in G[v] if x not in forced_in_current[v]][0] def continuation(): """Here after searching first recursive subgraph.""" if force(v, w): actions.append(main) actions.append(continuation) if safely_remove(v, w): actions.append(main)
def __init__(self,D): # refine partition of states by reversed neighborhoods N = D.reverse() P = PartitionRefinement(D.states()) P.refine([s for s in D.states() if D.isfinal(s)]) unrefined = Sequence(P,key=id) while unrefined: part = arbitrary_item(unrefined) unrefined.remove(part) for symbol in D.alphabet: neighbors = Set() for state in part: neighbors |= N.transition(state,symbol) for new,old in P.refine(neighbors): if old in unrefined or len(new) < len(old): unrefined.append(new) else: unrefined.append(old) # convert partition to DFA P.freeze() self.partition = P self.initial = P[D.initial] self.alphabet = D.alphabet self.DFA = D
def check(self, G, N): """Make sure G has N Hamiltonian cycles.""" count = 0 for C in HamiltonianCycles(G): # Count the cycle. count += 1 # Check that it's a degree-two undirected subgraph. for v in C: self.assertEqual(len(C[v]), 2) for w in C[v]: assert v in G and w in G[v] and v in C[w] # Check that it connects all vertices. nreached = 0 x = arbitrary_item(G) a, b = x, x while True: nreached += 1 a, b = b, [z for z in C[b] if z != a][0] if b == x: break self.assertEqual(nreached, len(G)) # Did we find enough cycles? self.assertEqual(count, N)
def states(self): visited = Set() unvisited = Set(self.initial) while unvisited: state = arbitrary_item(unvisited) yield state unvisited.remove(state) visited.add(state) for symbol in self.alphabet: unvisited |= self.transition(state,symbol) - visited
def LexBFS(G): """Find lexicographic breadth-first-search traversal order of a graph. G should be represented in such a way that "for v in G" loops through the vertices, and "G[v]" produces a sequence of the neighbors of v; for instance, G may be a dictionary mapping each vertex to its neighbor set. Running time is O(n+m) and additional space usage over G is O(n). """ P = PartitionRefinement(G) S = Sequence(P, key=id) while S: set = S[0] v = arbitrary_item(set) yield v P.remove(v) if not set: S.remove(set) for new,old in P.refine(G[v]): S.insertBefore(old,new)
def greedyMatching(G, initialMatching=None): """Near-linear-time greedy heuristic for creating high-cardinality matching. If there is any vertex with one unmatched neighbor, we match it. Otherwise, if there is a vertex with two unmatched neighbors, we contract it away and store the contraction on a stack for later matching. If neither of these two cases applies, we match an arbitrary edge. """ # Copy initial matching so we can use it nondestructively matching = {} if initialMatching: for x in initialMatching: matching[x] = initialMatching[x] # Copy graph to new subgraph of available edges # Representation: nested dictionary rep->rep->pair # where the reps are representative vertices for merged clusters # and the pair is an unmatched original pair of vertices avail = {} has_edge = False for v in G: if v not in matching: avail[v] = {} for w in G[v]: if w not in matching: avail[v][w] = (v,w) has_edge = True if not avail[v]: del avail[v] if not has_edge: return matching # make sets of degree one and degree two vertices deg1 = Set([v for v in avail if len(avail[v]) == 1]) deg2 = Set([v for v in avail if len(avail[v]) == 2]) d2edges = [] def updateDegree(v): """Cluster degree changed, update sets.""" if v in deg1: deg1.remove(v) elif v in deg2: deg2.remove(v) if len(avail[v]) == 0: del avail[v] elif len(avail[v]) == 1: deg1.add(v) elif len(avail[v]) == 2: deg2.add(v) def addMatch(v,w): """Add edge connecting two given cluster reps, update avail.""" p,q = avail[v][w] matching[p] = q matching[q] = p for x in avail[v].keys(): if x != w: del avail[x][v] updateDegree(x) for x in avail[w].keys(): if x != v: del avail[x][w] updateDegree(x) avail[v] = avail[w] = {} updateDegree(v) updateDegree(w) def contract(v): """Handle degree two vertex.""" u,w = avail[v] # find reps for two neighbors d2edges.extend([avail[v][u],avail[v][w]]) del avail[u][v] del avail[w][v] if len(avail[u]) > len(avail[w]): u,w = w,u # swap to preserve near-linear time bound for x in avail[u].keys(): del avail[x][u] if x in avail[w]: updateDegree(x) elif x != w: avail[x][w] = avail[w][x] = avail[u][x] avail[u] = avail[v] = {} updateDegree(u) updateDegree(v) updateDegree(w) # loop adding edges or contracting deg2 clusters while avail: if deg1: v = arbitrary_item(deg1) w = arbitrary_item(avail[v]) addMatch(v,w) elif deg2: v = arbitrary_item(deg2) contract(v) else: v = arbitrary_item(avail) w = arbitrary_item(avail[v]) addMatch(v,w) # at this point the edges listed in d2edges form a matchable tree # repeat the degree one part of the algorithm only on those edges avail = {} d2edges = [(u,v) for u,v in d2edges if u not in matching and v not in matching] for u,v in d2edges: avail[u] = {} avail[v] = {} for u,v in d2edges: avail[u][v] = avail[v][u] = (u,v) deg1 = Set([v for v in avail if len(avail[v]) == 1]) while deg1: v = arbitrary_item(deg1) w = arbitrary_item(avail[v]) addMatch(v,w) return matching
def isfinal(self,state): rep = arbitrary_item(state) return self.DFA.isfinal(rep)
def transition(self,state,symbol): rep = arbitrary_item(state) return self.partition[self.DFA.transition(rep,symbol)]