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.");
53ABSL_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);
134class 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_;
150bool SimpleLns::NextFragment() {
151 const int size = Size();
153 for (
int i = index_; i < index_ + number_of_variables_; ++i) {
154 AppendToFragment(i % size);
166class 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_;
184bool 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) {
311void ChangeValue::OnStart() { index_ = 0; }
316class 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"; }
330class 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,
349 number_of_nexts_(next_vars.size()),
350 ignore_path_vars_(path_vars.empty()),
351 next_base_to_increment_(iteration_parameters.number_of_base_nodes),
352 base_nodes_(iteration_parameters.number_of_base_nodes),
353 base_alternatives_(iteration_parameters.number_of_base_nodes),
354 base_sibling_alternatives_(iteration_parameters.number_of_base_nodes),
355 end_nodes_(iteration_parameters.number_of_base_nodes),
356 base_paths_(iteration_parameters.number_of_base_nodes),
357 just_started_(false),
359 iteration_parameters_(
std::move(iteration_parameters)),
360 optimal_paths_enabled_(false),
361 alternative_index_(next_vars.size(), -1) {
366 path_basis_.push_back(0);
367 for (
int i = 1; i < base_nodes_.size(); ++i) {
370 if ((path_basis_.size() > 2) ||
371 (!next_vars.empty() && !next_vars.back()
374 .skip_locally_optimal_paths())) {
381void PathOperator::OnStart() {
382 optimal_paths_enabled_ =
false;
383 InitializeBaseNodes();
384 InitializeAlternatives();
389 while (IncrementPosition()) {
413 int64_t destination) {
414 if (destination == before_chain || destination == chain_end)
return false;
417 const int64_t destination_path =
Path(destination);
418 const int64_t after_chain =
Next(chain_end);
419 SetNext(chain_end,
Next(destination), destination_path);
421 int current = destination;
423 while (current != chain_end) {
429 SetNext(destination,
Next(before_chain), destination_path);
431 SetNext(before_chain, after_chain,
Path(before_chain));
436 int64_t* chain_last) {
438 int64_t path =
Path(before_chain);
439 int64_t current =
Next(before_chain);
440 if (current == after_chain) {
443 int64_t current_next =
Next(current);
444 SetNext(current, after_chain, path);
445 while (current_next != after_chain) {
446 const int64_t
next =
Next(current_next);
447 SetNext(current_next, current, path);
448 current = current_next;
451 SetNext(before_chain, current, path);
452 *chain_last = current;
460 int64_t destination_path =
Path(destination);
461 SetNext(node,
Next(destination), destination_path);
462 SetNext(destination, node, destination_path);
469 const int64_t kNoPath = -1;
472 const int64_t after_chain =
Next(chain_end);
473 int64_t current =
Next(before_chain);
474 while (current != after_chain) {
476 SetNext(current, current, kNoPath);
479 SetNext(before_chain, after_chain,
Path(before_chain));
486 if (active == inactive)
return false;
487 const int64_t prev =
Prev(active);
491bool PathOperator::IncrementPosition() {
494 if (!just_started_) {
495 const int number_of_paths = path_starts_.size();
501 int last_restarted = base_node_size;
502 for (
int i = base_node_size - 1; i >= 0; --i) {
506 const int sibling_alternative_index =
508 if (sibling_alternative_index >= 0) {
509 if (base_sibling_alternatives_[i] <
510 alternative_sets_[sibling_alternative_index].size() - 1) {
511 ++base_sibling_alternatives_[i];
514 base_sibling_alternatives_[i] = 0;
517 const int alternative_index = alternative_index_[base_nodes_[i]];
518 if (alternative_index >= 0) {
519 if (base_alternatives_[i] <
520 alternative_sets_[alternative_index].size() - 1) {
521 ++base_alternatives_[i];
524 base_alternatives_[i] = 0;
525 base_sibling_alternatives_[i] = 0;
528 base_alternatives_[i] = 0;
529 base_sibling_alternatives_[i] = 0;
530 base_nodes_[i] =
OldNext(base_nodes_[i]);
535 base_alternatives_[i] = 0;
536 base_sibling_alternatives_[i] = 0;
550 for (
int i = last_restarted; i < base_node_size; ++i) {
551 base_alternatives_[i] = 0;
552 base_sibling_alternatives_[i] = 0;
555 if (last_restarted > 0) {
561 if (optimal_paths_enabled_ &&
563 if (path_basis_.size() > 1) {
564 for (
int i = 1; i < path_basis_.size(); ++i) {
574 std::vector<int> current_starts(base_node_size);
575 for (
int i = 0; i < base_node_size; ++i) {
580 optimal_paths_enabled_ =
true;
582 for (
int i = base_node_size - 1; i >= 0; --i) {
583 const int next_path_index = base_paths_[i] + 1;
584 if (next_path_index < number_of_paths) {
585 base_paths_[i] = next_path_index;
586 base_alternatives_[i] = 0;
587 base_sibling_alternatives_[i] = 0;
588 base_nodes_[i] = path_starts_[next_path_index];
594 base_alternatives_[i] = 0;
595 base_sibling_alternatives_[i] = 0;
596 base_nodes_[i] = path_starts_[0];
602 if (path_basis_.size() > 1) {
603 for (
int j = 1; j < path_basis_.size(); ++j) {
605 path_basis_[j - 1])] +
619 if (!CheckEnds())
return false;
621 for (
int i = 0; i < base_node_size; ++i) {
627 if (stop)
return false;
630 just_started_ =
false;
636void PathOperator::InitializePathStarts() {
644 has_prevs[
next] =
true;
649 if (optimal_paths_.empty() &&
682 std::vector<int64_t> new_path_starts;
683 const bool use_empty_path_symmetry_breaker =
684 absl::GetFlag(FLAGS_cp_use_empty_path_symmetry_breaker);
694 new_path_starts.push_back(i);
702 std::vector<int> node_paths(max_next + 1, -1);
703 for (
int i = 0; i < path_starts_.size(); ++i) {
704 int node = path_starts_[i];
706 node_paths[node] = i;
709 node_paths[node] = i;
713 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]);
741 if (base_paths_[j] == i && !found_bases.contains(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);
756void PathOperator::InitializeInactives() {
759 inactives_.push_back(
OldNext(i) == i);
763void PathOperator::InitializeBaseNodes() {
765 InitializeInactives();
766 InitializePathStarts();
772 base_nodes_[i] = path_starts_[0];
774 first_start_ =
false;
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;
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];
797 base_alternatives_[i] = 0;
798 base_sibling_alternatives_[i] = 0;
800 just_started_ =
true;
803void 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;
817bool 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;
1102void 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);
1535 if (breaks_set.contains(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];
1634void 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),
1695void 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);
1755const int LinKernighan::kNeighbors = 5 + 1;
1757bool 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();
1832void PathLns::DeactivateChain(int64_t node) {
1833 for (
int i = 0, current = node;
1834 (ChainsAreFullPaths() || i < chunk_size_) && !
IsPathEnd(current);
1835 ++i, current =
Next(current)) {
1843void 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_;
1948CompoundOperator::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;
1968void CompoundOperator::Reset() {
1969 for (LocalSearchOperator*
const op : operators_) {
1974void 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);
1984bool 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);
2011int64_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;
2017int64_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_;
2066void RandomCompoundOperator::Start(
const Assignment* assignment) {
2067 for (LocalSearchOperator*
const op : operators_) {
2068 op->Start(assignment);
2072RandomCompoundOperator::RandomCompoundOperator(
2073 std::vector<LocalSearchOperator*> operators)
2076RandomCompoundOperator::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;
2087void RandomCompoundOperator::Reset() {
2088 for (LocalSearchOperator*
const op : operators_) {
2093bool 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_;
2169MultiArmedBanditCompoundOperator::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;
2199void MultiArmedBanditCompoundOperator::Reset() {
2200 for (LocalSearchOperator*
const op : operators_) {
2205double 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]));
2212void 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);
2247bool 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;
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; }
2486class 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; }
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_;
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_;
2532class 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());
2618void 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);
2664void 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();
2779void 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;
2798void 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;
2824void 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});
2855class 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_;
2880PathStateFilter::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;
2901void 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();
2921void 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();
2945void 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();
2956void 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)),
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();
3078void 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());
3086void 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());
3099void 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);
3128void 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);
3155UnaryDimensionChecker::Interval
3156UnaryDimensionChecker::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];
3173bool 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];
3183class UnaryDimensionFilter :
public LocalSearchFilter {
3185 std::string DebugString()
const override {
return name_; }
3186 UnaryDimensionFilter(std::unique_ptr<UnaryDimensionChecker> checker,
3187 const std::string& dimension_name)
3188 : checker_(
std::move(checker)),
3189 name_(
absl::StrCat(
"UnaryDimensionFilter(", dimension_name,
")")) {}
3191 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3192 int64_t objective_min, int64_t objective_max)
override {
3193 return checker_->Check();
3196 void Synchronize(
const Assignment* assignment,
3197 const Assignment*
delta)
override {
3202 std::unique_ptr<UnaryDimensionChecker> checker_;
3203 const std::string name_;
3209 Solver* solver, std::unique_ptr<UnaryDimensionChecker> checker,
3210 const std::string& dimension_name) {
3211 UnaryDimensionFilter* filter =
3212 new UnaryDimensionFilter(std::move(checker), dimension_name);
3220class VariableDomainFilter :
public LocalSearchFilter {
3222 VariableDomainFilter() {}
3223 ~VariableDomainFilter()
override {}
3224 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3225 int64_t objective_min, int64_t objective_max)
override;
3226 void Synchronize(
const Assignment* assignment,
3227 const Assignment*
delta)
override {}
3229 std::string DebugString()
const override {
return "VariableDomainFilter"; }
3232bool VariableDomainFilter::Accept(
const Assignment*
delta,
3233 const Assignment* deltadelta,
3234 int64_t objective_min,
3235 int64_t objective_max) {
3237 const int size = container.Size();
3238 for (
int i = 0; i < size; ++i) {
3239 const IntVarElement& element = container.Element(i);
3240 if (element.Activated() && !element.Var()->Contains(element.Value())) {
3249 return RevAlloc(
new VariableDomainFilter());
3254const int IntVarLocalSearchFilter::kUnassigned = -1;
3257 const std::vector<IntVar*>& vars) {
3262 if (!vars.empty()) {
3263 for (
int i = 0; i < vars.size(); ++i) {
3264 const int index = vars[i]->index();
3265 if (
index >= var_index_to_index_.size()) {
3266 var_index_to_index_.resize(
index + 1, kUnassigned);
3268 var_index_to_index_[
index] = i + vars_.size();
3270 vars_.insert(vars_.end(), vars.begin(), vars.end());
3271 values_.resize(vars_.size(), 0);
3272 var_synced_.resize(vars_.size(),
false);
3281 var_synced_.assign(var_synced_.size(),
false);
3292 const int size = container.
Size();
3293 for (
int i = 0; i < size; ++i) {
3296 if (
var !=
nullptr) {
3297 if (i < vars_.size() && vars_[i] ==
var) {
3298 values_[i] = element.
Value();
3299 var_synced_[i] =
true;
3301 const int64_t kUnallocated = -1;
3302 int64_t
index = kUnallocated;
3305 var_synced_[
index] =
true;
3324 SumObjectiveFilter(
const std::vector<IntVar*>& vars,
3334 for (
int i = 0; i < vars.size(); ++i) {
3339 ~SumObjectiveFilter()
override {
3343 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3344 int64_t objective_min, int64_t objective_max)
override {
3345 if (
delta ==
nullptr) {
3348 if (deltadelta->Empty()) {
3379 LOG(ERROR) <<
"Unknown local search filter enum value";
3386 virtual int64_t CostOfSynchronizedVariable(int64_t
index) = 0;
3390 virtual bool FillCostOfBoundDeltaVariable(
3392 int* container_index, int64_t* new_cost) = 0;
3393 bool IsIncremental()
const override {
return true; }
3395 std::string DebugString()
const override {
return "SumObjectiveFilter"; }
3397 int64_t GetSynchronizedObjectiveValue()
const override {
3400 int64_t GetAcceptedObjectiveValue()
const override {
return delta_sum_; }
3412 void OnSynchronize(
const Assignment*
delta)
override {
3415 const int64_t
cost = CostOfSynchronizedVariable(i);
3423 int64_t CostOfChanges(
const Assignment* changes,
3424 const int64_t*
const old_costs,
3425 bool cache_delta_values) {
3426 int64_t total_cost = 0;
3428 const int size = container.
Size();
3429 for (
int i = 0; i < size; ++i) {
3430 const IntVarElement& new_element = container.Element(i);
3431 IntVar*
const var = new_element.Var();
3434 total_cost =
CapSub(total_cost, old_costs[
index]);
3435 int64_t new_cost = 0LL;
3436 if (FillCostOfBoundDeltaVariable(container,
index, &i, &new_cost)) {
3437 total_cost =
CapAdd(total_cost, new_cost);
3439 if (cache_delta_values) {
3448class BinaryObjectiveFilter :
public SumObjectiveFilter {
3450 BinaryObjectiveFilter(
const std::vector<IntVar*>& vars,
3453 : SumObjectiveFilter(vars, filter_enum),
3454 value_evaluator_(
std::move(value_evaluator)) {}
3455 ~BinaryObjectiveFilter()
override {}
3456 int64_t CostOfSynchronizedVariable(int64_t
index)
override {
3462 int index,
int* container_index,
3463 int64_t* new_cost)
override {
3464 const IntVarElement& element = container.Element(*container_index);
3465 if (element.Activated()) {
3466 *new_cost = value_evaluator_(
index, element.Value());
3469 const IntVar*
var = element.Var();
3471 *new_cost = value_evaluator_(
index,
var->Min());
3482class TernaryObjectiveFilter :
public SumObjectiveFilter {
3484 TernaryObjectiveFilter(
const std::vector<IntVar*>& vars,
3485 const std::vector<IntVar*>& secondary_vars,
3488 : SumObjectiveFilter(vars, filter_enum),
3489 secondary_vars_offset_(vars.size()),
3490 value_evaluator_(
std::move(value_evaluator)) {
3494 ~TernaryObjectiveFilter()
override {}
3495 int64_t CostOfSynchronizedVariable(int64_t
index)
override {
3500 index + secondary_vars_offset_))
3504 int index,
int* container_index,
3505 int64_t* new_cost)
override {
3508 const IntVarElement& element = container.Element(*container_index);
3509 const IntVar* secondary_var =
3511 if (element.Activated()) {
3512 const int64_t
value = element.Value();
3513 int hint_index = *container_index + 1;
3514 if (hint_index < container.Size() &&
3515 secondary_var == container.Element(hint_index).Var()) {
3517 container.Element(hint_index).Value());
3518 *container_index = hint_index;
3521 container.Element(secondary_var).Value());
3525 const IntVar*
var = element.Var();
3526 if (
var->Bound() && secondary_var->Bound()) {
3527 *new_cost = value_evaluator_(
index,
var->Min(), secondary_var->Min());
3535 int secondary_vars_offset_;
3544 new BinaryObjectiveFilter(vars, std::move(values), filter_enum));
3548 const std::vector<IntVar*>& vars,
3551 return RevAlloc(
new TernaryObjectiveFilter(vars, secondary_vars,
3552 std::move(values), filter_enum));
3556 int64_t initial_max) {
3559 initial_variable_bounds_.push_back({initial_min, initial_max});
3560 variable_bounds_.push_back({initial_min, initial_max});
3561 variable_is_relaxed_.push_back(
false);
3563 const int variable_index = variable_bounds_.size() - 1;
3564 return {
this, variable_index};
3567void LocalSearchState::RelaxVariableBounds(
int variable_index) {
3569 DCHECK(0 <= variable_index && variable_index < variable_is_relaxed_.size());
3570 if (!variable_is_relaxed_[variable_index]) {
3571 variable_is_relaxed_[variable_index] =
true;
3572 saved_variable_bounds_trail_.emplace_back(variable_bounds_[variable_index],
3574 variable_bounds_[variable_index] = initial_variable_bounds_[variable_index];
3578int64_t LocalSearchState::VariableMin(
int variable_index)
const {
3580 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3581 return variable_bounds_[variable_index].min;
3584int64_t LocalSearchState::VariableMax(
int variable_index)
const {
3586 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3587 return variable_bounds_[variable_index].max;
3590bool LocalSearchState::TightenVariableMin(
int variable_index,
3591 int64_t min_value) {
3593 DCHECK(variable_is_relaxed_[variable_index]);
3594 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3595 Bounds&
bounds = variable_bounds_[variable_index];
3596 if (
bounds.max < min_value) {
3597 state_is_valid_ =
false;
3600 return state_is_valid_;
3603bool LocalSearchState::TightenVariableMax(
int variable_index,
3604 int64_t max_value) {
3606 DCHECK(variable_is_relaxed_[variable_index]);
3607 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3608 Bounds&
bounds = variable_bounds_[variable_index];
3609 if (
bounds.min > max_value) {
3610 state_is_valid_ =
false;
3613 return state_is_valid_;
3621 saved_variable_bounds_trail_.clear();
3622 variable_is_relaxed_.assign(variable_is_relaxed_.size(),
false);
3626 for (
const auto& bounds_index : saved_variable_bounds_trail_) {
3627 DCHECK(variable_is_relaxed_[bounds_index.second]);
3628 variable_bounds_[bounds_index.second] = bounds_index.first;
3630 saved_variable_bounds_trail_.clear();
3631 variable_is_relaxed_.assign(variable_is_relaxed_.size(),
false);
3632 state_is_valid_ =
true;
3640 std::string
DebugString()
const override {
return "LocalSearchProfiler"; }
3642 operator_stats_.clear();
3643 filter_stats_.clear();
3647 if (
solver()->TopLevelSearch() ==
solver()->ActiveSearch()) {
3654 if (db->seconds() == 0)
continue;
3656 first_solution_statistics =
3661 std::vector<const LocalSearchOperator*> operators;
3662 for (
const auto& stat : operator_stats_) {
3663 operators.push_back(stat.first);
3666 operators.begin(), operators.end(),
3668 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3669 gtl::FindOrDie(operator_stats_, op2).neighbors;
3672 const OperatorStats& stats =
gtl::FindOrDie(operator_stats_, op);
3674 local_search_operator_statistics =
3680 stats.filtered_neighbors);
3682 stats.accepted_neighbors);
3685 std::vector<const LocalSearchFilter*> filters;
3686 for (
const auto& stat : filter_stats_) {
3687 filters.push_back(stat.first);
3689 std::sort(filters.begin(), filters.end(),
3692 return gtl::FindOrDie(filter_stats_, filter1).calls >
3693 gtl::FindOrDie(filter_stats_, filter2).calls;
3696 const FilterStats& stats =
gtl::FindOrDie(filter_stats_, filter);
3698 local_search_filter_statistics =
3701 filter->DebugString());
3708 solver()->filtered_neighbors());
3710 solver()->accepted_neighbors());
3711 return statistics_proto;
3714 size_t max_name_size = 0;
3715 std::vector<const LocalSearchOperator*> operators;
3716 for (
const auto& stat : operator_stats_) {
3717 operators.push_back(stat.first);
3719 std::max(max_name_size, stat.first->DebugString().length());
3722 operators.begin(), operators.end(),
3724 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3725 gtl::FindOrDie(operator_stats_, op2).neighbors;
3727 std::string overview =
"Local search operator statistics:\n";
3728 absl::StrAppendFormat(&overview,
3729 "%*s | Neighbors | Filtered | Accepted | Time (s)\n",
3731 OperatorStats total_stats;
3733 const OperatorStats& stats =
gtl::FindOrDie(operator_stats_, op);
3734 const std::string&
name = op->DebugString();
3735 absl::StrAppendFormat(&overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n",
3736 max_name_size,
name, stats.neighbors,
3737 stats.filtered_neighbors, stats.accepted_neighbors,
3739 total_stats.neighbors += stats.neighbors;
3740 total_stats.filtered_neighbors += stats.filtered_neighbors;
3741 total_stats.accepted_neighbors += stats.accepted_neighbors;
3742 total_stats.seconds += stats.seconds;
3744 absl::StrAppendFormat(&overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n",
3745 max_name_size,
"Total", total_stats.neighbors,
3746 total_stats.filtered_neighbors,
3747 total_stats.accepted_neighbors, total_stats.seconds);
3749 std::vector<const LocalSearchFilter*> filters;
3750 for (
const auto& stat : filter_stats_) {
3751 filters.push_back(stat.first);
3753 std::max(max_name_size, stat.first->DebugString().length());
3755 std::sort(filters.begin(), filters.end(),
3758 return gtl::FindOrDie(filter_stats_, filter1).calls >
3759 gtl::FindOrDie(filter_stats_, filter2).calls;
3761 absl::StrAppendFormat(&overview,
3762 "Local search filter statistics:\n%*s | Calls | "
3763 " Rejects | Time (s) "
3766 FilterStats total_filter_stats;
3768 const FilterStats& stats =
gtl::FindOrDie(filter_stats_, filter);
3769 const std::string&
name = filter->DebugString();
3770 absl::StrAppendFormat(&overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n",
3771 max_name_size,
name, stats.calls, stats.rejects,
3772 stats.seconds, stats.rejects / stats.seconds);
3773 total_filter_stats.calls += stats.calls;
3774 total_filter_stats.rejects += stats.rejects;
3775 total_filter_stats.seconds += stats.seconds;
3777 absl::StrAppendFormat(
3778 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n", max_name_size,
3779 "Total", total_filter_stats.calls, total_filter_stats.rejects,
3780 total_filter_stats.seconds,
3781 total_filter_stats.rejects / total_filter_stats.seconds);
3787 if (last_operator_ != op->
Self()) {
3789 last_operator_ = op->
Self();
3795 if (neighbor_found) {
3796 operator_stats_[op->
Self()].neighbors++;
3801 bool neighbor_found)
override {
3802 if (neighbor_found) {
3803 operator_stats_[op->
Self()].filtered_neighbors++;
3808 bool neighbor_found)
override {
3809 if (neighbor_found) {
3810 operator_stats_[op->
Self()].accepted_neighbors++;
3814 filter_stats_[filter].calls++;
3815 filter_timer_.
Start();
3818 filter_timer_.
Stop();
3819 auto& stats = filter_stats_[filter];
3820 stats.seconds += filter_timer_.
Get();
3827 profiled_decision_builders_.push_back(profiled_db);
3833 if (last_operator_ !=
nullptr) {
3835 operator_stats_[last_operator_].seconds += timer_.
Get();
3840 struct OperatorStats {
3841 int64_t neighbors = 0;
3842 int64_t filtered_neighbors = 0;
3843 int64_t accepted_neighbors = 0;
3847 struct FilterStats {
3849 int64_t rejects = 0;
3854 const LocalSearchOperator* last_operator_ =
nullptr;
3855 absl::flat_hash_map<const LocalSearchOperator*, OperatorStats>
3857 absl::flat_hash_map<const LocalSearchFilter*, FilterStats> filter_stats_;
3859 std::vector<ProfiledDecisionBuilder*> profiled_decision_builders_;
3888 if (local_search_profiler_ !=
nullptr) {
3895 if (local_search_profiler_ !=
nullptr) {
3901void LocalSearchFilterManager::InitializeForcedEvents() {
3902 const int num_events = filter_events_.size();
3903 int next_forced_event = num_events;
3904 next_forced_events_.resize(num_events);
3905 for (
int i = num_events - 1; i >= 0; --i) {
3906 next_forced_events_[i] = next_forced_event;
3907 if (filter_events_[i].filter->IsIncremental() ||
3908 (filter_events_[i].event_type == FilterEventType::kRelax &&
3909 next_forced_event != num_events)) {
3910 next_forced_event = i;
3916 std::vector<LocalSearchFilter*> filters)
3917 : synchronized_value_(
std::numeric_limits<int64_t>::
min()),
3918 accepted_value_(
std::numeric_limits<int64_t>::
min()) {
3919 filter_events_.reserve(2 * filters.size());
3921 filter_events_.push_back({filter, FilterEventType::kRelax});
3924 filter_events_.push_back({filter, FilterEventType::kAccept});
3926 InitializeForcedEvents();
3930 std::vector<FilterEvent> filter_events)
3931 : filter_events_(
std::move(filter_events)),
3932 synchronized_value_(
std::numeric_limits<int64_t>::
min()),
3933 accepted_value_(
std::numeric_limits<int64_t>::
min()) {
3934 InitializeForcedEvents();
3940 for (
int i = last_event_called_; i >= 0; --i) {
3941 auto [filter, event_type] = filter_events_[i];
3942 if (event_type == FilterEventType::kRelax) filter->Revert();
3944 last_event_called_ = -1;
3953 int64_t objective_min,
3954 int64_t objective_max) {
3956 accepted_value_ = 0;
3958 const int num_events = filter_events_.size();
3959 for (
int i = 0; i < num_events;) {
3960 last_event_called_ = i;
3961 auto [filter, event_type] = filter_events_[last_event_called_];
3962 switch (event_type) {
3963 case FilterEventType::kAccept: {
3965 const bool accept = filter->Accept(
3966 delta, deltadelta,
CapSub(objective_min, accepted_value_),
3967 CapSub(objective_max, accepted_value_));
3969 if (monitor !=
nullptr) monitor->
EndFiltering(filter, !accept);
3972 CapAdd(accepted_value_, filter->GetAcceptedObjectiveValue());
3974 ok = accepted_value_ <= objective_max;
3978 case FilterEventType::kRelax: {
3979 filter->Relax(
delta, deltadelta);
3983 LOG(
FATAL) <<
"Unknown filter event type.";
3989 i = next_forced_events_[i];
4000 const bool reset_to_assignment =
delta ==
nullptr ||
delta->Empty();
4002 for (
auto [filter, event_type] : filter_events_) {
4003 switch (event_type) {
4004 case FilterEventType::kAccept: {
4007 case FilterEventType::kRelax: {
4008 if (reset_to_assignment) {
4010 filter->Relax(assignment,
nullptr);
4012 filter->Relax(
delta,
nullptr);
4017 LOG(
FATAL) <<
"Unknown filter event type.";
4022 synchronized_value_ = 0;
4024 switch (event_type) {
4025 case FilterEventType::kAccept: {
4026 filter->Synchronize(assignment,
delta);
4027 synchronized_value_ =
CapAdd(synchronized_value_,
4028 filter->GetSynchronizedObjectiveValue());
4031 case FilterEventType::kRelax: {
4032 filter->Commit(assignment,
delta);
4036 LOG(
FATAL) <<
"Unknown filter event type.";
4053 std::string
DebugString()
const override {
return "FindOneNeighbor"; }
4057 int64_t objective_min, int64_t objective_max);
4058 void SynchronizeAll(
Solver* solver);
4061 IntVar*
const objective_;
4062 std::unique_ptr<Assignment> reference_assignment_;
4063 std::unique_ptr<Assignment> last_synchronized_assignment_;
4070 bool neighbor_found_;
4072 int64_t solutions_since_last_check_;
4073 int64_t check_period_;
4075 bool has_checked_assignment_ =
false;
4090 : assignment_(assignment),
4092 reference_assignment_(new
Assignment(assignment_)),
4093 filter_assignment_delta_(assignment->solver()->MakeAssignment()),
4095 ls_operator_(ls_operator),
4096 sub_decision_builder_(sub_decision_builder),
4098 original_limit_(limit),
4099 neighbor_found_(false),
4100 filter_manager_(filter_manager),
4101 solutions_since_last_check_(0),
4103 assignment_->solver()->
parameters().check_solution_period()),
4104 last_checked_assignment_(assignment) {
4105 CHECK(
nullptr != assignment);
4106 CHECK(
nullptr != ls_operator);
4110 if (
nullptr == limit) {
4118 VLOG(1) <<
"Disabling neighbor-check skipping outside of first accept.";
4125 VLOG(1) <<
"Disabling neighbor-check skipping for LNS.";
4129 if (!reference_assignment_->HasObjective()) {
4130 reference_assignment_->AddObjective(objective_);
4135 CHECK(
nullptr != solver);
4137 if (original_limit_ !=
nullptr) {
4138 limit_->
Copy(original_limit_);
4145 if (!neighbor_found_) {
4153 SynchronizeAll(solver);
4163 if (sub_decision_builder_) {
4164 restore = solver->
Compose(restore, sub_decision_builder_);
4172 delta->ClearObjective();
4173 deltadelta->
Clear();
4175 if (++counter >= absl::GetFlag(FLAGS_cp_local_search_sync_frequency) &&
4176 pool_->
SyncNeeded(reference_assignment_.get())) {
4179 SynchronizeAll(solver);
4182 bool has_neighbor =
false;
4183 if (!limit_->
Check()) {
4187 ls_operator_, has_neighbor,
delta, deltadelta);
4190 if (has_neighbor && !solver->IsUncheckedSolutionLimitReached()) {
4191 solver->neighbors_ += 1;
4198 const bool mh_filter =
4203 objective_min = objective_->
Min();
4204 objective_max = objective_->
Max();
4206 if (
delta->HasObjective() &&
delta->Objective() == objective_) {
4207 objective_min =
std::max(objective_min,
delta->ObjectiveMin());
4208 objective_max =
std::min(objective_max,
delta->ObjectiveMax());
4210 const bool move_filter = FilterAccept(solver,
delta, deltadelta,
4211 objective_min, objective_max);
4213 ls_operator_, mh_filter && move_filter);
4214 if (!mh_filter || !move_filter) {
4215 if (filter_manager_ !=
nullptr) filter_manager_->
Revert();
4218 solver->filtered_neighbors_ += 1;
4219 if (
delta->HasObjective()) {
4231 const bool check_solution = (solutions_since_last_check_ == 0) ||
4234 !
delta->AreAllElementsBound();
4235 if (has_checked_assignment_) solutions_since_last_check_++;
4236 if (solutions_since_last_check_ >= check_period_) {
4237 solutions_since_last_check_ = 0;
4239 const bool accept = !check_solution || solver->
SolveAndCommit(restore);
4243 solver->accepted_neighbors_ += 1;
4244 if (check_solution) {
4247 assignment_->
Store();
4249 neighbor_found_ =
true;
4250 has_checked_assignment_ =
true;
4264 solver->IncrementUncheckedSolutionCounter();
4266 SynchronizeAll(solver);
4269 neighbor_found_ =
true;
4271 if (filter_manager_ !=
nullptr) filter_manager_->
Revert();
4272 if (check_period_ > 1 && has_checked_assignment_) {
4278 VLOG(1) <<
"Imperfect filtering detected, backtracking to last "
4279 "checked solution and checking all solutions.";
4281 solutions_since_last_check_ = 0;
4283 SynchronizeAll(solver);
4288 if (neighbor_found_) {
4300 has_checked_assignment_ =
true;
4308 SynchronizeAll(solver);
4321 int64_t objective_min,
4322 int64_t objective_max) {
4323 if (filter_manager_ ==
nullptr)
return true;
4325 return filter_manager_->
Accept(monitor,
delta, deltadelta, objective_min,
4331template <
typename Container>
4332void AddDeltaElements(
const Container& old_container,
4333 const Container& new_container, Assignment*
delta) {
4334 for (
const auto& new_element : new_container.elements()) {
4335 const auto var = new_element.
Var();
4336 const auto old_element_ptr = old_container.ElementPtrOrNull(
var);
4337 if (old_element_ptr ==
nullptr || *old_element_ptr != new_element) {
4338 delta->FastAdd(
var)->Copy(new_element);
4343void MakeDelta(
const Assignment* old_assignment,
4344 const Assignment* new_assignment, Assignment*
delta) {
4347 AddDeltaElements(old_assignment->IntVarContainer(),
4348 new_assignment->IntVarContainer(),
delta);
4349 AddDeltaElements(old_assignment->IntervalVarContainer(),
4350 new_assignment->IntervalVarContainer(),
delta);
4351 AddDeltaElements(old_assignment->SequenceVarContainer(),
4352 new_assignment->SequenceVarContainer(),
delta);
4356void FindOneNeighbor::SynchronizeAll(Solver* solver) {
4357 Assignment*
const reference_assignment = reference_assignment_.get();
4359 neighbor_found_ =
false;
4361 solver->GetLocalSearchMonitor()->BeginOperatorStart();
4362 ls_operator_->
Start(reference_assignment);
4363 if (filter_manager_ !=
nullptr) {
4364 Assignment*
delta =
nullptr;
4365 if (last_synchronized_assignment_ ==
nullptr) {
4366 last_synchronized_assignment_ =
4367 absl::make_unique<Assignment>(reference_assignment);
4369 MakeDelta(last_synchronized_assignment_.get(), reference_assignment,
4370 filter_assignment_delta_);
4371 delta = filter_assignment_delta_;
4372 last_synchronized_assignment_->Copy(reference_assignment);
4376 solver->GetLocalSearchMonitor()->EndOperatorStart();
4389 solution_pool_(pool),
4396 return "LocalSearchPhaseParameters";
4403 return sub_decision_builder_;
4407 return filter_manager_;
4411 IntVar*
const objective_;
4423 ls_operator, sub_decision_builder,
4431 ls_operator, sub_decision_builder,
4440 ls_operator, sub_decision_builder,
4441 limit, filter_manager);
4449 sub_decision_builder,
nullptr,
nullptr);
4457 sub_decision_builder, limit,
nullptr);
4466 sub_decision_builder, limit,
4480class NestedSolveDecision :
public Decision {
4483 enum StateType { DECISION_PENDING, DECISION_FAILED, DECISION_FOUND };
4485 NestedSolveDecision(DecisionBuilder*
const db,
bool restore,
4486 const std::vector<SearchMonitor*>& monitors);
4487 NestedSolveDecision(DecisionBuilder*
const db,
bool restore);
4488 ~NestedSolveDecision()
override {}
4489 void Apply(Solver*
const solver)
override;
4490 void Refute(Solver*
const solver)
override;
4491 std::string DebugString()
const override {
return "NestedSolveDecision"; }
4492 int state()
const {
return state_; }
4495 DecisionBuilder*
const db_;
4497 std::vector<SearchMonitor*> monitors_;
4501NestedSolveDecision::NestedSolveDecision(
4502 DecisionBuilder*
const db,
bool restore,
4503 const std::vector<SearchMonitor*>& monitors)
4506 monitors_(monitors),
4507 state_(DECISION_PENDING) {
4508 CHECK(
nullptr != db);
4511NestedSolveDecision::NestedSolveDecision(DecisionBuilder*
const db,
4513 : db_(db), restore_(restore), state_(DECISION_PENDING) {
4514 CHECK(
nullptr != db);
4517void NestedSolveDecision::Apply(Solver*
const solver) {
4518 CHECK(
nullptr != solver);
4520 if (solver->Solve(db_, monitors_)) {
4521 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FOUND));
4523 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FAILED));
4526 if (solver->SolveAndCommit(db_, monitors_)) {
4527 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FOUND));
4529 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FAILED));
4534void NestedSolveDecision::Refute(Solver*
const solver) {}
4545class LocalSearch :
public DecisionBuilder {
4547 LocalSearch(Assignment*
const assignment, IntVar* objective,
4548 SolutionPool*
const pool, LocalSearchOperator*
const ls_operator,
4549 DecisionBuilder*
const sub_decision_builder,
4550 RegularLimit*
const limit,
4551 LocalSearchFilterManager* filter_manager);
4554 LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4555 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4556 LocalSearchOperator*
const ls_operator,
4557 DecisionBuilder*
const sub_decision_builder,
4558 RegularLimit*
const limit,
4559 LocalSearchFilterManager* filter_manager);
4560 LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4561 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4562 DecisionBuilder*
const first_solution_sub_decision_builder,
4563 LocalSearchOperator*
const ls_operator,
4564 DecisionBuilder*
const sub_decision_builder,
4565 RegularLimit*
const limit,
4566 LocalSearchFilterManager* filter_manager);
4567 LocalSearch(
const std::vector<SequenceVar*>& vars, IntVar* objective,
4568 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4569 LocalSearchOperator*
const ls_operator,
4570 DecisionBuilder*
const sub_decision_builder,
4571 RegularLimit*
const limit,
4572 LocalSearchFilterManager* filter_manager);
4573 ~LocalSearch()
override;
4574 Decision* Next(Solver*
const solver)
override;
4575 std::string DebugString()
const override {
return "LocalSearch"; }
4576 void Accept(ModelVisitor*
const visitor)
const override;
4579 void PushFirstSolutionDecision(DecisionBuilder* first_solution);
4580 void PushLocalSearchDecision();
4583 Assignment* assignment_;
4585 SolutionPool*
const pool_;
4586 LocalSearchOperator*
const ls_operator_;
4587 DecisionBuilder*
const first_solution_sub_decision_builder_;
4588 DecisionBuilder*
const sub_decision_builder_;
4589 std::vector<NestedSolveDecision*> nested_decisions_;
4590 int nested_decision_index_;
4591 RegularLimit*
const limit_;
4592 LocalSearchFilterManager*
const filter_manager_;
4596LocalSearch::LocalSearch(Assignment*
const assignment, IntVar* objective,
4597 SolutionPool*
const pool,
4598 LocalSearchOperator*
const ls_operator,
4599 DecisionBuilder*
const sub_decision_builder,
4600 RegularLimit*
const limit,
4601 LocalSearchFilterManager* filter_manager)
4602 : assignment_(nullptr),
4605 ls_operator_(ls_operator),
4606 first_solution_sub_decision_builder_(sub_decision_builder),
4607 sub_decision_builder_(sub_decision_builder),
4608 nested_decision_index_(0),
4610 filter_manager_(filter_manager),
4611 has_started_(false) {
4612 CHECK(
nullptr != assignment);
4613 CHECK(
nullptr != ls_operator);
4614 Solver*
const solver = assignment->solver();
4615 assignment_ = solver->GetOrCreateLocalSearchState();
4616 assignment_->Copy(assignment);
4617 DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment);
4618 PushFirstSolutionDecision(restore);
4619 PushLocalSearchDecision();
4622LocalSearch::LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4623 SolutionPool*
const pool,
4624 DecisionBuilder*
const first_solution,
4625 LocalSearchOperator*
const ls_operator,
4626 DecisionBuilder*
const sub_decision_builder,
4627 RegularLimit*
const limit,
4628 LocalSearchFilterManager* filter_manager)
4629 : assignment_(nullptr),
4632 ls_operator_(ls_operator),
4633 first_solution_sub_decision_builder_(sub_decision_builder),
4634 sub_decision_builder_(sub_decision_builder),
4635 nested_decision_index_(0),
4637 filter_manager_(filter_manager),
4638 has_started_(false) {
4639 CHECK(
nullptr != first_solution);
4640 CHECK(
nullptr != ls_operator);
4641 CHECK(!vars.empty());
4642 Solver*
const solver = vars[0]->solver();
4643 assignment_ = solver->GetOrCreateLocalSearchState();
4644 assignment_->Add(vars);
4645 PushFirstSolutionDecision(first_solution);
4646 PushLocalSearchDecision();
4649LocalSearch::LocalSearch(
4650 const std::vector<IntVar*>& vars, IntVar* objective,
4651 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4652 DecisionBuilder*
const first_solution_sub_decision_builder,
4653 LocalSearchOperator*
const ls_operator,
4654 DecisionBuilder*
const sub_decision_builder, RegularLimit*
const limit,
4655 LocalSearchFilterManager* filter_manager)
4656 : assignment_(nullptr),
4659 ls_operator_(ls_operator),
4660 first_solution_sub_decision_builder_(first_solution_sub_decision_builder),
4661 sub_decision_builder_(sub_decision_builder),
4662 nested_decision_index_(0),
4664 filter_manager_(filter_manager),
4665 has_started_(false) {
4666 CHECK(
nullptr != first_solution);
4667 CHECK(
nullptr != ls_operator);
4668 CHECK(!vars.empty());
4669 Solver*
const solver = vars[0]->solver();
4670 assignment_ = solver->GetOrCreateLocalSearchState();
4671 assignment_->Add(vars);
4672 PushFirstSolutionDecision(first_solution);
4673 PushLocalSearchDecision();
4676LocalSearch::LocalSearch(
const std::vector<SequenceVar*>& vars,
4677 IntVar* objective, SolutionPool*
const pool,
4678 DecisionBuilder*
const first_solution,
4679 LocalSearchOperator*
const ls_operator,
4680 DecisionBuilder*
const sub_decision_builder,
4681 RegularLimit*
const limit,
4682 LocalSearchFilterManager* filter_manager)
4683 : assignment_(nullptr),
4686 ls_operator_(ls_operator),
4687 first_solution_sub_decision_builder_(sub_decision_builder),
4688 sub_decision_builder_(sub_decision_builder),
4689 nested_decision_index_(0),
4691 filter_manager_(filter_manager),
4692 has_started_(false) {
4693 CHECK(
nullptr != first_solution);
4694 CHECK(
nullptr != ls_operator);
4695 CHECK(!vars.empty());
4696 Solver*
const solver = vars[0]->solver();
4697 assignment_ = solver->GetOrCreateLocalSearchState();
4698 assignment_->Add(vars);
4699 PushFirstSolutionDecision(first_solution);
4700 PushLocalSearchDecision();
4703LocalSearch::~LocalSearch() {}
4706void LocalSearch::Accept(ModelVisitor*
const visitor)
const {
4707 DCHECK(assignment_ !=
nullptr);
4710 const std::vector<IntVarElement>& elements =
4712 if (!elements.empty()) {
4713 std::vector<IntVar*> vars;
4714 for (
const IntVarElement& elem : elements) {
4715 vars.push_back(elem.Var());
4720 const std::vector<IntervalVarElement>& interval_elements =
4722 if (!interval_elements.empty()) {
4723 std::vector<IntervalVar*> interval_vars;
4724 for (
const IntervalVarElement& elem : interval_elements) {
4725 interval_vars.push_back(elem.Var());
4738Decision* LocalSearch::Next(Solver*
const solver) {
4739 CHECK(
nullptr != solver);
4740 CHECK_LT(0, nested_decisions_.size());
4741 if (!has_started_) {
4742 nested_decision_index_ = 0;
4743 solver->SaveAndSetValue(&has_started_,
true);
4744 }
else if (nested_decision_index_ < 0) {
4747 NestedSolveDecision* decision = nested_decisions_[nested_decision_index_];
4748 const int state = decision->state();
4750 case NestedSolveDecision::DECISION_FAILED: {
4754 ls_operator_->
Reset();
4756 nested_decision_index_ = -1;
4761 case NestedSolveDecision::DECISION_PENDING: {
4764 const int32_t kLocalSearchBalancedTreeDepth = 32;
4765 const int depth = solver->SearchDepth();
4766 if (depth < kLocalSearchBalancedTreeDepth) {
4767 return solver->balancing_decision();
4769 if (depth > kLocalSearchBalancedTreeDepth) {
4774 case NestedSolveDecision::DECISION_FOUND: {
4776 if (nested_decision_index_ + 1 < nested_decisions_.size()) {
4777 ++nested_decision_index_;
4782 LOG(ERROR) <<
"Unknown local search state";
4789void LocalSearch::PushFirstSolutionDecision(DecisionBuilder* first_solution) {
4790 CHECK(first_solution);
4791 Solver*
const solver = assignment_->
solver();
4793 DecisionBuilder* first_solution_and_store = solver->Compose(
4794 solver->MakeProfiledDecisionBuilderWrapper(first_solution),
4795 first_solution_sub_decision_builder_, store);
4796 std::vector<SearchMonitor*> monitor;
4797 monitor.push_back(
limit_);
4798 nested_decisions_.push_back(solver->RevAlloc(
4799 new NestedSolveDecision(first_solution_and_store,
false, monitor)));
4802void LocalSearch::PushLocalSearchDecision() {
4803 Solver*
const solver = assignment_->
solver();
4804 DecisionBuilder* find_neighbors = solver->
RevAlloc(
4805 new FindOneNeighbor(assignment_,
objective_, pool_, ls_operator_,
4806 sub_decision_builder_,
limit_, filter_manager_));
4807 nested_decisions_.push_back(
4808 solver->RevAlloc(
new NestedSolveDecision(find_neighbors,
false)));
4811class DefaultSolutionPool :
public SolutionPool {
4813 DefaultSolutionPool() {}
4815 ~DefaultSolutionPool()
override {}
4817 void Initialize(Assignment*
const assignment)
override {
4818 reference_assignment_ = absl::make_unique<Assignment>(assignment);
4821 void RegisterNewSolution(Assignment*
const assignment)
override {
4822 reference_assignment_->CopyIntersection(assignment);
4825 void GetNextSolution(Assignment*
const assignment)
override {
4826 assignment->CopyIntersection(reference_assignment_.get());
4829 bool SyncNeeded(Assignment*
const local_assignment)
override {
return false; }
4831 std::string DebugString()
const override {
return "DefaultSolutionPool"; }
4834 std::unique_ptr<Assignment> reference_assignment_;
4839 return RevAlloc(
new DefaultSolutionPool());
4866 first_solution, first_solution_sub_decision_builder,
4872 const std::vector<SequenceVar*>& vars,
DecisionBuilder* first_solution,
#define DCHECK_LE(val1, val2)
#define DCHECK_NE(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 E & Element(const V *const var) const
const std::vector< E > & elements() const
An Assignment is a variable -> domains mapping, used to report solutions to the user.
const IntervalContainer & IntervalVarContainer() 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
std::string DebugString() const override
const IntContainer & IntVarContainer() const
~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.
IntVar * Var() override
Creates a variable from the expression.
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.
IntVar * Var(int index) const
bool FindIndex(IntVar *const var, int64_t *index) const
IntVarLocalSearchFilter(const std::vector< IntVar * > &vars)
int64_t Value(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
DecisionBuilder * sub_decision_builder() const
~LocalSearchPhaseParameters() override
LocalSearchOperator * ls_operator() const
RegularLimit * limit() const
LocalSearchFilterManager *const filter_manager() const
LocalSearchPhaseParameters(IntVar *objective, SolutionPool *const pool, LocalSearchOperator *ls_operator, DecisionBuilder *sub_decision_builder, RegularLimit *const limit, LocalSearchFilterManager *filter_manager)
IntVar * objective() const
SolutionPool * solution_pool() 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
void AddFirstSolutionProfiledDecisionBuilder(ProfiledDecisionBuilder *profiled_db)
LocalSearchVariable AddVariable(int64_t initial_min, int64_t initial_max)
void set_strategy(ArgT0 &&arg0, ArgT... args)
void set_duration_seconds(double value)
void set_local_search_filter(ArgT0 &&arg0, ArgT... args)
void set_num_rejects(int64_t value)
void set_num_calls(int64_t value)
void set_duration_seconds(double value)
void set_num_accepted_neighbors(int64_t value)
void set_local_search_operator(ArgT0 &&arg0, ArgT... args)
void set_num_filtered_neighbors(int64_t value)
void set_num_neighbors(int64_t value)
void set_duration_seconds(double value)
::operations_research::LocalSearchStatistics_LocalSearchOperatorStatistics * add_local_search_operator_statistics()
::operations_research::LocalSearchStatistics_LocalSearchFilterStatistics * add_local_search_filter_statistics()
void set_total_num_neighbors(int64_t value)
void set_total_num_accepted_neighbors(int64_t value)
::operations_research::LocalSearchStatistics_FirstSolutionStatistics * add_first_solution_statistics()
void set_total_num_filtered_neighbors(int64_t value)
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.
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_
PathOperator(const std::vector< IntVar * > &next_vars, const std::vector< IntVar * > &path_vars, IterationParameters iteration_parameters)
Builds an instance of PathOperator from next and path variables.
bool 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
const std::vector< std::pair< int, int > > & ChangedArcs() const
ChainRange Chains(int path) const
int Start(int path) 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.
DecisionBuilder * MakeProfiledDecisionBuilderWrapper(DecisionBuilder *db)
Activates profiling on a decision builder.
ABSL_MUST_USE_RESULT 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.
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.
T * RevAlloc(T *object)
Registers the given object as being reversible.
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_
const int64_t & OldValue(int64_t index) const
IntVar * Var(int64_t index) const
Returns the variable of given index.
bool ApplyChanges(Assignment *delta, Assignment *deltadelta) const
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)
ReverseView< Container > reversed_view(const Container &c)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
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)
LocalSearchFilter * MakeUnaryDimensionFilter(Solver *solver, std::unique_ptr< UnaryDimensionChecker > checker, const std::string &dimension_name)
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)
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_
std::optional< int64_t > end
Set of parameters used to configure how the neighnorhood is traversed.
bool accept_path_end_base
True if path ends should be considered when iterating over neighbors.
int number_of_base_nodes
Number of nodes needed to define a neighbor.
std::function< int(int64_t)> start_empty_path_class
Callback returning an index such that if c1 = start_empty_path_class(StartNode(p1)),...
bool skip_locally_optimal_paths
Skip paths which have been proven locally optimal.