22#include "absl/container/flat_hash_set.h"
23#include "absl/log/check.h"
29inline bool Connects(std::function<
bool(
int,
int)> graph,
int i,
int j) {
30 return i == j || graph(i, j);
53void Search(std::function<
bool(
int,
int)> graph,
54 std::function<
bool(
const std::vector<int>&)> callback,
55 int* input_candidates,
int first_candidate_index,
56 int num_input_candidates, std::vector<int>* current_clique,
68 int disconnected_node = 0;
76 int num_disconnected_candidates = num_input_candidates;
81 int pre_increment = 0;
84 for (
int i = 0;
i < num_input_candidates && num_disconnected_candidates != 0;
86 int pivot_candidate = input_candidates[
i];
96 int disconnected_node_candidate = 0;
101 for (
int j = first_candidate_index;
102 j < num_input_candidates && count < num_disconnected_candidates; ++j) {
103 if (!Connects(graph, pivot_candidate, input_candidates[j])) {
105 disconnected_node_candidate = j;
111 if (count < num_disconnected_candidates) {
112 pivot = pivot_candidate;
113 num_disconnected_candidates = count;
115 if (i < first_candidate_index) {
116 disconnected_node = disconnected_node_candidate;
118 disconnected_node =
i;
126 std::vector<int> new_candidates;
127 new_candidates.reserve(num_input_candidates);
128 for (
int remaining_candidates = num_disconnected_candidates + pre_increment;
129 remaining_candidates >= 1; remaining_candidates--) {
133 const int selected = input_candidates[disconnected_node];
134 std::swap(input_candidates[disconnected_node],
135 input_candidates[first_candidate_index]);
138 new_candidates.clear();
139 for (
int i = 0;
i < first_candidate_index; ++
i) {
140 if (Connects(graph, selected, input_candidates[i])) {
141 new_candidates.push_back(input_candidates[i]);
144 const int new_first_candidate_index = new_candidates.size();
145 for (
int i = first_candidate_index + 1;
i < num_input_candidates; ++
i) {
146 if (Connects(graph, selected, input_candidates[i])) {
147 new_candidates.push_back(input_candidates[i]);
150 const int new_candidate_size = new_candidates.size();
153 current_clique->push_back(selected);
157 if (new_candidate_size == 0) {
158 *stop = callback(*current_clique);
160 if (new_first_candidate_index < new_candidate_size) {
161 Search(graph, callback, new_candidates.data(),
162 new_first_candidate_index, new_candidate_size, current_clique,
171 current_clique->pop_back();
176 first_candidate_index++;
179 if (remaining_candidates > 1) {
180 disconnected_node = first_candidate_index;
181 while (disconnected_node < num_input_candidates &&
182 Connects(graph, pivot, input_candidates[disconnected_node])) {
189class FindAndEliminate {
191 FindAndEliminate(std::function<
bool(
int,
int)> graph,
int node_count,
192 std::function<
bool(
const std::vector<int>&)> callback)
193 : graph_(std::move(graph)),
194 node_count_(node_count),
195 callback_(std::move(callback)) {}
197 bool GraphCallback(
int node1,
int node2) {
199 std::make_pair(std::min(node1, node2), std::max(node1, node2))) !=
203 return Connects(graph_, node1, node2);
206 bool SolutionCallback(
const std::vector<int>&
solution) {
209 for (
int i = 0;
i < size - 1; ++
i) {
210 for (
int j = i + 1; j < size; ++j) {
221 std::function<bool(
int,
int)> graph_;
223 std::function<bool(
const std::vector<int>&)> callback_;
224 absl::flat_hash_set<std::pair<int, int>> visited_;
230void FindCliques(std::function<
bool(
int,
int)> graph,
int node_count,
231 std::function<
bool(
const std::vector<int>&)> callback) {
232 std::unique_ptr<int[]> initial_candidates(
new int[node_count]);
233 std::vector<int> actual;
235 for (
int c = 0; c < node_count; ++c) {
236 initial_candidates[c] = c;
240 Search(std::move(graph), std::move(callback), initial_candidates.get(), 0,
241 node_count, &actual, &stop);
245 std::function<
bool(
const std::vector<int>&)> callback) {
246 FindAndEliminate cache(std::move(graph), node_count, std::move(callback));
247 std::unique_ptr<int[]> initial_candidates(
new int[node_count]);
248 std::vector<int> actual;
250 std::function<bool(
int,
int)> cached_graph = [&cache](
int i,
int j) {
251 return cache.GraphCallback(i, j);
253 std::function<bool(
const std::vector<int>&)> cached_callback =
254 [&cache](
const std::vector<int>& res) {
255 return cache.SolutionCallback(res);
258 for (
int c = 0; c < node_count; ++c) {
259 initial_candidates[c] = c;
263 Search(std::move(cached_graph), std::move(cached_callback),
264 initial_candidates.get(), 0, node_count, &actual, &stop);
269 weights_.assign(num_nodes, 0.0);
272 clique_.resize(num_nodes + 1);
273 clique_weight_.resize(num_nodes + 1);
274 left_to_process_.resize(num_nodes + 1);
275 x_.resize(num_nodes + 1);
278 graph_.resize(num_nodes);
280 bitset.ClearAndResize(num_nodes);
287 const int num_nodes = weights_.size();
288 for (
int k = 0; k < num_nodes; ++k) {
291 for (
const int i : graph_[k ^ 1]) {
293 graph_[i].Union(graph_[k]);
299 clique_index_and_weight_.clear();
300 std::vector<std::vector<int>> cliques;
302 const int num_nodes = weights_.size();
303 in_clique_.ClearAndResize(num_nodes);
308 left_to_process_[0].ClearAndResize(num_nodes);
309 x_[0].ClearAndResize(num_nodes);
310 for (
int i = 0; i < num_nodes; ++i) {
311 left_to_process_[0].Set(i);
317 while (!queue_.empty() && work_ <= work_limit_) {
318 const int node = queue_.back();
319 if (!in_clique_[node]) {
321 in_clique_.Set(node);
322 clique_[depth] = node;
323 left_to_process_[depth].Clear(node);
331 const double current_weight = weights_[node] + clique_weight_[depth - 1];
332 clique_weight_[depth] = current_weight;
333 left_to_process_[depth].SetToIntersectionOf(left_to_process_[depth - 1],
335 x_[depth].SetToIntersectionOf(x_[depth - 1], graph_[node]);
343 double pivot_weight = -1.0;
344 for (
const int candidate : x_[depth]) {
345 const double candidate_weight = weights_[candidate];
346 if (candidate_weight > pivot_weight) {
348 pivot_weight = candidate_weight;
351 double total_weight_left = 0.0;
352 for (
const int candidate : left_to_process_[depth]) {
353 const double candidate_weight = weights_[candidate];
354 if (candidate_weight > pivot_weight) {
356 pivot_weight = candidate_weight;
358 total_weight_left += candidate_weight;
363 if (current_weight + total_weight_left < weight_threshold_) {
367 if (pivot == -1 && current_weight >= weight_threshold_) {
369 clique_index_and_weight_.push_back({cliques.size(), current_weight});
370 cliques.emplace_back(clique_.begin(), clique_.begin() + depth);
374 for (
const int next : left_to_process_[depth]) {
375 if (graph_[pivot][next])
continue;
376 queue_.push_back(next);
383 DCHECK_EQ(clique_[depth], node);
384 in_clique_.Clear(node);
---------------— Search class --------------—
void Initialize(int num_nodes)
void TakeTransitiveClosureOfImplicationGraph()
std::vector< std::vector< int > > Run()
In SWIG mode, we don't want anything besides these top-level includes.
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
void FindCliques(std::function< bool(int, int)> graph, int node_count, std::function< bool(const std::vector< int > &)> callback)
void CoverArcsByCliques(std::function< bool(int, int)> graph, int node_count, std::function< bool(const std::vector< int > &)> callback)