26 #include "absl/container/flat_hash_map.h"
27 #include "absl/container/flat_hash_set.h"
28 #include "absl/memory/memory.h"
29 #include "absl/random/distributions.h"
30 #include "absl/random/random.h"
31 #include "absl/strings/str_cat.h"
45 "Frequency of checks for better solutions in the solution pool.");
48 "Size of TSPs solved in the TSPOpt operator.");
51 "Size of TSPs solved in the TSPLns operator.");
53 ABSL_FLAG(
bool, cp_use_empty_path_symmetry_breaker,
true,
54 "If true, equivalent empty paths are removed from the neighborhood "
68 Assignment* deltadelta);
80 <<
delta->DebugString() <<
"), deltadelta=("
81 << (deltadelta ? deltadelta->
DebugString() : std::string(
"nullptr"));
109 for (
int candidate : fragment_) {
123 fragment_.push_back(
index);
134 class SimpleLns :
public BaseLns {
136 SimpleLns(
const std::vector<IntVar*>& vars,
int number_of_variables)
137 :
BaseLns(vars), index_(0), number_of_variables_(number_of_variables) {
140 ~SimpleLns()
override {}
141 void InitFragments()
override { index_ = 0; }
142 bool NextFragment()
override;
143 std::string DebugString()
const override {
return "SimpleLns"; }
147 const int number_of_variables_;
150 bool SimpleLns::NextFragment() {
151 const int size = Size();
153 for (
int i = index_; i < index_ + number_of_variables_; ++i) {
154 AppendToFragment(i % size);
166 class RandomLns :
public BaseLns {
168 RandomLns(
const std::vector<IntVar*>& vars,
int number_of_variables,
170 : BaseLns(vars), rand_(seed), number_of_variables_(number_of_variables) {
172 CHECK_LE(number_of_variables_, Size());
174 ~RandomLns()
override {}
175 bool NextFragment()
override;
177 std::string DebugString()
const override {
return "RandomLns"; }
181 const int number_of_variables_;
184 bool RandomLns::NextFragment() {
186 for (
int i = 0; i < number_of_variables_; ++i) {
187 AppendToFragment(absl::Uniform<int>(rand_, 0, Size()));
194 const std::vector<IntVar*>& vars,
int number_of_variables) {
199 const std::vector<IntVar*>& vars,
int number_of_variables, int32_t seed) {
200 return RevAlloc(
new RandomLns(vars, number_of_variables, seed));
211 MoveTowardTargetLS(
const std::vector<IntVar*>& variables,
212 const std::vector<int64_t>& target_values)
214 target_(target_values),
218 variable_index_(Size() - 1) {
219 CHECK_EQ(target_values.size(), variables.size()) <<
"Illegal arguments.";
222 ~MoveTowardTargetLS()
override {}
224 std::string DebugString()
const override {
return "MoveTowardTargetLS"; }
228 bool MakeOneNeighbor()
override {
229 while (num_var_since_last_start_ < Size()) {
230 ++num_var_since_last_start_;
231 variable_index_ = (variable_index_ + 1) % Size();
232 const int64_t target_value = target_.at(variable_index_);
233 const int64_t current_value = OldValue(variable_index_);
234 if (current_value != target_value) {
235 SetValue(variable_index_, target_value);
243 void OnStart()
override {
257 num_var_since_last_start_ = 0;
261 const std::vector<int64_t> target_;
264 int64_t variable_index_;
267 int64_t num_var_since_last_start_;
273 typedef std::vector<IntVarElement> Elements;
276 std::vector<IntVar*> vars;
277 std::vector<int64_t> values;
280 for (
const auto& it : elements) {
281 vars.push_back(it.Var());
282 values.push_back(it.Value());
288 const std::vector<IntVar*>& variables,
289 const std::vector<int64_t>& target_values) {
290 return RevAlloc(
new MoveTowardTargetLS(variables, target_values));
301 const int size =
Size();
302 while (index_ < size) {
311 void ChangeValue::OnStart() { index_ = 0; }
316 class IncrementValue :
public ChangeValue {
318 explicit IncrementValue(
const std::vector<IntVar*>& vars)
319 : ChangeValue(vars) {}
320 ~IncrementValue()
override {}
321 int64_t ModifyValue(int64_t
index, int64_t
value)
override {
325 std::string DebugString()
const override {
return "IncrementValue"; }
330 class DecrementValue :
public ChangeValue {
332 explicit DecrementValue(
const std::vector<IntVar*>& vars)
333 : ChangeValue(vars) {}
334 ~DecrementValue()
override {}
335 int64_t ModifyValue(int64_t
index, int64_t
value)
override {
339 std::string DebugString()
const override {
return "DecrementValue"; }
346 const std::vector<IntVar*>& path_vars,
347 int number_of_base_nodes,
348 bool skip_locally_optimal_paths,
349 bool accept_path_end_base,
350 std::function<
int(int64_t)> start_empty_path_class)
352 number_of_nexts_(next_vars.size()),
353 ignore_path_vars_(path_vars.empty()),
354 next_base_to_increment_(number_of_base_nodes),
355 base_nodes_(number_of_base_nodes),
356 base_alternatives_(number_of_base_nodes),
357 base_sibling_alternatives_(number_of_base_nodes),
358 end_nodes_(number_of_base_nodes),
359 base_paths_(number_of_base_nodes),
360 just_started_(false),
362 accept_path_end_base_(accept_path_end_base),
363 start_empty_path_class_(std::move(start_empty_path_class)),
364 skip_locally_optimal_paths_(skip_locally_optimal_paths),
365 optimal_paths_enabled_(false),
366 alternative_index_(next_vars.size(), -1) {
371 path_basis_.push_back(0);
372 for (
int i = 1; i < base_nodes_.size(); ++i) {
375 if ((path_basis_.size() > 2) ||
376 (!next_vars.empty() && !next_vars.back()
379 .skip_locally_optimal_paths())) {
380 skip_locally_optimal_paths_ =
false;
386 void PathOperator::OnStart() {
387 optimal_paths_enabled_ =
false;
388 InitializeBaseNodes();
389 InitializeAlternatives();
394 while (IncrementPosition()) {
418 int64_t destination) {
419 if (destination == before_chain || destination == chain_end)
return false;
422 const int64_t destination_path =
Path(destination);
423 const int64_t after_chain =
Next(chain_end);
424 SetNext(chain_end,
Next(destination), destination_path);
426 int current = destination;
428 while (current != chain_end) {
434 SetNext(destination,
Next(before_chain), destination_path);
436 SetNext(before_chain, after_chain,
Path(before_chain));
441 int64_t* chain_last) {
443 int64_t path =
Path(before_chain);
444 int64_t current =
Next(before_chain);
445 if (current == after_chain) {
448 int64_t current_next =
Next(current);
449 SetNext(current, after_chain, path);
450 while (current_next != after_chain) {
451 const int64_t
next =
Next(current_next);
452 SetNext(current_next, current, path);
453 current = current_next;
456 SetNext(before_chain, current, path);
457 *chain_last = current;
465 int64_t destination_path =
Path(destination);
466 SetNext(node,
Next(destination), destination_path);
467 SetNext(destination, node, destination_path);
474 const int64_t kNoPath = -1;
477 const int64_t after_chain =
Next(chain_end);
478 int64_t current =
Next(before_chain);
479 while (current != after_chain) {
481 SetNext(current, current, kNoPath);
484 SetNext(before_chain, after_chain,
Path(before_chain));
491 if (active == inactive)
return false;
492 const int64_t prev =
Prev(active);
496 bool PathOperator::IncrementPosition() {
497 const int base_node_size = base_nodes_.size();
499 if (!just_started_) {
500 const int number_of_paths = path_starts_.size();
506 int last_restarted = base_node_size;
507 for (
int i = base_node_size - 1; i >= 0; --i) {
511 const int sibling_alternative_index =
513 if (sibling_alternative_index >= 0) {
514 if (base_sibling_alternatives_[i] <
515 alternative_sets_[sibling_alternative_index].size() - 1) {
516 ++base_sibling_alternatives_[i];
519 base_sibling_alternatives_[i] = 0;
522 const int alternative_index = alternative_index_[base_nodes_[i]];
523 if (alternative_index >= 0) {
524 if (base_alternatives_[i] <
525 alternative_sets_[alternative_index].size() - 1) {
526 ++base_alternatives_[i];
529 base_alternatives_[i] = 0;
530 base_sibling_alternatives_[i] = 0;
533 base_alternatives_[i] = 0;
534 base_sibling_alternatives_[i] = 0;
535 base_nodes_[i] =
OldNext(base_nodes_[i]);
536 if (accept_path_end_base_ || !
IsPathEnd(base_nodes_[i]))
break;
538 base_alternatives_[i] = 0;
539 base_sibling_alternatives_[i] = 0;
553 for (
int i = last_restarted; i < base_node_size; ++i) {
554 base_alternatives_[i] = 0;
555 base_sibling_alternatives_[i] = 0;
558 if (last_restarted > 0) {
564 if (optimal_paths_enabled_ && skip_locally_optimal_paths_) {
565 if (path_basis_.size() > 1) {
566 for (
int i = 1; i < path_basis_.size(); ++i) {
576 std::vector<int> current_starts(base_node_size);
577 for (
int i = 0; i < base_node_size; ++i) {
582 optimal_paths_enabled_ =
true;
584 for (
int i = base_node_size - 1; i >= 0; --i) {
585 const int next_path_index = base_paths_[i] + 1;
586 if (next_path_index < number_of_paths) {
587 base_paths_[i] = next_path_index;
588 base_alternatives_[i] = 0;
589 base_sibling_alternatives_[i] = 0;
590 base_nodes_[i] = path_starts_[next_path_index];
596 base_alternatives_[i] = 0;
597 base_sibling_alternatives_[i] = 0;
598 base_nodes_[i] = path_starts_[0];
601 if (!skip_locally_optimal_paths_)
return CheckEnds();
604 if (path_basis_.size() > 1) {
605 for (
int j = 1; j < path_basis_.size(); ++j) {
607 path_basis_[j - 1])] +
621 if (!CheckEnds())
return false;
623 for (
int i = 0; i < base_node_size; ++i) {
629 if (stop)
return false;
632 just_started_ =
false;
638 void PathOperator::InitializePathStarts() {
646 has_prevs[
next] =
true;
651 if (optimal_paths_.empty() && skip_locally_optimal_paths_) {
663 if (skip_locally_optimal_paths_) {
683 std::vector<int64_t> new_path_starts;
684 const bool use_empty_path_symmetry_breaker =
685 absl::GetFlag(FLAGS_cp_use_empty_path_symmetry_breaker);
689 if (start_empty_path_class_ !=
nullptr) {
690 if (empty_found[start_empty_path_class_(i)])
continue;
691 empty_found[start_empty_path_class_(i)] =
true;
694 new_path_starts.push_back(i);
702 std::vector<int> node_paths(max_next + 1, -1);
703 for (
int i = 0; i < path_starts_.size(); ++i) {
704 int node = path_starts_[i];
706 node_paths[node] = i;
709 node_paths[node] = i;
711 for (
int j = 0; j < base_nodes_.size(); ++j) {
713 base_alternatives_[j] = 0;
714 base_sibling_alternatives_[j] = 0;
715 if (
IsInactive(base_nodes_[j]) || node_paths[base_nodes_[j]] == -1) {
718 base_nodes_[j] = path_starts_[base_paths_[j]];
720 base_paths_[j] = node_paths[base_nodes_[j]];
727 absl::flat_hash_set<int> found_bases;
728 for (
int i = 0; i < path_starts_.size(); ++i) {
729 int index = new_index;
731 while (
index < new_path_starts.size() &&
732 new_path_starts[
index] < path_starts_[i]) {
735 const bool found = (
index < new_path_starts.size() &&
736 new_path_starts[
index] == path_starts_[i]);
740 for (
int j = 0; j < base_nodes_.size(); ++j) {
742 found_bases.insert(j);
743 base_paths_[j] = new_index;
747 base_nodes_[j] = new_path_starts[new_index];
753 path_starts_.swap(new_path_starts);
756 void PathOperator::InitializeInactives() {
759 inactives_.push_back(
OldNext(i) == i);
763 void PathOperator::InitializeBaseNodes() {
765 InitializeInactives();
766 InitializePathStarts();
770 for (
int i = 0; i < base_nodes_.size(); ++i) {
772 base_nodes_[i] = path_starts_[0];
774 first_start_ =
false;
776 for (
int i = 0; i < base_nodes_.size(); ++i) {
778 int64_t base_node = base_nodes_[i];
780 base_node = path_starts_[base_paths_[i]];
781 base_nodes_[i] = base_node;
783 end_nodes_[i] = base_node;
787 for (
int i = 1; i < base_nodes_.size(); ++i) {
789 !OnSamePath(base_nodes_[i - 1], base_nodes_[i])) {
790 const int64_t base_node = base_nodes_[i - 1];
791 base_nodes_[i] = base_node;
792 end_nodes_[i] = base_node;
793 base_paths_[i] = base_paths_[i - 1];
796 for (
int i = 0; i < base_nodes_.size(); ++i) {
797 base_alternatives_[i] = 0;
798 base_sibling_alternatives_[i] = 0;
800 just_started_ =
true;
803 void PathOperator::InitializeAlternatives() {
804 active_in_alternative_set_.resize(alternative_sets_.size(), -1);
805 for (
int i = 0; i < alternative_sets_.size(); ++i) {
806 const int64_t current_active = active_in_alternative_set_[i];
807 if (current_active >= 0 && !
IsInactive(current_active))
continue;
808 for (int64_t
index : alternative_sets_[i]) {
810 active_in_alternative_set_[i] =
index;
817 bool PathOperator::OnSamePath(int64_t node1, int64_t node2)
const {
840 int64_t exclude)
const {
841 if (before_chain == chain_end || before_chain == exclude)
return false;
842 int64_t current = before_chain;
844 while (current != chain_end) {
851 current =
Next(current);
853 if (current == exclude) {
873 const std::vector<IntVar*>& secondary_vars,
874 std::function<
int(int64_t)> start_empty_path_class)
876 std::move(start_empty_path_class)),
895 void OnNodeInitialization()
override { last_ = -1; }
903 if (last_base_ !=
BaseNode(0) || last_ == -1) {
915 && last_ != chain_last) {
921 const int64_t to_move =
Next(last_);
944 const std::vector<IntVar*>& secondary_vars,
const std::string&
name,
945 std::function<
int(int64_t)> start_empty_path_class,
946 int64_t chain_length = 1LL,
bool single_path =
false)
948 std::move(start_empty_path_class)),
949 chain_length_(chain_length),
950 single_path_(single_path),
955 const std::vector<IntVar*>& secondary_vars,
956 std::function<
int(int64_t)> start_empty_path_class,
957 int64_t chain_length = 1LL,
bool single_path =
false)
959 absl::StrCat(
"Relocate<", chain_length,
">"),
960 std::move(start_empty_path_class), chain_length, single_path) {
975 const int64_t chain_length_;
976 const bool single_path_;
977 const std::string name_;
982 const int64_t destination =
BaseNode(1);
984 const int64_t before_chain =
BaseNode(0);
985 int64_t chain_end = before_chain;
986 for (
int i = 0; i < chain_length_; ++i) {
987 if (
IsPathEnd(chain_end) || chain_end == destination) {
990 chain_end =
Next(chain_end);
993 MoveChain(before_chain, chain_end, destination);
1009 const std::vector<IntVar*>& secondary_vars,
1010 std::function<
int(int64_t)> start_empty_path_class)
1012 std::move(start_empty_path_class)) {}
1020 const int64_t prev_node0 =
BaseNode(0);
1021 const int64_t node0 =
Next(prev_node0);
1023 const int64_t prev_node1 =
BaseNode(1);
1024 const int64_t node1 =
Next(prev_node1);
1026 const bool ok =
MoveChain(prev_node0, node0, prev_node1);
1045 const std::vector<IntVar*>& secondary_vars,
1046 std::function<
int(int64_t)> start_empty_path_class)
1048 std::move(start_empty_path_class)) {}
1058 if (start1 == start0)
return false;
1060 if (node0 == start0)
return false;
1062 if (node1 == start1)
return false;
1082 const std::vector<IntVar*>& vars,
1083 const std::vector<IntVar*>& secondary_vars,
int number_of_base_nodes,
1084 std::function<
int(int64_t)> start_empty_path_class)
1085 :
PathOperator(vars, secondary_vars, number_of_base_nodes, false, false,
1086 std::move(start_empty_path_class)),
1097 void OnNodeInitialization()
override;
1102 void BaseInactiveNodeToPathOperator::OnNodeInitialization() {
1103 for (
int i = 0; i <
Size(); ++i) {
1109 inactive_node_ =
Size();
1113 while (inactive_node_ <
Size()) {
1136 const std::vector<IntVar*>& secondary_vars,
1137 std::function<
int(int64_t)> start_empty_path_class)
1139 std::move(start_empty_path_class)) {}
1143 std::string
DebugString()
const override {
return "MakeActiveOperator"; }
1162 const std::vector<IntVar*>& vars,
1163 const std::vector<IntVar*>& secondary_vars,
1164 std::function<
int(int64_t)> start_empty_path_class)
1166 std::move(start_empty_path_class)) {}
1169 const int64_t before_node_to_move =
BaseNode(1);
1170 const int64_t node =
Next(before_node_to_move);
1177 return "RelocateAndMakeActiveOpertor";
1190 const std::vector<IntVar*>& secondary_vars,
1191 std::function<
int(int64_t)> start_empty_path_class)
1193 std::move(start_empty_path_class)) {}
1198 return "MakeActiveAndRelocateOperator";
1203 const int64_t before_chain =
BaseNode(1);
1204 const int64_t chain_end =
Next(before_chain);
1205 const int64_t destination =
BaseNode(0);
1207 MoveChain(before_chain, chain_end, destination) &&
1222 const std::vector<IntVar*>& secondary_vars,
1223 std::function<
int(int64_t)> start_empty_path_class)
1225 std::move(start_empty_path_class)) {}
1232 std::string
DebugString()
const override {
return "MakeInactiveOperator"; }
1246 const std::vector<IntVar*>& vars,
1247 const std::vector<IntVar*>& secondary_vars,
1248 std::function<
int(int64_t)> start_empty_path_class)
1250 std::move(start_empty_path_class)) {}
1253 const int64_t destination =
BaseNode(1);
1254 const int64_t before_to_move =
BaseNode(0);
1255 const int64_t node_to_inactivate =
Next(destination);
1256 if (node_to_inactivate == before_to_move ||
IsPathEnd(node_to_inactivate) ||
1260 const int64_t node =
Next(before_to_move);
1265 return "RelocateAndMakeInactiveOperator";
1281 const std::vector<IntVar*>& secondary_vars,
1282 std::function<
int(int64_t)> start_empty_path_class)
1284 std::move(start_empty_path_class)) {}
1291 return "MakeChainInactiveOperator";
1318 const std::vector<IntVar*>& secondary_vars,
1319 std::function<
int(int64_t)> start_empty_path_class)
1321 std::move(start_empty_path_class)) {}
1325 std::string
DebugString()
const override {
return "SwapActiveOperator"; }
1350 const std::vector<IntVar*>& secondary_vars,
1351 std::function<
int(int64_t)> start_empty_path_class)
1353 std::move(start_empty_path_class)) {}
1358 return "ExtendedSwapActiveOperator";
1365 if (
Next(base0) == base1) {
1383 TSPOpt(
const std::vector<IntVar*>& vars,
1384 const std::vector<IntVar*>& secondary_vars,
1392 std::vector<std::vector<int64_t>> cost_;
1394 hamiltonian_path_solver_;
1396 const int chain_length_;
1400 const std::vector<IntVar*>& secondary_vars,
1402 :
PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1403 hamiltonian_path_solver_(cost_),
1405 chain_length_(chain_length) {}
1408 std::vector<int64_t>
nodes;
1410 for (
int i = 0; i < chain_length_ + 1; ++i) {
1411 nodes.push_back(chain_end);
1415 chain_end =
Next(chain_end);
1417 if (
nodes.size() <= 3) {
1421 const int size =
nodes.size() - 1;
1423 for (
int i = 0; i < size; ++i) {
1424 cost_[i].resize(size);
1425 cost_[i][0] = evaluator_(
nodes[i],
nodes[size], chain_path);
1426 for (
int j = 1; j < size; ++j) {
1427 cost_[i][j] = evaluator_(
nodes[i],
nodes[j], chain_path);
1431 std::vector<PathNodeIndex> path;
1434 for (
int i = 0; i < size - 1; ++i) {
1451 TSPLns(
const std::vector<IntVar*>& vars,
1452 const std::vector<IntVar*>& secondary_vars,
1463 void OnNodeInitialization()
override {
1465 has_long_enough_paths_ =
Size() != 0;
1468 std::vector<std::vector<int64_t>> cost_;
1469 HamiltonianPathSolver<int64_t, std::vector<std::vector<int64_t>>>
1470 hamiltonian_path_solver_;
1472 const int tsp_size_;
1474 bool has_long_enough_paths_;
1478 const std::vector<IntVar*>& secondary_vars,
1480 :
PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1481 hamiltonian_path_solver_(cost_),
1483 tsp_size_(tsp_size),
1485 has_long_enough_paths_(true) {
1487 cost_.resize(tsp_size_);
1488 for (
int i = 0; i < tsp_size_; ++i) {
1489 cost_[i].resize(tsp_size_);
1494 while (has_long_enough_paths_) {
1495 has_long_enough_paths_ =
false;
1505 const int64_t base_node =
BaseNode(0);
1506 std::vector<int64_t>
nodes;
1508 nodes.push_back(node);
1510 if (
nodes.size() <= tsp_size_) {
1513 has_long_enough_paths_ =
true;
1516 absl::flat_hash_set<int64_t> breaks_set;
1518 breaks_set.insert(base_node);
1520 while (breaks_set.size() < tsp_size_) {
1521 breaks_set.insert(
nodes[absl::Uniform<int>(rand_, 0,
nodes.size())]);
1523 CHECK_EQ(breaks_set.size(), tsp_size_);
1528 std::vector<int> breaks;
1529 std::vector<int64_t> meta_node_costs;
1532 int64_t node_path =
Path(node);
1536 breaks.push_back(node);
1537 meta_node_costs.push_back(
cost);
1544 meta_node_costs[0] +=
cost;
1545 CHECK_EQ(breaks.size(), tsp_size_);
1547 CHECK_EQ(meta_node_costs.size(), tsp_size_);
1548 for (
int i = 0; i < tsp_size_; ++i) {
1550 CapAdd(meta_node_costs[i],
1551 evaluator_(breaks[i],
Next(breaks[tsp_size_ - 1]), node_path));
1552 for (
int j = 1; j < tsp_size_; ++j) {
1554 CapAdd(meta_node_costs[i],
1555 evaluator_(breaks[i],
Next(breaks[j - 1]), node_path));
1561 std::vector<PathNodeIndex> path;
1563 bool nochange =
true;
1564 for (
int i = 0; i < path.size() - 1; ++i) {
1573 CHECK_EQ(0, path[path.size() - 1]);
1574 for (
int i = 0; i < tsp_size_ - 1; ++i) {
1575 SetNext(breaks[path[i]],
OldNext(breaks[path[i + 1] - 1]), node_path);
1577 SetNext(breaks[path[tsp_size_ - 1]],
OldNext(breaks[tsp_size_ - 1]),
1598 virtual std::string
DebugString()
const {
return "NearestNeighbors"; }
1601 void ComputeNearest(
int row);
1603 std::vector<std::vector<int>> neighbors_;
1615 path_operator_(path_operator),
1617 initialized_(false) {}
1621 if (!initialized_) {
1622 initialized_ =
true;
1624 neighbors_.push_back(std::vector<int>());
1631 return neighbors_[
index];
1634 void NearestNeighbors::ComputeNearest(
int row) {
1636 const int path = path_operator_.
Path(
row);
1638 const int64_t var_min =
var->Min();
1639 const int var_size =
var->Max() - var_min + 1;
1640 using ValuedIndex = std::pair<int64_t ,
int >;
1641 std::vector<ValuedIndex> neighbors(var_size);
1642 for (
int i = 0; i < var_size; ++i) {
1643 const int index = i + var_min;
1644 neighbors[i] = std::make_pair(evaluator_(
row,
index, path),
index);
1646 if (var_size > size_) {
1647 std::nth_element(neighbors.begin(), neighbors.begin() + size_ - 1,
1652 for (
int i = 0; i <
std::min(size_, var_size); ++i) {
1653 neighbors_[
row].push_back(neighbors[i].second);
1655 std::sort(neighbors_[
row].begin(), neighbors_[
row].end());
1661 const std::vector<IntVar*>& secondary_vars,
1669 void OnNodeInitialization()
override;
1671 static const int kNeighbors;
1673 bool InFromOut(int64_t in_i, int64_t in_j, int64_t* out, int64_t* gain);
1677 absl::flat_hash_set<int64_t> marked_;
1686 const std::vector<IntVar*>& secondary_vars,
1688 :
PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1690 neighbors_(evaluator, *this, kNeighbors),
1695 void LinKernighan::OnNodeInitialization() { neighbors_.
Initialize(); }
1700 int64_t path =
Path(node);
1701 int64_t base = node;
1706 marked_.insert(node);
1708 if (!InFromOut(node,
next, &out, &gain))
return false;
1709 marked_.insert(
next);
1710 marked_.insert(out);
1711 const int64_t node1 = out;
1713 const int64_t next1 =
Next(node1);
1715 if (!InFromOut(node1, next1, &out, &gain))
return false;
1716 marked_.insert(next1);
1717 marked_.insert(out);
1721 const int64_t next_out =
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;
1731 while (InFromOut(node,
next, &out, &gain)) {
1732 marked_.insert(
next);
1733 marked_.insert(out);
1738 int64_t in_cost = evaluator_(base, chain_last, path);
1739 int64_t out_cost = evaluator_(chain_last, out, path);
1755 const int LinKernighan::kNeighbors = 5 + 1;
1757 bool LinKernighan::InFromOut(int64_t in_i, int64_t in_j, int64_t* out,
1759 const std::vector<int>& nexts = neighbors_.
Neighbors(in_j);
1761 int64_t path =
Path(in_i);
1762 int64_t out_cost = evaluator_(in_i, in_j, path);
1763 const int64_t current_gain =
CapAdd(*gain, out_cost);
1764 for (
int k = 0; k < nexts.size(); ++k) {
1765 const int64_t
next = nexts[k];
1767 int64_t in_cost = evaluator_(in_j,
next, path);
1768 int64_t new_gain =
CapSub(current_gain, in_cost);
1769 if (new_gain > 0 &&
next !=
Next(in_j) && marked_.count(in_j) == 0 &&
1770 marked_.count(
next) == 0) {
1771 if (best_gain < new_gain) {
1773 best_gain = new_gain;
1791 const std::vector<IntVar*>& secondary_vars,
int number_of_chunks,
1792 int chunk_size,
bool unactive_fragments)
1793 :
PathOperator(vars, secondary_vars, number_of_chunks, true, true,
1795 number_of_chunks_(number_of_chunks),
1796 chunk_size_(chunk_size),
1797 unactive_fragments_(unactive_fragments) {
1807 inline bool ChainsAreFullPaths()
const {
return chunk_size_ == 0; }
1808 void DeactivateChain(int64_t node);
1809 void DeactivateUnactives();
1811 const int number_of_chunks_;
1812 const int chunk_size_;
1813 const bool unactive_fragments_;
1817 if (ChainsAreFullPaths()) {
1821 for (
int i = 0; i < number_of_chunks_; ++i) {
1825 for (
int i = 0; i < number_of_chunks_; ++i) {
1828 DeactivateUnactives();
1832 void PathLns::DeactivateChain(int64_t node) {
1833 for (
int i = 0, current = node;
1834 (ChainsAreFullPaths() || i < chunk_size_) && !
IsPathEnd(current);
1835 ++i, current =
Next(current)) {
1843 void PathLns::DeactivateUnactives() {
1844 if (unactive_fragments_) {
1845 for (
int i = 0; i <
Size(); ++i) {
1861 : operator_(op), limit_(limit), next_neighborhood_calls_(0) {
1862 CHECK(op !=
nullptr);
1867 next_neighborhood_calls_ = 0;
1868 operator_->
Start(assignment);
1872 if (next_neighborhood_calls_ >= limit_) {
1875 ++next_neighborhood_calls_;
1881 std::string
DebugString()
const override {
return "NeighborhoodLimit"; }
1885 const int64_t limit_;
1886 int64_t next_neighborhood_calls_;
1899 CompoundOperator(std::vector<LocalSearchOperator*> operators,
1900 std::function<int64_t(
int,
int)> evaluator);
1901 ~CompoundOperator()
override {}
1902 void Reset()
override;
1903 void Start(
const Assignment* assignment)
override;
1904 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
1905 bool HasFragments()
const override {
return has_fragments_; }
1906 bool HoldsDelta()
const override {
return true; }
1908 std::string DebugString()
const override {
1909 return operators_.empty()
1911 : operators_[operator_indices_[index_]]->DebugString();
1913 const LocalSearchOperator* Self()
const override {
1914 return operators_.empty() ? this
1915 : operators_[operator_indices_[index_]]->Self();
1919 class OperatorComparator {
1921 OperatorComparator(std::function<int64_t(
int,
int)> evaluator,
1922 int active_operator)
1923 :
evaluator_(std::move(evaluator)), active_operator_(active_operator) {}
1924 bool operator()(
int lhs,
int rhs)
const {
1925 const int64_t lhs_value = Evaluate(lhs);
1926 const int64_t rhs_value = Evaluate(rhs);
1927 return lhs_value < rhs_value || (lhs_value == rhs_value && lhs < rhs);
1931 int64_t Evaluate(
int operator_index)
const {
1932 return evaluator_(active_operator_, operator_index);
1936 const int active_operator_;
1940 std::vector<LocalSearchOperator*> operators_;
1941 std::vector<int> operator_indices_;
1943 Bitset64<> started_;
1944 const Assignment* start_assignment_;
1945 bool has_fragments_;
1948 CompoundOperator::CompoundOperator(std::vector<LocalSearchOperator*> operators,
1949 std::function<int64_t(
int,
int)> evaluator)
1951 operators_(std::move(operators)),
1953 started_(operators_.size()),
1954 start_assignment_(nullptr),
1955 has_fragments_(false) {
1956 operators_.erase(std::remove(operators_.begin(), operators_.end(),
nullptr),
1958 operator_indices_.resize(operators_.size());
1959 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
1960 for (LocalSearchOperator*
const op : operators_) {
1961 if (op->HasFragments()) {
1962 has_fragments_ =
true;
1968 void CompoundOperator::Reset() {
1969 for (LocalSearchOperator*
const op : operators_) {
1974 void CompoundOperator::Start(
const Assignment* assignment) {
1975 start_assignment_ = assignment;
1977 if (!operators_.empty()) {
1978 OperatorComparator comparator(
evaluator_, operator_indices_[index_]);
1979 std::sort(operator_indices_.begin(), operator_indices_.end(), comparator);
1984 bool CompoundOperator::MakeNextNeighbor(Assignment*
delta,
1985 Assignment* deltadelta) {
1986 if (!operators_.empty()) {
1990 const int64_t operator_index = operator_indices_[index_];
1991 if (!started_[operator_index]) {
1992 operators_[operator_index]->Start(start_assignment_);
1993 started_.
Set(operator_index);
1995 if (!operators_[operator_index]->HoldsDelta()) {
1998 if (operators_[operator_index]->MakeNextNeighbor(
delta, deltadelta)) {
2003 if (index_ == operators_.size()) {
2006 }
while (index_ != 0);
2011 int64_t CompoundOperatorNoRestart(
int size,
int active_index,
2012 int operator_index) {
2013 return (operator_index < active_index) ? size + operator_index - active_index
2014 : operator_index - active_index;
2017 int64_t CompoundOperatorRestart(
int active_index,
int operator_index) {
2023 const std::vector<LocalSearchOperator*>& ops) {
2028 const std::vector<LocalSearchOperator*>& ops,
bool restart) {
2030 std::function<int64_t(
int,
int)> eval = CompoundOperatorRestart;
2033 const int size = ops.size();
2035 return CompoundOperatorNoRestart(size, i, j);
2040 const std::vector<LocalSearchOperator*>& ops,
2041 std::function<int64_t(
int,
int)> evaluator) {
2042 return RevAlloc(
new CompoundOperator(ops, std::move(evaluator)));
2048 explicit RandomCompoundOperator(std::vector<LocalSearchOperator*> operators);
2049 RandomCompoundOperator(std::vector<LocalSearchOperator*> operators,
2051 ~RandomCompoundOperator()
override {}
2052 void Reset()
override;
2053 void Start(
const Assignment* assignment)
override;
2054 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
2055 bool HoldsDelta()
const override {
return true; }
2057 std::string DebugString()
const override {
return "RandomCompoundOperator"; }
2062 const std::vector<LocalSearchOperator*> operators_;
2063 bool has_fragments_;
2066 void RandomCompoundOperator::Start(
const Assignment* assignment) {
2067 for (LocalSearchOperator*
const op : operators_) {
2068 op->Start(assignment);
2072 RandomCompoundOperator::RandomCompoundOperator(
2073 std::vector<LocalSearchOperator*> operators)
2074 : RandomCompoundOperator(std::move(operators),
CpRandomSeed()) {}
2076 RandomCompoundOperator::RandomCompoundOperator(
2077 std::vector<LocalSearchOperator*> operators, int32_t seed)
2078 : rand_(seed), operators_(std::move(operators)), has_fragments_(false) {
2079 for (LocalSearchOperator*
const op : operators_) {
2080 if (op->HasFragments()) {
2081 has_fragments_ =
true;
2087 void RandomCompoundOperator::Reset() {
2088 for (LocalSearchOperator*
const op : operators_) {
2093 bool RandomCompoundOperator::MakeNextNeighbor(Assignment*
delta,
2094 Assignment* deltadelta) {
2095 const int size = operators_.size();
2096 std::vector<int> indices(size);
2097 std::iota(indices.begin(), indices.end(), 0);
2098 std::shuffle(indices.begin(), indices.end(), rand_);
2099 for (
int index : indices) {
2100 if (!operators_[
index]->HoldsDelta()) {
2103 if (operators_[
index]->MakeNextNeighbor(
delta, deltadelta)) {
2113 const std::vector<LocalSearchOperator*>& ops) {
2114 return RevAlloc(
new RandomCompoundOperator(ops));
2118 const std::vector<LocalSearchOperator*>& ops, int32_t seed) {
2119 return RevAlloc(
new RandomCompoundOperator(ops, seed));
2125 explicit MultiArmedBanditCompoundOperator(
2126 std::vector<LocalSearchOperator*> operators,
double memory_coefficient,
2127 double exploration_coefficient,
bool maximize);
2128 ~MultiArmedBanditCompoundOperator()
override {}
2129 void Reset()
override;
2130 void Start(
const Assignment* assignment)
override;
2131 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
2132 bool HoldsDelta()
const override {
return true; }
2134 std::string DebugString()
const override {
2135 return operators_.empty()
2137 : operators_[operator_indices_[index_]]->DebugString();
2139 const LocalSearchOperator* Self()
const override {
2140 return operators_.empty() ? this
2141 : operators_[operator_indices_[index_]]->Self();
2145 double Score(
int index);
2147 std::vector<LocalSearchOperator*> operators_;
2148 Bitset64<> started_;
2149 const Assignment* start_assignment_;
2150 bool has_fragments_;
2151 std::vector<int> operator_indices_;
2152 int64_t last_objective_;
2153 std::vector<double> avg_improvement_;
2155 std::vector<double> num_neighbors_per_operator_;
2161 const double memory_coefficient_;
2166 const double exploration_coefficient_;
2169 MultiArmedBanditCompoundOperator::MultiArmedBanditCompoundOperator(
2170 std::vector<LocalSearchOperator*> operators,
double memory_coefficient,
2171 double exploration_coefficient,
bool maximize)
2173 operators_(std::move(operators)),
2174 started_(operators_.size()),
2175 start_assignment_(nullptr),
2176 has_fragments_(false),
2177 last_objective_(std::numeric_limits<int64_t>::
max()),
2180 memory_coefficient_(memory_coefficient),
2181 exploration_coefficient_(exploration_coefficient) {
2185 operators_.erase(std::remove(operators_.begin(), operators_.end(),
nullptr),
2187 operator_indices_.resize(operators_.size());
2188 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
2189 num_neighbors_per_operator_.resize(operators_.size(), 0);
2190 avg_improvement_.resize(operators_.size(), 0);
2191 for (LocalSearchOperator*
const op : operators_) {
2192 if (op->HasFragments()) {
2193 has_fragments_ =
true;
2199 void MultiArmedBanditCompoundOperator::Reset() {
2200 for (LocalSearchOperator*
const op : operators_) {
2205 double MultiArmedBanditCompoundOperator::Score(
int index) {
2206 return avg_improvement_[
index] +
2207 exploration_coefficient_ *
2208 sqrt(2 * log(1 + num_neighbors_) /
2209 (1 + num_neighbors_per_operator_[
index]));
2212 void MultiArmedBanditCompoundOperator::Start(
const Assignment* assignment) {
2213 start_assignment_ = assignment;
2215 if (operators_.empty())
return;
2217 const double objective = assignment->ObjectiveValue();
2219 if (objective == last_objective_)
return;
2222 last_objective_ = objective;
2226 const double improvement =
2227 maximize_ ? objective - last_objective_ : last_objective_ - objective;
2228 if (improvement < 0) {
2231 last_objective_ = objective;
2232 avg_improvement_[operator_indices_[index_]] +=
2233 memory_coefficient_ *
2234 (improvement - avg_improvement_[operator_indices_[index_]]);
2236 std::sort(operator_indices_.begin(), operator_indices_.end(),
2237 [
this](
int lhs,
int rhs) {
2238 const double lhs_score = Score(lhs);
2239 const double rhs_score = Score(rhs);
2240 return lhs_score > rhs_score ||
2241 (lhs_score == rhs_score && lhs < rhs);
2247 bool MultiArmedBanditCompoundOperator::MakeNextNeighbor(
2248 Assignment*
delta, Assignment* deltadelta) {
2249 if (operators_.empty())
return false;
2251 const int operator_index = operator_indices_[index_];
2252 if (!started_[operator_index]) {
2253 operators_[operator_index]->Start(start_assignment_);
2254 started_.
Set(operator_index);
2256 if (!operators_[operator_index]->HoldsDelta()) {
2259 if (operators_[operator_index]->MakeNextNeighbor(
delta, deltadelta)) {
2261 ++num_neighbors_per_operator_[operator_index];
2266 if (index_ == operators_.size()) {
2269 }
while (index_ != 0);
2275 const std::vector<LocalSearchOperator*>& ops,
double memory_coefficient,
2276 double exploration_coefficient,
bool maximize) {
2277 return RevAlloc(
new MultiArmedBanditCompoundOperator(
2278 ops, memory_coefficient, exploration_coefficient, maximize));
2285 Solver* solver,
const std::vector<IntVar*>& vars,
2286 const std::vector<IntVar*>& secondary_vars,
2287 std::function<
int(int64_t)> start_empty_path_class) {
2289 new T(vars, secondary_vars, std::move(start_empty_path_class)));
2292 #define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass) \
2294 LocalSearchOperator* MakeLocalSearchOperator<OperatorClass>( \
2295 Solver * solver, const std::vector<IntVar*>& vars, \
2296 const std::vector<IntVar*>& secondary_vars, \
2297 std::function<int(int64_t)> start_empty_path_class) { \
2298 return solver->RevAlloc(new OperatorClass( \
2299 vars, secondary_vars, std::move(start_empty_path_class))); \
2315 #undef MAKE_LOCAL_SEARCH_OPERATOR
2323 const std::vector<IntVar*>& vars,
2324 const std::vector<IntVar*>& secondary_vars,
2333 std::vector<LocalSearchOperator*> operators;
2334 for (
int i = 1; i < 4; ++i) {
2336 new Relocate(vars, secondary_vars, absl::StrCat(
"OrOpt<", i,
">"),
2337 nullptr, i,
true)));
2343 result = MakeLocalSearchOperator<Relocate>(
this, vars, secondary_vars,
2348 result = MakeLocalSearchOperator<Exchange>(
this, vars, secondary_vars,
2354 MakeLocalSearchOperator<Cross>(
this, vars, secondary_vars,
nullptr);
2358 result = MakeLocalSearchOperator<MakeActiveOperator>(
2359 this, vars, secondary_vars,
nullptr);
2363 result = MakeLocalSearchOperator<MakeInactiveOperator>(
2364 this, vars, secondary_vars,
nullptr);
2368 result = MakeLocalSearchOperator<MakeChainInactiveOperator>(
2369 this, vars, secondary_vars,
nullptr);
2373 result = MakeLocalSearchOperator<SwapActiveOperator>(
2374 this, vars, secondary_vars,
nullptr);
2378 result = MakeLocalSearchOperator<ExtendedSwapActiveOperator>(
2379 this, vars, secondary_vars,
nullptr);
2398 if (secondary_vars.empty()) {
2399 result =
RevAlloc(
new IncrementValue(vars));
2402 <<
" does not support secondary variables";
2407 if (secondary_vars.empty()) {
2408 result =
RevAlloc(
new DecrementValue(vars));
2411 <<
" does not support secondary variables";
2416 if (secondary_vars.empty()) {
2417 result =
RevAlloc(
new SimpleLns(vars, 1));
2420 <<
" does not support secondary variables";
2425 LOG(
FATAL) <<
"Unknown operator " << op;
2433 return MakeOperator(vars, std::vector<IntVar*>(), std::move(evaluator), op);
2437 const std::vector<IntVar*>& vars,
2438 const std::vector<IntVar*>& secondary_vars,
2444 std::vector<LocalSearchOperator*> operators;
2446 new LinKernighan(vars, secondary_vars, evaluator,
false)));
2448 new LinKernighan(vars, secondary_vars, evaluator,
true)));
2454 new TSPOpt(vars, secondary_vars, evaluator,
2455 absl::GetFlag(FLAGS_cp_local_search_tsp_opt_size)));
2460 new TSPLns(vars, secondary_vars, evaluator,
2461 absl::GetFlag(FLAGS_cp_local_search_tsp_lns_size)));
2465 LOG(
FATAL) <<
"Unknown operator " << op;
2473 class SumOperation {
2475 SumOperation() : value_(0) {}
2476 void Init() { value_ = 0; }
2477 void Update(int64_t update) { value_ =
CapAdd(value_, update); }
2478 void Remove(int64_t remove) { value_ =
CapSub(value_, remove); }
2479 int64_t
value()
const {
return value_; }
2480 void set_value(int64_t new_value) { value_ = new_value; }
2486 class ProductOperation {
2488 ProductOperation() : value_(1) {}
2489 void Init() { value_ = 1; }
2490 void Update(int64_t update) { value_ *= update; }
2491 void Remove(int64_t remove) {
2496 int64_t
value()
const {
return value_; }
2497 void set_value(int64_t new_value) { value_ = new_value; }
2503 class MinOperation {
2505 void Init() { values_set_.clear(); }
2506 void Update(int64_t update) { values_set_.insert(update); }
2507 void Remove(int64_t remove) { values_set_.erase(remove); }
2508 int64_t
value()
const {
2509 return (!values_set_.empty()) ? *values_set_.begin() : 0;
2511 void set_value(int64_t new_value) {}
2514 std::set<int64_t> values_set_;
2517 class MaxOperation {
2519 void Init() { values_set_.clear(); }
2520 void Update(int64_t update) { values_set_.insert(update); }
2521 void Remove(int64_t remove) { values_set_.erase(remove); }
2522 int64_t
value()
const {
2523 return (!values_set_.empty()) ? *values_set_.rbegin() : 0;
2525 void set_value(int64_t new_value) {}
2528 std::set<int64_t> values_set_;
2532 class AcceptFilter :
public LocalSearchFilter {
2534 std::string DebugString()
const override {
return "AcceptFilter"; }
2535 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2536 int64_t obj_min, int64_t obj_max)
override {
2539 void Synchronize(
const Assignment* assignment,
2540 const Assignment*
delta)
override {}
2545 return RevAlloc(
new AcceptFilter());
2552 std::string DebugString()
const override {
return "RejectFilter"; }
2553 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2554 int64_t obj_min, int64_t obj_max)
override {
2557 void Synchronize(
const Assignment* assignment,
2558 const Assignment*
delta)
override {}
2563 return RevAlloc(
new RejectFilter());
2567 std::vector<int> path_end)
2568 : num_nodes_(num_nodes),
2569 num_paths_(path_start.size()),
2570 num_nodes_threshold_(std::
max(16, 4 * num_nodes_))
2572 DCHECK_EQ(path_start.size(), num_paths_);
2574 for (
int p = 0; p < num_paths_; ++p) {
2575 path_start_end_.push_back({path_start[p], path_end[p]});
2578 committed_index_.assign(num_nodes_, -1);
2579 committed_nodes_.assign(2 * num_paths_, {-1, -1});
2580 chains_.assign(num_paths_ + 1, {-1, -1});
2581 paths_.assign(num_paths_, {-1, -1});
2582 for (
int path = 0; path < num_paths_; ++path) {
2583 const int index = 2 * path;
2584 const PathStartEnd start_end = path_start_end_[path];
2585 committed_index_[start_end.start] =
index;
2586 committed_index_[start_end.end] =
index + 1;
2588 committed_nodes_[
index] = {start_end.start, path};
2589 committed_nodes_[
index + 1] = {start_end.end, path};
2592 paths_[path] = {path, path + 1};
2594 chains_[num_paths_] = {0, 0};
2596 for (
int node = 0; node < num_nodes_; ++node) {
2597 if (committed_index_[node] != -1)
continue;
2598 committed_index_[node] = committed_nodes_.size();
2599 committed_nodes_.push_back({node, -1});
2601 path_has_changed_.assign(num_paths_,
false);
2605 const PathBounds
bounds = paths_[path];
2607 chains_.data() +
bounds.end_index,
2608 committed_nodes_.data());
2612 const PathBounds
bounds = paths_[path];
2614 chains_.data() +
bounds.end_index,
2615 committed_nodes_.data());
2618 void PathState::MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm() {
2619 int num_visited_changed_arcs = 0;
2620 const int num_changed_arcs = tail_head_indices_.size();
2621 const int num_committed_nodes = committed_nodes_.size();
2623 for (
const int path : changed_paths_) {
2624 const int old_chain_size = chains_.size();
2625 const ChainBounds
bounds = chains_[paths_[path].begin_index];
2626 const int start_index =
bounds.begin_index;
2627 const int end_index =
bounds.end_index - 1;
2628 int current_index = start_index;
2632 int selected_arc = -1;
2633 int selected_tail_index = num_committed_nodes;
2634 for (
int i = num_visited_changed_arcs; i < num_changed_arcs; ++i) {
2635 const int tail_index = tail_head_indices_[i].tail_index;
2636 if (current_index <= tail_index && tail_index < selected_tail_index) {
2638 selected_tail_index = tail_index;
2646 if (start_index <= current_index && current_index <= end_index &&
2647 end_index < selected_tail_index) {
2648 chains_.emplace_back(current_index, end_index + 1);
2651 chains_.emplace_back(current_index, selected_tail_index + 1);
2652 current_index = tail_head_indices_[selected_arc].head_index;
2653 std::swap(tail_head_indices_[num_visited_changed_arcs],
2654 tail_head_indices_[selected_arc]);
2655 ++num_visited_changed_arcs;
2658 const int new_chain_size = chains_.size();
2659 paths_[path] = {old_chain_size, new_chain_size};
2661 chains_.emplace_back(0, 0);
2664 void PathState::MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm() {
2680 for (
const int path : changed_paths_) {
2681 const PathStartEnd start_end = path_start_end_[path];
2682 tail_head_indices_.push_back(
2683 {committed_index_[start_end.end], committed_index_[start_end.start]});
2688 const int num_arc_indices = tail_head_indices_.size();
2689 arcs_by_tail_index_.resize(num_arc_indices);
2690 arcs_by_head_index_.resize(num_arc_indices);
2691 for (
int i = 0; i < num_arc_indices; ++i) {
2692 arcs_by_tail_index_[i] = {tail_head_indices_[i].tail_index, i};
2693 arcs_by_head_index_[i] = {tail_head_indices_[i].head_index, i};
2695 std::sort(arcs_by_tail_index_.begin(), arcs_by_tail_index_.end());
2696 std::sort(arcs_by_head_index_.begin(), arcs_by_head_index_.end());
2698 next_arc_.resize(num_arc_indices);
2699 for (
int i = 0; i < num_arc_indices; ++i) {
2700 next_arc_[arcs_by_head_index_[i].arc] = arcs_by_tail_index_[i].arc;
2706 const int first_fake_arc = num_arc_indices - changed_paths_.size();
2707 for (
int fake_arc = first_fake_arc; fake_arc < num_arc_indices; ++fake_arc) {
2708 const int new_path_begin = chains_.size();
2709 int32_t arc = fake_arc;
2711 const int chain_begin = tail_head_indices_[arc].head_index;
2712 arc = next_arc_[arc];
2713 const int chain_end = tail_head_indices_[arc].tail_index + 1;
2714 chains_.emplace_back(chain_begin, chain_end);
2715 }
while (arc != fake_arc);
2716 const int path = changed_paths_[fake_arc - first_fake_arc];
2717 const int new_path_end = chains_.size();
2718 paths_[path] = {new_path_begin, new_path_end};
2720 chains_.emplace_back(0, 0);
2724 if (is_invalid_)
return;
2728 DCHECK_EQ(chains_.size(), num_paths_ + 1);
2729 DCHECK(changed_paths_.empty());
2730 tail_head_indices_.clear();
2731 int num_changed_arcs = 0;
2732 for (
const auto& arc : changed_arcs_) {
2734 std::tie(node,
next) = arc;
2735 const int node_index = committed_index_[node];
2736 const int next_index = committed_index_[
next];
2737 const int node_path = committed_nodes_[node_index].path;
2739 (next_index != node_index + 1 || node_path == -1)) {
2740 tail_head_indices_.push_back({node_index, next_index});
2741 changed_arcs_[num_changed_arcs++] = {node,
next};
2742 if (node_path != -1 && !path_has_changed_[node_path]) {
2743 path_has_changed_[node_path] =
true;
2744 changed_paths_.push_back(node_path);
2746 }
else if (node ==
next && node_path != -1) {
2747 changed_arcs_[num_changed_arcs++] = {node, node};
2750 changed_arcs_.resize(num_changed_arcs);
2752 if (tail_head_indices_.size() + changed_paths_.size() <= 8) {
2753 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
2755 MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
2761 if (committed_nodes_.size() < num_nodes_threshold_) {
2762 IncrementalCommit();
2769 is_invalid_ =
false;
2770 chains_.resize(num_paths_ + 1);
2771 for (
const int path : changed_paths_) {
2772 paths_[path] = {path, path + 1};
2773 path_has_changed_[path] =
false;
2775 changed_paths_.clear();
2776 changed_arcs_.clear();
2779 void PathState::CopyNewPathAtEndOfNodes(
int path) {
2781 const int new_path_begin_index = committed_nodes_.size();
2782 const PathBounds path_bounds = paths_[path];
2783 for (
int i = path_bounds.begin_index; i < path_bounds.end_index; ++i) {
2784 const ChainBounds chain_bounds = chains_[i];
2785 committed_nodes_.insert(committed_nodes_.end(),
2786 committed_nodes_.data() + chain_bounds.begin_index,
2787 committed_nodes_.data() + chain_bounds.end_index);
2789 const int new_path_end_index = committed_nodes_.size();
2791 for (
int i = new_path_begin_index; i < new_path_end_index; ++i) {
2792 committed_nodes_[i].path = path;
2798 void PathState::IncrementalCommit() {
2799 const int new_nodes_begin = committed_nodes_.size();
2801 const int chain_begin = committed_nodes_.size();
2802 CopyNewPathAtEndOfNodes(path);
2803 const int chain_end = committed_nodes_.size();
2804 chains_[path] = {chain_begin, chain_end};
2807 const int new_nodes_end = committed_nodes_.size();
2808 for (
int i = new_nodes_begin; i < new_nodes_end; ++i) {
2809 committed_index_[committed_nodes_[i].node] = i;
2815 std::tie(node,
next) = arc;
2816 if (node !=
next)
continue;
2817 const int index = committed_index_[node];
2818 committed_nodes_[
index].path = -1;
2824 void PathState::FullCommit() {
2827 const int old_num_nodes = committed_nodes_.size();
2828 for (
int path = 0; path < num_paths_; ++path) {
2829 const int new_path_begin = committed_nodes_.size() - old_num_nodes;
2830 CopyNewPathAtEndOfNodes(path);
2831 const int new_path_end = committed_nodes_.size() - old_num_nodes;
2832 chains_[path] = {new_path_begin, new_path_end};
2834 committed_nodes_.erase(committed_nodes_.begin(),
2835 committed_nodes_.begin() + old_num_nodes);
2838 constexpr
int kUnindexed = -1;
2839 committed_index_.assign(num_nodes_, kUnindexed);
2841 for (
const CommittedNode committed_node : committed_nodes_) {
2842 committed_index_[committed_node.node] =
index++;
2844 for (
int node = 0; node < num_nodes_; ++node) {
2845 if (committed_index_[node] != kUnindexed)
continue;
2846 committed_index_[node] =
index++;
2847 committed_nodes_.push_back({node, -1});
2855 class PathStateFilter :
public LocalSearchFilter {
2857 std::string DebugString()
const override {
return "PathStateFilter"; }
2858 PathStateFilter(std::unique_ptr<PathState> path_state,
2859 const std::vector<IntVar*>& nexts);
2860 void Relax(
const Assignment*
delta,
const Assignment* deltadelta)
override;
2861 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2862 int64_t objective_min, int64_t objective_max)
override {
2865 void Synchronize(
const Assignment*
delta,
2866 const Assignment* deltadelta)
override{};
2867 void Commit(
const Assignment* assignment,
const Assignment*
delta)
override;
2868 void Revert()
override;
2869 void Reset()
override;
2872 const std::unique_ptr<PathState> path_state_;
2874 std::vector<int> variable_index_to_node_;
2877 std::vector<bool> node_is_assigned_;
2880 PathStateFilter::PathStateFilter(std::unique_ptr<PathState> path_state,
2881 const std::vector<IntVar*>& nexts)
2882 : path_state_(std::move(path_state)) {
2886 for (
const IntVar*
next : nexts) {
2888 min_index = std::min<int>(min_index,
index);
2889 max_index = std::max<int>(max_index,
index);
2891 variable_index_to_node_.resize(max_index - min_index + 1, -1);
2892 index_offset_ = min_index;
2895 for (
int node = 0; node < nexts.size(); ++node) {
2896 const int index = nexts[node]->index() - index_offset_;
2897 variable_index_to_node_[
index] = node;
2901 void PathStateFilter::Relax(
const Assignment*
delta,
2902 const Assignment* deltadelta) {
2903 path_state_->Revert();
2904 for (
const IntVarElement& var_value :
delta->IntVarContainer().elements()) {
2905 if (var_value.Var() ==
nullptr)
continue;
2906 const int index = var_value.Var()->index() - index_offset_;
2907 if (
index < 0 || variable_index_to_node_.size() <=
index)
continue;
2908 const int node = variable_index_to_node_[
index];
2909 if (node == -1)
continue;
2910 if (var_value.Bound()) {
2911 path_state_->ChangeNext(node, var_value.Value());
2913 path_state_->Revert();
2914 path_state_->SetInvalid();
2918 path_state_->CutChains();
2921 void PathStateFilter::Reset() {
2922 path_state_->Revert();
2925 const int num_nodes = path_state_->NumNodes();
2926 node_is_assigned_.assign(num_nodes,
false);
2927 const int num_paths = path_state_->NumPaths();
2928 for (
int path = 0; path < num_paths; ++path) {
2929 const int start = path_state_->Start(path);
2930 const int end = path_state_->End(path);
2931 path_state_->ChangeNext(start, end);
2932 node_is_assigned_[start] =
true;
2933 node_is_assigned_[end] =
true;
2935 for (
int node = 0; node < num_nodes; ++node) {
2936 if (!node_is_assigned_[node]) path_state_->ChangeNext(node, node);
2938 path_state_->CutChains();
2939 path_state_->Commit();
2945 void PathStateFilter::Commit(
const Assignment* assignment,
2946 const Assignment*
delta) {
2947 path_state_->Revert();
2949 Relax(assignment,
nullptr);
2951 Relax(
delta,
nullptr);
2953 path_state_->Commit();
2956 void PathStateFilter::Revert() { path_state_->Revert(); }
2961 std::unique_ptr<PathState> path_state,
2962 const std::vector<IntVar*>& nexts) {
2963 PathStateFilter* filter =
new PathStateFilter(std::move(path_state), nexts);
2968 const PathState* path_state, std::vector<Interval> path_capacity,
2969 std::vector<int> path_class, std::vector<std::vector<Interval>>
demand,
2970 std::vector<Interval> node_capacity)
2971 : path_state_(path_state),
2972 path_capacity_(std::move(path_capacity)),
2973 path_class_(std::move(path_class)),
2974 demand_(std::move(
demand)),
2975 node_capacity_(std::move(node_capacity)),
2976 index_(path_state_->NumNodes(), 0),
2977 maximum_partial_demand_layer_size_(
2978 std::
max(16, 4 * path_state_->NumNodes()))
2980 const int num_nodes = path_state_->
NumNodes();
2981 const int num_paths = path_state_->
NumPaths();
2982 DCHECK_EQ(num_paths, path_capacity_.size());
2983 DCHECK_EQ(num_paths, path_class_.size());
2985 partial_demand_sums_rmq_.resize(maximum_rmq_exponent + 1);
2986 previous_nontrivial_index_.reserve(maximum_partial_demand_layer_size_);
2991 if (path_state_->
IsInvalid())
return true;
2993 const Interval path_capacity = path_capacity_[path];
2998 const int path_class = path_class_[path];
3000 Interval capacity_used = node_capacity_[path_state_->
Start(path)];
3003 if (capacity_used.
min > capacity_used.
max)
return false;
3005 for (
const auto chain : path_state_->
Chains(path)) {
3006 const int first_node = chain.First();
3007 const int last_node = chain.Last();
3009 const int first_index = index_[first_node];
3010 const int last_index = index_[last_node];
3012 const int chain_path = path_state_->
Path(first_node);
3013 const int chain_path_class =
3014 chain_path == -1 ? -1 : path_class_[chain_path];
3018 constexpr
int kMinRangeSizeForRMQ = 4;
3019 if (last_index - first_index > kMinRangeSizeForRMQ &&
3020 path_class == chain_path_class &&
3021 SubpathOnlyHasTrivialNodes(first_index, last_index)) {
3026 GetMinMaxPartialDemandSum(first_index, last_index);
3027 const Interval prev_sum = partial_demand_sums_rmq_[0][first_index - 1];
3035 if (capacity_used.
min > capacity_used.
max)
return false;
3037 const Interval last_sum = partial_demand_sums_rmq_[0][last_index];
3042 for (
const int node : chain) {
3043 const Interval node_capacity = node_capacity_[node];
3046 if (capacity_used.
min > capacity_used.
max)
return false;
3064 const int current_layer_size = partial_demand_sums_rmq_[0].size();
3067 for (
const auto chain : path_state_->
Chains(path)) {
3068 change_size += chain.NumNodes();
3071 if (current_layer_size + change_size <= maximum_partial_demand_layer_size_) {
3072 IncrementalCommit();
3078 void UnaryDimensionChecker::IncrementalCommit() {
3080 const int begin_index = partial_demand_sums_rmq_[0].size();
3081 AppendPathDemandsToSums(path);
3082 UpdateRMQStructure(begin_index, partial_demand_sums_rmq_[0].size());
3086 void UnaryDimensionChecker::FullCommit() {
3088 previous_nontrivial_index_.clear();
3089 for (
auto& sums : partial_demand_sums_rmq_) sums.clear();
3091 const int num_paths = path_state_->
NumPaths();
3092 for (
int path = 0; path < num_paths; ++path) {
3093 const int begin_index = partial_demand_sums_rmq_[0].size();
3094 AppendPathDemandsToSums(path);
3095 UpdateRMQStructure(begin_index, partial_demand_sums_rmq_[0].size());
3099 void UnaryDimensionChecker::AppendPathDemandsToSums(
int path) {
3100 const int path_class = path_class_[path];
3101 Interval demand_sum = {0, 0};
3102 int previous_nontrivial_index = -1;
3103 int index = partial_demand_sums_rmq_[0].size();
3106 partial_demand_sums_rmq_[0].push_back(demand_sum);
3107 previous_nontrivial_index_.push_back(-1);
3110 for (
const int node : path_state_->
Nodes(path)) {
3111 index_[node] =
index;
3112 const Interval
demand = demand_[path_class][node];
3115 partial_demand_sums_rmq_[0].push_back(demand_sum);
3117 const Interval node_capacity = node_capacity_[node];
3121 previous_nontrivial_index =
index;
3123 previous_nontrivial_index_.push_back(previous_nontrivial_index);
3128 void UnaryDimensionChecker::UpdateRMQStructure(
int begin_index,
int end_index) {
3131 const int maximum_rmq_exponent =
3133 for (
int layer = 1, window_size = 1; layer <= maximum_rmq_exponent;
3134 ++layer, window_size *= 2) {
3135 partial_demand_sums_rmq_[layer].resize(end_index);
3136 for (
int i = begin_index; i < end_index - window_size; ++i) {
3137 const Interval i1 = partial_demand_sums_rmq_[layer - 1][i];
3138 const Interval i2 = partial_demand_sums_rmq_[layer - 1][i + window_size];
3139 partial_demand_sums_rmq_[layer][i] = {
std::min(i1.min, i2.min),
3143 partial_demand_sums_rmq_[layer - 1].begin() + end_index - window_size,
3144 partial_demand_sums_rmq_[layer - 1].begin() + end_index,
3145 partial_demand_sums_rmq_[layer].begin() + end_index - window_size);
3155 UnaryDimensionChecker::Interval
3156 UnaryDimensionChecker::GetMinMaxPartialDemandSum(
int first_node_index,
3157 int last_node_index)
const {
3159 DCHECK_LT(first_node_index, last_node_index);
3160 DCHECK_LT(last_node_index, partial_demand_sums_rmq_[0].size());
3165 const int window_size = 1 << layer;
3167 const Interval i1 = partial_demand_sums_rmq_[layer][first_node_index];
3169 partial_demand_sums_rmq_[layer][last_node_index - window_size + 1];
3173 bool UnaryDimensionChecker::SubpathOnlyHasTrivialNodes(
3174 int first_node_index,
int last_node_index)
const {
3176 DCHECK_LT(first_node_index, last_node_index);
3177 DCHECK_LT(last_node_index, previous_nontrivial_index_.size());
3178 return first_node_index > previous_nontrivial_index_[last_node_index];
3183 class UnaryDimensionFilter :
public LocalSearchFilter {
3185 std::string DebugString()
const override {
return "UnaryDimensionFilter"; }
3186 explicit UnaryDimensionFilter(std::unique_ptr<UnaryDimensionChecker> checker)
3187 : checker_(std::move(checker)) {}
3189 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3190 int64_t objective_min, int64_t objective_max)
override {
3191 return checker_->Check();
3194 void Synchronize(
const Assignment* assignment,
3195 const Assignment*
delta)
override {
3200 std::unique_ptr<UnaryDimensionChecker> checker_;
3206 Solver* solver, std::unique_ptr<UnaryDimensionChecker> checker) {
3207 UnaryDimensionFilter* filter =
new UnaryDimensionFilter(std::move(checker));
3215 class VariableDomainFilter :
public LocalSearchFilter {
3217 VariableDomainFilter() {}
3218 ~VariableDomainFilter()
override {}
3219 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3220 int64_t objective_min, int64_t objective_max)
override;
3221 void Synchronize(
const Assignment* assignment,
3222 const Assignment*
delta)
override {}
3224 std::string DebugString()
const override {
return "VariableDomainFilter"; }
3227 bool VariableDomainFilter::Accept(
const Assignment*
delta,
3228 const Assignment* deltadelta,
3229 int64_t objective_min,
3230 int64_t objective_max) {
3232 const int size = container.Size();
3233 for (
int i = 0; i < size; ++i) {
3234 const IntVarElement& element = container.Element(i);
3235 if (element.Activated() && !element.Var()->Contains(element.Value())) {
3244 return RevAlloc(
new VariableDomainFilter());
3249 const int IntVarLocalSearchFilter::kUnassigned = -1;
3252 const std::vector<IntVar*>& vars) {
3257 if (!vars.empty()) {
3258 for (
int i = 0; i < vars.size(); ++i) {
3259 const int index = vars[i]->index();
3260 if (
index >= var_index_to_index_.size()) {
3261 var_index_to_index_.resize(
index + 1, kUnassigned);
3263 var_index_to_index_[
index] = i + vars_.size();
3265 vars_.insert(vars_.end(), vars.begin(), vars.end());
3266 values_.resize(vars_.size(), 0);
3267 var_synced_.resize(vars_.size(),
false);
3276 var_synced_.assign(var_synced_.size(),
false);
3287 const int size = container.
Size();
3288 for (
int i = 0; i < size; ++i) {
3291 if (
var !=
nullptr) {
3292 if (i < vars_.size() && vars_[i] ==
var) {
3293 values_[i] = element.
Value();
3294 var_synced_[i] =
true;
3296 const int64_t kUnallocated = -1;
3297 int64_t
index = kUnallocated;
3300 var_synced_[
index] =
true;
3319 SumObjectiveFilter(
const std::vector<IntVar*>& vars,
3329 for (
int i = 0; i < vars.size(); ++i) {
3334 ~SumObjectiveFilter()
override {
3338 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3339 int64_t objective_min, int64_t objective_max)
override {
3340 if (
delta ==
nullptr) {
3343 if (deltadelta->Empty()) {
3374 LOG(
ERROR) <<
"Unknown local search filter enum value";
3381 virtual int64_t CostOfSynchronizedVariable(int64_t
index) = 0;
3385 virtual bool FillCostOfBoundDeltaVariable(
3387 int* container_index, int64_t* new_cost) = 0;
3388 bool IsIncremental()
const override {
return true; }
3390 std::string DebugString()
const override {
return "SumObjectiveFilter"; }
3392 int64_t GetSynchronizedObjectiveValue()
const override {
3395 int64_t GetAcceptedObjectiveValue()
const override {
return delta_sum_; }
3407 void OnSynchronize(
const Assignment*
delta)
override {
3410 const int64_t
cost = CostOfSynchronizedVariable(i);
3418 int64_t CostOfChanges(
const Assignment* changes,
3419 const int64_t*
const old_costs,
3420 bool cache_delta_values) {
3421 int64_t total_cost = 0;
3423 const int size = container.
Size();
3424 for (
int i = 0; i < size; ++i) {
3425 const IntVarElement& new_element = container.Element(i);
3426 IntVar*
const var = new_element.Var();
3429 total_cost =
CapSub(total_cost, old_costs[
index]);
3430 int64_t new_cost = 0LL;
3431 if (FillCostOfBoundDeltaVariable(container,
index, &i, &new_cost)) {
3432 total_cost =
CapAdd(total_cost, new_cost);
3434 if (cache_delta_values) {
3443 class BinaryObjectiveFilter :
public SumObjectiveFilter {
3445 BinaryObjectiveFilter(
const std::vector<IntVar*>& vars,
3448 : SumObjectiveFilter(vars, filter_enum),
3449 value_evaluator_(std::move(value_evaluator)) {}
3450 ~BinaryObjectiveFilter()
override {}
3451 int64_t CostOfSynchronizedVariable(int64_t
index)
override {
3457 int index,
int* container_index,
3458 int64_t* new_cost)
override {
3459 const IntVarElement& element = container.Element(*container_index);
3460 if (element.Activated()) {
3461 *new_cost = value_evaluator_(
index, element.Value());
3464 const IntVar*
var = element.Var();
3466 *new_cost = value_evaluator_(
index,
var->Min());
3477 class TernaryObjectiveFilter :
public SumObjectiveFilter {
3479 TernaryObjectiveFilter(
const std::vector<IntVar*>& vars,
3480 const std::vector<IntVar*>& secondary_vars,
3483 : SumObjectiveFilter(vars, filter_enum),
3484 secondary_vars_offset_(vars.size()),
3485 value_evaluator_(std::move(value_evaluator)) {
3489 ~TernaryObjectiveFilter()
override {}
3490 int64_t CostOfSynchronizedVariable(int64_t
index)
override {
3495 index + secondary_vars_offset_))
3499 int index,
int* container_index,
3500 int64_t* new_cost)
override {
3503 const IntVarElement& element = container.Element(*container_index);
3504 const IntVar* secondary_var =
3506 if (element.Activated()) {
3507 const int64_t
value = element.Value();
3508 int hint_index = *container_index + 1;
3509 if (hint_index < container.Size() &&
3510 secondary_var == container.Element(hint_index).Var()) {
3512 container.Element(hint_index).Value());
3513 *container_index = hint_index;
3516 container.Element(secondary_var).Value());
3520 const IntVar*
var = element.Var();
3521 if (
var->Bound() && secondary_var->Bound()) {
3522 *new_cost = value_evaluator_(
index,
var->Min(), secondary_var->Min());
3530 int secondary_vars_offset_;
3539 new BinaryObjectiveFilter(vars, std::move(values), filter_enum));
3543 const std::vector<IntVar*>& vars,
3546 return RevAlloc(
new TernaryObjectiveFilter(vars, secondary_vars,
3547 std::move(values), filter_enum));
3551 int64_t initial_max) {
3554 initial_variable_bounds_.push_back({initial_min, initial_max});
3555 variable_bounds_.push_back({initial_min, initial_max});
3556 variable_is_relaxed_.push_back(
false);
3558 const int variable_index = variable_bounds_.size() - 1;
3559 return {
this, variable_index};
3562 void LocalSearchState::RelaxVariableBounds(
int variable_index) {
3564 DCHECK(0 <= variable_index && variable_index < variable_is_relaxed_.size());
3565 if (!variable_is_relaxed_[variable_index]) {
3566 variable_is_relaxed_[variable_index] =
true;
3567 saved_variable_bounds_trail_.emplace_back(variable_bounds_[variable_index],
3569 variable_bounds_[variable_index] = initial_variable_bounds_[variable_index];
3573 int64_t LocalSearchState::VariableMin(
int variable_index)
const {
3575 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3576 return variable_bounds_[variable_index].min;
3579 int64_t LocalSearchState::VariableMax(
int variable_index)
const {
3581 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3582 return variable_bounds_[variable_index].max;
3585 bool LocalSearchState::TightenVariableMin(
int variable_index,
3586 int64_t min_value) {
3588 DCHECK(variable_is_relaxed_[variable_index]);
3589 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3590 Bounds&
bounds = variable_bounds_[variable_index];
3591 if (
bounds.max < min_value) {
3592 state_is_valid_ =
false;
3595 return state_is_valid_;
3598 bool LocalSearchState::TightenVariableMax(
int variable_index,
3599 int64_t max_value) {
3601 DCHECK(variable_is_relaxed_[variable_index]);
3602 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3603 Bounds&
bounds = variable_bounds_[variable_index];
3604 if (
bounds.min > max_value) {
3605 state_is_valid_ =
false;
3608 return state_is_valid_;
3616 saved_variable_bounds_trail_.clear();
3617 variable_is_relaxed_.assign(variable_is_relaxed_.size(),
false);
3621 for (
const auto& bounds_index : saved_variable_bounds_trail_) {
3622 DCHECK(variable_is_relaxed_[bounds_index.second]);
3623 variable_bounds_[bounds_index.second] = bounds_index.first;
3625 saved_variable_bounds_trail_.clear();
3626 variable_is_relaxed_.assign(variable_is_relaxed_.size(),
false);
3627 state_is_valid_ =
true;
3635 std::string
DebugString()
const override {
return "LocalSearchProfiler"; }
3637 operator_stats_.clear();
3638 filter_stats_.clear();
3642 if (
solver()->TopLevelSearch() ==
solver()->ActiveSearch()) {
3647 LocalSearchStatistics statistics_proto;
3648 std::vector<const LocalSearchOperator*> operators;
3649 for (
const auto& stat : operator_stats_) {
3650 operators.push_back(stat.first);
3653 operators.begin(), operators.end(),
3655 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3656 gtl::FindOrDie(operator_stats_, op2).neighbors;
3659 const OperatorStats& stats =
gtl::FindOrDie(operator_stats_, op);
3660 LocalSearchStatistics::LocalSearchOperatorStatistics*
const
3661 local_search_operator_statistics =
3662 statistics_proto.add_local_search_operator_statistics();
3663 local_search_operator_statistics->set_local_search_operator(
3665 local_search_operator_statistics->set_num_neighbors(stats.neighbors);
3666 local_search_operator_statistics->set_num_filtered_neighbors(
3667 stats.filtered_neighbors);
3668 local_search_operator_statistics->set_num_accepted_neighbors(
3669 stats.accepted_neighbors);
3670 local_search_operator_statistics->set_duration_seconds(stats.seconds);
3672 std::vector<const LocalSearchFilter*> filters;
3673 for (
const auto& stat : filter_stats_) {
3674 filters.push_back(stat.first);
3676 std::sort(filters.begin(), filters.end(),
3679 return gtl::FindOrDie(filter_stats_, filter1).calls >
3680 gtl::FindOrDie(filter_stats_, filter2).calls;
3683 const FilterStats& stats =
gtl::FindOrDie(filter_stats_, filter);
3684 LocalSearchStatistics::LocalSearchFilterStatistics*
const
3685 local_search_filter_statistics =
3686 statistics_proto.add_local_search_filter_statistics();
3687 local_search_filter_statistics->set_local_search_filter(
3688 filter->DebugString());
3689 local_search_filter_statistics->set_num_calls(stats.calls);
3690 local_search_filter_statistics->set_num_rejects(stats.rejects);
3691 local_search_filter_statistics->set_duration_seconds(stats.seconds);
3693 statistics_proto.set_total_num_neighbors(
solver()->neighbors());
3694 statistics_proto.set_total_num_filtered_neighbors(
3695 solver()->filtered_neighbors());
3696 statistics_proto.set_total_num_accepted_neighbors(
3697 solver()->accepted_neighbors());
3698 return statistics_proto;
3701 size_t max_name_size = 0;
3702 std::vector<const LocalSearchOperator*> operators;
3703 for (
const auto& stat : operator_stats_) {
3704 operators.push_back(stat.first);
3706 std::max(max_name_size, stat.first->DebugString().length());
3709 operators.begin(), operators.end(),
3711 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3712 gtl::FindOrDie(operator_stats_, op2).neighbors;
3714 std::string overview =
"Local search operator statistics:\n";
3715 absl::StrAppendFormat(&overview,
3716 "%*s | Neighbors | Filtered | Accepted | Time (s)\n",
3718 OperatorStats total_stats;
3720 const OperatorStats& stats =
gtl::FindOrDie(operator_stats_, op);
3721 const std::string&
name = op->DebugString();
3722 absl::StrAppendFormat(&overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n",
3723 max_name_size,
name, stats.neighbors,
3724 stats.filtered_neighbors, stats.accepted_neighbors,
3726 total_stats.neighbors += stats.neighbors;
3727 total_stats.filtered_neighbors += stats.filtered_neighbors;
3728 total_stats.accepted_neighbors += stats.accepted_neighbors;
3729 total_stats.seconds += stats.seconds;
3731 absl::StrAppendFormat(&overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n",
3732 max_name_size,
"Total", total_stats.neighbors,
3733 total_stats.filtered_neighbors,
3734 total_stats.accepted_neighbors, total_stats.seconds);
3736 std::vector<const LocalSearchFilter*> filters;
3737 for (
const auto& stat : filter_stats_) {
3738 filters.push_back(stat.first);
3740 std::max(max_name_size, stat.first->DebugString().length());
3742 std::sort(filters.begin(), filters.end(),
3745 return gtl::FindOrDie(filter_stats_, filter1).calls >
3746 gtl::FindOrDie(filter_stats_, filter2).calls;
3748 absl::StrAppendFormat(&overview,
3749 "Local search filter statistics:\n%*s | Calls | "
3750 " Rejects | Time (s) "
3753 FilterStats total_filter_stats;
3755 const FilterStats& stats =
gtl::FindOrDie(filter_stats_, filter);
3756 const std::string&
name = filter->DebugString();
3757 absl::StrAppendFormat(&overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n",
3758 max_name_size,
name, stats.calls, stats.rejects,
3759 stats.seconds, stats.rejects / stats.seconds);
3760 total_filter_stats.calls += stats.calls;
3761 total_filter_stats.rejects += stats.rejects;
3762 total_filter_stats.seconds += stats.seconds;
3764 absl::StrAppendFormat(
3765 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n", max_name_size,
3766 "Total", total_filter_stats.calls, total_filter_stats.rejects,
3767 total_filter_stats.seconds,
3768 total_filter_stats.rejects / total_filter_stats.seconds);
3774 if (last_operator_ != op->
Self()) {
3776 last_operator_ = op->
Self();
3782 if (neighbor_found) {
3783 operator_stats_[op->
Self()].neighbors++;
3788 bool neighbor_found)
override {
3789 if (neighbor_found) {
3790 operator_stats_[op->
Self()].filtered_neighbors++;
3795 bool neighbor_found)
override {
3796 if (neighbor_found) {
3797 operator_stats_[op->
Self()].accepted_neighbors++;
3801 filter_stats_[filter].calls++;
3802 filter_timer_.
Start();
3805 filter_timer_.
Stop();
3806 auto& stats = filter_stats_[filter];
3807 stats.seconds += filter_timer_.
Get();
3816 if (last_operator_ !=
nullptr) {
3818 operator_stats_[last_operator_].seconds += timer_.
Get();
3823 struct OperatorStats {
3824 int64_t neighbors = 0;
3825 int64_t filtered_neighbors = 0;
3826 int64_t accepted_neighbors = 0;
3830 struct FilterStats {
3832 int64_t rejects = 0;
3837 const LocalSearchOperator* last_operator_ =
nullptr;
3838 absl::flat_hash_map<const LocalSearchOperator*, OperatorStats>
3840 absl::flat_hash_map<const LocalSearchFilter*, FilterStats> filter_stats_;
3857 if (local_search_profiler_ !=
nullptr) {
3864 if (local_search_profiler_ !=
nullptr) {
3867 return LocalSearchStatistics();
3870 void LocalSearchFilterManager::InitializeForcedEvents() {
3871 const int num_events = filter_events_.size();
3872 int next_forced_event = num_events;
3873 next_forced_events_.resize(num_events);
3874 for (
int i = num_events - 1; i >= 0; --i) {
3875 next_forced_events_[i] = next_forced_event;
3876 if (filter_events_[i].filter->IsIncremental() ||
3877 (filter_events_[i].event_type == FilterEventType::kRelax &&
3878 next_forced_event != num_events)) {
3879 next_forced_event = i;
3885 std::vector<LocalSearchFilter*> filters)
3886 : synchronized_value_(std::numeric_limits<int64_t>::
min()),
3887 accepted_value_(std::numeric_limits<int64_t>::
min()) {
3888 filter_events_.reserve(2 * filters.size());
3890 filter_events_.push_back({filter, FilterEventType::kRelax});
3893 filter_events_.push_back({filter, FilterEventType::kAccept});
3895 InitializeForcedEvents();
3899 std::vector<FilterEvent> filter_events)
3900 : filter_events_(std::move(filter_events)),
3901 synchronized_value_(std::numeric_limits<int64_t>::
min()),
3902 accepted_value_(std::numeric_limits<int64_t>::
min()) {
3903 InitializeForcedEvents();
3909 for (
int i = last_event_called_; i >= 0; --i) {
3910 auto [filter, event_type] = filter_events_[i];
3911 if (event_type == FilterEventType::kRelax) filter->Revert();
3913 last_event_called_ = -1;
3922 int64_t objective_min,
3923 int64_t objective_max) {
3925 accepted_value_ = 0;
3927 const int num_events = filter_events_.size();
3928 for (
int i = 0; i < num_events;) {
3929 last_event_called_ = i;
3930 auto [filter, event_type] = filter_events_[last_event_called_];
3931 switch (event_type) {
3932 case FilterEventType::kAccept: {
3934 const bool accept = filter->Accept(
3935 delta, deltadelta,
CapSub(objective_min, accepted_value_),
3936 CapSub(objective_max, accepted_value_));
3938 if (monitor !=
nullptr) monitor->
EndFiltering(filter, !accept);
3941 CapAdd(accepted_value_, filter->GetAcceptedObjectiveValue());
3943 ok = accepted_value_ <= objective_max;
3947 case FilterEventType::kRelax: {
3948 filter->Relax(
delta, deltadelta);
3952 LOG(
FATAL) <<
"Unknown filter event type.";
3958 i = next_forced_events_[i];
3969 const bool reset_to_assignment =
delta ==
nullptr ||
delta->Empty();
3971 for (
auto [filter, event_type] : filter_events_) {
3972 switch (event_type) {
3973 case FilterEventType::kAccept: {
3976 case FilterEventType::kRelax: {
3977 if (reset_to_assignment) {
3979 filter->Relax(assignment,
nullptr);
3981 filter->Relax(
delta,
nullptr);
3986 LOG(
FATAL) <<
"Unknown filter event type.";
3991 synchronized_value_ = 0;
3993 switch (event_type) {
3994 case FilterEventType::kAccept: {
3995 filter->Synchronize(assignment,
delta);
3996 synchronized_value_ =
CapAdd(synchronized_value_,
3997 filter->GetSynchronizedObjectiveValue());
4000 case FilterEventType::kRelax: {
4001 filter->Commit(assignment,
delta);
4005 LOG(
FATAL) <<
"Unknown filter event type.";
4022 std::string
DebugString()
const override {
return "FindOneNeighbor"; }
4026 int64_t objective_min, int64_t objective_max);
4027 void SynchronizeAll(
Solver* solver);
4030 IntVar*
const objective_;
4031 std::unique_ptr<Assignment> reference_assignment_;
4037 bool neighbor_found_;
4039 int64_t solutions_since_last_check_;
4040 int64_t check_period_;
4042 bool has_checked_assignment_ =
false;
4054 : assignment_(assignment),
4056 reference_assignment_(new
Assignment(assignment_)),
4058 ls_operator_(ls_operator),
4059 sub_decision_builder_(sub_decision_builder),
4061 original_limit_(limit),
4062 neighbor_found_(false),
4063 filter_manager_(filter_manager),
4064 solutions_since_last_check_(0),
4066 assignment_->solver()->
parameters().check_solution_period()),
4067 last_checked_assignment_(assignment) {
4068 CHECK(
nullptr != assignment);
4069 CHECK(
nullptr != ls_operator);
4073 if (
nullptr == limit) {
4081 VLOG(1) <<
"Disabling neighbor-check skipping outside of first accept.";
4088 VLOG(1) <<
"Disabling neighbor-check skipping for LNS.";
4092 if (!reference_assignment_->HasObjective()) {
4093 reference_assignment_->AddObjective(objective_);
4098 CHECK(
nullptr != solver);
4100 if (original_limit_ !=
nullptr) {
4101 limit_->
Copy(original_limit_);
4108 if (!neighbor_found_) {
4116 SynchronizeAll(solver);
4126 if (sub_decision_builder_) {
4127 restore = solver->
Compose(restore, sub_decision_builder_);
4135 delta->ClearObjective();
4136 deltadelta->
Clear();
4138 if (++counter >= absl::GetFlag(FLAGS_cp_local_search_sync_frequency) &&
4139 pool_->
SyncNeeded(reference_assignment_.get())) {
4142 SynchronizeAll(solver);
4145 bool has_neighbor =
false;
4146 if (!limit_->
Check()) {
4150 ls_operator_, has_neighbor,
delta, deltadelta);
4153 if (has_neighbor && !solver->IsUncheckedSolutionLimitReached()) {
4154 solver->neighbors_ += 1;
4161 const bool mh_filter =
4166 objective_min = objective_->
Min();
4167 objective_max = objective_->
Max();
4169 if (
delta->HasObjective() &&
delta->Objective() == objective_) {
4170 objective_min =
std::max(objective_min,
delta->ObjectiveMin());
4171 objective_max =
std::min(objective_max,
delta->ObjectiveMax());
4173 const bool move_filter = FilterAccept(solver,
delta, deltadelta,
4174 objective_min, objective_max);
4176 ls_operator_, mh_filter && move_filter);
4177 if (!mh_filter || !move_filter) {
4178 if (filter_manager_ !=
nullptr) filter_manager_->
Revert();
4181 solver->filtered_neighbors_ += 1;
4182 if (
delta->HasObjective()) {
4194 const bool check_solution = (solutions_since_last_check_ == 0) ||
4197 !
delta->AreAllElementsBound();
4198 if (has_checked_assignment_) solutions_since_last_check_++;
4199 if (solutions_since_last_check_ >= check_period_) {
4200 solutions_since_last_check_ = 0;
4202 const bool accept = !check_solution || solver->
SolveAndCommit(restore);
4206 solver->accepted_neighbors_ += 1;
4207 if (check_solution) {
4210 assignment_->
Store();
4212 neighbor_found_ =
true;
4213 has_checked_assignment_ =
true;
4227 solver->IncrementUncheckedSolutionCounter();
4229 SynchronizeAll(solver);
4232 neighbor_found_ =
true;
4234 if (filter_manager_ !=
nullptr) filter_manager_->
Revert();
4235 if (check_period_ > 1 && has_checked_assignment_) {
4241 VLOG(1) <<
"Imperfect filtering detected, backtracking to last "
4242 "checked solution and checking all solutions.";
4244 solutions_since_last_check_ = 0;
4246 SynchronizeAll(solver);
4251 if (neighbor_found_) {
4263 has_checked_assignment_ =
true;
4271 SynchronizeAll(solver);
4284 int64_t objective_min,
4285 int64_t objective_max) {
4286 if (filter_manager_ ==
nullptr)
return true;
4288 return filter_manager_->
Accept(monitor,
delta, deltadelta, objective_min,
4292 void FindOneNeighbor::SynchronizeAll(Solver* solver) {
4294 neighbor_found_ =
false;
4296 solver->GetLocalSearchMonitor()->BeginOperatorStart();
4297 ls_operator_->
Start(reference_assignment_.get());
4298 if (filter_manager_ !=
nullptr) {
4299 filter_manager_->
Synchronize(reference_assignment_.get(),
nullptr);
4301 solver->GetLocalSearchMonitor()->EndOperatorStart();
4314 solution_pool_(pool),
4321 return "LocalSearchPhaseParameters";
4328 return sub_decision_builder_;
4332 return filter_manager_;
4336 IntVar*
const objective_;
4348 ls_operator, sub_decision_builder,
4356 ls_operator, sub_decision_builder,
4365 ls_operator, sub_decision_builder,
4366 limit, filter_manager);
4374 sub_decision_builder,
nullptr,
nullptr);
4382 sub_decision_builder, limit,
nullptr);
4391 sub_decision_builder, limit,
4405 class NestedSolveDecision :
public Decision {
4408 enum StateType { DECISION_PENDING, DECISION_FAILED, DECISION_FOUND };
4410 NestedSolveDecision(DecisionBuilder*
const db,
bool restore,
4411 const std::vector<SearchMonitor*>& monitors);
4412 NestedSolveDecision(DecisionBuilder*
const db,
bool restore);
4413 ~NestedSolveDecision()
override {}
4414 void Apply(Solver*
const solver)
override;
4415 void Refute(Solver*
const solver)
override;
4416 std::string DebugString()
const override {
return "NestedSolveDecision"; }
4417 int state()
const {
return state_; }
4420 DecisionBuilder*
const db_;
4422 std::vector<SearchMonitor*> monitors_;
4426 NestedSolveDecision::NestedSolveDecision(
4427 DecisionBuilder*
const db,
bool restore,
4428 const std::vector<SearchMonitor*>& monitors)
4431 monitors_(monitors),
4432 state_(DECISION_PENDING) {
4433 CHECK(
nullptr != db);
4436 NestedSolveDecision::NestedSolveDecision(DecisionBuilder*
const db,
4438 : db_(db), restore_(restore), state_(DECISION_PENDING) {
4439 CHECK(
nullptr != db);
4442 void NestedSolveDecision::Apply(Solver*
const solver) {
4443 CHECK(
nullptr != solver);
4445 if (solver->Solve(db_, monitors_)) {
4446 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FOUND));
4448 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FAILED));
4451 if (solver->SolveAndCommit(db_, monitors_)) {
4452 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FOUND));
4454 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FAILED));
4459 void NestedSolveDecision::Refute(Solver*
const solver) {}
4470 class LocalSearch :
public DecisionBuilder {
4472 LocalSearch(Assignment*
const assignment, IntVar* objective,
4473 SolutionPool*
const pool, LocalSearchOperator*
const ls_operator,
4474 DecisionBuilder*
const sub_decision_builder,
4475 RegularLimit*
const limit,
4476 LocalSearchFilterManager* filter_manager);
4479 LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4480 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4481 LocalSearchOperator*
const ls_operator,
4482 DecisionBuilder*
const sub_decision_builder,
4483 RegularLimit*
const limit,
4484 LocalSearchFilterManager* filter_manager);
4485 LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4486 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4487 DecisionBuilder*
const first_solution_sub_decision_builder,
4488 LocalSearchOperator*
const ls_operator,
4489 DecisionBuilder*
const sub_decision_builder,
4490 RegularLimit*
const limit,
4491 LocalSearchFilterManager* filter_manager);
4492 LocalSearch(
const std::vector<SequenceVar*>& vars, IntVar* objective,
4493 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4494 LocalSearchOperator*
const ls_operator,
4495 DecisionBuilder*
const sub_decision_builder,
4496 RegularLimit*
const limit,
4497 LocalSearchFilterManager* filter_manager);
4498 ~LocalSearch()
override;
4499 Decision* Next(Solver*
const solver)
override;
4500 std::string DebugString()
const override {
return "LocalSearch"; }
4501 void Accept(ModelVisitor*
const visitor)
const override;
4504 void PushFirstSolutionDecision(DecisionBuilder* first_solution);
4505 void PushLocalSearchDecision();
4508 Assignment* assignment_;
4510 SolutionPool*
const pool_;
4511 LocalSearchOperator*
const ls_operator_;
4512 DecisionBuilder*
const first_solution_sub_decision_builder_;
4513 DecisionBuilder*
const sub_decision_builder_;
4514 std::vector<NestedSolveDecision*> nested_decisions_;
4515 int nested_decision_index_;
4516 RegularLimit*
const limit_;
4517 LocalSearchFilterManager*
const filter_manager_;
4521 LocalSearch::LocalSearch(Assignment*
const assignment, IntVar* objective,
4522 SolutionPool*
const pool,
4523 LocalSearchOperator*
const ls_operator,
4524 DecisionBuilder*
const sub_decision_builder,
4525 RegularLimit*
const limit,
4526 LocalSearchFilterManager* filter_manager)
4527 : assignment_(nullptr),
4530 ls_operator_(ls_operator),
4531 first_solution_sub_decision_builder_(sub_decision_builder),
4532 sub_decision_builder_(sub_decision_builder),
4533 nested_decision_index_(0),
4535 filter_manager_(filter_manager),
4536 has_started_(false) {
4537 CHECK(
nullptr != assignment);
4538 CHECK(
nullptr != ls_operator);
4539 Solver*
const solver = assignment->solver();
4540 assignment_ = solver->GetOrCreateLocalSearchState();
4541 assignment_->Copy(assignment);
4542 DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment);
4543 PushFirstSolutionDecision(restore);
4544 PushLocalSearchDecision();
4547 LocalSearch::LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4548 SolutionPool*
const pool,
4549 DecisionBuilder*
const first_solution,
4550 LocalSearchOperator*
const ls_operator,
4551 DecisionBuilder*
const sub_decision_builder,
4552 RegularLimit*
const limit,
4553 LocalSearchFilterManager* filter_manager)
4554 : assignment_(nullptr),
4557 ls_operator_(ls_operator),
4558 first_solution_sub_decision_builder_(sub_decision_builder),
4559 sub_decision_builder_(sub_decision_builder),
4560 nested_decision_index_(0),
4562 filter_manager_(filter_manager),
4563 has_started_(false) {
4564 CHECK(
nullptr != first_solution);
4565 CHECK(
nullptr != ls_operator);
4566 CHECK(!vars.empty());
4567 Solver*
const solver = vars[0]->solver();
4568 assignment_ = solver->GetOrCreateLocalSearchState();
4569 assignment_->Add(vars);
4570 PushFirstSolutionDecision(first_solution);
4571 PushLocalSearchDecision();
4574 LocalSearch::LocalSearch(
4575 const std::vector<IntVar*>& vars, IntVar* objective,
4576 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4577 DecisionBuilder*
const first_solution_sub_decision_builder,
4578 LocalSearchOperator*
const ls_operator,
4579 DecisionBuilder*
const sub_decision_builder, RegularLimit*
const limit,
4580 LocalSearchFilterManager* filter_manager)
4581 : assignment_(nullptr),
4584 ls_operator_(ls_operator),
4585 first_solution_sub_decision_builder_(first_solution_sub_decision_builder),
4586 sub_decision_builder_(sub_decision_builder),
4587 nested_decision_index_(0),
4589 filter_manager_(filter_manager),
4590 has_started_(false) {
4591 CHECK(
nullptr != first_solution);
4592 CHECK(
nullptr != ls_operator);
4593 CHECK(!vars.empty());
4594 Solver*
const solver = vars[0]->solver();
4595 assignment_ = solver->GetOrCreateLocalSearchState();
4596 assignment_->Add(vars);
4597 PushFirstSolutionDecision(first_solution);
4598 PushLocalSearchDecision();
4601 LocalSearch::LocalSearch(
const std::vector<SequenceVar*>& vars,
4602 IntVar* objective, SolutionPool*
const pool,
4603 DecisionBuilder*
const first_solution,
4604 LocalSearchOperator*
const ls_operator,
4605 DecisionBuilder*
const sub_decision_builder,
4606 RegularLimit*
const limit,
4607 LocalSearchFilterManager* filter_manager)
4608 : assignment_(nullptr),
4611 ls_operator_(ls_operator),
4612 first_solution_sub_decision_builder_(sub_decision_builder),
4613 sub_decision_builder_(sub_decision_builder),
4614 nested_decision_index_(0),
4616 filter_manager_(filter_manager),
4617 has_started_(false) {
4618 CHECK(
nullptr != first_solution);
4619 CHECK(
nullptr != ls_operator);
4620 CHECK(!vars.empty());
4621 Solver*
const solver = vars[0]->solver();
4622 assignment_ = solver->GetOrCreateLocalSearchState();
4623 assignment_->Add(vars);
4624 PushFirstSolutionDecision(first_solution);
4625 PushLocalSearchDecision();
4628 LocalSearch::~LocalSearch() {}
4631 void LocalSearch::Accept(ModelVisitor*
const visitor)
const {
4632 DCHECK(assignment_ !=
nullptr);
4635 const std::vector<IntVarElement>& elements =
4637 if (!elements.empty()) {
4638 std::vector<IntVar*> vars;
4639 for (
const IntVarElement& elem : elements) {
4640 vars.push_back(elem.Var());
4645 const std::vector<IntervalVarElement>& interval_elements =
4647 if (!interval_elements.empty()) {
4648 std::vector<IntervalVar*> interval_vars;
4649 for (
const IntervalVarElement& elem : interval_elements) {
4650 interval_vars.push_back(elem.Var());
4663 Decision* LocalSearch::Next(Solver*
const solver) {
4664 CHECK(
nullptr != solver);
4665 CHECK_LT(0, nested_decisions_.size());
4666 if (!has_started_) {
4667 nested_decision_index_ = 0;
4668 solver->SaveAndSetValue(&has_started_,
true);
4669 }
else if (nested_decision_index_ < 0) {
4672 NestedSolveDecision* decision = nested_decisions_[nested_decision_index_];
4673 const int state = decision->state();
4675 case NestedSolveDecision::DECISION_FAILED: {
4679 ls_operator_->
Reset();
4681 nested_decision_index_ = -1;
4686 case NestedSolveDecision::DECISION_PENDING: {
4689 const int32_t kLocalSearchBalancedTreeDepth = 32;
4690 const int depth = solver->SearchDepth();
4691 if (depth < kLocalSearchBalancedTreeDepth) {
4692 return solver->balancing_decision();
4694 if (depth > kLocalSearchBalancedTreeDepth) {
4699 case NestedSolveDecision::DECISION_FOUND: {
4701 if (nested_decision_index_ + 1 < nested_decisions_.size()) {
4702 ++nested_decision_index_;
4707 LOG(
ERROR) <<
"Unknown local search state";
4714 void LocalSearch::PushFirstSolutionDecision(DecisionBuilder* first_solution) {
4715 CHECK(first_solution);
4716 Solver*
const solver = assignment_->
solver();
4718 DecisionBuilder* first_solution_and_store = solver->Compose(
4719 first_solution, first_solution_sub_decision_builder_, store);
4720 std::vector<SearchMonitor*> monitor;
4721 monitor.push_back(
limit_);
4722 nested_decisions_.push_back(solver->RevAlloc(
4723 new NestedSolveDecision(first_solution_and_store,
false, monitor)));
4726 void LocalSearch::PushLocalSearchDecision() {
4727 Solver*
const solver = assignment_->
solver();
4728 DecisionBuilder* find_neighbors = solver->
RevAlloc(
4729 new FindOneNeighbor(assignment_,
objective_, pool_, ls_operator_,
4730 sub_decision_builder_,
limit_, filter_manager_));
4731 nested_decisions_.push_back(
4732 solver->RevAlloc(
new NestedSolveDecision(find_neighbors,
false)));
4735 class DefaultSolutionPool :
public SolutionPool {
4737 DefaultSolutionPool() {}
4739 ~DefaultSolutionPool()
override {}
4741 void Initialize(Assignment*
const assignment)
override {
4742 reference_assignment_ = absl::make_unique<Assignment>(assignment);
4745 void RegisterNewSolution(Assignment*
const assignment)
override {
4746 reference_assignment_->CopyIntersection(assignment);
4749 void GetNextSolution(Assignment*
const assignment)
override {
4750 assignment->CopyIntersection(reference_assignment_.get());
4753 bool SyncNeeded(Assignment*
const local_assignment)
override {
return false; }
4755 std::string DebugString()
const override {
return "DefaultSolutionPool"; }
4758 std::unique_ptr<Assignment> reference_assignment_;
4763 return RevAlloc(
new DefaultSolutionPool());
4790 first_solution, first_solution_sub_decision_builder,
4796 const std::vector<SequenceVar*>& vars,
DecisionBuilder* first_solution,
#define DCHECK_LE(val1, val2)
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
const std::vector< E > & elements() const
const E & Element(const V *const var) const
An Assignment is a variable -> domains mapping, used to report solutions to the user.
const IntContainer & IntVarContainer() const
void SetObjectiveValue(int64_t value)
IntVar * Objective() const
int64_t ObjectiveValue() const
bool HasObjective() const
void AddObjective(IntVar *const v)
void CopyIntersection(const Assignment *assignment)
Copies the intersection of the two assignments to the current assignment.
AssignmentContainer< IntVar, IntVarElement > IntContainer
const IntervalContainer & IntervalVarContainer() const
std::string DebugString() const override
~BaseInactiveNodeToPathOperator() override
BaseInactiveNodeToPathOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, int number_of_base_nodes, std::function< int(int64_t)> start_empty_path_class)
int64_t GetInactiveNode() const
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
This is the base class for building an Lns operator.
virtual bool NextFragment()=0
void AppendToFragment(int index)
BaseLns(const std::vector< IntVar * > &vars)
bool MakeOneNeighbor() override
This method should not be overridden. Override NextFragment() instead.
virtual void InitFragments()
A BaseObject is the root of all reversibly allocated objects.
virtual std::string DebugString() const
ChangeValue(const std::vector< IntVar * > &vars)
virtual int64_t ModifyValue(int64_t index, int64_t value)=0
bool MakeOneNeighbor() override
This method should not be overridden. Override ModifyValue() instead.
bool MakeNeighbor() override
Cross(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
A DecisionBuilder is responsible for creating the search tree.
A Decision represents a choice point in the search tree.
bool MakeNeighbor() override
Exchange(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
bool MakeNeighbor() override
ExtendedSwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
~ExtendedSwapActiveOperator() override
std::string DebugString() const override
~FindOneNeighbor() override
FindOneNeighbor(Assignment *const assignment, IntVar *objective, SolutionPool *const pool, LocalSearchOperator *const ls_operator, DecisionBuilder *const sub_decision_builder, const RegularLimit *const limit, LocalSearchFilterManager *filter_manager)
Decision * Next(Solver *const solver) override
This is the main method of the decision builder class.
std::string DebugString() const override
void ChangeCostMatrix(CostFunction cost)
std::vector< int > TravelingSalesmanPath()
virtual int64_t Min() const =0
virtual int64_t Max() const =0
The class IntVar is a subset of IntExpr.
void SynchronizeOnAssignment(const Assignment *assignment)
virtual void OnSynchronize(const Assignment *delta)
~IntVarLocalSearchFilter() override
void Synchronize(const Assignment *assignment, const Assignment *delta) override
This method should not be overridden.
bool FindIndex(IntVar *const var, int64_t *index) const
IntVarLocalSearchFilter(const std::vector< IntVar * > &vars)
int64_t Value(int index) const
IntVar * Var(int index) const
void AddVars(const std::vector< IntVar * > &vars)
Add variables to "track" to the filter.
bool IsVarSynced(int index) const
Specialization of LocalSearchOperator built from an array of IntVars which specifies the scope of the...
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
Redefines MakeNextNeighbor to export a simpler interface.
virtual bool MakeOneNeighbor()
Creates a new neighbor.
bool MakeNeighbor() override
LinKernighan(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, const Solver::IndexEvaluator3 &evaluator, bool topt)
std::string DebugString() const override
Local Search Filters are used for fast neighbor pruning.
Filter manager: when a move is made, filters are executed to decide whether the solution is feasible ...
int64_t GetAcceptedObjectiveValue() const
LocalSearchFilterManager(std::vector< FilterEvent > filter_events)
bool Accept(LocalSearchMonitor *const monitor, const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max)
Returns true iff all filters return true, and the sum of their accepted objectives is between objecti...
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
virtual void EndMakeNextNeighbor(const LocalSearchOperator *op, bool neighbor_found, const Assignment *delta, const Assignment *deltadelta)=0
virtual void EndAcceptNeighbor(const LocalSearchOperator *op, bool neighbor_found)=0
virtual void BeginMakeNextNeighbor(const LocalSearchOperator *op)=0
virtual void EndFiltering(const LocalSearchFilter *filter, bool reject)=0
virtual void BeginFilterNeighbor(const LocalSearchOperator *op)=0
virtual void BeginAcceptNeighbor(const LocalSearchOperator *op)=0
virtual void BeginFiltering(const LocalSearchFilter *filter)=0
virtual void EndFilterNeighbor(const LocalSearchOperator *op, bool neighbor_found)=0
The base class for all local search operators.
virtual bool HasFragments() const
virtual bool HoldsDelta() const
virtual const LocalSearchOperator * Self() const
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
virtual void Start(const Assignment *assignment)=0
LocalSearchOperator * ls_operator() const
~LocalSearchPhaseParameters() override
SolutionPool * solution_pool() const
IntVar * objective() const
LocalSearchFilterManager *const filter_manager() const
RegularLimit * limit() const
LocalSearchPhaseParameters(IntVar *objective, SolutionPool *const pool, LocalSearchOperator *ls_operator, DecisionBuilder *sub_decision_builder, RegularLimit *const limit, LocalSearchFilterManager *filter_manager)
DecisionBuilder * sub_decision_builder() const
std::string DebugString() const override
void BeginFiltering(const LocalSearchFilter *filter) override
void Install() override
Install itself on the solver.
void BeginOperatorStart() override
Local search operator events.
void RestartSearch() override
Restart the search.
void EndMakeNextNeighbor(const LocalSearchOperator *op, bool neighbor_found, const Assignment *delta, const Assignment *deltadelta) override
LocalSearchStatistics ExportToLocalSearchStatistics() const
void BeginMakeNextNeighbor(const LocalSearchOperator *op) override
void EndAcceptNeighbor(const LocalSearchOperator *op, bool neighbor_found) override
void BeginAcceptNeighbor(const LocalSearchOperator *op) override
void ExitSearch() override
End of the search.
LocalSearchProfiler(Solver *solver)
void EndFilterNeighbor(const LocalSearchOperator *op, bool neighbor_found) override
void EndOperatorStart() override
std::string PrintOverview() const
void EndFiltering(const LocalSearchFilter *filter, bool reject) override
void BeginFilterNeighbor(const LocalSearchOperator *op) override
std::string DebugString() const override
LocalSearchVariable AddVariable(int64_t initial_min, int64_t initial_max)
bool MakeNeighbor() override
~MakeActiveAndRelocate() override
MakeActiveAndRelocate(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
~MakeActiveOperator() override
bool MakeNeighbor() override
MakeActiveOperator(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
Returns the index of the node to which the base node of index base_index must be set to when it reach...
bool MakeNeighbor() override
~MakeChainInactiveOperator() override
bool OnSamePathAsPreviousBase(int64_t base_index) override
Returns true if a base node has to be on the same path as the "previous" base node (base node of inde...
std::string DebugString() const override
MakeChainInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
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
std::string DebugString() const override
static const char kIntervalsArgument[]
static const char kVarsArgument[]
static const char kVariableGroupExtension[]
const std::vector< int > & Neighbors(int index) const
NearestNeighbors(Solver::IndexEvaluator3 evaluator, const PathOperator &path_operator, int size)
virtual std::string DebugString() const
virtual ~NearestNeighbors()
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
bool HoldsDelta() const override
NeighborhoodLimit(LocalSearchOperator *const op, int64_t limit)
void Start(const Assignment *assignment) 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
bool HasFragments() const override
std::string DebugString() const override
Base class of the local search operators dedicated to path modifications (a path is a set of nodes li...
int64_t StartNode(int i) const
Returns the start node of the ith base node.
bool IsInactive(int64_t node) const
Returns true if node is inactive.
virtual bool ConsiderAlternatives(int64_t base_index) const
Indicates if alternatives should be considered when iterating over base nodes.
virtual bool MakeNeighbor()=0
int PathClass(int i) const
Returns the class of the path of the ith base node.
virtual void OnNodeInitialization()
Called by OnStart() after initializing node information.
virtual bool OnSamePathAsPreviousBase(int64_t base_index)
Returns true if a base node has to be on the same path as the "previous" base node (base node of inde...
int number_of_nexts() const
Number of next variables.
bool CheckChainValidity(int64_t before_chain, int64_t chain_end, int64_t exclude) const
Returns true if the chain is a valid path without cycles from before_chain to chain_end and does not ...
virtual bool RestartAtPathStartOnSynchronize()
When the operator is being synchronized with a new solution (when Start() is called),...
bool IsPathEnd(int64_t node) const
Returns true if node is the last node on the path; defined by the fact that node is outside the range...
int64_t Next(int64_t node) const
Returns the node after node in the current delta.
bool MoveChain(int64_t before_chain, int64_t chain_end, int64_t destination)
Moves the chain starting after the node before_chain and ending at the node chain_end after the node ...
bool MakeActive(int64_t node, int64_t destination)
Insert the inactive node after destination.
bool ReverseChain(int64_t before_chain, int64_t after_chain, int64_t *chain_last)
Reverses the chain starting after before_chain and ending before after_chain.
std::vector< int64_t > start_to_path_
void SetNext(int64_t from, int64_t to, int64_t path)
Sets 'to' to be the node after 'from' on the given path.
int64_t Prev(int64_t node) const
Returns the node before node in the current delta.
int64_t OldNext(int64_t node) const
bool SkipUnchanged(int index) const override
const int number_of_nexts_
bool SwapActiveAndInactive(int64_t active, int64_t inactive)
Replaces active by inactive in the current path, making active inactive.
void ResetPosition()
Reset the position of the operator to its position when Start() was last called; this can be used to ...
virtual int64_t GetBaseNodeRestartPosition(int base_index)
Returns the index of the node to which the base node of index base_index must be set to when it reach...
int64_t BaseNode(int i) const
Returns the ith base node of the operator.
PathOperator(const std::vector< IntVar * > &next_vars, const std::vector< IntVar * > &path_vars, int number_of_base_nodes, bool skip_locally_optimal_paths, bool accept_path_end_base, std::function< int(int64_t)> start_empty_path_class)
Builds an instance of PathOperator from next and path variables.
int next_base_to_increment_
int GetSiblingAlternativeIndex(int node) const
Returns the index of the alternative set of the sibling of node.
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
int64_t Path(int64_t node) const
Returns the index of the path to which node belongs in the current delta.
virtual bool InitPosition() const
Returns true if the operator needs to restart its initial position at each call to Start()
const bool ignore_path_vars_
bool MakeChainInactive(int64_t before_chain, int64_t chain_end)
Makes the nodes on the chain starting after before_chain and ending at chain_end inactive.
const std::vector< int > & ChangedPaths() const
NodeRange Nodes(int path) const
ChainRange Chains(int path) const
int Start(int path) const
const std::vector< std::pair< int, int > > & ChangedArcs() const
PathState(int num_nodes, std::vector< int > path_start, std::vector< int > path_end)
Usual limit based on wall_time, number of explored branches and number of failures in the search tree...
bool Check() override
This method is called to check the status of the limit.
void Init() override
This method is called when the search limit is initialized.
void Copy(const SearchLimit *const limit) override
Copy a limit.
RegularLimit * MakeIdenticalClone() const
int64_t solutions() const
bool MakeNeighbor() override
~RelocateAndMakeActiveOperator() override
RelocateAndMakeActiveOperator(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
bool MakeNeighbor() override
~RelocateAndMakeInactiveOperator() override
RelocateAndMakeInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
Relocate(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, int64_t chain_length=1LL, bool single_path=false)
bool MakeNeighbor() override
Relocate(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, const std::string &name, std::function< int(int64_t)> start_empty_path_class, int64_t chain_length=1LL, bool single_path=false)
bool OnSamePathAsPreviousBase(int64_t base_index) override
Returns true if a base node has to be on the same path as the "previous" base node (base node of inde...
std::string DebugString() const override
virtual void Install()
Registers itself on the solver such that it gets notified of the search and propagation events.
This class is used to manage a pool of solutions.
virtual bool SyncNeeded(Assignment *const local_assignment)=0
This method checks if the local solution needs to be updated with an external one.
virtual void RegisterNewSolution(Assignment *const assignment)=0
This method is called when a new solution has been accepted by the local search.
virtual void GetNextSolution(Assignment *const assignment)=0
This method is called when the local search starts a new neighborhood to initialize the default assig...
virtual void Initialize(Assignment *const assignment)=0
This method is called to initialize the solution pool with the assignment from the local search.
RegularLimit * MakeSolutionsLimit(int64_t solutions)
Creates a search limit that constrains the number of solutions found during the search.
LocalSearchFilter * MakeVariableDomainFilter()
bool SolveAndCommit(DecisionBuilder *const db, const std::vector< SearchMonitor * > &monitors)
SolveAndCommit using a decision builder and up to three search monitors, usually one for the objectiv...
LocalSearchOperator * MakeMoveTowardTargetOperator(const Assignment &target)
Creates a local search operator that tries to move the assignment of some variables toward a target.
LocalSearchStatistics GetLocalSearchStatistics() const
Returns detailed local search statistics.
ConstraintSolverParameters parameters() const
Stored Parameters.
std::function< int64_t(int64_t, int64_t, int64_t)> IndexEvaluator3
void SetSearchContext(Search *search, const std::string &search_context)
void TopPeriodicCheck()
Performs PeriodicCheck on the top-level search; for instance, can be called from a nested solve to ch...
std::function< int64_t(int64_t, int64_t)> IndexEvaluator2
LocalSearchOperator * ConcatenateOperators(const std::vector< LocalSearchOperator * > &ops)
Creates a local search operator which concatenates a vector of operators.
LocalSearchFilter * MakeRejectFilter()
LocalSearchFilter * MakeAcceptFilter()
Local Search Filters.
LocalSearchOperator * MakeRandomLnsOperator(const std::vector< IntVar * > &vars, int number_of_variables)
Creates a large neighborhood search operator which creates fragments (set of relaxed variables) with ...
LocalSearchOperator * RandomConcatenateOperators(const std::vector< LocalSearchOperator * > &ops)
Randomized version of local search concatenator; calls a random operator at each call to MakeNextNeig...
LocalSearchOperators
This enum is used in Solver::MakeOperator to specify the neighborhood to create.
@ EXCHANGE
Operator which exchanges the positions of two nodes.
@ MAKEINACTIVE
Operator which makes path nodes inactive.
@ RELOCATE
Relocate neighborhood with length of 1 (see OROPT comment).
@ SWAPACTIVE
Operator which replaces an active node by an inactive one.
@ SIMPLELNS
Operator which defines one neighbor per variable.
@ INCREMENT
Operator which defines one neighbor per variable.
@ MAKECHAININACTIVE
Operator which makes a "chain" of path nodes inactive.
@ TWOOPT
Operator which reverses a sub-chain of a path.
@ FULLPATHLNS
Operator which relaxes one entire path and all inactive nodes, thus defining num_paths neighbors.
@ EXTENDEDSWAPACTIVE
Operator which makes an inactive node active and an active one inactive.
@ OROPT
Relocate: OROPT and RELOCATE.
@ PATHLNS
Operator which relaxes two sub-chains of three consecutive arcs each.
@ UNACTIVELNS
Operator which relaxes all inactive nodes and one sub-chain of six consecutive arcs.
@ MAKEACTIVE
Operator which inserts an inactive node into a path.
@ DECREMENT
Operator which defines a neighborhood to decrement values.
@ CROSS
Operator which cross exchanges the starting chains of 2 paths, including exchanging the whole paths.
LocalSearchPhaseParameters * MakeLocalSearchPhaseParameters(IntVar *objective, LocalSearchOperator *const ls_operator, DecisionBuilder *const sub_decision_builder)
Local Search Phase Parameters.
bool IsLocalSearchProfilingEnabled() const
Returns whether we are profiling local search.
IntVarLocalSearchFilter * MakeSumObjectiveFilter(const std::vector< IntVar * > &vars, IndexEvaluator2 values, Solver::LocalSearchFilterBound filter_enum)
Search * ActiveSearch() const
Returns the active search, nullptr outside search.
LocalSearchOperator * MakeNeighborhoodLimit(LocalSearchOperator *const op, int64_t limit)
Creates a local search operator that wraps another local search operator and limits the number of nei...
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
SolutionPool * MakeDefaultSolutionPool()
Solution Pool.
bool UseFastLocalSearch() const
Returns true if fast local search is enabled.
LocalSearchOperator * MakeOperator(const std::vector< IntVar * > &vars, LocalSearchOperators op)
Local Search Operators.
std::string LocalSearchProfile() const
Returns local search profiling information in a human readable format.
T * RevAlloc(T *object)
Registers the given object as being reversible.
Solver(const std::string &name)
Solver API.
DecisionBuilder * MakeLocalSearchPhase(Assignment *const assignment, LocalSearchPhaseParameters *const parameters)
Local Search decision builders factories.
LocalSearchOperator * MultiArmedBanditConcatenateOperators(const std::vector< LocalSearchOperator * > &ops, double memory_coefficient, double exploration_coefficient, bool maximize)
Creates a local search operator which concatenates a vector of operators.
Assignment * MakeAssignment()
This method creates an empty assignment.
DecisionBuilder * Compose(DecisionBuilder *const db1, DecisionBuilder *const db2)
Creates a decision builder which sequentially composes decision builders.
DecisionBuilder * MakeStoreAssignment(Assignment *assignment)
Returns a DecisionBuilder which stores an Assignment (calls void Assignment::Store())
DecisionBuilder * MakeRestoreAssignment(Assignment *assignment)
Returns a DecisionBuilder which restores an Assignment (calls void Assignment::Restore())
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
EvaluatorLocalSearchOperators
This enum is used in Solver::MakeOperator associated with an evaluator to specify the neighborhood to...
@ TSPOPT
Sliding TSP operator.
@ LK
Lin-Kernighan local search.
LocalSearchFilterBound
This enum is used in Solver::MakeLocalSearchObjectiveFilter.
@ GE
Move is accepted when the current objective value >= objective.Min.
@ LE
Move is accepted when the current objective value <= objective.Max.
@ EQ
Move is accepted when the current objective value is in the interval objective.Min .
~SwapActiveOperator() override
bool MakeNeighbor() override
SwapActiveOperator(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
bool MakeNeighbor() override
TSPLns(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int tsp_size)
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
std::string DebugString() const override
bool MakeNeighbor() override
TSPOpt(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int chain_length)
std::string DebugString() const override
int64_t GetBaseNodeRestartPosition(int base_index) override
Returns the index of the node to which the base node of index base_index must be set to when it reach...
bool MakeNeighbor() override
bool IsIncremental() const override
TwoOpt(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
Returns true if a base node has to be on the same path as the "previous" base node (base node of inde...
std::string DebugString() const override
UnaryDimensionChecker(const PathState *path_state, std::vector< Interval > path_capacity, std::vector< int > path_class, std::vector< std::vector< Interval >> demand, std::vector< Interval > node_capacity)
void RevertChanges(bool incremental)
void Deactivate(int64_t index)
void SetValue(int64_t index, const int64_t &value)
const int64_t & Value(int64_t index) const
Returns the value in the current assignment of the variable of given index.
std::vector< int64_t > prev_values_
bool ApplyChanges(Assignment *delta, Assignment *deltadelta) const
const int64_t & OldValue(int64_t index) const
IntVar * Var(int64_t index) const
Returns the variable of given index.
void AddVars(const std::vector< IntVar * > &vars)
SharedBoundsManager * bounds
int64_t *const synchronized_costs_
ABSL_FLAG(int, cp_local_search_sync_frequency, 16, "Frequency of checks for better solutions in the solution pool.")
Solver::LocalSearchFilterBound filter_enum_
int64_t synchronized_sum_
int64_t *const delta_costs_
const int primary_vars_size_
#define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass)
bool ContainsKey(const Collection &collection, const Key &key)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
ReverseView< Container > reversed_view(const Container &c)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
LocalSearchOperator * MakeLocalSearchOperator(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
Operator Factories.
void InstallLocalSearchProfiler(LocalSearchProfiler *monitor)
int64_t CapSub(int64_t x, int64_t y)
void DeleteLocalSearchProfiler(LocalSearchProfiler *monitor)
bool AcceptDelta(Search *const search, Assignment *delta, Assignment *deltadelta)
void AcceptNeighbor(Search *const search)
LocalSearchFilter * MakeUnaryDimensionFilter(Solver *solver, std::unique_ptr< UnaryDimensionChecker > checker)
int MostSignificantBitPosition32(uint32_t n)
LocalSearchFilter * MakePathStateFilter(Solver *solver, std::unique_ptr< PathState > path_state, const std::vector< IntVar * > &nexts)
bool LocalOptimumReached(Search *const search)
void AcceptUncheckedNeighbor(Search *const search)
LocalSearchProfiler * BuildLocalSearchProfiler(Solver *solver)
std::function< int64_t(int64_t, int64_t)> evaluator_