23#ifndef OR_TOOLS_GRAPH_CLIQUES_H_
24#define OR_TOOLS_GRAPH_CLIQUES_H_
35#include "absl/strings/str_cat.h"
49void FindCliques(std::function<
bool(
int,
int)> graph,
int node_count,
50 std::function<
bool(
const std::vector<int>&)> callback);
59 std::function<
bool(
const std::vector<int>&)> callback);
148template <
typename NodeIndex>
171 : is_arc_(
std::move(is_arc)),
172 clique_callback_(
std::move(clique_callback)),
173 num_nodes_(num_nodes) {}
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) {}
233 State& operator=(
const State& other) {
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;
245 inline void MoveFirstCandidateToNotSet() {
246 ++first_candidate_index;
247 --num_remaining_candidates;
251 std::string DebugString() {
253 absl::StrAppend(&buffer,
"pivot = ", pivot,
254 "\nnum_remaining_candidates = ", num_remaining_candidates,
256 for (CandidateIndex
i(0);
i < candidates.size(); ++
i) {
257 if (i > 0) buffer +=
", ";
258 absl::StrAppend(&buffer, candidates[i]);
261 &buffer,
"]\nfirst_candidate_index = ", first_candidate_index.value(),
262 "\ncandidate_for_recursion = ", candidate_for_recursion.value());
271 int num_remaining_candidates;
280 util_intops::StrongVector<CandidateIndex, NodeIndex> candidates;
284 CandidateIndex first_candidate_index;
288 CandidateIndex candidate_for_recursion;
301 static const double kPushStateDeterministicTimeSecondsPerCandidate;
317 void InitializeState(State* state);
321 return node1 == node2 || is_arc_(node1, node2);
327 CandidateIndex SelectCandidateIndexForRecursion(State* state);
330 std::string CliqueDebugString(
const std::vector<NodeIndex>& clique);
348 std::vector<State> states_;
351 std::vector<NodeIndex> current_clique_;
355 int64_t num_remaining_iterations_;
359 TimeLimit* time_limit_;
377 void SetWeight(
int i,
double weight) { weights_[i] = weight; }
403 std::vector<std::vector<int>>
Run();
408 return clique_index_and_weight_;
413 bool HasEdge(
int i,
int j)
const {
return graph_[i][j]; }
417 int64_t work_limit_ = std::numeric_limits<int64_t>::max();
418 double weight_threshold_ = 0.0;
420 std::vector<double> weights_;
421 std::vector<Bitset64<int>> graph_;
424 std::vector<int> queue_;
429 std::vector<int> clique_;
434 std::vector<double> clique_weight_;
437 std::vector<Bitset64<int>> left_to_process_;
438 std::vector<Bitset64<int>> x_;
440 std::vector<std::pair<int, double>> clique_index_and_weight_;
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;
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];
456 for (CandidateIndex i(state->first_candidate_index); i < num_candidates;
458 if (!IsArc(pivot_candidate, state->candidates[i])) {
462 if (count < num_disconnected_candidates) {
463 pivot_index = pivot_candidate_index;
464 state->pivot = pivot_candidate;
465 num_disconnected_candidates = count;
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;
476template <
typename NodeIndex>
478BronKerboschAlgorithm<NodeIndex>::SelectCandidateIndexForRecursion(
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;
488 state->candidate_for_recursion = disconnected_node_index;
489 return disconnected_node_index;
492template <
typename NodeIndex>
493void BronKerboschAlgorithm<NodeIndex>::Initialize() {
494 DCHECK(states_.empty());
495 states_.reserve(num_nodes_);
496 states_.emplace_back();
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);
506 DVLOG(2) <<
"Initialized";
509template <
typename NodeIndex>
510void BronKerboschAlgorithm<NodeIndex>::PopState() {
511 DCHECK(!states_.empty());
513 if (!states_.empty()) {
514 State*
const state = &states_.back();
515 current_clique_.pop_back();
516 state->MoveFirstCandidateToNotSet();
520template <
typename NodeIndex>
521std::string BronKerboschAlgorithm<NodeIndex>::CliqueDebugString(
522 const std::vector<NodeIndex>& clique) {
523 std::string message =
"Clique: [ ";
525 absl::StrAppend(&message, node,
" ");
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;
539 State*
const previous_state = &states_.back();
540 const double deterministic_time =
542 previous_state->candidates.size();
543 time_limit_->AdvanceDeterministicTime(deterministic_time,
"PushState");
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)) {
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)) {
566 current_clique_.push_back(selected);
567 if (new_candidates.empty()) {
570 DVLOG(2) << CliqueDebugString(current_clique_);
571 const CliqueResponse response = clique_callback_(current_clique_);
576 num_remaining_iterations_ = 1;
578 current_clique_.pop_back();
579 previous_state->MoveFirstCandidateToNotSet();
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;
591 InitializeState(new_state);
594template <
typename NodeIndex>
599 if (states_.empty()) {
602 for (num_remaining_iterations_ = max_num_iterations;
603 !states_.empty() && num_remaining_iterations_ > 0 &&
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) {
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;
621 NodeIndex& f = state->candidates[state->first_candidate_index];
622 NodeIndex& s = state->candidates[selected_index];
627 time_limit_ =
nullptr;
632template <
typename NodeIndex>
634 int64_t max_num_iterations) {
639template <
typename NodeIndex>
644template <
typename NodeIndex>
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 Initialize(int num_nodes)
void SetWeight(int i, double weight)
void SetMinimumWeight(double min_weight)
Set the minimum weight of the maximal cliques we are looking for.
std::vector< std::pair< int, double > > & GetMutableIndexAndWeight()
void AddEdge(int a, int b)
Add an edge in the graph.
bool HasEdge(int i, int j) const
void TakeTransitiveClosureOfImplicationGraph()
std::vector< std::vector< int > > Run()
void SetWorkLimit(int64_t limit)
We count the number of basic operations, and stop when we reach this 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)
BlossomGraph::NodeIndex NodeIndex
BronKerboschAlgorithmStatus
@ COMPLETED
The algorithm has enumerated all maximal cliques.
const double BronKerboschAlgorithm< NodeIndex >::kPushStateDeterministicTimeSecondsPerCandidate
@ 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)