24 #ifndef OR_TOOLS_GRAPH_CLIQUES_H_
25 #define OR_TOOLS_GRAPH_CLIQUES_H_
31 #include "absl/strings/str_cat.h"
32 #include "ortools/base/int_type.h"
33 #include "ortools/base/int_type_indexed_vector.h"
34 #include "ortools/base/logging.h"
35 #include "ortools/util/time_limit.h"
44 void FindCliques(std::function<
bool(
int,
int)> graph,
int node_count,
45 std::function<
bool(
const std::vector<int>&)> callback);
54 std::function<
bool(
const std::vector<int>&)> callback);
142 template <
typename NodeIndex>
165 : is_arc_(std::move(is_arc)),
166 clique_callback_(std::move(clique_callback)),
167 num_nodes_(num_nodes) {}
192 TimeLimit* time_limit);
208 DEFINE_INT_TYPE(CandidateIndex, ptrdiff_t);
220 State(
const State& other)
221 : pivot(other.pivot),
222 num_remaining_candidates(other.num_remaining_candidates),
223 candidates(other.candidates),
224 first_candidate_index(other.first_candidate_index),
225 candidate_for_recursion(other.candidate_for_recursion) {}
227 State& operator=(
const State& other) {
229 num_remaining_candidates = other.num_remaining_candidates;
230 candidates = other.candidates;
231 first_candidate_index = other.first_candidate_index;
232 candidate_for_recursion = other.candidate_for_recursion;
239 inline void MoveFirstCandidateToNotSet() {
240 ++first_candidate_index;
241 --num_remaining_candidates;
245 std::string DebugString() {
247 absl::StrAppend(&buffer,
"pivot = ", pivot,
248 "\nnum_remaining_candidates = ", num_remaining_candidates,
250 for (CandidateIndex i(0); i < candidates.size(); ++i) {
251 if (i > 0) buffer +=
", ";
252 absl::StrAppend(&buffer, candidates[i]);
255 &buffer,
"]\nfirst_candidate_index = ", first_candidate_index.value(),
256 "\ncandidate_for_recursion = ", candidate_for_recursion.value());
265 int num_remaining_candidates;
274 gtl::ITIVector<CandidateIndex, NodeIndex> candidates;
278 CandidateIndex first_candidate_index;
282 CandidateIndex candidate_for_recursion;
295 static const double kPushStateDeterministicTimeSecondsPerCandidate;
311 void InitializeState(State* state);
315 return node1 == node2 || is_arc_(node1, node2);
321 CandidateIndex SelectCandidateIndexForRecursion(State* state);
324 std::string CliqueDebugString(
const std::vector<NodeIndex>& clique);
342 std::vector<State> states_;
345 std::vector<NodeIndex> current_clique_;
349 int64 num_remaining_iterations_;
353 TimeLimit* time_limit_;
356 template <
typename NodeIndex>
357 void BronKerboschAlgorithm<NodeIndex>::InitializeState(State* state) {
358 DCHECK(state !=
nullptr);
359 const int num_candidates = state->candidates.size();
360 int num_disconnected_candidates = num_candidates;
362 CandidateIndex pivot_index(-1);
363 for (CandidateIndex pivot_candidate_index(0);
364 pivot_candidate_index < num_candidates &&
365 num_disconnected_candidates > 0;
366 ++pivot_candidate_index) {
367 const NodeIndex pivot_candidate = state->candidates[pivot_candidate_index];
369 for (CandidateIndex i(state->first_candidate_index); i < num_candidates;
371 if (!IsArc(pivot_candidate, state->candidates[i])) {
375 if (count < num_disconnected_candidates) {
376 pivot_index = pivot_candidate_index;
377 state->pivot = pivot_candidate;
378 num_disconnected_candidates = count;
381 state->num_remaining_candidates = num_disconnected_candidates;
382 if (pivot_index >= state->first_candidate_index) {
383 std::swap(state->candidates[pivot_index],
384 state->candidates[state->first_candidate_index]);
385 ++state->num_remaining_candidates;
389 template <
typename NodeIndex>
390 typename BronKerboschAlgorithm<NodeIndex>::CandidateIndex
391 BronKerboschAlgorithm<NodeIndex>::SelectCandidateIndexForRecursion(
393 DCHECK(state !=
nullptr);
394 CandidateIndex disconnected_node_index =
395 std::max(state->first_candidate_index, state->candidate_for_recursion);
396 while (disconnected_node_index < state->candidates.size() &&
397 state->candidates[disconnected_node_index] != state->pivot &&
398 IsArc(state->pivot, state->candidates[disconnected_node_index])) {
399 ++disconnected_node_index;
401 state->candidate_for_recursion = disconnected_node_index;
402 return disconnected_node_index;
405 template <
typename NodeIndex>
406 void BronKerboschAlgorithm<NodeIndex>::Initialize() {
407 DCHECK(states_.empty());
408 states_.reserve(num_nodes_);
409 states_.emplace_back();
411 State*
const root_state = &states_.back();
412 root_state->first_candidate_index = 0;
413 root_state->candidate_for_recursion = 0;
414 root_state->candidates.resize(num_nodes_, 0);
415 std::iota(root_state->candidates.begin(), root_state->candidates.end(), 0);
416 root_state->num_remaining_candidates = num_nodes_;
417 InitializeState(root_state);
419 DVLOG(2) <<
"Initialized";
422 template <
typename NodeIndex>
423 void BronKerboschAlgorithm<NodeIndex>::PopState() {
424 DCHECK(!states_.empty());
426 if (!states_.empty()) {
427 State*
const state = &states_.back();
428 current_clique_.pop_back();
429 state->MoveFirstCandidateToNotSet();
433 template <
typename NodeIndex>
434 std::string BronKerboschAlgorithm<NodeIndex>::CliqueDebugString(
435 const std::vector<NodeIndex>& clique) {
436 std::string message =
"Clique: [ ";
438 absl::StrAppend(&message, node,
" ");
444 template <
typename NodeIndex>
445 void BronKerboschAlgorithm<NodeIndex>::PushState(
NodeIndex selected) {
446 DCHECK(!states_.empty());
447 DCHECK(time_limit_ !=
nullptr);
448 DVLOG(2) <<
"PushState: New depth = " << states_.size() + 1
449 <<
", selected node = " << selected;
450 gtl::ITIVector<CandidateIndex, NodeIndex> new_candidates;
452 State*
const previous_state = &states_.back();
453 const double deterministic_time =
454 kPushStateDeterministicTimeSecondsPerCandidate *
455 previous_state->candidates.size();
456 time_limit_->AdvanceDeterministicTime(deterministic_time,
"PushState");
463 new_candidates.reserve(previous_state->candidates.size());
464 for (CandidateIndex i(0); i < previous_state->first_candidate_index; ++i) {
465 const NodeIndex candidate = previous_state->candidates[i];
466 if (IsArc(selected, candidate)) {
467 new_candidates.push_back(candidate);
470 const CandidateIndex new_first_candidate_index(new_candidates.size());
471 for (CandidateIndex i = previous_state->first_candidate_index + 1;
472 i < previous_state->candidates.size(); ++i) {
473 const NodeIndex candidate = previous_state->candidates[i];
474 if (IsArc(selected, candidate)) {
475 new_candidates.push_back(candidate);
479 current_clique_.push_back(selected);
480 if (new_candidates.empty()) {
483 DVLOG(2) << CliqueDebugString(current_clique_);
484 const CliqueResponse response = clique_callback_(current_clique_);
489 num_remaining_iterations_ = 1;
491 current_clique_.pop_back();
492 previous_state->MoveFirstCandidateToNotSet();
499 states_.emplace_back();
500 State*
const new_state = &states_.back();
501 new_state->candidates.swap(new_candidates);
502 new_state->first_candidate_index = new_first_candidate_index;
504 InitializeState(new_state);
507 template <
typename NodeIndex>
509 int64 max_num_iterations, TimeLimit* time_limit) {
510 CHECK(time_limit !=
nullptr);
511 time_limit_ = time_limit;
512 if (states_.empty()) {
515 for (num_remaining_iterations_ = max_num_iterations;
516 !states_.empty() && num_remaining_iterations_ > 0 &&
517 !time_limit->LimitReached();
518 --num_remaining_iterations_) {
519 State*
const state = &states_.back();
520 DVLOG(2) <<
"Loop: " << states_.size() <<
" states, "
521 << state->num_remaining_candidates <<
" candidate to explore\n"
522 << state->DebugString();
523 if (state->num_remaining_candidates == 0) {
528 const CandidateIndex selected_index =
529 SelectCandidateIndexForRecursion(state);
530 DVLOG(2) <<
"selected_index = " << selected_index;
531 const NodeIndex selected = state->candidates[selected_index];
532 DVLOG(2) <<
"Selected candidate = " << selected;
534 NodeIndex& f = state->candidates[state->first_candidate_index];
535 NodeIndex& s = state->candidates[selected_index];
540 time_limit_ =
nullptr;
545 template <
typename NodeIndex>
547 int64 max_num_iterations) {
548 TimeLimit time_limit(std::numeric_limits<double>::infinity());
549 return RunWithTimeLimit(max_num_iterations, &time_limit);
552 template <
typename NodeIndex>
554 return RunIterations(kint64max);
557 template <
typename NodeIndex>
559 NodeIndex>::kPushStateDeterministicTimeSecondsPerCandidate = 0.54663e-7;
562 #endif // OR_TOOLS_GRAPH_CLIQUES_H_