28#include "absl/algorithm/container.h"
29#include "absl/container/flat_hash_map.h"
30#include "absl/container/flat_hash_set.h"
31#include "absl/flags/flag.h"
32#include "absl/log/check.h"
33#include "absl/random/distributions.h"
34#include "absl/random/random.h"
35#include "absl/strings/str_cat.h"
36#include "absl/strings/str_format.h"
37#include "absl/strings/string_view.h"
38#include "absl/time/time.h"
39#include "absl/types/span.h"
53 "Frequency of checks for better solutions in the solution pool.");
56 "Size of TSPs solved in the TSPOpt operator.");
59 "Size of TSPs solved in the TSPLns operator.");
61ABSL_FLAG(
bool, cp_use_empty_path_symmetry_breaker,
true,
62 "If true, equivalent empty paths are removed from the neighborhood "
85 CHECK(
delta !=
nullptr);
86 VLOG(2) <<
DebugString() <<
"::MakeNextNeighbor(delta=("
87 <<
delta->DebugString() <<
"), deltadelta=("
88 << (deltadelta ? deltadelta->
DebugString() : std::string(
"nullptr"));
116 for (
int candidate : fragment_) {
130 fragment_.push_back(
index);
141class SimpleLns :
public BaseLns {
143 SimpleLns(
const std::vector<IntVar*>& vars,
int number_of_variables)
144 :
BaseLns(vars), index_(0), number_of_variables_(number_of_variables) {
145 CHECK_GT(number_of_variables_, 0);
147 ~SimpleLns()
override {}
148 void InitFragments()
override { index_ = 0; }
149 bool NextFragment()
override;
150 std::string DebugString()
const override {
return "SimpleLns"; }
154 const int number_of_variables_;
157bool SimpleLns::NextFragment() {
158 const int size = Size();
160 for (
int i = index_;
i < index_ + number_of_variables_; ++
i) {
161 AppendToFragment(i %
size);
173class RandomLns :
public BaseLns {
175 RandomLns(
const std::vector<IntVar*>& vars,
int number_of_variables,
177 : BaseLns(vars), rand_(seed), number_of_variables_(number_of_variables) {
178 CHECK_GT(number_of_variables_, 0);
179 CHECK_LE(number_of_variables_, Size());
181 ~RandomLns()
override {}
182 bool NextFragment()
override;
184 std::string DebugString()
const override {
return "RandomLns"; }
188 const int number_of_variables_;
191bool RandomLns::NextFragment() {
192 DCHECK_GT(Size(), 0);
193 for (
int i = 0;
i < number_of_variables_; ++
i) {
194 AppendToFragment(absl::Uniform<int>(rand_, 0, Size()));
201 const std::vector<IntVar*>& vars,
int number_of_variables) {
202 return MakeRandomLnsOperator(vars, number_of_variables,
CpRandomSeed());
206 const std::vector<IntVar*>& vars,
int number_of_variables, int32_t seed) {
207 return RevAlloc(
new RandomLns(vars, number_of_variables, seed));
218 MoveTowardTargetLS(
const std::vector<IntVar*>& variables,
219 const std::vector<int64_t>& target_values)
221 target_(target_values),
225 variable_index_(Size() - 1) {
226 CHECK_EQ(target_values.size(), variables.size()) <<
"Illegal arguments.";
229 ~MoveTowardTargetLS()
override {}
231 std::string DebugString()
const override {
return "MoveTowardTargetLS"; }
235 bool MakeOneNeighbor()
override {
236 while (num_var_since_last_start_ < Size()) {
237 ++num_var_since_last_start_;
238 variable_index_ = (variable_index_ + 1) % Size();
239 const int64_t target_value = target_.at(variable_index_);
240 const int64_t current_value = OldValue(variable_index_);
241 if (current_value != target_value) {
242 SetValue(variable_index_, target_value);
250 void OnStart()
override {
262 CHECK_GE(variable_index_, 0);
263 CHECK_LT(variable_index_, Size());
264 num_var_since_last_start_ = 0;
268 const std::vector<int64_t> target_;
271 int64_t variable_index_;
274 int64_t num_var_since_last_start_;
280 typedef std::vector<IntVarElement> Elements;
283 std::vector<IntVar*> vars;
284 std::vector<int64_t> values;
287 for (
const auto& it : elements) {
288 vars.push_back(it.Var());
289 values.push_back(it.Value());
291 return MakeMoveTowardTargetOperator(vars, values);
295 const std::vector<IntVar*>& variables,
296 const std::vector<int64_t>& target_values) {
297 return RevAlloc(
new MoveTowardTargetLS(variables, target_values));
309 while (index_ <
size) {
318void ChangeValue::OnStart() { index_ = 0; }
323class IncrementValue :
public ChangeValue {
325 explicit IncrementValue(
const std::vector<IntVar*>& vars)
326 : ChangeValue(vars) {}
327 ~IncrementValue()
override {}
328 int64_t ModifyValue(int64_t, int64_t
value)
override {
return value + 1; }
330 std::string DebugString()
const override {
return "IncrementValue"; }
335class DecrementValue :
public ChangeValue {
337 explicit DecrementValue(
const std::vector<IntVar*>& vars)
338 : ChangeValue(vars) {}
339 ~DecrementValue()
override {}
340 int64_t ModifyValue(int64_t, int64_t
value)
override {
return value - 1; }
342 std::string DebugString()
const override {
return "DecrementValue"; }
349 const std::vector<IntVar*>& path_vars,
352 number_of_nexts_(next_vars.
size()),
353 ignore_path_vars_(path_vars.empty()),
354 base_nodes_(iteration_parameters.number_of_base_nodes),
355 base_alternatives_(iteration_parameters.number_of_base_nodes),
356 base_sibling_alternatives_(iteration_parameters.number_of_base_nodes),
357 end_nodes_(iteration_parameters.number_of_base_nodes),
358 base_paths_(iteration_parameters.number_of_base_nodes),
359 node_path_starts_(number_of_nexts_, -1),
360 node_path_ends_(number_of_nexts_, -1),
361 calls_per_base_node_(iteration_parameters.number_of_base_nodes, 0),
362 just_started_(false),
364 next_base_to_increment_(iteration_parameters.number_of_base_nodes),
365 iteration_parameters_(
std::move(iteration_parameters)),
366 optimal_paths_enabled_(false),
367 active_paths_(number_of_nexts_),
368 alternative_index_(next_vars.
size(), -1) {
373 path_basis_.push_back(0);
374 for (
int i = 1; i < base_nodes_.size(); ++i) {
377 if ((path_basis_.size() > 2) ||
378 (!next_vars.empty() && !next_vars.back()
381 .skip_locally_optimal_paths())) {
388void PathOperator::OnStart() {
389 optimal_paths_enabled_ =
false;
390 InitializeBaseNodes();
391 InitializeAlternatives();
396 while (IncrementPosition()) {
420 int64_t destination) {
421 if (destination == before_chain || destination == chain_end)
return false;
424 const int64_t destination_path =
Path(destination);
425 const int64_t after_chain =
Next(chain_end);
426 SetNext(chain_end,
Next(destination), destination_path);
428 int current = destination;
430 while (current != chain_end) {
436 SetNext(destination,
Next(before_chain), destination_path);
438 SetNext(before_chain, after_chain,
Path(before_chain));
443 int64_t* chain_last) {
445 int64_t path =
Path(before_chain);
446 int64_t current =
Next(before_chain);
447 if (current == after_chain) {
450 int64_t current_next =
Next(current);
451 SetNext(current, after_chain, path);
452 while (current_next != after_chain) {
453 const int64_t
next =
Next(current_next);
454 SetNext(current_next, current, path);
455 current = current_next;
458 SetNext(before_chain, current, path);
459 *chain_last = current;
467 int64_t destination_path =
Path(destination);
468 SetNext(node,
Next(destination), destination_path);
469 SetNext(destination, node, destination_path);
476 const int64_t kNoPath = -1;
479 const int64_t after_chain =
Next(chain_end);
480 int64_t current =
Next(before_chain);
481 while (current != after_chain) {
483 SetNext(current, current, kNoPath);
486 SetNext(before_chain, after_chain,
Path(before_chain));
493 if (active == inactive)
return false;
494 const int64_t prev =
Prev(active);
498bool PathOperator::IncrementPosition() {
502 just_started_ =
false;
505 const int number_of_paths = path_starts_.size();
511 int last_restarted = base_node_size;
512 for (
int i = base_node_size - 1; i >= 0; --i) {
516 const int sibling_alternative_index =
518 if (sibling_alternative_index >= 0) {
519 if (base_sibling_alternatives_[i] <
520 alternative_sets_[sibling_alternative_index].
size() - 1) {
521 ++base_sibling_alternatives_[i];
524 base_sibling_alternatives_[
i] = 0;
527 const int alternative_index = alternative_index_[base_nodes_[
i]];
528 if (alternative_index >= 0) {
529 if (base_alternatives_[i] <
530 alternative_sets_[alternative_index].
size() - 1) {
531 ++base_alternatives_[
i];
534 base_alternatives_[
i] = 0;
535 base_sibling_alternatives_[
i] = 0;
539 ++calls_per_base_node_[i] <
544 calls_per_base_node_[
i] = 0;
545 base_alternatives_[
i] = 0;
546 base_sibling_alternatives_[
i] = 0;
547 base_nodes_[
i] =
OldNext(base_nodes_[i]);
552 calls_per_base_node_[
i] = 0;
553 base_alternatives_[
i] = 0;
554 base_sibling_alternatives_[
i] = 0;
558 next_base_to_increment_ = base_node_size;
568 for (
int i = last_restarted;
i < base_node_size; ++
i) {
569 calls_per_base_node_[
i] = 0;
570 base_alternatives_[
i] = 0;
571 base_sibling_alternatives_[
i] = 0;
574 if (last_restarted > 0) {
580 if (optimal_paths_enabled_ &&
582 if (path_basis_.size() > 1) {
583 for (
int i = 1;
i < path_basis_.size(); ++
i) {
584 active_paths_.DeactivatePathPair(
StartNode(path_basis_[i - 1]),
588 active_paths_.DeactivatePathPair(
StartNode(path_basis_[0]),
592 std::vector<int> current_starts(base_node_size);
593 for (
int i = 0;
i < base_node_size; ++
i) {
598 optimal_paths_enabled_ =
true;
600 for (
int i = base_node_size - 1;
i >= 0; --
i) {
601 const int next_path_index = base_paths_[
i] + 1;
602 if (next_path_index < number_of_paths) {
603 base_paths_[
i] = next_path_index;
604 calls_per_base_node_[
i] = 0;
605 base_alternatives_[
i] = 0;
606 base_sibling_alternatives_[
i] = 0;
607 base_nodes_[
i] = path_starts_[next_path_index];
613 calls_per_base_node_[
i] = 0;
614 base_alternatives_[
i] = 0;
615 base_sibling_alternatives_[
i] = 0;
616 base_nodes_[
i] = path_starts_[0];
622 if (path_basis_.size() > 1) {
623 for (
int j = 1; j < path_basis_.size(); ++j) {
624 if (active_paths_.IsPathPairActive(
StartNode(path_basis_[j - 1]),
630 if (active_paths_.IsPathPairActive(
StartNode(path_basis_[0]),
637 if (!CheckEnds())
return false;
639 for (
int i = 0;
i < base_node_size; ++
i) {
645 if (stop)
return false;
650void PathOperator::InitializePathStarts() {
658 has_prevs[
next] =
true;
660 max_next = std::max(max_next,
next);
664 active_paths_.Initialize(
665 [&has_prevs](
int node) {
return !has_prevs[node]; });
671 active_paths_.ActivatePath(i);
682 std::vector<int64_t> new_path_starts;
683 const bool use_empty_path_symmetry_breaker =
684 absl::GetFlag(FLAGS_cp_use_empty_path_symmetry_breaker);
694 new_path_starts.push_back(i);
702 std::vector<int> node_paths(max_next + 1, -1);
703 for (
int i = 0;
i < path_starts_.size(); ++
i) {
704 int node = path_starts_[
i];
706 node_paths[node] =
i;
709 node_paths[node] =
i;
713 calls_per_base_node_[j] = 0;
714 base_alternatives_[j] = 0;
715 base_sibling_alternatives_[j] = 0;
716 if (
IsInactive(base_nodes_[j]) || node_paths[base_nodes_[j]] == -1) {
719 base_nodes_[j] = path_starts_[base_paths_[j]];
721 base_paths_[j] = node_paths[base_nodes_[j]];
728 absl::flat_hash_set<int> found_bases;
729 for (
int i = 0;
i < path_starts_.size(); ++
i) {
730 int index = new_index;
732 while (
index < new_path_starts.size() &&
733 new_path_starts[
index] < path_starts_[i]) {
736 const bool found = (
index < new_path_starts.size() &&
737 new_path_starts[
index] == path_starts_[i]);
742 if (base_paths_[j] == i && !found_bases.contains(j)) {
743 found_bases.insert(j);
744 base_paths_[j] = new_index;
748 base_nodes_[j] = new_path_starts[new_index];
754 path_starts_.swap(new_path_starts);
758 path_ends_.reserve(path_starts_.size());
760 for (
const int start_node : path_starts_) {
761 int64_t node = start_node;
763 path_ends_.push_back(node);
764 max_node_index = std::max(max_node_index, node);
766 node_path_starts_.assign(max_node_index + 1, -1);
767 node_path_ends_.assign(max_node_index + 1, -1);
768 for (
int i = 0;
i < path_starts_.size(); ++
i) {
769 const int64_t start_node = path_starts_[
i];
770 const int64_t end_node = path_ends_[
i];
771 int64_t node = start_node;
773 node_path_starts_[node] = start_node;
774 node_path_ends_[node] = end_node;
777 node_path_starts_[node] = start_node;
778 node_path_ends_[node] = end_node;
782void PathOperator::InitializeInactives() {
785 inactives_.push_back(
OldNext(i) == i);
789void PathOperator::InitializeBaseNodes() {
791 InitializeInactives();
792 InitializePathStarts();
798 base_nodes_[
i] = path_starts_[0];
800 first_start_ =
false;
804 int64_t base_node = base_nodes_[
i];
806 base_node = path_starts_[base_paths_[
i]];
807 base_nodes_[
i] = base_node;
809 end_nodes_[
i] = base_node;
815 !OnSamePath(base_nodes_[i - 1], base_nodes_[i])) {
816 const int64_t base_node = base_nodes_[
i - 1];
817 base_nodes_[
i] = base_node;
818 end_nodes_[
i] = base_node;
819 base_paths_[
i] = base_paths_[
i - 1];
823 base_alternatives_[
i] = 0;
824 base_sibling_alternatives_[
i] = 0;
825 calls_per_base_node_[
i] = 0;
827 just_started_ =
true;
830void PathOperator::InitializeAlternatives() {
831 active_in_alternative_set_.resize(alternative_sets_.size(), -1);
832 for (
int i = 0;
i < alternative_sets_.size(); ++
i) {
833 const int64_t current_active = active_in_alternative_set_[
i];
834 if (current_active >= 0 && !
IsInactive(current_active))
continue;
835 for (int64_t
index : alternative_sets_[i]) {
837 active_in_alternative_set_[
i] =
index;
844bool PathOperator::OnSamePath(int64_t node1, int64_t node2)
const {
867 int64_t exclude)
const {
868 if (before_chain == chain_end || before_chain == exclude)
return false;
869 int64_t current = before_chain;
871 while (current != chain_end) {
878 current =
Next(current);
880 if (current == exclude) {
900 const std::vector<IntVar*>& vars,
901 const std::vector<IntVar*>& secondary_vars,
902 std::function<
int(int64_t)> start_empty_path_class,
903 std::function<
const std::vector<int>&(
int,
int)> get_neighbors =
nullptr)
905 vars, secondary_vars, get_neighbors == nullptr ? 2 : 1,
907 std::move(start_empty_path_class),
std::move(get_neighbors)),
934 void OnNodeInitialization()
override { last_ = -1; }
949 node1 =
Next(neighbor);
955 if (last_base_ != node0 || last_ == -1 ||
HasNeighbors()) {
967 && last_ != chain_last) {
973 const int64_t to_move =
Next(last_);
974 DCHECK_EQ(
Next(to_move), node1);
996 const std::vector<IntVar*>& secondary_vars,
const std::string&
name,
997 std::function<
int(int64_t)> start_empty_path_class,
998 std::function<
const std::vector<int>&(
int,
int)> get_neighbors,
999 int64_t chain_length = 1LL,
bool single_path =
false)
1001 vars, secondary_vars, get_neighbors == nullptr ? 2 : 1,
1003 std::move(start_empty_path_class),
std::move(get_neighbors)),
1004 chain_length_(chain_length),
1005 single_path_(single_path),
1007 CHECK_GT(chain_length_, 0);
1010 const std::vector<IntVar*>& secondary_vars,
1011 std::function<
int(int64_t)> start_empty_path_class,
1012 std::function<
const std::vector<int>&(
int,
int)> get_neighbors,
1013 int64_t chain_length = 1LL,
bool single_path =
false)
1015 absl::StrCat(
"Relocate<", chain_length,
">"),
1016 std::move(start_empty_path_class),
std::move(get_neighbors),
1017 chain_length, single_path) {}
1019 const std::vector<IntVar*>& secondary_vars,
1020 std::function<
int(int64_t)> start_empty_path_class,
1021 int64_t chain_length = 1LL,
bool single_path =
false)
1023 absl::StrCat(
"Relocate<", chain_length,
">"),
1024 std::move(start_empty_path_class), nullptr, chain_length,
1035 return single_path_;
1039 const int64_t chain_length_;
1040 const bool single_path_;
1041 const std::string name_;
1045 const auto do_move = [
this](int64_t before_chain, int64_t destination) {
1047 int64_t chain_end = before_chain;
1048 for (
int i = 0; i < chain_length_; ++i) {
1049 if (
IsPathEnd(chain_end) || chain_end == destination)
return false;
1050 chain_end =
Next(chain_end);
1053 MoveChain(before_chain, chain_end, destination);
1058 return do_move(
Prev(node),
1078 const std::vector<IntVar*>& vars,
1079 const std::vector<IntVar*>& secondary_vars,
1080 std::function<
int(int64_t)> start_empty_path_class,
1081 std::function<
const std::vector<int>&(
int,
int)> get_neighbors =
nullptr)
1082 :
PathOperator(vars, secondary_vars, get_neighbors == nullptr ? 2 : 1,
1085 std::move(start_empty_path_class), get_neighbors) {}
1093 const auto do_move = [
this](int64_t node1, int64_t node2) {
1095 if (node1 == node2)
return false;
1096 const int64_t prev_node1 =
Prev(node1);
1124 const std::vector<IntVar*>& vars,
1125 const std::vector<IntVar*>& secondary_vars,
1126 std::function<
int(int64_t)> start_empty_path_class,
1127 std::function<
const std::vector<int>&(
int,
int)> get_neighbors =
nullptr)
1129 vars, secondary_vars, get_neighbors == nullptr ? 2 : 1,
1131 std::move(start_empty_path_class),
std::move(get_neighbors)) {}
1140 int64_t start1 = -1;
1143 if (node0 == start0)
return false;
1161 node1 = (start0 < start1) ?
Prev(neighbor) :
Next(neighbor);
1166 if (start1 == start0 || node1 == start1)
return false;
1169 if (start0 < start1) {
1178 const int first1 =
Next(start1);
1213 const std::vector<IntVar*>& vars,
1214 const std::vector<IntVar*>& secondary_vars,
int number_of_base_nodes,
1215 std::function<
int(int64_t)> start_empty_path_class,
1216 std::function<
const std::vector<int>&(
int,
int)> get_neighbors =
nullptr)
1217 :
PathOperator(vars, secondary_vars, number_of_base_nodes, false, false,
1218 std::move(start_empty_path_class),
1219 std::move(get_neighbors)),
1230 void OnNodeInitialization()
override;
1235void BaseInactiveNodeToPathOperator::OnNodeInitialization() {
1236 for (
int i = 0; i <
Size(); ++i) {
1242 inactive_node_ =
Size();
1246 while (inactive_node_ <
Size()) {
1269 const std::vector<IntVar*>& vars,
1270 const std::vector<IntVar*>& secondary_vars,
1271 std::function<
int(int64_t)> start_empty_path_class,
1272 std::function<
const std::vector<int>&(
int,
int)> get_neighbors =
nullptr)
1274 std::move(start_empty_path_class),
1275 std::move(get_neighbors)) {}
1279 std::string
DebugString()
const override {
return "MakeActiveOperator"; }
1300 const std::vector<IntVar*>& vars,
1301 const std::vector<IntVar*>& secondary_vars,
1302 std::function<
int(int64_t)> start_empty_path_class)
1304 std::move(start_empty_path_class)) {}
1307 const int64_t before_node_to_move =
BaseNode(1);
1308 const int64_t node =
Next(before_node_to_move);
1315 return "RelocateAndMakeActiveOpertor";
1328 const std::vector<IntVar*>& secondary_vars,
1329 std::function<
int(int64_t)> start_empty_path_class)
1331 std::move(start_empty_path_class)) {}
1336 return "MakeActiveAndRelocateOperator";
1341 const int64_t before_chain =
BaseNode(1);
1342 const int64_t chain_end =
Next(before_chain);
1343 const int64_t destination =
BaseNode(0);
1345 MoveChain(before_chain, chain_end, destination) &&
1360 const std::vector<IntVar*>& secondary_vars,
1361 std::function<
int(int64_t)> start_empty_path_class)
1363 std::move(start_empty_path_class), nullptr) {}
1370 std::string
DebugString()
const override {
return "MakeInactiveOperator"; }
1384 const std::vector<IntVar*>& vars,
1385 const std::vector<IntVar*>& secondary_vars,
1386 std::function<
int(int64_t)> start_empty_path_class)
1388 std::move(start_empty_path_class), nullptr) {}
1391 const int64_t destination =
BaseNode(1);
1392 const int64_t before_to_move =
BaseNode(0);
1393 const int64_t node_to_inactivate =
Next(destination);
1394 if (node_to_inactivate == before_to_move ||
IsPathEnd(node_to_inactivate) ||
1398 const int64_t node =
Next(before_to_move);
1403 return "RelocateAndMakeInactiveOperator";
1419 const std::vector<IntVar*>& secondary_vars,
1420 std::function<
int(int64_t)> start_empty_path_class)
1422 std::move(start_empty_path_class), nullptr) {}
1429 return "MakeChainInactiveOperator";
1456 const std::vector<IntVar*>& secondary_vars,
1457 std::function<
int(int64_t)> start_empty_path_class)
1459 std::move(start_empty_path_class)) {}
1463 std::string
DebugString()
const override {
return "SwapActiveOperator"; }
1488 const std::vector<IntVar*>& secondary_vars,
1489 std::function<
int(int64_t)> start_empty_path_class)
1491 std::move(start_empty_path_class)) {}
1496 return "ExtendedSwapActiveOperator";
1503 if (
Next(base0) == base1) {
1521 TSPOpt(
const std::vector<IntVar*>& vars,
1522 const std::vector<IntVar*>& secondary_vars,
1530 std::vector<std::vector<int64_t>> cost_;
1532 hamiltonian_path_solver_;
1534 const int chain_length_;
1538 const std::vector<IntVar*>& secondary_vars,
1540 :
PathOperator(vars, secondary_vars, 1, true, false, nullptr, nullptr),
1541 hamiltonian_path_solver_(cost_),
1542 evaluator_(
std::move(evaluator)),
1543 chain_length_(chain_length) {}
1546 std::vector<int64_t>
nodes;
1548 for (
int i = 0; i < chain_length_ + 1; ++i) {
1549 nodes.push_back(chain_end);
1553 chain_end =
Next(chain_end);
1555 if (
nodes.size() <= 3) {
1561 for (
int i = 0; i <
size; ++i) {
1562 cost_[i].resize(
size);
1564 for (
int j = 1; j <
size; ++j) {
1565 cost_[i][j] = evaluator_(
nodes[i],
nodes[j], chain_path);
1569 std::vector<PathNodeIndex> path;
1571 CHECK_EQ(
size + 1, path.size());
1572 for (
int i = 0; i <
size - 1; ++i) {
1589 TSPLns(
const std::vector<IntVar*>& vars,
1590 const std::vector<IntVar*>& secondary_vars,
1601 void OnNodeInitialization()
override {
1603 has_long_enough_paths_ =
Size() != 0;
1606 std::vector<std::vector<int64_t>> cost_;
1607 HamiltonianPathSolver<int64_t, std::vector<std::vector<int64_t>>>
1608 hamiltonian_path_solver_;
1610 const int tsp_size_;
1612 bool has_long_enough_paths_;
1616 const std::vector<IntVar*>& secondary_vars,
1618 :
PathOperator(vars, secondary_vars, 1, true, false, nullptr, nullptr),
1619 hamiltonian_path_solver_(cost_),
1620 evaluator_(
std::move(evaluator)),
1621 tsp_size_(tsp_size),
1623 has_long_enough_paths_(true) {
1624 CHECK_GE(tsp_size_, 0);
1625 cost_.resize(tsp_size_);
1626 for (
int i = 0; i < tsp_size_; ++i) {
1627 cost_[i].resize(tsp_size_);
1632 while (has_long_enough_paths_) {
1633 has_long_enough_paths_ =
false;
1643 const int64_t base_node =
BaseNode(0);
1644 std::vector<int64_t>
nodes;
1646 nodes.push_back(node);
1648 if (
nodes.size() <= tsp_size_) {
1651 has_long_enough_paths_ =
true;
1654 absl::flat_hash_set<int64_t> breaks_set;
1656 breaks_set.insert(base_node);
1657 CHECK(!
nodes.empty());
1658 while (breaks_set.size() < tsp_size_) {
1659 breaks_set.insert(
nodes[absl::Uniform<int>(rand_, 0,
nodes.size())]);
1661 CHECK_EQ(breaks_set.size(), tsp_size_);
1666 std::vector<int> breaks;
1667 std::vector<int64_t> meta_node_costs;
1670 int64_t node_path =
Path(node);
1673 if (breaks_set.contains(node)) {
1674 breaks.push_back(node);
1675 meta_node_costs.push_back(cost);
1678 cost =
CapAdd(cost, evaluator_(node,
next, node_path));
1682 meta_node_costs[0] += cost;
1683 CHECK_EQ(breaks.size(), tsp_size_);
1685 CHECK_EQ(meta_node_costs.size(), tsp_size_);
1686 for (
int i = 0; i < tsp_size_; ++i) {
1688 CapAdd(meta_node_costs[i],
1689 evaluator_(breaks[i],
Next(breaks[tsp_size_ - 1]), node_path));
1690 for (
int j = 1; j < tsp_size_; ++j) {
1692 CapAdd(meta_node_costs[i],
1693 evaluator_(breaks[i],
Next(breaks[j - 1]), node_path));
1699 std::vector<PathNodeIndex> path;
1701 bool nochange =
true;
1702 for (
int i = 0; i < path.size() - 1; ++i) {
1711 CHECK_EQ(0, path[path.size() - 1]);
1712 for (
int i = 0; i < tsp_size_ - 1; ++i) {
1713 SetNext(breaks[path[i]],
OldNext(breaks[path[i + 1] - 1]), node_path);
1715 SetNext(breaks[path[tsp_size_ - 1]],
OldNext(breaks[tsp_size_ - 1]),
1741 virtual std::string
DebugString()
const {
return "NearestNeighbors"; }
1744 void ComputeNearest(
int row);
1746 std::vector<std::vector<int>> neighbors_;
1755 : evaluator_(
std::move(evaluator)),
1756 path_operator_(path_operator),
1758 initialized_(false) {}
1762 if (!initialized_) {
1763 initialized_ =
true;
1765 neighbors_.push_back(std::vector<int>());
1772 return neighbors_[
index];
1775void NearestNeighbors::ComputeNearest(
int row) {
1777 const int path = path_operator_.
Path(
row);
1779 const int64_t var_min =
var->
Min();
1780 const int var_size =
var->Max() - var_min + 1;
1781 using ValuedIndex = std::pair<int64_t ,
int >;
1782 std::vector<ValuedIndex> neighbors(var_size);
1783 for (
int i = 0; i < var_size; ++i) {
1784 const int index = i + var_min;
1785 neighbors[i] = std::make_pair(evaluator_(
row,
index, path),
index);
1787 if (var_size > size_) {
1788 std::nth_element(neighbors.begin(), neighbors.begin() + size_ - 1,
1793 for (
int i = 0;
i < std::min(size_, var_size); ++
i) {
1794 neighbors_[
row].push_back(neighbors[i].second);
1796 std::sort(neighbors_[
row].begin(), neighbors_[
row].
end());
1802 const std::vector<IntVar*>& secondary_vars,
1810 void OnNodeInitialization()
override;
1812 static const int kNeighbors;
1814 bool InFromOut(int64_t in_i, int64_t in_j, int64_t* out, int64_t* gain);
1818 absl::flat_hash_set<int64_t> marked_;
1827 const std::vector<IntVar*>& secondary_vars,
1829 :
PathOperator(vars, secondary_vars, 1, true, false, nullptr, nullptr),
1830 evaluator_(evaluator),
1831 neighbors_(evaluator, *this, kNeighbors),
1836void LinKernighan::OnNodeInitialization() { neighbors_.
Initialize(); }
1841 int64_t path =
Path(node);
1842 int64_t base = node;
1847 marked_.insert(node);
1849 if (!InFromOut(node,
next, &out, &gain))
return false;
1850 marked_.insert(
next);
1851 marked_.insert(out);
1852 const int64_t node1 = out;
1854 const int64_t next1 =
Next(node1);
1856 if (!InFromOut(node1, next1, &out, &gain))
return false;
1857 marked_.insert(next1);
1858 marked_.insert(out);
1862 const int64_t next_out =
Next(out);
1863 const int64_t in_cost = evaluator_(node, next_out, path);
1864 const int64_t out_cost = evaluator_(out, next_out, path);
1865 if (
CapAdd(
CapSub(gain, in_cost), out_cost) > 0)
return true;
1872 while (InFromOut(node,
next, &out, &gain)) {
1873 marked_.insert(
next);
1874 marked_.insert(out);
1879 int64_t in_cost = evaluator_(base, chain_last, path);
1880 int64_t out_cost = evaluator_(chain_last, out, path);
1896const int LinKernighan::kNeighbors = 5 + 1;
1898bool LinKernighan::InFromOut(int64_t in_i, int64_t in_j, int64_t* out,
1900 const std::vector<int>& nexts = neighbors_.
Neighbors(in_j);
1901 int64_t best_gain = std::numeric_limits<int64_t>::min();
1902 int64_t path =
Path(in_i);
1903 int64_t out_cost = evaluator_(in_i, in_j, path);
1904 const int64_t current_gain =
CapAdd(*gain, out_cost);
1905 for (
int k = 0; k < nexts.size(); ++k) {
1906 const int64_t
next = nexts[k];
1908 int64_t in_cost = evaluator_(in_j,
next, path);
1909 int64_t new_gain =
CapSub(current_gain, in_cost);
1910 if (new_gain > 0 &&
next !=
Next(in_j) && marked_.count(in_j) == 0 &&
1911 marked_.count(
next) == 0) {
1912 if (best_gain < new_gain) {
1914 best_gain = new_gain;
1920 return (best_gain > std::numeric_limits<int64_t>::min());
1932 const std::vector<IntVar*>& secondary_vars,
int number_of_chunks,
1933 int chunk_size,
bool unactive_fragments)
1934 :
PathOperator(vars, secondary_vars, number_of_chunks, true, true,
1936 number_of_chunks_(number_of_chunks),
1937 chunk_size_(chunk_size),
1938 unactive_fragments_(unactive_fragments) {
1939 CHECK_GE(chunk_size_, 0);
1948 inline bool ChainsAreFullPaths()
const {
return chunk_size_ == 0; }
1949 void DeactivateChain(int64_t node);
1950 void DeactivateUnactives();
1952 const int number_of_chunks_;
1953 const int chunk_size_;
1954 const bool unactive_fragments_;
1958 if (ChainsAreFullPaths()) {
1962 for (
int i = 0; i < number_of_chunks_; ++i) {
1966 for (
int i = 0; i < number_of_chunks_; ++i) {
1969 DeactivateUnactives();
1973void PathLns::DeactivateChain(int64_t node) {
1974 for (
int i = 0, current = node;
1975 (ChainsAreFullPaths() || i < chunk_size_) && !
IsPathEnd(current);
1976 ++i, current =
Next(current)) {
1984void PathLns::DeactivateUnactives() {
1985 if (unactive_fragments_) {
1986 for (
int i = 0;
i <
Size(); ++
i) {
2002 : operator_(op), limit_(limit), next_neighborhood_calls_(0) {
2003 CHECK(op !=
nullptr);
2008 next_neighborhood_calls_ = 0;
2009 operator_->
Start(assignment);
2013 if (next_neighborhood_calls_ >= limit_) {
2016 ++next_neighborhood_calls_;
2022 std::string
DebugString()
const override {
return "NeighborhoodLimit"; }
2026 const int64_t limit_;
2027 int64_t next_neighborhood_calls_;
2040 CompoundOperator(std::vector<LocalSearchOperator*> operators,
2041 std::function<int64_t(
int,
int)> evaluator);
2042 ~CompoundOperator()
override {}
2043 void Reset()
override;
2044 void Start(
const Assignment* assignment)
override;
2045 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
2046 bool HasFragments()
const override {
return has_fragments_; }
2047 bool HoldsDelta()
const override {
return true; }
2049 std::string DebugString()
const override {
2050 return operators_.empty()
2052 : operators_[operator_indices_[index_]]->DebugString();
2054 const LocalSearchOperator* Self()
const override {
2055 return operators_.empty() ? this
2056 : operators_[operator_indices_[index_]]->Self();
2060 class OperatorComparator {
2062 OperatorComparator(std::function<int64_t(
int,
int)> evaluator,
2063 int active_operator)
2064 : evaluator_(
std::move(evaluator)), active_operator_(active_operator) {}
2065 bool operator()(
int lhs,
int rhs)
const {
2066 const int64_t lhs_value = Evaluate(lhs);
2067 const int64_t rhs_value = Evaluate(rhs);
2068 return lhs_value < rhs_value || (lhs_value == rhs_value && lhs < rhs);
2072 int64_t Evaluate(
int operator_index)
const {
2073 return evaluator_(active_operator_, operator_index);
2076 std::function<int64_t(
int,
int)> evaluator_;
2077 const int active_operator_;
2081 std::vector<LocalSearchOperator*> operators_;
2082 std::vector<int> operator_indices_;
2083 std::function<int64_t(
int,
int)> evaluator_;
2084 Bitset64<> started_;
2085 const Assignment* start_assignment_;
2086 bool has_fragments_;
2089CompoundOperator::CompoundOperator(std::vector<LocalSearchOperator*> operators,
2090 std::function<int64_t(
int,
int)> evaluator)
2092 operators_(
std::move(operators)),
2093 evaluator_(
std::move(evaluator)),
2094 started_(operators_.
size()),
2095 start_assignment_(nullptr),
2096 has_fragments_(
false) {
2097 operators_.erase(std::remove(operators_.begin(), operators_.end(),
nullptr),
2099 operator_indices_.resize(operators_.size());
2100 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
2101 for (LocalSearchOperator*
const op : operators_) {
2102 if (op->HasFragments()) {
2103 has_fragments_ =
true;
2109void CompoundOperator::Reset() {
2110 for (LocalSearchOperator*
const op : operators_) {
2115void CompoundOperator::Start(
const Assignment* assignment) {
2116 start_assignment_ = assignment;
2118 if (!operators_.empty()) {
2119 OperatorComparator comparator(evaluator_, operator_indices_[index_]);
2120 std::sort(operator_indices_.begin(), operator_indices_.end(), comparator);
2125bool CompoundOperator::MakeNextNeighbor(Assignment*
delta,
2126 Assignment* deltadelta) {
2127 if (!operators_.empty()) {
2131 const int64_t operator_index = operator_indices_[index_];
2132 if (!started_[operator_index]) {
2133 operators_[operator_index]->Start(start_assignment_);
2134 started_.
Set(operator_index);
2136 if (!operators_[operator_index]->HoldsDelta()) {
2139 if (operators_[operator_index]->MakeNextNeighbor(
delta, deltadelta)) {
2144 if (index_ == operators_.size()) {
2147 }
while (index_ != 0);
2152int64_t CompoundOperatorNoRestart(
int size,
int active_index,
2153 int operator_index) {
2154 return (operator_index < active_index) ?
size + operator_index - active_index
2155 : operator_index - active_index;
2158int64_t CompoundOperatorRestart(
int,
int) {
return 0; }
2162 const std::vector<LocalSearchOperator*>& ops) {
2163 return ConcatenateOperators(ops,
false);
2167 const std::vector<LocalSearchOperator*>& ops,
bool restart) {
2169 std::function<int64_t(
int,
int)> eval = CompoundOperatorRestart;
2170 return ConcatenateOperators(ops, eval);
2172 const int size = ops.size();
2173 return ConcatenateOperators(ops, [
size](
int i,
int j) {
2174 return CompoundOperatorNoRestart(
size, i, j);
2179 const std::vector<LocalSearchOperator*>& ops,
2180 std::function<int64_t(
int,
int)> evaluator) {
2181 return RevAlloc(
new CompoundOperator(ops, std::move(evaluator)));
2187 explicit RandomCompoundOperator(std::vector<LocalSearchOperator*> operators);
2188 RandomCompoundOperator(std::vector<LocalSearchOperator*> operators,
2190 ~RandomCompoundOperator()
override {}
2191 void Reset()
override;
2192 void Start(
const Assignment* assignment)
override;
2193 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
2194 bool HoldsDelta()
const override {
return true; }
2196 std::string DebugString()
const override {
return "RandomCompoundOperator"; }
2201 const std::vector<LocalSearchOperator*> operators_;
2202 bool has_fragments_;
2205void RandomCompoundOperator::Start(
const Assignment* assignment) {
2206 for (LocalSearchOperator*
const op : operators_) {
2207 op->Start(assignment);
2211RandomCompoundOperator::RandomCompoundOperator(
2212 std::vector<LocalSearchOperator*> operators)
2215RandomCompoundOperator::RandomCompoundOperator(
2216 std::vector<LocalSearchOperator*> operators, int32_t seed)
2217 : rand_(seed), operators_(
std::move(operators)), has_fragments_(
false) {
2218 for (LocalSearchOperator*
const op : operators_) {
2219 if (op->HasFragments()) {
2220 has_fragments_ =
true;
2226void RandomCompoundOperator::Reset() {
2227 for (LocalSearchOperator*
const op : operators_) {
2232bool RandomCompoundOperator::MakeNextNeighbor(Assignment*
delta,
2233 Assignment* deltadelta) {
2234 const int size = operators_.size();
2235 std::vector<int> indices(
size);
2236 std::iota(indices.begin(), indices.end(), 0);
2237 std::shuffle(indices.begin(), indices.end(), rand_);
2238 for (
int index : indices) {
2239 if (!operators_[
index]->HoldsDelta()) {
2242 if (operators_[
index]->MakeNextNeighbor(
delta, deltadelta)) {
2252 const std::vector<LocalSearchOperator*>& ops) {
2253 return RevAlloc(
new RandomCompoundOperator(ops));
2257 const std::vector<LocalSearchOperator*>& ops, int32_t seed) {
2258 return RevAlloc(
new RandomCompoundOperator(ops, seed));
2264 explicit MultiArmedBanditCompoundOperator(
2265 std::vector<LocalSearchOperator*> operators,
double memory_coefficient,
2266 double exploration_coefficient,
bool maximize);
2267 ~MultiArmedBanditCompoundOperator()
override {}
2268 void Reset()
override;
2269 void Start(
const Assignment* assignment)
override;
2270 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
2271 bool HoldsDelta()
const override {
return true; }
2273 std::string DebugString()
const override {
2274 return operators_.empty()
2276 : operators_[operator_indices_[index_]]->DebugString();
2278 const LocalSearchOperator* Self()
const override {
2279 return operators_.empty() ? this
2280 : operators_[operator_indices_[index_]]->Self();
2284 double Score(
int index);
2286 std::vector<LocalSearchOperator*> operators_;
2287 Bitset64<> started_;
2288 const Assignment* start_assignment_;
2289 bool has_fragments_;
2290 std::vector<int> operator_indices_;
2291 int64_t last_objective_;
2292 std::vector<double> avg_improvement_;
2294 std::vector<double> num_neighbors_per_operator_;
2295 const bool maximize_;
2300 const double memory_coefficient_;
2307 const double exploration_coefficient_;
2310MultiArmedBanditCompoundOperator::MultiArmedBanditCompoundOperator(
2311 std::vector<LocalSearchOperator*> operators,
double memory_coefficient,
2312 double exploration_coefficient,
bool maximize)
2314 operators_(
std::move(operators)),
2315 started_(operators_.
size()),
2316 start_assignment_(nullptr),
2317 has_fragments_(
false),
2318 last_objective_(
std::numeric_limits<int64_t>::
max()),
2320 maximize_(maximize),
2321 memory_coefficient_(memory_coefficient),
2322 exploration_coefficient_(exploration_coefficient) {
2323 DCHECK_GE(memory_coefficient_, 0);
2324 DCHECK_LE(memory_coefficient_, 1);
2325 DCHECK_GE(exploration_coefficient_, 0);
2326 operators_.erase(std::remove(operators_.begin(), operators_.end(),
nullptr),
2328 operator_indices_.resize(operators_.size());
2329 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
2330 num_neighbors_per_operator_.resize(operators_.size(), 0);
2331 avg_improvement_.resize(operators_.size(), 0);
2332 for (LocalSearchOperator*
const op : operators_) {
2333 if (op->HasFragments()) {
2334 has_fragments_ =
true;
2340void MultiArmedBanditCompoundOperator::Reset() {
2341 for (LocalSearchOperator*
const op : operators_) {
2346double MultiArmedBanditCompoundOperator::Score(
int index) {
2347 return avg_improvement_[
index] +
2348 exploration_coefficient_ *
2349 sqrt(2 * log(1 + num_neighbors_) /
2350 (1 + num_neighbors_per_operator_[
index]));
2353void MultiArmedBanditCompoundOperator::Start(
const Assignment* assignment) {
2354 start_assignment_ = assignment;
2356 if (operators_.empty())
return;
2358 const double objective = assignment->ObjectiveValue();
2360 if (objective == last_objective_)
return;
2362 if (last_objective_ == std::numeric_limits<int64_t>::max()) {
2363 last_objective_ = objective;
2367 const double improvement =
2368 maximize_ ? objective - last_objective_ : last_objective_ - objective;
2369 if (improvement < 0) {
2372 last_objective_ = objective;
2373 avg_improvement_[operator_indices_[index_]] +=
2374 memory_coefficient_ *
2375 (improvement - avg_improvement_[operator_indices_[index_]]);
2377 std::sort(operator_indices_.begin(), operator_indices_.end(),
2378 [
this](
int lhs,
int rhs) {
2379 const double lhs_score = Score(lhs);
2380 const double rhs_score = Score(rhs);
2381 return lhs_score > rhs_score ||
2382 (lhs_score == rhs_score && lhs < rhs);
2388bool MultiArmedBanditCompoundOperator::MakeNextNeighbor(
2389 Assignment*
delta, Assignment* deltadelta) {
2390 if (operators_.empty())
return false;
2392 const int operator_index = operator_indices_[index_];
2393 if (!started_[operator_index]) {
2394 operators_[operator_index]->Start(start_assignment_);
2395 started_.
Set(operator_index);
2397 if (!operators_[operator_index]->HoldsDelta()) {
2400 if (operators_[operator_index]->MakeNextNeighbor(
delta, deltadelta)) {
2402 ++num_neighbors_per_operator_[operator_index];
2407 if (index_ == operators_.size()) {
2410 }
while (index_ != 0);
2416 const std::vector<LocalSearchOperator*>& ops,
double memory_coefficient,
2417 double exploration_coefficient,
bool maximize) {
2418 return RevAlloc(
new MultiArmedBanditCompoundOperator(
2419 ops, memory_coefficient, exploration_coefficient, maximize));
2426 Solver* solver,
const std::vector<IntVar*>& vars,
2427 const std::vector<IntVar*>& secondary_vars,
2428 std::function<
int(int64_t)> start_empty_path_class) {
2430 new T(vars, secondary_vars, std::move(start_empty_path_class),
nullptr));
2435 Solver* solver,
const std::vector<IntVar*>& vars,
2436 const std::vector<IntVar*>& secondary_vars,
2437 std::function<
int(int64_t)> start_empty_path_class,
2438 std::function<
const std::vector<int>&(
int,
int)> get_neighbors) {
2439 return solver->
RevAlloc(
new T(vars, secondary_vars,
2440 std::move(start_empty_path_class),
2441 std::move(get_neighbors)));
2444#define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass) \
2446 LocalSearchOperator* MakeLocalSearchOperator<OperatorClass>( \
2447 Solver * solver, const std::vector<IntVar*>& vars, \
2448 const std::vector<IntVar*>& secondary_vars, \
2449 std::function<int(int64_t)> start_empty_path_class) { \
2450 return solver->RevAlloc(new OperatorClass( \
2451 vars, secondary_vars, std::move(start_empty_path_class))); \
2454#define MAKE_LOCAL_SEARCH_OPERATOR_WITH_NEIGHBORS(OperatorClass) \
2456 LocalSearchOperator* MakeLocalSearchOperatorWithNeighbors<OperatorClass>( \
2457 Solver * solver, const std::vector<IntVar*>& vars, \
2458 const std::vector<IntVar*>& secondary_vars, \
2459 std::function<int(int64_t)> start_empty_path_class, \
2460 std::function<const std::vector<int>&(int, int)> get_neighbors) { \
2461 return solver->RevAlloc(new OperatorClass( \
2462 vars, secondary_vars, std::move(start_empty_path_class), \
2463 std::move(get_neighbors))); \
2483#undef MAKE_LOCAL_SEARCH_OPERATOR
2490 std::function<
const std::vector<int>&(
int,
int)> get_neighbors) {
2491 return MakeOperator(vars, std::vector<IntVar*>(), op, get_neighbors);
2495 const std::vector<IntVar*>& vars,
2497 std::function<
const std::vector<int>&(
int,
int)> get_neighbors) {
2501 this, vars, secondary_vars,
nullptr, std::move(get_neighbors));
2504 std::vector<LocalSearchOperator*> operators;
2505 for (
int i = 1; i < 4; ++i) {
2506 operators.push_back(
2507 RevAlloc(
new Relocate(vars, secondary_vars,
2508 absl::StrCat(
"OrOpt<", i,
">"),
2513 return ConcatenateOperators(operators);
2517 this, vars, secondary_vars,
nullptr, std::move(get_neighbors));
2521 this, vars, secondary_vars,
nullptr, std::move(get_neighbors));
2525 this, vars, secondary_vars,
nullptr, std::move(get_neighbors));
2529 this, vars, secondary_vars,
nullptr);
2533 this, vars, secondary_vars,
nullptr);
2537 this, vars, secondary_vars,
nullptr);
2541 this, vars, secondary_vars,
nullptr);
2545 this, vars, secondary_vars,
nullptr);
2548 return RevAlloc(
new PathLns(vars, secondary_vars, 2, 3,
false));
2551 return RevAlloc(
new PathLns(vars, secondary_vars,
2557 return RevAlloc(
new PathLns(vars, secondary_vars, 1, 6,
true));
2560 if (!secondary_vars.empty()) {
2561 LOG(FATAL) <<
"Operator " << op
2562 <<
" does not support secondary variables";
2564 return RevAlloc(
new IncrementValue(vars));
2567 if (!secondary_vars.empty()) {
2568 LOG(FATAL) <<
"Operator " << op
2569 <<
" does not support secondary variables";
2571 return RevAlloc(
new DecrementValue(vars));
2574 if (!secondary_vars.empty()) {
2575 LOG(FATAL) <<
"Operator " << op
2576 <<
" does not support secondary variables";
2578 return RevAlloc(
new SimpleLns(vars, 1));
2581 LOG(FATAL) <<
"Unknown operator " << op;
2589 return MakeOperator(vars, std::vector<IntVar*>(), std::move(evaluator), op);
2593 const std::vector<IntVar*>& vars,
2594 const std::vector<IntVar*>& secondary_vars,
2599 std::vector<LocalSearchOperator*> operators;
2600 operators.push_back(RevAlloc(
2601 new LinKernighan(vars, secondary_vars, evaluator,
false)));
2602 operators.push_back(RevAlloc(
2603 new LinKernighan(vars, secondary_vars, evaluator,
true)));
2604 return ConcatenateOperators(operators);
2608 new TSPOpt(vars, secondary_vars, std::move(evaluator),
2609 absl::GetFlag(FLAGS_cp_local_search_tsp_opt_size)));
2613 new TSPLns(vars, secondary_vars, std::move(evaluator),
2614 absl::GetFlag(FLAGS_cp_local_search_tsp_lns_size)));
2617 LOG(FATAL) <<
"Unknown operator " << op;
2626 std::string DebugString()
const override {
return "AcceptFilter"; }
2627 bool Accept(
const Assignment*,
const Assignment*, int64_t, int64_t)
override {
2630 void Synchronize(
const Assignment*,
const Assignment*)
override {}
2635 return RevAlloc(
new AcceptFilter());
2642 std::string DebugString()
const override {
return "RejectFilter"; }
2643 bool Accept(
const Assignment*,
const Assignment*, int64_t, int64_t)
override {
2646 void Synchronize(
const Assignment*,
const Assignment*)
override {}
2651 return RevAlloc(
new RejectFilter());
2655 std::vector<int> path_end)
2656 : num_nodes_(num_nodes),
2657 num_paths_(path_start.
size()),
2658 num_nodes_threshold_(
std::
max(16, 4 * num_nodes_))
2660 DCHECK_EQ(path_start.size(), num_paths_);
2661 DCHECK_EQ(path_end.size(), num_paths_);
2662 for (
int p = 0; p < num_paths_; ++p) {
2663 path_start_end_.push_back({path_start[p], path_end[p]});
2666 committed_index_.assign(num_nodes_, -1);
2667 committed_paths_.assign(num_nodes_, -1);
2668 committed_nodes_.assign(2 * num_paths_, -1);
2669 chains_.assign(num_paths_ + 1, {-1, -1});
2670 paths_.assign(num_paths_, {-1, -1});
2671 for (
int path = 0; path < num_paths_; ++path) {
2672 const int index = 2 * path;
2673 const auto& [
start,
end] = path_start_end_[path];
2680 committed_paths_[
start] = path;
2681 committed_paths_[
end] = path;
2684 paths_[path] = {path, path + 1};
2686 chains_[num_paths_] = {0, 0};
2688 for (
int node = 0; node < num_nodes_; ++node) {
2689 if (committed_index_[node] != -1)
continue;
2690 committed_index_[node] = committed_nodes_.size();
2691 committed_nodes_.push_back(node);
2696 const PathBounds bounds = paths_[path];
2698 chains_.data() + bounds.end_index,
2699 committed_nodes_.data());
2703 const PathBounds bounds = paths_[path];
2705 chains_.data() + bounds.end_index,
2706 committed_nodes_.data());
2710 changed_paths_.push_back(path);
2711 const int path_begin_index = chains_.size();
2712 chains_.insert(chains_.end(), chains.begin(), chains.end());
2713 const int path_end_index = chains_.size();
2714 paths_[path] = {path_begin_index, path_end_index};
2715 chains_.emplace_back(0, 0);
2719 for (
const int loop : new_loops) {
2720 if (
Path(loop) == -1)
continue;
2721 changed_loops_.push_back(loop);
2727 if (committed_nodes_.size() < num_nodes_threshold_) {
2728 IncrementalCommit();
2735 is_invalid_ =
false;
2736 chains_.resize(num_paths_ + 1);
2737 for (
const int path : changed_paths_) {
2738 paths_[path] = {path, path + 1};
2740 changed_paths_.clear();
2741 changed_loops_.clear();
2744void PathState::CopyNewPathAtEndOfNodes(
int path) {
2746 const PathBounds path_bounds = paths_[path];
2747 for (
int i = path_bounds.begin_index; i < path_bounds.end_index; ++i) {
2748 const ChainBounds chain_bounds = chains_[i];
2749 committed_nodes_.insert(committed_nodes_.end(),
2750 committed_nodes_.data() + chain_bounds.begin_index,
2751 committed_nodes_.data() + chain_bounds.end_index);
2752 if (committed_paths_[committed_nodes_.back()] == path)
continue;
2753 for (
int i = chain_bounds.begin_index; i < chain_bounds.end_index; ++i) {
2754 const int node = committed_nodes_[i];
2755 committed_paths_[node] = path;
2762void PathState::IncrementalCommit() {
2763 const int new_nodes_begin = committed_nodes_.size();
2765 const int chain_begin = committed_nodes_.size();
2766 CopyNewPathAtEndOfNodes(path);
2767 const int chain_end = committed_nodes_.size();
2768 chains_[path] = {chain_begin, chain_end};
2771 const int new_nodes_end = committed_nodes_.size();
2772 for (
int i = new_nodes_begin;
i < new_nodes_end; ++
i) {
2773 const int node = committed_nodes_[
i];
2774 committed_index_[node] =
i;
2779 committed_paths_[loop] = -1;
2785void PathState::FullCommit() {
2788 const int old_num_nodes = committed_nodes_.size();
2789 for (
int path = 0; path < num_paths_; ++path) {
2790 const int new_path_begin = committed_nodes_.size() - old_num_nodes;
2791 CopyNewPathAtEndOfNodes(path);
2792 const int new_path_end = committed_nodes_.size() - old_num_nodes;
2793 chains_[path] = {new_path_begin, new_path_end};
2795 committed_nodes_.erase(committed_nodes_.begin(),
2796 committed_nodes_.begin() + old_num_nodes);
2799 constexpr int kUnindexed = -1;
2800 committed_index_.assign(num_nodes_, kUnindexed);
2802 for (
const int node : committed_nodes_) {
2803 committed_index_[node] =
index++;
2805 for (
int node = 0; node < num_nodes_; ++node) {
2806 if (committed_index_[node] != kUnindexed)
continue;
2807 committed_index_[node] =
index++;
2808 committed_nodes_.push_back(node);
2809 committed_paths_[node] = -1;
2817class PathStateFilter :
public LocalSearchFilter {
2819 std::string DebugString()
const override {
return "PathStateFilter"; }
2820 PathStateFilter(std::unique_ptr<PathState> path_state,
2821 const std::vector<IntVar*>& nexts);
2822 void Relax(
const Assignment*
delta,
const Assignment* deltadelta)
override;
2823 bool Accept(
const Assignment*,
const Assignment*, int64_t, int64_t)
override {
2826 void Synchronize(
const Assignment*,
const Assignment*)
override{};
2827 void Commit(
const Assignment* assignment,
const Assignment*
delta)
override;
2828 void Revert()
override;
2829 void Reset()
override;
2833 struct TailHeadIndices {
2840 bool operator<(
const IndexArc& other)
const {
return index < other.index; }
2847 void MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
2850 void MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
2852 const std::unique_ptr<PathState> path_state_;
2854 std::vector<int> variable_index_to_node_;
2857 std::vector<bool> node_is_assigned_;
2858 std::vector<int> loops_;
2861 std::vector<int> changed_paths_;
2862 std::vector<bool> path_has_changed_;
2863 std::vector<std::pair<int, int>> changed_arcs_;
2864 std::vector<int> changed_loops_;
2865 std::vector<TailHeadIndices> tail_head_indices_;
2866 std::vector<IndexArc> arcs_by_tail_index_;
2867 std::vector<IndexArc> arcs_by_head_index_;
2868 std::vector<int> next_arc_;
2869 std::vector<PathState::ChainBounds> path_chains_;
2872PathStateFilter::PathStateFilter(std::unique_ptr<PathState> path_state,
2873 const std::vector<IntVar*>& nexts)
2874 : path_state_(
std::move(path_state)) {
2876 int min_index = std::numeric_limits<int>::max();
2877 int max_index = std::numeric_limits<int>::min();
2878 for (
const IntVar*
next : nexts) {
2880 min_index = std::min<int>(min_index,
index);
2881 max_index = std::max<int>(max_index,
index);
2883 variable_index_to_node_.resize(max_index - min_index + 1, -1);
2884 index_offset_ = min_index;
2887 for (
int node = 0; node < nexts.size(); ++node) {
2888 const int index = nexts[node]->index() - index_offset_;
2889 variable_index_to_node_[
index] = node;
2891 path_has_changed_.assign(path_state_->NumPaths(),
false);
2894void PathStateFilter::Relax(
const Assignment*
delta,
const Assignment*) {
2895 path_state_->Revert();
2896 changed_arcs_.clear();
2897 for (
const IntVarElement& var_value :
delta->IntVarContainer().elements()) {
2898 if (var_value.Var() ==
nullptr)
continue;
2899 const int index = var_value.Var()->index() - index_offset_;
2900 if (
index < 0 || variable_index_to_node_.size() <=
index)
continue;
2901 const int node = variable_index_to_node_[
index];
2902 if (node == -1)
continue;
2903 if (var_value.Bound()) {
2904 changed_arcs_.emplace_back(node, var_value.Value());
2906 path_state_->Revert();
2907 path_state_->SetInvalid();
2914void PathStateFilter::Reset() {
2915 path_state_->Revert();
2918 const int num_nodes = path_state_->NumNodes();
2919 node_is_assigned_.assign(num_nodes,
false);
2921 const int num_paths = path_state_->NumPaths();
2922 for (
int path = 0; path < num_paths; ++path) {
2923 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
2924 path_state_->ChangePath(
2925 path, {{start_index, start_index + 1}, {end_index - 1, end_index}});
2926 node_is_assigned_[path_state_->Start(path)] =
true;
2927 node_is_assigned_[path_state_->End(path)] =
true;
2929 for (
int node = 0; node < num_nodes; ++node) {
2930 if (!node_is_assigned_[node]) loops_.push_back(node);
2932 path_state_->ChangeLoops(loops_);
2933 path_state_->Commit();
2939void PathStateFilter::Commit(
const Assignment* assignment,
2940 const Assignment*
delta) {
2941 path_state_->Revert();
2943 Relax(assignment,
nullptr);
2945 Relax(
delta,
nullptr);
2947 path_state_->Commit();
2950void PathStateFilter::Revert() { path_state_->Revert(); }
2952void PathStateFilter::CutChains() {
2956 for (
const int path : changed_paths_) path_has_changed_[path] =
false;
2957 changed_paths_.clear();
2958 tail_head_indices_.clear();
2959 changed_loops_.clear();
2960 int num_changed_arcs = 0;
2961 for (
const auto [node,
next] : changed_arcs_) {
2962 const int node_index = path_state_->CommittedIndex(node);
2963 const int next_index = path_state_->CommittedIndex(
next);
2964 const int node_path = path_state_->Path(node);
2966 (next_index != node_index + 1 || node_path == -1)) {
2967 tail_head_indices_.push_back({node_index, next_index});
2968 changed_arcs_[num_changed_arcs++] = {node,
next};
2969 if (node_path != -1 && !path_has_changed_[node_path]) {
2970 path_has_changed_[node_path] =
true;
2971 changed_paths_.push_back(node_path);
2973 }
else if (node ==
next && node_path != -1) {
2974 changed_loops_.push_back(node);
2977 changed_arcs_.resize(num_changed_arcs);
2979 path_state_->ChangeLoops(changed_loops_);
2980 if (tail_head_indices_.size() + changed_paths_.size() <= 8) {
2981 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
2983 MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
2987void PathStateFilter::
2988 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm() {
2989 int num_visited_changed_arcs = 0;
2990 const int num_changed_arcs = tail_head_indices_.size();
2992 for (
const int path : changed_paths_) {
2993 path_chains_.clear();
2994 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
2995 int current_index = start_index;
2999 int selected_arc = -1;
3000 int selected_tail_index = std::numeric_limits<int>::max();
3001 for (
int i = num_visited_changed_arcs;
i < num_changed_arcs; ++
i) {
3002 const int tail_index = tail_head_indices_[
i].tail_index;
3013 if (start_index <= current_index && current_index < end_index &&
3014 end_index <= selected_tail_index) {
3015 path_chains_.emplace_back(current_index, end_index);
3018 path_chains_.emplace_back(current_index, selected_tail_index + 1);
3019 current_index = tail_head_indices_[selected_arc].head_index;
3020 std::swap(tail_head_indices_[num_visited_changed_arcs],
3021 tail_head_indices_[selected_arc]);
3022 ++num_visited_changed_arcs;
3025 path_state_->ChangePath(path, path_chains_);
3029void PathStateFilter::MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm() {
3045 for (
const int path : changed_paths_) {
3046 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
3047 tail_head_indices_.push_back({end_index - 1, start_index});
3052 const int num_arc_indices = tail_head_indices_.size();
3053 arcs_by_tail_index_.resize(num_arc_indices);
3054 arcs_by_head_index_.resize(num_arc_indices);
3055 for (
int i = 0;
i < num_arc_indices; ++
i) {
3056 arcs_by_tail_index_[
i] = {tail_head_indices_[
i].tail_index,
i};
3057 arcs_by_head_index_[
i] = {tail_head_indices_[
i].head_index,
i};
3059 std::sort(arcs_by_tail_index_.begin(), arcs_by_tail_index_.end());
3060 std::sort(arcs_by_head_index_.begin(), arcs_by_head_index_.end());
3062 next_arc_.resize(num_arc_indices);
3063 for (
int i = 0;
i < num_arc_indices; ++
i) {
3064 next_arc_[arcs_by_head_index_[
i].arc] = arcs_by_tail_index_[
i].arc;
3070 const int first_fake_arc = num_arc_indices - changed_paths_.size();
3071 for (
int fake_arc = first_fake_arc; fake_arc < num_arc_indices; ++fake_arc) {
3072 path_chains_.clear();
3073 int32_t
arc = fake_arc;
3075 const int chain_begin = tail_head_indices_[
arc].head_index;
3077 const int chain_end = tail_head_indices_[
arc].tail_index + 1;
3078 path_chains_.emplace_back(chain_begin, chain_end);
3079 }
while (
arc != fake_arc);
3080 const int path = changed_paths_[fake_arc - first_fake_arc];
3081 path_state_->ChangePath(path, path_chains_);
3088 std::unique_ptr<PathState> path_state,
3089 const std::vector<IntVar*>& nexts) {
3090 PathStateFilter* filter =
new PathStateFilter(std::move(path_state), nexts);
3095using EInterval = DimensionChecker::ExtendedInterval;
3097constexpr int64_t
kint64min = std::numeric_limits<int64_t>::min();
3098constexpr int64_t
kint64max = std::numeric_limits<int64_t>::max();
3100EInterval operator&(
const EInterval& i1,
const EInterval& i2) {
3101 return {std::max(i1.num_negative_infinity == 0 ? i1.min :
kint64min,
3102 i2.num_negative_infinity == 0 ? i2.min :
kint64min),
3103 std::min(i1.num_positive_infinity == 0 ? i1.max :
kint64max,
3104 i2.num_positive_infinity == 0 ? i2.max :
kint64max),
3105 std::min(i1.num_negative_infinity, i2.num_negative_infinity),
3106 std::min(i1.num_positive_infinity, i2.num_positive_infinity)};
3109EInterval operator&=(EInterval& i1,
const EInterval& i2) {
3114bool IsEmpty(
const EInterval&
interval) {
3115 const int64_t minimum_value =
3117 const int64_t maximum_value =
3119 return minimum_value > maximum_value;
3122EInterval
operator+(
const EInterval& i1,
const EInterval& i2) {
3123 return {
CapAdd(i1.min, i2.min),
CapAdd(i1.max, i2.max),
3124 i1.num_negative_infinity + i2.num_negative_infinity,
3125 i1.num_positive_infinity + i2.num_positive_infinity};
3128EInterval& operator+=(EInterval& i1,
const EInterval& i2) {
3133EInterval
operator-(
const EInterval& i1,
const EInterval& i2) {
3134 return {
CapSub(i1.min, i2.max),
CapSub(i1.max, i2.min),
3135 i1.num_negative_infinity + i2.num_positive_infinity,
3136 i1.num_positive_infinity + i2.num_negative_infinity};
3141EInterval Delta(
const EInterval& from,
const EInterval&
to) {
3143 to.num_negative_infinity - from.num_negative_infinity,
3144 to.num_positive_infinity - from.num_positive_infinity};
3147EInterval ToExtendedInterval(DimensionChecker::Interval
interval) {
3150 return {is_neg_infinity ? 0 :
interval.min,
3151 is_pos_infinity ? 0 :
interval.max, is_neg_infinity ? 1 : 0,
3152 is_pos_infinity ? 1 : 0};
3155std::vector<EInterval> ToExtendedIntervals(
3156 absl::Span<const DimensionChecker::Interval> intervals) {
3157 std::vector<EInterval> extended_intervals;
3158 extended_intervals.reserve(intervals.size());
3159 for (
const auto&
interval : intervals) {
3160 extended_intervals.push_back(ToExtendedInterval(
interval));
3162 return extended_intervals;
3166DimensionChecker::DimensionChecker(
3167 const PathState* path_state, std::vector<Interval> path_capacity,
3168 std::vector<int> path_class,
3169 std::vector<std::function<
Interval(int64_t, int64_t)>>
3170 demand_per_path_class,
3171 std::vector<Interval> node_capacity,
int min_range_size_for_riq)
3172 : path_state_(path_state),
3173 path_capacity_(ToExtendedIntervals(path_capacity)),
3174 path_class_(
std::move(path_class)),
3175 demand_per_path_class_(
std::move(demand_per_path_class)),
3176 node_capacity_(ToExtendedIntervals(node_capacity)),
3177 index_(path_state_->NumNodes(), 0),
3178 maximum_riq_layer_size_(
std::
max(
3179 16, 4 * path_state_->NumNodes())),
3180 min_range_size_for_riq_(min_range_size_for_riq) {
3181 const int num_nodes = path_state_->
NumNodes();
3182 cached_demand_.resize(num_nodes);
3183 const int num_paths = path_state_->
NumPaths();
3184 DCHECK_EQ(num_paths, path_capacity_.size());
3185 DCHECK_EQ(num_paths, path_class_.size());
3187 riq_.resize(maximum_riq_exponent + 1);
3192 if (path_state_->
IsInvalid())
return true;
3194 const EInterval path_capacity = path_capacity_[path];
3195 const int path_class = path_class_[path];
3198 int prev_node = path_state_->
Start(path);
3199 EInterval cumul = node_capacity_[prev_node] & path_capacity;
3200 if (IsEmpty(cumul))
return false;
3202 for (
const auto chain : path_state_->
Chains(path)) {
3203 const int first_node = chain.First();
3204 const int last_node = chain.Last();
3206 if (prev_node != first_node) {
3209 const EInterval
demand = ToExtendedInterval(
3210 demand_per_path_class_[path_class](prev_node, first_node));
3212 cumul &= path_capacity;
3213 cumul &= node_capacity_[first_node];
3214 if (IsEmpty(cumul))
return false;
3215 prev_node = first_node;
3219 const int first_index = index_[first_node];
3220 const int last_index = index_[last_node];
3221 const int chain_path = path_state_->
Path(first_node);
3222 const int chain_path_class =
3223 chain_path == -1 ? -1 : path_class_[chain_path];
3227 const bool chain_is_cached = chain_path_class == path_class;
3228 if (last_index - first_index > min_range_size_for_riq_ &&
3230 UpdateCumulUsingChainRIQ(first_index, last_index, path_capacity, cumul);
3231 if (IsEmpty(cumul))
return false;
3232 prev_node = chain.Last();
3234 for (
const int node : chain.WithoutFirstNode()) {
3237 ? cached_demand_[prev_node]
3238 : ToExtendedInterval(
3239 demand_per_path_class_[path_class](prev_node, node));
3241 cumul &= node_capacity_[node];
3242 cumul &= path_capacity;
3243 if (IsEmpty(cumul))
return false;
3253 const int current_layer_size = riq_[0].size();
3256 for (
const auto chain : path_state_->
Chains(path)) {
3257 change_size += chain.NumNodes();
3260 if (current_layer_size + change_size <= maximum_riq_layer_size_) {
3261 IncrementalCommit();
3267void DimensionChecker::IncrementalCommit() {
3269 const int begin_index = riq_[0].size();
3270 AppendPathDemandsToSums(path);
3271 UpdateRIQStructure(begin_index, riq_[0].
size());
3275void DimensionChecker::FullCommit() {
3277 for (
auto& layer : riq_) layer.clear();
3279 const int num_paths = path_state_->
NumPaths();
3280 for (
int path = 0; path < num_paths; ++path) {
3281 const int begin_index = riq_[0].size();
3282 AppendPathDemandsToSums(path);
3283 UpdateRIQStructure(begin_index, riq_[0].
size());
3287void DimensionChecker::AppendPathDemandsToSums(
int path) {
3290 const int path_class = path_class_[path];
3291 EInterval demand_sum = {0, 0, 0, 0};
3292 int prev = path_state_->
Start(path);
3293 int index = riq_[0].size();
3294 for (
const int node : path_state_->
Nodes(path)) {
3297 prev == node ? EInterval{0, 0, 0, 0}
3298 : ToExtendedInterval(
3299 demand_per_path_class_[path_class](prev, node));
3301 cached_demand_[prev] =
demand;
3304 index_[node] =
index++;
3305 riq_[0].push_back({.cumuls_to_fst = node_capacity_[node],
3306 .tightest_tsum = demand_sum,
3307 .cumuls_to_lst = node_capacity_[node],
3308 .tsum_at_fst = demand_sum,
3309 .tsum_at_lst = demand_sum});
3311 cached_demand_[path_state_->
End(path)] = {0, 0, 0, 0};
3314void DimensionChecker::UpdateRIQStructure(
int begin_index,
int end_index) {
3317 const int max_layer =
3319 for (
int layer = 1, half_window = 1; layer <= max_layer;
3320 ++layer, half_window *= 2) {
3321 riq_[layer].resize(end_index);
3322 for (
int i = begin_index + 2 * half_window - 1;
i < end_index; ++
i) {
3328 const RIQNode& fw = riq_[layer - 1][
i - half_window];
3329 const RIQNode& lw = riq_[layer - 1][
i];
3330 const EInterval lst_to_lst = Delta(fw.tsum_at_lst, lw.tsum_at_lst);
3331 const EInterval fst_to_fst = Delta(fw.tsum_at_fst, lw.tsum_at_fst);
3334 .cumuls_to_fst = fw.cumuls_to_fst & lw.cumuls_to_fst - fst_to_fst,
3335 .tightest_tsum = fw.tightest_tsum & lw.tightest_tsum,
3336 .cumuls_to_lst = fw.cumuls_to_lst + lst_to_lst & lw.cumuls_to_lst,
3337 .tsum_at_fst = fw.tsum_at_fst,
3338 .tsum_at_lst = lw.tsum_at_lst};
3347void DimensionChecker::UpdateCumulUsingChainRIQ(
3348 int first_index,
int last_index,
const ExtendedInterval& path_capacity,
3349 ExtendedInterval& cumul)
const {
3350 DCHECK_LE(0, first_index);
3351 DCHECK_LT(first_index, last_index);
3352 DCHECK_LT(last_index, riq_[0].
size());
3354 const int window = 1 << layer;
3355 const RIQNode& fw = riq_[layer][first_index + window - 1];
3356 const RIQNode& lw = riq_[layer][last_index];
3359 cumul &= fw.cumuls_to_fst;
3360 cumul &= lw.cumuls_to_fst - Delta(fw.tsum_at_fst, lw.tsum_at_fst);
3361 cumul &= path_capacity -
3362 Delta(fw.tsum_at_fst, fw.tightest_tsum & lw.tightest_tsum);
3365 if (IsEmpty(cumul))
return;
3368 cumul += Delta(fw.tsum_at_fst, lw.tsum_at_lst);
3371 cumul &= fw.cumuls_to_lst + Delta(fw.tsum_at_lst, lw.tsum_at_lst);
3372 cumul &= lw.cumuls_to_lst;
3377class DimensionFilter :
public LocalSearchFilter {
3379 std::string DebugString()
const override {
return name_; }
3380 DimensionFilter(std::unique_ptr<DimensionChecker> checker,
3381 absl::string_view dimension_name)
3382 : checker_(
std::move(checker)),
3383 name_(
absl::StrCat(
"DimensionFilter(", dimension_name,
")")) {}
3385 bool Accept(
const Assignment*,
const Assignment*, int64_t, int64_t)
override {
3386 return checker_->Check();
3389 void Synchronize(
const Assignment*,
const Assignment*)
override {
3394 std::unique_ptr<DimensionChecker> checker_;
3395 const std::string name_;
3401 Solver* solver, std::unique_ptr<DimensionChecker> checker,
3402 const std::string& dimension_name) {
3403 DimensionFilter* filter =
3404 new DimensionFilter(std::move(checker), dimension_name);
3412class VariableDomainFilter :
public LocalSearchFilter {
3414 VariableDomainFilter() {}
3415 ~VariableDomainFilter()
override {}
3416 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3417 int64_t objective_min, int64_t objective_max)
override;
3418 void Synchronize(
const Assignment*,
const Assignment*)
override {}
3420 std::string DebugString()
const override {
return "VariableDomainFilter"; }
3423bool VariableDomainFilter::Accept(
const Assignment*
delta,
const Assignment*,
3425 const Assignment::IntContainer& container =
delta->IntVarContainer();
3426 const int size = container.Size();
3427 for (
int i = 0;
i <
size; ++
i) {
3428 const IntVarElement& element = container.Element(i);
3429 if (element.Activated() && !element.Var()->Contains(element.Value())) {
3438 return RevAlloc(
new VariableDomainFilter());
3443const int IntVarLocalSearchFilter::kUnassigned = -1;
3446 const std::vector<IntVar*>& vars) {
3451 if (!vars.empty()) {
3452 for (
int i = 0; i < vars.size(); ++i) {
3453 const int index = vars[i]->index();
3454 if (
index >= var_index_to_index_.size()) {
3457 var_index_to_index_[
index] = i +
vars_.size();
3459 vars_.insert(
vars_.end(), vars.begin(), vars.end());
3460 values_.resize(
vars_.size(), 0);
3461 var_synced_.resize(
vars_.size(),
false);
3470 var_synced_.assign(var_synced_.size(),
false);
3471 SynchronizeOnAssignment(assignment);
3473 SynchronizeOnAssignment(
delta);
3475 OnSynchronize(
delta);
3482 for (
int i = 0; i <
size; ++i) {
3485 if (
var !=
nullptr) {
3487 values_[i] = element.
Value();
3488 var_synced_[i] =
true;
3490 const int64_t kUnallocated = -1;
3491 int64_t
index = kUnallocated;
3494 var_synced_[
index] =
true;
3511template <
typename Filter>
3514 SumObjectiveFilter(
const std::vector<IntVar*>& vars, Filter filter)
3523 ~SumObjectiveFilter()
override {}
3524 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3525 int64_t objective_min, int64_t objective_max)
override {
3526 if (
delta ==
nullptr)
return false;
3527 if (deltadelta->Empty()) {
3547 virtual int64_t CostOfSynchronizedVariable(int64_t
index) = 0;
3549 virtual int64_t CostOfChanges(
const Assignment* changes,
3550 bool incremental) = 0;
3551 bool IsIncremental()
const override {
return true; }
3553 std::string DebugString()
const override {
return "SumObjectiveFilter"; }
3555 int64_t GetSynchronizedObjectiveValue()
const override {
3558 int64_t GetAcceptedObjectiveValue()
const override {
return delta_sum_; }
3570 void OnSynchronize(
const Assignment*)
override {
3573 const int64_t cost = CostOfSynchronizedVariable(i);
3583template <
typename Filter>
3584class BinaryObjectiveFilter :
public SumObjectiveFilter<Filter> {
3586 BinaryObjectiveFilter(
const std::vector<IntVar*>& vars,
3587 Solver::IndexEvaluator2 value_evaluator, Filter filter)
3588 : SumObjectiveFilter<Filter>(vars,
std::move(filter)),
3589 value_evaluator_(
std::move(value_evaluator)) {}
3590 ~BinaryObjectiveFilter()
override {}
3591 int64_t CostOfSynchronizedVariable(int64_t
index)
override {
3592 return IntVarLocalSearchFilter::IsVarSynced(
index)
3593 ? value_evaluator_(
index, IntVarLocalSearchFilter::Value(
index))
3596 int64_t CostOfChanges(
const Assignment* changes,
bool incremental)
override {
3597 int64_t total_cost = 0;
3598 const Assignment::IntContainer& container = changes->IntVarContainer();
3599 for (
const IntVarElement& new_element : container.elements()) {
3600 IntVar*
const var = new_element.Var();
3602 if (this->FindIndex(
var, &
index)) {
3604 int64_t new_cost = 0LL;
3605 if (new_element.Activated()) {
3606 new_cost = value_evaluator_(
index, new_element.Value());
3607 }
else if (
var->Bound()) {
3608 new_cost = value_evaluator_(
index,
var->Min());
3610 total_cost =
CapAdd(total_cost, new_cost);
3620 Solver::IndexEvaluator2 value_evaluator_;
3623template <
typename Filter>
3624class TernaryObjectiveFilter :
public SumObjectiveFilter<Filter> {
3626 TernaryObjectiveFilter(
const std::vector<IntVar*>& vars,
3627 const std::vector<IntVar*>& secondary_vars,
3628 Solver::IndexEvaluator3 value_evaluator, Filter filter)
3629 : SumObjectiveFilter<Filter>(vars,
std::move(filter)),
3630 secondary_vars_offset_(vars.
size()),
3631 secondary_values_(vars.
size(), -1),
3632 value_evaluator_(
std::move(value_evaluator)) {
3633 IntVarLocalSearchFilter::AddVars(secondary_vars);
3634 CHECK_GE(IntVarLocalSearchFilter::Size(), 0);
3636 ~TernaryObjectiveFilter()
override {}
3637 int64_t CostOfSynchronizedVariable(int64_t
index)
override {
3638 DCHECK_LT(
index, secondary_vars_offset_);
3639 return IntVarLocalSearchFilter::IsVarSynced(
index)
3640 ? value_evaluator_(
index, IntVarLocalSearchFilter::Value(
index),
3641 IntVarLocalSearchFilter::Value(
3642 index + secondary_vars_offset_))
3645 int64_t CostOfChanges(
const Assignment* changes,
bool incremental)
override {
3646 int64_t total_cost = 0;
3647 const Assignment::IntContainer& container = changes->IntVarContainer();
3648 for (
const IntVarElement& new_element : container.elements()) {
3649 IntVar*
const var = new_element.Var();
3651 if (this->FindIndex(
var, &
index) &&
index < secondary_vars_offset_) {
3652 secondary_values_[
index] = -1;
3658 const int max_secondary_index = 2 * secondary_vars_offset_;
3659 for (
const IntVarElement& new_element : container.elements()) {
3660 IntVar*
const var = new_element.Var();
3662 if (new_element.Activated() && this->FindIndex(
var, &
index) &&
3663 index >= secondary_vars_offset_ &&
3665 index < max_secondary_index) {
3666 secondary_values_[
index - secondary_vars_offset_] = new_element.Value();
3669 for (
const IntVarElement& new_element : container.elements()) {
3670 IntVar*
const var = new_element.Var();
3672 if (this->FindIndex(
var, &
index) &&
index < secondary_vars_offset_) {
3674 int64_t new_cost = 0LL;
3675 if (new_element.Activated()) {
3676 new_cost = value_evaluator_(
index, new_element.Value(),
3677 secondary_values_[
index]);
3678 }
else if (
var->Bound() &&
3679 IntVarLocalSearchFilter::Var(
index + secondary_vars_offset_)
3681 new_cost = value_evaluator_(
3683 IntVarLocalSearchFilter::Var(
index + secondary_vars_offset_)
3686 total_cost =
CapAdd(total_cost, new_cost);
3696 int secondary_vars_offset_;
3697 std::vector<int64_t> secondary_values_;
3698 Solver::IndexEvaluator3 value_evaluator_;
3705 switch (filter_enum) {
3707 auto filter = [](int64_t
value, int64_t, int64_t max_value) {
3708 return value <= max_value;
3710 return RevAlloc(
new BinaryObjectiveFilter<
decltype(filter)>(
3711 vars, std::move(values), std::move(filter)));
3714 auto filter = [](int64_t
value, int64_t min_value, int64_t) {
3715 return value >= min_value;
3717 return RevAlloc(
new BinaryObjectiveFilter<
decltype(filter)>(
3718 vars, std::move(values), std::move(filter)));
3721 auto filter = [](int64_t
value, int64_t min_value, int64_t max_value) {
3722 return min_value <=
value &&
value <= max_value;
3724 return RevAlloc(
new BinaryObjectiveFilter<
decltype(filter)>(
3725 vars, std::move(values), std::move(filter)));
3728 LOG(ERROR) <<
"Unknown local search filter enum value";
3735 const std::vector<IntVar*>& vars,
3738 switch (filter_enum) {
3740 auto filter = [](int64_t
value, int64_t, int64_t max_value) {
3741 return value <= max_value;
3743 return RevAlloc(
new TernaryObjectiveFilter<
decltype(filter)>(
3744 vars, secondary_vars, std::move(values), std::move(filter)));
3747 auto filter = [](int64_t
value, int64_t min_value, int64_t) {
3748 return value >= min_value;
3750 return RevAlloc(
new TernaryObjectiveFilter<
decltype(filter)>(
3751 vars, secondary_vars, std::move(values), std::move(filter)));
3754 auto filter = [](int64_t
value, int64_t min_value, int64_t max_value) {
3755 return min_value <=
value &&
value <= max_value;
3757 return RevAlloc(
new TernaryObjectiveFilter<
decltype(filter)>(
3758 vars, secondary_vars, std::move(values), std::move(filter)));
3761 LOG(ERROR) <<
"Unknown local search filter enum value";
3768 DCHECK_GE(num_nodes, num_nodes_);
3769 DCHECK(!graph_was_built_);
3770 graph_was_built_ =
true;
3771 std::sort(arcs_.begin(), arcs_.end());
3772 arcs_of_node_.clear();
3774 for (
int a = 0;
a < arcs_.size(); ++
a) {
3776 if (
tail != prev_tail) {
3778 arcs_of_node_.resize(
tail.value() + 1,
a);
3781 num_nodes_ = std::max(num_nodes_, num_nodes);
3782 arcs_of_node_.resize(num_nodes_ + 1, arcs_.size());
3783 DCHECK(!HasDirectedCycle()) <<
"Graph has a directed cycle";
3786bool SubDagComputer::HasDirectedCycle()
const {
3787 DCHECK(graph_was_built_);
3796 std::vector<DFSEvent> event_stack;
3798 for (
NodeId node(0); node.value() < num_nodes_; ++node) {
3799 if (node_was_visited[node])
continue;
3800 event_stack.push_back({node,
true});
3801 while (!event_stack.empty()) {
3802 const auto [node, to_open] = event_stack.back();
3803 event_stack.pop_back();
3804 if (node_was_visited[node])
continue;
3806 if (node_is_open[node])
return true;
3807 node_is_open[node] =
true;
3808 event_stack.push_back({node,
false});
3809 for (
int a = arcs_of_node_[node];
3810 a < arcs_of_node_[
NodeId(node.value() + 1)]; ++
a) {
3812 if (node_was_visited[
head])
continue;
3813 event_stack.push_back({
head,
true});
3816 node_was_visited[node] =
true;
3817 node_is_open[node] =
false;
3824const std::vector<SubDagComputer::ArcId>&
3826 DCHECK_LT(node.value(), num_nodes_);
3827 DCHECK(graph_was_built_);
3828 if (indegree_of_node_.size() < num_nodes_) {
3829 indegree_of_node_.resize(num_nodes_, 0);
3832 nodes_to_visit_.clear();
3833 nodes_to_visit_.push_back(node);
3834 while (!nodes_to_visit_.empty()) {
3835 const NodeId node = nodes_to_visit_.back();
3836 nodes_to_visit_.pop_back();
3838 for (
int a = arcs_of_node_[node];
a < arcs_of_node_[
next]; ++
a) {
3840 if (indegree_of_node_[
head] == 0) {
3841 nodes_to_visit_.push_back(
head);
3843 ++indegree_of_node_[
head];
3847 sorted_arcs_.clear();
3848 nodes_to_visit_.push_back(node);
3849 while (!nodes_to_visit_.empty()) {
3850 const NodeId node = nodes_to_visit_.back();
3851 nodes_to_visit_.pop_back();
3853 for (
int a = arcs_of_node_[node];
a < arcs_of_node_[
next]; ++
a) {
3855 --indegree_of_node_[
head];
3856 if (indegree_of_node_[
head] == 0) {
3857 nodes_to_visit_.push_back(
head);
3859 sorted_arcs_.push_back(arcs_[
a].arc_id);
3863 DCHECK(absl::c_all_of(indegree_of_node_, [](
int x) {
return x == 0; }));
3864 return sorted_arcs_;
3869 int64_t relaxed_max) {
3870 DCHECK(state_domains_are_all_nonempty_);
3871 DCHECK_LE(relaxed_min, relaxed_max);
3872 relaxed_domains_.push_back({relaxed_min, relaxed_max});
3873 current_domains_.push_back({relaxed_min, relaxed_max});
3874 domain_is_trailed_.push_back(
false);
3880 return {
this, domain_id};
3884 DCHECK(state_domains_are_all_nonempty_);
3885 if (!state_has_relaxed_domains_) {
3886 trailed_num_committed_empty_domains_ = num_committed_empty_domains_;
3888 state_has_relaxed_domains_ =
true;
3889 if (!domain_is_trailed_[domain_id]) {
3890 domain_is_trailed_[domain_id] =
true;
3891 trailed_domains_.push_back({current_domains_[domain_id], domain_id});
3892 if (IntersectionIsEmpty(relaxed_domains_[domain_id],
3893 current_domains_[domain_id])) {
3894 DCHECK_GT(num_committed_empty_domains_, 0);
3895 --num_committed_empty_domains_;
3897 current_domains_[domain_id] = relaxed_domains_[domain_id];
3902 DCHECK(state_domains_are_all_nonempty_);
3903 return current_domains_[domain_id].min;
3907 DCHECK(state_domains_are_all_nonempty_);
3908 return current_domains_[domain_id].max;
3912 int64_t min_value) {
3913 DCHECK(state_domains_are_all_nonempty_);
3914 DCHECK(domain_is_trailed_[domain_id]);
3915 VariableDomain& domain = current_domains_[domain_id];
3916 if (domain.max < min_value) {
3917 state_domains_are_all_nonempty_ =
false;
3919 domain.min = std::max(domain.min, min_value);
3920 return state_domains_are_all_nonempty_;
3924 int64_t max_value) {
3925 DCHECK(state_domains_are_all_nonempty_);
3926 DCHECK(domain_is_trailed_[domain_id]);
3927 VariableDomain& domain = current_domains_[domain_id];
3928 if (domain.min > max_value) {
3929 state_domains_are_all_nonempty_ =
false;
3931 domain.max = std::min(domain.max, max_value);
3932 return state_domains_are_all_nonempty_;
3936 int64_t
min, int64_t
max) {
3937 DCHECK(state_domains_are_all_nonempty_);
3938 DCHECK(!domain_is_trailed_[domain_id]);
3939 const bool domain_was_empty = IntersectionIsEmpty(
3940 relaxed_domains_[domain_id], current_domains_[domain_id]);
3941 relaxed_domains_[domain_id] = {
min,
max};
3942 const bool domain_is_empty =
3943 IntersectionIsEmpty({
min,
max}, current_domains_[domain_id]);
3945 if (!domain_was_empty && domain_is_empty) {
3946 num_committed_empty_domains_++;
3947 }
else if (domain_was_empty && !domain_is_empty) {
3948 num_committed_empty_domains_--;
3956 DCHECK(StateIsFeasible());
3958 state_has_relaxed_domains_ =
false;
3959 trailed_domains_.clear();
3960 domain_is_trailed_.assign(domain_is_trailed_.size(),
false);
3962 for (Constraint* constraint : trailed_constraints_) {
3963 constraint->Commit();
3965 trailed_constraints_.clear();
3970 for (
const auto& [domain,
id] : trailed_domains_) {
3971 DCHECK(domain_is_trailed_[
id]);
3972 current_domains_[id] = domain;
3974 trailed_domains_.clear();
3975 state_has_relaxed_domains_ =
false;
3976 domain_is_trailed_.assign(domain_is_trailed_.size(),
false);
3977 state_domains_are_all_nonempty_ =
true;
3978 num_committed_empty_domains_ = trailed_num_committed_empty_domains_;
3980 for (Constraint* constraint : trailed_constraints_) {
3981 constraint->Revert();
3983 trailed_constraints_.clear();
3987NodeId LocalSearchState::DependencyGraph::GetOrCreateNodeOfDomainId(
3989 if (domain_id.value() >= dag_node_of_domain_.size()) {
3990 dag_node_of_domain_.resize(domain_id.value() + 1,
NodeId(0));
3992 if (dag_node_of_domain_[domain_id] ==
NodeId(0)) {
3993 dag_node_of_domain_[domain_id] =
NodeId(num_dag_nodes_++);
3995 return dag_node_of_domain_[domain_id];
3998NodeId LocalSearchState::DependencyGraph::GetOrCreateNodeOfConstraintId(
3999 ConstraintId constraint_id) {
4000 if (constraint_id.value() >= dag_node_of_constraint_.size()) {
4001 dag_node_of_constraint_.resize(constraint_id.value() + 1,
NodeId(0));
4003 if (dag_node_of_constraint_[constraint_id] ==
NodeId(0)) {
4004 dag_node_of_constraint_[constraint_id] =
NodeId(num_dag_nodes_++);
4006 return dag_node_of_constraint_[constraint_id];
4009void LocalSearchState::DependencyGraph::AddDomainsConstraintDependencies(
4010 const std::vector<VariableDomainId>& domain_ids,
4011 ConstraintId constraint_id) {
4012 const NodeId cnode = GetOrCreateNodeOfConstraintId(constraint_id);
4013 for (
int i = 0;
i < domain_ids.size(); ++
i) {
4014 const NodeId dnode = GetOrCreateNodeOfDomainId(domain_ids[i]);
4015 dag_.AddArc(dnode, cnode);
4016 dependency_of_dag_arc_.push_back({.domain_id = domain_ids[
i],
4018 .constraint_id = constraint_id});
4022void LocalSearchState::DependencyGraph::AddConstraintDomainDependency(
4024 const NodeId cnode = GetOrCreateNodeOfConstraintId(constraint_id);
4025 const NodeId dnode = GetOrCreateNodeOfDomainId(domain_id);
4026 dag_.AddArc(cnode, dnode);
4027 dependency_of_dag_arc_.push_back({.domain_id = domain_id,
4029 .constraint_id = constraint_id});
4033const std::vector<LocalSearchState::DependencyGraph::Dependency>&
4034LocalSearchState::DependencyGraph::ComputeSortedDependencies(
4036 sorted_dependencies_.clear();
4037 const NodeId node = dag_node_of_domain_[domain_id];
4038 for (
const ArcId a : dag_.ComputeSortedSubDagArcs(node)) {
4039 if (dependency_of_dag_arc_[
a].input_index == -1)
continue;
4040 sorted_dependencies_.push_back(dependency_of_dag_arc_[
a]);
4042 return sorted_dependencies_;
4046 const std::vector<VariableDomainId>& input_domain_ids,
4047 const std::vector<int64_t>& input_weights, int64_t input_offset,
4049 DCHECK_EQ(input_domain_ids.size(), input_weights.size());
4051 const ConstraintId constraint_id(constraints_.size());
4052 dependency_graph_.AddDomainsConstraintDependencies(input_domain_ids,
4054 dependency_graph_.AddConstraintDomainDependency(constraint_id,
4057 auto constraint = std::make_unique<WeightedSum>(
4058 this, input_domain_ids, input_weights, input_offset, output_domain_id);
4059 constraints_.push_back(std::move(constraint));
4062void LocalSearchState::DependencyGraph::BuildDependencyDAG(
int num_domains) {
4063 dag_.BuildGraph(num_dag_nodes_);
4065 const int num_assigned_nodes = dag_node_of_domain_.size();
4066 DCHECK_GE(num_domains, num_assigned_nodes);
4067 num_domains = std::max(num_domains, num_assigned_nodes);
4068 dag_node_of_domain_.resize(num_domains,
NodeId(0));
4073 triggers_of_domain_.clear();
4074 const int num_domains = current_domains_.size();
4075 dependency_graph_.BuildDependencyDAG(num_domains);
4076 for (
int vid = 0; vid < num_domains; ++vid) {
4077 triggers_of_domain_.push_back(triggers_.size());
4078 for (
const auto& [domain_id, input_index, constraint_id] :
4080 triggers_.push_back({.constraint = constraints_[constraint_id].get(),
4081 .input_index = input_index});
4084 triggers_of_domain_.push_back(triggers_.size());
4087LocalSearchState::WeightedSum::WeightedSum(
4089 const std::vector<VariableDomainId>& input_domain_ids,
4090 const std::vector<int64_t>& input_weights, int64_t input_offset,
4092 : output_(output), state_(state) {
4095 invariants_.num_neg_inf = 0;
4096 invariants_.num_pos_inf = 0;
4097 invariants_.wsum_mins = input_offset;
4098 invariants_.wsum_maxs = input_offset;
4099 for (
int i = 0; i < input_domain_ids.size(); ++i) {
4101 const int64_t
weight = input_weights[i];
4104 inputs_.push_back({.min =
min,
4106 .committed_min =
min,
4107 .committed_max =
max,
4109 .domain = domain_id,
4110 .is_trailed =
false});
4114 ++invariants_.num_neg_inf;
4116 invariants_.wsum_mins =
4120 ++invariants_.num_pos_inf;
4122 invariants_.wsum_maxs =
4127 ++invariants_.num_pos_inf;
4129 invariants_.wsum_maxs =
4133 ++invariants_.num_neg_inf;
4135 invariants_.wsum_mins =
4140 committed_invariants_ = invariants_;
4143LocalSearchState::VariableDomain LocalSearchState::WeightedSum::Propagate(
4145 if (!constraint_is_trailed_) {
4146 constraint_is_trailed_ =
true;
4147 state_->TrailConstraint(
this);
4149 WeightedVariable&
input = inputs_[input_index];
4150 if (!
input.is_trailed) {
4151 input.is_trailed =
true;
4152 trailed_inputs_.push_back(&
input);
4158 if (
input.min != new_min) {
4161 --invariants_.num_neg_inf;
4163 invariants_.wsum_mins =
4168 ++invariants_.num_neg_inf;
4170 invariants_.wsum_mins =
4173 input.min = new_min;
4175 if (
input.max != new_max) {
4178 --invariants_.num_pos_inf;
4180 invariants_.wsum_maxs =
4185 ++invariants_.num_pos_inf;
4187 invariants_.wsum_maxs =
4190 input.max = new_max;
4193 if (
input.min != new_min) {
4196 --invariants_.num_pos_inf;
4198 invariants_.wsum_maxs =
4203 ++invariants_.num_pos_inf;
4205 invariants_.wsum_maxs =
4208 input.min = new_min;
4210 if (
input.max != new_max) {
4213 --invariants_.num_neg_inf;
4215 invariants_.wsum_mins =
4220 ++invariants_.num_neg_inf;
4222 invariants_.wsum_mins =
4225 input.max = new_max;
4228 return {invariants_.num_neg_inf == 0 ? invariants_.wsum_mins :
kint64min,
4229 invariants_.num_pos_inf == 0 ? invariants_.wsum_maxs :
kint64max};
4232void LocalSearchState::WeightedSum::Commit() {
4233 committed_invariants_ = invariants_;
4234 constraint_is_trailed_ =
false;
4235 for (WeightedVariable* wv : trailed_inputs_) wv->Commit();
4236 trailed_inputs_.clear();
4239void LocalSearchState::WeightedSum::Revert() {
4240 invariants_ = committed_invariants_;
4241 constraint_is_trailed_ =
false;
4242 for (WeightedVariable* wv : trailed_inputs_) wv->Revert();
4243 trailed_inputs_.clear();
4248 for (
int t = triggers_of_domain_[domain_id]; t < triggers_of_domain_[next_id];
4250 const auto& [constraint, input_index] = triggers_[t];
4251 constraint->Propagate(input_index);
4258 for (
int t = triggers_of_domain_[domain_id]; t < triggers_of_domain_[next_id];
4260 const auto& [constraint, input_index] = triggers_[t];
4261 const auto [output_min, output_max] = constraint->Propagate(input_index);
4279 std::string
DebugString()
const override {
return "LocalSearchProfiler"; }
4281 operator_stats_.clear();
4282 filter_stats_.clear();
4286 if (solver()->TopLevelSearch() == solver()->ActiveSearch()) {
4290 template <
typename Callback>
4293 if (db->seconds() == 0)
continue;
4294 callback(db->name(), db->seconds());
4298 template <
typename Callback>
4300 std::vector<const LocalSearchOperator*> operators;
4301 for (
const auto& stat : operator_stats_) {
4302 operators.push_back(stat.first);
4305 operators.begin(), operators.end(),
4307 return gtl::FindOrDie(operator_stats_, op1).neighbors >
4308 gtl::FindOrDie(operator_stats_, op2).neighbors;
4311 const OperatorStats& stats =
gtl::FindOrDie(operator_stats_, op);
4312 callback(op->DebugString(), stats.neighbors, stats.filtered_neighbors,
4313 stats.accepted_neighbors, stats.seconds);
4317 template <
typename Callback>
4319 absl::flat_hash_map<std::string, std::vector<const LocalSearchFilter*>>
4320 filters_per_context;
4321 for (
const auto& stat : filter_stats_) {
4322 filters_per_context[stat.second.context].push_back(stat.first);
4324 for (
auto& [
context, filters] : filters_per_context) {
4325 std::sort(filters.begin(), filters.end(),
4328 return gtl::FindOrDie(filter_stats_, filter1).calls >
4329 gtl::FindOrDie(filter_stats_, filter2).calls;
4332 const FilterStats& stats =
gtl::FindOrDie(filter_stats_, filter);
4339 LocalSearchStatistics statistics_proto;
4340 ParseFirstSolutionStatistics(
4341 [&statistics_proto](absl::string_view
name,
double duration_seconds) {
4342 LocalSearchStatistics::FirstSolutionStatistics*
const
4343 first_solution_statistics =
4344 statistics_proto.add_first_solution_statistics();
4345 first_solution_statistics->set_strategy(
name);
4346 first_solution_statistics->set_duration_seconds(duration_seconds);
4348 ParseLocalSearchOperatorStatistics([&statistics_proto](
4349 absl::string_view
name,
4350 int64_t num_neighbors,
4351 int64_t num_filtered_neighbors,
4352 int64_t num_accepted_neighbors,
4353 double duration_seconds) {
4354 LocalSearchStatistics::LocalSearchOperatorStatistics*
const
4355 local_search_operator_statistics =
4356 statistics_proto.add_local_search_operator_statistics();
4357 local_search_operator_statistics->set_local_search_operator(
name);
4358 local_search_operator_statistics->set_num_neighbors(num_neighbors);
4359 local_search_operator_statistics->set_num_filtered_neighbors(
4360 num_filtered_neighbors);
4361 local_search_operator_statistics->set_num_accepted_neighbors(
4362 num_accepted_neighbors);
4363 local_search_operator_statistics->set_duration_seconds(duration_seconds);
4365 ParseLocalSearchFilterStatistics([&statistics_proto](
4367 const std::string&
name,
4368 int64_t num_calls, int64_t num_rejects,
4369 double duration_seconds) {
4370 LocalSearchStatistics::LocalSearchFilterStatistics*
const
4371 local_search_filter_statistics =
4372 statistics_proto.add_local_search_filter_statistics();
4373 local_search_filter_statistics->set_local_search_filter(
name);
4374 local_search_filter_statistics->set_num_calls(num_calls);
4375 local_search_filter_statistics->set_num_rejects(num_rejects);
4376 local_search_filter_statistics->set_duration_seconds(duration_seconds);
4377 local_search_filter_statistics->set_num_rejects_per_second(
4378 num_rejects / duration_seconds);
4379 local_search_filter_statistics->set_context(
context);
4381 statistics_proto.set_total_num_neighbors(solver()->neighbors());
4382 statistics_proto.set_total_num_filtered_neighbors(
4383 solver()->filtered_neighbors());
4384 statistics_proto.set_total_num_accepted_neighbors(
4385 solver()->accepted_neighbors());
4386 return statistics_proto;
4389 std::string overview;
4390 size_t max_name_size = 0;
4391 ParseFirstSolutionStatistics(
4392 [&max_name_size](absl::string_view
name,
double) {
4393 max_name_size = std::max(max_name_size,
name.length());
4395 if (max_name_size > 0) {
4396 absl::StrAppendFormat(&overview,
4397 "First solution statistics:\n%*s | Time (s)\n",
4399 ParseFirstSolutionStatistics(
4400 [&overview, max_name_size](
const std::string&
name,
4401 double duration_seconds) {
4402 absl::StrAppendFormat(&overview,
"%*s | %7.2g\n", max_name_size,
4403 name, duration_seconds);
4407 ParseLocalSearchOperatorStatistics([&max_name_size](absl::string_view
name,
4410 max_name_size = std::max(max_name_size,
name.length());
4412 if (max_name_size > 0) {
4413 absl::StrAppendFormat(
4415 "Local search operator statistics:\n%*s | Neighbors | Filtered "
4416 "| Accepted | Time (s)\n",
4418 OperatorStats total_stats;
4419 ParseLocalSearchOperatorStatistics(
4420 [&overview, &total_stats, max_name_size](
4421 absl::string_view
name, int64_t num_neighbors,
4422 int64_t num_filtered_neighbors, int64_t num_accepted_neighbors,
4423 double duration_seconds) {
4424 absl::StrAppendFormat(
4425 &overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n", max_name_size,
4426 name, num_neighbors, num_filtered_neighbors,
4427 num_accepted_neighbors, duration_seconds);
4428 total_stats.neighbors += num_neighbors;
4429 total_stats.filtered_neighbors += num_filtered_neighbors;
4430 total_stats.accepted_neighbors += num_accepted_neighbors;
4431 total_stats.seconds += duration_seconds;
4433 absl::StrAppendFormat(
4434 &overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n", max_name_size,
4435 "Total", total_stats.neighbors, total_stats.filtered_neighbors,
4436 total_stats.accepted_neighbors, total_stats.seconds);
4439 ParseLocalSearchFilterStatistics(
4440 [&max_name_size](absl::string_view, absl::string_view
name, int64_t,
4442 max_name_size = std::max(max_name_size,
name.length());
4444 if (max_name_size > 0) {
4445 std::optional<std::string> filter_context;
4446 FilterStats total_filter_stats;
4447 ParseLocalSearchFilterStatistics(
4448 [&overview, &filter_context, &total_filter_stats, max_name_size](
4449 const std::string&
context,
const std::string&
name,
4450 int64_t num_calls, int64_t num_rejects,
double duration_seconds) {
4451 if (!filter_context.has_value() ||
4452 filter_context.value() !=
context) {
4453 if (filter_context.has_value()) {
4454 absl::StrAppendFormat(
4455 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n",
4456 max_name_size,
"Total", total_filter_stats.calls,
4457 total_filter_stats.rejects, total_filter_stats.seconds,
4458 total_filter_stats.rejects / total_filter_stats.seconds);
4459 total_filter_stats = {};
4462 absl::StrAppendFormat(
4464 "Local search filter statistics%s:\n%*s | Calls | "
4465 " Rejects | Time (s) | Rejects/s\n",
4469 absl::StrAppendFormat(
4470 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n",
4471 max_name_size,
name, num_calls, num_rejects, duration_seconds,
4472 num_rejects / duration_seconds);
4473 total_filter_stats.calls += num_calls;
4474 total_filter_stats.rejects += num_rejects;
4475 total_filter_stats.seconds += duration_seconds;
4477 absl::StrAppendFormat(
4478 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n", max_name_size,
4479 "Total", total_filter_stats.calls, total_filter_stats.rejects,
4480 total_filter_stats.seconds,
4481 total_filter_stats.rejects / total_filter_stats.seconds);
4488 if (last_operator_ != op->
Self()) {
4490 last_operator_ = op->
Self();
4495 if (neighbor_found) {
4496 operator_stats_[op->
Self()].neighbors++;
4501 bool neighbor_found)
override {
4502 if (neighbor_found) {
4503 operator_stats_[op->
Self()].filtered_neighbors++;
4508 bool neighbor_found)
override {
4509 if (neighbor_found) {
4510 operator_stats_[op->
Self()].accepted_neighbors++;
4514 FilterStats& filter_stats = filter_stats_[filter];
4515 filter_stats.calls++;
4516 filter_stats.context = solver()->
context();
4517 filter_timer_.Start();
4520 filter_timer_.Stop();
4521 auto& stats = filter_stats_[filter];
4522 stats.seconds += filter_timer_.Get();
4529 profiled_decision_builders_.push_back(profiled_db);
4532 void Install()
override { SearchMonitor::Install(); }
4536 if (last_operator_ !=
nullptr) {
4538 operator_stats_[last_operator_].seconds += timer_.
Get();
4543 struct OperatorStats {
4544 int64_t neighbors = 0;
4545 int64_t filtered_neighbors = 0;
4546 int64_t accepted_neighbors = 0;
4550 struct FilterStats {
4552 int64_t rejects = 0;
4558 const LocalSearchOperator* last_operator_ =
nullptr;
4559 absl::flat_hash_map<const LocalSearchOperator*, OperatorStats>
4561 absl::flat_hash_map<const LocalSearchFilter*, FilterStats> filter_stats_;
4563 std::vector<ProfiledDecisionBuilder*> profiled_decision_builders_;
4568 if (IsLocalSearchProfilingEnabled()) {
4571 local_search_profiler_->AddFirstSolutionProfiledDecisionBuilder(
4592 if (local_search_profiler_ !=
nullptr) {
4593 return local_search_profiler_->PrintOverview();
4599 if (local_search_profiler_ !=
nullptr) {
4600 return local_search_profiler_->ExportToLocalSearchStatistics();
4602 return LocalSearchStatistics();
4605void LocalSearchFilterManager::FindIncrementalEventEnd() {
4606 const int num_events = events_.size();
4607 incremental_events_end_ = num_events;
4608 int last_priority = -1;
4609 for (
int e = num_events - 1; e >= 0; --e) {
4610 const auto& [filter, event_type, priority] = events_[e];
4611 if (priority != last_priority) {
4612 incremental_events_end_ = e + 1;
4613 last_priority = priority;
4615 if (filter->IsIncremental())
break;
4620 std::vector<LocalSearchFilter*> filters)
4621 : synchronized_value_(
std::numeric_limits<int64_t>::
min()),
4622 accepted_value_(
std::numeric_limits<int64_t>::
min()) {
4623 events_.reserve(2 * filters.size());
4631 FindIncrementalEventEnd();
4635 std::vector<FilterEvent> filter_events)
4636 : events_(
std::move(filter_events)),
4637 synchronized_value_(
std::numeric_limits<int64_t>::
min()),
4638 accepted_value_(
std::numeric_limits<int64_t>::
min()) {
4639 std::sort(events_.begin(), events_.end(),
4641 return e1.priority < e2.priority;
4643 FindIncrementalEventEnd();
4649 for (
int e = last_event_called_; e >= 0; --e) {
4650 const auto [filter, event_type, _priority] = events_[e];
4653 last_event_called_ = -1;
4662 int64_t objective_min,
4663 int64_t objective_max) {
4665 accepted_value_ = 0;
4666 bool feasible =
true;
4667 bool reordered =
false;
4668 int events_end = events_.size();
4669 for (
int e = 0; e < events_end; ++e) {
4670 last_event_called_ = e;
4671 const auto [filter, event_type, priority] = events_[e];
4672 switch (event_type) {
4674 if (!feasible && !filter->IsIncremental())
continue;
4676 const bool accept = filter->Accept(
4677 delta, deltadelta,
CapSub(objective_min, accepted_value_),
4678 CapSub(objective_max, accepted_value_));
4680 if (monitor !=
nullptr) monitor->
EndFiltering(filter, !accept);
4683 CapAdd(accepted_value_, filter->GetAcceptedObjectiveValue());
4685 feasible = accepted_value_ <= objective_max;
4688 events_end = incremental_events_end_;
4693 int to_move = e - 1;
4694 if (to_move >= 0 && events_[to_move].filter == filter) --to_move;
4695 if (to_move >= 0 && events_[to_move].priority == priority) {
4696 std::rotate(events_.begin() + to_move,
4697 events_.begin() + to_move + 1,
4698 events_.begin() + e + 1);
4705 filter->Relax(
delta, deltadelta);
4709 LOG(FATAL) <<
"Unknown filter event type.";
4720 const bool reset_to_assignment =
delta ==
nullptr ||
delta->Empty();
4722 for (
auto [filter, event_type, unused_priority] : events_) {
4723 switch (event_type) {
4728 if (reset_to_assignment) {
4730 filter->Relax(assignment,
nullptr);
4732 filter->Relax(
delta,
nullptr);
4737 LOG(FATAL) <<
"Unknown filter event type.";
4742 synchronized_value_ = 0;
4744 switch (event_type) {
4746 filter->Synchronize(assignment,
delta);
4747 synchronized_value_ =
CapAdd(synchronized_value_,
4748 filter->GetSynchronizedObjectiveValue());
4752 filter->Commit(assignment,
delta);
4756 LOG(FATAL) <<
"Unknown filter event type.";
4772 std::string
DebugString()
const override {
return "FindOneNeighbor"; }
4776 int64_t objective_min, int64_t objective_max);
4777 void SynchronizeAll(
Solver* solver);
4780 IntVar*
const objective_;
4781 std::unique_ptr<Assignment> reference_assignment_;
4782 std::unique_ptr<Assignment> last_synchronized_assignment_;
4789 bool neighbor_found_;
4791 int64_t solutions_since_last_check_;
4792 int64_t check_period_;
4794 bool has_checked_assignment_ =
false;
4809 : assignment_(assignment),
4810 objective_(objective),
4811 reference_assignment_(new
Assignment(assignment_)),
4812 filter_assignment_delta_(assignment->solver()->MakeAssignment()),
4814 ls_operator_(ls_operator),
4815 sub_decision_builder_(sub_decision_builder),
4817 original_limit_(limit),
4818 neighbor_found_(false),
4819 filter_manager_(filter_manager),
4820 solutions_since_last_check_(0),
4822 assignment_->solver()->
parameters().check_solution_period()),
4823 last_checked_assignment_(assignment) {
4824 CHECK(
nullptr != assignment);
4825 CHECK(
nullptr != ls_operator);
4829 if (
nullptr == limit) {
4837 VLOG(1) <<
"Disabling neighbor-check skipping outside of first accept.";
4844 VLOG(1) <<
"Disabling neighbor-check skipping for LNS.";
4848 if (!reference_assignment_->HasObjective()) {
4849 reference_assignment_->AddObjective(objective_);
4854 CHECK(
nullptr != solver);
4856 if (original_limit_ !=
nullptr) {
4857 limit_->
Copy(original_limit_);
4864 if (!neighbor_found_) {
4872 SynchronizeAll(solver);
4882 if (sub_decision_builder_) {
4883 restore = solver->
Compose(restore, sub_decision_builder_);
4891 delta->ClearObjective();
4892 deltadelta->
Clear();
4894 if (++counter >= absl::GetFlag(FLAGS_cp_local_search_sync_frequency) &&
4895 pool_->
SyncNeeded(reference_assignment_.get())) {
4898 SynchronizeAll(solver);
4901 bool has_neighbor =
false;
4902 if (!limit_->
Check()) {
4906 ls_operator_, has_neighbor,
delta, deltadelta);
4909 if (has_neighbor && !solver->IsUncheckedSolutionLimitReached()) {
4910 solver->neighbors_ += 1;
4917 const bool mh_filter =
4919 int64_t objective_min = std::numeric_limits<int64_t>::min();
4920 int64_t objective_max = std::numeric_limits<int64_t>::max();
4922 objective_min = objective_->
Min();
4923 objective_max = objective_->
Max();
4925 if (
delta->HasObjective() &&
delta->Objective() == objective_) {
4926 objective_min = std::max(objective_min,
delta->ObjectiveMin());
4927 objective_max = std::min(objective_max,
delta->ObjectiveMax());
4929 const bool move_filter = FilterAccept(solver,
delta, deltadelta,
4930 objective_min, objective_max);
4932 ls_operator_, mh_filter && move_filter);
4933 if (!mh_filter || !move_filter) {
4934 if (filter_manager_ !=
nullptr) filter_manager_->
Revert();
4937 solver->filtered_neighbors_ += 1;
4938 if (
delta->HasObjective()) {
4950 const bool check_solution = (solutions_since_last_check_ == 0) ||
4953 !
delta->AreAllElementsBound();
4954 if (has_checked_assignment_) solutions_since_last_check_++;
4955 if (solutions_since_last_check_ >= check_period_) {
4956 solutions_since_last_check_ = 0;
4958 const bool accept = !check_solution || solver->
SolveAndCommit(restore);
4962 solver->accepted_neighbors_ += 1;
4963 if (check_solution) {
4966 assignment_->
Store();
4968 neighbor_found_ =
true;
4969 has_checked_assignment_ =
true;
4983 solver->IncrementUncheckedSolutionCounter();
4985 SynchronizeAll(solver);
4988 neighbor_found_ =
true;
4990 if (filter_manager_ !=
nullptr) filter_manager_->
Revert();
4991 if (check_period_ > 1 && has_checked_assignment_) {
4997 VLOG(1) <<
"Imperfect filtering detected, backtracking to last "
4998 "checked solution and checking all solutions.";
5000 solutions_since_last_check_ = 0;
5002 SynchronizeAll(solver);
5009 last_synchronized_assignment_.reset();
5010 if (neighbor_found_) {
5022 has_checked_assignment_ =
true;
5030 SynchronizeAll(solver);
5040 last_synchronized_assignment_.reset();
5047 int64_t objective_min,
5048 int64_t objective_max) {
5049 if (filter_manager_ ==
nullptr)
return true;
5051 return filter_manager_->
Accept(monitor,
delta, deltadelta, objective_min,
5057template <
typename Container>
5058void AddDeltaElements(
const Container& old_container,
5059 const Container& new_container, Assignment*
delta) {
5060 for (
const auto& new_element : new_container.elements()) {
5061 const auto var = new_element.
Var();
5062 const auto old_element_ptr = old_container.ElementPtrOrNull(
var);
5063 if (old_element_ptr ==
nullptr || *old_element_ptr != new_element) {
5064 delta->FastAdd(
var)->Copy(new_element);
5069void MakeDelta(
const Assignment* old_assignment,
5070 const Assignment* new_assignment, Assignment*
delta) {
5071 DCHECK_NE(
delta,
nullptr);
5073 AddDeltaElements(old_assignment->IntVarContainer(),
5074 new_assignment->IntVarContainer(),
delta);
5075 AddDeltaElements(old_assignment->IntervalVarContainer(),
5076 new_assignment->IntervalVarContainer(),
delta);
5077 AddDeltaElements(old_assignment->SequenceVarContainer(),
5078 new_assignment->SequenceVarContainer(),
delta);
5082void FindOneNeighbor::SynchronizeAll(Solver* solver) {
5083 Assignment*
const reference_assignment = reference_assignment_.get();
5085 neighbor_found_ =
false;
5087 solver->GetLocalSearchMonitor()->BeginOperatorStart();
5088 ls_operator_->
Start(reference_assignment);
5089 if (filter_manager_ !=
nullptr) {
5090 Assignment*
delta =
nullptr;
5091 if (last_synchronized_assignment_ ==
nullptr) {
5092 last_synchronized_assignment_ =
5093 std::make_unique<Assignment>(reference_assignment);
5095 MakeDelta(last_synchronized_assignment_.get(), reference_assignment,
5096 filter_assignment_delta_);
5097 delta = filter_assignment_delta_;
5098 last_synchronized_assignment_->Copy(reference_assignment);
5102 solver->GetLocalSearchMonitor()->EndOperatorStart();
5115 solution_pool_(pool),
5122 return "LocalSearchPhaseParameters";
5129 return sub_decision_builder_;
5135 IntVar*
const objective_;
5147 ls_operator, sub_decision_builder,
5155 ls_operator, sub_decision_builder,
5164 ls_operator, sub_decision_builder,
5165 limit, filter_manager);
5173 sub_decision_builder,
nullptr,
nullptr);
5181 sub_decision_builder, limit,
nullptr);
5190 sub_decision_builder, limit,
5204class NestedSolveDecision :
public Decision {
5207 enum StateType { DECISION_PENDING, DECISION_FAILED, DECISION_FOUND };
5209 NestedSolveDecision(DecisionBuilder* db,
bool restore,
5210 const std::vector<SearchMonitor*>& monitors);
5211 NestedSolveDecision(DecisionBuilder* db,
bool restore);
5212 ~NestedSolveDecision()
override {}
5213 void Apply(Solver* solver)
override;
5214 void Refute(Solver* solver)
override;
5215 std::string DebugString()
const override {
return "NestedSolveDecision"; }
5216 int state()
const {
return state_; }
5219 DecisionBuilder*
const db_;
5221 std::vector<SearchMonitor*> monitors_;
5225NestedSolveDecision::NestedSolveDecision(
5226 DecisionBuilder*
const db,
bool restore,
5227 const std::vector<SearchMonitor*>& monitors)
5230 monitors_(monitors),
5231 state_(DECISION_PENDING) {
5232 CHECK(
nullptr != db);
5235NestedSolveDecision::NestedSolveDecision(DecisionBuilder*
const db,
5237 : db_(db), restore_(restore), state_(DECISION_PENDING) {
5238 CHECK(
nullptr != db);
5241void NestedSolveDecision::Apply(Solver*
const solver) {
5242 CHECK(
nullptr != solver);
5244 if (solver->Solve(db_, monitors_)) {
5245 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FOUND));
5247 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FAILED));
5250 if (solver->SolveAndCommit(db_, monitors_)) {
5251 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FOUND));
5253 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FAILED));
5258void NestedSolveDecision::Refute(Solver*
const) {}
5301 void PushLocalSearchDecision();
5305 IntVar*
const objective_ =
nullptr;
5310 std::vector<NestedSolveDecision*> nested_decisions_;
5311 int nested_decision_index_;
5323 : assignment_(nullptr),
5324 objective_(objective),
5326 ls_operator_(ls_operator),
5327 first_solution_sub_decision_builder_(sub_decision_builder),
5328 sub_decision_builder_(sub_decision_builder),
5329 nested_decision_index_(0),
5331 filter_manager_(filter_manager),
5332 has_started_(false) {
5333 CHECK(
nullptr != assignment);
5334 CHECK(
nullptr != ls_operator);
5337 assignment_->
Copy(assignment);
5350 : assignment_(nullptr),
5351 objective_(objective),
5353 ls_operator_(ls_operator),
5354 first_solution_sub_decision_builder_(sub_decision_builder),
5355 sub_decision_builder_(sub_decision_builder),
5356 nested_decision_index_(0),
5358 filter_manager_(filter_manager),
5359 has_started_(false) {
5360 CHECK(
nullptr != first_solution);
5361 CHECK(
nullptr != ls_operator);
5362 CHECK(!vars.empty());
5363 Solver*
const solver = vars[0]->solver();
5365 assignment_->
Add(vars);
5371 const std::vector<IntVar*>& vars,
IntVar* objective,
5377 : assignment_(nullptr),
5378 objective_(objective),
5380 ls_operator_(ls_operator),
5381 first_solution_sub_decision_builder_(first_solution_sub_decision_builder),
5382 sub_decision_builder_(sub_decision_builder),
5383 nested_decision_index_(0),
5385 filter_manager_(filter_manager),
5386 has_started_(false) {
5387 CHECK(
nullptr != first_solution);
5388 CHECK(
nullptr != ls_operator);
5389 CHECK(!vars.empty());
5390 Solver*
const solver = vars[0]->solver();
5392 assignment_->
Add(vars);
5404 : assignment_(nullptr),
5405 objective_(objective),
5407 ls_operator_(ls_operator),
5408 first_solution_sub_decision_builder_(sub_decision_builder),
5409 sub_decision_builder_(sub_decision_builder),
5410 nested_decision_index_(0),
5412 filter_manager_(filter_manager),
5413 has_started_(false) {
5414 CHECK(
nullptr != first_solution);
5415 CHECK(
nullptr != ls_operator);
5416 CHECK(!vars.empty());
5417 Solver*
const solver = vars[0]->solver();
5419 assignment_->
Add(vars);
5428 DCHECK(assignment_ !=
nullptr);
5431 const std::vector<IntVarElement>& elements =
5433 if (!elements.empty()) {
5434 std::vector<IntVar*> vars;
5436 vars.push_back(elem.Var());
5441 const std::vector<IntervalVarElement>& interval_elements =
5443 if (!interval_elements.empty()) {
5444 std::vector<IntervalVar*> interval_vars;
5446 interval_vars.push_back(elem.Var());
5460 CHECK(
nullptr != solver);
5461 CHECK_LT(0, nested_decisions_.size());
5462 if (!has_started_) {
5463 nested_decision_index_ = 0;
5465 }
else if (nested_decision_index_ < 0) {
5468 NestedSolveDecision* decision = nested_decisions_[nested_decision_index_];
5469 const int state = decision->state();
5471 case NestedSolveDecision::DECISION_FAILED: {
5472 const bool local_optimum_reached =
5474 if (local_optimum_reached) {
5478 ls_operator_->
Reset();
5480 if (!local_optimum_reached || solver->IsUncheckedSolutionLimitReached()) {
5481 nested_decision_index_ = -1;
5486 case NestedSolveDecision::DECISION_PENDING: {
5489 const int32_t kLocalSearchBalancedTreeDepth = 32;
5491 if (depth < kLocalSearchBalancedTreeDepth) {
5494 if (depth > kLocalSearchBalancedTreeDepth) {
5499 case NestedSolveDecision::DECISION_FOUND: {
5501 if (nested_decision_index_ + 1 < nested_decisions_.size()) {
5502 ++nested_decision_index_;
5507 LOG(ERROR) <<
"Unknown local search state";
5515 CHECK(first_solution);
5520 first_solution_sub_decision_builder_, store);
5521 std::vector<SearchMonitor*> monitor;
5522 monitor.push_back(limit_);
5523 nested_decisions_.push_back(solver->
RevAlloc(
5524 new NestedSolveDecision(first_solution_and_store,
false, monitor)));
5531 sub_decision_builder_, limit_, filter_manager_));
5532 nested_decisions_.push_back(
5533 solver->
RevAlloc(
new NestedSolveDecision(find_neighbors,
false)));
5543 reference_assignment_ = std::make_unique<Assignment>(assignment);
5547 reference_assignment_->CopyIntersection(assignment);
5556 std::string
DebugString()
const override {
return "DefaultSolutionPool"; }
5559 std::unique_ptr<Assignment> reference_assignment_;
5590 first_solution, first_solution_sub_decision_builder,
5596 const std::vector<SequenceVar*>& vars,
DecisionBuilder* first_solution,
5605template <
bool is_profile_active>
5606Assignment* Solver::RunUncheckedLocalSearchInternal(
5609 const std::vector<SearchMonitor*>& monitors,
RegularLimit* limit,
5610 absl::flat_hash_set<IntVar*>* touched) {
5611 DCHECK(initial_solution !=
nullptr);
5612 DCHECK(initial_solution->
Objective() !=
nullptr);
5613 DCHECK(filter_manager !=
nullptr);
5614 if (limit !=
nullptr) limit->
Init();
5618 current_solution->
Copy(initial_solution);
5623 const auto sync_with_solution =
5624 [
this, ls_monitor, ls_operator,
5627 IncrementUncheckedSolutionCounter();
5628 if constexpr (is_profile_active) {
5631 ls_operator->Start(current_solution);
5632 filter_manager->Synchronize(current_solution,
delta);
5633 if constexpr (is_profile_active) {
5637 sync_with_solution(
nullptr);
5639 if (!ls_operator->HoldsDelta()) {
5642 delta.ClearObjective();
5644 if (limit !=
nullptr && (limit->
CheckWithOffset(absl::ZeroDuration()) ||
5648 if constexpr (is_profile_active) {
5651 const bool has_neighbor =
5652 ls_operator->MakeNextNeighbor(&
delta, &deltadelta);
5653 if constexpr (is_profile_active) {
5657 if (!has_neighbor) {
5660 if constexpr (is_profile_active) {
5667 const bool filter_accept =
5668 filter_manager->Accept(ls_monitor, &
delta, &deltadelta,
5670 if constexpr (is_profile_active) {
5675 if (!filter_accept) {
5676 filter_manager->Revert();
5679 filtered_neighbors_ += 1;
5682 filter_manager->GetAcceptedObjectiveValue());
5683 DCHECK(
delta.AreAllElementsBound());
5684 accepted_neighbors_ += 1;
5689 if (touched !=
nullptr) {
5690 for (
const auto& element :
delta.IntVarContainer().elements()) {
5691 touched->insert(element.Var());
5695 sync_with_solution(&
delta);
5697 if (parameters_.print_local_search_profile()) {
5699 if (!profile.empty()) LOG(INFO) << profile;
5707 const std::vector<SearchMonitor*>& monitors,
RegularLimit* limit,
5708 absl::flat_hash_set<IntVar*>* touched) {
5710 return RunUncheckedLocalSearchInternal<true>(initial_solution,
5711 filter_manager, ls_operator,
5712 monitors, limit, touched);
5714 return RunUncheckedLocalSearchInternal<false>(initial_solution,
5715 filter_manager, ls_operator,
5716 monitors, limit, touched);
const std::vector< IntVar * > vars_
void Start()
When Start() is called multiple times, only the most recent is used.
const E & Element(const V *const var) const
const std::vector< E > & elements() const
bool HasObjective() const
void SetObjectiveValue(int64_t value)
IntVarElement * Add(IntVar *var)
const IntervalContainer & IntervalVarContainer() const
std::string DebugString() const override
void Copy(const Assignment *assignment)
IntVar * Objective() const
const IntContainer & IntVarContainer() const
int64_t ObjectiveValue() const
void CopyIntersection(const Assignment *assignment)
void AddObjective(IntVar *const v)
bool MakeOneNeighbor() override
BaseInactiveNodeToPathOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, int number_of_base_nodes, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors=nullptr)
int64_t GetInactiveNode() const
~BaseInactiveNodeToPathOperator() override
void AppendToFragment(int index)
virtual void InitFragments()
BaseLns(const std::vector< IntVar * > &vars)
--— Base Large Neighborhood Search operator --—
bool MakeOneNeighbor() override
This method should not be overridden. Override NextFragment() instead.
virtual bool NextFragment()=0
virtual std::string DebugString() const
void ClearAll()
Sets all bits to 0.
void Set(IndexType i)
Sets the bit at position i to 1.
virtual int64_t ModifyValue(int64_t index, int64_t value)=0
bool MakeOneNeighbor() override
This method should not be overridden. Override ModifyValue() instead.
ChangeValue(const std::vector< IntVar * > &vars)
--— ChangeValue Operators --—
Cross(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors=nullptr)
std::string DebugString() const override
bool MakeNeighbor() override
void GetNextSolution(Assignment *const assignment) override
std::string DebugString() const override
bool SyncNeeded(Assignment *const) override
~DefaultSolutionPool() override
void Initialize(Assignment *const assignment) override
void RegisterNewSolution(Assignment *const assignment) override
std::string DebugString() const override
Exchange(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors=nullptr)
bool MakeNeighbor() override
--— ExtendedSwapActiveOperator --—
ExtendedSwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
bool MakeNeighbor() override
std::string DebugString() const override
~ExtendedSwapActiveOperator() override
--— Finds a neighbor of the assignment passed --—
std::string DebugString() const override
-------— Decision Builder -------—
Decision * Next(Solver *solver) override
~FindOneNeighbor() override
FindOneNeighbor(Assignment *assignment, IntVar *objective, SolutionPool *pool, LocalSearchOperator *ls_operator, DecisionBuilder *sub_decision_builder, const RegularLimit *limit, LocalSearchFilterManager *filter_manager)
void ChangeCostMatrix(CostFunction cost)
Replaces the cost matrix while avoiding re-allocating memory.
std::vector< int > TravelingSalesmanPath()
Returns the TSP tour in the vector pointed to by the argument.
virtual int64_t Min() const =0
virtual int64_t Max() const =0
IntVarLocalSearchFilter(const std::vector< IntVar * > &vars)
void SynchronizeOnAssignment(const Assignment *assignment)
void AddVars(const std::vector< IntVar * > &vars)
Add variables to "track" to the filter.
void Synchronize(const Assignment *assignment, const Assignment *delta) override
~IntVarLocalSearchFilter() override
void SetValue(int64_t index, int64_t value)
void RevertChanges(bool change_was_incremental)
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
--— Base operator class for operators manipulating IntVars --—
void AddVars(const std::vector< IntVar * > &vars)
void Deactivate(int64_t index)
IntVar * Var(int64_t index) const
Returns the variable of given index.
int64_t Value(int64_t index) const
bool ApplyChanges(Assignment *delta, Assignment *deltadelta) const
int64_t OldValue(int64_t index) const
virtual bool MakeOneNeighbor()
IntVar * Var() override
Creates a variable from the expression.
LinKernighan(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, const Solver::IndexEvaluator3 &evaluator, bool topt)
bool MakeNeighbor() override
std::string DebugString() const override
LocalSearchFilterManager(std::vector< FilterEvent > filter_events)
void Revert()
Calls Revert() of filters, in reverse order of Relax events.
int64_t GetAcceptedObjectiveValue() const
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
bool Accept(LocalSearchMonitor *monitor, const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max)
virtual void EndAcceptNeighbor(const LocalSearchOperator *op, bool neighbor_found)=0
virtual void BeginAcceptNeighbor(const LocalSearchOperator *op)=0
virtual void BeginMakeNextNeighbor(const LocalSearchOperator *op)=0
virtual void BeginOperatorStart()=0
Local search operator events.
virtual void BeginFilterNeighbor(const LocalSearchOperator *op)=0
virtual void BeginFiltering(const LocalSearchFilter *filter)=0
virtual void EndFilterNeighbor(const LocalSearchOperator *op, bool neighbor_found)=0
virtual void EndOperatorStart()=0
virtual void EndMakeNextNeighbor(const LocalSearchOperator *op, bool neighbor_found, const Assignment *delta, const Assignment *deltadelta)=0
virtual void EndFiltering(const LocalSearchFilter *filter, bool reject)=0
virtual bool HoldsDelta() const
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
virtual const LocalSearchOperator * Self() const
virtual bool HasFragments() const
virtual void Start(const Assignment *assignment)=0
-------— Local Search Phase Parameters -------—
SolutionPool * solution_pool() const
~LocalSearchPhaseParameters() override
LocalSearchFilterManager * filter_manager() const
IntVar * objective() const
LocalSearchPhaseParameters(IntVar *objective, SolutionPool *const pool, LocalSearchOperator *ls_operator, DecisionBuilder *sub_decision_builder, RegularLimit *const limit, LocalSearchFilterManager *filter_manager)
LocalSearchOperator * ls_operator() const
RegularLimit * limit() const
std::string DebugString() const override
DecisionBuilder * sub_decision_builder() const
--— LocalSearchProfiler --—
void BeginOperatorStart() override
Local search operator events.
void EndFiltering(const LocalSearchFilter *filter, bool reject) override
void BeginMakeNextNeighbor(const LocalSearchOperator *op) override
std::string DebugString() const override
LocalSearchProfiler(Solver *solver)
void BeginFiltering(const LocalSearchFilter *filter) override
LocalSearchStatistics ExportToLocalSearchStatistics() const
void ExitSearch() override
End of the search.
void EndAcceptNeighbor(const LocalSearchOperator *op, bool neighbor_found) override
void EndOperatorStart() override
void ParseLocalSearchOperatorStatistics(const Callback &callback) const
std::string PrintOverview() const
void ParseLocalSearchFilterStatistics(const Callback &callback) const
void AddFirstSolutionProfiledDecisionBuilder(ProfiledDecisionBuilder *profiled_db)
void BeginAcceptNeighbor(const LocalSearchOperator *) override
bool IsActive() const override
void Install() override
Install itself on the solver.
void BeginFilterNeighbor(const LocalSearchOperator *) override
void ParseFirstSolutionStatistics(const Callback &callback) const
void RestartSearch() override
Restart the search.
void EndFilterNeighbor(const LocalSearchOperator *op, bool neighbor_found) override
void EndMakeNextNeighbor(const LocalSearchOperator *op, bool neighbor_found, const Assignment *, const Assignment *) override
void AddWeightedSumConstraint(const std::vector< VariableDomainId > &input_domain_ids, const std::vector< int64_t > &input_weights, int64_t input_offset, VariableDomainId output_domain_id)
Adds a constraint that output = input_offset + sum_i weight_i * input_i.
int64_t VariableDomainMin(VariableDomainId domain_id) const
VariableDomainId AddVariableDomain(int64_t relaxed_min, int64_t relaxed_max)
Adds a variable domain to this state, returns a handler to the new domain.
bool TightenVariableDomainMin(VariableDomainId domain_id, int64_t value)
void ChangeRelaxedVariableDomain(VariableDomainId domain_id, int64_t min, int64_t max)
void PropagateRelax(VariableDomainId domain_id)
Propagation of all events.
Variable MakeVariable(VariableDomainId domain_id)
void RelaxVariableDomain(VariableDomainId domain_id)
bool TightenVariableDomainMax(VariableDomainId domain_id, int64_t value)
void CompileConstraints()
int64_t VariableDomainMax(VariableDomainId domain_id) const
bool PropagateTighten(VariableDomainId domain_id)
--— Local search decision builder --—
void PushFirstSolutionDecision(DecisionBuilder *first_solution)
std::string DebugString() const override
-------— Decision Builder -------—
LocalSearch(Assignment *assignment, IntVar *objective, SolutionPool *pool, LocalSearchOperator *ls_operator, DecisionBuilder *sub_decision_builder, RegularLimit *limit, LocalSearchFilterManager *filter_manager)
void Accept(ModelVisitor *visitor) const override
Model Visitor support.
Decision * Next(Solver *solver) override
void PushLocalSearchDecision()
--— MakeActiveAndRelocate --—
MakeActiveAndRelocate(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
~MakeActiveAndRelocate() override
bool MakeNeighbor() override
std::string DebugString() const override
--— MakeActiveOperator --—
~MakeActiveOperator() override
MakeActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors=nullptr)
bool MakeNeighbor() override
std::string DebugString() const override
--— MakeChainInactiveOperator --—
bool OnSamePathAsPreviousBase(int64_t) override
bool MakeNeighbor() override
int64_t GetBaseNodeRestartPosition(int base_index) override
std::string DebugString() const override
~MakeChainInactiveOperator() override
MakeChainInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
--— MakeInactiveOperator --—
MakeInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
bool MakeNeighbor() override
std::string DebugString() const override
~MakeInactiveOperator() override
virtual void VisitIntervalArrayArgument(const std::string &arg_name, const std::vector< IntervalVar * > &arguments)
virtual void VisitIntegerVariableArrayArgument(const std::string &arg_name, const std::vector< IntVar * > &arguments)
static const char kVariableGroupExtension[]
static const char kVarsArgument[]
virtual void EndVisitExtension(const std::string &type)
virtual void BeginVisitExtension(const std::string &type)
static const char kIntervalsArgument[]
const std::vector< int > & Neighbors(int index) const
NearestNeighbors(const NearestNeighbors &)=delete
This type is neither copyable nor movable.
NearestNeighbors(Solver::IndexEvaluator3 evaluator, const PathOperator &path_operator, int size)
virtual std::string DebugString() const
virtual ~NearestNeighbors()
NearestNeighbors & operator=(const NearestNeighbors &)=delete
--— Limit the number of neighborhoods explored --—
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
NeighborhoodLimit(LocalSearchOperator *const op, int64_t limit)
std::string DebugString() const override
void Start(const Assignment *assignment) override
bool HoldsDelta() const override
--— Path-based Large Neighborhood Search --—
std::string DebugString() const override
bool MakeNeighbor() override
PathLns(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, int number_of_chunks, int chunk_size, bool unactive_fragments)
bool HasFragments() const override
int64_t Prev(int64_t node) const
Returns the node before node in the current delta.
PathOperator(const std::vector< IntVar * > &next_vars, const std::vector< IntVar * > &path_vars, IterationParameters iteration_parameters)
Builds an instance of PathOperator from next and path variables.
bool MakeActive(int64_t node, int64_t destination)
Insert the inactive node after destination.
bool IsPathStart(int64_t node) const
Returns true if node is the first node on the path.
bool CheckChainValidity(int64_t before_chain, int64_t chain_end, int64_t exclude) const
bool SkipUnchanged(int index) const override
int GetNeighborForBaseNode(int64_t base_index) const
int number_of_nexts() const
Number of next variables.
int GetSiblingAlternativeIndex(int node) const
Returns the index of the alternative set of the sibling of node.
bool HasNeighbors() const
int64_t OldNext(int64_t node) const
bool MakeChainInactive(int64_t before_chain, int64_t chain_end)
int64_t PrevNext(int64_t node) const
bool IsInactive(int64_t node) const
Returns true if node is inactive.
virtual void OnNodeInitialization()
virtual bool ConsiderAlternatives(int64_t base_index) const
const bool ignore_path_vars_
bool SwapActiveAndInactive(int64_t active, int64_t inactive)
Replaces active by inactive in the current path, making active inactive.
virtual bool RestartAtPathStartOnSynchronize()
virtual bool InitPosition() const
int64_t EndNode(int i) const
Returns the end node of the ith base node.
int PathClassFromStartNode(int64_t start_node) const
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
int CurrentNodePathStart(int64_t node) const
virtual bool MakeNeighbor()=0
int64_t Path(int64_t node) const
int CurrentNodePathEnd(int64_t node) const
bool MoveChain(int64_t before_chain, int64_t chain_end, int64_t destination)
virtual int64_t GetBaseNodeRestartPosition(int base_index)
int64_t StartNode(int i) const
Returns the start node of the ith base node.
virtual bool OnSamePathAsPreviousBase(int64_t base_index)
const int number_of_nexts_
bool IsPathEnd(int64_t node) const
void SetNext(int64_t from, int64_t to, int64_t path)
Sets 'to' to be the node after 'from' on the given path.
int64_t BaseNode(int i) const
Returns the ith base node of the operator.
bool ReverseChain(int64_t before_chain, int64_t after_chain, int64_t *chain_last)
A ChainRange is a range of Chains, committed or not.
int NumNodes() const
Instance-constant accessors.
const std::vector< int > & ChangedLoops() const
Returns the set of loops that were added by the change.
const std::vector< int > & ChangedPaths() const
void Commit()
Set the current state G1 as committed. See class comment for details.
ChainRange Chains(int path) const
Returns the current range of chains of path.
int NumPaths() const
Returns the number of paths (empty paths included).
void Revert()
Erase incremental changes. See class comment for details.
NodeRange Nodes(int path) const
Returns the current range of nodes of path.
int Path(int node) const
State-dependent accessors.
void ChangePath(int path, const std::vector< ChainBounds > &chains)
State modifiers.
int End(int path) const
Returns the end of a path.
PathState(int num_nodes, std::vector< int > path_start, std::vector< int > path_end)
int Start(int path) const
Returns the start of a path.
void ChangeLoops(const std::vector< int > &new_loops)
Describes the nodes that are newly loops in this change.
bool IsUncheckedSolutionLimitReached() override
bool CheckWithOffset(absl::Duration offset) override
void Init() override
This method is called when the search limit is initialized.
int64_t solutions() const
void Copy(const SearchLimit *limit) override
RegularLimit * MakeIdenticalClone() const
-— RelocateAndMakeActiveOperator --—
RelocateAndMakeActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
bool MakeNeighbor() override
~RelocateAndMakeActiveOperator() override
std::string DebugString() const override
--— RelocateAndMakeInactiveOperator --—
bool MakeNeighbor() override
~RelocateAndMakeInactiveOperator() override
RelocateAndMakeInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
Relocate(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, int64_t chain_length=1LL, bool single_path=false)
Relocate(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors, int64_t chain_length=1LL, bool single_path=false)
std::string DebugString() const override
Relocate(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, const std::string &name, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors, int64_t chain_length=1LL, bool single_path=false)
bool OnSamePathAsPreviousBase(int64_t) override
bool MakeNeighbor() override
virtual bool AtSolution()
virtual bool AcceptDelta(Assignment *delta, Assignment *deltadelta)
virtual bool SyncNeeded(Assignment *local_assignment)=0
virtual void Initialize(Assignment *assignment)=0
virtual void RegisterNewSolution(Assignment *assignment)=0
virtual void GetNextSolution(Assignment *assignment)=0
For the time being, Solver is neither MT_SAFE nor MT_HOT.
Search * ActiveSearch() const
Returns the active search, nullptr outside search.
bool IsLocalSearchProfilingEnabled() const
Returns whether we are profiling local search.
DecisionBuilder * MakeRestoreAssignment(Assignment *assignment)
LocalSearchOperator * MakeMoveTowardTargetOperator(const Assignment &target)
LocalSearchOperator * ConcatenateOperators(const std::vector< LocalSearchOperator * > &ops)
LocalSearchFilter * MakeVariableDomainFilter()
ABSL_MUST_USE_RESULT RegularLimit * MakeSolutionsLimit(int64_t solutions)
LocalSearchPhaseParameters * MakeLocalSearchPhaseParameters(IntVar *objective, LocalSearchOperator *ls_operator, DecisionBuilder *sub_decision_builder)
Local Search Phase Parameters.
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
Decision * balancing_decision() const
IntVarLocalSearchFilter * MakeSumObjectiveFilter(const std::vector< IntVar * > &vars, IndexEvaluator2 values, Solver::LocalSearchFilterBound filter_enum)
DecisionBuilder * MakeProfiledDecisionBuilderWrapper(DecisionBuilder *db)
Activates profiling on a decision builder.
bool UseFastLocalSearch() const
Returns true if fast local search is enabled.
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
Assignment * RunUncheckedLocalSearch(const Assignment *initial_solution, LocalSearchFilterManager *filter_manager, LocalSearchOperator *ls_operator, const std::vector< SearchMonitor * > &monitors, RegularLimit *limit, absl::flat_hash_set< IntVar * > *touched=nullptr)
const std::string & context() const
Gets the current context of the search.
LocalSearchStatistics GetLocalSearchStatistics() const
Returns detailed local search statistics.
DecisionBuilder * Compose(DecisionBuilder *db1, DecisionBuilder *db2)
std::string LocalSearchProfile() const
LocalSearchOperator * RandomConcatenateOperators(const std::vector< LocalSearchOperator * > &ops)
void SaveAndSetValue(T *adr, T val)
All-in-one SaveAndSetValue.
LocalSearchFilter * MakeAcceptFilter()
Local Search Filters.
SolutionPool * MakeDefaultSolutionPool()
Solution Pool.
LocalSearchOperator * MakeNeighborhoodLimit(LocalSearchOperator *op, int64_t limit)
DecisionBuilder * MakeLocalSearchPhase(Assignment *assignment, LocalSearchPhaseParameters *parameters)
@ LE
Move is accepted when the current objective value <= objective.Max.
@ GE
Move is accepted when the current objective value >= objective.Min.
LocalSearchFilter * MakeRejectFilter()
friend class SearchMonitor
std::function< int64_t(int64_t, int64_t, int64_t)> IndexEvaluator3
DecisionBuilder * MakeStoreAssignment(Assignment *assignment)
LocalSearchOperator * MultiArmedBanditConcatenateOperators(const std::vector< LocalSearchOperator * > &ops, double memory_coefficient, double exploration_coefficient, bool maximize)
LocalSearchOperator * MakeOperator(const std::vector< IntVar * > &vars, LocalSearchOperators op, std::function< const std::vector< int > &(int, int)> get_neighbors=nullptr)
Local Search Operators.
Assignment * MakeAssignment()
This method creates an empty assignment.
EvaluatorLocalSearchOperators
@ RELOCATE
Relocate neighborhood with length of 1 (see OROPT comment).
void SetSearchContext(Search *search, absl::string_view search_context)
ConstraintSolverParameters parameters() const
Stored Parameters.
Assignment * GetOrCreateLocalSearchState()
std::function< int64_t(int64_t, int64_t)> IndexEvaluator2
LocalSearchOperator * MakeRandomLnsOperator(const std::vector< IntVar * > &vars, int number_of_variables)
bool SolveAndCommit(DecisionBuilder *db, const std::vector< SearchMonitor * > &monitors)
const std::vector< ArcId > & ComputeSortedSubDagArcs(NodeId node)
void BuildGraph(int num_nodes)
--— SwapActiveOperator --—
~SwapActiveOperator() override
std::string DebugString() const override
SwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
bool MakeNeighbor() override
TSPLns(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int tsp_size)
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
bool MakeNeighbor() override
std::string DebugString() const override
--— TSP-based operators --—
bool MakeNeighbor() override
TSPOpt(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int chain_length)
std::string DebugString() const override
int64_t GetBaseNodeRestartPosition(int base_index) override
std::string DebugString() const override
TwoOpt(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors=nullptr)
bool MakeNeighbor() override
bool IsIncremental() const override
bool OnSamePathAsPreviousBase(int64_t) override
const std::string name
A name for logging purposes.
GurobiMPCallbackContext * context
#define MAKE_LOCAL_SEARCH_OPERATOR_WITH_NEIGHBORS(OperatorClass)
ABSL_FLAG(int, cp_local_search_sync_frequency, 16, "Frequency of checks for better solutions in the solution pool.")
std::vector< int64_t > synchronized_costs_
int64_t synchronized_sum_
const int primary_vars_size_
#define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass)
std::vector< int64_t > delta_costs_
ReverseView< Container > reversed_view(const Container &c)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
For infeasible and unbounded see Not checked if options check_solutions_if_inf_or_unbounded and the If options first_solution_only is false
problem is infeasible or unbounded (default).
In SWIG mode, we don't want anything besides these top-level includes.
void DeleteLocalSearchProfiler(LocalSearchProfiler *monitor)
int64_t CapAdd(int64_t x, int64_t y)
SubDagComputer::ArcId ArcId
void AcceptNeighbor(Search *search)
Notifies the search that a neighbor has been accepted by local search.
void AcceptUncheckedNeighbor(Search *search)
int64_t CapSub(int64_t x, int64_t y)
LinearExpr operator-(LinearExpr lhs, const LinearExpr &rhs)
LocalSearchOperator * MakeLocalSearchOperator(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
Operator Factories.
LocalSearchFilter * MakePathStateFilter(Solver *solver, std::unique_ptr< PathState > path_state, const std::vector< IntVar * > &nexts)
bool LocalOptimumReached(Search *search)
Returns true if a local optimum has been reached and cannot be improved.
LinearExpr operator+(LinearExpr lhs, const LinearExpr &rhs)
LocalSearchOperator * MakeLocalSearchOperatorWithNeighbors(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors)
void InstallLocalSearchProfiler(LocalSearchProfiler *monitor)
int64_t CapProd(int64_t x, int64_t y)
LocalSearchProfiler * BuildLocalSearchProfiler(Solver *solver)
static const int kUnassigned
--— Routing model --—
int MostSignificantBitPosition32(uint32_t n)
LocalSearchState::VariableDomainId VariableDomainId
LocalSearchFilter * MakeDimensionFilter(Solver *solver, std::unique_ptr< DimensionChecker > checker, const std::string &dimension_name)
bool AcceptDelta(Search *search, Assignment *delta, Assignment *deltadelta)
SubDagComputer::NodeId NodeId
trees with all degrees equal to
static int input(yyscan_t yyscanner)
std::optional< int64_t > end
Set of parameters used to configure how the neighnorhood is traversed.
std::function< int(int64_t)> start_empty_path_class
std::function< const std::vector< int > &(int, int)> get_neighbors
Callback returning neighbors of a node on a path starting at start_node.
bool accept_path_end_base
True if path ends should be considered when iterating over neighbors.
bool skip_locally_optimal_paths
int number_of_base_nodes
Number of nodes needed to define a neighbor.
static const int64_t kint64max
static const int64_t kint64min