24#ifndef OR_TOOLS_GRAPH_CLIQUES_H_
25#define OR_TOOLS_GRAPH_CLIQUES_H_
33#include "absl/strings/str_cat.h"
34#include "ortools/base/logging.h"
35#include "ortools/base/strong_int.h"
36#include "ortools/base/strong_vector.h"
37#include "ortools/util/time_limit.h"
46void FindCliques(std::function<
bool(
int,
int)> graph,
int node_count,
47 std::function<
bool(
const std::vector<int>&)> callback);
56 std::function<
bool(
const std::vector<int>&)> callback);
145template <
typename NodeIndex>
168 : is_arc_(std::move(is_arc)),
169 clique_callback_(std::move(clique_callback)),
170 num_nodes_(num_nodes) {}
195 TimeLimit* time_limit);
211 DEFINE_STRONG_INT_TYPE(CandidateIndex, ptrdiff_t);
223 State(
const State& other)
224 : pivot(other.pivot),
225 num_remaining_candidates(other.num_remaining_candidates),
226 candidates(other.candidates),
227 first_candidate_index(other.first_candidate_index),
228 candidate_for_recursion(other.candidate_for_recursion) {}
230 State& operator=(
const State& other) {
232 num_remaining_candidates = other.num_remaining_candidates;
233 candidates = other.candidates;
234 first_candidate_index = other.first_candidate_index;
235 candidate_for_recursion = other.candidate_for_recursion;
242 inline void MoveFirstCandidateToNotSet() {
243 ++first_candidate_index;
244 --num_remaining_candidates;
248 std::string DebugString() {
250 absl::StrAppend(&buffer,
"pivot = ", pivot,
251 "\nnum_remaining_candidates = ", num_remaining_candidates,
253 for (CandidateIndex i(0); i < candidates.size(); ++i) {
254 if (i > 0) buffer +=
", ";
255 absl::StrAppend(&buffer, candidates[i]);
258 &buffer,
"]\nfirst_candidate_index = ", first_candidate_index.value(),
259 "\ncandidate_for_recursion = ", candidate_for_recursion.value());
268 int num_remaining_candidates;
277 absl::StrongVector<CandidateIndex, NodeIndex> candidates;
281 CandidateIndex first_candidate_index;
285 CandidateIndex candidate_for_recursion;
298 static const double kPushStateDeterministicTimeSecondsPerCandidate;
314 void InitializeState(State* state);
318 return node1 == node2 || is_arc_(node1, node2);
324 CandidateIndex SelectCandidateIndexForRecursion(State* state);
327 std::string CliqueDebugString(
const std::vector<NodeIndex>& clique);
345 std::vector<State> states_;
348 std::vector<NodeIndex> current_clique_;
352 int64_t num_remaining_iterations_;
356 TimeLimit* time_limit_;
359template <
typename NodeIndex>
360void BronKerboschAlgorithm<NodeIndex>::InitializeState(State* state) {
361 DCHECK(state !=
nullptr);
362 const int num_candidates = state->candidates.size();
363 int num_disconnected_candidates = num_candidates;
365 CandidateIndex pivot_index(-1);
366 for (CandidateIndex pivot_candidate_index(0);
367 pivot_candidate_index < num_candidates &&
368 num_disconnected_candidates > 0;
369 ++pivot_candidate_index) {
370 const NodeIndex pivot_candidate = state->candidates[pivot_candidate_index];
372 for (CandidateIndex i(state->first_candidate_index); i < num_candidates;
374 if (!IsArc(pivot_candidate, state->candidates[i])) {
378 if (count < num_disconnected_candidates) {
379 pivot_index = pivot_candidate_index;
380 state->pivot = pivot_candidate;
381 num_disconnected_candidates = count;
384 state->num_remaining_candidates = num_disconnected_candidates;
385 if (pivot_index >= state->first_candidate_index) {
386 std::swap(state->candidates[pivot_index],
387 state->candidates[state->first_candidate_index]);
388 ++state->num_remaining_candidates;
392template <
typename NodeIndex>
393typename BronKerboschAlgorithm<NodeIndex>::CandidateIndex
394BronKerboschAlgorithm<NodeIndex>::SelectCandidateIndexForRecursion(
396 DCHECK(state !=
nullptr);
397 CandidateIndex disconnected_node_index =
398 std::max(state->first_candidate_index, state->candidate_for_recursion);
399 while (disconnected_node_index < state->candidates.size() &&
400 state->candidates[disconnected_node_index] != state->pivot &&
401 IsArc(state->pivot, state->candidates[disconnected_node_index])) {
402 ++disconnected_node_index;
404 state->candidate_for_recursion = disconnected_node_index;
405 return disconnected_node_index;
408template <
typename NodeIndex>
409void BronKerboschAlgorithm<NodeIndex>::Initialize() {
410 DCHECK(states_.empty());
411 states_.reserve(num_nodes_);
412 states_.emplace_back();
414 State*
const root_state = &states_.back();
415 root_state->first_candidate_index = 0;
416 root_state->candidate_for_recursion = 0;
417 root_state->candidates.resize(num_nodes_, 0);
418 std::iota(root_state->candidates.begin(), root_state->candidates.end(), 0);
419 root_state->num_remaining_candidates = num_nodes_;
420 InitializeState(root_state);
422 DVLOG(2) <<
"Initialized";
425template <
typename NodeIndex>
426void BronKerboschAlgorithm<NodeIndex>::PopState() {
427 DCHECK(!states_.empty());
429 if (!states_.empty()) {
430 State*
const state = &states_.back();
431 current_clique_.pop_back();
432 state->MoveFirstCandidateToNotSet();
436template <
typename NodeIndex>
437std::string BronKerboschAlgorithm<NodeIndex>::CliqueDebugString(
438 const std::vector<NodeIndex>& clique) {
439 std::string message =
"Clique: [ ";
441 absl::StrAppend(&message, node,
" ");
447template <
typename NodeIndex>
448void BronKerboschAlgorithm<NodeIndex>::PushState(
NodeIndex selected) {
449 DCHECK(!states_.empty());
450 DCHECK(time_limit_ !=
nullptr);
451 DVLOG(2) <<
"PushState: New depth = " << states_.size() + 1
452 <<
", selected node = " << selected;
453 absl::StrongVector<CandidateIndex, NodeIndex> new_candidates;
455 State*
const previous_state = &states_.back();
456 const double deterministic_time =
457 kPushStateDeterministicTimeSecondsPerCandidate *
458 previous_state->candidates.size();
459 time_limit_->AdvanceDeterministicTime(deterministic_time,
"PushState");
466 new_candidates.reserve(previous_state->candidates.size());
467 for (CandidateIndex i(0); i < previous_state->first_candidate_index; ++i) {
468 const NodeIndex candidate = previous_state->candidates[i];
469 if (IsArc(selected, candidate)) {
470 new_candidates.push_back(candidate);
473 const CandidateIndex new_first_candidate_index(new_candidates.size());
474 for (CandidateIndex i = previous_state->first_candidate_index + 1;
475 i < previous_state->candidates.size(); ++i) {
476 const NodeIndex candidate = previous_state->candidates[i];
477 if (IsArc(selected, candidate)) {
478 new_candidates.push_back(candidate);
482 current_clique_.push_back(selected);
483 if (new_candidates.empty()) {
486 DVLOG(2) << CliqueDebugString(current_clique_);
487 const CliqueResponse response = clique_callback_(current_clique_);
492 num_remaining_iterations_ = 1;
494 current_clique_.pop_back();
495 previous_state->MoveFirstCandidateToNotSet();
502 states_.emplace_back();
503 State*
const new_state = &states_.back();
504 new_state->candidates.swap(new_candidates);
505 new_state->first_candidate_index = new_first_candidate_index;
507 InitializeState(new_state);
510template <
typename NodeIndex>
512 int64_t max_num_iterations, TimeLimit* time_limit) {
513 CHECK(time_limit !=
nullptr);
514 time_limit_ = time_limit;
515 if (states_.empty()) {
518 for (num_remaining_iterations_ = max_num_iterations;
519 !states_.empty() && num_remaining_iterations_ > 0 &&
520 !time_limit->LimitReached();
521 --num_remaining_iterations_) {
522 State*
const state = &states_.back();
523 DVLOG(2) <<
"Loop: " << states_.size() <<
" states, "
524 << state->num_remaining_candidates <<
" candidate to explore\n"
525 << state->DebugString();
526 if (state->num_remaining_candidates == 0) {
531 const CandidateIndex selected_index =
532 SelectCandidateIndexForRecursion(state);
533 DVLOG(2) <<
"selected_index = " << selected_index;
534 const NodeIndex selected = state->candidates[selected_index];
535 DVLOG(2) <<
"Selected candidate = " << selected;
537 NodeIndex& f = state->candidates[state->first_candidate_index];
538 NodeIndex& s = state->candidates[selected_index];
543 time_limit_ =
nullptr;
548template <
typename NodeIndex>
550 int64_t max_num_iterations) {
551 TimeLimit time_limit(std::numeric_limits<double>::infinity());
552 return RunWithTimeLimit(max_num_iterations, &time_limit);
555template <
typename NodeIndex>
557 return RunIterations(std::numeric_limits<int64_t>::max());
560template <
typename NodeIndex>
562 NodeIndex>::kPushStateDeterministicTimeSecondsPerCandidate = 0.54663e-7;
BronKerboschAlgorithmStatus Run()
BronKerboschAlgorithm(IsArcCallback is_arc, NodeIndex num_nodes, CliqueCallback clique_callback)
BronKerboschAlgorithmStatus RunIterations(int64_t max_num_iterations)
BronKerboschAlgorithmStatus RunWithTimeLimit(int64_t max_num_iterations, TimeLimit *time_limit)
BronKerboschAlgorithmStatus RunWithTimeLimit(TimeLimit *time_limit)
std::function< bool(NodeIndex, NodeIndex)> IsArcCallback
std::function< CliqueResponse(const std::vector< NodeIndex > &)> CliqueCallback
void FindCliques(std::function< bool(int, int)> graph, int node_count, std::function< bool(const std::vector< int > &)> callback)
BronKerboschAlgorithmStatus
void CoverArcsByCliques(std::function< bool(int, int)> graph, int node_count, std::function< bool(const std::vector< int > &)> callback)