Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
cliques.h
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// Maximal clique algorithms, based on the Bron-Kerbosch algorithm.
15// See http://en.wikipedia.org/wiki/Bron-Kerbosch_algorithm
16// and
17// C. Bron and J. Kerbosch, Joep, "Algorithm 457: finding all cliques of an
18// undirected graph", CACM 16 (9): 575-577, 1973.
19// http://dl.acm.org/citation.cfm?id=362367&bnc=1.
20//
21// Keywords: undirected graph, clique, clique cover, Bron, Kerbosch.
22
23#ifndef OR_TOOLS_GRAPH_CLIQUES_H_
24#define OR_TOOLS_GRAPH_CLIQUES_H_
25
26#include <cstddef>
27#include <cstdint>
28#include <functional>
29#include <limits>
30#include <numeric>
31#include <string>
32#include <utility>
33#include <vector>
34
35#include "absl/strings/str_cat.h"
39#include "ortools/util/bitset.h"
41
42namespace operations_research {
43
44// Finds all maximal cliques, even of size 1, in the
45// graph described by the graph callback. graph->Run(i, j) indicates
46// if there is an arc between i and j.
47// This function takes ownership of 'callback' and deletes it after it has run.
48// If 'callback' returns true, then the search for cliques stops.
49void FindCliques(std::function<bool(int, int)> graph, int node_count,
50 std::function<bool(const std::vector<int>&)> callback);
51
52// Covers the maximum number of arcs of the graph with cliques. The graph
53// is described by the graph callback. graph->Run(i, j) indicates if
54// there is an arc between i and j.
55// This function takes ownership of 'callback' and deletes it after it has run.
56// It calls 'callback' upon each clique.
57// It ignores cliques of size 1.
58void CoverArcsByCliques(std::function<bool(int, int)> graph, int node_count,
59 std::function<bool(const std::vector<int>&)> callback);
60
61// Possible return values of the callback for reporting cliques. The returned
62// value determines whether the algorithm will continue the search.
63enum class CliqueResponse {
64 // The algorithm will continue searching for other maximal cliques.
66 // The algorithm will stop the search immediately. The search can be resumed
67 // by calling BronKerboschAlgorithm::Run (resp. RunIterations) again.
69};
70
71// The status value returned by BronKerboschAlgorithm::Run and
72// BronKerboschAlgorithm::RunIterations.
74 // The algorithm has enumerated all maximal cliques.
76 // The search algorithm was interrupted either because it reached the
77 // iteration limit or because the clique callback returned
78 // CliqueResponse::STOP.
80};
81
82// Implements the Bron-Kerbosch algorithm for finding maximal cliques.
83// The graph is represented as a callback that gets two nodes as its arguments
84// and it returns true if and only if there is an arc between the two nodes. The
85// cliques are reported back to the user using a second callback.
86//
87// Typical usage:
88// auto graph = [](int node1, int node2) { return true; };
89// auto on_clique = [](const std::vector<int>& clique) {
90// LOG(INFO) << "Clique!";
91// };
92//
93// BronKerboschAlgorithm<int> bron_kerbosch(graph, num_nodes, on_clique);
94// bron_kerbosch.Run();
95//
96// or:
97//
98// BronKerboschAlgorithm bron_kerbosch(graph, num_nodes, clique);
99// bron_kerbosch.RunIterations(kMaxNumIterations);
100//
101// This is a non-recursive implementation of the Bron-Kerbosch algorithm with
102// pivots as described in the paper by Bron and Kerbosch (1973) (the version 2
103// algorithm in the paper).
104// The basic idea of the algorithm is to incrementally build the cliques using
105// depth-first search. During the search, the algorithm maintains two sets of
106// candidates (nodes that are connected to all nodes in the current clique):
107// - the "not" set - these are candidates that were already visited by the
108// search and all the maximal cliques that contain them as a part of the
109// current clique were already reported.
110// - the actual candidates - these are candidates that were not visited yet, and
111// they can be added to the clique.
112// In each iteration, the algorithm does the first of the following actions that
113// applies:
114// A. If there are no actual candidates and there are candidates in the "not"
115// set, or if all actual candidates are connected to the same node in the
116// "not" set, the current clique can't be extended to a maximal clique that
117// was not already reported. Return from the recursive call and move the
118// selected candidate to the set "not".
119// B. If there are no candidates at all, it means that the current clique can't
120// be extended and that it is in fact a maximal clique. Report it to the user
121// and return from the recursive call. Move the selected candidate to the set
122// "not".
123// C. Otherwise, there are actual candidates, extend the current clique with one
124// of these candidates and process it recursively.
125//
126// To avoid unnecessary steps, the algorithm selects a pivot at each level of
127// the recursion to guide the selection of candidates added to the current
128// clique. The pivot can be either in the "not" set and among the actual
129// candidates. The algorithm tries to move the pivot and all actual candidates
130// connected to it to the set "not" as quickly as possible. This will fulfill
131// the conditions of step A, and the search algorithm will be able to leave the
132// current branch. Selecting a pivot that has the lowest number of disconnected
133// nodes among the candidates can reduce the running time significantly.
134//
135// The worst-case maximal depth of the recursion is equal to the number of nodes
136// in the graph, which makes the natural recursive implementation impractical
137// for nodes with more than a few thousands of nodes. To avoid the limitation,
138// this class simulates the recursion by maintaining a stack with the state at
139// each level of the recursion. The algorithm then runs in a loop. In each
140// iteration, the algorithm can do one or both of:
141// 1. Return to the previous recursion level (step A or B of the algorithm) by
142// removing the top state from the stack.
143// 2. Select the next candidate and enter the next recursion level (step C of
144// the algorithm) by adding a new state to the stack.
145//
146// The worst-case time complexity of the algorithm is O(3^(N/3)), and the memory
147// complexity is O(N^2), where N is the number of nodes in the graph.
148template <typename NodeIndex>
150 public:
151 // A callback called by the algorithm to test if there is an arc between a
152 // pair of nodes. The callback must return true if and only if there is an
153 // arc. Note that to function properly, the function must be symmetrical
154 // (represent an undirected graph).
155 using IsArcCallback = std::function<bool(NodeIndex, NodeIndex)>;
156 // A callback called by the algorithm to report a maximal clique to the user.
157 // The clique is returned as a list of nodes in the clique, in no particular
158 // order. The caller must make a copy of the vector if they want to keep the
159 // nodes.
160 //
161 // The return value of the callback controls how the algorithm continues after
162 // this clique. See the description of the values of 'CliqueResponse' for more
163 // details.
165 std::function<CliqueResponse(const std::vector<NodeIndex>&)>;
166
167 // Initializes the Bron-Kerbosch algorithm for the given graph and clique
168 // callback function.
170 CliqueCallback clique_callback)
171 : is_arc_(std::move(is_arc)),
172 clique_callback_(std::move(clique_callback)),
173 num_nodes_(num_nodes) {}
174
175 // Runs the Bron-Kerbosch algorithm for kint64max iterations. In practice,
176 // this is equivalent to running until completion or until the clique callback
177 // returns BronKerboschAlgorithmStatus::STOP. If the method returned because
178 // the search is finished, it will return COMPLETED; otherwise, it will return
179 // INTERRUPTED and it can be resumed by calling this method again.
181
182 // Runs at most 'max_num_iterations' iterations of the Bron-Kerbosch
183 // algorithm. When this function returns INTERRUPTED, there is still work to
184 // be done to process all the cliques in the graph. In such case the method
185 // can be called again and it will resume the work where the previous call had
186 // stopped. When it returns COMPLETED any subsequent call to the method will
187 // resume the search from the beginning.
188 BronKerboschAlgorithmStatus RunIterations(int64_t max_num_iterations);
189
190 // Runs at most 'max_num_iterations' iterations of the Bron-Kerbosch
191 // algorithm, until the time limit is exceeded or until all cliques are
192 // enumerated. When this function returns INTERRUPTED, there is still work to
193 // be done to process all the cliques in the graph. In such case the method
194 // can be called again and it will resume the work where the previous call had
195 // stopped. When it returns COMPLETED any subsequent call to the method will
196 // resume the search from the beginning.
197 BronKerboschAlgorithmStatus RunWithTimeLimit(int64_t max_num_iterations,
199
200 // Runs the Bron-Kerbosch algorithm for at most kint64max iterations, until
201 // the time limit is excceded or until all cliques are enumerated. In
202 // practice, running the algorithm for kint64max iterations is equivalent to
203 // running until completion or until the other stopping conditions apply. When
204 // this function returns INTERRUPTED, there is still work to be done to
205 // process all the cliques in the graph. In such case the method can be called
206 // again and it will resume the work where the previous call had stopped. When
207 // it returns COMPLETED any subsequent call to the method will resume the
208 // search from the beginning.
210 return RunWithTimeLimit(std::numeric_limits<int64_t>::max(), time_limit);
211 }
212
213 private:
214 DEFINE_INT_TYPE(CandidateIndex, ptrdiff_t);
215
216 // A data structure that maintains the variables of one "iteration" of the
217 // search algorithm. These are the variables that would normally be allocated
218 // on the stack in the recursive implementation.
219 //
220 // Note that most of the variables in the structure are explicitly left
221 // uninitialized by the constructor to avoid wasting resources on values that
222 // will be overwritten anyway. Most of the initialization is done in
223 // BronKerboschAlgorithm::InitializeState.
224 struct State {
225 State() {}
226 State(const State& other)
227 : pivot(other.pivot),
228 num_remaining_candidates(other.num_remaining_candidates),
229 candidates(other.candidates),
230 first_candidate_index(other.first_candidate_index),
231 candidate_for_recursion(other.candidate_for_recursion) {}
232
233 State& operator=(const State& other) {
234 pivot = other.pivot;
235 num_remaining_candidates = other.num_remaining_candidates;
236 candidates = other.candidates;
237 first_candidate_index = other.first_candidate_index;
238 candidate_for_recursion = other.candidate_for_recursion;
239 return *this;
240 }
241
242 // Moves the first candidate in the state to the "not" set. Assumes that the
243 // first candidate is also the pivot or a candidate disconnected from the
244 // pivot (as done by RunIteration).
245 inline void MoveFirstCandidateToNotSet() {
246 ++first_candidate_index;
247 --num_remaining_candidates;
248 }
249
250 // Creates a human-readable representation of the current state.
251 std::string DebugString() {
252 std::string buffer;
253 absl::StrAppend(&buffer, "pivot = ", pivot,
254 "\nnum_remaining_candidates = ", num_remaining_candidates,
255 "\ncandidates = [");
256 for (CandidateIndex i(0); i < candidates.size(); ++i) {
257 if (i > 0) buffer += ", ";
258 absl::StrAppend(&buffer, candidates[i]);
259 }
260 absl::StrAppend(
261 &buffer, "]\nfirst_candidate_index = ", first_candidate_index.value(),
262 "\ncandidate_for_recursion = ", candidate_for_recursion.value());
263 return buffer;
264 }
265
266 // The pivot node selected for the given level of the recursion.
267 NodeIndex pivot;
268 // The number of remaining candidates to be explored at the given level of
269 // the recursion; the number is computed as num_disconnected_nodes +
270 // pre_increment in the original algorithm.
271 int num_remaining_candidates;
272 // The list of nodes that are candidates for extending the current clique.
273 // This vector has the format proposed in the paper by Bron-Kerbosch; the
274 // first 'first_candidate_index' elements of the vector represent the
275 // "not" set of nodes that were already visited by the algorithm. The
276 // remaining elements are the actual candidates for extending the current
277 // clique.
278 // NOTE(user): We could store the delta between the iterations; however,
279 // we need to evaluate the impact this would have on the performance.
280 util_intops::StrongVector<CandidateIndex, NodeIndex> candidates;
281 // The index of the first actual candidate in 'candidates'. This number is
282 // also the number of elements of the "not" set stored at the beginning of
283 // 'candidates'.
284 CandidateIndex first_candidate_index;
285
286 // The current position in candidates when looking for the pivot and/or the
287 // next candidate disconnected from the pivot.
288 CandidateIndex candidate_for_recursion;
289 };
290
291 // The deterministic time coefficients for the push and pop operations of the
292 // Bron-Kerbosch algorithm. The coefficients are set to match approximately
293 // the running time in seconds on a recent workstation on the random graph
294 // benchmark.
295 // NOTE(user): PushState is not the only source of complexity in the
296 // algorithm, but non-negative linear least squares produced zero coefficients
297 // for all other deterministic counters tested during the benchmarking. When
298 // we optimize the algorithm, we might need to add deterministic time to the
299 // other places that may produce complexity, namely InitializeState, PopState
300 // and SelectCandidateIndexForRecursion.
301 static const double kPushStateDeterministicTimeSecondsPerCandidate;
302
303 // Initializes the root state of the algorithm.
304 void Initialize();
305
306 // Removes the top state from the state stack. This is equivalent to returning
307 // in the recursive implementation of the algorithm.
308 void PopState();
309
310 // Adds a new state to the top of the stack, adding the node 'selected' to the
311 // current clique. This is equivalent to making a recurisve call in the
312 // recursive implementation of the algorithm.
313 void PushState(NodeIndex selected);
314
315 // Initializes the given state. Runs the pivot selection algorithm in the
316 // state.
317 void InitializeState(State* state);
318
319 // Returns true if (node1, node2) is an arc in the graph or if node1 == node2.
320 inline bool IsArc(NodeIndex node1, NodeIndex node2) const {
321 return node1 == node2 || is_arc_(node1, node2);
322 }
323
324 // Selects the next node for recursion. The selected node is either the pivot
325 // (if it is not in the set "not") or a node that is disconnected from the
326 // pivot.
327 CandidateIndex SelectCandidateIndexForRecursion(State* state);
328
329 // Returns a human-readable string representation of the clique.
330 std::string CliqueDebugString(const std::vector<NodeIndex>& clique);
331
332 // The callback called when the algorithm needs to determine if (node1, node2)
333 // is an arc in the graph.
334 IsArcCallback is_arc_;
335
336 // The callback called when the algorithm discovers a maximal clique. The
337 // return value of the callback controls how the algorithm proceeds with the
338 // clique search.
339 CliqueCallback clique_callback_;
340
341 // The number of nodes in the graph.
342 const NodeIndex num_nodes_;
343
344 // Contains the state of the aglorithm. The vector serves as an external stack
345 // for the recursive part of the algorithm - instead of using the C++ stack
346 // and natural recursion, it is implemented as a loop and new states are added
347 // to the top of the stack. The algorithm ends when the stack is empty.
348 std::vector<State> states_;
349
350 // A vector that receives the current clique found by the algorithm.
351 std::vector<NodeIndex> current_clique_;
352
353 // Set to true if the algorithm is active (it was not stopped by a the clique
354 // callback).
355 int64_t num_remaining_iterations_;
356
357 // The current time limit used by the solver. The time limit is assigned by
358 // the Run methods and it can be different for each call to run.
359 TimeLimit* time_limit_;
360};
361
362// More specialized version used to separate clique-cuts in MIP solver.
363// This finds all maximal clique with a weight greater than a given threshold.
364// It also has computation limit.
365//
366// This implementation assumes small graph since we use a dense bitmask
367// representation to encode the graph adjacency. So it shouldn't really be used
368// with more than a few thousands nodes.
370 public:
371 // Resets the class to an empty graph will all weights of zero.
372 // This also reset the work done.
373 void Initialize(int num_nodes);
374
375 // Set the weight of a given node, must be in [0, num_nodes).
376 // Weights are assumed to be non-negative.
377 void SetWeight(int i, double weight) { weights_[i] = weight; }
378
379 // Add an edge in the graph.
380 void AddEdge(int a, int b) {
381 graph_[a].Set(b);
382 graph_[b].Set(a);
383 }
384
385 // We count the number of basic operations, and stop when we reach this limit.
386 void SetWorkLimit(int64_t limit) { work_limit_ = limit; }
387
388 // Set the minimum weight of the maximal cliques we are looking for.
389 void SetMinimumWeight(double min_weight) { weight_threshold_ = min_weight; }
390
391 // This function is quite specific. It interprets node i as the negated
392 // literal of node i ^ 1. And all j in graph[i] as literal that are in at most
393 // two relation. So i implies all not(j) for all j in graph[i].
394 //
395 // The transitive close runs in O(num_nodes ^ 3) in the worst case, but since
396 // we process 64 bits at the time, it is okay to run it for graph up to 1k
397 // nodes.
399
400 // Runs the algo and returns all maximal clique with a weight above the
401 // configured thrheshold via SetMinimumWeight(). It is possible we reach the
402 // work limit before that.
403 std::vector<std::vector<int>> Run();
404
405 // Specific API where the index refer in the last result of Run().
406 // This allows to select cliques when they are many.
407 std::vector<std::pair<int, double>>& GetMutableIndexAndWeight() {
408 return clique_index_and_weight_;
409 }
410
411 int64_t WorkDone() const { return work_; }
412
413 bool HasEdge(int i, int j) const { return graph_[i][j]; }
414
415 private:
416 int64_t work_ = 0;
417 int64_t work_limit_ = std::numeric_limits<int64_t>::max();
418 double weight_threshold_ = 0.0;
419
420 std::vector<double> weights_;
421 std::vector<Bitset64<int>> graph_;
422
423 // Iterative DFS queue.
424 std::vector<int> queue_;
425
426 // Current clique we are constructing.
427 // Note this is always of size num_nodes, the clique is in [0, depth)
428 Bitset64<int> in_clique_;
429 std::vector<int> clique_;
430
431 // We maintain the weight of the clique. We use a stack to avoid floating
432 // point issue with +/- weights many times. So clique_weight_[i] is the sum of
433 // weight from [0, i) of element of the cliques.
434 std::vector<double> clique_weight_;
435
436 // Correspond to P and X in BronKerbosch description.
437 std::vector<Bitset64<int>> left_to_process_;
438 std::vector<Bitset64<int>> x_;
439
440 std::vector<std::pair<int, double>> clique_index_and_weight_;
441};
442
443template <typename NodeIndex>
444void BronKerboschAlgorithm<NodeIndex>::InitializeState(State* state) {
445 DCHECK(state != nullptr);
446 const int num_candidates = state->candidates.size();
447 int num_disconnected_candidates = num_candidates;
448 state->pivot = 0;
449 CandidateIndex pivot_index(-1);
450 for (CandidateIndex pivot_candidate_index(0);
451 pivot_candidate_index < num_candidates &&
452 num_disconnected_candidates > 0;
453 ++pivot_candidate_index) {
454 const NodeIndex pivot_candidate = state->candidates[pivot_candidate_index];
455 int count = 0;
456 for (CandidateIndex i(state->first_candidate_index); i < num_candidates;
457 ++i) {
458 if (!IsArc(pivot_candidate, state->candidates[i])) {
459 ++count;
460 }
461 }
462 if (count < num_disconnected_candidates) {
463 pivot_index = pivot_candidate_index;
464 state->pivot = pivot_candidate;
465 num_disconnected_candidates = count;
466 }
467 }
468 state->num_remaining_candidates = num_disconnected_candidates;
469 if (pivot_index >= state->first_candidate_index) {
470 std::swap(state->candidates[pivot_index],
471 state->candidates[state->first_candidate_index]);
472 ++state->num_remaining_candidates;
473 }
474}
475
476template <typename NodeIndex>
478BronKerboschAlgorithm<NodeIndex>::SelectCandidateIndexForRecursion(
479 State* state) {
480 DCHECK(state != nullptr);
481 CandidateIndex disconnected_node_index =
482 std::max(state->first_candidate_index, state->candidate_for_recursion);
483 while (disconnected_node_index < state->candidates.size() &&
484 state->candidates[disconnected_node_index] != state->pivot &&
485 IsArc(state->pivot, state->candidates[disconnected_node_index])) {
486 ++disconnected_node_index;
487 }
488 state->candidate_for_recursion = disconnected_node_index;
489 return disconnected_node_index;
490}
491
492template <typename NodeIndex>
493void BronKerboschAlgorithm<NodeIndex>::Initialize() {
494 DCHECK(states_.empty());
495 states_.reserve(num_nodes_);
496 states_.emplace_back();
497
498 State* const root_state = &states_.back();
499 root_state->first_candidate_index = 0;
500 root_state->candidate_for_recursion = 0;
501 root_state->candidates.resize(num_nodes_, 0);
502 std::iota(root_state->candidates.begin(), root_state->candidates.end(), 0);
503 root_state->num_remaining_candidates = num_nodes_;
504 InitializeState(root_state);
505
506 DVLOG(2) << "Initialized";
507}
508
509template <typename NodeIndex>
510void BronKerboschAlgorithm<NodeIndex>::PopState() {
511 DCHECK(!states_.empty());
512 states_.pop_back();
513 if (!states_.empty()) {
514 State* const state = &states_.back();
515 current_clique_.pop_back();
516 state->MoveFirstCandidateToNotSet();
517 }
518}
519
520template <typename NodeIndex>
521std::string BronKerboschAlgorithm<NodeIndex>::CliqueDebugString(
522 const std::vector<NodeIndex>& clique) {
523 std::string message = "Clique: [ ";
524 for (const NodeIndex node : clique) {
525 absl::StrAppend(&message, node, " ");
526 }
527 message += "]";
528 return message;
529}
530
531template <typename NodeIndex>
532void BronKerboschAlgorithm<NodeIndex>::PushState(NodeIndex selected) {
533 DCHECK(!states_.empty());
534 DCHECK(time_limit_ != nullptr);
535 DVLOG(2) << "PushState: New depth = " << states_.size() + 1
536 << ", selected node = " << selected;
537 util_intops::StrongVector<CandidateIndex, NodeIndex> new_candidates;
538
539 State* const previous_state = &states_.back();
540 const double deterministic_time =
542 previous_state->candidates.size();
543 time_limit_->AdvanceDeterministicTime(deterministic_time, "PushState");
544
545 // Add all candidates from previous_state->candidates that are connected to
546 // 'selected' in the graph to the vector 'new_candidates', skipping the node
547 // 'selected'; this node is always at the position
548 // 'previous_state->first_candidate_index', so we can skip it by skipping the
549 // element at this particular index.
550 new_candidates.reserve(previous_state->candidates.size());
551 for (CandidateIndex i(0); i < previous_state->first_candidate_index; ++i) {
552 const NodeIndex candidate = previous_state->candidates[i];
553 if (IsArc(selected, candidate)) {
554 new_candidates.push_back(candidate);
555 }
556 }
557 const CandidateIndex new_first_candidate_index(new_candidates.size());
558 for (CandidateIndex i = previous_state->first_candidate_index + 1;
559 i < previous_state->candidates.size(); ++i) {
560 const NodeIndex candidate = previous_state->candidates[i];
561 if (IsArc(selected, candidate)) {
562 new_candidates.push_back(candidate);
563 }
564 }
565
566 current_clique_.push_back(selected);
567 if (new_candidates.empty()) {
568 // We've found a clique. Report it to the user, but do not push the state
569 // because it would be popped immediately anyway.
570 DVLOG(2) << CliqueDebugString(current_clique_);
571 const CliqueResponse response = clique_callback_(current_clique_);
572 if (response == CliqueResponse::STOP) {
573 // The number of remaining iterations will be decremented at the end of
574 // the loop in RunIterations; setting it to 0 here would make it -1 at
575 // the end of the main loop.
576 num_remaining_iterations_ = 1;
577 }
578 current_clique_.pop_back();
579 previous_state->MoveFirstCandidateToNotSet();
580 return;
581 }
582
583 // NOTE(user): The following line may invalidate previous_state (if the
584 // vector data was re-allocated in the process). We must avoid using
585 // previous_state below here.
586 states_.emplace_back();
587 State* const new_state = &states_.back();
588 new_state->candidates.swap(new_candidates);
589 new_state->first_candidate_index = new_first_candidate_index;
590
591 InitializeState(new_state);
592}
593
594template <typename NodeIndex>
596 int64_t max_num_iterations, TimeLimit* time_limit) {
597 CHECK(time_limit != nullptr);
598 time_limit_ = time_limit;
599 if (states_.empty()) {
600 Initialize();
601 }
602 for (num_remaining_iterations_ = max_num_iterations;
603 !states_.empty() && num_remaining_iterations_ > 0 &&
604 !time_limit->LimitReached();
605 --num_remaining_iterations_) {
606 State* const state = &states_.back();
607 DVLOG(2) << "Loop: " << states_.size() << " states, "
608 << state->num_remaining_candidates << " candidate to explore\n"
609 << state->DebugString();
610 if (state->num_remaining_candidates == 0) {
611 PopState();
612 continue;
613 }
614
615 const CandidateIndex selected_index =
616 SelectCandidateIndexForRecursion(state);
617 DVLOG(2) << "selected_index = " << selected_index;
618 const NodeIndex selected = state->candidates[selected_index];
619 DVLOG(2) << "Selected candidate = " << selected;
620
621 NodeIndex& f = state->candidates[state->first_candidate_index];
622 NodeIndex& s = state->candidates[selected_index];
623 std::swap(f, s);
624
625 PushState(selected);
626 }
627 time_limit_ = nullptr;
628 return states_.empty() ? BronKerboschAlgorithmStatus::COMPLETED
630}
631
632template <typename NodeIndex>
634 int64_t max_num_iterations) {
635 TimeLimit time_limit(std::numeric_limits<double>::infinity());
636 return RunWithTimeLimit(max_num_iterations, &time_limit);
637}
638
639template <typename NodeIndex>
641 return RunIterations(std::numeric_limits<int64_t>::max());
642}
643
644template <typename NodeIndex>
645const double BronKerboschAlgorithm<
647} // namespace operations_research
648
649#endif // OR_TOOLS_GRAPH_CLIQUES_H_
std::function< bool(NodeIndex, NodeIndex)> IsArcCallback
Definition cliques.h:155
BronKerboschAlgorithmStatus RunWithTimeLimit(TimeLimit *time_limit)
Definition cliques.h:209
BronKerboschAlgorithmStatus Run()
Definition cliques.h:640
std::function< CliqueResponse(const std::vector< NodeIndex > &)> CliqueCallback
Definition cliques.h:164
BronKerboschAlgorithm(IsArcCallback is_arc, NodeIndex num_nodes, CliqueCallback clique_callback)
Definition cliques.h:169
BronKerboschAlgorithmStatus RunIterations(int64_t max_num_iterations)
Definition cliques.h:633
BronKerboschAlgorithmStatus RunWithTimeLimit(int64_t max_num_iterations, TimeLimit *time_limit)
Definition cliques.h:595
void SetMinimumWeight(double min_weight)
Set the minimum weight of the maximal cliques we are looking for.
Definition cliques.h:389
std::vector< std::pair< int, double > > & GetMutableIndexAndWeight()
Definition cliques.h:407
void AddEdge(int a, int b)
Add an edge in the graph.
Definition cliques.h:380
std::vector< std::vector< int > > Run()
Definition cliques.cc:298
void SetWorkLimit(int64_t limit)
We count the number of basic operations, and stop when we reach this limit.
Definition cliques.h:386
void push_back(const value_type &val)
void reserve(size_type n)
#define DEFINE_INT_TYPE(int_type_name, value_type)
Definition int_type.h:165
time_limit
Definition solve.cc:22
In SWIG mode, we don't want anything besides these top-level includes.
void FindCliques(std::function< bool(int, int)> graph, int node_count, std::function< bool(const std::vector< int > &)> callback)
Definition cliques.cc:230
BlossomGraph::NodeIndex NodeIndex
@ COMPLETED
The algorithm has enumerated all maximal cliques.
Definition cliques.h:75
const double BronKerboschAlgorithm< NodeIndex >::kPushStateDeterministicTimeSecondsPerCandidate
Definition cliques.h:646
@ CONTINUE
The algorithm will continue searching for other maximal cliques.
Definition cliques.h:65
void CoverArcsByCliques(std::function< bool(int, int)> graph, int node_count, std::function< bool(const std::vector< int > &)> callback)
Definition cliques.cc:244
STL namespace.