Given an edge-coloring ec, compute the $c^*$ value at a vertex, which is the color-blind partition at $v$.
{{{id=1| def cstar(G, v, k, ec): c = [0] * k; for x,y in ec.keys(): if v in [x,y]: c[ec[(x,y)]] += 1; c.sort(); return c; /// }}}BFSOrder returns a list of the edges of $G$ that match the order they would be visited by a breadth-first search.
{{{id=3| def BFSOrder(G, u): O = [u]; Q = [u]; d = {}; for v in G.vertices(): d[v] = G.num_verts(); d[u] = 0; while len(Q) > 0: v = Q[0]; Q.remove(v); for y in G.neighbors(v): if d[y] == G.num_verts(): d[y] = d[v] + 1; Q.append(y); O.append(y); return O; /// }}}colexEdgeOrder returns a list of the edges of $G$ minimizing the co-lexicographic rank of the pair $uv$.
{{{id=5| def colexEdgeOrder(G, Vorder): Eorder = []; for i in range(0,len(Vorder)): for j in range(0,i): if G.has_edge( [Vorder[i],Vorder[j]] ): Eorder.append( (Vorder[i],Vorder[j]) ); return Eorder; /// }}}getColorBlindPartition does the same as cstar
{{{id=7| def getColorBlindPartition(G,r, v, coloring): overc = [0]*r; for u in G.neighbors(v): edge1 = (v,u); edge2 = (u,v); if edge1 in coloring.keys(): overc[ coloring[edge1] ] += 1; elif edge2 in coloring.keys(): overc[ coloring[edge2] ] += 1; else: return None; # not fully defined! # overc is determined! overc.sort(); # ok, now we have it! return overc; /// }}}compareLists checks if two sorted lists contain the same elements.
{{{id=9| def compareLists( l1, l2 ): if l1 == None: return False; if l2 == None: return False; if len(l1) != len(l2): return False; for i in range( len(l1) ): if l1[i] != l2[i]: return False; return True; /// }}}dal computes the color-blind index of a graph $G$, it returns a pair $(r,c)$ where $c$ is a $r$-dal-coloring of $G$. It uses the recursive method findColorBlindColoring.
If no dal-coloring exists, then (None,None) is returned.
{{{id=11| def findColorBlindColoring(G, r, EdgeOrder, coloring, cur_edge, covered_vertices = [], timeout = None ): if timeout != None and timeout < time(): return None, "Timeout!"; for u,v,e in G.edges(): cstaru = getColorBlindPartition(G, r, u, coloring); cstarv = getColorBlindPartition(G, r, v, coloring); if compareLists(cstaru, cstarv): return None, None; if cur_edge >= len(EdgeOrder): return r, coloring; # create a random offset, helps to explore random-ish colorings first. offset = int(random()* r); # select a color for cur_edge... for i in range(r): e = EdgeOrder[cur_edge]; u = e[0]; v = e[1]; newcolors = {}; for f in coloring.keys(): newcolors[f] = coloring[f]; newcolors[ e ] = (i + offset) % r; # Determine if this is a good choice... # Does it distinguish any pair that is determined so far? newr,c = findColorBlindColoring(G, r, EdgeOrder, newcolors, cur_edge + 1, timeout = timeout ); if c != None: return newr,c; # otherwise, loop again. return None, None; def dal(G, maxtime=None, r=1): if not G.is_connected(): val = 0; fullc = {}; for C in G.components(): v,c = dal(C); if v == None: return None,None; for e in c.keys(): fullc[e] = c[e]; if v > val: val = v; return val, fullc; k = G.chromatic_number(); D = G.degree_sequence()[0]; if Partitions(D).cardinality() < k: # a very simple failure! return None, None; # if dal is not defined, then we will spin our wheels for a long time. # when do we know we can stop? while r < G.num_edges(): # We will try to find an edge-coloring on r colors # that distinguishes all neighbors. # Let's select a vertex ordering based on BFS u = None; du = G.num_verts(); for v in G.vertices(): if G.degree(v) < du: u = v; du = v; # ok, u is a vertex of minimum degree. VOrder = BFSOrder(G, u); Eorder = colexEdgeOrder(G, VOrder); timeout = None; if maxtime != None: timeout = time() + r * maxtime; newr, coloring = findColorBlindColoring(G, r, Eorder, {}, 0, timeout=timeout); if coloring != None: return newr, coloring; print "No coloring for r=%d" % r; r += 1; return None, None; /// }}}ShowEdgeColoredGraph draws the graph $G$ with colors representing the colors on the edges of $G$.
{{{id=13| def ShowEdgeColoredGraph(G, r, c): color_names = ["red", "green", "black", "blue", "purple" ]; edge_colors = {}; for i in range(r): edge_colors[ color_names[i] ] = []; for e in c.keys(): if c[e] == i: edge_colors[ color_names[i] ].append(e); G.show(save_pos=True,edge_colors=edge_colors); /// }}}dalColorings is an iterator for all colorings of some edges and with some vertices being assigned a $c^*$-value. This is used to test reducibility.
{{{id=15| class dalColorings(object): def __init__(self, G, k, to_assign_cstar=[], show=True): self.G = G; self.k = k; self.ec = {}; self.vc = {}; self.i = 0; self.to_assign = to_assign_cstar; self.show = show; self.colorings = []; self.E = G.edges(); # colexEdgeOrder(G, G.vertices()); def __iter__(self): return self; def __next__(self): return self.next(); def next(self): # goal: construct the NEXT coloring! if self.i >= len(self.E): self.i -= 1; while self.i >= 0: if self.i >= len(self.E): return self.ec; m = 0; for j in range(self.i): x,y,_ = self.E[j]; if self.ec[(x,y)] >= m: m = self.ec[(x,y)]+1; d = 0; u,v,_ = self.E[self.i]; if (u,v) in self.ec.keys(): d = self.ec[(u,v)] + 1; if d >= min(self.k, m + 1): del self.ec[(u,v)]; self.i -= 1; continue; # try the next color! self.ec[(u,v)] = d; # does it work? fails = False; cstaru = getColorBlindPartition(self.G, self.k, u, self.ec); cstarv = getColorBlindPartition(self.G, self.k, v, self.ec); if cstaru != None: for x in self.G.neighbors(u): cstarx = getColorBlindPartition(self.G, self.k, x, self.ec); if compareLists(cstarx, cstaru): fails = True; if cstarv != None: for x in self.G.neighbors(v): cstarx = getColorBlindPartition(self.G, self.k, x, self.ec); if compareLists(cstarx, cstarv): fails = True; if not fails: self.i += 1; # loop ends... so we are done with things! raise StopIteration(); /// }}}canExtend tests if a given edge coloring and $c^*$-assignment to some vertices can be extended to a dal-coloring of the configuration (where $c^*(v) \neq c^*(u)$ for all edges $uv$, even if one of the endpoints was preset).
{{{id=17| def CERecurse(G, k, E, c, cstar, i): #print i, E; for x,y,_ in G.edges(): if x in cstar.keys(): cx = cstar[x]; else: cx = getColorBlindPartition(G,k,x,c); if y in cstar.keys(): cy = cstar[y]; else: cy = getColorBlindPartition(G,k,y,c); if compareLists(cx,cy): return False; if i >= len(E): return c; x,y = E[i]; for d in range(0,k): c[(x,y)] = d; cp = CERecurse(G, k, E, c, cstar, i+1); if cp != False: return cp; del c[(x,y)]; return False; def canExtend(G, k, c, cstar): E = []; for x,y,e in G.edges(): if (x,y) not in c.keys() and (y,x) not in c.keys(): E.append( (x,y) ); return CERecurse(G, k, E, c, cstar, 0); /// }}}enumerate3Colorings enumerates all possible 3-edge-colorings of a subset of edges and 3-colorings of the specified vertices. Used for reducibility.
{{{id=19| def e3CRecurse(H, tac, E, vc, ec, vi, ei): l = []; if vi < len(tac): # select partitions! for p in [ [1,1,1], [0,1,2], [0,0,3] ]: vc[tac[vi]] = p; lp = e3CRecurse(H, tac, E, vc, ec, vi + 1, ei); for vcp, ecp in lp: l.append( (vcp,ecp) ); del vc[tac[vi]]; return l; if ei >= len(E): vcp = {}; ecp = {}; for v in vc.keys(): vcp[v] = vc[v]; for e in ec.keys(): ecp[e] = ec[e]; return [ (vcp, ecp) ]; for c in range(3): ec[E[ei]] = c; lp = e3CRecurse(H, tac, E, vc, ec, vi, ei + 1); for vcp, ecp in lp: l.append( (vcp,ecp) ); del ec[E[ei]]; return l; def enumerate3Colorings(H, to_assign_cstar): vc = {}; ec = {}; E = H.edges(); return e3CRecurse(H, to_assign_cstar, E, vc, ec, 0, 0); /// }}}is3Reducible determines if a configuration is reducible after deleting some vertices and edges.
{{{id=20| def is3Reducible(G, Deletion): H = Graph(); count = 0; to_assign_cstar = []; for x,y,e in G.edges(): if x not in Deletion and y in Deletion: H.add_edge(x,y); if x not in to_assign_cstar: to_assign_cstar.append(x); if y not in Deletion and x in Deletion: H.add_edge(x,y); if y not in to_assign_cstar: to_assign_cstar.append(y); F = Graph(G.edges()); for v in G.vertices(): if v not in Deletion and v not in to_assign_cstar: F.delete_vertex(v); hpos = {}; for v in H.vertices(): if v in G.vertices(): hpos[v] = G.get_pos()[v]; H.set_pos(hpos); H.show(); for vc, ec in enumerate3Colorings(H, to_assign_cstar): # try to extend to edges of G count += 1; #print vc; #print ec; ecp = canExtend(F, 3, ec, vc); if not ecp: print "Vertices:", vc; print "Edges:", ec; ShowEdgeColoredGraph(F, 3, ec); return False; #else: # ShowEdgeColoredGraph(F, 3, ecp); if count % 100 == 0: print "checked %d colorings" % count; return True; /// }}}is3DCReducible tests if a configuration is reducible after deleting some vertices and edges and contracting others.
{{{id=22| def is3DCReducible(G, Deletion, Matching): H = Graph(); count = 0; to_assign_cstar = []; for x,y,e in G.edges(): if x not in Deletion and y in Deletion: H.add_edge(x,y); if x not in to_assign_cstar: to_assign_cstar.append(x); if y not in Deletion and x in Deletion: H.add_edge(x,y); if y not in to_assign_cstar: to_assign_cstar.append(y); F = Graph(G.edges()); for v in G.vertices(): if v not in Deletion and v not in to_assign_cstar: F.delete_vertex(v); hpos = {}; for v in H.vertices(): if v in G.vertices(): hpos[v] = G.get_pos()[v]; H.set_pos(hpos); H.show(); for vc, ec in enumerate3Colorings(H, to_assign_cstar): valid = True; for x,y in Matching: if compareLists(vc[x],vc[y]): valid = False; if not valid: continue; # try to extend to edges of G count += 1; #print vc; #print ec; ecp = canExtend(F, 3, ec, vc); if not ecp: print "Vertices:", vc; print "Edges:", ec; ShowEdgeColoredGraph(F, 3, ec); return False; #else: # ShowEdgeColoredGraph(F, 3, ecp); if count % 100 == 0: print "checked %d colorings" % count; return True; /// }}}We now test if the 1-buoy reduction is reducible.
{{{id=31| G = Graph(); G.add_cycle(range( 0, 3)); G.add_cycle(range( 3, 7)); G.add_edge(0,3); G.add_edge(4,6); G.add_edge(1,7); G.add_edge(2,8); G.add_edge(5,9); pos = {}; pos[0] = (0, 0); pos[3] = ( 1, 0); pos[1] = (-1,-1); pos[7] = (-2,-2); pos[2] = (-1, 1); pos[8] = (-2, 2); pos[4] = (2,1); pos[5] = (3,0); pos[6] = (2,-1); pos[9] = (4,0); G.set_pos(pos) G.show(save_pos=True); print is3DCReducible(G, range(0,7), [ [7,8] ]) ///We now test that the 2-triangle reduction is reducible.
{{{id=25| G = Graph(); G.add_cycle(range( 0, 3)); G.add_cycle(range( 3, 6)); G.add_cycle(range( 6, 9)); G.add_cycle(range( 9,12)); G.add_edge(0,3); G.add_edge(1,4); G.add_edge(2,6); G.add_edge(5,9); G.add_edge(7,12); G.add_edge(8,13); G.add_edge(10,14); G.add_edge(11,15); pos = {}; pos[0] = (-1, 1); pos[3] = ( 1, 1); pos[1] = (-1,-1); pos[4] = ( 1,-1); pos[2] = (-2, 0); pos[5] = ( 2, 0); pos[9] = ( 3, 0); pos[10] = (4, 1); pos[11] = (4,-1); pos[14] = (5, 2); pos[15] = (5,-2); pos[6] = (-3,0); pos[7] = (-4,1); pos[8] = (-4,-1); pos[12] = (-5, 2); pos[13] = (-5,-2); G.set_pos(pos) G.show(save_pos=True); print is3Reducible(G, range(6)) ///We now test if the 2-buoy reduction is reducible.
{{{id=27| G = Graph(); G.add_cycle(range( 0, 4)); G.add_cycle(range( 4, 8)); G.add_cycle(range( 8, 11)); G.add_cycle(range( 11,14)); G.add_edge(0,2); G.add_edge(4,6); G.add_edges([ [1,8], [3,5], [ 7, 11] ] ); pos = {}; pos[0] = (-2, 1); pos[1] = (-3, 0); pos[2] = (-2,-1); pos[3] = (-1, 0); pos[4] = ( 2, 1); pos[5] = ( 1, 0); pos[6] = ( 2,-1); pos[7] = ( 3, 0); pos[8] = (-4,0); pos[9] = (-5,1); pos[10] = (-5,-1); pos[11] = ( 4,0); pos[12] = ( 5,1); pos[13] = ( 5,-1); G.set_pos(pos) G.show(save_pos=True); print is3DCReducible(G, range(8), [ [8,11] ]) ///We now test if the sparse reduction is reducible.
{{{id=29| G = Graph(); G.add_cycle(range( 0, 3)); G.add_cycle(range( 3, 6)); G.add_cycle(range( 6, 9)); G.add_cycle(range( 9,12)); G.add_cycle(range(12,15)); G.add_cycle(range(15,18)); G.add_edges( [ [0,6], [2,9], [1,4], [3,15], [5,12] ] ); G.delete_vertices([7,8,10,11,13,14,16,17]); pos = {}; pos[1] = (-1,0); pos[4] = (1,0); pos[0] = (-2,1); pos[2] = (-2,-1); pos[6] = (-3,2); pos[9] = (-3,-2); pos[3] = (2,1); pos[5] = (2,-1); pos[12]= (3,-2); pos[15] = (3,2); G.set_pos(pos); G.show(); print is3DCReducible(G, range(6), [ [12,15], [9,6] ]) ///We now create a variable gadget and test all possible colorings (see Claim 2.3 in the paper).
{{{id=33| G = Graph(); G.add_edges([ ("p%d"%i,"p%d"%(i+1)) for i in range(7) ]); G.add_edges([ ("p%d"%i,"v%d"%i) for i in range(1, 7) ]); G.add_edges([ ("v%d"%i,"r%d"%(2*i-1)) for i in range(1, 7) ]); G.add_edges([ ("v%d"%i,"r%d"%(2*i)) for i in range(1, 7) ]); pos = {}; for i in range(8): pos["p%d"%i] = (i,0); for i in range(1,7): pos["v%d"%i] = (i,1); pos["r%d"%(2*i-1)] = (i-0.25,2); pos["r%d"%(2*i)] = (i+0.25,2); G.set_pos(pos); G.show() ///