def do_search(G, X, n_colors, Cso): """Local search around X using sum(|B[i]||C[i]| - |C[i]|^2) objective """ for n in G: for n1 in G[n]: assert n in G[n1], '%d %d' % (n, n1) def makeX(): X1 = [-1] * len(X) for c, cls in enumerate(color_classes): for x in cls: X1[x] = c return X1 # color_classes[c] = nodes with color c color_classes = [set([n for n, x in enumerate(X) if x == c]) for c in range(n_colors)] assert sum(len(cls) for cls in color_classes) == len(X) # broken_classes[c] = edges where both colors are c broken_classes = [set([]) for c in range(n_colors)] # We only need counts for our objective functions n_cc = [len(color_classes[c]) for c in range(n_colors)] n_bc = [len(broken_classes[c]) for c in range(n_colors)] def objective(n_cc, n_bc): return sum((2 * n_cc[c] * n_bc[c] - n_cc[c] ** 2) for c in range(n_colors)) v = objective(n_cc, n_bc) LEN = 10000 solutions = SortedDeque([(v, normalize(X), n_cc, n_bc)], LEN) tested = set() counter = count() best_v = v best_X = tuple(X) best_n_col = len([x for x in n_cc if x > 0]) normX = normalize(X) print 'best_X', v, best_n_col, color_counts(normX, n_colors), normX needs_perturbation = False while solutions: v, X, n_cc, n_bc = solutions.popleft() check_counts(G, n_colors, X, n_cc, n_bc)
def solve(n_nodes, n_edges, edges): """ Return chromatic number for graph n_nodes:number of nodes in graph n_edges: number of edges in graph edges: list of all edges Returns: number of colors, list of node colors in same order as nodes, optimal? """ # G = node: edges from node G = defaultdict(list) for n1, n2 in edges: G[n1].append(n2) G[n2].append(n1) for n in G: G[n] = frozenset(G[n]) Cso = get_Cso(G) # Lower and upper bounds on number of colors n_min = len(Cso[0][0]) n_max = len(G) print 'n_min=%d,n_max=%d' % (n_min, n_max) MAX_SOLUTIONS = 10 * 1000 MAX_VISITED = 50 * 1000 * 1000 assert MAX_VISITED >= 3 * MAX_SOLUTIONS candidate_solutions = SortedDeque([], MAX_SOLUTIONS) optimized_solutions = SortedDeque([], MAX_SOLUTIONS) visited_starting, visited_minimum, visited_tested = set([]), set([]), set([]) X = [-1] * len(G) X = populate1(G, X, Cso) def print_best(): max_min = 0 min_k = optimized_solutions[0][0] for i, soln in enumerate(optimized_solutions): if soln[0] > min_k: break max_min = i #print '=' * 80 #print log_name if os.path.exists(log_name): shutil.copy(log_name, log_name + '.old') with open(log_name , 'wt') as f: f.write('VERSION=%d\n' % VERSION) f.write('log_name=%s\n' % log_name) f.write('n_min=%d,n_max=%d\n' % (n_min, n_max)) f.write('fraction_changed=%f\n' % fraction_changed) fraction_changed f.write('candidate_solutions=%d,optimized_solutions=%d\n' % (len(candidate_solutions), len(optimized_solutions))) f.write('count=%d,visited_starting=%d,visited_tested=%d,visited_minimum=%d\n' % (count, len(visited_starting), len(visited_tested), len(visited_minimum))) f.write('lowest number colors: %d : %s\n' % (len(optimized_solutions), [optimized_solutions[i][0:3] for i in range(min(len(optimized_solutions), 50))])) f.write('best solutions: %d of %d\n' % (max_min + 1, len(optimized_solutions))) for i in range(max_min + 1): X = optimized_solutions[i][-1] optimal = len(set(X)) == n_min f.write('%3d: %s: %s\n' % (i, optimal, optimized_solutions[i])) n_colors = len(G) last_report_time = time.time() #while len(solutions) < MAX_SOLUTIONS: for i, X in enumerate(previous_X): hX = hash(normalize(X)) if hX not in visited_minimum: add_solution(optimized_solutions, G, X, -1000 - i) visited_minimum.add(hX) print '^^^', len(optimized_solutions), len(visited_minimum) fraction_changed = 0.33 for count in xrange(10**9): if count % 100 == 0: fraction_changed = 0.33 fraction_changed *= 0.9 if fraction_changed < 0.05: fraction_changed = 0.05 for visited in visited_starting, visited_minimum, visited_tested : if len(visited) >= MAX_VISITED: # 1000 * 1000: v1 = len(visited) visited = set(list(visited)[(len(visited)*2)//3:]) v2 = len(visited) visited = visited | set(s[-2] for s in optimized_solutions) print 'reseting visited', v1, v2, len(visited) #if len(solutions) % 1000 == 2: if time.time() > last_report_time + 60: print_best() last_report_time = time.time() #X = do_kempe(G, X) # Replenish candidate_solutions target_score = candidate_solutions[0][0] if candidate_solutions else 0 candidate_index = 0 for ii in range(1000): while True: X = repopulate(G, visited_starting, X, Cso, target_score, visited_minimum, fraction_changed) if X is None: print '!! Repopulate failed !!!', candidate_index, len(candidate_solutions) candidate_index += 1 target_score = candidate_solutions[candidate_index][0] X = candidate_solutions[candidate_index][-1] continue hX = hash(X) if hX not in visited_minimum and hX not in visited_tested: break print 'repopulating' add_solution(candidate_solutions, G, X, count) nn = len(set(X)) if len(candidate_solutions) >= 10 and (nn <= target_score or ii >= 10): break #print ('~', len(set(X))), print (len(candidate_solutions), nn) , print '.', # Take best candidate solution while candidate_solutions: soln = candidate_solutions.popleft() # solution = (n_colors, score, count, hash(nX), nX) if soln[3] not in visited_tested and soln[3] not in visited_minimum: X = soln[-1] break visited_tested.add(soln[3]) print soln[0], ':', fraction_changed optimal = len(set(set(X))) == n_min if not optimal: X_list = do_search(G, visited_minimum, X) for X in X_list: n_col = len(set(X)) optimal = n_col == n_min #print 'i=%d,optimal=%s' % (len(solutions), optimal) add_solution(optimized_solutions, G, X, count) add_solution(candidate_solutions, G, X, count) if n_col < n_colors: print 'n_colors %d => %d, count=%d, visited=%s, solutions=%s, fraction_changed=%f' % (n_colors, n_col, count, (len(visited_starting), len(visited_tested), len(visited_minimum)), (len(candidate_solutions), len(optimized_solutions)), fraction_changed) n_colors = n_col if optimal: break if optimal: break if len(optimized_solutions) >= 1: if random.randrange(0, 3) != 0: X = optimized_solutions[0][-1] else: max_all = len(optimized_solutions) - 1 max_min = 0 min_k = optimized_solutions[0][0] for i, soln in enumerate(optimized_solutions): if soln[0] > min_k: break max_min = i if random.randrange(0, 3) != 0: max_i = max_min else: max_i = max_all X = optimized_solutions[random.randrange(0, max_i+1)][-1] #visited = [hash(s[1]) for s in solutions] if False: print '------------' print 'solutions', len(solutions), [(solutions[i][0], (solutions[i][2], len(solutions)-solutions[i][2] -1), hash(s[1])) for i in range(min(len(solutions), 40))] print 'visited', len(visited), visited print '*^*Done' print_best() exit() for n in G: assert X[n] >= 0, '%d %s' % (n, G[n]) assert all(X[n] != X[n1] for n1 in G[n]), '\n%d %s\n%d %s' % ( n, G[n], X[n], [X[n1] for n1 in G[n]] ) for n in G: print n, X[n], G[n], [X[i] for i in G[n]] print len(set(X)), optimal print '+' * 40 return len(set(X)), X, optimal #print G assert len(G) == n_nodes #print '-' * 80 # Nodes by order C = [-1] * n_nodes C[0] = 0 Q, V = [0], set([]) while Q: n = Q.pop() if n in V: continue V.add(n) #print n, G[n], for c in count(): # print c, if not any(c == C[i] for i in G[n]): break C[n] = c #print ':', c for m in G[n]: Q.append(m) #print '-' * 80 #print C #print '=' * 80 return len(set(C)), [C[i] for i in range(n_nodes)], True
def solve(n_nodes, n_edges, edges): """ Return chromatic number for graph n_nodes:number of nodes in graph n_edges: number of edges in graph edges: list of all edges Returns: number of colors, list of node colors in same order as nodes, optimal? """ # G = node: edges from node G = defaultdict(list) for n1, n2 in edges: G[n1].append(n2) G[n2].append(n1) for n in G: G[n] = frozenset(G[n]) Cso = get_Cso(G) # Lower and upper bounds on number of colors n_min = len(Cso[0][0]) n_max = len(G) print "n_min=%d,n_max=%d" % (n_min, n_max) MAX_SOLUTIONS = 10 * 1000 MAX_VISITED = 1000 * 1000 assert MAX_VISITED >= 3 * MAX_SOLUTIONS candidate_solutions = SortedDeque([], MAX_SOLUTIONS) optimized_solutions = SortedDeque([], MAX_SOLUTIONS) visited_starting, visited_minimum, visited_tested = set([]), set([]), set([]) X = [-1] * len(G) X = populate1(G, X, Cso) def print_best(): max_min = 0 min_k = optimized_solutions[0][0] for i, soln in enumerate(optimized_solutions): if soln[0] > min_k: break max_min = i # print '=' * 80 # print log_name if os.path.exists(log_name): shutil.copy(log_name, log_name + ".old") with open(log_name, "wt") as f: f.write("VERSION=%d\n" % VERSION) f.write("log_name=%s\n" % log_name) f.write("n_min=%d,n_max=%d\n" % (n_min, n_max)) f.write( "candidate_solutions=%d,optimized_solutions=%d\n" % (len(candidate_solutions), len(optimized_solutions)) ) f.write( "count=%d,visited_starting=%d,visited_tested=%d,visited_minimum=%d\n" % (count, len(visited_starting), len(visited_tested), len(visited_minimum)) ) f.write( "lowest number colors: %d : %s\n" % ( len(optimized_solutions), [optimized_solutions[i][0:3] for i in range(min(len(optimized_solutions), 50))], ) ) f.write("best solutions: %d of %d\n" % (max_min + 1, len(optimized_solutions))) for i in range(max_min + 1): X = optimized_solutions[i][-1] optimal = len(set(X)) == n_min f.write("%3d: %s: %s\n" % (i, optimal, optimized_solutions[i])) n_colors = len(G) last_report_time = time.time() # while len(solutions) < MAX_SOLUTIONS: for count in xrange(10 ** 9): for visited in visited_starting, visited_minimum, visited_tested: if len(visited) >= MAX_VISITED: # 1000 * 1000: v1 = len(visited) visited = set(list(visited)[(len(visited) * 2) // 3 :]) v2 = len(visited) visited = visited | set(s[-2] for s in solutions) print "reseting visited", v1, v2, len(visited) # if len(solutions) % 1000 == 2: if time.time() > last_report_time + 60: print_best() last_report_time = time.time() # X = do_kempe(G, X) # Replenish candidate_solutions for ii in range(1000): while True: X = repopulate(G, visited_starting, X, Cso) hX = hash(X) if hX not in visited_minimum and hX not in visited_tested: break print "repopulating" add_solution(candidate_solutions, G, X, count) nn = len(set(X)) if len(candidate_solutions) >= 10 and (nn <= candidate_solutions[0][0] or ii >= 10): break # print ('~', len(set(X))), print (len(candidate_solutions), nn), print ".", # Take best candidate solution while candidate_solutions: soln = candidate_solutions.popleft() # solution = (n_colors, score, count, hash(nX), nX) if soln[3] not in visited_tested and soln[3] not in visited_minimum: X = soln[-1] break visited_tested.add(soln[3]) print soln[0] optimal = len(set(set(X))) == n_min if not optimal: X = do_search(G, visited_minimum, X) n_col = len(set(X)) optimal = n_col == n_min # print 'i=%d,optimal=%s' % (len(solutions), optimal) add_solution(optimized_solutions, G, X, count) add_solution(candidate_solutions, G, X, count) if n_col < n_colors: print "n_colors %d => %d, count=%d, visited=%s, solutions=%s" % ( n_colors, n_col, count, (len(visited_starting), len(visited_tested), len(visited_minimum)), (len(candidate_solutions), len(optimized_solutions)), ) n_colors = n_col if optimal: break if len(optimized_solutions) >= 1: if random.randrange(0, 3) != 0: X = optimized_solutions[0][-1] else: max_all = len(optimized_solutions) - 1 max_min = 0 min_k = optimized_solutions[0][0] for i, soln in enumerate(optimized_solutions): if soln[0] > min_k: break max_min = i if random.randrange(0, 3) != 0: max_i = max_min else: max_i = max_all X = optimized_solutions[random.randrange(0, max_i + 1)][-1] # visited = [hash(s[1]) for s in solutions] if False: print "------------" print "solutions", len(solutions), [ (solutions[i][0], (solutions[i][2], len(solutions) - solutions[i][2] - 1), hash(s[1])) for i in range(min(len(solutions), 40)) ] print "visited", len(visited), visited print "*^*Done" print_best() exit() for n in G: assert X[n] >= 0, "%d %s" % (n, G[n]) assert all(X[n] != X[n1] for n1 in G[n]), "\n%d %s\n%d %s" % (n, G[n], X[n], [X[n1] for n1 in G[n]]) for n in G: print n, X[n], G[n], [X[i] for i in G[n]] print len(set(X)), optimal print "+" * 40 return len(set(X)), X, optimal # print G assert len(G) == n_nodes # print '-' * 80 # Nodes by order C = [-1] * n_nodes C[0] = 0 Q, V = [0], set([]) while Q: n = Q.pop() if n in V: continue V.add(n) # print n, G[n], for c in count(): # print c, if not any(c == C[i] for i in G[n]): break C[n] = c # print ':', c for m in G[n]: Q.append(m) # print '-' * 80 # print C # print '=' * 80 return len(set(C)), [C[i] for i in range(n_nodes)], True
def do_search(G, X, n_colors): """Local search around X using sum(|B[i]||C[i]| - |C[i]|^2) objective """ for n in G: for n1 in G[n]: assert n in G[n1], '%d %d' % (n, n1) def makeX(): X1 = [-1] * len(X) for c, cls in enumerate(color_classes): for x in cls: X1[x] = c return X1 # color_classes[c] = nodes with color c color_classes = [set([n for n, x in enumerate(X) if x == c]) for c in range(n_colors)] assert sum(len(cls) for cls in color_classes) == len(X) # broken_classes[c] = edges where both colors are c broken_classes = [set([]) for c in range(n_colors)] # We only need counts for our objective functions n_cc = [len(color_classes[c]) for c in range(n_colors)] n_bc = [len(broken_classes[c]) for c in range(n_colors)] def objective(n_cc, n_bc): return sum((2 * n_cc[c] * n_bc[c] - n_cc[c] ** 2) for c in range(n_colors)) #def delta(n, c, n_cc_c0, n_cc_c, n_bc_c0, n_bc_c): # c0 = X(G[n]) # before = 2 * n_cc_c0 * n_bc_c0 - n_cc_c0**2 # n_cc_c0 -= 1 # n_cc_c += 1 # n_bc_c0 -= sum((int(X(G[n1]) == c0) for n1 in G[n]) # n_bc_c += sum((int(X(G[n1]) == c) for n1 in G[n]) # after = 2 * n_cc_c0 * n_bc_c0 - n_cc_c0**2 # return after - before v = objective(n_cc, n_bc) LEN = 10000 solutions = SortedDeque([(v, tuple(X), n_cc, n_bc)], LEN) tested = set() counter = count() best_v = v best_X = tuple(X) best_n_col = len([x for x in n_cc if x > 0]) normX = normalize(X) print 'best_X', v, best_n_col, color_counts(normX), normX while solutions: v, X, n_cc, n_bc = solutions.pop() if hash(X) in tested: continue tested.add(hash(X)) #assert v == objective(n_cc, n_bc) n_col = len([x for x in n_cc if x > 0]) print '*', v, n_col, len(solutions), len(tested), best_n_col #, X, n_cc, n_bc if False: if len(tested) % 100 == 1: print X print n_cc print n_bc validate(G, X) if n_col < best_n_col or (n_col == best_n_col and v < best_v): if n_col < best_n_col: normX = normalize(X) print 'best_X', v, n_col, color_counts(normX), normX validate(G, X) best_v = v best_X = X[:] best_n_col = n_col for n in G: c0 = X[n] for c in range(n_colors): # Looking for a new color if c == c0: continue #X2[n] = c #d = delta(n, c, n_cc[c0], n_cc[c], n_bc[c0], n_bc[c]) c0 = X[n] # Can't remove any c0 color nodes if none exist if n_cc[c0] == 0: continue n_cc_c0, n_cc_c, n_bc_c0, n_bc_c = n_cc[c0], n_cc[c], n_bc[c0], n_bc[c] before = 2 * n_cc_c0 * n_bc_c0 - n_cc_c0**2 + 2 * n_cc_c * n_bc_c - n_cc_c**2 n_cc_c0 -= 1 n_cc_c += 1 n_bc_c0 -= sum(int(X[n1] == c0) for n1 in G[n]) n_bc_c += sum(int(X[n1] == c) for n1 in G[n]) after = 2 * n_cc_c0 * n_bc_c0 - n_cc_c0**2 + 2 * n_cc_c * n_bc_c - n_cc_c**2 #print '&', before, after if after < before: # and v + after - before > best_v: X2, n_cc2, n_bc2 = list(X), n_cc[:], n_bc[:] X2[n] = c n_cc2[c0], n_cc2[c], n_bc2[c0], n_bc2[c] = n_cc_c0, n_cc_c, n_bc_c0, n_bc_c v2 = objective(n_cc2, n_bc2) #assert v2 == v + after - before tX2 = tuple(X2) if hash(tX2) in tested: continue solutions.insert((v + after - before, tX2, n_cc2, n_bc2)) return best_X