// // Created on 12.10.21. // #ifndef CPP_HEURISTICS_H #define CPP_HEURISTICS_H #include "conflict_graph.h" #include "solution.h" #include #include #include "bucket_queue.h" #include #include #include #include using namespace std; namespace planarity { class LimitedList { private: struct LimitedListNode { node_t value{}; std::shared_ptr prev = nullptr; }; std::size_t _cnt = 0; std::shared_ptr _first = nullptr; std::shared_ptr _last = nullptr; public: std::size_t length; explicit LimitedList(std::size_t length) : length(length) { } node_t add(node_t value) { node_t ret = INVALID_NODE; if (_first == nullptr) { _first = std::make_shared(); _first->value = value; _last = _first; _cnt++; } else { auto newElement = std::make_shared(); _first->prev = newElement; newElement->value = value; _first = newElement; _cnt++; if (_cnt > length) { ret = _last->value; _last = _last->prev; _cnt--; } } return ret; } void reset() { _first = nullptr; _last = nullptr; } }; color_t greedyColoring(const ConflictGraph& g, const vector>& ordering, vector& coloring, color_t ub=INVALID_COLOR) { int target_size = 256; if (ub != INVALID_COLOR) target_size = ub; dynamic_bitset<> done(g.getNumNodes()); dynamic_bitset<> taken; taken.reserve(target_size); color_t max_color = 0; for (auto cEntry: ordering) { auto n = get<1>(cEntry); auto modified = g.adjacency[n] & done; for(auto n2=modified.find_first(); n2 != boost::dynamic_bitset >::npos; n2 = modified.find_next(n2)) { taken[coloring[n2]] = true; } taken.flip(); auto cc = taken.find_first(); if (cc == dynamic_bitset<>::npos) { if (taken.size() == taken.capacity()) { taken.reserve(taken.capacity() + 256); } cc = taken.size(); taken.push_back(false); } coloring[n] = static_cast(cc); max_color = max(max_color, coloring[n]); done.set(n); taken.reset(); } return max_color + 1; } Coloring greedyDegree(ConflictGraph& g) { vector> nodes; vector coloring(g.getNumNodes()); fill(coloring.begin(), coloring.end(), -1); nodes.reserve(g.getNumNodes()); for (int i=0; i < g.getNumNodes(); i++) { nodes.emplace_back(g.getDegree(i), i); } sort(nodes.rbegin(), nodes.rend()); auto numColors = greedyColoring(g, nodes, coloring); return {coloring, numColors, nodes}; } Coloring greedyDegeneracy(ConflictGraph& g) { BucketQueue q(g.getNumNodes()); vector> nodes; nodes.reserve(g.getNumNodes()); vector degeneracy; degeneracy.reserve(g.getNumNodes()); auto done = dynamic_bitset<>(g.getNumNodes()); vector coloring(g.getNumNodes()); fill(coloring.begin(), coloring.end(), -1); for(int i=0; i < g.getNumNodes(); i++) { auto degree = g.getDegree(i); q.add(degree, i); degeneracy.push_back(degree); } while(! q.empty()) { auto element = q.next(); nodes.emplace_back(static_cast(get<0>(element)), get<1>(element)); done[get<1>(element)] = true; // TODO: This call is slow for (auto nb:g.getNeighborsExcept(get<1>(element), done)) { q.change(degeneracy[nb], degeneracy[nb]-1, nb); degeneracy[nb] -= 1; } } reverse(nodes.begin(), nodes.end()); auto numColors = greedyColoring(g, nodes, coloring); return {coloring, numColors, nodes}; } Coloring dsatur(const ConflictGraph& g) { vector> restricted; dynamic_bitset<> done(g.getNumNodes()); restricted.reserve(g.getNumNodes()); BucketQueue q(g.getNumNodes(), false); vector> ordering; ordering.reserve(g.getNumNodes()); color_t max_color = 0; vector coloring(g.getNumNodes(), INVALID_COLOR); vector> by_degree; by_degree.reserve(g.getNumNodes()); for(int i=0; i < g.getNumNodes(); i++) { q.add(0, i); by_degree.emplace_back(g.adjacency[i].count(), i); } sort(by_degree.rbegin(), by_degree.rend()); unordered_map r_node_map; vector node_map; node_map.reserve(g.getNumNodes()); for(int i=0; i < by_degree.size(); i++) { node_map.push_back(get<1>(by_degree[i])); r_node_map[get<1>(by_degree[i])] = i; restricted.emplace_back(dynamic_bitset<>()); } while(! q.empty()) { auto entry = q.next(); auto n = node_map[get<1>(entry)]; ordering.emplace_back(ordering.size(), n); restricted[n].flip(); auto cc = restricted[n].find_first(); if (cc == dynamic_bitset<>::npos) { cc = restricted[n].size(); } coloring[n] = cc; auto modified_nb = g.adjacency[n] - done; for(auto n2=modified_nb.find_first(); n2 != dynamic_bitset<>::npos; n2 = modified_nb.find_next(n2)) { if (restricted[n2].size() <= cc) restricted[n2].resize(cc+1); if (!restricted[n2].test(cc)) { restricted[n2].set(cc); auto cnt = restricted[n2].count(); q.change(cnt-1, cnt, r_node_map[n2]); } } max_color = max(max_color, static_cast(cc)); done.set(n); } assert(g.verify(coloring)); return Coloring(coloring, max_color+1, ordering); } void squeakyWheel(const ConflictGraph& g, Coloring& start, const string& export_path="", int iterations=50000, bool use_same=false, float budget_ratio=0.95, int budget_constant=-4) { vector> current_order(start.ordering); vector coloring(start.coloring); for(int i=0; i < iterations; i++) { int target = static_cast(budget_ratio * (float) start.numColors); for(int n=0; n < g.getNumNodes(); n++) { get<0>(current_order[n]) = n; if (coloring[get<1>(current_order[n])] > target) { get<0>(current_order[n]) += budget_constant * (coloring[get<1>(current_order[n])] - target); } } sort(current_order.begin(), current_order.end()); std::fill(coloring.begin(), coloring.end(), -1); auto new_result = greedyColoring(g, current_order, coloring, start.numColors); cout << new_result << endl; if (0 <= new_result && (new_result < start.numColors or (use_same && new_result == start.numColors))) { start.numColors = new_result; start.ordering = current_order; start.coloring = coloring; if (!export_path.empty()) { start.store(export_path, g); } } } } bool tabuSearchFlexible(const ConflictGraph& g, Coloring& start, color_t ub, const string& export_path="", unsigned int iterations=100000, unsigned int retention=10, unsigned int resetIterations=10000, color_t failed_limit=INVALID_COLOR) { auto c_retention = retention; int eliminatedColors = 0; unordered_set usedColors(start.numColors); auto originalColors = start.numColors; while(usedColors.size() < start.numColors && usedColors.size() < failed_limit) { vector> partitions; vector> taboos; vector> tabuQueue; vector> neighborhood_counts(g.getNumNodes(), vector(start.numColors, 0)); vector> neighborhood_counts_real(g.getNumNodes(), vector(start.numColors, 0)); taboos.reserve(g.getNumNodes() * start.numColors); node_t c_taboo_idx = 0; unsigned int iteration = 0; vector current_coloring(start.coloring); dynamic_bitset<> flexible(g.getNumNodes()); int selected_color = -1; std::function setFlexibility = [&](node_t node, bool flexibility) { if (flexibility) { if (flexible.test(node)) return; flexible.set(node); } else { if (!flexible.test(node)) return; flexible.reset(node); } for (auto n2 = g.adjacency[node].find_first(); n2 != dynamic_bitset<>::npos; n2 = g.adjacency[node].find_next(n2)) { if (flexibility) { assert(neighborhood_counts[n2][current_coloring[node]] > 0); neighborhood_counts[n2][current_coloring[node]] -= 1; } else { neighborhood_counts[n2][current_coloring[node]] += 1; } } }; auto changeColor = [&](node_t node, color_t oldColor, color_t newColor) { assert(current_coloring[node] == oldColor); current_coloring[node] = newColor; partitions[oldColor].reset(node); partitions[newColor].set(node); taboos[node].set(oldColor); tabuQueue.emplace_back(iteration + c_retention, node, oldColor); bool is_flex = flexible.test(node); // If node is flexible, it wasn't counted for (auto n2=g.adjacency[node].find_first(); n2 != dynamic_bitset<>::npos; n2 = g.adjacency[node].find_next(n2)) { neighborhood_counts_real[n2][newColor] += 1; assert(neighborhood_counts_real[n2][oldColor] > 0); neighborhood_counts_real[n2][oldColor] -= 1; if (neighborhood_counts_real[n2][oldColor] == 0 && oldColor != selected_color && oldColor != current_coloring[n2]) { if (!flexible.test(n2)) { setFlexibility(n2, true); } } // Removed possibility for flexibility if (neighborhood_counts_real[n2][newColor] == 1 && flexible.test(n2)) { bool found = false; for (color_t k = 0; k < start.numColors; k++) { if (k != selected_color && k != current_coloring[n2] && neighborhood_counts_real[n2][k] == 0) { found = true; break; } } if (!found) setFlexibility(n2, false); } if (! is_flex) { neighborhood_counts[n2][newColor] += 1; neighborhood_counts[n2][oldColor] -= 1; } } }; partitions.reserve(start.numColors); for (int k = 0; k < start.numColors; k++) { partitions.emplace_back(g.getNumNodes()); } for (int n = 0; n < g.getNumNodes(); n++) { partitions[start.coloring[n]].set(n); taboos.emplace_back(start.numColors); } while (iteration < iterations) { for (int n = 0; n < g.getNumNodes(); n++) { std::fill(neighborhood_counts[n].begin(), neighborhood_counts[n].end(), 0); std::fill(neighborhood_counts_real[n].begin(), neighborhood_counts_real[n].end(), 0); } for (int n = 0; n < g.getNumNodes(); n++) { for (auto n2=g.adjacency[n].find_first(); n2 != dynamic_bitset<>::npos; n2 = g.adjacency[n].find_next(n2)) { neighborhood_counts[n2][start.coloring[n]] += 1; neighborhood_counts_real[n2][start.coloring[n]] += 1; } } flexible.reset(); for(node_t n=0; n < g.getNumNodes(); n++) { for(color_t k=0; k < start.numColors; k++) { if (k != start.coloring[n] && neighborhood_counts_real[n][k] == 0) { flexible.set(n); break; } } } dynamic_bitset<>::size_type min_count; // Temporary coloring as to not mess up ours // Pick color with fewest elements for (int k = 0; k < partitions.size(); k++) { if (partitions[k].empty() || usedColors.find(k) != usedColors.end()) continue; auto count = (partitions[k] - flexible).count(); if (selected_color == -1 || count < min_count || (count == min_count && rand() % 2 == 0)) { min_count = count; selected_color = k; } } flexible.reset(); for(node_t n=0; n < g.getNumNodes(); n++) { if (! flexible.test(n)) { for (color_t k = 0; k < start.numColors; k++) { if (k != selected_color && k != start.coloring[n] && neighborhood_counts_real[n][k] == 0) { setFlexibility(n, true); break; } } } } auto ¤t_partition = partitions[selected_color]; auto startCount = current_partition.count(); bool swapped = false; // Used to limit the any calls on the partition c_retention = startCount * 6 / 10; // Try to eliminate color while (iteration < iterations && (swapped || partitions[selected_color].any())) { swapped = false; if (iteration % 100 == 0) { if (iteration % 1000 == 0) cout << "Count " << current_partition.count() << endl; c_retention = current_partition.count() * 6 / 10; } // Remove tabus after retention while (c_taboo_idx < tabuQueue.size() && get<0>(tabuQueue[c_taboo_idx]) < iteration) { taboos[get<1>(tabuQueue[c_taboo_idx])].reset(get<2>(tabuQueue[c_taboo_idx])); c_taboo_idx++; } node_t c_min = INVALID_NODE; node_t c_min_node = INVALID_NODE; color_t alternative_color = INVALID_COLOR; // Find node with the smallest number of neighbors colored in an alternative color for (auto n = current_partition.find_first(); n != boost::dynamic_bitset<>::npos && c_min != 0; n = current_partition.find_next(n)) { for (auto k = 0; k < start.numColors; k++) { // Already eliminated that color, or taboo if (k == selected_color || partitions[k].empty()) continue; auto count = neighborhood_counts[n][k]; // Non found, just change color if (count == 0) { if (neighborhood_counts_real[n][k] == 0) { changeColor(n, selected_color, k); c_min = INVALID_NODE; break; } else { c_min = static_cast(count); c_min_node = static_cast(n); alternative_color = k; } } if (taboos[n].test(k)) continue; if (c_min_node == INVALID_NODE || count < c_min || (count == c_min && rand() % 2 == 0)) { c_min_node = static_cast(n); c_min = static_cast(count); alternative_color = k; } } } // Change color of neighbors and propagate the change as long as there is only one while (c_min == 1 && neighborhood_counts_real[c_min_node][alternative_color] == 1) { // Switch colors int next_n = static_cast((g.adjacency[c_min_node] & partitions[alternative_color]).find_first()); changeColor(c_min_node, selected_color, alternative_color); changeColor(next_n, alternative_color, selected_color); c_min = INVALID_NODE; c_min_node = next_n; alternative_color = INVALID_COLOR; for (auto k = 0; k < start.numColors; k++) { // Already eliminated that color, or taboo if (k == selected_color || partitions[k].empty() || taboos[next_n].test(k)) continue; auto count = neighborhood_counts[next_n][k]; // Non found, just change color if (count == 0) { if (neighborhood_counts_real[c_min_node][k] == 0) { changeColor(c_min_node, selected_color, k); c_min = INVALID_COLOR; break; } else { c_min = static_cast(count); alternative_color = k; } } if (c_min == INVALID_NODE || count <= 1 || (count < c_min && (rand() % 2) == 0)) { c_min = static_cast(count); alternative_color = k; } } } if (c_min != INVALID_COLOR && neighborhood_counts_real[c_min_node][alternative_color] >= 1 && current_coloring[c_min_node] == selected_color) { swapped = true; changeColor(c_min_node, selected_color, alternative_color); auto colored_neighbors = partitions[alternative_color] & g.adjacency[c_min_node]; for (auto n = colored_neighbors.find_first(); n != boost::dynamic_bitset<>::npos; n = colored_neighbors.find_next(n)) { changeColor(n, alternative_color, selected_color); } } iteration++; if (iteration >= iterations) { auto newCount = current_partition.count(); if (newCount < startCount) { startCount = newCount; iteration -= resetIterations; } } } // Check if we eliminated all nodes, or if we ran out of iterations if (!current_partition.any()) { cout << "Eliminated color " << selected_color << endl; auto c_max_color = partitions.size() - 1; if (selected_color != c_max_color) { partitions[selected_color] = partitions[c_max_color]; for (int n = 0; n < g.getNumNodes(); n++) { if (current_coloring[n] == c_max_color) { current_coloring[n] = selected_color; } neighborhood_counts[n][selected_color] = neighborhood_counts[n][c_max_color]; taboos[n].reset(selected_color); } } partitions.pop_back(); start.coloring = current_coloring; start.numColors -= 1; // TODO: Adapt ordering? For the return would be sufficient... if (!export_path.empty() && start.numColors < ub) { start.store(export_path, g); } eliminatedColors++; } else { cout << "Failed to eliminate color " << selected_color << endl; usedColors.insert(selected_color); } } } cout << "Eliminated " << eliminatedColors << " Colors from originally " << originalColors << endl; return eliminatedColors > 0; } node_t twwHeuristic(ConflictGraph& g) { dynamic_bitset<> done(g.getNumNodes()); vector> red_edges(g.getNumNodes(), dynamic_bitset<>(g.getNumNodes())); node_t tww = 0; BucketQueue q(g.getNumNodes(), false); vector degrees(g.getNumNodes(), 0); for(node_t n=0; n < g.getNumNodes(); n++) { auto degree = g.adjacency[n].count(); q.add(degree, n); degrees[n] = degree; } for(auto contracted=0; contracted < g.getNumNodes() - 1; contracted++) { node_t c_min = INVALID_NODE; node_t contract1 = INVALID_NODE; node_t contract2 = INVALID_NODE; auto not_done = ~done; // Find minimum nodes to contract // for (auto n = not_done.find_first(); n != dynamic_bitset<>::npos; n = not_done.find_next(n)) { auto nxt = q.next(); auto n = get<1>(nxt); for(auto n2 = not_done.find_first(); n2 != dynamic_bitset<>::npos; n2 = not_done.find_next(n2)) { if (n == n2) continue; auto new_reds = g.adjacency[n] ^ g.adjacency[n2]; new_reds.reset(n); new_reds.reset(n2); new_reds |= (red_edges[n] | red_edges[n2]); auto new_red_cnt = new_reds.count(); if (c_min == INVALID_NODE || new_red_cnt < c_min) { c_min = new_red_cnt; contract1 = n; contract2 = n2; } } //} for(auto c_n = g.adjacency[contract1].find_first(); c_n != dynamic_bitset<>::npos; c_n = g.adjacency[contract1].find_next(c_n)) { if (! done.test(c_n)) { q.change(degrees[c_n], degrees[c_n] - 1, c_n); degrees[c_n] -= 1; } } // Contract done.set(contract1); auto new_reds = g.adjacency[contract1] ^ g.adjacency[contract2]; new_reds |= red_edges[contract1]; new_reds.reset(contract1); new_reds.reset(contract2); red_edges[contract2] |= new_reds; auto new_red_cnt = red_edges[contract2].count(); if (new_red_cnt > tww) tww = new_red_cnt; for(auto c_n=red_edges[contract2].find_first(); c_n != dynamic_bitset<>::npos; c_n = red_edges[contract2].find_next(c_n)) { red_edges[c_n].set(contract2); red_edges[c_n].reset(contract1); auto c_red_cnt = red_edges[c_n].count(); if (c_red_cnt > tww) tww = c_red_cnt; } } return tww; } void orderingSearch(const ConflictGraph& g, Coloring& start, const string& export_path="", long long iterations=10000000) { unordered_map nodePosition(g.getNumNodes()); vector ordering(g.getNumNodes()); vector coloring = start.coloring; color_t c_colors = start.numColors; vector> taboos(g.getNumNodes(), dynamic_bitset<>(c_colors + 1)); vector blockingPos(c_colors + 1); dynamic_bitset<> taken(c_colors + 1); dynamic_bitset<> done(g.getNumNodes()); vector tenures(g.getNumNodes(), LimitedList(c_colors / 2)); for(auto i=0; i < g.getNumNodes(); i++) { nodePosition[get<1>(start.ordering[i])] = i; ordering[i] = get<1>(start.ordering[i]); } node_t pos = INVALID_NODE; for(long long iteration=0; iteration < iterations; iteration++) { if (pos == INVALID_NODE) { // Find position of badly colored vertex for (pos=0; pos < g.getNumNodes(); pos++) { if (coloring[ordering[pos]] == c_colors - 1) break; } } // If non could be found, successfully eliminated color... if (pos >= g.getNumNodes()) { c_colors = 0; for (auto cc: coloring) { if (cc > c_colors - 1) { c_colors = cc + 1; } } for(auto cl:tenures) { if (cl.length >= c_colors - 3) { cl.length = c_colors - 3; cl.reset(); } } for(auto cl: taboos) cl.reset(); cout << "Eliminated color, now " << c_colors << " colors" << endl; for (pos=0; pos < g.getNumNodes(); pos++) { if (coloring[ordering[pos]] == c_colors - 1) break; } } assert(pos < g.getNumNodes()); node_t n = ordering[pos]; for(auto& ce: blockingPos) { ce = INVALID_NODE; } // Check where we could move the node to for(auto n2=g.adjacency[n].find_first(); n2 != dynamic_bitset<>::npos; n2 = g.adjacency[n].find_next(n2)) { auto n2_color = coloring[n2]; if (blockingPos[n2_color] == INVALID_NODE || blockingPos[n2_color] > nodePosition[n2]) { blockingPos[n2_color] = nodePosition[n2]; } } // Find largest number that we can assign to the current color without breaking taboos. Avoid num_colors - 2 // as it would immediately create conflicts color_t max_cc = INVALID_COLOR; if (taboos[n].count() >= c_colors - 1) { taboos[n].reset(); cout << "Reset " << n << endl; } for(auto cc = c_colors - 2; cc > 0; cc--) { if (max_cc == INVALID_COLOR) { if (!taboos[n].test(cc - 1)) { max_cc = cc - 1; break; } } } // All taboo if (max_cc == INVALID_COLOR) { // If also taboo, default to c_colors -3, else use color we wanted to avoid. if (taboos[n].test(c_colors - 2)) max_cc = c_colors - 3; else max_cc = c_colors - 2; } if (blockingPos[max_cc] == INVALID_NODE) { coloring[n] = max_cc; pos = INVALID_NODE; } else { auto target_pos = blockingPos[max_cc]; cout << "Pos: " << pos << " to " << target_pos << "\t" << n << " to " << max_cc << " (" << c_colors << ")" << endl; // Rearrange for (auto c_pos = pos; c_pos > target_pos; c_pos--) { auto c_target = ordering[c_pos - 1]; nodePosition[c_target] = c_pos; swap(ordering[c_pos], ordering[c_pos - 1]); } ordering[target_pos] = n; nodePosition[n] = target_pos; // Set taboos if (! taboos[n].test(max_cc)) { taboos[n].set(max_cc); // auto n_taboo_old = tenures[n].add(max_cc); // if (n_taboo_old != INVALID_NODE) // taboos[n].reset(n_taboo_old); } for (auto n2 = g.adjacency[n].find_first(); n2 != dynamic_bitset<>::npos; n2 = g.adjacency[n].find_next(n2)) { if (nodePosition[n2] > target_pos) { if (! taboos[n2].test(coloring[n2])) { taboos[n2].set(coloring[n2]); // auto n2_taboo_old = tenures[n2].add(coloring[n2]); // if (n2_taboo_old != INVALID_NODE) // taboos[n2].reset(n2_taboo_old); } } } // Propagate done.reset(); for (node_t i = 0; i < target_pos; i++) { done.set(ordering[i]); } pos = INVALID_NODE; for (node_t i = target_pos; i < g.getNumNodes(); i++) { auto cn = ordering[i]; auto modified = g.adjacency[cn] & done; taken.reset(); for (auto n2 = modified.find_first(); n2 != dynamic_bitset<>::npos; n2 = modified.find_next(n2)) { taken.set(coloring[n2]); } taken.flip(); auto cc = taken.find_first(); if (cc >= c_colors - 1) { pos = i; break; } coloring[cn] = static_cast(cc); done.set(cn); } } } } // void orderingSearch(ConflictGraph& g, vector& clique, color_t ub) { // vector> restricted(g.getNumNodes(), dynamic_bitset<>(ub)); // dynamic_bitset<> done(g.getNumNodes()); // BucketQueue q(g.getNumNodes(), false); // vector> ordering; // ordering.reserve(g.getNumNodes()); // // color_t max_color = 0; // vector coloring(g.getNumNodes(), INVALID_COLOR); // // vector> by_degree; // // by_degree.reserve(g.getNumNodes()); // for(int i=0; i < g.getNumNodes(); i++) { // q.add(0, i); // by_degree.emplace_back(g.adjacency[i].count(), i); // } // sort(by_degree.rbegin(), by_degree.rend()); // // unordered_map r_node_map; // vector node_map; // node_map.reserve(g.getNumNodes()); // // for(int i=0; i < by_degree.size(); i++) { // node_map.push_back(get<1>(by_degree[i])); // r_node_map[get<1>(by_degree[i])] = i; // restricted.emplace_back(dynamic_bitset<>()); // } // // while(! q.empty()) { // auto entry = q.next(); // auto n = node_map[get<1>(entry)]; // ordering.emplace_back(ordering.size(), n); // restricted[n].flip(); // auto cc = restricted[n].find_first(); // if (cc == dynamic_bitset<>::npos) { // cc = restricted[n].size(); // } // // coloring[n] = cc; // // auto modified_nb = g.adjacency[n] - done; // for(auto n2=modified_nb.find_first(); n2 != dynamic_bitset<>::npos; n2 = modified_nb.find_next(n2)) { // if (restricted[n2].size() <= cc) // restricted[n2].resize(cc+1); // if (!restricted[n2].test(cc)) { // restricted[n2].set(cc); // auto cnt = restricted[n2].count(); // q.change(cnt-1, cnt, r_node_map[n2]); // } // } // max_color = max(max_color, static_cast(cc)); // done.set(n); // } // assert(g.verify(coloring)); // return Coloring(coloring, max_color+1, ordering); // } bool tabuSearchLookahead(const ConflictGraph &g, Coloring &start, color_t ub, const string &export_path = "", unsigned int iterations = 100000, unsigned int retention = 10, unsigned int resetIterations = 2500, color_t failed_limit = INVALID_COLOR, unsigned int branching_width = 3, node_t branching_depth = 5, node_t seed=0) { std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); // TODO: What in case of early failure? auto c_retention = retention; int eliminatedColors = 0; unordered_set usedColors(start.numColors); auto originalColors = start.numColors; // Temporary coloring as to not mess up ours int selected_color = -1; while (usedColors.size() < start.numColors && usedColors.size() < failed_limit) { vector> partitions(start.numColors, dynamic_bitset<>(g.getNumNodes())); vector> taboos; vector> tabuQueue; vector> neighborhood_counts(g.getNumNodes(), vector(start.numColors, 0)); taboos.reserve(g.getNumNodes() * start.numColors); node_t c_taboo_idx = 0; unsigned int iteration = 0; vector current_coloring(start.coloring); dynamic_bitset<> flexible(g.getNumNodes()); unordered_map flexibleRev; auto changeColor = [&](node_t node, color_t oldColor, color_t newColor) { current_coloring[node] = newColor; partitions[oldColor].reset(node); partitions[newColor].set(node); taboos[node].set(oldColor); tabuQueue.emplace_back(iteration + c_retention, node, oldColor); for (auto n2 = g.adjacency[node].find_first(); n2 != dynamic_bitset<>::npos; n2 = g.adjacency[node].find_next(n2)) { neighborhood_counts[n2][newColor] += 1; neighborhood_counts[n2][oldColor] -= 1; if (neighborhood_counts[n2][oldColor] == 0 && oldColor != selected_color && oldColor != current_coloring[n2]) { flexible.set(n2); flexibleRev[n2] = oldColor; } if (neighborhood_counts[n2][newColor] == 1 && flexible.test(node) && flexibleRev[node] == newColor) { flexible.reset(n2); for(color_t k=0; k < start.numColors; k++) { if (k != selected_color && k != current_coloring[n2] && neighborhood_counts[n2][k] == 0) { flexibleRev[n2] = k; flexible.set(n2); break; } } } } if (flexible.test(node)) { flexible.reset(node); for(color_t k=0; k < start.numColors; k++) { if (k != selected_color && k != current_coloring[node] && neighborhood_counts[node][k] == 0) { flexibleRev[node] = k; flexible.set(node); break; } } } }; std::function&, dynamic_bitset<>&, node_t, vector>>&, node_t, color_t, node_t, bool)> lookAhead = [&](dynamic_bitset<>& currentComponent, dynamic_bitset<>& seen, node_t branching, vector>> &solutions, node_t depth, color_t color, node_t min_count, bool taboo) { vector>> sub_nb; sub_nb.reserve(branching + 1); dynamic_bitset<> c_adjacency(g.getNumNodes()); dynamic_bitset<> taboo_colors(taboos.back().size()); for (auto n = currentComponent.find_first(); n != dynamic_bitset<>::npos; n = currentComponent.find_next(n)) { if (!flexible.test(n)) { c_adjacency |= g.adjacency[n]; if (taboo) taboo_colors |= taboos[n]; } } for (auto k = 0; k < start.numColors; k++) { if (k != color && !(seen & partitions[k]).any() && (!taboo || !taboo_colors.test(k))) { auto c_part = (c_adjacency & partitions[k]); auto n_cnt = c_part.count(); if (n_cnt == 0) sub_nb.clear(); sub_nb.emplace_back(n_cnt, k, c_part); if (sub_nb.size() > branching) { sort(sub_nb.begin(), sub_nb.end()); sub_nb.pop_back(); } if (n_cnt == 0) break; } } for (auto &c_nb : sub_nb) { if (depth == 0 || get<0>(c_nb) == 0) { if (get<0>(c_nb) < min_count) { solutions.clear(); solutions.emplace_back(get<1>(c_nb), get<2>(c_nb)); min_count = get<0>(c_nb); } } else { auto c_seen = seen | get<2>(c_nb); auto result = lookAhead(get<2>(c_nb), c_seen, branching, solutions, depth - 1, color, min_count, false); if (result < min_count) { min_count = result; solutions.emplace_back(get<1>(c_nb), get<2>(c_nb)); } } } return min_count; }; for (int n = 0; n < g.getNumNodes(); n++) { partitions[start.coloring[n]].set(n); taboos.emplace_back(start.numColors); for (auto n2 = g.adjacency[n].find_first(); n2 != dynamic_bitset<>::npos; n2 = g.adjacency[n].find_next(n2)) { neighborhood_counts[n2][start.coloring[n]] += 1; } } while (iteration < iterations) { dynamic_bitset<>::size_type min_count; for(node_t n=0; n < g.getNumNodes(); n++) { for(color_t k=0; k < start.numColors; k++) { if (k != start.coloring[n] && neighborhood_counts[n][k] == 0) { flexible.set(n); flexibleRev[n] = k; break; } } } // Pick color with fewest elements vector> color_counts; color_counts.reserve(partitions.size()); for (int k = 0; k < partitions.size(); k++) { if (partitions[k].empty() || usedColors.find(k) != usedColors.end()) continue; auto count = (partitions[k] - flexible).count(); color_counts.emplace_back(count, k); } sort(color_counts.begin(), color_counts.end()); selected_color = color_counts[seed].second; min_count = color_counts[seed].first; flexible.reset(); for(node_t n=0; n < g.getNumNodes(); n++) { for(color_t k=0; k < start.numColors; k++) { if (k != selected_color && k != start.coloring[n] && neighborhood_counts[n][k] == 0) { flexible.set(n); flexibleRev[n] = k; break; } } } auto ¤t_partition = partitions[selected_color]; auto startCount = current_partition.count(); bool swapped = false; // Used to limit the any calls on the partition c_retention = startCount * 6 / 10; // Try to eliminate color while (iteration < iterations && (swapped || partitions[selected_color].any())) { swapped = false; if (iteration % 100 == 0) c_retention = current_partition.count() * 6 / 10; // Remove tabus after retention while (c_taboo_idx < tabuQueue.size() && get<0>(tabuQueue[c_taboo_idx]) < iteration) { taboos[get<1>(tabuQueue[c_taboo_idx])][get<2>(tabuQueue[c_taboo_idx])] = false; c_taboo_idx++; } int c_min = -1; int c_min_node = -1; int alternative_color = -1; cout << "Color " << selected_color << ", Count " << current_partition.count() << endl; // Find node with the smallest number of neighbors colored in an alternative color for (auto n = current_partition.find_first(); n != boost::dynamic_bitset<>::npos && c_min != 0; n = current_partition.find_next(n)) { for (auto k = 0; k < start.numColors; k++) { // Already eliminated that color, or taboo if (k == selected_color || partitions[k].empty()) continue; auto count = neighborhood_counts[n][k]; // Non found, just change color if (count == 0) { changeColor(n, selected_color, k); c_min = 0; break; } if (taboos[n][k]) continue; if (c_min_node == -1 || count < c_min) { c_min_node = static_cast(n); c_min = static_cast(count); alternative_color = k; } } } // No options... if (c_min == -1) { usedColors.insert(selected_color); cout << "Early fail to eliminate color " << selected_color << endl; break; } if (c_min >= 1) { swapped = true; dynamic_bitset<> component(g.getNumNodes()); node_t cnt = 0; component.set(c_min_node); vector>> sol; sol.reserve(branching_depth); auto result = lookAhead(component, component, branching_width, sol, branching_depth, selected_color, INVALID_NODE, true); cout << "Propagated to " << result << " nodes" << endl; while (!sol.empty()) { auto &ck = sol.back(); for (auto n = component.find_first(); n != dynamic_bitset<>::npos; n = component.find_next(n)) { auto oldColor = current_coloring[n]; if (flexible.test(n)) { changeColor(n, oldColor, selected_color); } else { changeColor(n, oldColor, get<0>(ck)); } } component = partitions[get<0>(ck)] & get<1>(ck); sol.pop_back(); } for (auto n = component.find_first(); n != dynamic_bitset<>::npos; n = component.find_next(n)) { auto oldColor = current_coloring[n]; changeColor(n, oldColor, selected_color); } } iteration++; if (iteration >= iterations) { auto newCount = current_partition.count(); if (newCount < startCount) { startCount = newCount; iteration -= resetIterations; } } } // Check if we eliminated all nodes, or if we ran out of iterations if (!current_partition.any()) { auto c_max_color = partitions.size() - 1; if (selected_color != c_max_color) { partitions[selected_color] = partitions[c_max_color]; for (int n = 0; n < g.getNumNodes(); n++) { if (current_coloring[n] == c_max_color) { current_coloring[n] = selected_color; } neighborhood_counts[n][selected_color] = neighborhood_counts[n][c_max_color]; taboos[n].reset(selected_color); } } assert(g.verify(current_coloring)); partitions.pop_back(); start.coloring = current_coloring; start.numColors -= 1; // TODO: Adapt ordering? For the return would be sufficient... if (!export_path.empty() && start.numColors < ub) { start.store(export_path, g); } std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); cout << seed << "::"<< std::chrono::duration_cast(end - begin).count() << "::Eliminated " << selected_color << ", Remaining " << partitions.size() << endl; eliminatedColors++; } else { cout << "Failed to eliminate color " << selected_color << endl; usedColors.insert(selected_color); } } } cout << "Eliminated " << eliminatedColors << " Colors from originally " << originalColors << endl; return eliminatedColors > 0; } bool tabuSearch(const ConflictGraph& g, Coloring& start, color_t ub, std::mutex& sync, unsigned int& cycle, const string& export_path="", node_t seed = 0, unsigned int iterations=100000, unsigned int retention=10, unsigned int resetIterations=10000, color_t failed_limit=INVALID_COLOR) { // Check how many different colors: std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); unordered_set seen_colors; for(node_t n=0; n < g.getNumNodes(); n++) { seen_colors.insert(start.coloring[n]); } cout << ub << endl; cout << seen_colors.size() << endl; if (ub > seen_colors.size()) { unordered_map color_map; color_t max_color = seen_colors.size() - 1; color_t current_gap = 0; for(node_t n=0; n < g.getNumNodes(); n++) { if (start.coloring[n] > max_color) { auto ncolor = color_map.find(start.coloring[n]); if (ncolor == color_map.end()) { for(; current_gap <= max_color; current_gap++) { if (seen_colors.find(current_gap) == seen_colors.end()) { ncolor = color_map.emplace(start.coloring[n], current_gap).first; break; } } } start.coloring[n] = ncolor->second; } seen_colors.insert(start.coloring[n]); } ub = max_color + 1; start.store(export_path, g); assert(g.verify(start.coloring)); } auto c_retention = retention; int eliminatedColors = 0; sync.lock(); unordered_set usedColors(start.numColors); auto originalColors = start.numColors; auto my_cycle = cycle; sync.unlock(); while(usedColors.size() < start.numColors && usedColors.size() < failed_limit) { sync.lock(); vector current_coloring(start.coloring); color_t current_num_colors = start.numColors; my_cycle = cycle; sync.unlock(); vector> partitions; vector> taboos; vector> tabuQueue; vector> neighborhood_counts(g.getNumNodes(), vector(current_num_colors, 0)); taboos.reserve(g.getNumNodes() * current_num_colors); node_t c_taboo_idx = 0; unsigned int iteration = 0; auto changeColor = [&](node_t node, color_t oldColor, color_t newColor) { current_coloring[node] = newColor; partitions[oldColor].reset(node); partitions[newColor].set(node); taboos[node].set(oldColor); tabuQueue.emplace_back(iteration + c_retention, node, oldColor); for (auto n2=g.adjacency[node].find_first(); n2 != dynamic_bitset<>::npos; n2 = g.adjacency[node].find_next(n2)) { neighborhood_counts[n2][newColor] += 1; neighborhood_counts[n2][oldColor] -= 1; } }; partitions.reserve(current_num_colors); for (int k = 0; k < current_num_colors; k++) { partitions.emplace_back(g.getNumNodes()); } for (int n = 0; n < g.getNumNodes(); n++) { partitions[current_coloring[n]].set(n); taboos.emplace_back(current_num_colors); for (auto n2=g.adjacency[n].find_first(); n2 != dynamic_bitset<>::npos; n2 = g.adjacency[n].find_next(n2)) { neighborhood_counts[n2][current_coloring[n]] += 1; } } while (iteration < iterations) { if (my_cycle < cycle) break; // Temporary coloring as to not mess up ours int selected_color = -1; // Pick color with fewest elements vector> color_counts; color_counts.reserve(current_num_colors); for (int k = 0; k < partitions.size(); k++) { if (partitions[k].empty() || usedColors.find(k) != usedColors.end()) continue; auto count = (partitions[k] - g.reduced).count(); color_counts.emplace_back(count, k); } sort(color_counts.rbegin(), color_counts.rend()); selected_color = color_counts[seed].second; auto ¤t_partition = partitions[selected_color]; auto startCount = current_partition.count(); bool swapped = false; // Used to limit the any calls on the partition c_retention = startCount * 6 / 10; // Try to eliminate color while (iteration < iterations && (swapped || partitions[selected_color].any())) { swapped = false; if (iteration % 1000 == 0) { cout << "Count " << current_partition.count() << endl; c_retention = max(static_cast(current_partition.count() * 6 / 10), retention); } // Remove tabus after retention while (c_taboo_idx < tabuQueue.size() && get<0>(tabuQueue[c_taboo_idx]) < iteration) { taboos[get<1>(tabuQueue[c_taboo_idx])].reset(get<2>(tabuQueue[c_taboo_idx])); c_taboo_idx++; } int c_min = -1; int c_min_node = -1; int alternative_color = -1; // Find node with the smallest number of neighbors colored in an alternative color for (auto n = current_partition.find_first(); n != boost::dynamic_bitset<>::npos && c_min != 0; n = current_partition.find_next(n)) { for (auto k = 0; k < current_num_colors; k++) { // Already eliminated that color, or taboo if (k == selected_color || partitions[k].empty()) continue; auto count = neighborhood_counts[n][k]; // Non found, just change color if (count == 0) { changeColor(n, selected_color, k); c_min = 0; break; } if (taboos[n].test(k)) continue; if (c_min_node == -1 || count < c_min || (count == c_min && rand() % 2 == 0)) { c_min_node = static_cast(n); c_min = static_cast(count); alternative_color = k; } } } // No options... if (c_min == -1) { usedColors.insert(selected_color); cout << "Early fail to eliminate color " << selected_color << endl; break; } // Change color of neighbors and propagate the change as long as there is only one while (c_min == 1) { // Switch colors int next_n = static_cast((g.adjacency[c_min_node] & partitions[alternative_color]).find_first()); changeColor(c_min_node, selected_color, alternative_color); changeColor(next_n, alternative_color, selected_color); c_min = -1; c_min_node = next_n; alternative_color = -1; for (auto k = 0; k < current_num_colors; k++) { // Already eliminated that color, or taboo if (k == selected_color || partitions[k].empty() || taboos[next_n].test(k)) continue; auto count = neighborhood_counts[next_n][k]; // Non found, just change color if (count == 0) { changeColor(next_n, selected_color, k); c_min = 0; break; } if (c_min == -1 || count <= 1 || (count < c_min && (rand() % 2) == 0)) { c_min = static_cast(count); alternative_color = k; } } } if (c_min > 1) { swapped = true; changeColor(c_min_node, selected_color, alternative_color); auto colored_neighbors = partitions[alternative_color] & g.adjacency[c_min_node]; for (auto n = colored_neighbors.find_first(); n != boost::dynamic_bitset<>::npos; n = colored_neighbors.find_next(n)) { changeColor(n, alternative_color, selected_color); } } iteration++; if (iteration >= iterations) { auto newCount = current_partition.count(); if (newCount < startCount) { startCount = newCount; iteration -= resetIterations; } } } // Check if we eliminated all nodes, or if we ran out of iterations if (!current_partition.any()) { sync.lock(); if (my_cycle == cycle) { cycle++; my_cycle = cycle; auto c_max_color = partitions.size() - 1; if (selected_color != c_max_color) { partitions[selected_color] = partitions[c_max_color]; for (int n = 0; n < g.getNumNodes(); n++) { if (current_coloring[n] == c_max_color) { current_coloring[n] = selected_color; } neighborhood_counts[n][selected_color] = neighborhood_counts[n][c_max_color]; taboos[n].reset(selected_color); } } partitions.pop_back(); start.coloring = current_coloring; start.numColors -= 1; // TODO: Adapt ordering? For the return would be sufficient... if (!export_path.empty() && start.numColors < ub) { start.store(export_path, g); } std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); cout << seed << "::"<< std::chrono::duration_cast(end - begin).count() << "::Eliminated " << selected_color << ", Remaining " << partitions.size() << endl; eliminatedColors++; } sync.unlock(); break; } else { cout << "Failed to eliminate color " << selected_color << endl; usedColors.insert(selected_color); break; } } } cout << "Eliminated " << eliminatedColors << " Colors from originally " << originalColors << endl; return eliminatedColors > 0; } } #endif //CPP_HEURISTICS_H