24 #ifndef OR_TOOLS_GRAPH_CLIQUES_H_
25 #define OR_TOOLS_GRAPH_CLIQUES_H_
34 #include "absl/strings/str_cat.h"
47 void FindCliques(std::function<
bool(
int,
int)> graph,
int node_count,
48 std::function<
bool(
const std::vector<int>&)>
callback);
57 std::function<
bool(
const std::vector<int>&)>
callback);
146 template <
typename NodeIndex>
169 : is_arc_(std::move(is_arc)),
170 clique_callback_(std::move(clique_callback)),
171 num_nodes_(num_nodes) {}
212 DEFINE_INT_TYPE(CandidateIndex, ptrdiff_t);
224 State(
const State& other)
225 : pivot(other.pivot),
226 num_remaining_candidates(other.num_remaining_candidates),
227 candidates(other.candidates),
228 first_candidate_index(other.first_candidate_index),
229 candidate_for_recursion(other.candidate_for_recursion) {}
231 State& operator=(
const State& other) {
233 num_remaining_candidates = other.num_remaining_candidates;
234 candidates = other.candidates;
235 first_candidate_index = other.first_candidate_index;
236 candidate_for_recursion = other.candidate_for_recursion;
243 inline void MoveFirstCandidateToNotSet() {
244 ++first_candidate_index;
245 --num_remaining_candidates;
249 std::string DebugString() {
251 absl::StrAppend(&buffer,
"pivot = ", pivot,
252 "\nnum_remaining_candidates = ", num_remaining_candidates,
254 for (CandidateIndex i(0); i < candidates.size(); ++i) {
255 if (i > 0) buffer +=
", ";
256 absl::StrAppend(&buffer, candidates[i]);
259 &buffer,
"]\nfirst_candidate_index = ", first_candidate_index.value(),
260 "\ncandidate_for_recursion = ", candidate_for_recursion.value());
269 int num_remaining_candidates;
282 CandidateIndex first_candidate_index;
286 CandidateIndex candidate_for_recursion;
299 static const double kPushStateDeterministicTimeSecondsPerCandidate;
315 void InitializeState(State* state);
319 return node1 == node2 || is_arc_(node1, node2);
325 CandidateIndex SelectCandidateIndexForRecursion(State* state);
328 std::string CliqueDebugString(
const std::vector<NodeIndex>& clique);
346 std::vector<State> states_;
349 std::vector<NodeIndex> current_clique_;
353 int64_t num_remaining_iterations_;
357 TimeLimit* time_limit_;
360 template <
typename NodeIndex>
361 void BronKerboschAlgorithm<NodeIndex>::InitializeState(State* state) {
362 DCHECK(state !=
nullptr);
363 const int num_candidates = state->candidates.size();
364 int num_disconnected_candidates = num_candidates;
366 CandidateIndex pivot_index(-1);
367 for (CandidateIndex pivot_candidate_index(0);
368 pivot_candidate_index < num_candidates &&
369 num_disconnected_candidates > 0;
370 ++pivot_candidate_index) {
371 const NodeIndex pivot_candidate = state->candidates[pivot_candidate_index];
373 for (CandidateIndex i(state->first_candidate_index); i < num_candidates;
375 if (!IsArc(pivot_candidate, state->candidates[i])) {
379 if (count < num_disconnected_candidates) {
380 pivot_index = pivot_candidate_index;
381 state->pivot = pivot_candidate;
382 num_disconnected_candidates = count;
385 state->num_remaining_candidates = num_disconnected_candidates;
386 if (pivot_index >= state->first_candidate_index) {
387 std::swap(state->candidates[pivot_index],
388 state->candidates[state->first_candidate_index]);
389 ++state->num_remaining_candidates;
393 template <
typename NodeIndex>
394 typename BronKerboschAlgorithm<NodeIndex>::CandidateIndex
395 BronKerboschAlgorithm<NodeIndex>::SelectCandidateIndexForRecursion(
397 DCHECK(state !=
nullptr);
398 CandidateIndex disconnected_node_index =
399 std::max(state->first_candidate_index, state->candidate_for_recursion);
400 while (disconnected_node_index < state->candidates.size() &&
401 state->candidates[disconnected_node_index] != state->pivot &&
402 IsArc(state->pivot, state->candidates[disconnected_node_index])) {
403 ++disconnected_node_index;
405 state->candidate_for_recursion = disconnected_node_index;
406 return disconnected_node_index;
409 template <
typename NodeIndex>
410 void BronKerboschAlgorithm<NodeIndex>::Initialize() {
411 DCHECK(states_.empty());
412 states_.reserve(num_nodes_);
413 states_.emplace_back();
415 State*
const root_state = &states_.back();
416 root_state->first_candidate_index = 0;
417 root_state->candidate_for_recursion = 0;
418 root_state->candidates.resize(num_nodes_, 0);
419 std::iota(root_state->candidates.begin(), root_state->candidates.end(), 0);
420 root_state->num_remaining_candidates = num_nodes_;
421 InitializeState(root_state);
423 DVLOG(2) <<
"Initialized";
426 template <
typename NodeIndex>
427 void BronKerboschAlgorithm<NodeIndex>::PopState() {
428 DCHECK(!states_.empty());
430 if (!states_.empty()) {
431 State*
const state = &states_.back();
432 current_clique_.pop_back();
433 state->MoveFirstCandidateToNotSet();
437 template <
typename NodeIndex>
438 std::string BronKerboschAlgorithm<NodeIndex>::CliqueDebugString(
439 const std::vector<NodeIndex>& clique) {
440 std::string
message =
"Clique: [ ";
442 absl::StrAppend(&
message, node,
" ");
448 template <
typename NodeIndex>
449 void BronKerboschAlgorithm<NodeIndex>::PushState(
NodeIndex selected) {
450 DCHECK(!states_.empty());
451 DCHECK(time_limit_ !=
nullptr);
452 DVLOG(2) <<
"PushState: New depth = " << states_.size() + 1
453 <<
", selected node = " << selected;
456 State*
const previous_state = &states_.back();
457 const double deterministic_time =
458 kPushStateDeterministicTimeSecondsPerCandidate *
459 previous_state->candidates.size();
460 time_limit_->AdvanceDeterministicTime(deterministic_time,
"PushState");
467 new_candidates.
reserve(previous_state->candidates.size());
468 for (CandidateIndex i(0); i < previous_state->first_candidate_index; ++i) {
469 const NodeIndex candidate = previous_state->candidates[i];
470 if (IsArc(selected, candidate)) {
474 const CandidateIndex new_first_candidate_index(new_candidates.
size());
475 for (CandidateIndex i = previous_state->first_candidate_index + 1;
476 i < previous_state->candidates.size(); ++i) {
477 const NodeIndex candidate = previous_state->candidates[i];
478 if (IsArc(selected, candidate)) {
483 current_clique_.push_back(selected);
484 if (new_candidates.
empty()) {
487 DVLOG(2) << CliqueDebugString(current_clique_);
493 num_remaining_iterations_ = 1;
495 current_clique_.pop_back();
496 previous_state->MoveFirstCandidateToNotSet();
503 states_.emplace_back();
504 State*
const new_state = &states_.back();
505 new_state->candidates.swap(new_candidates);
506 new_state->first_candidate_index = new_first_candidate_index;
508 InitializeState(new_state);
511 template <
typename NodeIndex>
516 if (states_.empty()) {
519 for (num_remaining_iterations_ = max_num_iterations;
520 !states_.empty() && num_remaining_iterations_ > 0 &&
522 --num_remaining_iterations_) {
523 State*
const state = &states_.back();
524 DVLOG(2) <<
"Loop: " << states_.size() <<
" states, "
525 << state->num_remaining_candidates <<
" candidate to explore\n"
526 << state->DebugString();
527 if (state->num_remaining_candidates == 0) {
532 const CandidateIndex selected_index =
533 SelectCandidateIndexForRecursion(state);
534 DVLOG(2) <<
"selected_index = " << selected_index;
535 const NodeIndex selected = state->candidates[selected_index];
536 DVLOG(2) <<
"Selected candidate = " << selected;
538 NodeIndex& f = state->candidates[state->first_candidate_index];
539 NodeIndex& s = state->candidates[selected_index];
544 time_limit_ =
nullptr;
549 template <
typename NodeIndex>
551 int64_t max_num_iterations) {
553 return RunWithTimeLimit(max_num_iterations, &
time_limit);
556 template <
typename NodeIndex>
561 template <
typename NodeIndex>
563 NodeIndex>::kPushStateDeterministicTimeSecondsPerCandidate = 0.54663e-7;
void reserve(size_type n)
void push_back(const value_type &x)
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
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
SharedResponseManager * response
ModelSharedTimeLimit * time_limit
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Collection of objects used to extend the Constraint Solver library.
void CoverArcsByCliques(std::function< bool(int, int)> graph, int node_count, std::function< bool(const std::vector< int > &)> callback)
BronKerboschAlgorithmStatus
void FindCliques(std::function< bool(int, int)> graph, int node_count, std::function< bool(const std::vector< int > &)> callback)
#define DVLOG(verboselevel)