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_;
2168 const double exploration_coefficient_;
2171MultiArmedBanditCompoundOperator::MultiArmedBanditCompoundOperator(
2172 std::vector<LocalSearchOperator*> operators,
double memory_coefficient,
2173 double exploration_coefficient,
bool maximize)
2175 operators_(
std::move(operators)),
2176 started_(operators_.size()),
2177 start_assignment_(nullptr),
2178 has_fragments_(false),
2179 last_objective_(
std::numeric_limits<int64_t>::
max()),
2182 memory_coefficient_(memory_coefficient),
2183 exploration_coefficient_(exploration_coefficient) {
2187 operators_.erase(std::remove(operators_.begin(), operators_.end(),
nullptr),
2189 operator_indices_.resize(operators_.size());
2190 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
2191 num_neighbors_per_operator_.resize(operators_.size(), 0);
2192 avg_improvement_.resize(operators_.size(), 0);
2193 for (LocalSearchOperator*
const op : operators_) {
2194 if (op->HasFragments()) {
2195 has_fragments_ =
true;
2201void MultiArmedBanditCompoundOperator::Reset() {
2202 for (LocalSearchOperator*
const op : operators_) {
2207double MultiArmedBanditCompoundOperator::Score(
int index) {
2208 return avg_improvement_[
index] +
2209 exploration_coefficient_ *
2210 sqrt(2 * log(1 + num_neighbors_) /
2211 (1 + num_neighbors_per_operator_[
index]));
2214void MultiArmedBanditCompoundOperator::Start(
const Assignment* assignment) {
2215 start_assignment_ = assignment;
2217 if (operators_.empty())
return;
2219 const double objective = assignment->ObjectiveValue();
2221 if (objective == last_objective_)
return;
2224 last_objective_ = objective;
2228 const double improvement =
2229 maximize_ ? objective - last_objective_ : last_objective_ - objective;
2230 if (improvement < 0) {
2233 last_objective_ = objective;
2234 avg_improvement_[operator_indices_[index_]] +=
2235 memory_coefficient_ *
2236 (improvement - avg_improvement_[operator_indices_[index_]]);
2238 std::sort(operator_indices_.begin(), operator_indices_.end(),
2239 [
this](
int lhs,
int rhs) {
2240 const double lhs_score = Score(lhs);
2241 const double rhs_score = Score(rhs);
2242 return lhs_score > rhs_score ||
2243 (lhs_score == rhs_score && lhs < rhs);
2249bool MultiArmedBanditCompoundOperator::MakeNextNeighbor(
2250 Assignment*
delta, Assignment* deltadelta) {
2251 if (operators_.empty())
return false;
2253 const int operator_index = operator_indices_[index_];
2254 if (!started_[operator_index]) {
2255 operators_[operator_index]->Start(start_assignment_);
2256 started_.
Set(operator_index);
2258 if (!operators_[operator_index]->HoldsDelta()) {
2261 if (operators_[operator_index]->MakeNextNeighbor(
delta, deltadelta)) {
2263 ++num_neighbors_per_operator_[operator_index];
2268 if (index_ == operators_.size()) {
2271 }
while (index_ != 0);
2277 const std::vector<LocalSearchOperator*>& ops,
double memory_coefficient,
2278 double exploration_coefficient,
bool maximize) {
2279 return RevAlloc(
new MultiArmedBanditCompoundOperator(
2280 ops, memory_coefficient, exploration_coefficient, maximize));
2287 Solver* solver,
const std::vector<IntVar*>& vars,
2288 const std::vector<IntVar*>& secondary_vars,
2289 std::function<
int(int64_t)> start_empty_path_class) {
2291 new T(vars, secondary_vars, std::move(start_empty_path_class)));
2294#define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass) \
2296 LocalSearchOperator* MakeLocalSearchOperator<OperatorClass>( \
2297 Solver * solver, const std::vector<IntVar*>& vars, \
2298 const std::vector<IntVar*>& secondary_vars, \
2299 std::function<int(int64_t)> start_empty_path_class) { \
2300 return solver->RevAlloc(new OperatorClass( \
2301 vars, secondary_vars, std::move(start_empty_path_class))); \
2317#undef MAKE_LOCAL_SEARCH_OPERATOR
2325 const std::vector<IntVar*>& vars,
2326 const std::vector<IntVar*>& secondary_vars,
2335 std::vector<LocalSearchOperator*> operators;
2336 for (
int i = 1; i < 4; ++i) {
2338 new Relocate(vars, secondary_vars, absl::StrCat(
"OrOpt<", i,
">"),
2339 nullptr, i,
true)));
2345 result = MakeLocalSearchOperator<Relocate>(
this, vars, secondary_vars,
2350 result = MakeLocalSearchOperator<Exchange>(
this, vars, secondary_vars,
2356 MakeLocalSearchOperator<Cross>(
this, vars, secondary_vars,
nullptr);
2360 result = MakeLocalSearchOperator<MakeActiveOperator>(
2361 this, vars, secondary_vars,
nullptr);
2365 result = MakeLocalSearchOperator<MakeInactiveOperator>(
2366 this, vars, secondary_vars,
nullptr);
2370 result = MakeLocalSearchOperator<MakeChainInactiveOperator>(
2371 this, vars, secondary_vars,
nullptr);
2375 result = MakeLocalSearchOperator<SwapActiveOperator>(
2376 this, vars, secondary_vars,
nullptr);
2380 result = MakeLocalSearchOperator<ExtendedSwapActiveOperator>(
2381 this, vars, secondary_vars,
nullptr);
2400 if (secondary_vars.empty()) {
2401 result =
RevAlloc(
new IncrementValue(vars));
2404 <<
" does not support secondary variables";
2409 if (secondary_vars.empty()) {
2410 result =
RevAlloc(
new DecrementValue(vars));
2413 <<
" does not support secondary variables";
2418 if (secondary_vars.empty()) {
2419 result =
RevAlloc(
new SimpleLns(vars, 1));
2422 <<
" does not support secondary variables";
2427 LOG(
FATAL) <<
"Unknown operator " << op;
2435 return MakeOperator(vars, std::vector<IntVar*>(), std::move(evaluator), op);
2439 const std::vector<IntVar*>& vars,
2440 const std::vector<IntVar*>& secondary_vars,
2446 std::vector<LocalSearchOperator*> operators;
2448 new LinKernighan(vars, secondary_vars, evaluator,
false)));
2450 new LinKernighan(vars, secondary_vars, evaluator,
true)));
2456 new TSPOpt(vars, secondary_vars, evaluator,
2457 absl::GetFlag(FLAGS_cp_local_search_tsp_opt_size)));
2462 new TSPLns(vars, secondary_vars, evaluator,
2463 absl::GetFlag(FLAGS_cp_local_search_tsp_lns_size)));
2467 LOG(
FATAL) <<
"Unknown operator " << op;
2477 SumOperation() : value_(0) {}
2478 void Init() { value_ = 0; }
2479 void Update(int64_t update) { value_ =
CapAdd(value_, update); }
2480 void Remove(int64_t remove) { value_ =
CapSub(value_, remove); }
2481 int64_t
value()
const {
return value_; }
2482 void set_value(int64_t new_value) { value_ = new_value; }
2488class ProductOperation {
2490 ProductOperation() : value_(1) {}
2491 void Init() { value_ = 1; }
2492 void Update(int64_t update) { value_ *= update; }
2493 void Remove(int64_t remove) {
2498 int64_t
value()
const {
return value_; }
2499 void set_value(int64_t new_value) { value_ = new_value; }
2507 void Init() { values_set_.clear(); }
2508 void Update(int64_t update) { values_set_.insert(update); }
2509 void Remove(int64_t remove) { values_set_.erase(remove); }
2510 int64_t
value()
const {
2511 return (!values_set_.empty()) ? *values_set_.begin() : 0;
2513 void set_value(int64_t new_value) {}
2516 std::set<int64_t> values_set_;
2521 void Init() { values_set_.clear(); }
2522 void Update(int64_t update) { values_set_.insert(update); }
2523 void Remove(int64_t remove) { values_set_.erase(remove); }
2524 int64_t
value()
const {
2525 return (!values_set_.empty()) ? *values_set_.rbegin() : 0;
2527 void set_value(int64_t new_value) {}
2530 std::set<int64_t> values_set_;
2534class AcceptFilter :
public LocalSearchFilter {
2536 std::string DebugString()
const override {
return "AcceptFilter"; }
2537 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2538 int64_t obj_min, int64_t obj_max)
override {
2541 void Synchronize(
const Assignment* assignment,
2542 const Assignment*
delta)
override {}
2547 return RevAlloc(
new AcceptFilter());
2554 std::string DebugString()
const override {
return "RejectFilter"; }
2555 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2556 int64_t obj_min, int64_t obj_max)
override {
2559 void Synchronize(
const Assignment* assignment,
2560 const Assignment*
delta)
override {}
2565 return RevAlloc(
new RejectFilter());
2569 std::vector<int> path_end)
2570 : num_nodes_(num_nodes),
2571 num_paths_(path_start.size()),
2572 num_nodes_threshold_(
std::
max(16, 4 * num_nodes_))
2574 DCHECK_EQ(path_start.size(), num_paths_);
2576 for (
int p = 0; p < num_paths_; ++p) {
2577 path_start_end_.push_back({path_start[p], path_end[p]});
2580 committed_index_.assign(num_nodes_, -1);
2581 committed_nodes_.assign(2 * num_paths_, {-1, -1});
2582 chains_.assign(num_paths_ + 1, {-1, -1});
2583 paths_.assign(num_paths_, {-1, -1});
2584 for (
int path = 0; path < num_paths_; ++path) {
2585 const int index = 2 * path;
2586 const PathStartEnd start_end = path_start_end_[path];
2587 committed_index_[start_end.start] =
index;
2588 committed_index_[start_end.end] =
index + 1;
2590 committed_nodes_[
index] = {start_end.start, path};
2591 committed_nodes_[
index + 1] = {start_end.end, path};
2594 paths_[path] = {path, path + 1};
2596 chains_[num_paths_] = {0, 0};
2598 for (
int node = 0; node < num_nodes_; ++node) {
2599 if (committed_index_[node] != -1)
continue;
2600 committed_index_[node] = committed_nodes_.size();
2601 committed_nodes_.push_back({node, -1});
2606 const PathBounds
bounds = paths_[path];
2608 chains_.data() +
bounds.end_index,
2609 committed_nodes_.data());
2613 const PathBounds
bounds = paths_[path];
2615 chains_.data() +
bounds.end_index,
2616 committed_nodes_.data());
2620 changed_paths_.push_back(path);
2621 const int path_begin_index = chains_.size();
2622 chains_.insert(chains_.end(), chains.begin(), chains.end());
2623 const int path_end_index = chains_.size();
2624 paths_[path] = {path_begin_index, path_end_index};
2625 chains_.emplace_back(0, 0);
2629 for (
const int loop : new_loops) {
2630 if (
Path(loop) == -1)
continue;
2631 changed_loops_.push_back(loop);
2637 if (committed_nodes_.size() < num_nodes_threshold_) {
2638 IncrementalCommit();
2645 is_invalid_ =
false;
2646 chains_.resize(num_paths_ + 1);
2647 for (
const int path : changed_paths_) {
2648 paths_[path] = {path, path + 1};
2650 changed_paths_.clear();
2651 changed_loops_.clear();
2654void PathState::CopyNewPathAtEndOfNodes(
int path) {
2656 const int new_path_begin_index = committed_nodes_.size();
2657 const PathBounds path_bounds = paths_[path];
2658 for (
int i = path_bounds.begin_index; i < path_bounds.end_index; ++i) {
2659 const ChainBounds chain_bounds = chains_[i];
2660 committed_nodes_.insert(committed_nodes_.end(),
2661 committed_nodes_.data() + chain_bounds.begin_index,
2662 committed_nodes_.data() + chain_bounds.end_index);
2664 const int new_path_end_index = committed_nodes_.size();
2666 for (
int i = new_path_begin_index; i < new_path_end_index; ++i) {
2667 committed_nodes_[i].path = path;
2673void PathState::IncrementalCommit() {
2674 const int new_nodes_begin = committed_nodes_.size();
2676 const int chain_begin = committed_nodes_.size();
2677 CopyNewPathAtEndOfNodes(path);
2678 const int chain_end = committed_nodes_.size();
2679 chains_[path] = {chain_begin, chain_end};
2682 const int new_nodes_end = committed_nodes_.size();
2683 for (
int i = new_nodes_begin; i < new_nodes_end; ++i) {
2684 committed_index_[committed_nodes_[i].node] = i;
2689 const int index = committed_index_[loop];
2690 committed_nodes_[
index].path = -1;
2696void PathState::FullCommit() {
2699 const int old_num_nodes = committed_nodes_.size();
2700 for (
int path = 0; path < num_paths_; ++path) {
2701 const int new_path_begin = committed_nodes_.size() - old_num_nodes;
2702 CopyNewPathAtEndOfNodes(path);
2703 const int new_path_end = committed_nodes_.size() - old_num_nodes;
2704 chains_[path] = {new_path_begin, new_path_end};
2706 committed_nodes_.erase(committed_nodes_.begin(),
2707 committed_nodes_.begin() + old_num_nodes);
2710 constexpr int kUnindexed = -1;
2711 committed_index_.assign(num_nodes_, kUnindexed);
2713 for (
const CommittedNode committed_node : committed_nodes_) {
2714 committed_index_[committed_node.node] =
index++;
2716 for (
int node = 0; node < num_nodes_; ++node) {
2717 if (committed_index_[node] != kUnindexed)
continue;
2718 committed_index_[node] =
index++;
2719 committed_nodes_.push_back({node, -1});
2727class PathStateFilter :
public LocalSearchFilter {
2729 std::string DebugString()
const override {
return "PathStateFilter"; }
2730 PathStateFilter(std::unique_ptr<PathState> path_state,
2731 const std::vector<IntVar*>& nexts);
2732 void Relax(
const Assignment*
delta,
const Assignment* deltadelta)
override;
2733 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2734 int64_t objective_min, int64_t objective_max)
override {
2737 void Synchronize(
const Assignment*
delta,
2738 const Assignment* deltadelta)
override{};
2739 void Commit(
const Assignment* assignment,
const Assignment*
delta)
override;
2740 void Revert()
override;
2741 void Reset()
override;
2745 struct TailHeadIndices {
2752 bool operator<(
const IndexArc& other)
const {
return index < other.index; }
2759 void MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
2762 void MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
2764 const std::unique_ptr<PathState> path_state_;
2766 std::vector<int> variable_index_to_node_;
2769 std::vector<bool> node_is_assigned_;
2770 std::vector<int> loops_;
2773 std::vector<int> changed_paths_;
2774 std::vector<bool> path_has_changed_;
2775 std::vector<std::pair<int, int>> changed_arcs_;
2776 std::vector<int> changed_loops_;
2777 std::vector<TailHeadIndices> tail_head_indices_;
2778 std::vector<IndexArc> arcs_by_tail_index_;
2779 std::vector<IndexArc> arcs_by_head_index_;
2780 std::vector<int> next_arc_;
2781 std::vector<PathState::ChainBounds> path_chains_;
2784PathStateFilter::PathStateFilter(std::unique_ptr<PathState> path_state,
2785 const std::vector<IntVar*>& nexts)
2786 : path_state_(
std::move(path_state)) {
2790 for (
const IntVar*
next : nexts) {
2792 min_index = std::min<int>(min_index,
index);
2793 max_index = std::max<int>(max_index,
index);
2795 variable_index_to_node_.resize(max_index - min_index + 1, -1);
2796 index_offset_ = min_index;
2799 for (
int node = 0; node < nexts.size(); ++node) {
2800 const int index = nexts[node]->index() - index_offset_;
2801 variable_index_to_node_[
index] = node;
2803 path_has_changed_.assign(path_state_->NumPaths(),
false);
2806void PathStateFilter::Relax(
const Assignment*
delta,
2807 const Assignment* deltadelta) {
2808 path_state_->Revert();
2809 changed_arcs_.clear();
2810 for (
const IntVarElement& var_value :
delta->IntVarContainer().elements()) {
2811 if (var_value.Var() ==
nullptr)
continue;
2812 const int index = var_value.Var()->index() - index_offset_;
2813 if (
index < 0 || variable_index_to_node_.size() <=
index)
continue;
2814 const int node = variable_index_to_node_[
index];
2815 if (node == -1)
continue;
2816 if (var_value.Bound()) {
2817 changed_arcs_.emplace_back(node, var_value.Value());
2819 path_state_->Revert();
2820 path_state_->SetInvalid();
2827void PathStateFilter::Reset() {
2828 path_state_->Revert();
2831 const int num_nodes = path_state_->NumNodes();
2832 node_is_assigned_.assign(num_nodes,
false);
2834 const int num_paths = path_state_->NumPaths();
2835 for (
int path = 0; path < num_paths; ++path) {
2836 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
2837 path_state_->ChangePath(
2838 path, {{start_index, start_index + 1}, {end_index - 1, end_index}});
2839 node_is_assigned_[path_state_->Start(path)] =
true;
2840 node_is_assigned_[path_state_->End(path)] =
true;
2842 for (
int node = 0; node < num_nodes; ++node) {
2843 if (!node_is_assigned_[node]) loops_.push_back(node);
2845 path_state_->ChangeLoops(loops_);
2846 path_state_->Commit();
2852void PathStateFilter::Commit(
const Assignment* assignment,
2853 const Assignment*
delta) {
2854 path_state_->Revert();
2856 Relax(assignment,
nullptr);
2858 Relax(
delta,
nullptr);
2860 path_state_->Commit();
2863void PathStateFilter::Revert() { path_state_->Revert(); }
2865void PathStateFilter::CutChains() {
2869 for (
const int path : changed_paths_) path_has_changed_[path] =
false;
2870 changed_paths_.clear();
2871 tail_head_indices_.clear();
2872 changed_loops_.clear();
2873 int num_changed_arcs = 0;
2874 for (
const auto [node,
next] : changed_arcs_) {
2875 const int node_index = path_state_->CommittedIndex(node);
2876 const int next_index = path_state_->CommittedIndex(
next);
2877 const int node_path = path_state_->Path(node);
2879 (next_index != node_index + 1 || node_path == -1)) {
2880 tail_head_indices_.push_back({node_index, next_index});
2881 changed_arcs_[num_changed_arcs++] = {node,
next};
2882 if (node_path != -1 && !path_has_changed_[node_path]) {
2883 path_has_changed_[node_path] =
true;
2884 changed_paths_.push_back(node_path);
2886 }
else if (node ==
next && node_path != -1) {
2887 changed_loops_.push_back(node);
2890 changed_arcs_.resize(num_changed_arcs);
2892 path_state_->ChangeLoops(changed_loops_);
2893 if (tail_head_indices_.size() + changed_paths_.size() <= 8) {
2894 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
2896 MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
2900void PathStateFilter::
2901 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm() {
2902 int num_visited_changed_arcs = 0;
2903 const int num_changed_arcs = tail_head_indices_.size();
2905 for (
const int path : changed_paths_) {
2906 path_chains_.clear();
2907 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
2908 int current_index = start_index;
2912 int selected_arc = -1;
2914 for (
int i = num_visited_changed_arcs; i < num_changed_arcs; ++i) {
2915 const int tail_index = tail_head_indices_[i].tail_index;
2926 if (start_index <= current_index && current_index < end_index &&
2927 end_index <= selected_tail_index) {
2928 path_chains_.emplace_back(current_index, end_index);
2931 path_chains_.emplace_back(current_index, selected_tail_index + 1);
2932 current_index = tail_head_indices_[selected_arc].head_index;
2933 std::swap(tail_head_indices_[num_visited_changed_arcs],
2934 tail_head_indices_[selected_arc]);
2935 ++num_visited_changed_arcs;
2938 path_state_->ChangePath(path, path_chains_);
2942void PathStateFilter::MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm() {
2958 for (
const int path : changed_paths_) {
2959 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
2960 tail_head_indices_.push_back({end_index - 1, start_index});
2965 const int num_arc_indices = tail_head_indices_.size();
2966 arcs_by_tail_index_.resize(num_arc_indices);
2967 arcs_by_head_index_.resize(num_arc_indices);
2968 for (
int i = 0; i < num_arc_indices; ++i) {
2969 arcs_by_tail_index_[i] = {tail_head_indices_[i].tail_index, i};
2970 arcs_by_head_index_[i] = {tail_head_indices_[i].head_index, i};
2972 std::sort(arcs_by_tail_index_.begin(), arcs_by_tail_index_.end());
2973 std::sort(arcs_by_head_index_.begin(), arcs_by_head_index_.end());
2975 next_arc_.resize(num_arc_indices);
2976 for (
int i = 0; i < num_arc_indices; ++i) {
2977 next_arc_[arcs_by_head_index_[i].arc] = arcs_by_tail_index_[i].arc;
2983 const int first_fake_arc = num_arc_indices - changed_paths_.size();
2984 for (
int fake_arc = first_fake_arc; fake_arc < num_arc_indices; ++fake_arc) {
2985 path_chains_.clear();
2986 int32_t
arc = fake_arc;
2988 const int chain_begin = tail_head_indices_[
arc].head_index;
2990 const int chain_end = tail_head_indices_[
arc].tail_index + 1;
2991 path_chains_.emplace_back(chain_begin, chain_end);
2992 }
while (
arc != fake_arc);
2993 const int path = changed_paths_[fake_arc - first_fake_arc];
2994 path_state_->ChangePath(path, path_chains_);
3001 std::unique_ptr<PathState> path_state,
3002 const std::vector<IntVar*>& nexts) {
3003 PathStateFilter* filter =
new PathStateFilter(std::move(path_state), nexts);
3007UnaryDimensionChecker::UnaryDimensionChecker(
3008 const PathState* path_state, std::vector<Interval> path_capacity,
3009 std::vector<int> path_class,
3010 std::vector<std::function<
Interval(int64_t)>> min_max_demand_per_path_class,
3011 std::vector<Interval> node_capacity)
3012 : path_state_(path_state),
3013 path_capacity_(
std::move(path_capacity)),
3014 path_class_(
std::move(path_class)),
3015 min_max_demand_per_path_class_(
std::move(min_max_demand_per_path_class)),
3016 node_capacity_(
std::move(node_capacity)),
3017 index_(path_state_->NumNodes(), 0),
3018 maximum_partial_demand_layer_size_(
3019 std::
max(16, 4 * path_state_->NumNodes()))
3021 const int num_nodes = path_state_->
NumNodes();
3022 cached_demand_.resize(num_nodes);
3023 const int num_paths = path_state_->
NumPaths();
3024 DCHECK_EQ(num_paths, path_capacity_.size());
3025 DCHECK_EQ(num_paths, path_class_.size());
3027 partial_demand_sums_rmq_.resize(maximum_rmq_exponent + 1);
3028 previous_nontrivial_index_.reserve(maximum_partial_demand_layer_size_);
3033 if (path_state_->
IsInvalid())
return true;
3035 const Interval path_capacity = path_capacity_[path];
3040 const int path_class = path_class_[path];
3042 Interval capacity_used = node_capacity_[path_state_->
Start(path)];
3045 if (capacity_used.
min > capacity_used.
max)
return false;
3047 for (
const auto chain : path_state_->
Chains(path)) {
3048 const int first_node = chain.First();
3049 const int last_node = chain.Last();
3051 const int first_index = index_[first_node];
3052 const int last_index = index_[last_node];
3054 const int chain_path = path_state_->
Path(first_node);
3055 const int chain_path_class =
3056 chain_path == -1 ? -1 : path_class_[chain_path];
3060 constexpr int kMinRangeSizeForRMQ = 4;
3061 const bool chain_is_cached = chain_path_class == path_class;
3062 if (last_index - first_index > kMinRangeSizeForRMQ && chain_is_cached &&
3063 SubpathOnlyHasTrivialNodes(first_index, last_index)) {
3068 GetMinMaxPartialDemandSum(first_index, last_index);
3069 const Interval prev_sum = partial_demand_sums_rmq_[0][first_index - 1];
3077 if (capacity_used.
min > capacity_used.
max)
return false;
3079 const Interval last_sum = partial_demand_sums_rmq_[0][last_index];
3084 for (
const int node : chain) {
3085 const Interval node_capacity = node_capacity_[node];
3088 if (capacity_used.
min > capacity_used.
max)
return false;
3091 ? cached_demand_[node]
3092 : min_max_demand_per_path_class_[path_class](node);
3093 capacity_used = {
CapAdd(capacity_used.min,
demand.min),
3095 capacity_used = {
std::max(capacity_used.min, path_capacity.
min),
3109 const int current_layer_size = partial_demand_sums_rmq_[0].size();
3112 for (
const auto chain : path_state_->
Chains(path)) {
3113 change_size += chain.NumNodes();
3116 if (current_layer_size + change_size <= maximum_partial_demand_layer_size_) {
3117 IncrementalCommit();
3123void UnaryDimensionChecker::IncrementalCommit() {
3125 const int begin_index = partial_demand_sums_rmq_[0].size();
3126 AppendPathDemandsToSums(path);
3127 UpdateRMQStructure(begin_index, partial_demand_sums_rmq_[0].size());
3131void UnaryDimensionChecker::FullCommit() {
3133 previous_nontrivial_index_.clear();
3134 for (
auto& sums : partial_demand_sums_rmq_) sums.clear();
3136 const int num_paths = path_state_->
NumPaths();
3137 for (
int path = 0; path < num_paths; ++path) {
3138 const int begin_index = partial_demand_sums_rmq_[0].size();
3139 AppendPathDemandsToSums(path);
3140 UpdateRMQStructure(begin_index, partial_demand_sums_rmq_[0].size());
3144void UnaryDimensionChecker::AppendPathDemandsToSums(
int path) {
3145 const int path_class = path_class_[path];
3146 Interval demand_sum = {0, 0};
3147 int previous_nontrivial_index = -1;
3148 int index = partial_demand_sums_rmq_[0].size();
3151 partial_demand_sums_rmq_[0].push_back(demand_sum);
3152 previous_nontrivial_index_.push_back(-1);
3155 for (
const int node : path_state_->
Nodes(path)) {
3156 index_[node] =
index;
3157 const Interval
demand = min_max_demand_per_path_class_[path_class](node);
3158 cached_demand_[node] =
demand;
3161 partial_demand_sums_rmq_[0].push_back(demand_sum);
3163 const Interval node_capacity = node_capacity_[node];
3167 previous_nontrivial_index =
index;
3169 previous_nontrivial_index_.push_back(previous_nontrivial_index);
3174void UnaryDimensionChecker::UpdateRMQStructure(
int begin_index,
int end_index) {
3177 const int maximum_rmq_exponent =
3179 for (
int layer = 1, window_size = 1; layer <= maximum_rmq_exponent;
3180 ++layer, window_size *= 2) {
3181 partial_demand_sums_rmq_[layer].resize(end_index);
3182 for (
int i = begin_index; i < end_index - window_size; ++i) {
3183 const Interval i1 = partial_demand_sums_rmq_[layer - 1][i];
3184 const Interval i2 = partial_demand_sums_rmq_[layer - 1][i + window_size];
3185 partial_demand_sums_rmq_[layer][i] = {
std::min(i1.min, i2.min),
3189 partial_demand_sums_rmq_[layer - 1].begin() + end_index - window_size,
3190 partial_demand_sums_rmq_[layer - 1].begin() + end_index,
3191 partial_demand_sums_rmq_[layer].begin() + end_index - window_size);
3201UnaryDimensionChecker::Interval
3202UnaryDimensionChecker::GetMinMaxPartialDemandSum(
int first_node_index,
3203 int last_node_index)
const {
3205 DCHECK_LT(first_node_index, last_node_index);
3206 DCHECK_LT(last_node_index, partial_demand_sums_rmq_[0].size());
3211 const int window_size = 1 << layer;
3213 const Interval i1 = partial_demand_sums_rmq_[layer][first_node_index];
3215 partial_demand_sums_rmq_[layer][last_node_index - window_size + 1];
3219bool UnaryDimensionChecker::SubpathOnlyHasTrivialNodes(
3220 int first_node_index,
int last_node_index)
const {
3222 DCHECK_LT(first_node_index, last_node_index);
3223 DCHECK_LT(last_node_index, previous_nontrivial_index_.size());
3224 return first_node_index > previous_nontrivial_index_[last_node_index];
3229class UnaryDimensionFilter :
public LocalSearchFilter {
3231 std::string DebugString()
const override {
return name_; }
3232 UnaryDimensionFilter(std::unique_ptr<UnaryDimensionChecker> checker,
3233 const std::string& dimension_name)
3234 : checker_(
std::move(checker)),
3235 name_(
absl::StrCat(
"UnaryDimensionFilter(", dimension_name,
")")) {}
3237 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3238 int64_t objective_min, int64_t objective_max)
override {
3239 return checker_->Check();
3242 void Synchronize(
const Assignment* assignment,
3243 const Assignment*
delta)
override {
3248 std::unique_ptr<UnaryDimensionChecker> checker_;
3249 const std::string name_;
3255 Solver* solver, std::unique_ptr<UnaryDimensionChecker> checker,
3256 const std::string& dimension_name) {
3257 UnaryDimensionFilter* filter =
3258 new UnaryDimensionFilter(std::move(checker), dimension_name);
3266class VariableDomainFilter :
public LocalSearchFilter {
3268 VariableDomainFilter() {}
3269 ~VariableDomainFilter()
override {}
3270 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3271 int64_t objective_min, int64_t objective_max)
override;
3272 void Synchronize(
const Assignment* assignment,
3273 const Assignment*
delta)
override {}
3275 std::string DebugString()
const override {
return "VariableDomainFilter"; }
3278bool VariableDomainFilter::Accept(
const Assignment*
delta,
3279 const Assignment* deltadelta,
3280 int64_t objective_min,
3281 int64_t objective_max) {
3283 const int size = container.Size();
3284 for (
int i = 0; i < size; ++i) {
3285 const IntVarElement& element = container.Element(i);
3286 if (element.Activated() && !element.Var()->Contains(element.Value())) {
3295 return RevAlloc(
new VariableDomainFilter());
3300const int IntVarLocalSearchFilter::kUnassigned = -1;
3303 const std::vector<IntVar*>& vars) {
3308 if (!vars.empty()) {
3309 for (
int i = 0; i < vars.size(); ++i) {
3310 const int index = vars[i]->index();
3311 if (
index >= var_index_to_index_.size()) {
3312 var_index_to_index_.resize(
index + 1, kUnassigned);
3314 var_index_to_index_[
index] = i + vars_.size();
3316 vars_.insert(vars_.end(), vars.begin(), vars.end());
3317 values_.resize(vars_.size(), 0);
3318 var_synced_.resize(vars_.size(),
false);
3327 var_synced_.assign(var_synced_.size(),
false);
3338 const int size = container.
Size();
3339 for (
int i = 0; i < size; ++i) {
3342 if (
var !=
nullptr) {
3343 if (i < vars_.size() && vars_[i] ==
var) {
3344 values_[i] = element.
Value();
3345 var_synced_[i] =
true;
3347 const int64_t kUnallocated = -1;
3348 int64_t
index = kUnallocated;
3351 var_synced_[
index] =
true;
3370 SumObjectiveFilter(
const std::vector<IntVar*>& vars,
3380 for (
int i = 0; i < vars.size(); ++i) {
3385 ~SumObjectiveFilter()
override {
3389 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3390 int64_t objective_min, int64_t objective_max)
override {
3391 if (
delta ==
nullptr) {
3394 if (deltadelta->Empty()) {
3425 LOG(ERROR) <<
"Unknown local search filter enum value";
3432 virtual int64_t CostOfSynchronizedVariable(int64_t
index) = 0;
3436 virtual bool FillCostOfBoundDeltaVariable(
3438 int* container_index, int64_t* new_cost) = 0;
3439 bool IsIncremental()
const override {
return true; }
3441 std::string DebugString()
const override {
return "SumObjectiveFilter"; }
3443 int64_t GetSynchronizedObjectiveValue()
const override {
3446 int64_t GetAcceptedObjectiveValue()
const override {
return delta_sum_; }
3458 void OnSynchronize(
const Assignment*
delta)
override {
3461 const int64_t
cost = CostOfSynchronizedVariable(i);
3469 int64_t CostOfChanges(
const Assignment* changes,
3470 const int64_t*
const old_costs,
3471 bool cache_delta_values) {
3472 int64_t total_cost = 0;
3474 const int size = container.
Size();
3475 for (
int i = 0; i < size; ++i) {
3476 const IntVarElement& new_element = container.Element(i);
3477 IntVar*
const var = new_element.Var();
3480 total_cost =
CapSub(total_cost, old_costs[
index]);
3481 int64_t new_cost = 0LL;
3482 if (FillCostOfBoundDeltaVariable(container,
index, &i, &new_cost)) {
3483 total_cost =
CapAdd(total_cost, new_cost);
3485 if (cache_delta_values) {
3494class BinaryObjectiveFilter :
public SumObjectiveFilter {
3496 BinaryObjectiveFilter(
const std::vector<IntVar*>& vars,
3499 : SumObjectiveFilter(vars, filter_enum),
3500 value_evaluator_(
std::move(value_evaluator)) {}
3501 ~BinaryObjectiveFilter()
override {}
3502 int64_t CostOfSynchronizedVariable(int64_t
index)
override {
3508 int index,
int* container_index,
3509 int64_t* new_cost)
override {
3510 const IntVarElement& element = container.Element(*container_index);
3511 if (element.Activated()) {
3512 *new_cost = value_evaluator_(
index, element.Value());
3515 const IntVar*
var = element.Var();
3517 *new_cost = value_evaluator_(
index,
var->Min());
3528class TernaryObjectiveFilter :
public SumObjectiveFilter {
3530 TernaryObjectiveFilter(
const std::vector<IntVar*>& vars,
3531 const std::vector<IntVar*>& secondary_vars,
3534 : SumObjectiveFilter(vars, filter_enum),
3535 secondary_vars_offset_(vars.size()),
3536 value_evaluator_(
std::move(value_evaluator)) {
3540 ~TernaryObjectiveFilter()
override {}
3541 int64_t CostOfSynchronizedVariable(int64_t
index)
override {
3546 index + secondary_vars_offset_))
3550 int index,
int* container_index,
3551 int64_t* new_cost)
override {
3554 const IntVarElement& element = container.Element(*container_index);
3555 const IntVar* secondary_var =
3557 if (element.Activated()) {
3558 const int64_t
value = element.Value();
3559 int hint_index = *container_index + 1;
3560 if (hint_index < container.Size() &&
3561 secondary_var == container.Element(hint_index).Var()) {
3563 container.Element(hint_index).Value());
3564 *container_index = hint_index;
3567 container.Element(secondary_var).Value());
3571 const IntVar*
var = element.Var();
3572 if (
var->Bound() && secondary_var->Bound()) {
3573 *new_cost = value_evaluator_(
index,
var->Min(), secondary_var->Min());
3581 int secondary_vars_offset_;
3590 new BinaryObjectiveFilter(vars, std::move(values), filter_enum));
3594 const std::vector<IntVar*>& vars,
3597 return RevAlloc(
new TernaryObjectiveFilter(vars, secondary_vars,
3598 std::move(values), filter_enum));
3602 int64_t initial_max) {
3605 initial_variable_bounds_.push_back({initial_min, initial_max});
3606 variable_bounds_.push_back({initial_min, initial_max});
3607 variable_is_relaxed_.push_back(
false);
3609 const int variable_index = variable_bounds_.size() - 1;
3610 return {
this, variable_index};
3613void LocalSearchState::RelaxVariableBounds(
int variable_index) {
3615 DCHECK(0 <= variable_index && variable_index < variable_is_relaxed_.size());
3616 if (!variable_is_relaxed_[variable_index]) {
3617 variable_is_relaxed_[variable_index] =
true;
3618 saved_variable_bounds_trail_.emplace_back(variable_bounds_[variable_index],
3620 variable_bounds_[variable_index] = initial_variable_bounds_[variable_index];
3624int64_t LocalSearchState::VariableMin(
int variable_index)
const {
3626 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3627 return variable_bounds_[variable_index].min;
3630int64_t LocalSearchState::VariableMax(
int variable_index)
const {
3632 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3633 return variable_bounds_[variable_index].max;
3636bool LocalSearchState::TightenVariableMin(
int variable_index,
3637 int64_t min_value) {
3639 DCHECK(variable_is_relaxed_[variable_index]);
3640 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3641 Bounds&
bounds = variable_bounds_[variable_index];
3642 if (
bounds.max < min_value) {
3643 state_is_valid_ =
false;
3646 return state_is_valid_;
3649bool LocalSearchState::TightenVariableMax(
int variable_index,
3650 int64_t max_value) {
3652 DCHECK(variable_is_relaxed_[variable_index]);
3653 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3654 Bounds&
bounds = variable_bounds_[variable_index];
3655 if (
bounds.min > max_value) {
3656 state_is_valid_ =
false;
3659 return state_is_valid_;
3667 saved_variable_bounds_trail_.clear();
3668 variable_is_relaxed_.assign(variable_is_relaxed_.size(),
false);
3672 for (
const auto& bounds_index : saved_variable_bounds_trail_) {
3673 DCHECK(variable_is_relaxed_[bounds_index.second]);
3674 variable_bounds_[bounds_index.second] = bounds_index.first;
3676 saved_variable_bounds_trail_.clear();
3677 variable_is_relaxed_.assign(variable_is_relaxed_.size(),
false);
3678 state_is_valid_ =
true;
3686 std::string
DebugString()
const override {
return "LocalSearchProfiler"; }
3688 operator_stats_.clear();
3689 filter_stats_.clear();
3693 if (
solver()->TopLevelSearch() ==
solver()->ActiveSearch()) {
3698 LocalSearchStatistics statistics_proto;
3700 if (db->seconds() == 0)
continue;
3701 LocalSearchStatistics::FirstSolutionStatistics*
const
3702 first_solution_statistics =
3703 statistics_proto.add_first_solution_statistics();
3704 first_solution_statistics->set_strategy(db->name());
3705 first_solution_statistics->set_duration_seconds(db->seconds());
3707 std::vector<const LocalSearchOperator*> operators;
3708 for (
const auto& stat : operator_stats_) {
3709 operators.push_back(stat.first);
3712 operators.begin(), operators.end(),
3714 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3715 gtl::FindOrDie(operator_stats_, op2).neighbors;
3718 const OperatorStats& stats =
gtl::FindOrDie(operator_stats_, op);
3719 LocalSearchStatistics::LocalSearchOperatorStatistics*
const
3720 local_search_operator_statistics =
3721 statistics_proto.add_local_search_operator_statistics();
3722 local_search_operator_statistics->set_local_search_operator(
3724 local_search_operator_statistics->set_num_neighbors(stats.neighbors);
3725 local_search_operator_statistics->set_num_filtered_neighbors(
3726 stats.filtered_neighbors);
3727 local_search_operator_statistics->set_num_accepted_neighbors(
3728 stats.accepted_neighbors);
3729 local_search_operator_statistics->set_duration_seconds(stats.seconds);
3731 std::vector<const LocalSearchFilter*> filters;
3732 for (
const auto& stat : filter_stats_) {
3733 filters.push_back(stat.first);
3735 std::sort(filters.begin(), filters.end(),
3738 return gtl::FindOrDie(filter_stats_, filter1).calls >
3739 gtl::FindOrDie(filter_stats_, filter2).calls;
3742 const FilterStats& stats =
gtl::FindOrDie(filter_stats_, filter);
3743 LocalSearchStatistics::LocalSearchFilterStatistics*
const
3744 local_search_filter_statistics =
3745 statistics_proto.add_local_search_filter_statistics();
3746 local_search_filter_statistics->set_local_search_filter(
3747 filter->DebugString());
3748 local_search_filter_statistics->set_num_calls(stats.calls);
3749 local_search_filter_statistics->set_num_rejects(stats.rejects);
3750 local_search_filter_statistics->set_duration_seconds(stats.seconds);
3752 statistics_proto.set_total_num_neighbors(
solver()->neighbors());
3753 statistics_proto.set_total_num_filtered_neighbors(
3754 solver()->filtered_neighbors());
3755 statistics_proto.set_total_num_accepted_neighbors(
3756 solver()->accepted_neighbors());
3757 return statistics_proto;
3760 size_t max_name_size = 0;
3761 std::vector<const LocalSearchOperator*> operators;
3762 for (
const auto& stat : operator_stats_) {
3763 operators.push_back(stat.first);
3765 std::max(max_name_size, stat.first->DebugString().length());
3768 operators.begin(), operators.end(),
3770 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3771 gtl::FindOrDie(operator_stats_, op2).neighbors;
3773 std::string overview =
"Local search operator statistics:\n";
3774 absl::StrAppendFormat(&overview,
3775 "%*s | Neighbors | Filtered | Accepted | Time (s)\n",
3777 OperatorStats total_stats;
3779 const OperatorStats& stats =
gtl::FindOrDie(operator_stats_, op);
3780 const std::string&
name = op->DebugString();
3781 absl::StrAppendFormat(&overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n",
3782 max_name_size,
name, stats.neighbors,
3783 stats.filtered_neighbors, stats.accepted_neighbors,
3785 total_stats.neighbors += stats.neighbors;
3786 total_stats.filtered_neighbors += stats.filtered_neighbors;
3787 total_stats.accepted_neighbors += stats.accepted_neighbors;
3788 total_stats.seconds += stats.seconds;
3790 absl::StrAppendFormat(&overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n",
3791 max_name_size,
"Total", total_stats.neighbors,
3792 total_stats.filtered_neighbors,
3793 total_stats.accepted_neighbors, total_stats.seconds);
3795 std::vector<const LocalSearchFilter*> filters;
3796 for (
const auto& stat : filter_stats_) {
3797 filters.push_back(stat.first);
3799 std::max(max_name_size, stat.first->DebugString().length());
3801 std::sort(filters.begin(), filters.end(),
3804 return gtl::FindOrDie(filter_stats_, filter1).calls >
3805 gtl::FindOrDie(filter_stats_, filter2).calls;
3807 absl::StrAppendFormat(&overview,
3808 "Local search filter statistics:\n%*s | Calls | "
3809 " Rejects | Time (s) "
3812 FilterStats total_filter_stats;
3814 const FilterStats& stats =
gtl::FindOrDie(filter_stats_, filter);
3815 const std::string&
name = filter->DebugString();
3816 absl::StrAppendFormat(&overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n",
3817 max_name_size,
name, stats.calls, stats.rejects,
3818 stats.seconds, stats.rejects / stats.seconds);
3819 total_filter_stats.calls += stats.calls;
3820 total_filter_stats.rejects += stats.rejects;
3821 total_filter_stats.seconds += stats.seconds;
3823 absl::StrAppendFormat(
3824 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n", max_name_size,
3825 "Total", total_filter_stats.calls, total_filter_stats.rejects,
3826 total_filter_stats.seconds,
3827 total_filter_stats.rejects / total_filter_stats.seconds);
3833 if (last_operator_ != op->
Self()) {
3835 last_operator_ = op->
Self();
3841 if (neighbor_found) {
3842 operator_stats_[op->
Self()].neighbors++;
3847 bool neighbor_found)
override {
3848 if (neighbor_found) {
3849 operator_stats_[op->
Self()].filtered_neighbors++;
3854 bool neighbor_found)
override {
3855 if (neighbor_found) {
3856 operator_stats_[op->
Self()].accepted_neighbors++;
3860 filter_stats_[filter].calls++;
3861 filter_timer_.
Start();
3864 filter_timer_.
Stop();
3865 auto& stats = filter_stats_[filter];
3866 stats.seconds += filter_timer_.
Get();
3873 profiled_decision_builders_.push_back(profiled_db);
3879 if (last_operator_ !=
nullptr) {
3881 operator_stats_[last_operator_].seconds += timer_.
Get();
3886 struct OperatorStats {
3887 int64_t neighbors = 0;
3888 int64_t filtered_neighbors = 0;
3889 int64_t accepted_neighbors = 0;
3893 struct FilterStats {
3895 int64_t rejects = 0;
3900 const LocalSearchOperator* last_operator_ =
nullptr;
3901 absl::flat_hash_map<const LocalSearchOperator*, OperatorStats>
3903 absl::flat_hash_map<const LocalSearchFilter*, FilterStats> filter_stats_;
3905 std::vector<ProfiledDecisionBuilder*> profiled_decision_builders_;
3934 if (local_search_profiler_ !=
nullptr) {
3941 if (local_search_profiler_ !=
nullptr) {
3944 return LocalSearchStatistics();
3947void LocalSearchFilterManager::InitializeForcedEvents() {
3948 const int num_events = filter_events_.size();
3949 int next_forced_event = num_events;
3950 next_forced_events_.resize(num_events);
3951 for (
int i = num_events - 1; i >= 0; --i) {
3952 next_forced_events_[i] = next_forced_event;
3953 if (filter_events_[i].filter->IsIncremental() ||
3954 (filter_events_[i].event_type == FilterEventType::kRelax &&
3955 next_forced_event != num_events)) {
3956 next_forced_event = i;
3962 std::vector<LocalSearchFilter*> filters)
3963 : synchronized_value_(
std::numeric_limits<int64_t>::
min()),
3964 accepted_value_(
std::numeric_limits<int64_t>::
min()) {
3965 filter_events_.reserve(2 * filters.size());
3967 filter_events_.push_back({filter, FilterEventType::kRelax});
3970 filter_events_.push_back({filter, FilterEventType::kAccept});
3972 InitializeForcedEvents();
3976 std::vector<FilterEvent> filter_events)
3977 : filter_events_(
std::move(filter_events)),
3978 synchronized_value_(
std::numeric_limits<int64_t>::
min()),
3979 accepted_value_(
std::numeric_limits<int64_t>::
min()) {
3980 InitializeForcedEvents();
3986 for (
int i = last_event_called_; i >= 0; --i) {
3987 auto [filter, event_type] = filter_events_[i];
3988 if (event_type == FilterEventType::kRelax) filter->Revert();
3990 last_event_called_ = -1;
3999 int64_t objective_min,
4000 int64_t objective_max) {
4002 accepted_value_ = 0;
4004 const int num_events = filter_events_.size();
4005 for (
int i = 0; i < num_events;) {
4006 last_event_called_ = i;
4007 auto [filter, event_type] = filter_events_[last_event_called_];
4008 switch (event_type) {
4009 case FilterEventType::kAccept: {
4011 const bool accept = filter->Accept(
4012 delta, deltadelta,
CapSub(objective_min, accepted_value_),
4013 CapSub(objective_max, accepted_value_));
4015 if (monitor !=
nullptr) monitor->
EndFiltering(filter, !accept);
4018 CapAdd(accepted_value_, filter->GetAcceptedObjectiveValue());
4020 ok = accepted_value_ <= objective_max;
4024 case FilterEventType::kRelax: {
4025 filter->Relax(
delta, deltadelta);
4029 LOG(
FATAL) <<
"Unknown filter event type.";
4035 i = next_forced_events_[i];
4046 const bool reset_to_assignment =
delta ==
nullptr ||
delta->Empty();
4048 for (
auto [filter, event_type] : filter_events_) {
4049 switch (event_type) {
4050 case FilterEventType::kAccept: {
4053 case FilterEventType::kRelax: {
4054 if (reset_to_assignment) {
4056 filter->Relax(assignment,
nullptr);
4058 filter->Relax(
delta,
nullptr);
4063 LOG(
FATAL) <<
"Unknown filter event type.";
4068 synchronized_value_ = 0;
4070 switch (event_type) {
4071 case FilterEventType::kAccept: {
4072 filter->Synchronize(assignment,
delta);
4073 synchronized_value_ =
CapAdd(synchronized_value_,
4074 filter->GetSynchronizedObjectiveValue());
4077 case FilterEventType::kRelax: {
4078 filter->Commit(assignment,
delta);
4082 LOG(
FATAL) <<
"Unknown filter event type.";
4099 std::string
DebugString()
const override {
return "FindOneNeighbor"; }
4103 int64_t objective_min, int64_t objective_max);
4104 void SynchronizeAll(
Solver* solver);
4107 IntVar*
const objective_;
4108 std::unique_ptr<Assignment> reference_assignment_;
4109 std::unique_ptr<Assignment> last_synchronized_assignment_;
4116 bool neighbor_found_;
4118 int64_t solutions_since_last_check_;
4119 int64_t check_period_;
4121 bool has_checked_assignment_ =
false;
4136 : assignment_(assignment),
4138 reference_assignment_(new
Assignment(assignment_)),
4139 filter_assignment_delta_(assignment->solver()->MakeAssignment()),
4141 ls_operator_(ls_operator),
4142 sub_decision_builder_(sub_decision_builder),
4144 original_limit_(limit),
4145 neighbor_found_(false),
4146 filter_manager_(filter_manager),
4147 solutions_since_last_check_(0),
4149 assignment_->solver()->
parameters().check_solution_period()),
4150 last_checked_assignment_(assignment) {
4151 CHECK(
nullptr != assignment);
4152 CHECK(
nullptr != ls_operator);
4156 if (
nullptr == limit) {
4164 VLOG(1) <<
"Disabling neighbor-check skipping outside of first accept.";
4171 VLOG(1) <<
"Disabling neighbor-check skipping for LNS.";
4175 if (!reference_assignment_->HasObjective()) {
4176 reference_assignment_->AddObjective(objective_);
4181 CHECK(
nullptr != solver);
4183 if (original_limit_ !=
nullptr) {
4184 limit_->
Copy(original_limit_);
4191 if (!neighbor_found_) {
4199 SynchronizeAll(solver);
4209 if (sub_decision_builder_) {
4210 restore = solver->
Compose(restore, sub_decision_builder_);
4218 delta->ClearObjective();
4219 deltadelta->
Clear();
4221 if (++counter >= absl::GetFlag(FLAGS_cp_local_search_sync_frequency) &&
4222 pool_->
SyncNeeded(reference_assignment_.get())) {
4225 SynchronizeAll(solver);
4228 bool has_neighbor =
false;
4229 if (!limit_->
Check()) {
4233 ls_operator_, has_neighbor,
delta, deltadelta);
4236 if (has_neighbor && !solver->IsUncheckedSolutionLimitReached()) {
4237 solver->neighbors_ += 1;
4244 const bool mh_filter =
4249 objective_min = objective_->
Min();
4250 objective_max = objective_->
Max();
4252 if (
delta->HasObjective() &&
delta->Objective() == objective_) {
4253 objective_min =
std::max(objective_min,
delta->ObjectiveMin());
4254 objective_max =
std::min(objective_max,
delta->ObjectiveMax());
4256 const bool move_filter = FilterAccept(solver,
delta, deltadelta,
4257 objective_min, objective_max);
4259 ls_operator_, mh_filter && move_filter);
4260 if (!mh_filter || !move_filter) {
4261 if (filter_manager_ !=
nullptr) filter_manager_->
Revert();
4264 solver->filtered_neighbors_ += 1;
4265 if (
delta->HasObjective()) {
4277 const bool check_solution = (solutions_since_last_check_ == 0) ||
4280 !
delta->AreAllElementsBound();
4281 if (has_checked_assignment_) solutions_since_last_check_++;
4282 if (solutions_since_last_check_ >= check_period_) {
4283 solutions_since_last_check_ = 0;
4285 const bool accept = !check_solution || solver->
SolveAndCommit(restore);
4289 solver->accepted_neighbors_ += 1;
4290 if (check_solution) {
4293 assignment_->
Store();
4295 neighbor_found_ =
true;
4296 has_checked_assignment_ =
true;
4310 solver->IncrementUncheckedSolutionCounter();
4312 SynchronizeAll(solver);
4315 neighbor_found_ =
true;
4317 if (filter_manager_ !=
nullptr) filter_manager_->
Revert();
4318 if (check_period_ > 1 && has_checked_assignment_) {
4324 VLOG(1) <<
"Imperfect filtering detected, backtracking to last "
4325 "checked solution and checking all solutions.";
4327 solutions_since_last_check_ = 0;
4329 SynchronizeAll(solver);
4334 if (neighbor_found_) {
4346 has_checked_assignment_ =
true;
4354 SynchronizeAll(solver);
4367 int64_t objective_min,
4368 int64_t objective_max) {
4369 if (filter_manager_ ==
nullptr)
return true;
4371 return filter_manager_->
Accept(monitor,
delta, deltadelta, objective_min,
4377template <
typename Container>
4378void AddDeltaElements(
const Container& old_container,
4379 const Container& new_container, Assignment*
delta) {
4380 for (
const auto& new_element : new_container.elements()) {
4381 const auto var = new_element.
Var();
4382 const auto old_element_ptr = old_container.ElementPtrOrNull(
var);
4383 if (old_element_ptr ==
nullptr || *old_element_ptr != new_element) {
4384 delta->FastAdd(
var)->Copy(new_element);
4389void MakeDelta(
const Assignment* old_assignment,
4390 const Assignment* new_assignment, Assignment*
delta) {
4393 AddDeltaElements(old_assignment->IntVarContainer(),
4394 new_assignment->IntVarContainer(),
delta);
4395 AddDeltaElements(old_assignment->IntervalVarContainer(),
4396 new_assignment->IntervalVarContainer(),
delta);
4397 AddDeltaElements(old_assignment->SequenceVarContainer(),
4398 new_assignment->SequenceVarContainer(),
delta);
4402void FindOneNeighbor::SynchronizeAll(Solver* solver) {
4403 Assignment*
const reference_assignment = reference_assignment_.get();
4405 neighbor_found_ =
false;
4407 solver->GetLocalSearchMonitor()->BeginOperatorStart();
4408 ls_operator_->
Start(reference_assignment);
4409 if (filter_manager_ !=
nullptr) {
4410 Assignment*
delta =
nullptr;
4411 if (last_synchronized_assignment_ ==
nullptr) {
4412 last_synchronized_assignment_ =
4413 absl::make_unique<Assignment>(reference_assignment);
4415 MakeDelta(last_synchronized_assignment_.get(), reference_assignment,
4416 filter_assignment_delta_);
4417 delta = filter_assignment_delta_;
4418 last_synchronized_assignment_->Copy(reference_assignment);
4422 solver->GetLocalSearchMonitor()->EndOperatorStart();
4435 solution_pool_(pool),
4442 return "LocalSearchPhaseParameters";
4449 return sub_decision_builder_;
4453 return filter_manager_;
4457 IntVar*
const objective_;
4469 ls_operator, sub_decision_builder,
4477 ls_operator, sub_decision_builder,
4486 ls_operator, sub_decision_builder,
4487 limit, filter_manager);
4495 sub_decision_builder,
nullptr,
nullptr);
4503 sub_decision_builder, limit,
nullptr);
4512 sub_decision_builder, limit,
4526class NestedSolveDecision :
public Decision {
4529 enum StateType { DECISION_PENDING, DECISION_FAILED, DECISION_FOUND };
4531 NestedSolveDecision(DecisionBuilder*
const db,
bool restore,
4532 const std::vector<SearchMonitor*>& monitors);
4533 NestedSolveDecision(DecisionBuilder*
const db,
bool restore);
4534 ~NestedSolveDecision()
override {}
4535 void Apply(Solver*
const solver)
override;
4536 void Refute(Solver*
const solver)
override;
4537 std::string DebugString()
const override {
return "NestedSolveDecision"; }
4538 int state()
const {
return state_; }
4541 DecisionBuilder*
const db_;
4543 std::vector<SearchMonitor*> monitors_;
4547NestedSolveDecision::NestedSolveDecision(
4548 DecisionBuilder*
const db,
bool restore,
4549 const std::vector<SearchMonitor*>& monitors)
4552 monitors_(monitors),
4553 state_(DECISION_PENDING) {
4554 CHECK(
nullptr != db);
4557NestedSolveDecision::NestedSolveDecision(DecisionBuilder*
const db,
4559 : db_(db), restore_(restore), state_(DECISION_PENDING) {
4560 CHECK(
nullptr != db);
4563void NestedSolveDecision::Apply(Solver*
const solver) {
4564 CHECK(
nullptr != solver);
4566 if (solver->Solve(db_, monitors_)) {
4567 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FOUND));
4569 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FAILED));
4572 if (solver->SolveAndCommit(db_, monitors_)) {
4573 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FOUND));
4575 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FAILED));
4580void NestedSolveDecision::Refute(Solver*
const solver) {}
4591class LocalSearch :
public DecisionBuilder {
4593 LocalSearch(Assignment*
const assignment, IntVar* objective,
4594 SolutionPool*
const pool, LocalSearchOperator*
const ls_operator,
4595 DecisionBuilder*
const sub_decision_builder,
4596 RegularLimit*
const limit,
4597 LocalSearchFilterManager* filter_manager);
4600 LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4601 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4602 LocalSearchOperator*
const ls_operator,
4603 DecisionBuilder*
const sub_decision_builder,
4604 RegularLimit*
const limit,
4605 LocalSearchFilterManager* filter_manager);
4606 LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4607 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4608 DecisionBuilder*
const first_solution_sub_decision_builder,
4609 LocalSearchOperator*
const ls_operator,
4610 DecisionBuilder*
const sub_decision_builder,
4611 RegularLimit*
const limit,
4612 LocalSearchFilterManager* filter_manager);
4613 LocalSearch(
const std::vector<SequenceVar*>& vars, IntVar* objective,
4614 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4615 LocalSearchOperator*
const ls_operator,
4616 DecisionBuilder*
const sub_decision_builder,
4617 RegularLimit*
const limit,
4618 LocalSearchFilterManager* filter_manager);
4619 ~LocalSearch()
override;
4620 Decision* Next(Solver*
const solver)
override;
4621 std::string DebugString()
const override {
return "LocalSearch"; }
4622 void Accept(ModelVisitor*
const visitor)
const override;
4625 void PushFirstSolutionDecision(DecisionBuilder* first_solution);
4626 void PushLocalSearchDecision();
4629 Assignment* assignment_;
4631 SolutionPool*
const pool_;
4632 LocalSearchOperator*
const ls_operator_;
4633 DecisionBuilder*
const first_solution_sub_decision_builder_;
4634 DecisionBuilder*
const sub_decision_builder_;
4635 std::vector<NestedSolveDecision*> nested_decisions_;
4636 int nested_decision_index_;
4637 RegularLimit*
const limit_;
4638 LocalSearchFilterManager*
const filter_manager_;
4642LocalSearch::LocalSearch(Assignment*
const assignment, IntVar* objective,
4643 SolutionPool*
const pool,
4644 LocalSearchOperator*
const ls_operator,
4645 DecisionBuilder*
const sub_decision_builder,
4646 RegularLimit*
const limit,
4647 LocalSearchFilterManager* filter_manager)
4648 : assignment_(nullptr),
4651 ls_operator_(ls_operator),
4652 first_solution_sub_decision_builder_(sub_decision_builder),
4653 sub_decision_builder_(sub_decision_builder),
4654 nested_decision_index_(0),
4656 filter_manager_(filter_manager),
4657 has_started_(false) {
4658 CHECK(
nullptr != assignment);
4659 CHECK(
nullptr != ls_operator);
4660 Solver*
const solver = assignment->solver();
4661 assignment_ = solver->GetOrCreateLocalSearchState();
4662 assignment_->Copy(assignment);
4663 DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment);
4664 PushFirstSolutionDecision(restore);
4665 PushLocalSearchDecision();
4668LocalSearch::LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4669 SolutionPool*
const pool,
4670 DecisionBuilder*
const first_solution,
4671 LocalSearchOperator*
const ls_operator,
4672 DecisionBuilder*
const sub_decision_builder,
4673 RegularLimit*
const limit,
4674 LocalSearchFilterManager* filter_manager)
4675 : assignment_(nullptr),
4678 ls_operator_(ls_operator),
4679 first_solution_sub_decision_builder_(sub_decision_builder),
4680 sub_decision_builder_(sub_decision_builder),
4681 nested_decision_index_(0),
4683 filter_manager_(filter_manager),
4684 has_started_(false) {
4685 CHECK(
nullptr != first_solution);
4686 CHECK(
nullptr != ls_operator);
4687 CHECK(!vars.empty());
4688 Solver*
const solver = vars[0]->solver();
4689 assignment_ = solver->GetOrCreateLocalSearchState();
4690 assignment_->Add(vars);
4691 PushFirstSolutionDecision(first_solution);
4692 PushLocalSearchDecision();
4695LocalSearch::LocalSearch(
4696 const std::vector<IntVar*>& vars, IntVar* objective,
4697 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4698 DecisionBuilder*
const first_solution_sub_decision_builder,
4699 LocalSearchOperator*
const ls_operator,
4700 DecisionBuilder*
const sub_decision_builder, RegularLimit*
const limit,
4701 LocalSearchFilterManager* filter_manager)
4702 : assignment_(nullptr),
4705 ls_operator_(ls_operator),
4706 first_solution_sub_decision_builder_(first_solution_sub_decision_builder),
4707 sub_decision_builder_(sub_decision_builder),
4708 nested_decision_index_(0),
4710 filter_manager_(filter_manager),
4711 has_started_(false) {
4712 CHECK(
nullptr != first_solution);
4713 CHECK(
nullptr != ls_operator);
4714 CHECK(!vars.empty());
4715 Solver*
const solver = vars[0]->solver();
4716 assignment_ = solver->GetOrCreateLocalSearchState();
4717 assignment_->Add(vars);
4718 PushFirstSolutionDecision(first_solution);
4719 PushLocalSearchDecision();
4722LocalSearch::LocalSearch(
const std::vector<SequenceVar*>& vars,
4723 IntVar* objective, SolutionPool*
const pool,
4724 DecisionBuilder*
const first_solution,
4725 LocalSearchOperator*
const ls_operator,
4726 DecisionBuilder*
const sub_decision_builder,
4727 RegularLimit*
const limit,
4728 LocalSearchFilterManager* filter_manager)
4729 : assignment_(nullptr),
4732 ls_operator_(ls_operator),
4733 first_solution_sub_decision_builder_(sub_decision_builder),
4734 sub_decision_builder_(sub_decision_builder),
4735 nested_decision_index_(0),
4737 filter_manager_(filter_manager),
4738 has_started_(false) {
4739 CHECK(
nullptr != first_solution);
4740 CHECK(
nullptr != ls_operator);
4741 CHECK(!vars.empty());
4742 Solver*
const solver = vars[0]->solver();
4743 assignment_ = solver->GetOrCreateLocalSearchState();
4744 assignment_->Add(vars);
4745 PushFirstSolutionDecision(first_solution);
4746 PushLocalSearchDecision();
4749LocalSearch::~LocalSearch() {}
4752void LocalSearch::Accept(ModelVisitor*
const visitor)
const {
4753 DCHECK(assignment_ !=
nullptr);
4756 const std::vector<IntVarElement>& elements =
4758 if (!elements.empty()) {
4759 std::vector<IntVar*> vars;
4760 for (
const IntVarElement& elem : elements) {
4761 vars.push_back(elem.Var());
4766 const std::vector<IntervalVarElement>& interval_elements =
4768 if (!interval_elements.empty()) {
4769 std::vector<IntervalVar*> interval_vars;
4770 for (
const IntervalVarElement& elem : interval_elements) {
4771 interval_vars.push_back(elem.Var());
4784Decision* LocalSearch::Next(Solver*
const solver) {
4785 CHECK(
nullptr != solver);
4786 CHECK_LT(0, nested_decisions_.size());
4787 if (!has_started_) {
4788 nested_decision_index_ = 0;
4789 solver->SaveAndSetValue(&has_started_,
true);
4790 }
else if (nested_decision_index_ < 0) {
4793 NestedSolveDecision* decision = nested_decisions_[nested_decision_index_];
4794 const int state = decision->state();
4796 case NestedSolveDecision::DECISION_FAILED: {
4800 ls_operator_->
Reset();
4802 nested_decision_index_ = -1;
4807 case NestedSolveDecision::DECISION_PENDING: {
4810 const int32_t kLocalSearchBalancedTreeDepth = 32;
4811 const int depth = solver->SearchDepth();
4812 if (depth < kLocalSearchBalancedTreeDepth) {
4813 return solver->balancing_decision();
4815 if (depth > kLocalSearchBalancedTreeDepth) {
4820 case NestedSolveDecision::DECISION_FOUND: {
4822 if (nested_decision_index_ + 1 < nested_decisions_.size()) {
4823 ++nested_decision_index_;
4828 LOG(ERROR) <<
"Unknown local search state";
4835void LocalSearch::PushFirstSolutionDecision(DecisionBuilder* first_solution) {
4836 CHECK(first_solution);
4837 Solver*
const solver = assignment_->
solver();
4839 DecisionBuilder* first_solution_and_store = solver->Compose(
4840 solver->MakeProfiledDecisionBuilderWrapper(first_solution),
4841 first_solution_sub_decision_builder_, store);
4842 std::vector<SearchMonitor*> monitor;
4843 monitor.push_back(
limit_);
4844 nested_decisions_.push_back(solver->RevAlloc(
4845 new NestedSolveDecision(first_solution_and_store,
false, monitor)));
4848void LocalSearch::PushLocalSearchDecision() {
4849 Solver*
const solver = assignment_->
solver();
4850 DecisionBuilder* find_neighbors = solver->
RevAlloc(
4851 new FindOneNeighbor(assignment_,
objective_, pool_, ls_operator_,
4852 sub_decision_builder_,
limit_, filter_manager_));
4853 nested_decisions_.push_back(
4854 solver->RevAlloc(
new NestedSolveDecision(find_neighbors,
false)));
4857class DefaultSolutionPool :
public SolutionPool {
4859 DefaultSolutionPool() {}
4861 ~DefaultSolutionPool()
override {}
4863 void Initialize(Assignment*
const assignment)
override {
4864 reference_assignment_ = absl::make_unique<Assignment>(assignment);
4867 void RegisterNewSolution(Assignment*
const assignment)
override {
4868 reference_assignment_->CopyIntersection(assignment);
4871 void GetNextSolution(Assignment*
const assignment)
override {
4872 assignment->CopyIntersection(reference_assignment_.get());
4875 bool SyncNeeded(Assignment*
const local_assignment)
override {
return false; }
4877 std::string DebugString()
const override {
return "DefaultSolutionPool"; }
4880 std::unique_ptr<Assignment> reference_assignment_;
4885 return RevAlloc(
new DefaultSolutionPool());
4912 first_solution, first_solution_sub_decision_builder,
4918 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)
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
const std::vector< int > & ChangedLoops() const
void ChangePath(int path, const std::vector< ChainBounds > &chains)
void ChangeLoops(const std::vector< int > &new_loops)
NodeRange Nodes(int path) 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
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.