24#ifndef OR_TOOLS_GRAPH_CLIQUES_H_
25#define OR_TOOLS_GRAPH_CLIQUES_H_
35#include "absl/strings/str_cat.h"
49 std::function<
bool(
const std::vector<int>&)>
callback);
58 std::function<
bool(
const std::vector<int>&)>
callback);
147template <
typename NodeIndex>
170 : is_arc_(
std::move(is_arc)),
171 clique_callback_(
std::move(clique_callback)),
172 num_nodes_(num_nodes) {}
225 State(
const State& other)
226 : pivot(other.pivot),
227 num_remaining_candidates(other.num_remaining_candidates),
228 candidates(other.candidates),
229 first_candidate_index(other.first_candidate_index),
230 candidate_for_recursion(other.candidate_for_recursion) {}
232 State& operator=(
const State& other) {
234 num_remaining_candidates = other.num_remaining_candidates;
235 candidates = other.candidates;
236 first_candidate_index = other.first_candidate_index;
237 candidate_for_recursion = other.candidate_for_recursion;
244 inline void MoveFirstCandidateToNotSet() {
245 ++first_candidate_index;
246 --num_remaining_candidates;
250 std::string DebugString() {
252 absl::StrAppend(&buffer,
"pivot = ", pivot,
253 "\nnum_remaining_candidates = ", num_remaining_candidates,
255 for (CandidateIndex
i(0);
i < candidates.size(); ++
i) {
256 if (i > 0) buffer +=
", ";
257 absl::StrAppend(&buffer, candidates[i]);
260 &buffer,
"]\nfirst_candidate_index = ", first_candidate_index.value(),
261 "\ncandidate_for_recursion = ", candidate_for_recursion.value());
270 int num_remaining_candidates;
283 CandidateIndex first_candidate_index;
287 CandidateIndex candidate_for_recursion;
300 static const double kPushStateDeterministicTimeSecondsPerCandidate;
316 void InitializeState(State* state);
320 return node1 == node2 || is_arc_(node1, node2);
326 CandidateIndex SelectCandidateIndexForRecursion(State* state);
329 std::string CliqueDebugString(
const std::vector<NodeIndex>& clique);
347 std::vector<State> states_;
350 std::vector<NodeIndex> current_clique_;
354 int64_t num_remaining_iterations_;
358 TimeLimit* time_limit_;
361template <
typename NodeIndex>
362void BronKerboschAlgorithm<NodeIndex>::InitializeState(State* state) {
363 DCHECK(state !=
nullptr);
364 const int num_candidates = state->candidates.size();
365 int num_disconnected_candidates = num_candidates;
367 CandidateIndex pivot_index(-1);
368 for (CandidateIndex pivot_candidate_index(0);
369 pivot_candidate_index < num_candidates &&
370 num_disconnected_candidates > 0;
371 ++pivot_candidate_index) {
372 const NodeIndex pivot_candidate = state->candidates[pivot_candidate_index];
374 for (CandidateIndex
i(state->first_candidate_index); i < num_candidates;
376 if (!IsArc(pivot_candidate, state->candidates[i])) {
380 if (count < num_disconnected_candidates) {
381 pivot_index = pivot_candidate_index;
382 state->pivot = pivot_candidate;
383 num_disconnected_candidates = count;
386 state->num_remaining_candidates = num_disconnected_candidates;
387 if (pivot_index >= state->first_candidate_index) {
388 std::swap(state->candidates[pivot_index],
389 state->candidates[state->first_candidate_index]);
390 ++state->num_remaining_candidates;
394template <
typename NodeIndex>
395typename BronKerboschAlgorithm<NodeIndex>::CandidateIndex
396BronKerboschAlgorithm<NodeIndex>::SelectCandidateIndexForRecursion(
398 DCHECK(state !=
nullptr);
399 CandidateIndex disconnected_node_index =
400 std::max(state->first_candidate_index, state->candidate_for_recursion);
401 while (disconnected_node_index < state->candidates.size() &&
402 state->candidates[disconnected_node_index] != state->pivot &&
403 IsArc(state->pivot, state->candidates[disconnected_node_index])) {
404 ++disconnected_node_index;
406 state->candidate_for_recursion = disconnected_node_index;
407 return disconnected_node_index;
410template <
typename NodeIndex>
411void BronKerboschAlgorithm<NodeIndex>::Initialize() {
412 DCHECK(states_.empty());
413 states_.reserve(num_nodes_);
414 states_.emplace_back();
416 State*
const root_state = &states_.back();
417 root_state->first_candidate_index = 0;
418 root_state->candidate_for_recursion = 0;
419 root_state->candidates.resize(num_nodes_, 0);
420 std::iota(root_state->candidates.begin(), root_state->candidates.end(), 0);
421 root_state->num_remaining_candidates = num_nodes_;
422 InitializeState(root_state);
424 DVLOG(2) <<
"Initialized";
427template <
typename NodeIndex>
428void BronKerboschAlgorithm<NodeIndex>::PopState() {
429 DCHECK(!states_.empty());
431 if (!states_.empty()) {
432 State*
const state = &states_.back();
433 current_clique_.pop_back();
434 state->MoveFirstCandidateToNotSet();
438template <
typename NodeIndex>
439std::string BronKerboschAlgorithm<NodeIndex>::CliqueDebugString(
440 const std::vector<NodeIndex>& clique) {
441 std::string
message =
"Clique: [ ";
443 absl::StrAppend(&
message, node,
" ");
449template <
typename NodeIndex>
450void BronKerboschAlgorithm<NodeIndex>::PushState(
NodeIndex selected) {
451 DCHECK(!states_.empty());
452 DCHECK(time_limit_ !=
nullptr);
453 DVLOG(2) <<
"PushState: New depth = " << states_.size() + 1
454 <<
", selected node = " << selected;
457 State*
const previous_state = &states_.back();
458 const double deterministic_time =
459 kPushStateDeterministicTimeSecondsPerCandidate *
460 previous_state->candidates.size();
461 time_limit_->AdvanceDeterministicTime(deterministic_time,
"PushState");
468 new_candidates.
reserve(previous_state->candidates.size());
469 for (CandidateIndex
i(0);
i < previous_state->first_candidate_index; ++
i) {
470 const NodeIndex candidate = previous_state->candidates[
i];
471 if (IsArc(selected, candidate)) {
475 const CandidateIndex new_first_candidate_index(new_candidates.size());
476 for (CandidateIndex i = previous_state->first_candidate_index + 1;
477 i < previous_state->candidates.size(); ++i) {
478 const NodeIndex candidate = previous_state->candidates[
i];
479 if (IsArc(selected, candidate)) {
484 current_clique_.push_back(selected);
485 if (new_candidates.empty()) {
488 DVLOG(2) << CliqueDebugString(current_clique_);
489 const CliqueResponse response = clique_callback_(current_clique_);
494 num_remaining_iterations_ = 1;
496 current_clique_.pop_back();
497 previous_state->MoveFirstCandidateToNotSet();
504 states_.emplace_back();
505 State*
const new_state = &states_.back();
506 new_state->candidates.swap(new_candidates);
507 new_state->first_candidate_index = new_first_candidate_index;
509 InitializeState(new_state);
512template <
typename NodeIndex>
517 if (states_.empty()) {
520 for (num_remaining_iterations_ = max_num_iterations;
521 !states_.empty() && num_remaining_iterations_ > 0 &&
523 --num_remaining_iterations_) {
524 State*
const state = &states_.back();
525 DVLOG(2) <<
"Loop: " << states_.size() <<
" states, "
526 << state->num_remaining_candidates <<
" candidate to explore\n"
527 << state->DebugString();
528 if (state->num_remaining_candidates == 0) {
533 const CandidateIndex selected_index =
534 SelectCandidateIndexForRecursion(state);
535 DVLOG(2) <<
"selected_index = " << selected_index;
536 const NodeIndex selected = state->candidates[selected_index];
537 DVLOG(2) <<
"Selected candidate = " << selected;
539 NodeIndex& f = state->candidates[state->first_candidate_index];
540 NodeIndex& s = state->candidates[selected_index];
545 time_limit_ =
nullptr;
550template <
typename NodeIndex>
552 int64_t max_num_iterations) {
554 return RunWithTimeLimit(max_num_iterations, &
time_limit);
557template <
typename NodeIndex>
559 return RunIterations(std::numeric_limits<int64_t>::max());
562template <
typename NodeIndex>
564 NodeIndex>::kPushStateDeterministicTimeSecondsPerCandidate = 0.54663e-7;
std::function< bool(NodeIndex, NodeIndex)> IsArcCallback
BronKerboschAlgorithmStatus RunWithTimeLimit(TimeLimit *time_limit)
BronKerboschAlgorithmStatus Run()
std::function< CliqueResponse(const std::vector< NodeIndex > &)> CliqueCallback
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)
void push_back(const value_type &val)
void reserve(size_type n)
#define DEFINE_INT_TYPE(int_type_name, value_type)
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)
BronKerboschAlgorithmStatus
@ COMPLETED
The algorithm has enumerated all maximal cliques.
@ CONTINUE
The algorithm will continue searching for other maximal cliques.
void CoverArcsByCliques(std::function< bool(int, int)> graph, int node_count, std::function< bool(const std::vector< int > &)> callback)