27#include "absl/algorithm/container.h"
28#include "absl/base/nullability.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"
54 "Frequency of checks for better solutions in the solution pool.");
57 "Size of TSPs solved in the TSPOpt operator.");
60 "Size of TSPs solved in the TSPLns operator.");
72bool AcceptDelta(Search* search, Assignment* delta, Assignment* deltadelta);
82 CHECK(delta !=
nullptr);
83 VLOG(2) <<
DebugString() <<
"::MakeNextNeighbor(delta=("
85 << (deltadelta ? deltadelta->
DebugString() : std::string(
"nullptr"))
114 for (
int candidate : fragment_) {
127 if (index >= 0 && index <
Size()) {
128 fragment_.push_back(index);
139class SimpleLns :
public BaseLns {
141 SimpleLns(
const std::vector<IntVar*>& vars,
int number_of_variables)
142 :
BaseLns(vars), index_(0), number_of_variables_(number_of_variables) {
143 CHECK_GT(number_of_variables_, 0);
145 ~SimpleLns()
override {}
146 void InitFragments()
override { index_ = 0; }
147 bool NextFragment()
override;
148 std::string DebugString()
const override {
return "SimpleLns"; }
152 const int number_of_variables_;
155bool SimpleLns::NextFragment() {
156 const int size = Size();
158 for (
int i = index_;
i < index_ + number_of_variables_; ++
i) {
159 AppendToFragment(i % size);
171class RandomLns :
public BaseLns {
173 RandomLns(
const std::vector<IntVar*>& vars,
int number_of_variables,
175 : BaseLns(vars), rand_(seed), number_of_variables_(number_of_variables) {
176 CHECK_GT(number_of_variables_, 0);
177 CHECK_LE(number_of_variables_, Size());
179 ~RandomLns()
override {}
180 bool NextFragment()
override;
182 std::string DebugString()
const override {
return "RandomLns"; }
186 const int number_of_variables_;
189bool RandomLns::NextFragment() {
190 DCHECK_GT(Size(), 0);
191 for (
int i = 0;
i < number_of_variables_; ++
i) {
192 AppendToFragment(absl::Uniform<int>(rand_, 0, Size()));
199 const std::vector<IntVar*>& vars,
int number_of_variables) {
204 const std::vector<IntVar*>& vars,
int number_of_variables, int32_t seed) {
205 return RevAlloc(
new RandomLns(vars, number_of_variables, seed));
216 MoveTowardTargetLS(
const std::vector<IntVar*>& variables,
217 const std::vector<int64_t>& target_values)
219 target_(target_values),
223 variable_index_(Size() - 1) {
224 CHECK_EQ(target_values.size(), variables.size()) <<
"Illegal arguments.";
227 ~MoveTowardTargetLS()
override {}
229 std::string DebugString()
const override {
return "MoveTowardTargetLS"; }
233 bool MakeOneNeighbor()
override {
234 while (num_var_since_last_start_ < Size()) {
235 ++num_var_since_last_start_;
236 variable_index_ = (variable_index_ + 1) % Size();
237 const int64_t target_value = target_.at(variable_index_);
238 const int64_t current_value = OldValue(variable_index_);
239 if (current_value != target_value) {
240 SetValue(variable_index_, target_value);
248 void OnStart()
override {
260 CHECK_GE(variable_index_, 0);
261 CHECK_LT(variable_index_, Size());
262 num_var_since_last_start_ = 0;
266 const std::vector<int64_t> target_;
269 int64_t variable_index_;
272 int64_t num_var_since_last_start_;
278 typedef std::vector<IntVarElement> Elements;
281 std::vector<IntVar*> vars;
282 std::vector<int64_t> values;
285 for (
const auto& it : elements) {
286 vars.push_back(it.Var());
287 values.push_back(it.Value());
293 const std::vector<IntVar*>& variables,
294 const std::vector<int64_t>& target_values) {
295 return RevAlloc(
new MoveTowardTargetLS(variables, target_values));
306 const int size =
Size();
307 while (index_ < size) {
321class IncrementValue :
public ChangeValue {
323 explicit IncrementValue(
const std::vector<IntVar*>& vars)
324 : ChangeValue(vars) {}
325 ~IncrementValue()
override {}
326 int64_t ModifyValue(int64_t, int64_t value)
override {
return value + 1; }
328 std::string DebugString()
const override {
return "IncrementValue"; }
335 explicit DecrementValue(
const std::vector<IntVar*>& vars)
336 : ChangeValue(vars) {}
337 ~DecrementValue()
override {}
338 int64_t ModifyValue(int64_t, int64_t value)
override {
return value - 1; }
340 std::string DebugString()
const override {
return "DecrementValue"; }
347 std::function<const std::vector<int>&(int, int)>;
351template <
bool ignore_path_vars>
355 const std::vector<IntVar*>& secondary_vars,
356 std::function<
int(int64_t)> start_empty_path_class,
360 (get_incoming_neighbors == nullptr &&
361 get_outgoing_neighbors == nullptr)
366 std::move(start_empty_path_class),
367 std::move(get_incoming_neighbors),
368 std::move(get_outgoing_neighbors)),
394template <
bool ignore_path_vars>
396 const int64_t node0 = this->
BaseNode(0);
397 int64_t before_chain = node0;
398 int64_t after_chain = -1;
401 if (neighbor < 0 || this->
IsInactive(neighbor))
return false;
407 if (this->
IsPathEnd(neighbor))
return false;
409 after_chain = this->
Next(neighbor);
413 before_chain = this->
Prev(neighbor);
421 if (last_base_ != node0 || last_ == -1 || this->
HasNeighbors()) {
428 last_ = this->
Next(before_chain);
430 if (this->
ReverseChain(before_chain, after_chain, &chain_last)
433 && last_ != chain_last) {
439 DCHECK_EQ(before_chain, node0);
440 const int64_t to_move = this->
Next(last_);
441 DCHECK_EQ(this->
Next(to_move), after_chain);
442 return this->
MoveChain(last_, to_move, before_chain);
446 Solver* solver,
const std::vector<IntVar*>& vars,
447 const std::vector<IntVar*>& secondary_vars,
448 std::function<
int(int64_t)> start_empty_path_class,
451 if (secondary_vars.empty()) {
453 vars, secondary_vars, std::move(start_empty_path_class),
454 std::move(get_incoming_neighbors), std::move(get_outgoing_neighbors)));
457 vars, secondary_vars, std::move(start_empty_path_class),
458 std::move(get_incoming_neighbors), std::move(get_outgoing_neighbors)));
463template <
bool ignore_path_vars>
467 const std::vector<IntVar*>& secondary_vars, absl::string_view name,
468 std::function<
int(int64_t)> start_empty_path_class,
471 bool single_path =
false)
473 vars, secondary_vars,
474 (get_incoming_neighbors == nullptr &&
475 get_outgoing_neighbors == nullptr)
479 false,
std::move(start_empty_path_class),
480 chain_length == 1 ?
std::move(get_incoming_neighbors) : nullptr,
481 std::move(get_outgoing_neighbors)),
482 chain_length_(chain_length),
483 single_path_(single_path),
484 name_(
absl::StrCat(name,
"<", chain_length,
">")) {
485 CHECK_GT(chain_length_, 0);
500 const int64_t chain_length_;
501 const bool single_path_;
502 const std::string name_;
505template <
bool ignore_path_vars>
507 const auto do_move = [
this](int64_t before_chain, int64_t destination) {
509 int64_t chain_end = before_chain;
510 for (
int i = 0; i < chain_length_; ++i) {
511 if (this->
IsPathEnd(chain_end) || chain_end == destination)
return false;
512 chain_end = this->
Next(chain_end);
515 this->
MoveChain(before_chain, chain_end, destination);
517 const int64_t node0 = this->
BaseNode(0);
520 if (neighbor < 0 || this->
IsInactive(neighbor))
return false;
522 return do_move(this->
Prev(neighbor),
525 DCHECK_EQ(chain_length_, 1);
530 <<
"Path starts have no incoming neighbors.";
531 return do_move(this->
Prev(neighbor),
535 return do_move(node0, this->
BaseNode(1));
539 Solver* solver,
const std::vector<IntVar*>& vars,
540 const std::vector<IntVar*>& secondary_vars,
541 std::function<
int(int64_t)> start_empty_path_class,
544 bool single_path,
const std::string& name) {
545 if (secondary_vars.empty()) {
547 vars, secondary_vars, name, std::move(start_empty_path_class),
548 std::move(get_incoming_neighbors), std::move(get_outgoing_neighbors),
549 chain_length, single_path));
552 vars, secondary_vars, name, std::move(start_empty_path_class),
553 std::move(get_incoming_neighbors), std::move(get_outgoing_neighbors),
554 chain_length, single_path));
559template <
bool ignore_path_vars>
563 const std::vector<IntVar*>& secondary_vars,
564 std::function<
int(int64_t)> start_empty_path_class,
568 (get_incoming_neighbors == nullptr &&
569 get_outgoing_neighbors == nullptr)
574 std::move(start_empty_path_class),
575 std::move(get_incoming_neighbors),
576 std::move(get_outgoing_neighbors)) {}
583template <
bool ignore_path_vars>
585 const int64_t node0 = this->
BaseNode(0);
588 if (neighbor < 0 || this->
IsInactive(neighbor))
return false;
594 <<
"Path starts have no incoming neighbors.";
602 Solver* solver,
const std::vector<IntVar*>& vars,
603 const std::vector<IntVar*>& secondary_vars,
604 std::function<
int(int64_t)> start_empty_path_class,
607 if (secondary_vars.empty()) {
609 vars, secondary_vars, std::move(start_empty_path_class),
610 std::move(get_incoming_neighbors), std::move(get_outgoing_neighbors)));
613 vars, secondary_vars, std::move(start_empty_path_class),
614 std::move(get_incoming_neighbors), std::move(get_outgoing_neighbors)));
619template <
bool ignore_path_vars>
622 Cross(
const std::vector<IntVar*>& vars,
623 const std::vector<IntVar*>& secondary_vars,
624 std::function<
int(int64_t)> start_empty_path_class,
628 (get_incoming_neighbors == nullptr &&
629 get_outgoing_neighbors == nullptr)
634 std::move(start_empty_path_class),
635 std::move(get_incoming_neighbors),
636 std::move(get_outgoing_neighbors)) {}
643template <
bool ignore_path_vars>
645 const int64_t start0 = this->
StartNode(0);
647 const int64_t node0 = this->
BaseNode(0);
649 if (node0 == start0)
return false;
650 bool cross_path_starts =
false;
653 if (neighbor < 0)
return false;
654 cross_path_starts = outgoing;
665 node1 = cross_path_starts ? this->
Prev(neighbor) : this->
Next(neighbor);
669 cross_path_starts = start0 < start1;
671 if (start1 == start0 || node1 == start1)
return false;
674 if (cross_path_starts) {
683 const int first1 = this->
Next(start1);
685 moved |= this->
MoveChain(start0, node0, start1);
718 Solver* solver,
const std::vector<IntVar*>& vars,
719 const std::vector<IntVar*>& secondary_vars,
720 std::function<
int(int64_t)> start_empty_path_class,
723 if (secondary_vars.empty()) {
725 vars, secondary_vars, std::move(start_empty_path_class),
726 std::move(get_incoming_neighbors), std::move(get_outgoing_neighbors)));
729 vars, secondary_vars, std::move(start_empty_path_class),
730 std::move(get_incoming_neighbors), std::move(get_outgoing_neighbors)));
736template <
bool ignore_path_vars>
740 const std::vector<IntVar*>& vars,
741 const std::vector<IntVar*>& secondary_vars,
int number_of_base_nodes,
742 std::function<
int(int64_t)> start_empty_path_class,
746 number_of_base_nodes, false, false,
747 std::move(start_empty_path_class),
748 std::move(get_incoming_neighbors),
749 std::move(get_outgoing_neighbors)),
765template <
bool ignore_path_vars>
767 for (
int i = 0; i < this->Size(); ++i) {
768 if (this->IsInactive(i)) {
773 inactive_node_ = this->Size();
776template <
bool ignore_path_vars>
778 while (inactive_node_ < this->
Size()) {
792template <
bool ignore_path_vars>
797 const std::vector<IntVar*>& secondary_vars,
798 std::function<
int(int64_t)> start_empty_path_class,
802 vars, secondary_vars, 1,
std::move(start_empty_path_class),
803 std::move(get_incoming_neighbors),
804 std::move(get_outgoing_neighbors)) {}
808 std::string
DebugString()
const override {
return "MakeActiveOperator"; }
811template <
bool ignore_path_vars>
819 Solver* solver,
const std::vector<IntVar*>& vars,
820 const std::vector<IntVar*>& secondary_vars,
821 std::function<
int(int64_t)> start_empty_path_class,
824 if (secondary_vars.empty()) {
826 vars, secondary_vars, std::move(start_empty_path_class),
827 std::move(get_incoming_neighbors), std::move(get_outgoing_neighbors)));
830 vars, secondary_vars, std::move(start_empty_path_class),
831 std::move(get_incoming_neighbors), std::move(get_outgoing_neighbors)));
836template <
bool ignore_path_vars>
841 const std::vector<IntVar*>& vars,
842 const std::vector<IntVar*>& secondary_vars,
843 std::function<
int(int64_t)> start_empty_path_class)
845 vars, secondary_vars, 2,
std::move(start_empty_path_class)) {}
848 const int64_t before_node_to_move = this->
BaseNode(1);
849 const int64_t node = this->
Next(before_node_to_move);
856 return "RelocateAndMakeActiveOperator";
861 Solver* solver,
const std::vector<IntVar*>& vars,
862 const std::vector<IntVar*>& secondary_vars,
863 std::function<
int(int64_t)> start_empty_path_class) {
864 if (secondary_vars.empty()) {
866 vars, secondary_vars, std::move(start_empty_path_class)));
869 vars, secondary_vars, std::move(start_empty_path_class)));
874template <
bool ignore_path_vars>
879 const std::vector<IntVar*>& vars,
880 const std::vector<IntVar*>& secondary_vars,
881 std::function<
int(int64_t)> start_empty_path_class)
883 vars, secondary_vars, 3,
std::move(start_empty_path_class)) {}
891 return "ExchangeAndMakeActiveOperator";
896 return base_index == 2;
901 Solver* solver,
const std::vector<IntVar*>& vars,
902 const std::vector<IntVar*>& secondary_vars,
903 std::function<
int(int64_t)> start_empty_path_class) {
904 if (secondary_vars.empty()) {
906 vars, secondary_vars, std::move(start_empty_path_class)));
909 vars, secondary_vars, std::move(start_empty_path_class)));
914template <
bool ignore_path_vars>
919 const std::vector<IntVar*>& vars,
920 const std::vector<IntVar*>& secondary_vars,
921 std::function<
int(int64_t)> start_empty_path_class)
923 vars, secondary_vars, 2,
std::move(start_empty_path_class)) {}
926 return (base_index == 1) ? this->
Prev(this->
EndNode(1))
930 const int64_t end_node0 = this->
Prev(this->
EndNode(0));
931 const int64_t end_node1 = this->
BaseNode(1);
932 if (end_node0 == end_node1 || end_node1 != this->
Prev(this->
EndNode(1))) {
937 DCHECK_NE(start_node0, start_node1);
938 return this->
SwapNodes(end_node0, end_node1) &&
939 this->
SwapNodes(start_node0, start_node1) &&
943 return "ExchangePathEndsAndMakeActiveOperator";
948 Solver* solver,
const std::vector<IntVar*>& vars,
949 const std::vector<IntVar*>& secondary_vars,
950 std::function<
int(int64_t)> start_empty_path_class) {
951 if (secondary_vars.empty()) {
954 vars, secondary_vars, std::move(start_empty_path_class)));
957 vars, secondary_vars, std::move(start_empty_path_class)));
962template <
bool ignore_path_vars>
967 const std::vector<IntVar*>& vars,
968 const std::vector<IntVar*>& secondary_vars,
969 std::function<
int(int64_t)> start_empty_path_class)
971 vars, secondary_vars, 2,
std::move(start_empty_path_class)) {}
976 return "MakeActiveAndRelocateOperator";
980template <
bool ignore_path_vars>
982 const int64_t before_chain = this->
BaseNode(1);
983 const int64_t chain_end = this->
Next(before_chain);
984 const int64_t destination = this->
BaseNode(0);
986 this->
MoveChain(before_chain, chain_end, destination) &&
991 Solver* solver,
const std::vector<IntVar*>& vars,
992 const std::vector<IntVar*>& secondary_vars,
993 std::function<
int(int64_t)> start_empty_path_class) {
994 if (secondary_vars.empty()) {
996 vars, secondary_vars, std::move(start_empty_path_class)));
999 vars, secondary_vars, std::move(start_empty_path_class)));
1004template <
bool ignore_path_vars>
1008 const std::vector<IntVar*>& secondary_vars,
1009 std::function<
int(int64_t)> start_empty_path_class)
1010 :
PathOperator<ignore_path_vars>(vars, secondary_vars, 1, true, false,
1011 std::move(start_empty_path_class),
1012 nullptr, nullptr) {}
1019 std::string
DebugString()
const override {
return "MakeInactiveOperator"; }
1023 Solver* solver,
const std::vector<IntVar*>& vars,
1024 const std::vector<IntVar*>& secondary_vars,
1025 std::function<
int(int64_t)> start_empty_path_class) {
1026 if (secondary_vars.empty()) {
1028 vars, secondary_vars, std::move(start_empty_path_class)));
1031 vars, secondary_vars, std::move(start_empty_path_class)));
1036template <
bool ignore_path_vars>
1040 const std::vector<IntVar*>& vars,
1041 const std::vector<IntVar*>& secondary_vars,
1042 std::function<
int(int64_t)> start_empty_path_class)
1043 :
PathOperator<ignore_path_vars>(vars, secondary_vars, 2, true, false,
1044 std::move(start_empty_path_class),
1045 nullptr, nullptr) {}
1048 const int64_t destination = this->
BaseNode(1);
1049 const int64_t before_to_move = this->
BaseNode(0);
1050 const int64_t node_to_inactivate = this->
Next(destination);
1051 if (node_to_inactivate == before_to_move ||
1056 const int64_t node = this->
Next(before_to_move);
1058 this->
MoveChain(before_to_move, node, destination);
1062 return "RelocateAndMakeInactiveOperator";
1067 Solver* solver,
const std::vector<IntVar*>& vars,
1068 const std::vector<IntVar*>& secondary_vars,
1069 std::function<
int(int64_t)> start_empty_path_class) {
1070 if (secondary_vars.empty()) {
1072 vars, secondary_vars, std::move(start_empty_path_class)));
1075 vars, secondary_vars, std::move(start_empty_path_class)));
1080template <
bool ignore_path_vars>
1084 const std::vector<IntVar*>& secondary_vars,
1085 std::function<
int(int64_t)> start_empty_path_class)
1086 :
PathOperator<ignore_path_vars>(vars, secondary_vars, 2,
1089 std::move(start_empty_path_class),
1090 nullptr, nullptr) {}
1093 const int64_t chain_end = this->
BaseNode(1);
1095 !this->
Var(chain_end)->Contains(chain_end)) {
1105 return "MakeChainInactiveOperator";
1117 return (base_index == 0) ? this->
StartNode(base_index)
1123 Solver* solver,
const std::vector<IntVar*>& vars,
1124 const std::vector<IntVar*>& secondary_vars,
1125 std::function<
int(int64_t)> start_empty_path_class) {
1126 if (secondary_vars.empty()) {
1128 vars, secondary_vars, std::move(start_empty_path_class)));
1131 vars, secondary_vars, std::move(start_empty_path_class)));
1136template <
bool ignore_path_vars>
1141 const std::vector<IntVar*>& secondary_vars,
1142 std::function<
int(int64_t)> start_empty_path_class)
1144 vars, secondary_vars, 1,
std::move(start_empty_path_class)) {}
1152 std::string
DebugString()
const override {
return "SwapActiveOperator"; }
1156 Solver* solver,
const std::vector<IntVar*>& vars,
1157 const std::vector<IntVar*>& secondary_vars,
1158 std::function<
int(int64_t)> start_empty_path_class) {
1159 if (secondary_vars.empty()) {
1161 vars, secondary_vars, std::move(start_empty_path_class)));
1164 vars, secondary_vars, std::move(start_empty_path_class)));
1169template <
bool ignore_path_vars>
1174 const std::vector<IntVar*>& secondary_vars,
1175 std::function<
int(int64_t)> start_empty_path_class,
1178 vars, secondary_vars, 2,
std::move(start_empty_path_class)),
1179 last_before_chain_(-1),
1180 last_chain_end_(-1),
1181 current_chain_size_(0),
1182 max_chain_size_(max_chain_size) {
1183 DCHECK_GE(max_chain_size_, 1);
1191 last_chain_end_ = -1;
1192 current_chain_size_ = 0;
1207 std::string
DebugString()
const override {
return "SwapActiveChainOperator"; }
1211 last_chain_end_ = -1;
1212 current_chain_size_ = 0;
1215 int64_t last_before_chain_;
1216 int64_t last_chain_end_;
1217 int current_chain_size_;
1218 const int max_chain_size_;
1221template <
bool ignore_path_vars>
1223 const int64_t before_chain = this->
BaseNode(0);
1224 const int64_t chain_end = this->
BaseNode(1);
1225 if (last_before_chain_ != before_chain || last_chain_end_ == -1) {
1227 last_before_chain_ = before_chain;
1229 if (!this->
IsPathEnd(chain_end) && before_chain != chain_end &&
1232 ++current_chain_size_;
1235 last_chain_end_ = -1;
1236 current_chain_size_ = 0;
1240 if (current_chain_size_ >= max_chain_size_) {
1243 current_chain_size_ = 0;
1246 if (!this->
IsPathEnd(last_chain_end_) &&
1248 ++current_chain_size_;
1251 last_chain_end_ = -1;
1252 current_chain_size_ = 0;
1257 Solver* solver,
const std::vector<IntVar*>& vars,
1258 const std::vector<IntVar*>& secondary_vars,
1259 std::function<
int(int64_t)> start_empty_path_class,
int max_chain_size) {
1260 if (secondary_vars.empty()) {
1262 vars, secondary_vars, std::move(start_empty_path_class),
1266 vars, secondary_vars, std::move(start_empty_path_class), max_chain_size));
1271template <
bool ignore_path_vars>
1276 const std::vector<IntVar*>& secondary_vars,
1277 std::function<
int(int64_t)> start_empty_path_class)
1279 vars, secondary_vars, 2,
std::move(start_empty_path_class)) {}
1282 const int64_t base0 = this->
BaseNode(0);
1283 const int64_t base1 = this->
BaseNode(1);
1284 if (this->
Next(base0) == base1) {
1292 return "ExtendedSwapActiveOperator";
1297 Solver* solver,
const std::vector<IntVar*>& vars,
1298 const std::vector<IntVar*>& secondary_vars,
1299 std::function<
int(int64_t)> start_empty_path_class) {
1300 if (secondary_vars.empty()) {
1302 vars, secondary_vars, std::move(start_empty_path_class)));
1305 vars, secondary_vars, std::move(start_empty_path_class)));
1310template <
bool ignore_path_vars>
1313 TSPOpt(
const std::vector<IntVar*>& vars,
1314 const std::vector<IntVar*>& secondary_vars,
1322 std::vector<std::vector<int64_t>> cost_;
1324 hamiltonian_path_solver_;
1326 const int chain_length_;
1329template <
bool ignore_path_vars>
1331 const std::vector<IntVar*>& secondary_vars,
1334 :
PathOperator<ignore_path_vars>(vars, secondary_vars, 1, true, false,
1335 nullptr, nullptr, nullptr),
1336 hamiltonian_path_solver_(cost_),
1337 evaluator_(
std::move(evaluator)),
1338 chain_length_(chain_length) {}
1340template <
bool ignore_path_vars>
1342 std::vector<int64_t> nodes;
1343 int64_t chain_end = this->
BaseNode(0);
1344 for (
int i = 0; i < chain_length_ + 1; ++i) {
1345 nodes.push_back(chain_end);
1349 chain_end = this->
Next(chain_end);
1351 if (nodes.size() <= 3) {
1355 const int size = nodes.size() - 1;
1357 for (
int i = 0; i < size; ++i) {
1358 cost_[i].resize(size);
1359 cost_[i][0] = evaluator_(nodes[i], nodes[size], chain_path);
1360 for (
int j = 1; j < size; ++j) {
1361 cost_[i][j] = evaluator_(nodes[i], nodes[j], chain_path);
1364 hamiltonian_path_solver_.ChangeCostMatrix(cost_);
1365 std::vector<PathNodeIndex> path;
1366 hamiltonian_path_solver_.TravelingSalesmanPath(&path);
1367 CHECK_EQ(size + 1, path.size());
1368 for (
int i = 0; i < size - 1; ++i) {
1369 this->
SetNext(nodes[path[i]], nodes[path[i + 1]], chain_path);
1371 this->
SetNext(nodes[path[size - 1]], nodes[size], chain_path);
1376 const std::vector<IntVar*>& vars,
1377 const std::vector<IntVar*>& secondary_vars,
1380 if (secondary_vars.empty()) {
1382 vars, secondary_vars, std::move(evaluator), chain_length));
1385 vars, secondary_vars, std::move(evaluator), chain_length));
1388template <
bool ignore_path_vars>
1391 TSPLns(
const std::vector<IntVar*>& vars,
1392 const std::vector<IntVar*>& secondary_vars,
1405 has_long_enough_paths_ = this->
Size() != 0;
1408 std::vector<std::vector<int64_t>> cost_;
1409 HamiltonianPathSolver<int64_t, std::vector<std::vector<int64_t>>>
1410 hamiltonian_path_solver_;
1412 const int tsp_size_;
1414 bool has_long_enough_paths_;
1417template <
bool ignore_path_vars>
1419 const std::vector<IntVar*>& secondary_vars,
1422 :
PathOperator<ignore_path_vars>(vars, secondary_vars, 1, true, false,
1423 nullptr, nullptr, nullptr),
1424 hamiltonian_path_solver_(cost_),
1425 evaluator_(
std::move(evaluator)),
1426 tsp_size_(tsp_size),
1428 has_long_enough_paths_(true) {
1429 CHECK_GE(tsp_size_, 0);
1430 cost_.resize(tsp_size_);
1431 for (
int i = 0; i < tsp_size_; ++i) {
1432 cost_[i].resize(tsp_size_);
1436template <
bool ignore_path_vars>
1438 while (has_long_enough_paths_) {
1439 has_long_enough_paths_ =
false;
1443 this->
Var(0)->solver()->TopPeriodicCheck();
1448template <
bool ignore_path_vars>
1450 const int64_t base_node = this->
BaseNode(0);
1451 std::vector<int64_t> nodes;
1453 node = this->
Next(node)) {
1454 nodes.push_back(node);
1456 if (nodes.size() <= tsp_size_) {
1459 has_long_enough_paths_ =
true;
1462 absl::flat_hash_set<int64_t> breaks_set;
1464 breaks_set.insert(base_node);
1465 CHECK(!nodes.empty());
1466 while (breaks_set.size() < tsp_size_) {
1467 breaks_set.insert(nodes[absl::Uniform<int>(rand_, 0, nodes.size())]);
1469 CHECK_EQ(breaks_set.size(), tsp_size_);
1474 std::vector<int> breaks;
1475 std::vector<int64_t> meta_node_costs;
1478 int64_t node_path = this->
Path(node);
1480 int64_t next = this->
Next(node);
1481 if (breaks_set.contains(node)) {
1482 breaks.push_back(node);
1483 meta_node_costs.push_back(cost);
1486 cost =
CapAdd(cost, evaluator_(node, next, node_path));
1490 meta_node_costs[0] += cost;
1491 CHECK_EQ(breaks.size(), tsp_size_);
1493 CHECK_EQ(meta_node_costs.size(), tsp_size_);
1494 for (
int i = 0; i < tsp_size_; ++i) {
1497 evaluator_(breaks[i], this->
Next(breaks[tsp_size_ - 1]), node_path));
1498 for (
int j = 1; j < tsp_size_; ++j) {
1500 CapAdd(meta_node_costs[i],
1501 evaluator_(breaks[i], this->
Next(breaks[j - 1]), node_path));
1506 hamiltonian_path_solver_.ChangeCostMatrix(cost_);
1507 std::vector<PathNodeIndex> path;
1508 hamiltonian_path_solver_.TravelingSalesmanPath(&path);
1509 bool nochange =
true;
1510 for (
int i = 0; i < path.size() - 1; ++i) {
1519 CHECK_EQ(0, path[path.size() - 1]);
1520 for (
int i = 0; i < tsp_size_ - 1; ++i) {
1521 this->
SetNext(breaks[path[i]], this->
OldNext(breaks[path[i + 1] - 1]),
1524 this->
SetNext(breaks[path[tsp_size_ - 1]],
1525 this->
OldNext(breaks[tsp_size_ - 1]), node_path);
1530 const std::vector<IntVar*>& vars,
1531 const std::vector<IntVar*>& secondary_vars,
1534 if (secondary_vars.empty()) {
1536 new TSPLns<true>(vars, secondary_vars, std::move(evaluator), tsp_size));
1539 new TSPLns<false>(vars, secondary_vars, std::move(evaluator), tsp_size));
1550template <
bool ignore_path_vars>
1563 const std::vector<int>&
Neighbors(
int index)
const;
1565 virtual std::string
DebugString()
const {
return "NearestNeighbors"; }
1568 void ComputeNearest(
int row, absl::Span<const int> path);
1570 std::vector<std::vector<int>> neighbors_;
1576template <
bool ignore_path_vars>
1580 : neighbors_(path_operator.number_of_nexts()),
1581 evaluator_(
std::move(evaluator)),
1582 path_operator_(path_operator),
1585template <
bool ignore_path_vars>
1587 absl::Span<const int> path) {
1588 for (
int node : path) {
1589 if (node < path_operator_.number_of_nexts()) ComputeNearest(node, path);
1593template <
bool ignore_path_vars>
1596 return neighbors_[index];
1599template <
bool ignore_path_vars>
1600void NearestNeighbors<ignore_path_vars>::ComputeNearest(
1601 int row, absl::Span<const int> path_nodes) {
1603 const int path = path_operator_.Path(row);
1604 const IntVar* var = path_operator_.
Var(row);
1605 using ValuedIndex = std::pair<int64_t ,
int >;
1606 std::vector<ValuedIndex> neighbors;
1607 for (
int index : path_nodes) {
1608 if (!var->
Contains(index))
continue;
1609 neighbors.push_back({evaluator_(row, index, path), index});
1611 const int neighbors_size = neighbors.size();
1612 if (neighbors_size > size_) {
1613 std::nth_element(neighbors.begin(), neighbors.begin() + size_ - 1,
1618 neighbors_[row].clear();
1619 for (
int i = 0;
i < std::min(size_, neighbors_size); ++
i) {
1620 neighbors_[row].push_back(neighbors[i].second);
1622 std::sort(neighbors_[row].
begin(), neighbors_[row].
end());
1625template <
bool ignore_path_vars>
1629 const std::vector<IntVar*>& secondary_vars,
1639 bool GetBestOut(int64_t in_i, int64_t in_j, int64_t* out, int64_t* gain);
1643 absl::flat_hash_set<int64_t> marked_;
1645 std::vector<int> old_path_starts_;
1652template <
bool ignore_path_vars>
1654 const std::vector<IntVar*>& vars,
1655 const std::vector<IntVar*>& secondary_vars,
1657 :
PathOperator<ignore_path_vars>(vars, secondary_vars, 1, true, false,
1658 nullptr, nullptr, nullptr),
1659 evaluator_(evaluator),
1660 neighbors_(evaluator, *this, 5 + 1),
1665template <
bool ignore_path_vars>
1667 absl::flat_hash_set<int> touched_paths;
1668 for (
int i = 0; i < this->number_of_nexts(); ++i) {
1669 if (this->IsPathStart(i)) {
1670 for (
int node = i; !this->IsPathEnd(node); node = this->
Next(node)) {
1671 if (i != old_path_starts_[node]) {
1672 touched_paths.insert(old_path_starts_[node]);
1673 touched_paths.insert(i);
1674 old_path_starts_[node] = i;
1677 }
else if (this->
Next(i) == i) {
1678 touched_paths.insert(old_path_starts_[i]);
1679 old_path_starts_[
i] = -1;
1682 for (
int touched_path_start : touched_paths) {
1683 if (touched_path_start == -1)
continue;
1684 std::vector<int> path;
1685 int node = touched_path_start;
1686 while (!this->IsPathEnd(node)) {
1687 path.push_back(node);
1688 node = this->
Next(node);
1690 path.push_back(node);
1691 neighbors_.Initialize(path);
1695template <
bool ignore_path_vars>
1699 int64_t path = this->
Path(node);
1700 int64_t
base = node;
1701 int64_t next = this->
Next(node);
1702 if (this->
IsPathEnd(next))
return false;
1705 marked_.insert(node);
1707 if (!GetBestOut(node, next, &out, &gain))
return false;
1708 marked_.insert(next);
1709 marked_.insert(out);
1710 const int64_t node1 = out;
1711 if (this->
IsPathEnd(node1))
return false;
1712 const int64_t next1 = this->
Next(node1);
1713 if (this->
IsPathEnd(next1))
return false;
1714 if (!GetBestOut(node1, next1, &out, &gain))
return false;
1715 marked_.insert(next1);
1716 marked_.insert(out);
1721 const int64_t next_out = this->
Next(out);
1722 const int64_t in_cost = evaluator_(node, next_out, path);
1723 const int64_t out_cost = evaluator_(out, next_out, path);
1724 if (
CapAdd(
CapSub(gain, in_cost), out_cost) > 0)
return true;
1726 if (this->
IsPathEnd(node))
return false;
1728 if (this->
IsPathEnd(next))
return false;
1731 while (GetBestOut(node, next, &out, &gain)) {
1732 marked_.insert(next);
1733 marked_.insert(out);
1735 if (this->
Next(base) == out ||
1743 LOG(ERROR) <<
"ReverseChain failed: " <<
base <<
" " << out;
1745 node = this->
Next(node)) {
1746 LOG(ERROR) <<
"node: " << node;
1748 LOG(ERROR) <<
"node: " << node;
1752 const int64_t in_cost = evaluator_(
base, chain_last, path);
1753 const int64_t out_cost = evaluator_(chain_last, out, path);
1769template <
bool ignore_path_vars>
1770bool LinKernighan<ignore_path_vars>::GetBestOut(int64_t in_i, int64_t in_j,
1771 int64_t* out, int64_t* gain) {
1772 int64_t best_gain = std::numeric_limits<int64_t>::min();
1773 const int64_t path = this->Path(in_i);
1774 const int64_t out_cost = evaluator_(in_i, in_j, path);
1775 const int64_t current_gain =
CapAdd(*gain, out_cost);
1776 for (
int next : neighbors_.Neighbors(in_j)) {
1777 if (next != in_j && next != this->
Next(in_j) && !marked_.contains(in_j) &&
1778 !marked_.contains(next)) {
1779 const int64_t in_cost = evaluator_(in_j, next, path);
1780 const int64_t new_gain =
CapSub(current_gain, in_cost);
1781 if (new_gain > 0 && best_gain < new_gain) {
1783 best_gain = new_gain;
1788 return (best_gain > std::numeric_limits<int64_t>::min());
1792 Solver* solver,
const std::vector<IntVar*>& vars,
1793 const std::vector<IntVar*>& secondary_vars,
1795 if (secondary_vars.empty()) {
1797 std::move(evaluator), topt));
1800 std::move(evaluator), topt));
1805template <
bool ignore_path_vars>
1809 const std::vector<IntVar*>& secondary_vars,
int number_of_chunks,
1810 int chunk_size,
bool unactive_fragments)
1811 :
PathOperator<ignore_path_vars>(vars, secondary_vars, number_of_chunks,
1812 true, true, nullptr, nullptr, nullptr),
1813 number_of_chunks_(number_of_chunks),
1814 chunk_size_(chunk_size),
1815 unactive_fragments_(unactive_fragments) {
1816 CHECK_GE(chunk_size_, 0);
1825 inline bool ChainsAreFullPaths()
const {
return chunk_size_ == 0; }
1826 void DeactivateChain(int64_t node);
1827 void DeactivateUnactives();
1829 const int number_of_chunks_;
1830 const int chunk_size_;
1831 const bool unactive_fragments_;
1834template <
bool ignore_path_vars>
1836 if (ChainsAreFullPaths()) {
1840 for (
int i = 0; i < number_of_chunks_; ++i) {
1844 for (
int i = 0; i < number_of_chunks_; ++i) {
1845 DeactivateChain(this->
BaseNode(i));
1847 DeactivateUnactives();
1851template <
bool ignore_path_vars>
1852void PathLns<ignore_path_vars>::DeactivateChain(int64_t node) {
1853 for (
int i = 0, current = node;
1854 (ChainsAreFullPaths() || i < chunk_size_) && !this->IsPathEnd(current);
1855 ++i, current = this->
Next(current)) {
1856 this->Deactivate(current);
1857 if constexpr (!ignore_path_vars) {
1858 this->Deactivate(this->number_of_nexts_ + current);
1863template <
bool ignore_path_vars>
1864void PathLns<ignore_path_vars>::DeactivateUnactives() {
1865 if (unactive_fragments_) {
1866 for (
int i = 0;
i < this->Size(); ++
i) {
1867 if (this->IsInactive(i)) {
1868 this->Deactivate(i);
1869 if constexpr (!ignore_path_vars) {
1870 this->Deactivate(this->number_of_nexts_ + i);
1878 const std::vector<IntVar*>& vars,
1879 const std::vector<IntVar*>& secondary_vars,
1880 int number_of_chunks,
int chunk_size,
1881 bool unactive_fragments) {
1882 if (secondary_vars.empty()) {
1884 number_of_chunks, chunk_size,
1885 unactive_fragments));
1888 vars, secondary_vars, number_of_chunks, chunk_size, unactive_fragments));
1896 : operator_(op), limit_(limit), next_neighborhood_calls_(0) {
1897 CHECK(op !=
nullptr);
1902 next_neighborhood_calls_ = 0;
1903 operator_->Start(assignment);
1907 if (next_neighborhood_calls_ >= limit_) {
1910 ++next_neighborhood_calls_;
1911 return operator_->MakeNextNeighbor(delta, deltadelta);
1914 bool HoldsDelta()
const override {
return operator_->HoldsDelta(); }
1916 std::string
DebugString()
const override {
return "NeighborhoodLimit"; }
1920 const int64_t limit_;
1921 int64_t next_neighborhood_calls_;
1934 CompoundOperator(std::vector<LocalSearchOperator*> operators,
1935 std::function<int64_t(
int,
int)> evaluator);
1936 ~CompoundOperator()
override {}
1937 void EnterSearch()
override;
1938 void Reset()
override;
1939 void Start(
const Assignment* assignment)
override;
1940 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta)
override;
1941 bool HasFragments()
const override {
return has_fragments_; }
1942 bool HoldsDelta()
const override {
return true; }
1944 std::string DebugString()
const override {
1945 return operators_.empty()
1947 : operators_[operator_indices_[index_]]->DebugString();
1949 const LocalSearchOperator* Self()
const override {
1950 return operators_.empty() ? this
1951 : operators_[operator_indices_[index_]]->Self();
1955 class OperatorComparator {
1957 OperatorComparator(std::function<int64_t(
int,
int)> evaluator,
1958 int active_operator)
1959 : evaluator_(std::move(evaluator)), active_operator_(active_operator) {}
1960 bool operator()(
int lhs,
int rhs)
const {
1961 const int64_t lhs_value = Evaluate(lhs);
1962 const int64_t rhs_value = Evaluate(rhs);
1963 return lhs_value < rhs_value || (lhs_value == rhs_value && lhs < rhs);
1967 int64_t Evaluate(
int operator_index)
const {
1968 return evaluator_(active_operator_, operator_index);
1971 std::function<int64_t(
int,
int)> evaluator_;
1972 const int active_operator_;
1976 std::vector<LocalSearchOperator*> operators_;
1977 std::vector<int> operator_indices_;
1978 std::function<int64_t(
int,
int)> evaluator_;
1979 Bitset64<> started_;
1980 const Assignment* start_assignment_;
1981 bool has_fragments_;
1984CompoundOperator::CompoundOperator(std::vector<LocalSearchOperator*> operators,
1985 std::function<int64_t(
int,
int)> evaluator)
1987 operators_(std::move(operators)),
1988 evaluator_(std::move(evaluator)),
1989 started_(operators_.size()),
1990 start_assignment_(nullptr),
1991 has_fragments_(
false) {
1992 operators_.erase(std::remove(operators_.begin(), operators_.end(),
nullptr),
1994 operator_indices_.resize(operators_.size());
1995 absl::c_iota(operator_indices_, 0);
1996 for (LocalSearchOperator*
const op : operators_) {
1997 if (op->HasFragments()) {
1998 has_fragments_ =
true;
2004void CompoundOperator::EnterSearch() {
2005 absl::c_iota(operator_indices_, 0);
2007 for (LocalSearchOperator*
const op : operators_) {
2012void CompoundOperator::Reset() {
2013 for (LocalSearchOperator*
const op : operators_) {
2018void CompoundOperator::Start(
const Assignment* assignment) {
2019 start_assignment_ = assignment;
2021 if (!operators_.empty()) {
2022 std::sort(operator_indices_.begin(), operator_indices_.end(),
2023 OperatorComparator(evaluator_, operator_indices_[index_]));
2028bool CompoundOperator::MakeNextNeighbor(Assignment* delta,
2029 Assignment* deltadelta) {
2030 const auto is_leaf = [](
const LocalSearchOperator* op) {
2031 return op == op->Self();
2033 if (!operators_.empty()) {
2034 Solver* solver = delta->solver();
2038 const int64_t operator_index = operator_indices_[index_];
2039 LocalSearchOperator* op = operators_[operator_index];
2040 if (!started_[operator_index]) {
2041 op->Start(start_assignment_);
2042 started_.
Set(operator_index);
2044 if (!op->HoldsDelta()) {
2048 solver->GetLocalSearchMonitor()->BeginMakeNextNeighbor(op);
2050 if (op->MakeNextNeighbor(delta, deltadelta))
return true;
2052 solver->GetLocalSearchMonitor()->EndMakeNextNeighbor(
2053 op,
false, delta, deltadelta);
2057 if (index_ == operators_.size()) {
2060 }
while (index_ != 0);
2065int64_t CompoundOperatorNoRestart(
int size,
int active_index,
2066 int operator_index) {
2067 return (operator_index < active_index) ? size + operator_index - active_index
2068 : operator_index - active_index;
2073 const std::vector<LocalSearchOperator*>& ops) {
2078 const std::vector<LocalSearchOperator*>& ops,
bool restart) {
2081 ops, std::function<int64_t(
int,
int)>([](
int,
int) {
return 0; }));
2083 const int size = ops.size();
2085 return CompoundOperatorNoRestart(size, i, j);
2090 const std::vector<LocalSearchOperator*>& ops,
2091 std::function<int64_t(
int,
int)> evaluator) {
2092 return RevAlloc(
new CompoundOperator(ops, std::move(evaluator)));
2098 explicit RandomCompoundOperator(std::vector<LocalSearchOperator*> operators);
2099 RandomCompoundOperator(std::vector<LocalSearchOperator*> operators,
2101 ~RandomCompoundOperator()
override {}
2102 void EnterSearch()
override;
2103 void Reset()
override;
2104 void Start(
const Assignment* assignment)
override;
2105 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta)
override;
2106 bool HoldsDelta()
const override {
return true; }
2108 std::string DebugString()
const override {
return "RandomCompoundOperator"; }
2113 const std::vector<LocalSearchOperator*> operators_;
2114 bool has_fragments_;
2117void RandomCompoundOperator::Start(
const Assignment* assignment) {
2118 for (LocalSearchOperator*
const op : operators_) {
2119 op->Start(assignment);
2123RandomCompoundOperator::RandomCompoundOperator(
2124 std::vector<LocalSearchOperator*> operators)
2125 : RandomCompoundOperator(std::move(operators),
CpRandomSeed()) {}
2127RandomCompoundOperator::RandomCompoundOperator(
2128 std::vector<LocalSearchOperator*> operators, int32_t seed)
2129 : rand_(seed), operators_(std::move(operators)), has_fragments_(
false) {
2130 for (LocalSearchOperator*
const op : operators_) {
2131 if (op->HasFragments()) {
2132 has_fragments_ =
true;
2138void RandomCompoundOperator::EnterSearch() {
2139 for (LocalSearchOperator*
const op : operators_) {
2144void RandomCompoundOperator::Reset() {
2145 for (LocalSearchOperator*
const op : operators_) {
2150bool RandomCompoundOperator::MakeNextNeighbor(Assignment* delta,
2151 Assignment* deltadelta) {
2152 const int size = operators_.size();
2153 std::vector<int> indices(size);
2154 absl::c_iota(indices, 0);
2155 std::shuffle(indices.begin(), indices.end(), rand_);
2156 for (
int index : indices) {
2157 if (!operators_[index]->HoldsDelta()) {
2160 if (operators_[index]->MakeNextNeighbor(delta, deltadelta)) {
2170 const std::vector<LocalSearchOperator*>& ops) {
2171 return RevAlloc(
new RandomCompoundOperator(ops));
2175 const std::vector<LocalSearchOperator*>& ops, int32_t seed) {
2176 return RevAlloc(
new RandomCompoundOperator(ops, seed));
2182 explicit MultiArmedBanditCompoundOperator(
2183 std::vector<LocalSearchOperator*> operators,
double memory_coefficient,
2184 double exploration_coefficient,
bool maximize);
2185 ~MultiArmedBanditCompoundOperator()
override {}
2186 void EnterSearch()
override;
2187 void Reset()
override;
2188 void Start(
const Assignment* assignment)
override;
2189 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta)
override;
2190 bool HoldsDelta()
const override {
return true; }
2192 std::string DebugString()
const override {
2193 return operators_.empty()
2195 : operators_[operator_indices_[index_]]->DebugString();
2197 const LocalSearchOperator* Self()
const override {
2198 return operators_.empty() ? this
2199 : operators_[operator_indices_[index_]]->Self();
2203 double Score(
int index);
2205 std::vector<LocalSearchOperator*> operators_;
2206 Bitset64<> started_;
2207 const Assignment* start_assignment_;
2208 bool has_fragments_;
2209 std::vector<int> operator_indices_;
2210 int64_t last_objective_;
2211 std::vector<double> avg_improvement_;
2213 std::vector<double> num_neighbors_per_operator_;
2214 const bool maximize_;
2219 const double memory_coefficient_;
2226 const double exploration_coefficient_;
2229MultiArmedBanditCompoundOperator::MultiArmedBanditCompoundOperator(
2230 std::vector<LocalSearchOperator*> operators,
double memory_coefficient,
2231 double exploration_coefficient,
bool maximize)
2233 operators_(std::move(operators)),
2234 started_(operators_.size()),
2235 start_assignment_(nullptr),
2236 has_fragments_(
false),
2237 last_objective_(std::numeric_limits<int64_t>::max()),
2239 maximize_(maximize),
2240 memory_coefficient_(memory_coefficient),
2241 exploration_coefficient_(exploration_coefficient) {
2242 DCHECK_GE(memory_coefficient_, 0);
2243 DCHECK_LE(memory_coefficient_, 1);
2244 DCHECK_GE(exploration_coefficient_, 0);
2245 operators_.erase(std::remove(operators_.begin(), operators_.end(),
nullptr),
2247 operator_indices_.resize(operators_.size());
2248 absl::c_iota(operator_indices_, 0);
2249 num_neighbors_per_operator_.resize(operators_.size(), 0);
2250 avg_improvement_.resize(operators_.size(), 0);
2251 for (LocalSearchOperator*
const op : operators_) {
2252 if (op->HasFragments()) {
2253 has_fragments_ =
true;
2259void MultiArmedBanditCompoundOperator::EnterSearch() {
2260 last_objective_ = std::numeric_limits<int64_t>::max();
2262 absl::c_iota(operator_indices_, 0);
2264 num_neighbors_per_operator_.resize(operators_.size(), 0);
2265 avg_improvement_.resize(operators_.size(), 0);
2266 for (LocalSearchOperator*
const op : operators_) {
2271void MultiArmedBanditCompoundOperator::Reset() {
2272 for (LocalSearchOperator*
const op : operators_) {
2277double MultiArmedBanditCompoundOperator::Score(
int index) {
2278 return avg_improvement_[index] +
2279 exploration_coefficient_ *
2280 sqrt(2 * log(1 + num_neighbors_) /
2281 (1 + num_neighbors_per_operator_[index]));
2284void MultiArmedBanditCompoundOperator::Start(
const Assignment* assignment) {
2285 start_assignment_ = assignment;
2287 if (operators_.empty())
return;
2289 const double objective = assignment->ObjectiveValue();
2291 if (objective == last_objective_)
return;
2293 if (last_objective_ == std::numeric_limits<int64_t>::max()) {
2294 last_objective_ = objective;
2298 const double improvement =
2299 maximize_ ? objective - last_objective_ : last_objective_ - objective;
2300 if (improvement < 0) {
2303 last_objective_ = objective;
2304 avg_improvement_[operator_indices_[index_]] +=
2305 memory_coefficient_ *
2306 (improvement - avg_improvement_[operator_indices_[index_]]);
2308 std::sort(operator_indices_.begin(), operator_indices_.end(),
2309 [
this](
int lhs,
int rhs) {
2310 const double lhs_score = Score(lhs);
2311 const double rhs_score = Score(rhs);
2312 return lhs_score > rhs_score ||
2313 (lhs_score == rhs_score && lhs < rhs);
2319bool MultiArmedBanditCompoundOperator::MakeNextNeighbor(
2320 Assignment* delta, Assignment* deltadelta) {
2321 if (operators_.empty())
return false;
2323 const int operator_index = operator_indices_[index_];
2324 if (!started_[operator_index]) {
2325 operators_[operator_index]->Start(start_assignment_);
2326 started_.
Set(operator_index);
2328 if (!operators_[operator_index]->HoldsDelta()) {
2331 if (operators_[operator_index]->MakeNextNeighbor(delta, deltadelta)) {
2333 ++num_neighbors_per_operator_[operator_index];
2338 if (index_ == operators_.size()) {
2341 }
while (index_ != 0);
2347 const std::vector<LocalSearchOperator*>& ops,
double memory_coefficient,
2348 double exploration_coefficient,
bool maximize) {
2349 return RevAlloc(
new MultiArmedBanditCompoundOperator(
2350 ops, memory_coefficient, exploration_coefficient, maximize));
2360 std::move(get_incoming_neighbors),
2361 std::move(get_outgoing_neighbors));
2365 const std::vector<IntVar*>& vars,
2371 return MakeTwoOpt(
this, vars, secondary_vars,
nullptr,
2372 std::move(get_incoming_neighbors),
2373 std::move(get_outgoing_neighbors));
2376 std::vector<LocalSearchOperator*> operators;
2377 for (
int i = 1; i < 4; ++i) {
2378 operators.push_back(
MakeRelocate(
this, vars, secondary_vars,
2390 this, vars, secondary_vars,
nullptr,
2391 std::move(get_incoming_neighbors), std::move(get_outgoing_neighbors));
2394 return MakeExchange(
this, vars, secondary_vars,
nullptr,
2395 std::move(get_incoming_neighbors),
2396 std::move(get_outgoing_neighbors));
2399 return MakeCross(
this, vars, secondary_vars,
nullptr,
2400 std::move(get_incoming_neighbors),
2401 std::move(get_outgoing_neighbors));
2404 return MakeActive(
this, vars, secondary_vars,
nullptr);
2407 return MakeInactive(
this, vars, secondary_vars,
nullptr);
2437 if (!secondary_vars.empty()) {
2438 LOG(FATAL) <<
"Operator " << op
2439 <<
" does not support secondary variables";
2441 return RevAlloc(
new IncrementValue(vars));
2444 if (!secondary_vars.empty()) {
2445 LOG(FATAL) <<
"Operator " << op
2446 <<
" does not support secondary variables";
2448 return RevAlloc(
new DecrementValue(vars));
2451 if (!secondary_vars.empty()) {
2452 LOG(FATAL) <<
"Operator " << op
2453 <<
" does not support secondary variables";
2455 return RevAlloc(
new SimpleLns(vars, 1));
2458 LOG(FATAL) <<
"Unknown operator " << op;
2466 return MakeOperator(vars, std::vector<IntVar*>(), std::move(evaluator), op);
2470 const std::vector<IntVar*>& vars,
2471 const std::vector<IntVar*>& secondary_vars,
2476 std::vector<LocalSearchOperator*> operators;
2484 return MakeTSPOpt(
this, vars, secondary_vars, std::move(evaluator),
2485 absl::GetFlag(FLAGS_cp_local_search_tsp_opt_size));
2488 return MakeTSPLns(
this, vars, secondary_vars, std::move(evaluator),
2489 absl::GetFlag(FLAGS_cp_local_search_tsp_lns_size));
2492 LOG(FATAL) <<
"Unknown operator " << op;
2501 std::string DebugString()
const override {
return "AcceptFilter"; }
2502 bool Accept(
const Assignment*,
const Assignment*, int64_t, int64_t)
override {
2505 void Synchronize(
const Assignment*,
const Assignment*)
override {}
2510 return RevAlloc(
new AcceptFilter());
2517 std::string DebugString()
const override {
return "RejectFilter"; }
2518 bool Accept(
const Assignment*,
const Assignment*, int64_t, int64_t)
override {
2521 void Synchronize(
const Assignment*,
const Assignment*)
override {}
2526 return RevAlloc(
new RejectFilter());
2535 VariableDomainFilter() {}
2536 ~VariableDomainFilter()
override {}
2537 bool Accept(
const Assignment* delta,
const Assignment* deltadelta,
2538 int64_t objective_min, int64_t objective_max)
override;
2539 void Synchronize(
const Assignment*,
const Assignment*)
override {}
2541 std::string DebugString()
const override {
return "VariableDomainFilter"; }
2544bool VariableDomainFilter::Accept(
const Assignment* delta,
const Assignment*,
2546 const Assignment::IntContainer& container = delta->IntVarContainer();
2547 const int size = container.Size();
2548 for (
int i = 0;
i < size; ++
i) {
2549 const IntVarElement& element = container.Element(i);
2550 if (element.Activated() && !element.Var()->Contains(element.Value())) {
2559 return RevAlloc(
new VariableDomainFilter());
2564const int IntVarLocalSearchFilter::kUnassigned = -1;
2567 const std::vector<IntVar*>& vars) {
2572 if (!vars.empty()) {
2573 for (
int i = 0; i < vars.size(); ++i) {
2574 const int index = vars[i]->index();
2575 if (index >= var_index_to_index_.size()) {
2576 var_index_to_index_.resize(index + 1, kUnassigned);
2578 var_index_to_index_[index] = i + vars_.size();
2580 vars_.insert(vars_.end(), vars.begin(), vars.end());
2581 values_.resize(vars_.size(), 0);
2582 var_synced_.resize(vars_.size(),
false);
2590 if (delta ==
nullptr || delta->
Empty()) {
2591 var_synced_.assign(var_synced_.size(),
false);
2602 const int size = container.
Size();
2603 for (
int i = 0; i < size; ++i) {
2606 if (var !=
nullptr) {
2607 if (i < vars_.size() && vars_[i] == var) {
2608 values_[i] = element.
Value();
2609 var_synced_[i] =
true;
2611 const int64_t kUnallocated = -1;
2612 int64_t index = kUnallocated;
2614 values_[index] = element.
Value();
2615 var_synced_[index] =
true;
2632template <
typename Filter>
2635 SumObjectiveFilter(
const std::vector<IntVar*>& vars, Filter filter)
2637 primary_vars_size_(vars.size()),
2638 synchronized_costs_(vars.size()),
2639 delta_costs_(vars.size()),
2640 filter_(
std::move(filter)),
2641 synchronized_sum_(
std::numeric_limits<int64_t>::min()),
2642 delta_sum_(
std::numeric_limits<int64_t>::min()),
2643 incremental_(false) {}
2644 ~SumObjectiveFilter()
override {}
2645 bool Accept(
const Assignment* delta,
const Assignment* deltadelta,
2646 int64_t objective_min, int64_t objective_max)
override {
2647 if (delta ==
nullptr)
return false;
2648 if (deltadelta->Empty()) {
2650 for (
int i = 0;
i < primary_vars_size_; ++
i) {
2651 delta_costs_[
i] = synchronized_costs_[
i];
2654 incremental_ =
false;
2655 delta_sum_ =
CapAdd(synchronized_sum_, CostOfChanges(delta,
false));
2658 delta_sum_ =
CapAdd(delta_sum_, CostOfChanges(deltadelta,
true));
2660 delta_sum_ =
CapAdd(synchronized_sum_, CostOfChanges(delta,
true));
2662 incremental_ =
true;
2664 return filter_(delta_sum_, objective_min, objective_max);
2668 virtual int64_t CostOfSynchronizedVariable(int64_t index) = 0;
2670 virtual int64_t CostOfChanges(
const Assignment* changes,
2671 bool incremental) = 0;
2672 bool IsIncremental()
const override {
return true; }
2674 std::string DebugString()
const override {
return "SumObjectiveFilter"; }
2676 int64_t GetSynchronizedObjectiveValue()
const override {
2677 return synchronized_sum_;
2679 int64_t GetAcceptedObjectiveValue()
const override {
return delta_sum_; }
2682 const int primary_vars_size_;
2683 std::vector<int64_t> synchronized_costs_;
2684 std::vector<int64_t> delta_costs_;
2686 int64_t synchronized_sum_;
2691 void OnSynchronize(
const Assignment*)
override {
2692 synchronized_sum_ = 0;
2693 for (
int i = 0;
i < primary_vars_size_; ++
i) {
2694 const int64_t cost = CostOfSynchronizedVariable(i);
2695 synchronized_costs_[
i] = cost;
2696 delta_costs_[
i] = cost;
2697 synchronized_sum_ =
CapAdd(synchronized_sum_, cost);
2699 delta_sum_ = synchronized_sum_;
2700 incremental_ =
false;
2704template <
typename Filter>
2705class BinaryObjectiveFilter :
public SumObjectiveFilter<Filter> {
2707 BinaryObjectiveFilter(
const std::vector<IntVar*>& vars,
2708 Solver::IndexEvaluator2 value_evaluator, Filter filter)
2709 : SumObjectiveFilter<Filter>(vars, std::move(filter)),
2710 value_evaluator_(std::move(value_evaluator)) {}
2711 ~BinaryObjectiveFilter()
override {}
2712 int64_t CostOfSynchronizedVariable(int64_t index)
override {
2713 return IntVarLocalSearchFilter::IsVarSynced(index)
2714 ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index))
2717 int64_t CostOfChanges(
const Assignment* changes,
bool incremental)
override {
2718 int64_t total_cost = 0;
2719 const Assignment::IntContainer& container = changes->IntVarContainer();
2720 for (
const IntVarElement& new_element : container.elements()) {
2721 IntVar*
const var = new_element.
Var();
2723 if (this->FindIndex(var, &index)) {
2724 total_cost =
CapSub(total_cost, this->delta_costs_[index]);
2725 int64_t new_cost = 0LL;
2726 if (new_element.Activated()) {
2727 new_cost = value_evaluator_(index, new_element.Value());
2728 }
else if (var->Bound()) {
2729 new_cost = value_evaluator_(index, var->Min());
2731 total_cost =
CapAdd(total_cost, new_cost);
2733 this->delta_costs_[index] = new_cost;
2741 Solver::IndexEvaluator2 value_evaluator_;
2744template <
typename Filter>
2745class TernaryObjectiveFilter :
public SumObjectiveFilter<Filter> {
2747 TernaryObjectiveFilter(
const std::vector<IntVar*>& vars,
2748 const std::vector<IntVar*>& secondary_vars,
2749 Solver::IndexEvaluator3 value_evaluator, Filter filter)
2750 : SumObjectiveFilter<Filter>(vars, std::move(filter)),
2751 secondary_vars_offset_(vars.size()),
2752 secondary_values_(vars.size(), -1),
2753 value_evaluator_(std::move(value_evaluator)) {
2754 IntVarLocalSearchFilter::AddVars(secondary_vars);
2755 CHECK_GE(IntVarLocalSearchFilter::Size(), 0);
2757 ~TernaryObjectiveFilter()
override {}
2758 int64_t CostOfSynchronizedVariable(int64_t index)
override {
2759 DCHECK_LT(index, secondary_vars_offset_);
2760 return IntVarLocalSearchFilter::IsVarSynced(index)
2761 ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index),
2762 IntVarLocalSearchFilter::Value(
2763 index + secondary_vars_offset_))
2766 int64_t CostOfChanges(
const Assignment* changes,
bool incremental)
override {
2767 int64_t total_cost = 0;
2768 const Assignment::IntContainer& container = changes->IntVarContainer();
2769 for (
const IntVarElement& new_element : container.elements()) {
2770 IntVar*
const var = new_element.Var();
2772 if (this->FindIndex(var, &index) && index < secondary_vars_offset_) {
2773 secondary_values_[index] = -1;
2779 const int max_secondary_index = 2 * secondary_vars_offset_;
2780 for (
const IntVarElement& new_element : container.elements()) {
2781 IntVar*
const var = new_element.Var();
2783 if (new_element.Activated() && this->FindIndex(var, &index) &&
2784 index >= secondary_vars_offset_ &&
2786 index < max_secondary_index) {
2787 secondary_values_[index - secondary_vars_offset_] = new_element.Value();
2790 for (
const IntVarElement& new_element : container.elements()) {
2791 IntVar*
const var = new_element.Var();
2793 if (this->FindIndex(var, &index) && index < secondary_vars_offset_) {
2794 total_cost =
CapSub(total_cost, this->delta_costs_[index]);
2795 int64_t new_cost = 0LL;
2796 if (new_element.Activated()) {
2797 new_cost = value_evaluator_(index, new_element.Value(),
2798 secondary_values_[index]);
2799 }
else if (var->Bound() &&
2800 IntVarLocalSearchFilter::Var(index + secondary_vars_offset_)
2802 new_cost = value_evaluator_(
2804 IntVarLocalSearchFilter::Var(index + secondary_vars_offset_)
2807 total_cost =
CapAdd(total_cost, new_cost);
2809 this->delta_costs_[index] = new_cost;
2817 int secondary_vars_offset_;
2818 std::vector<int64_t> secondary_values_;
2819 Solver::IndexEvaluator3 value_evaluator_;
2826 switch (filter_enum) {
2828 auto filter = [](int64_t value, int64_t, int64_t max_value) {
2829 return value <= max_value;
2831 return RevAlloc(
new BinaryObjectiveFilter<
decltype(filter)>(
2832 vars, std::move(values), std::move(filter)));
2835 auto filter = [](int64_t value, int64_t min_value, int64_t) {
2836 return value >= min_value;
2838 return RevAlloc(
new BinaryObjectiveFilter<
decltype(filter)>(
2839 vars, std::move(values), std::move(filter)));
2842 auto filter = [](int64_t value, int64_t min_value, int64_t max_value) {
2843 return min_value <= value && value <= max_value;
2845 return RevAlloc(
new BinaryObjectiveFilter<
decltype(filter)>(
2846 vars, std::move(values), std::move(filter)));
2849 LOG(ERROR) <<
"Unknown local search filter enum value";
2856 const std::vector<IntVar*>& vars,
2859 switch (filter_enum) {
2861 auto filter = [](int64_t value, int64_t, int64_t max_value) {
2862 return value <= max_value;
2864 return RevAlloc(
new TernaryObjectiveFilter<
decltype(filter)>(
2865 vars, secondary_vars, std::move(values), std::move(filter)));
2868 auto filter = [](int64_t value, int64_t min_value, int64_t) {
2869 return value >= min_value;
2871 return RevAlloc(
new TernaryObjectiveFilter<
decltype(filter)>(
2872 vars, secondary_vars, std::move(values), std::move(filter)));
2875 auto filter = [](int64_t value, int64_t min_value, int64_t max_value) {
2876 return min_value <= value && value <= max_value;
2878 return RevAlloc(
new TernaryObjectiveFilter<
decltype(filter)>(
2879 vars, secondary_vars, std::move(values), std::move(filter)));
2882 LOG(ERROR) <<
"Unknown local search filter enum value";
2889 DCHECK_GE(num_nodes, num_nodes_);
2890 DCHECK(!graph_was_built_);
2891 graph_was_built_ =
true;
2892 std::sort(arcs_.begin(), arcs_.end());
2893 arcs_of_node_.clear();
2895 for (
int a = 0; a < arcs_.size(); ++a) {
2896 const NodeId tail = arcs_[a].tail;
2897 if (tail != prev_tail) {
2899 arcs_of_node_.resize(tail.value() + 1, a);
2902 num_nodes_ = std::max(num_nodes_, num_nodes);
2903 arcs_of_node_.resize(num_nodes_ + 1, arcs_.size());
2904 DCHECK(!HasDirectedCycle()) <<
"Graph has a directed cycle";
2907bool SubDagComputer::HasDirectedCycle()
const {
2908 DCHECK(graph_was_built_);
2917 std::vector<DFSEvent> event_stack;
2919 for (
NodeId node(0); node.value() < num_nodes_; ++node) {
2920 if (node_was_visited[node])
continue;
2921 event_stack.push_back({node,
true});
2922 while (!event_stack.empty()) {
2923 const auto [node, to_open] = event_stack.back();
2924 event_stack.pop_back();
2925 if (node_was_visited[node])
continue;
2927 if (node_is_open[node])
return true;
2928 node_is_open[node] =
true;
2929 event_stack.push_back({node,
false});
2930 for (
int a = arcs_of_node_[node];
2931 a < arcs_of_node_[
NodeId(node.value() + 1)]; ++a) {
2932 const NodeId head = arcs_[a].head;
2933 if (node_was_visited[head])
continue;
2934 event_stack.push_back({head,
true});
2937 node_was_visited[node] =
true;
2938 node_is_open[node] =
false;
2945const std::vector<SubDagComputer::ArcId>&
2947 DCHECK_LT(node.value(), num_nodes_);
2948 DCHECK(graph_was_built_);
2949 if (indegree_of_node_.size() < num_nodes_) {
2950 indegree_of_node_.resize(num_nodes_, 0);
2953 nodes_to_visit_.clear();
2954 nodes_to_visit_.push_back(node);
2955 while (!nodes_to_visit_.empty()) {
2956 const NodeId node = nodes_to_visit_.back();
2957 nodes_to_visit_.pop_back();
2958 const NodeId next(node.value() + 1);
2959 for (
int a = arcs_of_node_[node]; a < arcs_of_node_[next]; ++a) {
2960 const NodeId head = arcs_[a].head;
2961 if (indegree_of_node_[head] == 0) {
2962 nodes_to_visit_.push_back(head);
2964 ++indegree_of_node_[head];
2968 sorted_arcs_.clear();
2969 nodes_to_visit_.push_back(node);
2970 while (!nodes_to_visit_.empty()) {
2971 const NodeId node = nodes_to_visit_.back();
2972 nodes_to_visit_.pop_back();
2973 const NodeId next(node.value() + 1);
2974 for (
int a = arcs_of_node_[node]; a < arcs_of_node_[next]; ++a) {
2975 const NodeId head = arcs_[a].head;
2976 --indegree_of_node_[head];
2977 if (indegree_of_node_[head] == 0) {
2978 nodes_to_visit_.push_back(head);
2980 sorted_arcs_.push_back(arcs_[a].arc_id);
2984 DCHECK(absl::c_all_of(indegree_of_node_, [](
int x) {
return x == 0; }));
2985 return sorted_arcs_;
2990 int64_t relaxed_max) {
2991 DCHECK(state_domains_are_all_nonempty_);
2992 DCHECK_LE(relaxed_min, relaxed_max);
2993 relaxed_domains_.push_back({relaxed_min, relaxed_max});
2994 current_domains_.push_back({relaxed_min, relaxed_max});
2995 domain_is_trailed_.push_back(
false);
3001 return {
this, domain_id};
3005 int64_t min, int64_t max) {
3007 return {
this, domain_id};
3015 DCHECK(state_domains_are_all_nonempty_);
3016 if (!state_has_relaxed_domains_) {
3017 trailed_num_committed_empty_domains_ = num_committed_empty_domains_;
3019 state_has_relaxed_domains_ =
true;
3020 if (!domain_is_trailed_[domain_id]) {
3021 domain_is_trailed_[domain_id] =
true;
3022 trailed_domains_.push_back({current_domains_[domain_id], domain_id});
3023 if (IntersectionIsEmpty(relaxed_domains_[domain_id],
3024 current_domains_[domain_id])) {
3025 DCHECK_GT(num_committed_empty_domains_, 0);
3026 --num_committed_empty_domains_;
3028 current_domains_[domain_id] = relaxed_domains_[domain_id];
3035 DCHECK(state_domains_are_all_nonempty_);
3036 return current_domains_[domain_id].min;
3040 DCHECK(state_domains_are_all_nonempty_);
3041 return current_domains_[domain_id].max;
3045 int64_t min_value) {
3046 DCHECK(state_domains_are_all_nonempty_);
3047 DCHECK(domain_is_trailed_[domain_id]);
3048 VariableDomain& domain = current_domains_[domain_id];
3049 if (domain.max < min_value) {
3050 state_domains_are_all_nonempty_ =
false;
3052 domain.min = std::max(domain.min, min_value);
3053 return state_domains_are_all_nonempty_;
3057 int64_t max_value) {
3058 DCHECK(state_domains_are_all_nonempty_);
3059 DCHECK(domain_is_trailed_[domain_id]);
3060 VariableDomain& domain = current_domains_[domain_id];
3061 if (domain.min > max_value) {
3062 state_domains_are_all_nonempty_ =
false;
3064 domain.max = std::min(domain.max, max_value);
3065 return state_domains_are_all_nonempty_;
3069 int64_t min, int64_t max) {
3070 DCHECK(state_domains_are_all_nonempty_);
3071 DCHECK(!domain_is_trailed_[domain_id]);
3072 const bool domain_was_empty = IntersectionIsEmpty(
3073 relaxed_domains_[domain_id], current_domains_[domain_id]);
3074 relaxed_domains_[domain_id] = {min, max};
3075 const bool domain_is_empty =
3076 IntersectionIsEmpty({min, max}, current_domains_[domain_id]);
3078 if (!domain_was_empty && domain_is_empty) {
3079 num_committed_empty_domains_++;
3080 }
else if (domain_was_empty && !domain_is_empty) {
3081 num_committed_empty_domains_--;
3091 state_has_relaxed_domains_ =
false;
3092 trailed_domains_.clear();
3093 domain_is_trailed_.assign(domain_is_trailed_.size(),
false);
3095 for (Constraint* constraint : trailed_constraints_) {
3096 constraint->Commit();
3098 trailed_constraints_.clear();
3103 for (
const auto& [domain,
id] : trailed_domains_) {
3104 DCHECK(domain_is_trailed_[
id]);
3105 current_domains_[id] = domain;
3107 trailed_domains_.clear();
3108 state_has_relaxed_domains_ =
false;
3109 domain_is_trailed_.assign(domain_is_trailed_.size(),
false);
3110 state_domains_are_all_nonempty_ =
true;
3111 num_committed_empty_domains_ = trailed_num_committed_empty_domains_;
3113 for (Constraint* constraint : trailed_constraints_) {
3114 constraint->Revert();
3116 trailed_constraints_.clear();
3120NodeId LocalSearchState::DependencyGraph::GetOrCreateNodeOfDomainId(
3122 if (domain_id.value() >= dag_node_of_domain_.size()) {
3123 dag_node_of_domain_.resize(domain_id.value() + 1,
NodeId(0));
3125 if (dag_node_of_domain_[domain_id] ==
NodeId(0)) {
3126 dag_node_of_domain_[domain_id] =
NodeId(num_dag_nodes_++);
3128 return dag_node_of_domain_[domain_id];
3131NodeId LocalSearchState::DependencyGraph::GetOrCreateNodeOfConstraintId(
3132 ConstraintId constraint_id) {
3133 if (constraint_id.value() >= dag_node_of_constraint_.size()) {
3134 dag_node_of_constraint_.resize(constraint_id.value() + 1,
NodeId(0));
3136 if (dag_node_of_constraint_[constraint_id] ==
NodeId(0)) {
3137 dag_node_of_constraint_[constraint_id] =
NodeId(num_dag_nodes_++);
3139 return dag_node_of_constraint_[constraint_id];
3142void LocalSearchState::DependencyGraph::AddDomainsConstraintDependencies(
3143 const std::vector<VariableDomainId>& domain_ids,
3144 ConstraintId constraint_id) {
3145 const NodeId cnode = GetOrCreateNodeOfConstraintId(constraint_id);
3146 for (
int i = 0;
i < domain_ids.size(); ++
i) {
3147 const NodeId dnode = GetOrCreateNodeOfDomainId(domain_ids[i]);
3148 dag_.AddArc(dnode, cnode);
3149 dependency_of_dag_arc_.push_back({.domain_id = domain_ids[
i],
3151 .constraint_id = constraint_id});
3155void LocalSearchState::DependencyGraph::AddConstraintDomainDependency(
3157 const NodeId cnode = GetOrCreateNodeOfConstraintId(constraint_id);
3158 const NodeId dnode = GetOrCreateNodeOfDomainId(domain_id);
3159 dag_.AddArc(cnode, dnode);
3160 dependency_of_dag_arc_.push_back({.domain_id = domain_id,
3162 .constraint_id = constraint_id});
3166const std::vector<LocalSearchState::DependencyGraph::Dependency>&
3167LocalSearchState::DependencyGraph::ComputeSortedDependencies(
3169 sorted_dependencies_.clear();
3170 const NodeId node = dag_node_of_domain_[domain_id];
3171 for (
const ArcId a : dag_.ComputeSortedSubDagArcs(node)) {
3172 if (dependency_of_dag_arc_[a].input_index == -1)
continue;
3173 sorted_dependencies_.push_back(dependency_of_dag_arc_[a]);
3175 return sorted_dependencies_;
3179 const std::vector<VariableDomainId>& input_domain_ids,
3180 const std::vector<int64_t>& input_weights, int64_t input_offset,
3182 DCHECK_EQ(input_domain_ids.size(), input_weights.size());
3184 const ConstraintId constraint_id(constraints_.size());
3185 dependency_graph_.AddDomainsConstraintDependencies(input_domain_ids,
3187 dependency_graph_.AddConstraintDomainDependency(constraint_id,
3190 auto constraint = std::make_unique<WeightedSum>(
3191 this, input_domain_ids, input_weights, input_offset, output_domain_id);
3192 constraints_.push_back(std::move(constraint));
3195void LocalSearchState::DependencyGraph::BuildDependencyDAG(
int num_domains) {
3196 dag_.BuildGraph(num_dag_nodes_);
3198 const int num_assigned_nodes = dag_node_of_domain_.size();
3199 DCHECK_GE(num_domains, num_assigned_nodes);
3200 num_domains = std::max(num_domains, num_assigned_nodes);
3201 dag_node_of_domain_.resize(num_domains,
NodeId(0));
3206 triggers_of_domain_.clear();
3207 const int num_domains = current_domains_.size();
3208 dependency_graph_.BuildDependencyDAG(num_domains);
3209 for (
int vid = 0; vid < num_domains; ++vid) {
3210 triggers_of_domain_.push_back(triggers_.size());
3211 for (
const auto& [domain_id, input_index, constraint_id] :
3213 triggers_.push_back({.constraint = constraints_[constraint_id].get(),
3214 .input_index = input_index});
3217 triggers_of_domain_.push_back(triggers_.size());
3220LocalSearchState::WeightedSum::WeightedSum(
3222 const std::vector<VariableDomainId>& input_domain_ids,
3223 const std::vector<int64_t>& input_weights, int64_t input_offset,
3225 : output_(output), state_(state) {
3228 invariants_.num_neg_inf = 0;
3229 invariants_.num_pos_inf = 0;
3230 invariants_.wsum_mins = input_offset;
3231 invariants_.wsum_maxs = input_offset;
3232 for (
int i = 0; i < input_domain_ids.size(); ++i) {
3234 const int64_t weight = input_weights[i];
3237 inputs_.push_back({.min = min,
3239 .committed_min = min,
3240 .committed_max = max,
3242 .domain = domain_id,
3243 .is_trailed =
false});
3247 ++invariants_.num_neg_inf;
3249 invariants_.wsum_mins =
3253 ++invariants_.num_pos_inf;
3255 invariants_.wsum_maxs =
3260 ++invariants_.num_pos_inf;
3262 invariants_.wsum_maxs =
3266 ++invariants_.num_neg_inf;
3268 invariants_.wsum_mins =
3273 committed_invariants_ = invariants_;
3276LocalSearchState::VariableDomain LocalSearchState::WeightedSum::Propagate(
3278 if (!constraint_is_trailed_) {
3279 constraint_is_trailed_ =
true;
3280 state_->TrailConstraint(
this);
3282 WeightedVariable&
input = inputs_[input_index];
3283 if (!
input.is_trailed) {
3284 input.is_trailed =
true;
3285 trailed_inputs_.push_back(&
input);
3287 const int64_t new_min = state_->VariableDomainMin(
input.domain);
3288 const int64_t new_max = state_->VariableDomainMax(
input.domain);
3289 const int64_t weight =
input.weight;
3291 if (
input.min != new_min) {
3294 --invariants_.num_neg_inf;
3296 invariants_.wsum_mins =
3301 ++invariants_.num_neg_inf;
3303 invariants_.wsum_mins =
3306 input.min = new_min;
3308 if (
input.max != new_max) {
3311 --invariants_.num_pos_inf;
3313 invariants_.wsum_maxs =
3318 ++invariants_.num_pos_inf;
3320 invariants_.wsum_maxs =
3323 input.max = new_max;
3326 if (
input.min != new_min) {
3329 --invariants_.num_pos_inf;
3331 invariants_.wsum_maxs =
3336 ++invariants_.num_pos_inf;
3338 invariants_.wsum_maxs =
3341 input.min = new_min;
3343 if (
input.max != new_max) {
3346 --invariants_.num_neg_inf;
3348 invariants_.wsum_mins =
3353 ++invariants_.num_neg_inf;
3355 invariants_.wsum_mins =
3358 input.max = new_max;
3361 return {invariants_.num_neg_inf == 0 ? invariants_.wsum_mins :
kint64min,
3362 invariants_.num_pos_inf == 0 ? invariants_.wsum_maxs :
kint64max};
3365void LocalSearchState::WeightedSum::Commit() {
3366 committed_invariants_ = invariants_;
3367 constraint_is_trailed_ =
false;
3368 for (WeightedVariable* wv : trailed_inputs_) wv->Commit();
3369 trailed_inputs_.clear();
3372void LocalSearchState::WeightedSum::Revert() {
3373 invariants_ = committed_invariants_;
3374 constraint_is_trailed_ =
false;
3375 for (WeightedVariable* wv : trailed_inputs_) wv->Revert();
3376 trailed_inputs_.clear();
3381 for (
int t = triggers_of_domain_[domain_id]; t < triggers_of_domain_[next_id];
3383 const auto& [constraint, input_index] = triggers_[t];
3384 constraint->Propagate(input_index);
3391 for (
int t = triggers_of_domain_[domain_id]; t < triggers_of_domain_[next_id];
3393 const auto& [constraint, input_index] = triggers_[t];
3394 const auto [output_min, output_max] = constraint->Propagate(input_index);
3412 std::string
DebugString()
const override {
return "LocalSearchProfiler"; }
3414 operator_stats_.clear();
3415 filter_stats_per_context_.clear();
3416 last_operator_ =
nullptr;
3421 last_operator_ =
nullptr;
3423 template <
typename Callback>
3426 if (db->seconds() == 0)
continue;
3427 callback(db->name(), db->seconds());
3431 template <
typename Callback>
3433 std::vector<const LocalSearchOperator*> operators;
3434 for (
const auto& stat : operator_stats_) {
3435 operators.push_back(stat.first);
3438 operators.begin(), operators.end(),
3440 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3441 gtl::FindOrDie(operator_stats_, op2).neighbors;
3444 const OperatorStats& stats =
gtl::FindOrDie(operator_stats_, op);
3445 const std::string& label = op->DebugString();
3447 if (label.empty() &&
3448 dynamic_cast<const CompoundOperator*
>(op) !=
nullptr) {
3451 callback(label, stats.neighbors, stats.filtered_neighbors,
3452 stats.accepted_neighbors, stats.seconds,
3453 stats.make_next_neighbor_seconds, stats.accept_neighbor_seconds);
3457 template <
typename Callback>
3459 for (
const auto& [context, filter_stats] : filter_stats_per_context_) {
3460 std::vector<const LocalSearchFilter*> filters;
3461 for (
const auto& [filter, stats] : filter_stats) {
3462 filters.push_back(filter);
3465 filters.begin(), filters.end(),
3468 return gtl::FindOrDie(*filter_stats_ptr, filter1).calls >
3469 gtl::FindOrDie(*filter_stats_ptr, filter2).calls;
3473 callback(context, filter->DebugString(), stats.calls, stats.rejects,
3481 [&statistics_proto](absl::string_view name,
double duration_seconds) {
3483 first_solution_statistics =
3489 [&statistics_proto](
3490 absl::string_view name, int64_t num_neighbors,
3491 int64_t num_filtered_neighbors, int64_t num_accepted_neighbors,
3492 double duration_seconds,
double make_next_neighbor_duration_seconds,
3493 double accept_neighbor_duration_seconds) {
3495 local_search_operator_statistics =
3500 num_filtered_neighbors);
3502 num_accepted_neighbors);
3505 local_search_operator_statistics
3507 make_next_neighbor_duration_seconds);
3508 local_search_operator_statistics
3510 accept_neighbor_duration_seconds);
3513 absl::string_view context,
3514 absl::string_view name,
3515 int64_t num_calls, int64_t num_rejects,
3516 double duration_seconds) {
3518 local_search_filter_statistics =
3525 num_rejects / duration_seconds);
3526 local_search_filter_statistics->
set_context(context);
3530 solver()->filtered_neighbors());
3532 solver()->accepted_neighbors());
3533 return statistics_proto;
3536 std::string overview;
3537 size_t max_name_size = 0;
3539 [&max_name_size](absl::string_view name,
double) {
3540 max_name_size = std::max(max_name_size, name.length());
3542 if (max_name_size > 0) {
3543 absl::StrAppendFormat(&overview,
3544 "First solution statistics:\n%*s | Time (s)\n",
3547 [&overview, max_name_size](absl::string_view name,
3548 double duration_seconds) {
3549 absl::StrAppendFormat(&overview,
"%*s | %7.2g\n", max_name_size,
3550 name, duration_seconds);
3555 [&max_name_size](absl::string_view name, int64_t, int64_t, int64_t,
3556 double,
double,
double) {
3557 max_name_size = std::max(max_name_size, name.length());
3559 if (max_name_size > 0) {
3560 absl::StrAppendFormat(
3562 "Local search operator statistics:\n%*s | Neighbors | Filtered "
3563 "| Accepted | Time (s)\n",
3565 OperatorStats total_stats;
3567 [&overview, &total_stats, max_name_size](
3568 absl::string_view name, int64_t num_neighbors,
3569 int64_t num_filtered_neighbors, int64_t num_accepted_neighbors,
3570 double duration_seconds,
3575 absl::StrAppendFormat(
3576 &overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n", max_name_size,
3577 name, num_neighbors, num_filtered_neighbors,
3578 num_accepted_neighbors, duration_seconds);
3579 total_stats.neighbors += num_neighbors;
3580 total_stats.filtered_neighbors += num_filtered_neighbors;
3581 total_stats.accepted_neighbors += num_accepted_neighbors;
3582 total_stats.seconds += duration_seconds;
3584 absl::StrAppendFormat(
3585 &overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n", max_name_size,
3586 "Total", total_stats.neighbors, total_stats.filtered_neighbors,
3587 total_stats.accepted_neighbors, total_stats.seconds);
3591 [&max_name_size](absl::string_view, absl::string_view name, int64_t,
3593 max_name_size = std::max(max_name_size, name.length());
3595 if (max_name_size > 0) {
3596 std::optional<std::string> filter_context;
3597 FilterStats total_filter_stats;
3599 [&overview, &filter_context, &total_filter_stats, max_name_size](
3600 const std::string& context,
const std::string& name,
3601 int64_t num_calls, int64_t num_rejects,
double duration_seconds) {
3602 if (!filter_context.has_value() ||
3603 filter_context.value() != context) {
3604 if (filter_context.has_value()) {
3605 absl::StrAppendFormat(
3606 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n",
3607 max_name_size,
"Total", total_filter_stats.calls,
3608 total_filter_stats.rejects, total_filter_stats.seconds,
3609 total_filter_stats.rejects / total_filter_stats.seconds);
3610 total_filter_stats = {};
3612 filter_context = context;
3613 absl::StrAppendFormat(
3615 "Local search filter statistics%s:\n%*s | Calls | "
3616 " Rejects | Time (s) | Rejects/s\n",
3617 context.empty() ?
"" :
" (" + context +
")", max_name_size,
3620 absl::StrAppendFormat(
3621 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n",
3622 max_name_size, name, num_calls, num_rejects, duration_seconds,
3623 num_rejects / duration_seconds);
3624 total_filter_stats.calls += num_calls;
3625 total_filter_stats.rejects += num_rejects;
3626 total_filter_stats.seconds += duration_seconds;
3628 absl::StrAppendFormat(
3629 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n", max_name_size,
3630 "Total", total_filter_stats.calls, total_filter_stats.rejects,
3631 total_filter_stats.seconds,
3632 total_filter_stats.rejects / total_filter_stats.seconds);
3639 if (last_operator_ != op->
Self()) {
3641 last_operator_ = op->
Self();
3643 make_next_neighbor_timer_.Start();
3649 if (!make_next_neighbor_timer_.IsRunning())
return;
3650 make_next_neighbor_timer_.Stop();
3651 operator_stats_[op->
Self()].make_next_neighbor_seconds +=
3652 make_next_neighbor_timer_.Get();
3653 if (neighbor_found) {
3654 operator_stats_[op->
Self()].neighbors++;
3659 bool neighbor_found)
override {
3660 if (neighbor_found) {
3661 operator_stats_[op->
Self()].filtered_neighbors++;
3665 accept_neighbor_timer_.Start();
3668 bool neighbor_found)
override {
3669 accept_neighbor_timer_.Stop();
3670 operator_stats_[op->
Self()].accept_neighbor_seconds +=
3671 accept_neighbor_timer_.Get();
3672 if (neighbor_found) {
3673 operator_stats_[op->
Self()].accepted_neighbors++;
3677 FilterStats& filter_stats =
3678 filter_stats_per_context_[
solver()->context()][filter];
3679 filter_stats.calls++;
3680 filter_timer_.Start();
3683 filter_timer_.Stop();
3684 auto& stats = filter_stats_per_context_[
solver()->context()][filter];
3685 stats.seconds += filter_timer_.Get();
3692 profiled_decision_builders_.push_back(profiled_db);
3699 if (last_operator_ !=
nullptr) {
3701 operator_stats_[last_operator_].seconds += timer_.Get();
3706 struct OperatorStats {
3707 int64_t neighbors = 0;
3708 int64_t filtered_neighbors = 0;
3709 int64_t accepted_neighbors = 0;
3711 double make_next_neighbor_seconds = 0;
3712 double accept_neighbor_seconds = 0;
3715 struct FilterStats {
3717 int64_t rejects = 0;
3721 WallTimer make_next_neighbor_timer_;
3722 WallTimer accept_neighbor_timer_;
3723 WallTimer filter_timer_;
3724 const LocalSearchOperator* last_operator_ =
nullptr;
3725 absl::flat_hash_map<const LocalSearchOperator*, OperatorStats>
3727 absl::flat_hash_map<
3728 std::string, absl::flat_hash_map<const LocalSearchFilter*, FilterStats>>
3729 filter_stats_per_context_;
3731 std::vector<ProfiledDecisionBuilder*> profiled_decision_builders_;
3739 local_search_profiler_->AddFirstSolutionProfiledDecisionBuilder(
3760 if (local_search_profiler_ !=
nullptr) {
3761 return local_search_profiler_->PrintOverview();
3767 if (local_search_profiler_ !=
nullptr) {
3768 return local_search_profiler_->ExportToLocalSearchStatistics();
3773void LocalSearchFilterManager::FindIncrementalEventEnd() {
3774 const int num_events = events_.size();
3775 incremental_events_end_ = num_events;
3776 int last_priority = -1;
3777 for (
int e = num_events - 1; e >= 0; --e) {
3778 const auto& [filter, event_type, priority] = events_[e];
3779 if (priority != last_priority) {
3780 incremental_events_end_ = e + 1;
3781 last_priority = priority;
3783 if (filter->IsIncremental())
break;
3788 std::vector<LocalSearchFilter*> filters)
3789 : synchronized_value_(
std::numeric_limits<int64_t>::min()),
3790 accepted_value_(
std::numeric_limits<int64_t>::min()) {
3791 events_.reserve(2 * filters.size());
3799 FindIncrementalEventEnd();
3803 std::vector<FilterEvent> filter_events)
3804 : events_(
std::move(filter_events)),
3805 synchronized_value_(
std::numeric_limits<int64_t>::min()),
3806 accepted_value_(
std::numeric_limits<int64_t>::min()) {
3807 std::sort(events_.begin(), events_.end(),
3809 return e1.priority < e2.priority;
3811 FindIncrementalEventEnd();
3817 for (
int e = last_event_called_; e >= 0; --e) {
3818 const auto [filter, event_type, _priority] = events_[e];
3821 last_event_called_ = -1;
3830 int64_t objective_min,
3831 int64_t objective_max) {
3833 accepted_value_ = 0;
3834 bool feasible =
true;
3835 bool reordered =
false;
3836 int events_end = events_.size();
3837 for (
int e = 0; e < events_end; ++e) {
3838 last_event_called_ = e;
3839 const auto [filter, event_type, priority] = events_[e];
3840 switch (event_type) {
3842 if (!feasible && !filter->IsIncremental())
continue;
3844 const bool accept = filter->Accept(
3845 delta, deltadelta,
CapSub(objective_min, accepted_value_),
3846 CapSub(objective_max, accepted_value_));
3848 if (monitor !=
nullptr) monitor->
EndFiltering(filter, !accept);
3851 CapAdd(accepted_value_, filter->GetAcceptedObjectiveValue());
3853 feasible = accepted_value_ <= objective_max;
3856 events_end = incremental_events_end_;
3861 int to_move = e - 1;
3862 if (to_move >= 0 && events_[to_move].filter == filter) --to_move;
3863 if (to_move >= 0 && events_[to_move].priority == priority) {
3864 std::rotate(events_.begin() + to_move,
3865 events_.begin() + to_move + 1,
3866 events_.begin() + e + 1);
3873 filter->Relax(delta, deltadelta);
3877 LOG(FATAL) <<
"Unknown filter event type.";
3888 const bool reset_to_assignment = delta ==
nullptr || delta->
Empty();
3890 for (
auto [filter, event_type, unused_priority] : events_) {
3891 switch (event_type) {
3896 if (reset_to_assignment) {
3898 filter->Relax(assignment,
nullptr);
3900 filter->Relax(delta,
nullptr);
3905 LOG(FATAL) <<
"Unknown filter event type.";
3910 synchronized_value_ = 0;
3912 switch (event_type) {
3914 filter->Synchronize(assignment, delta);
3915 synchronized_value_ =
CapAdd(synchronized_value_,
3916 filter->GetSynchronizedObjectiveValue());
3920 filter->Commit(assignment, delta);
3924 LOG(FATAL) <<
"Unknown filter event type.";
3941 std::string
DebugString()
const override {
return "FindOneNeighbor"; }
3945 int64_t objective_min, int64_t objective_max);
3946 void SynchronizeAll(
Solver* solver);
3949 IntVar*
const objective_;
3950 std::unique_ptr<Assignment> reference_assignment_;
3951 std::unique_ptr<Assignment> last_synchronized_assignment_;
3958 bool neighbor_found_;
3960 int64_t solutions_since_last_check_;
3961 int64_t check_period_;
3963 bool has_checked_assignment_ =
false;
3978 : assignment_(assignment),
3979 objective_(objective),
3980 reference_assignment_(new
Assignment(assignment_)),
3981 filter_assignment_delta_(assignment->solver()->MakeAssignment()),
3983 ls_operator_(ls_operator),
3984 sub_decision_builder_(sub_decision_builder),
3986 original_limit_(limit),
3987 neighbor_found_(false),
3988 filter_manager_(filter_manager),
3989 solutions_since_last_check_(0),
3991 assignment_->solver()->parameters().check_solution_period()),
3992 last_checked_assignment_(assignment) {
3993 CHECK(
nullptr != assignment);
3994 CHECK(
nullptr != ls_operator);
3996 Solver*
const solver = assignment_->solver();
3998 if (
nullptr == limit) {
4005 if (limit_->solutions() != 1) {
4006 VLOG(1) <<
"Disabling neighbor-check skipping outside of first accept.";
4013 VLOG(1) <<
"Disabling neighbor-check skipping for LNS.";
4017 if (!reference_assignment_->HasObjective()) {
4018 reference_assignment_->AddObjective(objective_);
4025 neighbor_found_ =
false;
4026 last_synchronized_assignment_.reset();
4030 CHECK(
nullptr != solver);
4032 if (original_limit_ !=
nullptr) {
4033 limit_->Copy(original_limit_);
4036 if (!last_checked_assignment_.HasObjective()) {
4037 last_checked_assignment_.AddObjective(assignment_->Objective());
4040 if (!neighbor_found_) {
4047 pool_->Initialize(assignment_);
4048 SynchronizeAll(solver);
4058 if (sub_decision_builder_) {
4059 restore = solver->
Compose(restore, sub_decision_builder_);
4064 if (!ls_operator_->HoldsDelta()) {
4068 deltadelta->
Clear();
4070 if (++counter >= absl::GetFlag(FLAGS_cp_local_search_sync_frequency) &&
4071 pool_->SyncNeeded(reference_assignment_.get())) {
4074 SynchronizeAll(solver);
4077 bool has_neighbor =
false;
4078 if (!limit_->Check()) {
4080 has_neighbor = ls_operator_->MakeNextNeighbor(delta, deltadelta);
4082 ls_operator_, has_neighbor, delta, deltadelta);
4085 if (has_neighbor && !solver->IsUncheckedSolutionLimitReached()) {
4086 solver->neighbors_ += 1;
4093 const bool mh_filter =
4094 AcceptDelta(solver->ParentSearch(), delta, deltadelta);
4095 int64_t objective_min = std::numeric_limits<int64_t>::min();
4096 int64_t objective_max = std::numeric_limits<int64_t>::max();
4098 objective_min = objective_->Min();
4099 objective_max = objective_->Max();
4102 objective_min = std::max(objective_min, delta->
ObjectiveMin());
4103 objective_max = std::min(objective_max, delta->
ObjectiveMax());
4105 const bool move_filter = FilterAccept(solver, delta, deltadelta,
4106 objective_min, objective_max);
4108 ls_operator_, mh_filter && move_filter);
4109 if (!mh_filter || !move_filter) {
4110 if (filter_manager_ !=
nullptr) filter_manager_->Revert();
4113 solver->filtered_neighbors_ += 1;
4118 if (!assignment_->HasObjective()) {
4119 assignment_->AddObjective(delta->
Objective());
4120 last_checked_assignment_.AddObjective(delta->
Objective());
4126 const bool check_solution = (solutions_since_last_check_ == 0) ||
4130 if (has_checked_assignment_) solutions_since_last_check_++;
4131 if (solutions_since_last_check_ >= check_period_) {
4132 solutions_since_last_check_ = 0;
4134 const bool accept = !check_solution ||
4140 solver->accepted_neighbors_ += 1;
4141 if (check_solution) {
4143 ls_operator_->DebugString());
4144 assignment_->Store();
4145 last_checked_assignment_.CopyIntersection(assignment_);
4146 neighbor_found_ =
true;
4147 has_checked_assignment_ =
true;
4151 ls_operator_->DebugString());
4152 assignment_->CopyIntersection(assignment_copy);
4153 assignment_->SetObjectiveValue(
4154 filter_manager_ ? filter_manager_->GetAcceptedObjectiveValue()
4161 solver->IncrementUncheckedSolutionCounter();
4162 pool_->RegisterNewSolution(assignment_);
4163 SynchronizeAll(solver);
4166 neighbor_found_ =
true;
4168 if (filter_manager_ !=
nullptr) filter_manager_->Revert();
4169 if (check_period_ > 1 && has_checked_assignment_) {
4175 VLOG(1) <<
"Imperfect filtering detected, backtracking to last "
4176 "checked solution and checking all solutions.";
4178 solutions_since_last_check_ = 0;
4179 pool_->RegisterNewSolution(&last_checked_assignment_);
4180 SynchronizeAll(solver);
4181 assignment_->CopyIntersection(&last_checked_assignment_);
4187 last_synchronized_assignment_.reset();
4188 if (neighbor_found_) {
4193 if (last_checked_assignment_.ObjectiveValue() !=
4194 assignment_->ObjectiveValue()) {
4199 last_checked_assignment_.CopyIntersection(assignment_);
4200 has_checked_assignment_ =
true;
4207 pool_->RegisterNewSolution(assignment_);
4208 SynchronizeAll(solver);
4218 last_synchronized_assignment_.reset();
4225 int64_t objective_min,
4226 int64_t objective_max) {
4227 if (filter_manager_ ==
nullptr)
return true;
4229 return filter_manager_->
Accept(monitor, delta, deltadelta, objective_min,
4235template <
typename Container>
4236void AddDeltaElements(
const Container& old_container,
4237 const Container& new_container, Assignment* delta) {
4238 for (
const auto& new_element : new_container.elements()) {
4239 const auto var = new_element.Var();
4240 const auto old_element_ptr = old_container.ElementPtrOrNull(var);
4241 if (old_element_ptr ==
nullptr || *old_element_ptr != new_element) {
4242 delta->FastAdd(var)->Copy(new_element);
4247void MakeDelta(
const Assignment* old_assignment,
4249 DCHECK_NE(delta,
nullptr);
4251 AddDeltaElements(old_assignment->IntVarContainer(),
4252 new_assignment->IntVarContainer(), delta);
4253 AddDeltaElements(old_assignment->IntervalVarContainer(),
4254 new_assignment->IntervalVarContainer(), delta);
4255 AddDeltaElements(old_assignment->SequenceVarContainer(),
4256 new_assignment->SequenceVarContainer(), delta);
4260void FindOneNeighbor::SynchronizeAll(
Solver* solver) {
4261 Assignment*
const reference_assignment = reference_assignment_.get();
4262 pool_->GetNextSolution(reference_assignment);
4263 neighbor_found_ =
false;
4265 solver->GetLocalSearchMonitor()->BeginOperatorStart();
4266 ls_operator_->Start(reference_assignment);
4267 if (filter_manager_ !=
nullptr) {
4268 Assignment* delta =
nullptr;
4269 if (last_synchronized_assignment_ ==
nullptr) {
4270 last_synchronized_assignment_ =
4271 std::make_unique<Assignment>(reference_assignment);
4273 MakeDelta(last_synchronized_assignment_.get(), reference_assignment,
4274 filter_assignment_delta_);
4275 delta = filter_assignment_delta_;
4276 last_synchronized_assignment_->Copy(reference_assignment);
4278 filter_manager_->Synchronize(reference_assignment_.get(), delta);
4280 solver->GetLocalSearchMonitor()->EndOperatorStart();
4293 solution_pool_(pool),
4300 return "LocalSearchPhaseParameters";
4307 return sub_decision_builder_;
4313 IntVar*
const objective_;
4325 ls_operator, sub_decision_builder,
4333 ls_operator, sub_decision_builder,
4342 ls_operator, sub_decision_builder,
4343 limit, filter_manager);
4351 sub_decision_builder,
nullptr,
nullptr);
4359 sub_decision_builder, limit,
nullptr);
4368 sub_decision_builder, limit,
4382class NestedSolveDecision :
public Decision {
4385 enum StateType { DECISION_PENDING, DECISION_FAILED, DECISION_FOUND };
4387 NestedSolveDecision(DecisionBuilder* absl_nonnull db,
bool restore,
4388 const std::vector<SearchMonitor*>& monitors);
4389 NestedSolveDecision(DecisionBuilder* absl_nonnull db,
bool restore);
4390 ~NestedSolveDecision()
override {}
4391 void Apply(Solver* solver)
override;
4392 void Refute(Solver* solver)
override;
4393 std::string DebugString()
const override {
return "NestedSolveDecision"; }
4394 int state()
const {
return state_; }
4397 DecisionBuilder*
const db_;
4398 const bool restore_;
4399 const std::vector<SearchMonitor*> monitors_;
4403NestedSolveDecision::NestedSolveDecision(
4405 const std::vector<SearchMonitor*>& monitors)
4408 monitors_(monitors),
4409 state_(DECISION_PENDING) {}
4411NestedSolveDecision::NestedSolveDecision(
DecisionBuilder* absl_nonnull db,
4413 : NestedSolveDecision(db, restore, {}) {}
4415void NestedSolveDecision::Apply(
Solver*
const solver) {
4416 CHECK(
nullptr != solver);
4418 if (solver->Solve(db_, monitors_)) {
4419 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FOUND));
4421 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FAILED));
4424 if (solver->SolveAndCommit(db_, monitors_)) {
4425 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FOUND));
4427 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FAILED));
4432void NestedSolveDecision::Refute(Solver*
const) {}
4475 void PushLocalSearchDecision();
4479 IntVar*
const objective_ =
nullptr;
4485 std::vector<NestedSolveDecision*> nested_decisions_;
4486 int nested_decision_index_;
4498 : assignment_(nullptr),
4499 objective_(objective),
4501 ls_operator_(ls_operator),
4502 first_solution_sub_decision_builder_(sub_decision_builder),
4503 sub_decision_builder_(sub_decision_builder),
4504 nested_decision_index_(0),
4506 filter_manager_(filter_manager),
4507 has_started_(false) {
4508 CHECK(
nullptr != assignment);
4509 CHECK(
nullptr != ls_operator);
4512 assignment_->
Copy(assignment);
4525 : assignment_(nullptr),
4526 objective_(objective),
4528 ls_operator_(ls_operator),
4529 first_solution_sub_decision_builder_(sub_decision_builder),
4530 sub_decision_builder_(sub_decision_builder),
4531 nested_decision_index_(0),
4533 filter_manager_(filter_manager),
4534 has_started_(false) {
4535 CHECK(
nullptr != first_solution);
4536 CHECK(
nullptr != ls_operator);
4537 CHECK(!vars.empty());
4538 Solver*
const solver = vars[0]->solver();
4540 assignment_->
Add(vars);
4546 const std::vector<IntVar*>& vars,
IntVar* objective,
4552 : assignment_(nullptr),
4553 objective_(objective),
4555 ls_operator_(ls_operator),
4556 first_solution_sub_decision_builder_(first_solution_sub_decision_builder),
4557 sub_decision_builder_(sub_decision_builder),
4558 nested_decision_index_(0),
4560 filter_manager_(filter_manager),
4561 has_started_(false) {
4562 CHECK(
nullptr != first_solution);
4563 CHECK(
nullptr != ls_operator);
4564 CHECK(!vars.empty());
4565 Solver*
const solver = vars[0]->solver();
4567 assignment_->
Add(vars);
4579 : assignment_(nullptr),
4580 objective_(objective),
4582 ls_operator_(ls_operator),
4583 first_solution_sub_decision_builder_(sub_decision_builder),
4584 sub_decision_builder_(sub_decision_builder),
4585 nested_decision_index_(0),
4587 filter_manager_(filter_manager),
4588 has_started_(false) {
4589 CHECK(
nullptr != first_solution);
4590 CHECK(
nullptr != ls_operator);
4591 CHECK(!vars.empty());
4592 Solver*
const solver = vars[0]->solver();
4594 assignment_->
Add(vars);
4603 DCHECK(assignment_ !=
nullptr);
4606 const std::vector<IntVarElement>& elements =
4607 assignment_->IntVarContainer().elements();
4608 if (!elements.empty()) {
4609 std::vector<IntVar*> vars;
4611 vars.push_back(elem.Var());
4616 const std::vector<IntervalVarElement>& interval_elements =
4617 assignment_->IntervalVarContainer().elements();
4618 if (!interval_elements.empty()) {
4619 std::vector<IntervalVar*> interval_vars;
4621 interval_vars.push_back(elem.Var());
4635 CHECK(
nullptr != solver);
4636 CHECK_LT(0, nested_decisions_.size());
4637 if (!has_started_) {
4638 nested_decision_index_ = 0;
4640 find_neighbors_db_->EnterSearch();
4641 ls_operator_->EnterSearch();
4642 }
else if (nested_decision_index_ < 0) {
4645 NestedSolveDecision* decision = nested_decisions_[nested_decision_index_];
4646 const int state = decision->state();
4648 case NestedSolveDecision::DECISION_FAILED: {
4653 const bool continue_at_local_optimum =
4654 nested_decision_index_ == nested_decisions_.size() - 1 &&
4656 if (continue_at_local_optimum) {
4660 ls_operator_->Reset();
4662 if (!continue_at_local_optimum ||
4663 solver->IsUncheckedSolutionLimitReached()) {
4664 nested_decision_index_ = -1;
4669 case NestedSolveDecision::DECISION_PENDING: {
4672 const int32_t kLocalSearchBalancedTreeDepth = 32;
4674 if (depth < kLocalSearchBalancedTreeDepth) {
4677 if (depth > kLocalSearchBalancedTreeDepth) {
4682 case NestedSolveDecision::DECISION_FOUND: {
4684 if (nested_decision_index_ + 1 < nested_decisions_.size()) {
4685 ++nested_decision_index_;
4690 LOG(ERROR) <<
"Unknown local search state";
4698 CHECK(first_solution);
4699 Solver*
const solver = assignment_->solver();
4703 first_solution_sub_decision_builder_, store);
4704 std::vector<SearchMonitor*> monitor;
4705 monitor.push_back(limit_);
4706 nested_decisions_.push_back(solver->
RevAlloc(
4707 new NestedSolveDecision(first_solution_and_store,
false, monitor)));
4711 Solver*
const solver = assignment_->solver();
4712 find_neighbors_db_ = solver->
RevAlloc(
4714 sub_decision_builder_, limit_, filter_manager_));
4715 nested_decisions_.push_back(
4716 solver->
RevAlloc(
new NestedSolveDecision(find_neighbors_db_,
false)));
4726 reference_assignment_ = std::make_unique<Assignment>(assignment);
4730 reference_assignment_->CopyIntersection(assignment);
4739 std::string
DebugString()
const override {
return "DefaultSolutionPool"; }
4742 std::unique_ptr<Assignment> reference_assignment_;
4773 first_solution, first_solution_sub_decision_builder,
4779 const std::vector<SequenceVar*>& vars,
DecisionBuilder* first_solution,
4788template <
bool is_profile_active>
4789Assignment* Solver::RunUncheckedLocalSearchInternal(
4792 const std::vector<SearchMonitor*>& monitors,
RegularLimit* limit,
4793 absl::flat_hash_set<IntVar*>* touched) {
4794 DCHECK(initial_solution !=
nullptr);
4795 DCHECK(initial_solution->
Objective() !=
nullptr);
4796 DCHECK(filter_manager !=
nullptr);
4797 if (limit !=
nullptr) limit->
Init();
4801 current_solution->
Copy(initial_solution);
4806 const auto sync_with_solution =
4807 [
this, ls_monitor, ls_operator,
4809 filter_manager, current_solution](
Assignment* delta) {
4810 IncrementUncheckedSolutionCounter();
4811 if constexpr (is_profile_active) {
4814 ls_operator->Start(current_solution);
4815 filter_manager->Synchronize(current_solution, delta);
4816 if constexpr (is_profile_active) {
4820 sync_with_solution(
nullptr);
4822 if (!ls_operator->HoldsDelta()) {
4825 delta.ClearObjective();
4827 if (limit !=
nullptr && (limit->
CheckWithOffset(absl::ZeroDuration()) ||
4831 if constexpr (is_profile_active) {
4834 const bool has_neighbor =
4835 ls_operator->MakeNextNeighbor(&delta, &deltadelta);
4836 if constexpr (is_profile_active) {
4840 if (!has_neighbor) {
4843 if constexpr (is_profile_active) {
4847 const bool mh_accept = monitor->
AcceptDelta(&delta, &deltadelta);
4850 const bool filter_accept =
4851 filter_manager->Accept(ls_monitor, &delta, &deltadelta,
4852 delta.ObjectiveMin(), delta.ObjectiveMax());
4853 if constexpr (is_profile_active) {
4858 if (!filter_accept) {
4859 filter_manager->Revert();
4862 filtered_neighbors_ += 1;
4865 filter_manager->GetAcceptedObjectiveValue());
4866 DCHECK(delta.AreAllElementsBound());
4867 accepted_neighbors_ += 1;
4872 if (touched !=
nullptr) {
4873 for (
const auto& element : delta.IntVarContainer().elements()) {
4874 touched->insert(element.Var());
4878 sync_with_solution(&delta);
4880 if (parameters_.print_local_search_profile()) {
4882 if (!profile.empty()) LOG(INFO) << profile;
4890 const std::vector<SearchMonitor*>& monitors,
RegularLimit* limit,
4891 absl::flat_hash_set<IntVar*>* touched) {
4893 return RunUncheckedLocalSearchInternal<true>(initial_solution,
4894 filter_manager, ls_operator,
4895 monitors, limit, touched);
4897 return RunUncheckedLocalSearchInternal<false>(initial_solution,
4898 filter_manager, ls_operator,
4899 monitors, limit, touched);
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)
std::string DebugString() const override
bool AreAllElementsBound() const
void Copy(const Assignment *assignment)
int64_t ObjectiveMin() const
AssignmentContainer< IntVar, IntVarElement > IntContainer
IntVar * Objective() const
int64_t ObjectiveMax() const
const IntContainer & IntVarContainer() const
void CopyIntersection(const Assignment *assignment)
void AddObjective(IntVar *const v)
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, NeighborAccessor get_incoming_neighbors=nullptr, NeighborAccessor get_outgoing_neighbors=nullptr)
~BaseInactiveNodeToPathOperator() override=default
int64_t GetInactiveNode() const
bool MakeOneNeighbor() override
MakeNextNeighbor() in a subclass of IntVarLocalSearchOperator.
Here's a sample relaxing one variable at a time:
void AppendToFragment(int index)
virtual void InitFragments()
BaseLns(const std::vector< IntVar * > &vars)
bool MakeOneNeighbor() override
This method should not be overridden. Override NextFragment() instead.
virtual bool NextFragment()=0
virtual std::string DebugString() const
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)
bool MakeNeighbor() override
std::string DebugString() const override
Cross(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, NeighborAccessor get_incoming_neighbors, NeighborAccessor get_outgoing_neighbors)
~Cross() override=default
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
bool MakeNeighbor() override
ExchangeAndMakeActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
bool OnSamePathAsPreviousBase(int64_t base_index) override
it's currently way more complicated to implement.
std::string DebugString() const override
~ExchangeAndMakeActiveOperator() override
ExchangePathStartEndsAndMakeActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
int64_t GetBaseNodeRestartPosition(int base_index) override
std::string DebugString() const override
~ExchangePathStartEndsAndMakeActiveOperator() override=default
bool MakeNeighbor() override
bool MakeNeighbor() override
~Exchange() override=default
Exchange(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, NeighborAccessor get_incoming_neighbors, NeighborAccessor get_outgoing_neighbors)
std::string DebugString() const override
std::string DebugString() const override
ExtendedSwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
bool MakeNeighbor() override
~ExtendedSwapActiveOperator() override=default
std::string DebugString() const override
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)
IntVarLocalSearchFilter(const std::vector< IntVar * > &vars)
bool FindIndex(IntVar *const var, int64_t *index) const
virtual void OnSynchronize(const Assignment *)
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
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
IntVarLocalSearchOperator(const std::vector< IntVar * > &vars, bool keep_inverse_values=false)
virtual bool MakeOneNeighbor()
MakeNextNeighbor() in a subclass of IntVarLocalSearchOperator.
IntVar * Var() override
Creates a variable from the expression.
virtual bool Contains(int64_t v) const =0
std::string DebugString() const override
~LinKernighan() override=default
LinKernighan(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, const Solver::IndexEvaluator3 &evaluator, bool topt)
bool MakeNeighbor() override
LocalSearchFilterManager(std::vector< FilterEvent > filter_events)
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)
LocalSearchMonitor(Solver *solver)
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
The base class for all local search operators.
virtual const LocalSearchOperator * Self() const
virtual bool HasFragments() const
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
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)
bool StateIsFeasible() const
int64_t VariableDomainMin(VariableDomainId domain_id) const
VariableDomainId AddVariableDomain(int64_t relaxed_min, int64_t relaxed_max)
bool TightenVariableDomainMin(VariableDomainId domain_id, int64_t value)
void ChangeRelaxedVariableDomain(VariableDomainId domain_id, int64_t min, int64_t max)
void PropagateRelax(VariableDomainId domain_id)
static Variable DummyVariable()
Variable MakeVariable(VariableDomainId domain_id)
Variable MakeVariableWithRelaxedDomain(int64_t min, int64_t max)
bool TightenVariableDomainMax(VariableDomainId domain_id, int64_t value)
void CompileConstraints()
int64_t VariableDomainMax(VariableDomainId domain_id) const
bool PropagateTighten(VariableDomainId domain_id)
bool RelaxVariableDomain(VariableDomainId domain_id)
void set_duration_seconds(double value)
void set_strategy(Arg_ &&arg, Args_... args)
void set_context(Arg_ &&arg, Args_... args)
void set_num_rejects(::int64_t value)
void set_num_calls(::int64_t value)
void set_num_rejects_per_second(double value)
void set_duration_seconds(double value)
void set_local_search_filter(Arg_ &&arg, Args_... args)
void set_num_filtered_neighbors(::int64_t value)
void set_local_search_operator(Arg_ &&arg, Args_... args)
void set_num_neighbors(::int64_t value)
void set_make_next_neighbor_duration_seconds(double value)
void set_num_accepted_neighbors(::int64_t value)
void set_duration_seconds(double value)
void set_accept_neighbor_duration_seconds(double value)
LocalSearchStatistics_FirstSolutionStatistics FirstSolutionStatistics
void set_total_num_neighbors(::int64_t value)
void set_total_num_filtered_neighbors(::int64_t value)
::operations_research::LocalSearchStatistics_LocalSearchOperatorStatistics *PROTOBUF_NONNULL add_local_search_operator_statistics()
void set_total_num_accepted_neighbors(::int64_t value)
::operations_research::LocalSearchStatistics_LocalSearchFilterStatistics *PROTOBUF_NONNULL add_local_search_filter_statistics()
LocalSearchStatistics_LocalSearchFilterStatistics LocalSearchFilterStatistics
LocalSearchStatistics_LocalSearchOperatorStatistics LocalSearchOperatorStatistics
::operations_research::LocalSearchStatistics_FirstSolutionStatistics *PROTOBUF_NONNULL add_first_solution_statistics()
void PushFirstSolutionDecision(DecisionBuilder *first_solution)
std::string DebugString() const override
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
Decision * Next(Solver *solver) override
void PushLocalSearchDecision()
bool MakeNeighbor() override
~MakeActiveAndRelocateOperator() override=default
std::string DebugString() const override
MakeActiveAndRelocateOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
~MakeActiveOperator() override=default
bool MakeNeighbor() override
std::string DebugString() const override
MakeActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, NeighborAccessor get_incoming_neighbors, NeighborAccessor get_outgoing_neighbors)
bool OnSamePathAsPreviousBase(int64_t) override
it's currently way more complicated to implement.
MakeChainInactiveOperator(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
int64_t GetBaseNodeRestartPosition(int base_index) override
bool MakeNeighbor() override
~MakeChainInactiveOperator() override=default
std::string DebugString() const override
MakeInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
bool MakeNeighbor() override
~MakeInactiveOperator() override=default
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
virtual ~NearestNeighbors()=default
NearestNeighbors & operator=(const NearestNeighbors &)=delete
virtual std::string DebugString() const
NearestNeighbors(const NearestNeighbors &)=delete
NearestNeighbors(Solver::IndexEvaluator3 evaluator, const PathOperator< ignore_path_vars > &path_operator, int size)
void Initialize(absl::Span< const int > path)
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
bool HasFragments() const override
std::string DebugString() const override
PathLns(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, int number_of_chunks, int chunk_size, bool unactive_fragments)
bool MakeNeighbor() override
~PathLns() override=default
bool HasNeighbors() const
int number_of_nexts() const
Number of next variables.
bool MakeChainInactive(int64_t before_chain, int64_t chain_end)
int64_t Prev(int64_t node) const
Returns the node before node in the current delta.
int PathClassFromStartNode(int64_t start_node) const
int64_t Path(int64_t node) const
bool MakeActive(int64_t node, int64_t destination)
Insert the inactive node after destination.
int64_t OldNext(int64_t node) const
bool SwapNodes(int64_t node1, int64_t node2)
Swaps the nodes node1 and node2.
void SetNext(int64_t from, int64_t to, int64_t path)
Sets 'to' to be the node after 'from' on the given path.
int CurrentNodePathStart(int64_t node) const
virtual void OnNodeInitialization()
bool IsPathStart(int64_t node) const
Returns true if node is the first node on the path.
bool MoveChain(int64_t before_chain, int64_t chain_end, int64_t destination)
bool IsPathEnd(int64_t node) const
int64_t EndNode(int i) const
Returns the end node of the ith base node.
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 IsInactive(int64_t node) const
Returns true if node is inactive.
bool CheckChainValidity(int64_t before_chain, int64_t chain_end, int64_t exclude) const
bool ReverseChain(int64_t before_chain, int64_t after_chain, int64_t *chain_last)
virtual void SetNextBaseToIncrement(int64_t base_index)
Neighbor GetNeighborForBaseNode(int64_t base_index) const
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
int64_t StartNode(int i) const
Returns the start node of the ith base node.
int64_t BaseNode(int i) const
Returns the ith base node of the operator.
int CurrentNodePathEnd(int64_t node) const
bool IsUncheckedSolutionLimitReached() override
bool CheckWithOffset(absl::Duration offset) override
void Init() override
This method is called when the search limit is initialized.
RegularLimit * MakeIdenticalClone() const
std::string DebugString() const override
bool MakeNeighbor() override
RelocateAndMakeActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
~RelocateAndMakeActiveOperator() override=default
std::string DebugString() const override
RelocateAndMakeInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
~RelocateAndMakeInactiveOperator() override=default
bool MakeNeighbor() override
bool OnSamePathAsPreviousBase(int64_t) override
it's currently way more complicated to implement.
std::string DebugString() const override
~Relocate() override=default
bool MakeNeighbor() override
Relocate(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, absl::string_view name, std::function< int(int64_t)> start_empty_path_class, NeighborAccessor get_incoming_neighbors, NeighborAccessor get_outgoing_neighbors, int64_t chain_length=1LL, bool single_path=false)
virtual bool AtSolution()
virtual bool AcceptDelta(Assignment *delta, Assignment *deltadelta)
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)
Example:
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)
std::function< int64_t(int64_t, int64_t, int64_t)> IndexEvaluator3
LocalSearchStatistics GetLocalSearchStatistics() const
Returns detailed local search statistics.
DecisionBuilder * Compose(DecisionBuilder *db1, DecisionBuilder *db2)
std::string LocalSearchProfile() const
Returns local search profiling information in a human readable format.
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)
LocalSearchOperator * MakeOperator(const std::vector< IntVar * > &vars, LocalSearchOperators op, std::function< const std::vector< int > &(int, int)> get_incoming_neighbors=nullptr, std::function< const std::vector< int > &(int, int)> get_outgoing_neighbors=nullptr)
Local Search Operators.
@ LE
Move is accepted when the current objective value <= objective.Max.
@ GE
Move is accepted when the current objective value >= objective.Min.
std::function< int64_t(int64_t, int64_t)> IndexEvaluator2
LocalSearchFilter * MakeRejectFilter()
friend class SearchMonitor
DecisionBuilder * MakeStoreAssignment(Assignment *assignment)
LocalSearchOperator * MultiArmedBanditConcatenateOperators(const std::vector< LocalSearchOperator * > &ops, double memory_coefficient, double exploration_coefficient, bool maximize)
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()
Returns (or creates) an assignment representing the state of local search.
bool AcceptSolution(Search *search) const
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)
bool MakeNeighbor() override
void ResetIncrementalism() override
~SwapActiveChainOperator() override=default
SwapActiveChainOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, int max_chain_size)
bool OnSamePathAsPreviousBase(int64_t) override
it's currently way more complicated to implement.
std::string DebugString() const override
bool IsIncremental() const override
~SwapActiveOperator() override=default
SwapActiveOperator(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
~TSPLns() override=default
std::string DebugString() const override
bool MakeNeighbor() override
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
TSPLns(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int tsp_size)
TSPOpt(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int chain_length)
~TSPOpt() override=default
std::string DebugString() const override
bool MakeNeighbor() override
bool IsIncremental() const override
void ResetIncrementalism() override
bool OnSamePathAsPreviousBase(int64_t) override
it's currently way more complicated to implement.
int64_t GetBaseNodeRestartPosition(int base_index) override
~TwoOpt() override=default
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, NeighborAccessor get_incoming_neighbors, NeighborAccessor get_outgoing_neighbors)
bool MakeNeighbor() override
ABSL_FLAG(int, cp_local_search_sync_frequency, 16, "Frequency of checks for better solutions in the solution pool.")
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
std::function< const std::vector< int > &(int, int)> NeighborAccessor
void DeleteLocalSearchProfiler(LocalSearchProfiler *monitor)
LocalSearchOperator * MakeExtendedSwapActive(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
--— ExtendedSwapActive --—
int64_t CapAdd(int64_t x, int64_t y)
LocalSearchOperator * RelocateAndMakeInactive(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
--— RelocateAndMakeInactive --—
LocalSearchOperator * MakeTSPOpt(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int chain_length)
--— TSP-based operators --—
LocalSearchOperator * MakeCross(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_incoming_neighbors=nullptr, std::function< const std::vector< int > &(int, int)> get_outgoing_neighbors=nullptr)
--— Cross --—
SubDagComputer::ArcId ArcId
LocalSearchOperator * MakeSwapActive(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
--— SwapActive --—
LocalSearchOperator * ExchangeAndMakeActive(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
void AcceptNeighbor(Search *search)
LocalSearchOperator * MakeExchange(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_incoming_neighbors=nullptr, std::function< const std::vector< int > &(int, int)> get_outgoing_neighbors=nullptr)
--— Exchange --—
void AcceptUncheckedNeighbor(Search *search)
int64_t CapSub(int64_t x, int64_t y)
LocalSearchOperator * MakeActiveAndRelocate(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
--— MakeActiveAndRelocate --—
LocalSearchOperator * MakeTSPLns(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int tsp_size)
ClosedInterval::Iterator end(ClosedInterval interval)
LocalSearchOperator * MakeRelocate(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_incoming_neighbors=nullptr, std::function< const std::vector< int > &(int, int)> get_outgoing_neighbors=nullptr, int64_t chain_length=1LL, bool single_path=false, const std::string &name="Relocate")
--— Relocate --—
LocalSearchOperator * RelocateAndMakeActive(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
-— RelocateAndMakeActive --—
LocalSearchOperator * MakePathLns(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, int number_of_chunks, int chunk_size, bool unactive_fragments)
--— Path-based Large Neighborhood Search --—
LocalSearchOperator * MakeInactive(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
--— MakeInactive --—
LocalSearchOperator * MakeTwoOpt(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_incoming_neighbors=nullptr, std::function< const std::vector< int > &(int, int)> get_outgoing_neighbors=nullptr)
--— 2Opt --—
LocalSearchOperator * MakeChainInactive(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
--— MakeChainInactive --—
void InstallLocalSearchProfiler(LocalSearchProfiler *monitor)
int64_t CapProd(int64_t x, int64_t y)
LocalSearchProfiler * BuildLocalSearchProfiler(Solver *solver)
LocalSearchOperator * ExchangePathStartEndsAndMakeActive(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
LocalSearchOperator * MakeSwapActiveChain(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, int max_chain_size)
--— SwapActiveChain --—
LocalSearchOperator * MakeLinKernighan(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, const Solver::IndexEvaluator3 &evaluator, bool topt)
--— Lin-Kernighan --—
bool ContinueAtLocalOptimum(Search *search)
LocalSearchState::VariableDomainId VariableDomainId
ClosedInterval::Iterator begin(ClosedInterval interval)
LocalSearchOperator * MakeActive(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_incoming_neighbors=nullptr, std::function< const std::vector< int > &(int, int)> get_outgoing_neighbors=nullptr)
--— MakeActive --—
bool AcceptDelta(Search *search, Assignment *delta, Assignment *deltadelta)
SubDagComputer::NodeId NodeId
static int input(yyscan_t yyscanner)
static const int64_t kint64max
static const int32_t kint32max
static const int64_t kint64min