29 #include "absl/container/flat_hash_map.h"
30 #include "absl/container/flat_hash_set.h"
31 #include "absl/memory/memory.h"
32 #include "absl/random/distributions.h"
33 #include "absl/random/random.h"
34 #include "absl/strings/str_cat.h"
48 "Frequency of checks for better solutions in the solution pool.");
51 "Size of TSPs solved in the TSPOpt operator.");
54 "Size of TSPs solved in the TSPLns operator.");
56 ABSL_FLAG(
bool, cp_use_empty_path_symmetry_breaker,
true,
57 "If true, equivalent empty paths are removed from the neighborhood "
71 Assignment* deltadelta);
81 CHECK(
delta !=
nullptr);
83 <<
delta->DebugString() <<
"), deltadelta=("
84 << (deltadelta ? deltadelta->
DebugString() : std::string(
"nullptr"));
112 for (
int candidate : fragment_) {
126 fragment_.push_back(
index);
137 class SimpleLns :
public BaseLns {
139 SimpleLns(
const std::vector<IntVar*>& vars,
int number_of_variables)
140 :
BaseLns(vars), index_(0), number_of_variables_(number_of_variables) {
141 CHECK_GT(number_of_variables_, 0);
143 ~SimpleLns()
override {}
144 void InitFragments()
override { index_ = 0; }
145 bool NextFragment()
override;
146 std::string DebugString()
const override {
return "SimpleLns"; }
150 const int number_of_variables_;
153 bool SimpleLns::NextFragment() {
154 const int size = Size();
156 for (
int i = index_; i < index_ + number_of_variables_; ++i) {
157 AppendToFragment(i % size);
169 class RandomLns :
public BaseLns {
171 RandomLns(
const std::vector<IntVar*>& vars,
int number_of_variables,
173 : BaseLns(vars), rand_(seed), number_of_variables_(number_of_variables) {
174 CHECK_GT(number_of_variables_, 0);
175 CHECK_LE(number_of_variables_, Size());
177 ~RandomLns()
override {}
178 bool NextFragment()
override;
180 std::string DebugString()
const override {
return "RandomLns"; }
184 const int number_of_variables_;
187 bool RandomLns::NextFragment() {
188 DCHECK_GT(Size(), 0);
189 for (
int i = 0; i < number_of_variables_; ++i) {
190 AppendToFragment(absl::Uniform<int>(rand_, 0, Size()));
197 const std::vector<IntVar*>& vars,
int number_of_variables) {
202 const std::vector<IntVar*>& vars,
int number_of_variables, int32_t seed) {
203 return RevAlloc(
new RandomLns(vars, number_of_variables, seed));
214 MoveTowardTargetLS(
const std::vector<IntVar*>& variables,
215 const std::vector<int64_t>& target_values)
217 target_(target_values),
221 variable_index_(Size() - 1) {
222 CHECK_EQ(target_values.size(), variables.size()) <<
"Illegal arguments.";
225 ~MoveTowardTargetLS()
override {}
227 std::string DebugString()
const override {
return "MoveTowardTargetLS"; }
231 bool MakeOneNeighbor()
override {
232 while (num_var_since_last_start_ < Size()) {
233 ++num_var_since_last_start_;
234 variable_index_ = (variable_index_ + 1) % Size();
235 const int64_t target_value = target_.at(variable_index_);
236 const int64_t current_value = OldValue(variable_index_);
237 if (current_value != target_value) {
238 SetValue(variable_index_, target_value);
246 void OnStart()
override {
258 CHECK_GE(variable_index_, 0);
259 CHECK_LT(variable_index_, Size());
260 num_var_since_last_start_ = 0;
264 const std::vector<int64_t> target_;
267 int64_t variable_index_;
270 int64_t num_var_since_last_start_;
276 typedef std::vector<IntVarElement> Elements;
279 std::vector<IntVar*> vars;
280 std::vector<int64_t> values;
283 for (
const auto& it : elements) {
284 vars.push_back(it.Var());
285 values.push_back(it.Value());
291 const std::vector<IntVar*>& variables,
292 const std::vector<int64_t>& target_values) {
293 return RevAlloc(
new MoveTowardTargetLS(variables, target_values));
304 const int size =
Size();
305 while (index_ < size) {
314 void ChangeValue::OnStart() { index_ = 0; }
319 class IncrementValue :
public ChangeValue {
321 explicit IncrementValue(
const std::vector<IntVar*>& vars)
322 : ChangeValue(vars) {}
323 ~IncrementValue()
override {}
324 int64_t ModifyValue(int64_t
index, int64_t
value)
override {
328 std::string DebugString()
const override {
return "IncrementValue"; }
333 class DecrementValue :
public ChangeValue {
335 explicit DecrementValue(
const std::vector<IntVar*>& vars)
336 : ChangeValue(vars) {}
337 ~DecrementValue()
override {}
338 int64_t ModifyValue(int64_t
index, int64_t
value)
override {
342 std::string DebugString()
const override {
return "DecrementValue"; }
349 const std::vector<IntVar*>& path_vars,
352 number_of_nexts_(next_vars.size()),
353 ignore_path_vars_(path_vars.empty()),
354 next_base_to_increment_(iteration_parameters.number_of_base_nodes),
355 base_nodes_(iteration_parameters.number_of_base_nodes),
356 base_alternatives_(iteration_parameters.number_of_base_nodes),
357 base_sibling_alternatives_(iteration_parameters.number_of_base_nodes),
358 end_nodes_(iteration_parameters.number_of_base_nodes),
359 base_paths_(iteration_parameters.number_of_base_nodes),
360 just_started_(false),
362 iteration_parameters_(std::move(iteration_parameters)),
363 optimal_paths_enabled_(false),
364 alternative_index_(next_vars.size(), -1) {
369 path_basis_.push_back(0);
370 for (
int i = 1; i < base_nodes_.size(); ++i) {
373 if ((path_basis_.size() > 2) ||
374 (!next_vars.empty() && !next_vars.back()
377 .skip_locally_optimal_paths())) {
384 void PathOperator::OnStart() {
385 optimal_paths_enabled_ =
false;
386 InitializeBaseNodes();
387 InitializeAlternatives();
392 while (IncrementPosition()) {
416 int64_t destination) {
417 if (destination == before_chain || destination == chain_end)
return false;
420 const int64_t destination_path =
Path(destination);
421 const int64_t after_chain =
Next(chain_end);
422 SetNext(chain_end,
Next(destination), destination_path);
424 int current = destination;
426 while (current != chain_end) {
432 SetNext(destination,
Next(before_chain), destination_path);
434 SetNext(before_chain, after_chain,
Path(before_chain));
439 int64_t* chain_last) {
441 int64_t path =
Path(before_chain);
442 int64_t current =
Next(before_chain);
443 if (current == after_chain) {
446 int64_t current_next =
Next(current);
447 SetNext(current, after_chain, path);
448 while (current_next != after_chain) {
449 const int64_t
next =
Next(current_next);
450 SetNext(current_next, current, path);
451 current = current_next;
454 SetNext(before_chain, current, path);
455 *chain_last = current;
463 int64_t destination_path =
Path(destination);
464 SetNext(node,
Next(destination), destination_path);
465 SetNext(destination, node, destination_path);
472 const int64_t kNoPath = -1;
475 const int64_t after_chain =
Next(chain_end);
476 int64_t current =
Next(before_chain);
477 while (current != after_chain) {
479 SetNext(current, current, kNoPath);
482 SetNext(before_chain, after_chain,
Path(before_chain));
489 if (active == inactive)
return false;
490 const int64_t prev =
Prev(active);
494 bool PathOperator::IncrementPosition() {
497 if (!just_started_) {
498 const int number_of_paths = path_starts_.size();
504 int last_restarted = base_node_size;
505 for (
int i = base_node_size - 1; i >= 0; --i) {
509 const int sibling_alternative_index =
511 if (sibling_alternative_index >= 0) {
512 if (base_sibling_alternatives_[i] <
513 alternative_sets_[sibling_alternative_index].size() - 1) {
514 ++base_sibling_alternatives_[i];
517 base_sibling_alternatives_[i] = 0;
520 const int alternative_index = alternative_index_[base_nodes_[i]];
521 if (alternative_index >= 0) {
522 if (base_alternatives_[i] <
523 alternative_sets_[alternative_index].size() - 1) {
524 ++base_alternatives_[i];
527 base_alternatives_[i] = 0;
528 base_sibling_alternatives_[i] = 0;
531 base_alternatives_[i] = 0;
532 base_sibling_alternatives_[i] = 0;
533 base_nodes_[i] =
OldNext(base_nodes_[i]);
538 base_alternatives_[i] = 0;
539 base_sibling_alternatives_[i] = 0;
553 for (
int i = last_restarted; i < base_node_size; ++i) {
554 base_alternatives_[i] = 0;
555 base_sibling_alternatives_[i] = 0;
558 if (last_restarted > 0) {
564 if (optimal_paths_enabled_ &&
566 if (path_basis_.size() > 1) {
567 for (
int i = 1; i < path_basis_.size(); ++i) {
577 std::vector<int> current_starts(base_node_size);
578 for (
int i = 0; i < base_node_size; ++i) {
583 optimal_paths_enabled_ =
true;
585 for (
int i = base_node_size - 1; i >= 0; --i) {
586 const int next_path_index = base_paths_[i] + 1;
587 if (next_path_index < number_of_paths) {
588 base_paths_[i] = next_path_index;
589 base_alternatives_[i] = 0;
590 base_sibling_alternatives_[i] = 0;
591 base_nodes_[i] = path_starts_[next_path_index];
597 base_alternatives_[i] = 0;
598 base_sibling_alternatives_[i] = 0;
599 base_nodes_[i] = path_starts_[0];
605 if (path_basis_.size() > 1) {
606 for (
int j = 1; j < path_basis_.size(); ++j) {
608 path_basis_[j - 1])] +
622 if (!CheckEnds())
return false;
624 for (
int i = 0; i < base_node_size; ++i) {
630 if (stop)
return false;
633 just_started_ =
false;
639 void PathOperator::InitializePathStarts() {
647 has_prevs[
next] =
true;
652 if (optimal_paths_.empty() &&
685 std::vector<int64_t> new_path_starts;
686 const bool use_empty_path_symmetry_breaker =
687 absl::GetFlag(FLAGS_cp_use_empty_path_symmetry_breaker);
697 new_path_starts.push_back(i);
705 std::vector<int> node_paths(max_next + 1, -1);
706 for (
int i = 0; i < path_starts_.size(); ++i) {
707 int node = path_starts_[i];
709 node_paths[node] = i;
712 node_paths[node] = i;
716 base_alternatives_[j] = 0;
717 base_sibling_alternatives_[j] = 0;
718 if (
IsInactive(base_nodes_[j]) || node_paths[base_nodes_[j]] == -1) {
721 base_nodes_[j] = path_starts_[base_paths_[j]];
723 base_paths_[j] = node_paths[base_nodes_[j]];
730 absl::flat_hash_set<int> found_bases;
731 for (
int i = 0; i < path_starts_.size(); ++i) {
732 int index = new_index;
734 while (
index < new_path_starts.size() &&
735 new_path_starts[
index] < path_starts_[i]) {
738 const bool found = (
index < new_path_starts.size() &&
739 new_path_starts[
index] == path_starts_[i]);
744 if (base_paths_[j] == i && !found_bases.contains(j)) {
745 found_bases.insert(j);
746 base_paths_[j] = new_index;
750 base_nodes_[j] = new_path_starts[new_index];
756 path_starts_.swap(new_path_starts);
760 path_ends_.reserve(path_starts_.size());
761 for (
const int start_node : path_starts_) {
762 int64_t node = start_node;
764 path_ends_.push_back(node);
768 void PathOperator::InitializeInactives() {
771 inactives_.push_back(
OldNext(i) == i);
775 void PathOperator::InitializeBaseNodes() {
777 InitializeInactives();
778 InitializePathStarts();
784 base_nodes_[i] = path_starts_[0];
786 first_start_ =
false;
790 int64_t base_node = base_nodes_[i];
792 base_node = path_starts_[base_paths_[i]];
793 base_nodes_[i] = base_node;
795 end_nodes_[i] = base_node;
801 !OnSamePath(base_nodes_[i - 1], base_nodes_[i])) {
802 const int64_t base_node = base_nodes_[i - 1];
803 base_nodes_[i] = base_node;
804 end_nodes_[i] = base_node;
805 base_paths_[i] = base_paths_[i - 1];
809 base_alternatives_[i] = 0;
810 base_sibling_alternatives_[i] = 0;
812 just_started_ =
true;
815 void PathOperator::InitializeAlternatives() {
816 active_in_alternative_set_.resize(alternative_sets_.size(), -1);
817 for (
int i = 0; i < alternative_sets_.size(); ++i) {
818 const int64_t current_active = active_in_alternative_set_[i];
819 if (current_active >= 0 && !
IsInactive(current_active))
continue;
820 for (int64_t
index : alternative_sets_[i]) {
822 active_in_alternative_set_[i] =
index;
829 bool PathOperator::OnSamePath(int64_t node1, int64_t node2)
const {
852 int64_t exclude)
const {
853 if (before_chain == chain_end || before_chain == exclude)
return false;
854 int64_t current = before_chain;
856 while (current != chain_end) {
863 current =
Next(current);
865 if (current == exclude) {
885 const std::vector<IntVar*>& secondary_vars,
886 std::function<
int(int64_t)> start_empty_path_class)
888 std::move(start_empty_path_class)),
907 void OnNodeInitialization()
override { last_ = -1; }
915 if (last_base_ !=
BaseNode(0) || last_ == -1) {
927 && last_ != chain_last) {
933 const int64_t to_move =
Next(last_);
956 const std::vector<IntVar*>& secondary_vars,
const std::string&
name,
957 std::function<
int(int64_t)> start_empty_path_class,
958 int64_t chain_length = 1LL,
bool single_path =
false)
960 std::move(start_empty_path_class)),
961 chain_length_(chain_length),
962 single_path_(single_path),
964 CHECK_GT(chain_length_, 0);
967 const std::vector<IntVar*>& secondary_vars,
968 std::function<
int(int64_t)> start_empty_path_class,
969 int64_t chain_length = 1LL,
bool single_path =
false)
971 absl::StrCat(
"Relocate<", chain_length,
">"),
972 std::move(start_empty_path_class), chain_length, single_path) {
987 const int64_t chain_length_;
988 const bool single_path_;
989 const std::string name_;
994 const int64_t destination =
BaseNode(1);
996 const int64_t before_chain =
BaseNode(0);
997 int64_t chain_end = before_chain;
998 for (
int i = 0; i < chain_length_; ++i) {
999 if (
IsPathEnd(chain_end) || chain_end == destination) {
1002 chain_end =
Next(chain_end);
1005 MoveChain(before_chain, chain_end, destination);
1021 const std::vector<IntVar*>& secondary_vars,
1022 std::function<
int(int64_t)> start_empty_path_class)
1024 std::move(start_empty_path_class)) {}
1032 const int64_t prev_node0 =
BaseNode(0);
1033 const int64_t node0 =
Next(prev_node0);
1035 const int64_t prev_node1 =
BaseNode(1);
1036 const int64_t node1 =
Next(prev_node1);
1038 const bool ok =
MoveChain(prev_node0, node0, prev_node1);
1057 const std::vector<IntVar*>& secondary_vars,
1058 std::function<
int(int64_t)> start_empty_path_class)
1060 std::move(start_empty_path_class)) {}
1070 if (start1 == start0)
return false;
1072 if (node0 == start0)
return false;
1074 if (node1 == start1)
return false;
1077 if (start0 < start1) {
1085 const int first1 =
Next(start1);
1116 const std::vector<IntVar*>& vars,
1117 const std::vector<IntVar*>& secondary_vars,
int number_of_base_nodes,
1118 std::function<
int(int64_t)> start_empty_path_class)
1119 :
PathOperator(vars, secondary_vars, number_of_base_nodes, false, false,
1120 std::move(start_empty_path_class)),
1131 void OnNodeInitialization()
override;
1136 void BaseInactiveNodeToPathOperator::OnNodeInitialization() {
1137 for (
int i = 0; i <
Size(); ++i) {
1143 inactive_node_ =
Size();
1147 while (inactive_node_ <
Size()) {
1170 const std::vector<IntVar*>& secondary_vars,
1171 std::function<
int(int64_t)> start_empty_path_class)
1173 std::move(start_empty_path_class)) {}
1177 std::string
DebugString()
const override {
return "MakeActiveOperator"; }
1196 const std::vector<IntVar*>& vars,
1197 const std::vector<IntVar*>& secondary_vars,
1198 std::function<
int(int64_t)> start_empty_path_class)
1200 std::move(start_empty_path_class)) {}
1203 const int64_t before_node_to_move =
BaseNode(1);
1204 const int64_t node =
Next(before_node_to_move);
1211 return "RelocateAndMakeActiveOpertor";
1224 const std::vector<IntVar*>& secondary_vars,
1225 std::function<
int(int64_t)> start_empty_path_class)
1227 std::move(start_empty_path_class)) {}
1232 return "MakeActiveAndRelocateOperator";
1237 const int64_t before_chain =
BaseNode(1);
1238 const int64_t chain_end =
Next(before_chain);
1239 const int64_t destination =
BaseNode(0);
1241 MoveChain(before_chain, chain_end, destination) &&
1256 const std::vector<IntVar*>& secondary_vars,
1257 std::function<
int(int64_t)> start_empty_path_class)
1259 std::move(start_empty_path_class)) {}
1266 std::string
DebugString()
const override {
return "MakeInactiveOperator"; }
1280 const std::vector<IntVar*>& vars,
1281 const std::vector<IntVar*>& secondary_vars,
1282 std::function<
int(int64_t)> start_empty_path_class)
1284 std::move(start_empty_path_class)) {}
1287 const int64_t destination =
BaseNode(1);
1288 const int64_t before_to_move =
BaseNode(0);
1289 const int64_t node_to_inactivate =
Next(destination);
1290 if (node_to_inactivate == before_to_move ||
IsPathEnd(node_to_inactivate) ||
1294 const int64_t node =
Next(before_to_move);
1299 return "RelocateAndMakeInactiveOperator";
1315 const std::vector<IntVar*>& secondary_vars,
1316 std::function<
int(int64_t)> start_empty_path_class)
1318 std::move(start_empty_path_class)) {}
1325 return "MakeChainInactiveOperator";
1352 const std::vector<IntVar*>& secondary_vars,
1353 std::function<
int(int64_t)> start_empty_path_class)
1355 std::move(start_empty_path_class)) {}
1359 std::string
DebugString()
const override {
return "SwapActiveOperator"; }
1384 const std::vector<IntVar*>& secondary_vars,
1385 std::function<
int(int64_t)> start_empty_path_class)
1387 std::move(start_empty_path_class)) {}
1392 return "ExtendedSwapActiveOperator";
1399 if (
Next(base0) == base1) {
1417 TSPOpt(
const std::vector<IntVar*>& vars,
1418 const std::vector<IntVar*>& secondary_vars,
1426 std::vector<std::vector<int64_t>> cost_;
1428 hamiltonian_path_solver_;
1430 const int chain_length_;
1434 const std::vector<IntVar*>& secondary_vars,
1436 :
PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1437 hamiltonian_path_solver_(cost_),
1439 chain_length_(chain_length) {}
1442 std::vector<int64_t>
nodes;
1444 for (
int i = 0; i < chain_length_ + 1; ++i) {
1445 nodes.push_back(chain_end);
1449 chain_end =
Next(chain_end);
1451 if (
nodes.size() <= 3) {
1455 const int size =
nodes.size() - 1;
1457 for (
int i = 0; i < size; ++i) {
1458 cost_[i].resize(size);
1459 cost_[i][0] = evaluator_(
nodes[i],
nodes[size], chain_path);
1460 for (
int j = 1; j < size; ++j) {
1461 cost_[i][j] = evaluator_(
nodes[i],
nodes[j], chain_path);
1465 std::vector<PathNodeIndex> path;
1467 CHECK_EQ(size + 1, path.size());
1468 for (
int i = 0; i < size - 1; ++i) {
1485 TSPLns(
const std::vector<IntVar*>& vars,
1486 const std::vector<IntVar*>& secondary_vars,
1497 void OnNodeInitialization()
override {
1499 has_long_enough_paths_ =
Size() != 0;
1502 std::vector<std::vector<int64_t>> cost_;
1503 HamiltonianPathSolver<int64_t, std::vector<std::vector<int64_t>>>
1504 hamiltonian_path_solver_;
1506 const int tsp_size_;
1508 bool has_long_enough_paths_;
1512 const std::vector<IntVar*>& secondary_vars,
1514 :
PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1515 hamiltonian_path_solver_(cost_),
1517 tsp_size_(tsp_size),
1519 has_long_enough_paths_(true) {
1520 CHECK_GE(tsp_size_, 0);
1521 cost_.resize(tsp_size_);
1522 for (
int i = 0; i < tsp_size_; ++i) {
1523 cost_[i].resize(tsp_size_);
1528 while (has_long_enough_paths_) {
1529 has_long_enough_paths_ =
false;
1539 const int64_t base_node =
BaseNode(0);
1540 std::vector<int64_t>
nodes;
1542 nodes.push_back(node);
1544 if (
nodes.size() <= tsp_size_) {
1547 has_long_enough_paths_ =
true;
1550 absl::flat_hash_set<int64_t> breaks_set;
1552 breaks_set.insert(base_node);
1553 CHECK(!
nodes.empty());
1554 while (breaks_set.size() < tsp_size_) {
1555 breaks_set.insert(
nodes[absl::Uniform<int>(rand_, 0,
nodes.size())]);
1557 CHECK_EQ(breaks_set.size(), tsp_size_);
1562 std::vector<int> breaks;
1563 std::vector<int64_t> meta_node_costs;
1566 int64_t node_path =
Path(node);
1569 if (breaks_set.contains(node)) {
1570 breaks.push_back(node);
1571 meta_node_costs.push_back(
cost);
1578 meta_node_costs[0] +=
cost;
1579 CHECK_EQ(breaks.size(), tsp_size_);
1581 CHECK_EQ(meta_node_costs.size(), tsp_size_);
1582 for (
int i = 0; i < tsp_size_; ++i) {
1584 CapAdd(meta_node_costs[i],
1585 evaluator_(breaks[i],
Next(breaks[tsp_size_ - 1]), node_path));
1586 for (
int j = 1; j < tsp_size_; ++j) {
1588 CapAdd(meta_node_costs[i],
1589 evaluator_(breaks[i],
Next(breaks[j - 1]), node_path));
1595 std::vector<PathNodeIndex> path;
1597 bool nochange =
true;
1598 for (
int i = 0; i < path.size() - 1; ++i) {
1607 CHECK_EQ(0, path[path.size() - 1]);
1608 for (
int i = 0; i < tsp_size_ - 1; ++i) {
1609 SetNext(breaks[path[i]],
OldNext(breaks[path[i + 1] - 1]), node_path);
1611 SetNext(breaks[path[tsp_size_ - 1]],
OldNext(breaks[tsp_size_ - 1]),
1632 virtual std::string
DebugString()
const {
return "NearestNeighbors"; }
1635 void ComputeNearest(
int row);
1637 std::vector<std::vector<int>> neighbors_;
1649 path_operator_(path_operator),
1651 initialized_(false) {}
1655 if (!initialized_) {
1656 initialized_ =
true;
1658 neighbors_.push_back(std::vector<int>());
1665 return neighbors_[
index];
1668 void NearestNeighbors::ComputeNearest(
int row) {
1670 const int path = path_operator_.
Path(
row);
1672 const int64_t var_min =
var->Min();
1673 const int var_size =
var->Max() - var_min + 1;
1674 using ValuedIndex = std::pair<int64_t ,
int >;
1675 std::vector<ValuedIndex> neighbors(var_size);
1676 for (
int i = 0; i < var_size; ++i) {
1677 const int index = i + var_min;
1678 neighbors[i] = std::make_pair(evaluator_(
row,
index, path),
index);
1680 if (var_size > size_) {
1681 std::nth_element(neighbors.begin(), neighbors.begin() + size_ - 1,
1686 for (
int i = 0; i <
std::min(size_, var_size); ++i) {
1687 neighbors_[
row].push_back(neighbors[i].second);
1689 std::sort(neighbors_[
row].begin(), neighbors_[
row].
end());
1695 const std::vector<IntVar*>& secondary_vars,
1703 void OnNodeInitialization()
override;
1705 static const int kNeighbors;
1707 bool InFromOut(int64_t in_i, int64_t in_j, int64_t* out, int64_t* gain);
1711 absl::flat_hash_set<int64_t> marked_;
1720 const std::vector<IntVar*>& secondary_vars,
1722 :
PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1724 neighbors_(evaluator, *this, kNeighbors),
1729 void LinKernighan::OnNodeInitialization() { neighbors_.
Initialize(); }
1734 int64_t path =
Path(node);
1735 int64_t base = node;
1740 marked_.insert(node);
1742 if (!InFromOut(node,
next, &out, &gain))
return false;
1743 marked_.insert(
next);
1744 marked_.insert(out);
1745 const int64_t node1 = out;
1747 const int64_t next1 =
Next(node1);
1749 if (!InFromOut(node1, next1, &out, &gain))
return false;
1750 marked_.insert(next1);
1751 marked_.insert(out);
1755 const int64_t next_out =
Next(out);
1756 const int64_t in_cost = evaluator_(node, next_out, path);
1757 const int64_t out_cost = evaluator_(out, next_out, path);
1758 if (
CapAdd(
CapSub(gain, in_cost), out_cost) > 0)
return true;
1765 while (InFromOut(node,
next, &out, &gain)) {
1766 marked_.insert(
next);
1767 marked_.insert(out);
1772 int64_t in_cost = evaluator_(base, chain_last, path);
1773 int64_t out_cost = evaluator_(chain_last, out, path);
1789 const int LinKernighan::kNeighbors = 5 + 1;
1791 bool LinKernighan::InFromOut(int64_t in_i, int64_t in_j, int64_t* out,
1793 const std::vector<int>& nexts = neighbors_.
Neighbors(in_j);
1795 int64_t path =
Path(in_i);
1796 int64_t out_cost = evaluator_(in_i, in_j, path);
1797 const int64_t current_gain =
CapAdd(*gain, out_cost);
1798 for (
int k = 0; k < nexts.size(); ++k) {
1799 const int64_t
next = nexts[k];
1801 int64_t in_cost = evaluator_(in_j,
next, path);
1802 int64_t new_gain =
CapSub(current_gain, in_cost);
1803 if (new_gain > 0 &&
next !=
Next(in_j) && marked_.count(in_j) == 0 &&
1804 marked_.count(
next) == 0) {
1805 if (best_gain < new_gain) {
1807 best_gain = new_gain;
1825 const std::vector<IntVar*>& secondary_vars,
int number_of_chunks,
1826 int chunk_size,
bool unactive_fragments)
1827 :
PathOperator(vars, secondary_vars, number_of_chunks, true, true,
1829 number_of_chunks_(number_of_chunks),
1830 chunk_size_(chunk_size),
1831 unactive_fragments_(unactive_fragments) {
1832 CHECK_GE(chunk_size_, 0);
1841 inline bool ChainsAreFullPaths()
const {
return chunk_size_ == 0; }
1842 void DeactivateChain(int64_t node);
1843 void DeactivateUnactives();
1845 const int number_of_chunks_;
1846 const int chunk_size_;
1847 const bool unactive_fragments_;
1851 if (ChainsAreFullPaths()) {
1855 for (
int i = 0; i < number_of_chunks_; ++i) {
1859 for (
int i = 0; i < number_of_chunks_; ++i) {
1862 DeactivateUnactives();
1866 void PathLns::DeactivateChain(int64_t node) {
1867 for (
int i = 0, current = node;
1868 (ChainsAreFullPaths() || i < chunk_size_) && !
IsPathEnd(current);
1869 ++i, current =
Next(current)) {
1877 void PathLns::DeactivateUnactives() {
1878 if (unactive_fragments_) {
1879 for (
int i = 0; i <
Size(); ++i) {
1895 : operator_(op), limit_(limit), next_neighborhood_calls_(0) {
1896 CHECK(op !=
nullptr);
1901 next_neighborhood_calls_ = 0;
1902 operator_->
Start(assignment);
1906 if (next_neighborhood_calls_ >= limit_) {
1909 ++next_neighborhood_calls_;
1915 std::string
DebugString()
const override {
return "NeighborhoodLimit"; }
1919 const int64_t limit_;
1920 int64_t next_neighborhood_calls_;
1933 CompoundOperator(std::vector<LocalSearchOperator*> operators,
1934 std::function<int64_t(
int,
int)> evaluator);
1935 ~CompoundOperator()
override {}
1936 void Reset()
override;
1937 void Start(
const Assignment* assignment)
override;
1938 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
1939 bool HasFragments()
const override {
return has_fragments_; }
1940 bool HoldsDelta()
const override {
return true; }
1942 std::string DebugString()
const override {
1943 return operators_.empty()
1945 : operators_[operator_indices_[index_]]->DebugString();
1947 const LocalSearchOperator* Self()
const override {
1948 return operators_.empty() ? this
1949 : operators_[operator_indices_[index_]]->Self();
1953 class OperatorComparator {
1955 OperatorComparator(std::function<int64_t(
int,
int)> evaluator,
1956 int active_operator)
1957 :
evaluator_(std::move(evaluator)), active_operator_(active_operator) {}
1958 bool operator()(
int lhs,
int rhs)
const {
1959 const int64_t lhs_value = Evaluate(lhs);
1960 const int64_t rhs_value = Evaluate(rhs);
1961 return lhs_value < rhs_value || (lhs_value == rhs_value && lhs < rhs);
1965 int64_t Evaluate(
int operator_index)
const {
1966 return evaluator_(active_operator_, operator_index);
1970 const int active_operator_;
1974 std::vector<LocalSearchOperator*> operators_;
1975 std::vector<int> operator_indices_;
1977 Bitset64<> started_;
1978 const Assignment* start_assignment_;
1979 bool has_fragments_;
1982 CompoundOperator::CompoundOperator(std::vector<LocalSearchOperator*> operators,
1983 std::function<int64_t(
int,
int)> evaluator)
1985 operators_(std::move(operators)),
1987 started_(operators_.size()),
1988 start_assignment_(nullptr),
1989 has_fragments_(false) {
1990 operators_.erase(std::remove(operators_.begin(), operators_.end(),
nullptr),
1992 operator_indices_.resize(operators_.size());
1993 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
1994 for (LocalSearchOperator*
const op : operators_) {
1995 if (op->HasFragments()) {
1996 has_fragments_ =
true;
2002 void CompoundOperator::Reset() {
2003 for (LocalSearchOperator*
const op : operators_) {
2008 void CompoundOperator::Start(
const Assignment* assignment) {
2009 start_assignment_ = assignment;
2011 if (!operators_.empty()) {
2012 OperatorComparator comparator(
evaluator_, operator_indices_[index_]);
2013 std::sort(operator_indices_.begin(), operator_indices_.end(), comparator);
2018 bool CompoundOperator::MakeNextNeighbor(Assignment*
delta,
2019 Assignment* deltadelta) {
2020 if (!operators_.empty()) {
2024 const int64_t operator_index = operator_indices_[index_];
2025 if (!started_[operator_index]) {
2026 operators_[operator_index]->Start(start_assignment_);
2027 started_.
Set(operator_index);
2029 if (!operators_[operator_index]->HoldsDelta()) {
2032 if (operators_[operator_index]->MakeNextNeighbor(
delta, deltadelta)) {
2037 if (index_ == operators_.size()) {
2040 }
while (index_ != 0);
2045 int64_t CompoundOperatorNoRestart(
int size,
int active_index,
2046 int operator_index) {
2047 return (operator_index < active_index) ? size + operator_index - active_index
2048 : operator_index - active_index;
2051 int64_t CompoundOperatorRestart(
int active_index,
int operator_index) {
2057 const std::vector<LocalSearchOperator*>& ops) {
2062 const std::vector<LocalSearchOperator*>& ops,
bool restart) {
2064 std::function<int64_t(
int,
int)> eval = CompoundOperatorRestart;
2067 const int size = ops.size();
2069 return CompoundOperatorNoRestart(size, i, j);
2074 const std::vector<LocalSearchOperator*>& ops,
2075 std::function<int64_t(
int,
int)> evaluator) {
2076 return RevAlloc(
new CompoundOperator(ops, std::move(evaluator)));
2082 explicit RandomCompoundOperator(std::vector<LocalSearchOperator*> operators);
2083 RandomCompoundOperator(std::vector<LocalSearchOperator*> operators,
2085 ~RandomCompoundOperator()
override {}
2086 void Reset()
override;
2087 void Start(
const Assignment* assignment)
override;
2088 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
2089 bool HoldsDelta()
const override {
return true; }
2091 std::string DebugString()
const override {
return "RandomCompoundOperator"; }
2096 const std::vector<LocalSearchOperator*> operators_;
2097 bool has_fragments_;
2100 void RandomCompoundOperator::Start(
const Assignment* assignment) {
2101 for (LocalSearchOperator*
const op : operators_) {
2102 op->Start(assignment);
2106 RandomCompoundOperator::RandomCompoundOperator(
2107 std::vector<LocalSearchOperator*> operators)
2108 : RandomCompoundOperator(std::move(operators),
CpRandomSeed()) {}
2110 RandomCompoundOperator::RandomCompoundOperator(
2111 std::vector<LocalSearchOperator*> operators, int32_t seed)
2112 : rand_(seed), operators_(std::move(operators)), has_fragments_(false) {
2113 for (LocalSearchOperator*
const op : operators_) {
2114 if (op->HasFragments()) {
2115 has_fragments_ =
true;
2121 void RandomCompoundOperator::Reset() {
2122 for (LocalSearchOperator*
const op : operators_) {
2127 bool RandomCompoundOperator::MakeNextNeighbor(Assignment*
delta,
2128 Assignment* deltadelta) {
2129 const int size = operators_.size();
2130 std::vector<int> indices(size);
2131 std::iota(indices.begin(), indices.end(), 0);
2132 std::shuffle(indices.begin(), indices.end(), rand_);
2133 for (
int index : indices) {
2134 if (!operators_[
index]->HoldsDelta()) {
2137 if (operators_[
index]->MakeNextNeighbor(
delta, deltadelta)) {
2147 const std::vector<LocalSearchOperator*>& ops) {
2148 return RevAlloc(
new RandomCompoundOperator(ops));
2152 const std::vector<LocalSearchOperator*>& ops, int32_t seed) {
2153 return RevAlloc(
new RandomCompoundOperator(ops, seed));
2159 explicit MultiArmedBanditCompoundOperator(
2160 std::vector<LocalSearchOperator*> operators,
double memory_coefficient,
2161 double exploration_coefficient,
bool maximize);
2162 ~MultiArmedBanditCompoundOperator()
override {}
2163 void Reset()
override;
2164 void Start(
const Assignment* assignment)
override;
2165 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
2166 bool HoldsDelta()
const override {
return true; }
2168 std::string DebugString()
const override {
2169 return operators_.empty()
2171 : operators_[operator_indices_[index_]]->DebugString();
2173 const LocalSearchOperator* Self()
const override {
2174 return operators_.empty() ? this
2175 : operators_[operator_indices_[index_]]->Self();
2179 double Score(
int index);
2181 std::vector<LocalSearchOperator*> operators_;
2182 Bitset64<> started_;
2183 const Assignment* start_assignment_;
2184 bool has_fragments_;
2185 std::vector<int> operator_indices_;
2186 int64_t last_objective_;
2187 std::vector<double> avg_improvement_;
2189 std::vector<double> num_neighbors_per_operator_;
2195 const double memory_coefficient_;
2202 const double exploration_coefficient_;
2205 MultiArmedBanditCompoundOperator::MultiArmedBanditCompoundOperator(
2206 std::vector<LocalSearchOperator*> operators,
double memory_coefficient,
2207 double exploration_coefficient,
bool maximize)
2209 operators_(std::move(operators)),
2210 started_(operators_.size()),
2211 start_assignment_(nullptr),
2212 has_fragments_(false),
2213 last_objective_(std::numeric_limits<int64_t>::
max()),
2216 memory_coefficient_(memory_coefficient),
2217 exploration_coefficient_(exploration_coefficient) {
2218 DCHECK_GE(memory_coefficient_, 0);
2219 DCHECK_LE(memory_coefficient_, 1);
2220 DCHECK_GE(exploration_coefficient_, 0);
2221 operators_.erase(std::remove(operators_.begin(), operators_.end(),
nullptr),
2223 operator_indices_.resize(operators_.size());
2224 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
2225 num_neighbors_per_operator_.resize(operators_.size(), 0);
2226 avg_improvement_.resize(operators_.size(), 0);
2227 for (LocalSearchOperator*
const op : operators_) {
2228 if (op->HasFragments()) {
2229 has_fragments_ =
true;
2235 void MultiArmedBanditCompoundOperator::Reset() {
2236 for (LocalSearchOperator*
const op : operators_) {
2241 double MultiArmedBanditCompoundOperator::Score(
int index) {
2242 return avg_improvement_[
index] +
2243 exploration_coefficient_ *
2244 sqrt(2 * log(1 + num_neighbors_) /
2245 (1 + num_neighbors_per_operator_[
index]));
2248 void MultiArmedBanditCompoundOperator::Start(
const Assignment* assignment) {
2249 start_assignment_ = assignment;
2251 if (operators_.empty())
return;
2253 const double objective = assignment->ObjectiveValue();
2255 if (objective == last_objective_)
return;
2258 last_objective_ = objective;
2262 const double improvement =
2263 maximize_ ? objective - last_objective_ : last_objective_ - objective;
2264 if (improvement < 0) {
2267 last_objective_ = objective;
2268 avg_improvement_[operator_indices_[index_]] +=
2269 memory_coefficient_ *
2270 (improvement - avg_improvement_[operator_indices_[index_]]);
2272 std::sort(operator_indices_.begin(), operator_indices_.end(),
2273 [
this](
int lhs,
int rhs) {
2274 const double lhs_score = Score(lhs);
2275 const double rhs_score = Score(rhs);
2276 return lhs_score > rhs_score ||
2277 (lhs_score == rhs_score && lhs < rhs);
2283 bool MultiArmedBanditCompoundOperator::MakeNextNeighbor(
2284 Assignment*
delta, Assignment* deltadelta) {
2285 if (operators_.empty())
return false;
2287 const int operator_index = operator_indices_[index_];
2288 if (!started_[operator_index]) {
2289 operators_[operator_index]->Start(start_assignment_);
2290 started_.
Set(operator_index);
2292 if (!operators_[operator_index]->HoldsDelta()) {
2295 if (operators_[operator_index]->MakeNextNeighbor(
delta, deltadelta)) {
2297 ++num_neighbors_per_operator_[operator_index];
2302 if (index_ == operators_.size()) {
2305 }
while (index_ != 0);
2311 const std::vector<LocalSearchOperator*>& ops,
double memory_coefficient,
2312 double exploration_coefficient,
bool maximize) {
2313 return RevAlloc(
new MultiArmedBanditCompoundOperator(
2314 ops, memory_coefficient, exploration_coefficient, maximize));
2321 Solver* solver,
const std::vector<IntVar*>& vars,
2322 const std::vector<IntVar*>& secondary_vars,
2323 std::function<
int(int64_t)> start_empty_path_class) {
2325 new T(vars, secondary_vars, std::move(start_empty_path_class)));
2328 #define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass) \
2330 LocalSearchOperator* MakeLocalSearchOperator<OperatorClass>( \
2331 Solver * solver, const std::vector<IntVar*>& vars, \
2332 const std::vector<IntVar*>& secondary_vars, \
2333 std::function<int(int64_t)> start_empty_path_class) { \
2334 return solver->RevAlloc(new OperatorClass( \
2335 vars, secondary_vars, std::move(start_empty_path_class))); \
2351 #undef MAKE_LOCAL_SEARCH_OPERATOR
2359 const std::vector<IntVar*>& vars,
2360 const std::vector<IntVar*>& secondary_vars,
2369 std::vector<LocalSearchOperator*> operators;
2370 for (
int i = 1; i < 4; ++i) {
2372 new Relocate(vars, secondary_vars, absl::StrCat(
"OrOpt<", i,
">"),
2373 nullptr, i,
true)));
2379 result = MakeLocalSearchOperator<Relocate>(
this, vars, secondary_vars,
2384 result = MakeLocalSearchOperator<Exchange>(
this, vars, secondary_vars,
2390 MakeLocalSearchOperator<Cross>(
this, vars, secondary_vars,
nullptr);
2394 result = MakeLocalSearchOperator<MakeActiveOperator>(
2395 this, vars, secondary_vars,
nullptr);
2399 result = MakeLocalSearchOperator<MakeInactiveOperator>(
2400 this, vars, secondary_vars,
nullptr);
2404 result = MakeLocalSearchOperator<MakeChainInactiveOperator>(
2405 this, vars, secondary_vars,
nullptr);
2409 result = MakeLocalSearchOperator<SwapActiveOperator>(
2410 this, vars, secondary_vars,
nullptr);
2414 result = MakeLocalSearchOperator<ExtendedSwapActiveOperator>(
2415 this, vars, secondary_vars,
nullptr);
2434 if (secondary_vars.empty()) {
2435 result =
RevAlloc(
new IncrementValue(vars));
2437 LOG(FATAL) <<
"Operator " << op
2438 <<
" does not support secondary variables";
2443 if (secondary_vars.empty()) {
2444 result =
RevAlloc(
new DecrementValue(vars));
2446 LOG(FATAL) <<
"Operator " << op
2447 <<
" does not support secondary variables";
2452 if (secondary_vars.empty()) {
2453 result =
RevAlloc(
new SimpleLns(vars, 1));
2455 LOG(FATAL) <<
"Operator " << op
2456 <<
" does not support secondary variables";
2461 LOG(FATAL) <<
"Unknown operator " << op;
2469 return MakeOperator(vars, std::vector<IntVar*>(), std::move(evaluator), op);
2473 const std::vector<IntVar*>& vars,
2474 const std::vector<IntVar*>& secondary_vars,
2480 std::vector<LocalSearchOperator*> operators;
2482 new LinKernighan(vars, secondary_vars, evaluator,
false)));
2484 new LinKernighan(vars, secondary_vars, evaluator,
true)));
2490 new TSPOpt(vars, secondary_vars, evaluator,
2491 absl::GetFlag(FLAGS_cp_local_search_tsp_opt_size)));
2496 new TSPLns(vars, secondary_vars, evaluator,
2497 absl::GetFlag(FLAGS_cp_local_search_tsp_lns_size)));
2501 LOG(FATAL) <<
"Unknown operator " << op;
2510 std::string DebugString()
const override {
return "AcceptFilter"; }
2511 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2512 int64_t obj_min, int64_t obj_max)
override {
2515 void Synchronize(
const Assignment* assignment,
2516 const Assignment*
delta)
override {}
2521 return RevAlloc(
new AcceptFilter());
2528 std::string DebugString()
const override {
return "RejectFilter"; }
2529 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2530 int64_t obj_min, int64_t obj_max)
override {
2533 void Synchronize(
const Assignment* assignment,
2534 const Assignment*
delta)
override {}
2539 return RevAlloc(
new RejectFilter());
2543 std::vector<int> path_end)
2544 : num_nodes_(num_nodes),
2545 num_paths_(path_start.size()),
2546 num_nodes_threshold_(std::
max(16, 4 * num_nodes_))
2548 DCHECK_EQ(path_start.size(), num_paths_);
2549 DCHECK_EQ(path_end.size(), num_paths_);
2550 for (
int p = 0; p < num_paths_; ++p) {
2551 path_start_end_.push_back({path_start[p], path_end[p]});
2554 committed_index_.assign(num_nodes_, -1);
2555 committed_nodes_.assign(2 * num_paths_, {-1, -1});
2556 chains_.assign(num_paths_ + 1, {-1, -1});
2557 paths_.assign(num_paths_, {-1, -1});
2558 for (
int path = 0; path < num_paths_; ++path) {
2559 const int index = 2 * path;
2560 const PathStartEnd start_end = path_start_end_[path];
2561 committed_index_[start_end.start] =
index;
2562 committed_index_[start_end.end] =
index + 1;
2564 committed_nodes_[
index] = {start_end.start, path};
2565 committed_nodes_[
index + 1] = {start_end.end, path};
2568 paths_[path] = {path, path + 1};
2570 chains_[num_paths_] = {0, 0};
2572 for (
int node = 0; node < num_nodes_; ++node) {
2573 if (committed_index_[node] != -1)
continue;
2574 committed_index_[node] = committed_nodes_.size();
2575 committed_nodes_.push_back({node, -1});
2580 const PathBounds
bounds = paths_[path];
2582 chains_.data() +
bounds.end_index,
2583 committed_nodes_.data());
2587 const PathBounds
bounds = paths_[path];
2589 chains_.data() +
bounds.end_index,
2590 committed_nodes_.data());
2594 changed_paths_.push_back(path);
2595 const int path_begin_index = chains_.size();
2596 chains_.insert(chains_.end(), chains.begin(), chains.end());
2597 const int path_end_index = chains_.size();
2598 paths_[path] = {path_begin_index, path_end_index};
2599 chains_.emplace_back(0, 0);
2603 for (
const int loop : new_loops) {
2604 if (
Path(loop) == -1)
continue;
2605 changed_loops_.push_back(loop);
2611 if (committed_nodes_.size() < num_nodes_threshold_) {
2612 IncrementalCommit();
2619 is_invalid_ =
false;
2620 chains_.resize(num_paths_ + 1);
2621 for (
const int path : changed_paths_) {
2622 paths_[path] = {path, path + 1};
2624 changed_paths_.clear();
2625 changed_loops_.clear();
2628 void PathState::CopyNewPathAtEndOfNodes(
int path) {
2630 const int new_path_begin_index = committed_nodes_.size();
2631 const PathBounds path_bounds = paths_[path];
2632 for (
int i = path_bounds.begin_index; i < path_bounds.end_index; ++i) {
2633 const ChainBounds chain_bounds = chains_[i];
2634 committed_nodes_.insert(committed_nodes_.end(),
2635 committed_nodes_.data() + chain_bounds.begin_index,
2636 committed_nodes_.data() + chain_bounds.end_index);
2638 const int new_path_end_index = committed_nodes_.size();
2640 for (
int i = new_path_begin_index; i < new_path_end_index; ++i) {
2641 committed_nodes_[i].path = path;
2647 void PathState::IncrementalCommit() {
2648 const int new_nodes_begin = committed_nodes_.size();
2650 const int chain_begin = committed_nodes_.size();
2651 CopyNewPathAtEndOfNodes(path);
2652 const int chain_end = committed_nodes_.size();
2653 chains_[path] = {chain_begin, chain_end};
2656 const int new_nodes_end = committed_nodes_.size();
2657 for (
int i = new_nodes_begin; i < new_nodes_end; ++i) {
2658 committed_index_[committed_nodes_[i].node] = i;
2663 const int index = committed_index_[loop];
2664 committed_nodes_[
index].path = -1;
2670 void PathState::FullCommit() {
2673 const int old_num_nodes = committed_nodes_.size();
2674 for (
int path = 0; path < num_paths_; ++path) {
2675 const int new_path_begin = committed_nodes_.size() - old_num_nodes;
2676 CopyNewPathAtEndOfNodes(path);
2677 const int new_path_end = committed_nodes_.size() - old_num_nodes;
2678 chains_[path] = {new_path_begin, new_path_end};
2680 committed_nodes_.erase(committed_nodes_.begin(),
2681 committed_nodes_.begin() + old_num_nodes);
2684 constexpr
int kUnindexed = -1;
2685 committed_index_.assign(num_nodes_, kUnindexed);
2687 for (
const CommittedNode committed_node : committed_nodes_) {
2688 committed_index_[committed_node.node] =
index++;
2690 for (
int node = 0; node < num_nodes_; ++node) {
2691 if (committed_index_[node] != kUnindexed)
continue;
2692 committed_index_[node] =
index++;
2693 committed_nodes_.push_back({node, -1});
2701 class PathStateFilter :
public LocalSearchFilter {
2703 std::string DebugString()
const override {
return "PathStateFilter"; }
2704 PathStateFilter(std::unique_ptr<PathState> path_state,
2705 const std::vector<IntVar*>& nexts);
2706 void Relax(
const Assignment*
delta,
const Assignment* deltadelta)
override;
2707 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2708 int64_t objective_min, int64_t objective_max)
override {
2711 void Synchronize(
const Assignment*
delta,
2712 const Assignment* deltadelta)
override{};
2713 void Commit(
const Assignment* assignment,
const Assignment*
delta)
override;
2714 void Revert()
override;
2715 void Reset()
override;
2719 struct TailHeadIndices {
2726 bool operator<(
const IndexArc& other)
const {
return index < other.index; }
2733 void MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
2736 void MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
2738 const std::unique_ptr<PathState> path_state_;
2740 std::vector<int> variable_index_to_node_;
2743 std::vector<bool> node_is_assigned_;
2744 std::vector<int> loops_;
2747 std::vector<int> changed_paths_;
2748 std::vector<bool> path_has_changed_;
2749 std::vector<std::pair<int, int>> changed_arcs_;
2750 std::vector<int> changed_loops_;
2751 std::vector<TailHeadIndices> tail_head_indices_;
2752 std::vector<IndexArc> arcs_by_tail_index_;
2753 std::vector<IndexArc> arcs_by_head_index_;
2754 std::vector<int> next_arc_;
2755 std::vector<PathState::ChainBounds> path_chains_;
2758 PathStateFilter::PathStateFilter(std::unique_ptr<PathState> path_state,
2759 const std::vector<IntVar*>& nexts)
2760 : path_state_(std::move(path_state)) {
2764 for (
const IntVar*
next : nexts) {
2766 min_index = std::min<int>(min_index,
index);
2767 max_index = std::max<int>(max_index,
index);
2769 variable_index_to_node_.resize(max_index - min_index + 1, -1);
2770 index_offset_ = min_index;
2773 for (
int node = 0; node < nexts.size(); ++node) {
2774 const int index = nexts[node]->index() - index_offset_;
2775 variable_index_to_node_[
index] = node;
2777 path_has_changed_.assign(path_state_->NumPaths(),
false);
2780 void PathStateFilter::Relax(
const Assignment*
delta,
2781 const Assignment* deltadelta) {
2782 path_state_->Revert();
2783 changed_arcs_.clear();
2784 for (
const IntVarElement& var_value :
delta->IntVarContainer().elements()) {
2785 if (var_value.Var() ==
nullptr)
continue;
2786 const int index = var_value.Var()->index() - index_offset_;
2787 if (
index < 0 || variable_index_to_node_.size() <=
index)
continue;
2788 const int node = variable_index_to_node_[
index];
2789 if (node == -1)
continue;
2790 if (var_value.Bound()) {
2791 changed_arcs_.emplace_back(node, var_value.Value());
2793 path_state_->Revert();
2794 path_state_->SetInvalid();
2801 void PathStateFilter::Reset() {
2802 path_state_->Revert();
2805 const int num_nodes = path_state_->NumNodes();
2806 node_is_assigned_.assign(num_nodes,
false);
2808 const int num_paths = path_state_->NumPaths();
2809 for (
int path = 0; path < num_paths; ++path) {
2810 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
2811 path_state_->ChangePath(
2812 path, {{start_index, start_index + 1}, {end_index - 1, end_index}});
2813 node_is_assigned_[path_state_->Start(path)] =
true;
2814 node_is_assigned_[path_state_->End(path)] =
true;
2816 for (
int node = 0; node < num_nodes; ++node) {
2817 if (!node_is_assigned_[node]) loops_.push_back(node);
2819 path_state_->ChangeLoops(loops_);
2820 path_state_->Commit();
2826 void PathStateFilter::Commit(
const Assignment* assignment,
2827 const Assignment*
delta) {
2828 path_state_->Revert();
2830 Relax(assignment,
nullptr);
2832 Relax(
delta,
nullptr);
2834 path_state_->Commit();
2837 void PathStateFilter::Revert() { path_state_->Revert(); }
2839 void PathStateFilter::CutChains() {
2843 for (
const int path : changed_paths_) path_has_changed_[path] =
false;
2844 changed_paths_.clear();
2845 tail_head_indices_.clear();
2846 changed_loops_.clear();
2847 int num_changed_arcs = 0;
2848 for (
const auto [node,
next] : changed_arcs_) {
2849 const int node_index = path_state_->CommittedIndex(node);
2850 const int next_index = path_state_->CommittedIndex(
next);
2851 const int node_path = path_state_->Path(node);
2853 (next_index != node_index + 1 || node_path == -1)) {
2854 tail_head_indices_.push_back({node_index, next_index});
2855 changed_arcs_[num_changed_arcs++] = {node,
next};
2856 if (node_path != -1 && !path_has_changed_[node_path]) {
2857 path_has_changed_[node_path] =
true;
2858 changed_paths_.push_back(node_path);
2860 }
else if (node ==
next && node_path != -1) {
2861 changed_loops_.push_back(node);
2864 changed_arcs_.resize(num_changed_arcs);
2866 path_state_->ChangeLoops(changed_loops_);
2867 if (tail_head_indices_.size() + changed_paths_.size() <= 8) {
2868 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
2870 MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
2874 void PathStateFilter::
2875 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm() {
2876 int num_visited_changed_arcs = 0;
2877 const int num_changed_arcs = tail_head_indices_.size();
2879 for (
const int path : changed_paths_) {
2880 path_chains_.clear();
2881 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
2882 int current_index = start_index;
2886 int selected_arc = -1;
2888 for (
int i = num_visited_changed_arcs; i < num_changed_arcs; ++i) {
2889 const int tail_index = tail_head_indices_[i].tail_index;
2900 if (start_index <= current_index && current_index < end_index &&
2901 end_index <= selected_tail_index) {
2902 path_chains_.emplace_back(current_index, end_index);
2905 path_chains_.emplace_back(current_index, selected_tail_index + 1);
2906 current_index = tail_head_indices_[selected_arc].head_index;
2907 std::swap(tail_head_indices_[num_visited_changed_arcs],
2908 tail_head_indices_[selected_arc]);
2909 ++num_visited_changed_arcs;
2912 path_state_->ChangePath(path, path_chains_);
2916 void PathStateFilter::MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm() {
2932 for (
const int path : changed_paths_) {
2933 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
2934 tail_head_indices_.push_back({end_index - 1, start_index});
2939 const int num_arc_indices = tail_head_indices_.size();
2940 arcs_by_tail_index_.resize(num_arc_indices);
2941 arcs_by_head_index_.resize(num_arc_indices);
2942 for (
int i = 0; i < num_arc_indices; ++i) {
2943 arcs_by_tail_index_[i] = {tail_head_indices_[i].tail_index, i};
2944 arcs_by_head_index_[i] = {tail_head_indices_[i].head_index, i};
2946 std::sort(arcs_by_tail_index_.begin(), arcs_by_tail_index_.end());
2947 std::sort(arcs_by_head_index_.begin(), arcs_by_head_index_.end());
2949 next_arc_.resize(num_arc_indices);
2950 for (
int i = 0; i < num_arc_indices; ++i) {
2951 next_arc_[arcs_by_head_index_[i].arc] = arcs_by_tail_index_[i].arc;
2957 const int first_fake_arc = num_arc_indices - changed_paths_.size();
2958 for (
int fake_arc = first_fake_arc; fake_arc < num_arc_indices; ++fake_arc) {
2959 path_chains_.clear();
2960 int32_t
arc = fake_arc;
2962 const int chain_begin = tail_head_indices_[
arc].head_index;
2964 const int chain_end = tail_head_indices_[
arc].tail_index + 1;
2965 path_chains_.emplace_back(chain_begin, chain_end);
2966 }
while (
arc != fake_arc);
2967 const int path = changed_paths_[fake_arc - first_fake_arc];
2968 path_state_->ChangePath(path, path_chains_);
2975 std::unique_ptr<PathState> path_state,
2976 const std::vector<IntVar*>& nexts) {
2977 PathStateFilter* filter =
new PathStateFilter(std::move(path_state), nexts);
2982 using EInterval = DimensionChecker::ExtendedInterval;
2987 EInterval Intersect(
const EInterval& i1,
const EInterval& i2) {
2989 i2.num_negative_infinity == 0 ? i2.min :
kint64min),
2990 std::min(i1.num_negative_infinity, i2.num_negative_infinity),
2992 i2.num_positive_infinity == 0 ? i2.max :
kint64max),
2993 std::min(i1.num_positive_infinity, i2.num_positive_infinity)};
2996 bool IsEmpty(
const EInterval&
interval) {
2997 const int64_t minimum_value =
2999 const int64_t maximum_value =
3001 return minimum_value > maximum_value;
3004 EInterval
operator+(
const EInterval& i1,
const EInterval& i2) {
3005 return {
CapAdd(i1.min, i2.min),
3006 CapAdd(i1.num_negative_infinity, i2.num_negative_infinity),
3008 CapAdd(i1.num_positive_infinity, i2.num_positive_infinity)};
3011 EInterval& operator+=(EInterval& i1,
const EInterval& i2) {
3016 EInterval
operator-(
const EInterval& i1,
const EInterval& i2) {
3017 return {
CapSub(i1.min, i2.max),
3018 CapAdd(i1.num_negative_infinity, i2.num_positive_infinity),
3020 CapAdd(i1.num_positive_infinity, i2.num_negative_infinity)};
3025 EInterval Delta(
const EInterval& from,
const EInterval& to) {
3026 return {
CapSub(to.min, from.min),
3027 CapSub(to.num_negative_infinity, from.num_negative_infinity),
3028 CapSub(to.max, from.max),
3029 CapSub(to.num_positive_infinity, from.num_positive_infinity)};
3032 EInterval ToExtendedInterval(DimensionChecker::Interval
interval) {
3035 return {is_neg_infinity ? 0 :
interval.min, is_neg_infinity ? 1 : 0,
3036 is_pos_infinity ? 0 :
interval.max, is_pos_infinity ? 1 : 0};
3039 std::vector<EInterval> ToExtendedIntervals(
3040 const std::vector<DimensionChecker::Interval>& intervals) {
3041 std::vector<EInterval> extended_intervals;
3042 extended_intervals.reserve(intervals.size());
3043 for (
const auto&
interval : intervals) {
3044 extended_intervals.push_back(ToExtendedInterval(
interval));
3046 return extended_intervals;
3050 DimensionChecker::DimensionChecker(
3051 const PathState* path_state, std::vector<Interval> path_capacity,
3052 std::vector<int> path_class,
3053 std::vector<std::function<
Interval(int64_t, int64_t)>>
3054 demand_per_path_class,
3055 std::vector<Interval> node_capacity,
int min_range_size_for_riq)
3056 : path_state_(path_state),
3057 path_capacity_(ToExtendedIntervals(path_capacity)),
3058 path_class_(std::move(path_class)),
3059 demand_per_path_class_(std::move(demand_per_path_class)),
3060 node_capacity_(ToExtendedIntervals(node_capacity)),
3061 index_(path_state_->NumNodes(), 0),
3062 maximum_riq_layer_size_(std::
max(
3063 16, 4 * path_state_->NumNodes())),
3064 min_range_size_for_riq_(min_range_size_for_riq) {
3065 const int num_nodes = path_state_->
NumNodes();
3066 cached_demand_.resize(num_nodes);
3067 const int num_paths = path_state_->
NumPaths();
3068 DCHECK_EQ(num_paths, path_capacity_.size());
3069 DCHECK_EQ(num_paths, path_class_.size());
3071 forwards_demand_sums_riq_.resize(maximum_riq_exponent + 1);
3072 forwards_node_capacity_riq_.resize(maximum_riq_exponent + 1);
3073 backwards_node_capacity_riq_.resize(maximum_riq_exponent + 1);
3078 if (path_state_->
IsInvalid())
return true;
3080 const EInterval path_capacity = path_capacity_[path];
3081 const int path_class = path_class_[path];
3084 int prev_node = path_state_->
Start(path);
3085 EInterval cumul = Intersect(node_capacity_[prev_node], path_capacity);
3086 if (IsEmpty(cumul))
return false;
3088 for (
const auto chain : path_state_->
Chains(path)) {
3089 const int first_node = chain.First();
3090 const int last_node = chain.Last();
3092 if (prev_node != first_node) {
3095 const EInterval
demand = ToExtendedInterval(
3096 demand_per_path_class_[path_class](prev_node, first_node));
3098 cumul = Intersect(cumul, path_capacity);
3099 cumul = Intersect(cumul, node_capacity_[first_node]);
3100 if (IsEmpty(cumul))
return false;
3101 prev_node = first_node;
3105 const int first_index = index_[first_node];
3106 const int last_index = index_[last_node];
3107 const int chain_path = path_state_->
Path(first_node);
3108 const int chain_path_class =
3109 chain_path == -1 ? -1 : path_class_[chain_path];
3113 const bool chain_is_cached = chain_path_class == path_class;
3114 if (last_index - first_index > min_range_size_for_riq_ &&
3118 cumul, FirstIndexCumulsFromNodeCapacities(first_index, last_index));
3119 cumul = Intersect(cumul, FirstIndexCumulsFromPathCapacity(
3120 first_index, last_index, path_capacity));
3121 if (IsEmpty(cumul))
return false;
3124 cumul += TotalTransit(first_index, last_index);
3128 cumul, LastIndexCumulsFromNodeCapacities(first_index, last_index));
3129 if (IsEmpty(cumul))
return false;
3130 prev_node = chain.Last();
3132 for (
const int node : chain.WithoutFirstNode()) {
3135 ? cached_demand_[prev_node]
3136 : ToExtendedInterval(
3137 demand_per_path_class_[path_class](prev_node, node));
3139 cumul = Intersect(cumul, node_capacity_[node]);
3140 cumul = Intersect(cumul, path_capacity);
3141 if (IsEmpty(cumul))
return false;
3151 const int current_layer_size = forwards_demand_sums_riq_[0].size();
3154 for (
const auto chain : path_state_->
Chains(path)) {
3155 change_size += chain.NumNodes();
3158 if (current_layer_size + change_size <= maximum_riq_layer_size_) {
3159 IncrementalCommit();
3165 void DimensionChecker::IncrementalCommit() {
3167 const int begin_index = forwards_demand_sums_riq_[0].size();
3168 AppendPathDemandsToSums(path);
3169 UpdateRIQStructure(begin_index, forwards_demand_sums_riq_[0].size());
3173 void DimensionChecker::FullCommit() {
3175 for (
auto& layer : forwards_demand_sums_riq_) layer.clear();
3176 for (
auto& layer : forwards_node_capacity_riq_) layer.clear();
3177 for (
auto& layer : backwards_node_capacity_riq_) layer.clear();
3179 const int num_paths = path_state_->
NumPaths();
3180 for (
int path = 0; path < num_paths; ++path) {
3181 const int begin_index = forwards_demand_sums_riq_[0].size();
3182 AppendPathDemandsToSums(path);
3183 UpdateRIQStructure(begin_index, forwards_demand_sums_riq_[0].size());
3187 void DimensionChecker::AppendPathDemandsToSums(
int path) {
3190 const int path_class = path_class_[path];
3191 EInterval demand_sum = {0, 0, 0, 0};
3192 int prev = path_state_->
Start(path);
3193 int index = forwards_demand_sums_riq_[0].size();
3194 for (
const int node : path_state_->
Nodes(path)) {
3197 prev == node ? EInterval{0, 0, 0, 0}
3198 : ToExtendedInterval(
3199 demand_per_path_class_[path_class](prev, node));
3201 cached_demand_[prev] =
demand;
3204 index_[node] =
index++;
3205 forwards_demand_sums_riq_[0].push_back(demand_sum);
3206 forwards_node_capacity_riq_[0].push_back(node_capacity_[node]);
3207 backwards_node_capacity_riq_[0].push_back(node_capacity_[node]);
3209 cached_demand_[path_state_->
End(path)] = {0, 0, 0, 0};
3212 void DimensionChecker::UpdateRIQStructure(
int begin_index,
int end_index) {
3215 const int max_layer =
3217 for (
int layer = 1, window = 1; layer <= max_layer; ++layer, window *= 2) {
3218 forwards_demand_sums_riq_[layer].resize(end_index);
3220 forwards_demand_sums_riq_[layer - 1].begin() + begin_index,
3221 forwards_demand_sums_riq_[layer - 1].begin() + begin_index + window,
3222 forwards_demand_sums_riq_[layer].begin() + begin_index);
3223 for (
int i = begin_index + window; i < end_index; ++i) {
3224 forwards_demand_sums_riq_[layer][i] =
3225 Intersect(forwards_demand_sums_riq_[layer - 1][i - window],
3226 forwards_demand_sums_riq_[layer - 1][i]);
3229 for (
int layer = 1, window = 1; layer <= max_layer; ++layer, window *= 2) {
3230 forwards_node_capacity_riq_[layer].resize(end_index);
3232 forwards_node_capacity_riq_[layer - 1].begin() + begin_index,
3233 forwards_node_capacity_riq_[layer - 1].begin() + begin_index + window,
3234 forwards_node_capacity_riq_[layer].begin() + begin_index);
3235 for (
int i = begin_index + window; i < end_index; ++i) {
3236 const EInterval transition =
3237 Delta(forwards_demand_sums_riq_[0][i - window],
3238 forwards_demand_sums_riq_[0][i]);
3239 forwards_node_capacity_riq_[layer][i] = Intersect(
3240 forwards_node_capacity_riq_[layer - 1][i - window] + transition,
3241 forwards_node_capacity_riq_[layer - 1][i]);
3244 for (
int layer = 1, window = 1; layer <= max_layer; ++layer, window *= 2) {
3245 backwards_node_capacity_riq_[layer].resize(end_index);
3246 for (
int i = begin_index; i < end_index - window; ++i) {
3247 const EInterval transition =
3248 Delta(forwards_demand_sums_riq_[0][i],
3249 forwards_demand_sums_riq_[0][i + window]);
3250 backwards_node_capacity_riq_[layer][i] = Intersect(
3251 backwards_node_capacity_riq_[layer - 1][i],
3252 backwards_node_capacity_riq_[layer - 1][i + window] - transition);
3255 backwards_node_capacity_riq_[layer - 1].begin() + end_index - window,
3256 backwards_node_capacity_riq_[layer - 1].begin() + end_index,
3257 backwards_node_capacity_riq_[layer].begin() + end_index - window);
3261 EInterval DimensionChecker::FirstIndexCumulsFromPathCapacity(
3262 int first_node_index,
int last_node_index,
3263 const EInterval& path_capacity)
const {
3264 DCHECK_LE(0, first_node_index);
3265 DCHECK_LT(first_node_index, last_node_index);
3266 DCHECK_LT(last_node_index, forwards_demand_sums_riq_[0].size());
3271 const int window = 1 << layer;
3272 const EInterval tightest_sum =
3273 Intersect(forwards_demand_sums_riq_[layer][first_node_index + window - 1],
3274 forwards_demand_sums_riq_[layer][last_node_index]);
3275 const EInterval first_sum = forwards_demand_sums_riq_[0][first_node_index];
3276 return path_capacity - Delta(first_sum, tightest_sum);
3279 EInterval DimensionChecker::TotalTransit(
int first_node_index,
3280 int last_node_index)
const {
3281 const EInterval first_sum = forwards_demand_sums_riq_[0][first_node_index];
3282 const EInterval last_sum = forwards_demand_sums_riq_[0][last_node_index];
3283 return Delta(first_sum, last_sum);
3286 EInterval DimensionChecker::FirstIndexCumulsFromNodeCapacities(
3287 int first_node_index,
int last_node_index)
const {
3290 const int window = 1 << layer;
3292 const int right_index = last_node_index - window + 1;
3293 const EInterval transition =
3294 Delta(forwards_demand_sums_riq_[0][first_node_index],
3295 forwards_demand_sums_riq_[0][right_index]);
3297 backwards_node_capacity_riq_[layer][right_index] - transition,
3298 backwards_node_capacity_riq_[layer][first_node_index]);
3301 EInterval DimensionChecker::LastIndexCumulsFromNodeCapacities(
3302 int first_node_index,
int last_node_index)
const {
3303 DCHECK_LE(0, first_node_index);
3304 DCHECK_LT(first_node_index, last_node_index);
3305 DCHECK_LT(last_node_index, forwards_demand_sums_riq_[0].size());
3310 const int window = 1 << layer;
3312 const int left_index = first_node_index + window - 1;
3313 const EInterval transition =
3314 Delta(forwards_demand_sums_riq_[0][left_index],
3315 forwards_demand_sums_riq_[0][last_node_index]);
3316 return Intersect(forwards_node_capacity_riq_[layer][left_index] + transition,
3317 forwards_node_capacity_riq_[layer][last_node_index]);
3322 class DimensionFilter :
public LocalSearchFilter {
3324 std::string DebugString()
const override {
return name_; }
3325 DimensionFilter(std::unique_ptr<DimensionChecker> checker,
3326 const std::string& dimension_name)
3327 : checker_(std::move(checker)),
3328 name_(
absl::StrCat(
"DimensionFilter(", dimension_name,
")")) {}
3330 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3331 int64_t objective_min, int64_t objective_max)
override {
3332 return checker_->Check();
3335 void Synchronize(
const Assignment* assignment,
3336 const Assignment*
delta)
override {
3341 std::unique_ptr<DimensionChecker> checker_;
3342 const std::string name_;
3348 Solver* solver, std::unique_ptr<DimensionChecker> checker,
3349 const std::string& dimension_name) {
3350 DimensionFilter* filter =
3351 new DimensionFilter(std::move(checker), dimension_name);
3359 class VariableDomainFilter :
public LocalSearchFilter {
3361 VariableDomainFilter() {}
3362 ~VariableDomainFilter()
override {}
3363 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3364 int64_t objective_min, int64_t objective_max)
override;
3365 void Synchronize(
const Assignment* assignment,
3366 const Assignment*
delta)
override {}
3368 std::string DebugString()
const override {
return "VariableDomainFilter"; }
3371 bool VariableDomainFilter::Accept(
const Assignment*
delta,
3372 const Assignment* deltadelta,
3373 int64_t objective_min,
3374 int64_t objective_max) {
3376 const int size = container.Size();
3377 for (
int i = 0; i < size; ++i) {
3378 const IntVarElement& element = container.Element(i);
3379 if (element.Activated() && !element.Var()->Contains(element.Value())) {
3388 return RevAlloc(
new VariableDomainFilter());
3393 const int IntVarLocalSearchFilter::kUnassigned = -1;
3396 const std::vector<IntVar*>& vars) {
3401 if (!vars.empty()) {
3402 for (
int i = 0; i < vars.size(); ++i) {
3403 const int index = vars[i]->index();
3404 if (
index >= var_index_to_index_.size()) {
3405 var_index_to_index_.resize(
index + 1, kUnassigned);
3407 var_index_to_index_[
index] = i + vars_.size();
3409 vars_.insert(vars_.end(), vars.begin(), vars.end());
3410 values_.resize(vars_.size(), 0);
3411 var_synced_.resize(vars_.size(),
false);
3420 var_synced_.assign(var_synced_.size(),
false);
3431 const int size = container.
Size();
3432 for (
int i = 0; i < size; ++i) {
3435 if (
var !=
nullptr) {
3436 if (i < vars_.size() && vars_[i] ==
var) {
3437 values_[i] = element.
Value();
3438 var_synced_[i] =
true;
3440 const int64_t kUnallocated = -1;
3441 int64_t
index = kUnallocated;
3444 var_synced_[
index] =
true;
3461 template <
typename Filter>
3464 SumObjectiveFilter(
const std::vector<IntVar*>& vars, Filter filter)
3473 ~SumObjectiveFilter()
override {}
3474 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3475 int64_t objective_min, int64_t objective_max)
override {
3476 if (
delta ==
nullptr)
return false;
3477 if (deltadelta->Empty()) {
3497 virtual int64_t CostOfSynchronizedVariable(int64_t
index) = 0;
3499 virtual int64_t CostOfChanges(
const Assignment* changes,
3500 bool incremental) = 0;
3501 bool IsIncremental()
const override {
return true; }
3503 std::string DebugString()
const override {
return "SumObjectiveFilter"; }
3505 int64_t GetSynchronizedObjectiveValue()
const override {
3508 int64_t GetAcceptedObjectiveValue()
const override {
return delta_sum_; }
3520 void OnSynchronize(
const Assignment*
delta)
override {
3523 const int64_t
cost = CostOfSynchronizedVariable(i);
3533 template <
typename Filter>
3534 class BinaryObjectiveFilter :
public SumObjectiveFilter<Filter> {
3536 BinaryObjectiveFilter(
const std::vector<IntVar*>& vars,
3538 : SumObjectiveFilter<Filter>(vars, std::move(filter)),
3539 value_evaluator_(std::move(value_evaluator)) {}
3540 ~BinaryObjectiveFilter()
override {}
3541 int64_t CostOfSynchronizedVariable(int64_t
index)
override {
3546 int64_t CostOfChanges(
const Assignment* changes,
bool incremental)
override {
3547 int64_t total_cost = 0;
3549 for (
const IntVarElement& new_element : container.elements()) {
3550 IntVar*
const var = new_element.Var();
3552 if (this->FindIndex(
var, &
index)) {
3554 int64_t new_cost = 0LL;
3555 if (new_element.Activated()) {
3556 new_cost = value_evaluator_(
index, new_element.Value());
3557 }
else if (
var->Bound()) {
3558 new_cost = value_evaluator_(
index,
var->Min());
3560 total_cost =
CapAdd(total_cost, new_cost);
3573 template <
typename Filter>
3574 class TernaryObjectiveFilter :
public SumObjectiveFilter<Filter> {
3576 TernaryObjectiveFilter(
const std::vector<IntVar*>& vars,
3577 const std::vector<IntVar*>& secondary_vars,
3579 : SumObjectiveFilter<Filter>(vars, std::move(filter)),
3580 secondary_vars_offset_(vars.size()),
3581 secondary_values_(vars.size(), -1),
3582 value_evaluator_(std::move(value_evaluator)) {
3586 ~TernaryObjectiveFilter()
override {}
3587 int64_t CostOfSynchronizedVariable(int64_t
index)
override {
3588 DCHECK_LT(
index, secondary_vars_offset_);
3592 index + secondary_vars_offset_))
3595 int64_t CostOfChanges(
const Assignment* changes,
bool incremental)
override {
3596 int64_t total_cost = 0;
3598 for (
const IntVarElement& new_element : container.elements()) {
3599 IntVar*
const var = new_element.Var();
3601 if (this->FindIndex(
var, &
index) &&
index < secondary_vars_offset_) {
3602 secondary_values_[
index] = -1;
3608 const int max_secondary_index = 2 * secondary_vars_offset_;
3609 for (
const IntVarElement& new_element : container.elements()) {
3610 IntVar*
const var = new_element.Var();
3612 if (new_element.Activated() && this->FindIndex(
var, &
index) &&
3613 index >= secondary_vars_offset_ &&
3615 index < max_secondary_index) {
3616 secondary_values_[
index - secondary_vars_offset_] = new_element.Value();
3619 for (
const IntVarElement& new_element : container.elements()) {
3620 IntVar*
const var = new_element.Var();
3622 if (this->FindIndex(
var, &
index) &&
index < secondary_vars_offset_) {
3624 int64_t new_cost = 0LL;
3625 if (new_element.Activated()) {
3626 new_cost = value_evaluator_(
index, new_element.Value(),
3627 secondary_values_[
index]);
3628 }
else if (
var->Bound() &&
3631 new_cost = value_evaluator_(
3636 total_cost =
CapAdd(total_cost, new_cost);
3646 int secondary_vars_offset_;
3647 std::vector<int64_t> secondary_values_;
3655 switch (filter_enum) {
3657 auto filter = [](int64_t
value, int64_t min_value, int64_t max_value) {
3658 return value <= max_value;
3660 return RevAlloc(
new BinaryObjectiveFilter<decltype(filter)>(
3661 vars, std::move(values), std::move(filter)));
3664 auto filter = [](int64_t
value, int64_t min_value, int64_t max_value) {
3665 return value >= min_value;
3667 return RevAlloc(
new BinaryObjectiveFilter<decltype(filter)>(
3668 vars, std::move(values), std::move(filter)));
3671 auto filter = [](int64_t
value, int64_t min_value, int64_t max_value) {
3672 return min_value <=
value &&
value <= max_value;
3674 return RevAlloc(
new BinaryObjectiveFilter<decltype(filter)>(
3675 vars, std::move(values), std::move(filter)));
3678 LOG(ERROR) <<
"Unknown local search filter enum value";
3685 const std::vector<IntVar*>& vars,
3688 switch (filter_enum) {
3690 auto filter = [](int64_t
value, int64_t min_value, int64_t max_value) {
3691 return value <= max_value;
3693 return RevAlloc(
new TernaryObjectiveFilter<decltype(filter)>(
3694 vars, secondary_vars, std::move(values), std::move(filter)));
3697 auto filter = [](int64_t
value, int64_t min_value, int64_t max_value) {
3698 return value >= min_value;
3700 return RevAlloc(
new TernaryObjectiveFilter<decltype(filter)>(
3701 vars, secondary_vars, std::move(values), std::move(filter)));
3704 auto filter = [](int64_t
value, int64_t min_value, int64_t max_value) {
3705 return min_value <=
value &&
value <= max_value;
3707 return RevAlloc(
new TernaryObjectiveFilter<decltype(filter)>(
3708 vars, secondary_vars, std::move(values), std::move(filter)));
3711 LOG(ERROR) <<
"Unknown local search filter enum value";
3718 int64_t initial_max) {
3719 DCHECK(state_is_valid_);
3720 DCHECK_LE(initial_min, initial_max);
3721 initial_variable_bounds_.push_back({initial_min, initial_max});
3722 variable_bounds_.push_back({initial_min, initial_max});
3723 variable_is_relaxed_.push_back(
false);
3725 const int variable_index = variable_bounds_.size() - 1;
3726 return {
this, variable_index};
3729 void LocalSearchState::RelaxVariableBounds(
int variable_index) {
3730 DCHECK(state_is_valid_);
3731 DCHECK(0 <= variable_index && variable_index < variable_is_relaxed_.size());
3732 if (!variable_is_relaxed_[variable_index]) {
3733 variable_is_relaxed_[variable_index] =
true;
3734 saved_variable_bounds_trail_.emplace_back(variable_bounds_[variable_index],
3736 variable_bounds_[variable_index] = initial_variable_bounds_[variable_index];
3740 int64_t LocalSearchState::VariableMin(
int variable_index)
const {
3741 DCHECK(state_is_valid_);
3742 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3743 return variable_bounds_[variable_index].min;
3746 int64_t LocalSearchState::VariableMax(
int variable_index)
const {
3747 DCHECK(state_is_valid_);
3748 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3749 return variable_bounds_[variable_index].max;
3752 bool LocalSearchState::TightenVariableMin(
int variable_index,
3753 int64_t min_value) {
3754 DCHECK(state_is_valid_);
3755 DCHECK(variable_is_relaxed_[variable_index]);
3756 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3757 Bounds&
bounds = variable_bounds_[variable_index];
3758 if (
bounds.max < min_value) {
3759 state_is_valid_ =
false;
3762 return state_is_valid_;
3765 bool LocalSearchState::TightenVariableMax(
int variable_index,
3766 int64_t max_value) {
3767 DCHECK(state_is_valid_);
3768 DCHECK(variable_is_relaxed_[variable_index]);
3769 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3770 Bounds&
bounds = variable_bounds_[variable_index];
3771 if (
bounds.min > max_value) {
3772 state_is_valid_ =
false;
3775 return state_is_valid_;
3782 DCHECK(state_is_valid_);
3783 saved_variable_bounds_trail_.clear();
3784 variable_is_relaxed_.assign(variable_is_relaxed_.size(),
false);
3788 for (
const auto& bounds_index : saved_variable_bounds_trail_) {
3789 DCHECK(variable_is_relaxed_[bounds_index.second]);
3790 variable_bounds_[bounds_index.second] = bounds_index.first;
3792 saved_variable_bounds_trail_.clear();
3793 variable_is_relaxed_.assign(variable_is_relaxed_.size(),
false);
3794 state_is_valid_ =
true;
3802 std::string
DebugString()
const override {
return "LocalSearchProfiler"; }
3804 operator_stats_.clear();
3805 filter_stats_.clear();
3809 if (
solver()->TopLevelSearch() ==
solver()->ActiveSearch()) {
3813 template <
typename Callback>
3816 if (db->seconds() == 0)
continue;
3817 callback(db->name(), db->seconds());
3821 template <
typename Callback>
3823 std::vector<const LocalSearchOperator*> operators;
3824 for (
const auto& stat : operator_stats_) {
3825 operators.push_back(stat.first);
3828 operators.begin(), operators.end(),
3830 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3831 gtl::FindOrDie(operator_stats_, op2).neighbors;
3834 const OperatorStats& stats =
gtl::FindOrDie(operator_stats_, op);
3835 callback(op->DebugString(), stats.neighbors, stats.filtered_neighbors,
3836 stats.accepted_neighbors, stats.seconds);
3840 template <
typename Callback>
3842 absl::flat_hash_map<std::string, std::vector<const LocalSearchFilter*>>
3843 filters_per_context;
3844 for (
const auto& stat : filter_stats_) {
3845 filters_per_context[stat.second.context].push_back(stat.first);
3847 for (
auto& [
context, filters] : filters_per_context) {
3848 std::sort(filters.begin(), filters.end(),
3851 return gtl::FindOrDie(filter_stats_, filter1).calls >
3852 gtl::FindOrDie(filter_stats_, filter2).calls;
3855 const FilterStats& stats =
gtl::FindOrDie(filter_stats_, filter);
3862 LocalSearchStatistics statistics_proto;
3864 [&statistics_proto](
const std::string&
name,
double duration_seconds) {
3865 LocalSearchStatistics::FirstSolutionStatistics*
const
3866 first_solution_statistics =
3867 statistics_proto.add_first_solution_statistics();
3868 first_solution_statistics->set_strategy(
name);
3869 first_solution_statistics->set_duration_seconds(duration_seconds);
3872 const std::string&
name,
3873 int64_t num_neighbors,
3874 int64_t num_filtered_neighbors,
3875 int64_t num_accepted_neighbors,
3876 double duration_seconds) {
3877 LocalSearchStatistics::LocalSearchOperatorStatistics*
const
3878 local_search_operator_statistics =
3879 statistics_proto.add_local_search_operator_statistics();
3880 local_search_operator_statistics->set_local_search_operator(
name);
3881 local_search_operator_statistics->set_num_neighbors(num_neighbors);
3882 local_search_operator_statistics->set_num_filtered_neighbors(
3883 num_filtered_neighbors);
3884 local_search_operator_statistics->set_num_accepted_neighbors(
3885 num_accepted_neighbors);
3886 local_search_operator_statistics->set_duration_seconds(duration_seconds);
3890 const std::string&
name,
3891 int64_t num_calls, int64_t num_rejects,
3892 double duration_seconds) {
3893 LocalSearchStatistics::LocalSearchFilterStatistics*
const
3894 local_search_filter_statistics =
3895 statistics_proto.add_local_search_filter_statistics();
3896 local_search_filter_statistics->set_local_search_filter(
name);
3897 local_search_filter_statistics->set_num_calls(num_calls);
3898 local_search_filter_statistics->set_num_rejects(num_rejects);
3899 local_search_filter_statistics->set_duration_seconds(duration_seconds);
3900 local_search_filter_statistics->set_num_rejects_per_second(
3901 num_rejects / duration_seconds);
3902 local_search_filter_statistics->set_context(
context);
3904 statistics_proto.set_total_num_neighbors(
solver()->neighbors());
3905 statistics_proto.set_total_num_filtered_neighbors(
3906 solver()->filtered_neighbors());
3907 statistics_proto.set_total_num_accepted_neighbors(
3908 solver()->accepted_neighbors());
3909 return statistics_proto;
3912 std::string overview;
3913 size_t max_name_size = 0;
3915 [&max_name_size](
const std::string&
name,
double duration_seconds) {
3916 max_name_size =
std::max(max_name_size,
name.length());
3918 if (max_name_size > 0) {
3919 absl::StrAppendFormat(&overview,
3920 "First solution statistics:\n%*s | Time (s)\n",
3923 [&overview, max_name_size](
const std::string&
name,
3924 double duration_seconds) {
3925 absl::StrAppendFormat(&overview,
"%*s | %7.2g\n", max_name_size,
3926 name, duration_seconds);
3931 [&max_name_size](
const std::string&
name, int64_t num_neighbors,
3932 int64_t num_filtered_neighbors,
3933 int64_t num_accepted_neighbors,
3934 double duration_seconds) {
3935 max_name_size =
std::max(max_name_size,
name.length());
3937 if (max_name_size > 0) {
3938 absl::StrAppendFormat(
3940 "Local search operator statistics:\n%*s | Neighbors | Filtered "
3941 "| Accepted | Time (s)\n",
3943 OperatorStats total_stats;
3945 [&overview, &total_stats, max_name_size](
3946 const std::string&
name, int64_t num_neighbors,
3947 int64_t num_filtered_neighbors, int64_t num_accepted_neighbors,
3948 double duration_seconds) {
3949 absl::StrAppendFormat(
3950 &overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n", max_name_size,
3951 name, num_neighbors, num_filtered_neighbors,
3952 num_accepted_neighbors, duration_seconds);
3953 total_stats.neighbors += num_neighbors;
3954 total_stats.filtered_neighbors += num_filtered_neighbors;
3955 total_stats.accepted_neighbors += num_accepted_neighbors;
3956 total_stats.seconds += duration_seconds;
3958 absl::StrAppendFormat(
3959 &overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n", max_name_size,
3960 "Total", total_stats.neighbors, total_stats.filtered_neighbors,
3961 total_stats.accepted_neighbors, total_stats.seconds);
3965 [&max_name_size](
const std::string&
context,
const std::string&
name,
3966 int64_t num_calls, int64_t num_rejects,
3967 double duration_seconds) {
3968 max_name_size =
std::max(max_name_size,
name.length());
3970 if (max_name_size > 0) {
3971 std::optional<std::string> filter_context;
3972 FilterStats total_filter_stats;
3974 [&overview, &filter_context, &total_filter_stats, max_name_size](
3975 const std::string&
context,
const std::string&
name,
3976 int64_t num_calls, int64_t num_rejects,
double duration_seconds) {
3977 if (!filter_context.has_value() ||
3978 filter_context.value() !=
context) {
3979 if (filter_context.has_value()) {
3980 absl::StrAppendFormat(
3981 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n",
3982 max_name_size,
"Total", total_filter_stats.calls,
3983 total_filter_stats.rejects, total_filter_stats.seconds,
3984 total_filter_stats.rejects / total_filter_stats.seconds);
3985 total_filter_stats = {};
3988 absl::StrAppendFormat(
3990 "Local search filter statistics%s:\n%*s | Calls | "
3991 " Rejects | Time (s) | Rejects/s\n",
3995 absl::StrAppendFormat(
3996 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n",
3997 max_name_size,
name, num_calls, num_rejects, duration_seconds,
3998 num_rejects / duration_seconds);
3999 total_filter_stats.calls += num_calls;
4000 total_filter_stats.rejects += num_rejects;
4001 total_filter_stats.seconds += duration_seconds;
4003 absl::StrAppendFormat(
4004 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n", max_name_size,
4005 "Total", total_filter_stats.calls, total_filter_stats.rejects,
4006 total_filter_stats.seconds,
4007 total_filter_stats.rejects / total_filter_stats.seconds);
4014 if (last_operator_ != op->
Self()) {
4016 last_operator_ = op->
Self();
4022 if (neighbor_found) {
4023 operator_stats_[op->
Self()].neighbors++;
4028 bool neighbor_found)
override {
4029 if (neighbor_found) {
4030 operator_stats_[op->
Self()].filtered_neighbors++;
4035 bool neighbor_found)
override {
4036 if (neighbor_found) {
4037 operator_stats_[op->
Self()].accepted_neighbors++;
4041 FilterStats& filter_stats = filter_stats_[filter];
4042 filter_stats.calls++;
4043 filter_stats.context = solver()->
context();
4044 filter_timer_.Start();
4047 filter_timer_.Stop();
4048 auto& stats = filter_stats_[filter];
4049 stats.seconds += filter_timer_.Get();
4056 profiled_decision_builders_.push_back(profiled_db);
4062 if (last_operator_ !=
nullptr) {
4064 operator_stats_[last_operator_].seconds += timer_.
Get();
4069 struct OperatorStats {
4070 int64_t neighbors = 0;
4071 int64_t filtered_neighbors = 0;
4072 int64_t accepted_neighbors = 0;
4076 struct FilterStats {
4078 int64_t rejects = 0;
4084 const LocalSearchOperator* last_operator_ =
nullptr;
4085 absl::flat_hash_map<const LocalSearchOperator*, OperatorStats>
4087 absl::flat_hash_map<const LocalSearchFilter*, FilterStats> filter_stats_;
4089 std::vector<ProfiledDecisionBuilder*> profiled_decision_builders_;
4094 if (IsLocalSearchProfilingEnabled()) {
4097 local_search_profiler_->AddFirstSolutionProfiledDecisionBuilder(
4117 std::string Solver::LocalSearchProfile()
const {
4118 if (local_search_profiler_ !=
nullptr) {
4124 LocalSearchStatistics Solver::GetLocalSearchStatistics()
const {
4125 if (local_search_profiler_ !=
nullptr) {
4126 return local_search_profiler_->ExportToLocalSearchStatistics();
4128 return LocalSearchStatistics();
4131 void LocalSearchFilterManager::FindIncrementalEventEnd() {
4132 const int num_events = events_.size();
4133 incremental_events_end_ = num_events;
4134 int last_priority = -1;
4135 for (
int e = num_events - 1; e >= 0; --e) {
4136 const auto& [filter, event_type, priority] = events_[e];
4137 if (priority != last_priority) {
4138 incremental_events_end_ = e + 1;
4139 last_priority = priority;
4141 if (filter->IsIncremental())
break;
4145 LocalSearchFilterManager::LocalSearchFilterManager(
4146 std::vector<LocalSearchFilter*> filters)
4147 : synchronized_value_(std::numeric_limits<int64_t>::
min()),
4148 accepted_value_(std::numeric_limits<int64_t>::
min()) {
4149 events_.reserve(2 * filters.size());
4152 events_.push_back({filter, FilterEventType::kRelax, priority++});
4155 events_.push_back({filter, FilterEventType::kAccept, priority++});
4157 FindIncrementalEventEnd();
4161 std::vector<FilterEvent> filter_events)
4162 : events_(std::move(filter_events)),
4163 synchronized_value_(std::numeric_limits<int64_t>::
min()),
4164 accepted_value_(std::numeric_limits<int64_t>::
min()) {
4165 std::sort(events_.begin(), events_.end(),
4167 return e1.priority < e2.priority;
4169 FindIncrementalEventEnd();
4175 for (
int e = last_event_called_; e >= 0; --e) {
4176 const auto [filter, event_type, _priority] = events_[e];
4177 if (event_type == FilterEventType::kRelax) filter->Revert();
4179 last_event_called_ = -1;
4188 int64_t objective_min,
4189 int64_t objective_max) {
4191 accepted_value_ = 0;
4192 bool feasible =
true;
4193 bool reordered =
false;
4194 int events_end = events_.size();
4195 for (
int e = 0; e < events_end; ++e) {
4196 last_event_called_ = e;
4197 const auto [filter, event_type, priority] = events_[e];
4198 switch (event_type) {
4199 case FilterEventType::kAccept: {
4200 if (!feasible && !filter->IsIncremental())
continue;
4202 const bool accept = filter->Accept(
4203 delta, deltadelta,
CapSub(objective_min, accepted_value_),
4204 CapSub(objective_max, accepted_value_));
4206 if (monitor !=
nullptr) monitor->
EndFiltering(filter, !accept);
4209 CapAdd(accepted_value_, filter->GetAcceptedObjectiveValue());
4211 feasible = accepted_value_ <= objective_max;
4214 events_end = incremental_events_end_;
4219 int to_move = e - 1;
4220 if (to_move >= 0 && events_[to_move].filter == filter) --to_move;
4221 if (to_move >= 0 && events_[to_move].priority == priority) {
4222 std::rotate(events_.begin() + to_move,
4223 events_.begin() + to_move + 1,
4224 events_.begin() + e + 1);
4230 case FilterEventType::kRelax: {
4231 filter->Relax(
delta, deltadelta);
4235 LOG(FATAL) <<
"Unknown filter event type.";
4246 const bool reset_to_assignment =
delta ==
nullptr ||
delta->Empty();
4248 for (
auto [filter, event_type, unused_priority] : events_) {
4249 switch (event_type) {
4250 case FilterEventType::kAccept: {
4253 case FilterEventType::kRelax: {
4254 if (reset_to_assignment) {
4256 filter->Relax(assignment,
nullptr);
4258 filter->Relax(
delta,
nullptr);
4263 LOG(FATAL) <<
"Unknown filter event type.";
4268 synchronized_value_ = 0;
4270 switch (event_type) {
4271 case FilterEventType::kAccept: {
4272 filter->Synchronize(assignment,
delta);
4273 synchronized_value_ =
CapAdd(synchronized_value_,
4274 filter->GetSynchronizedObjectiveValue());
4277 case FilterEventType::kRelax: {
4278 filter->Commit(assignment,
delta);
4282 LOG(FATAL) <<
"Unknown filter event type.";
4299 std::string
DebugString()
const override {
return "FindOneNeighbor"; }
4303 int64_t objective_min, int64_t objective_max);
4304 void SynchronizeAll(
Solver* solver);
4307 IntVar*
const objective_;
4308 std::unique_ptr<Assignment> reference_assignment_;
4309 std::unique_ptr<Assignment> last_synchronized_assignment_;
4316 bool neighbor_found_;
4318 int64_t solutions_since_last_check_;
4319 int64_t check_period_;
4321 bool has_checked_assignment_ =
false;
4336 : assignment_(assignment),
4338 reference_assignment_(new
Assignment(assignment_)),
4339 filter_assignment_delta_(assignment->solver()->MakeAssignment()),
4341 ls_operator_(ls_operator),
4342 sub_decision_builder_(sub_decision_builder),
4344 original_limit_(limit),
4345 neighbor_found_(false),
4346 filter_manager_(filter_manager),
4347 solutions_since_last_check_(0),
4349 assignment_->solver()->
parameters().check_solution_period()),
4350 last_checked_assignment_(assignment) {
4351 CHECK(
nullptr != assignment);
4352 CHECK(
nullptr != ls_operator);
4356 if (
nullptr == limit) {
4364 VLOG(1) <<
"Disabling neighbor-check skipping outside of first accept.";
4371 VLOG(1) <<
"Disabling neighbor-check skipping for LNS.";
4375 if (!reference_assignment_->HasObjective()) {
4376 reference_assignment_->AddObjective(objective_);
4381 CHECK(
nullptr != solver);
4383 if (original_limit_ !=
nullptr) {
4384 limit_->
Copy(original_limit_);
4391 if (!neighbor_found_) {
4399 SynchronizeAll(solver);
4409 if (sub_decision_builder_) {
4410 restore = solver->
Compose(restore, sub_decision_builder_);
4418 delta->ClearObjective();
4419 deltadelta->
Clear();
4421 if (++counter >= absl::GetFlag(FLAGS_cp_local_search_sync_frequency) &&
4422 pool_->
SyncNeeded(reference_assignment_.get())) {
4425 SynchronizeAll(solver);
4428 bool has_neighbor =
false;
4429 if (!limit_->
Check()) {
4433 ls_operator_, has_neighbor,
delta, deltadelta);
4436 if (has_neighbor && !solver->IsUncheckedSolutionLimitReached()) {
4437 solver->neighbors_ += 1;
4444 const bool mh_filter =
4449 objective_min = objective_->
Min();
4450 objective_max = objective_->
Max();
4452 if (
delta->HasObjective() &&
delta->Objective() == objective_) {
4453 objective_min =
std::max(objective_min,
delta->ObjectiveMin());
4454 objective_max =
std::min(objective_max,
delta->ObjectiveMax());
4456 const bool move_filter = FilterAccept(solver,
delta, deltadelta,
4457 objective_min, objective_max);
4459 ls_operator_, mh_filter && move_filter);
4460 if (!mh_filter || !move_filter) {
4461 if (filter_manager_ !=
nullptr) filter_manager_->
Revert();
4464 solver->filtered_neighbors_ += 1;
4465 if (
delta->HasObjective()) {
4477 const bool check_solution = (solutions_since_last_check_ == 0) ||
4480 !
delta->AreAllElementsBound();
4481 if (has_checked_assignment_) solutions_since_last_check_++;
4482 if (solutions_since_last_check_ >= check_period_) {
4483 solutions_since_last_check_ = 0;
4485 const bool accept = !check_solution || solver->
SolveAndCommit(restore);
4489 solver->accepted_neighbors_ += 1;
4490 if (check_solution) {
4493 assignment_->
Store();
4495 neighbor_found_ =
true;
4496 has_checked_assignment_ =
true;
4510 solver->IncrementUncheckedSolutionCounter();
4512 SynchronizeAll(solver);
4515 neighbor_found_ =
true;
4517 if (filter_manager_ !=
nullptr) filter_manager_->
Revert();
4518 if (check_period_ > 1 && has_checked_assignment_) {
4524 VLOG(1) <<
"Imperfect filtering detected, backtracking to last "
4525 "checked solution and checking all solutions.";
4527 solutions_since_last_check_ = 0;
4529 SynchronizeAll(solver);
4534 if (neighbor_found_) {
4546 has_checked_assignment_ =
true;
4554 SynchronizeAll(solver);
4567 int64_t objective_min,
4568 int64_t objective_max) {
4569 if (filter_manager_ ==
nullptr)
return true;
4571 return filter_manager_->
Accept(monitor,
delta, deltadelta, objective_min,
4577 template <
typename Container>
4578 void AddDeltaElements(
const Container& old_container,
4579 const Container& new_container, Assignment*
delta) {
4580 for (
const auto& new_element : new_container.elements()) {
4581 const auto var = new_element.
Var();
4582 const auto old_element_ptr = old_container.ElementPtrOrNull(
var);
4583 if (old_element_ptr ==
nullptr || *old_element_ptr != new_element) {
4584 delta->FastAdd(
var)->Copy(new_element);
4589 void MakeDelta(
const Assignment* old_assignment,
4590 const Assignment* new_assignment, Assignment*
delta) {
4591 DCHECK_NE(
delta,
nullptr);
4593 AddDeltaElements(old_assignment->IntVarContainer(),
4594 new_assignment->IntVarContainer(),
delta);
4595 AddDeltaElements(old_assignment->IntervalVarContainer(),
4596 new_assignment->IntervalVarContainer(),
delta);
4597 AddDeltaElements(old_assignment->SequenceVarContainer(),
4598 new_assignment->SequenceVarContainer(),
delta);
4602 void FindOneNeighbor::SynchronizeAll(Solver* solver) {
4603 Assignment*
const reference_assignment = reference_assignment_.get();
4605 neighbor_found_ =
false;
4607 solver->GetLocalSearchMonitor()->BeginOperatorStart();
4608 ls_operator_->
Start(reference_assignment);
4609 if (filter_manager_ !=
nullptr) {
4610 Assignment*
delta =
nullptr;
4611 if (last_synchronized_assignment_ ==
nullptr) {
4612 last_synchronized_assignment_ =
4613 std::make_unique<Assignment>(reference_assignment);
4615 MakeDelta(last_synchronized_assignment_.get(), reference_assignment,
4616 filter_assignment_delta_);
4617 delta = filter_assignment_delta_;
4618 last_synchronized_assignment_->Copy(reference_assignment);
4622 solver->GetLocalSearchMonitor()->EndOperatorStart();
4635 solution_pool_(pool),
4642 return "LocalSearchPhaseParameters";
4649 return sub_decision_builder_;
4653 return filter_manager_;
4657 IntVar*
const objective_;
4669 ls_operator, sub_decision_builder,
4677 ls_operator, sub_decision_builder,
4686 ls_operator, sub_decision_builder,
4687 limit, filter_manager);
4695 sub_decision_builder,
nullptr,
nullptr);
4703 sub_decision_builder, limit,
nullptr);
4712 sub_decision_builder, limit,
4726 class NestedSolveDecision :
public Decision {
4729 enum StateType { DECISION_PENDING, DECISION_FAILED, DECISION_FOUND };
4731 NestedSolveDecision(DecisionBuilder*
const db,
bool restore,
4732 const std::vector<SearchMonitor*>& monitors);
4733 NestedSolveDecision(DecisionBuilder*
const db,
bool restore);
4734 ~NestedSolveDecision()
override {}
4735 void Apply(Solver*
const solver)
override;
4736 void Refute(Solver*
const solver)
override;
4737 std::string DebugString()
const override {
return "NestedSolveDecision"; }
4738 int state()
const {
return state_; }
4741 DecisionBuilder*
const db_;
4743 std::vector<SearchMonitor*> monitors_;
4747 NestedSolveDecision::NestedSolveDecision(
4748 DecisionBuilder*
const db,
bool restore,
4749 const std::vector<SearchMonitor*>& monitors)
4752 monitors_(monitors),
4753 state_(DECISION_PENDING) {
4754 CHECK(
nullptr != db);
4757 NestedSolveDecision::NestedSolveDecision(DecisionBuilder*
const db,
4759 : db_(db), restore_(restore), state_(DECISION_PENDING) {
4760 CHECK(
nullptr != db);
4763 void NestedSolveDecision::Apply(Solver*
const solver) {
4764 CHECK(
nullptr != solver);
4766 if (solver->Solve(db_, monitors_)) {
4767 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FOUND));
4769 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FAILED));
4772 if (solver->SolveAndCommit(db_, monitors_)) {
4773 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FOUND));
4775 solver->SaveAndSetValue(&state_,
static_cast<int>(DECISION_FAILED));
4780 void NestedSolveDecision::Refute(Solver*
const solver) {}
4791 class LocalSearch :
public DecisionBuilder {
4793 LocalSearch(Assignment*
const assignment, IntVar* objective,
4794 SolutionPool*
const pool, LocalSearchOperator*
const ls_operator,
4795 DecisionBuilder*
const sub_decision_builder,
4796 RegularLimit*
const limit,
4797 LocalSearchFilterManager* filter_manager);
4800 LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4801 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4802 LocalSearchOperator*
const ls_operator,
4803 DecisionBuilder*
const sub_decision_builder,
4804 RegularLimit*
const limit,
4805 LocalSearchFilterManager* filter_manager);
4806 LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4807 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4808 DecisionBuilder*
const first_solution_sub_decision_builder,
4809 LocalSearchOperator*
const ls_operator,
4810 DecisionBuilder*
const sub_decision_builder,
4811 RegularLimit*
const limit,
4812 LocalSearchFilterManager* filter_manager);
4813 LocalSearch(
const std::vector<SequenceVar*>& vars, IntVar* objective,
4814 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4815 LocalSearchOperator*
const ls_operator,
4816 DecisionBuilder*
const sub_decision_builder,
4817 RegularLimit*
const limit,
4818 LocalSearchFilterManager* filter_manager);
4819 ~LocalSearch()
override;
4820 Decision* Next(Solver*
const solver)
override;
4821 std::string DebugString()
const override {
return "LocalSearch"; }
4822 void Accept(ModelVisitor*
const visitor)
const override;
4825 void PushFirstSolutionDecision(DecisionBuilder* first_solution);
4826 void PushLocalSearchDecision();
4829 Assignment* assignment_;
4831 SolutionPool*
const pool_;
4832 LocalSearchOperator*
const ls_operator_;
4833 DecisionBuilder*
const first_solution_sub_decision_builder_;
4834 DecisionBuilder*
const sub_decision_builder_;
4835 std::vector<NestedSolveDecision*> nested_decisions_;
4836 int nested_decision_index_;
4837 RegularLimit*
const limit_;
4838 LocalSearchFilterManager*
const filter_manager_;
4842 LocalSearch::LocalSearch(Assignment*
const assignment, IntVar* objective,
4843 SolutionPool*
const pool,
4844 LocalSearchOperator*
const ls_operator,
4845 DecisionBuilder*
const sub_decision_builder,
4846 RegularLimit*
const limit,
4847 LocalSearchFilterManager* filter_manager)
4848 : assignment_(nullptr),
4851 ls_operator_(ls_operator),
4852 first_solution_sub_decision_builder_(sub_decision_builder),
4853 sub_decision_builder_(sub_decision_builder),
4854 nested_decision_index_(0),
4856 filter_manager_(filter_manager),
4857 has_started_(false) {
4858 CHECK(
nullptr != assignment);
4859 CHECK(
nullptr != ls_operator);
4860 Solver*
const solver = assignment->solver();
4861 assignment_ = solver->GetOrCreateLocalSearchState();
4862 assignment_->Copy(assignment);
4863 DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment);
4864 PushFirstSolutionDecision(restore);
4865 PushLocalSearchDecision();
4868 LocalSearch::LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4869 SolutionPool*
const pool,
4870 DecisionBuilder*
const first_solution,
4871 LocalSearchOperator*
const ls_operator,
4872 DecisionBuilder*
const sub_decision_builder,
4873 RegularLimit*
const limit,
4874 LocalSearchFilterManager* filter_manager)
4875 : assignment_(nullptr),
4878 ls_operator_(ls_operator),
4879 first_solution_sub_decision_builder_(sub_decision_builder),
4880 sub_decision_builder_(sub_decision_builder),
4881 nested_decision_index_(0),
4883 filter_manager_(filter_manager),
4884 has_started_(false) {
4885 CHECK(
nullptr != first_solution);
4886 CHECK(
nullptr != ls_operator);
4887 CHECK(!vars.empty());
4888 Solver*
const solver = vars[0]->solver();
4889 assignment_ = solver->GetOrCreateLocalSearchState();
4890 assignment_->Add(vars);
4891 PushFirstSolutionDecision(first_solution);
4892 PushLocalSearchDecision();
4895 LocalSearch::LocalSearch(
4896 const std::vector<IntVar*>& vars, IntVar* objective,
4897 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4898 DecisionBuilder*
const first_solution_sub_decision_builder,
4899 LocalSearchOperator*
const ls_operator,
4900 DecisionBuilder*
const sub_decision_builder, RegularLimit*
const limit,
4901 LocalSearchFilterManager* filter_manager)
4902 : assignment_(nullptr),
4905 ls_operator_(ls_operator),
4906 first_solution_sub_decision_builder_(first_solution_sub_decision_builder),
4907 sub_decision_builder_(sub_decision_builder),
4908 nested_decision_index_(0),
4910 filter_manager_(filter_manager),
4911 has_started_(false) {
4912 CHECK(
nullptr != first_solution);
4913 CHECK(
nullptr != ls_operator);
4914 CHECK(!vars.empty());
4915 Solver*
const solver = vars[0]->solver();
4916 assignment_ = solver->GetOrCreateLocalSearchState();
4917 assignment_->Add(vars);
4918 PushFirstSolutionDecision(first_solution);
4919 PushLocalSearchDecision();
4922 LocalSearch::LocalSearch(
const std::vector<SequenceVar*>& vars,
4923 IntVar* objective, SolutionPool*
const pool,
4924 DecisionBuilder*
const first_solution,
4925 LocalSearchOperator*
const ls_operator,
4926 DecisionBuilder*
const sub_decision_builder,
4927 RegularLimit*
const limit,
4928 LocalSearchFilterManager* filter_manager)
4929 : assignment_(nullptr),
4932 ls_operator_(ls_operator),
4933 first_solution_sub_decision_builder_(sub_decision_builder),
4934 sub_decision_builder_(sub_decision_builder),
4935 nested_decision_index_(0),
4937 filter_manager_(filter_manager),
4938 has_started_(false) {
4939 CHECK(
nullptr != first_solution);
4940 CHECK(
nullptr != ls_operator);
4941 CHECK(!vars.empty());
4942 Solver*
const solver = vars[0]->solver();
4943 assignment_ = solver->GetOrCreateLocalSearchState();
4944 assignment_->Add(vars);
4945 PushFirstSolutionDecision(first_solution);
4946 PushLocalSearchDecision();
4949 LocalSearch::~LocalSearch() {}
4952 void LocalSearch::Accept(ModelVisitor*
const visitor)
const {
4953 DCHECK(assignment_ !=
nullptr);
4956 const std::vector<IntVarElement>& elements =
4958 if (!elements.empty()) {
4959 std::vector<IntVar*> vars;
4960 for (
const IntVarElement& elem : elements) {
4961 vars.push_back(elem.Var());
4966 const std::vector<IntervalVarElement>& interval_elements =
4968 if (!interval_elements.empty()) {
4969 std::vector<IntervalVar*> interval_vars;
4970 for (
const IntervalVarElement& elem : interval_elements) {
4971 interval_vars.push_back(elem.Var());
4984 Decision* LocalSearch::Next(Solver*
const solver) {
4985 CHECK(
nullptr != solver);
4986 CHECK_LT(0, nested_decisions_.size());
4987 if (!has_started_) {
4988 nested_decision_index_ = 0;
4989 solver->SaveAndSetValue(&has_started_,
true);
4990 }
else if (nested_decision_index_ < 0) {
4993 NestedSolveDecision* decision = nested_decisions_[nested_decision_index_];
4994 const int state = decision->state();
4996 case NestedSolveDecision::DECISION_FAILED: {
5000 ls_operator_->
Reset();
5002 nested_decision_index_ = -1;
5007 case NestedSolveDecision::DECISION_PENDING: {
5010 const int32_t kLocalSearchBalancedTreeDepth = 32;
5011 const int depth = solver->SearchDepth();
5012 if (depth < kLocalSearchBalancedTreeDepth) {
5013 return solver->balancing_decision();
5015 if (depth > kLocalSearchBalancedTreeDepth) {
5020 case NestedSolveDecision::DECISION_FOUND: {
5022 if (nested_decision_index_ + 1 < nested_decisions_.size()) {
5023 ++nested_decision_index_;
5028 LOG(ERROR) <<
"Unknown local search state";
5035 void LocalSearch::PushFirstSolutionDecision(DecisionBuilder* first_solution) {
5036 CHECK(first_solution);
5037 Solver*
const solver = assignment_->
solver();
5039 DecisionBuilder* first_solution_and_store = solver->Compose(
5040 solver->MakeProfiledDecisionBuilderWrapper(first_solution),
5041 first_solution_sub_decision_builder_, store);
5042 std::vector<SearchMonitor*> monitor;
5043 monitor.push_back(
limit_);
5044 nested_decisions_.push_back(solver->RevAlloc(
5045 new NestedSolveDecision(first_solution_and_store,
false, monitor)));
5048 void LocalSearch::PushLocalSearchDecision() {
5049 Solver*
const solver = assignment_->
solver();
5050 DecisionBuilder* find_neighbors = solver->
RevAlloc(
5051 new FindOneNeighbor(assignment_,
objective_, pool_, ls_operator_,
5052 sub_decision_builder_,
limit_, filter_manager_));
5053 nested_decisions_.push_back(
5054 solver->RevAlloc(
new NestedSolveDecision(find_neighbors,
false)));
5057 class DefaultSolutionPool :
public SolutionPool {
5059 DefaultSolutionPool() {}
5061 ~DefaultSolutionPool()
override {}
5063 void Initialize(Assignment*
const assignment)
override {
5064 reference_assignment_ = std::make_unique<Assignment>(assignment);
5067 void RegisterNewSolution(Assignment*
const assignment)
override {
5068 reference_assignment_->CopyIntersection(assignment);
5071 void GetNextSolution(Assignment*
const assignment)
override {
5072 assignment->CopyIntersection(reference_assignment_.get());
5075 bool SyncNeeded(Assignment*
const local_assignment)
override {
return false; }
5077 std::string DebugString()
const override {
return "DefaultSolutionPool"; }
5080 std::unique_ptr<Assignment> reference_assignment_;
5085 return RevAlloc(
new DefaultSolutionPool());
5112 first_solution, first_solution_sub_decision_builder,
5118 const std::vector<SequenceVar*>& vars,
DecisionBuilder* first_solution,
const std::vector< E > & elements() const
const E & Element(const V *const var) const
An Assignment is a variable -> domains mapping, used to report solutions to the user.
const IntContainer & IntVarContainer() const
void SetObjectiveValue(int64_t value)
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
IntVar * Objective() const
const IntervalContainer & IntervalVarContainer() const
std::string DebugString() const override
~BaseInactiveNodeToPathOperator() override
BaseInactiveNodeToPathOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, int number_of_base_nodes, std::function< int(int64_t)> start_empty_path_class)
int64_t GetInactiveNode() const
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
This is the base class for building an Lns operator.
virtual bool NextFragment()=0
void AppendToFragment(int index)
BaseLns(const std::vector< IntVar * > &vars)
bool MakeOneNeighbor() override
This method should not be overridden. Override NextFragment() instead.
virtual void InitFragments()
A BaseObject is the root of all reversibly allocated objects.
virtual std::string DebugString() const
ChangeValue(const std::vector< IntVar * > &vars)
virtual int64_t ModifyValue(int64_t index, int64_t value)=0
bool MakeOneNeighbor() override
This method should not be overridden. Override ModifyValue() instead.
bool MakeNeighbor() override
Cross(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
A DecisionBuilder is responsible for creating the search tree.
A Decision represents a choice point in the search tree.
bool MakeNeighbor() override
Exchange(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
bool MakeNeighbor() override
ExtendedSwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
~ExtendedSwapActiveOperator() override
std::string DebugString() const override
~FindOneNeighbor() override
FindOneNeighbor(Assignment *const assignment, IntVar *objective, SolutionPool *const pool, LocalSearchOperator *const ls_operator, DecisionBuilder *const sub_decision_builder, const RegularLimit *const limit, LocalSearchFilterManager *filter_manager)
Decision * Next(Solver *const solver) override
This is the main method of the decision builder class.
std::string DebugString() const override
void ChangeCostMatrix(CostFunction cost)
std::vector< int > TravelingSalesmanPath()
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
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.
bool FindIndex(IntVar *const var, int64_t *index) const
IntVarLocalSearchFilter(const std::vector< IntVar * > &vars)
int64_t Value(int index) const
IntVar * Var(int index) const
void AddVars(const std::vector< IntVar * > &vars)
Add variables to "track" to the filter.
bool IsVarSynced(int index) const
Specialization of LocalSearchOperator built from an array of IntVars which specifies the scope of the...
void SetValue(int64_t index, int64_t value)
void Deactivate(int64_t index)
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
OnStart() should really be protected, but then SWIG doesn't see it.
void RevertChanges(bool change_was_incremental)
bool ApplyChanges(Assignment *delta, Assignment *deltadelta) const
int64_t OldValue(int64_t index) const
int64_t Value(int64_t index) const
Returns the value in the current assignment of the variable of given index.
virtual bool MakeOneNeighbor()
Creates a new neighbor.
IntVar * Var(int64_t index) const
Returns the variable of given index.
void AddVars(const std::vector< IntVar * > &vars)
bool MakeNeighbor() override
LinKernighan(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, const Solver::IndexEvaluator3 &evaluator, bool topt)
std::string DebugString() const override
Local Search Filters are used for fast neighbor pruning.
Filter manager: when a move is made, filters are executed to decide whether the solution is feasible ...
int64_t GetAcceptedObjectiveValue() const
LocalSearchFilterManager(std::vector< FilterEvent > filter_events)
bool Accept(LocalSearchMonitor *const monitor, const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max)
Returns true iff all filters return true, and the sum of their accepted objectives is between objecti...
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
virtual void EndMakeNextNeighbor(const LocalSearchOperator *op, bool neighbor_found, const Assignment *delta, const Assignment *deltadelta)=0
virtual void EndAcceptNeighbor(const LocalSearchOperator *op, bool neighbor_found)=0
virtual void BeginMakeNextNeighbor(const LocalSearchOperator *op)=0
virtual void EndFiltering(const LocalSearchFilter *filter, bool reject)=0
virtual void BeginFilterNeighbor(const LocalSearchOperator *op)=0
virtual void BeginAcceptNeighbor(const LocalSearchOperator *op)=0
virtual void BeginFiltering(const LocalSearchFilter *filter)=0
virtual void EndFilterNeighbor(const LocalSearchOperator *op, bool neighbor_found)=0
The base class for all local search operators.
virtual bool HasFragments() const
virtual bool HoldsDelta() const
virtual const LocalSearchOperator * Self() const
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
virtual void Start(const Assignment *assignment)=0
LocalSearchOperator * ls_operator() const
~LocalSearchPhaseParameters() override
SolutionPool * solution_pool() const
IntVar * objective() const
LocalSearchFilterManager *const filter_manager() const
RegularLimit * limit() const
LocalSearchPhaseParameters(IntVar *objective, SolutionPool *const pool, LocalSearchOperator *ls_operator, DecisionBuilder *sub_decision_builder, RegularLimit *const limit, LocalSearchFilterManager *filter_manager)
DecisionBuilder * sub_decision_builder() const
std::string DebugString() const override
void BeginFiltering(const LocalSearchFilter *filter) override
void Install() override
Install itself on the solver.
void BeginOperatorStart() override
Local search operator events.
void RestartSearch() override
Restart the search.
void ParseLocalSearchFilterStatistics(const Callback &callback) const
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 ParseLocalSearchOperatorStatistics(const Callback &callback) const
void EndOperatorStart() override
std::string PrintOverview() const
void EndFiltering(const LocalSearchFilter *filter, bool reject) override
void ParseFirstSolutionStatistics(const Callback &callback) const
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...
bool IsPathStart(int64_t node) const
Returns true if node is the first node on the path.
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.
int64_t EndNode(int i) const
Returns the end node of the ith base node.
int64_t PrevNext(int64_t node) const
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
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
const std::vector< int > & ChangedLoops() 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...
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
bool Check()
This method is called to check the status of the limit.
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.
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.
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.
const std::string & context() const
Gets the current context of the search.
T * RevAlloc(T *object)
Registers the given object as being reversible.
Solver(const std::string &name)
Solver API.
DecisionBuilder * MakeLocalSearchPhase(Assignment *const assignment, LocalSearchPhaseParameters *const parameters)
Local Search decision builders factories.
LocalSearchOperator * MultiArmedBanditConcatenateOperators(const std::vector< LocalSearchOperator * > &ops, double memory_coefficient, double exploration_coefficient, bool maximize)
Creates a local search operator which concatenates a vector of operators.
Assignment * MakeAssignment()
This method creates an empty assignment.
DecisionBuilder * Compose(DecisionBuilder *const db1, DecisionBuilder *const db2)
Creates a decision builder which sequentially composes decision builders.
DecisionBuilder * MakeStoreAssignment(Assignment *assignment)
Returns a DecisionBuilder which stores an Assignment (calls void Assignment::Store())
DecisionBuilder * MakeRestoreAssignment(Assignment *assignment)
Returns a DecisionBuilder which restores an Assignment (calls void Assignment::Restore())
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
EvaluatorLocalSearchOperators
This enum is used in Solver::MakeOperator associated with an evaluator to specify the neighborhood to...
@ TSPOPT
Sliding TSP operator.
@ LK
Lin-Kernighan local search.
LocalSearchFilterBound
This enum is used in Solver::MakeLocalSearchObjectiveFilter.
@ GE
Move is accepted when the current objective value >= objective.Min.
@ LE
Move is accepted when the current objective value <= objective.Max.
@ EQ
Move is accepted when the current objective value is in the interval objective.Min .
~SwapActiveOperator() override
bool MakeNeighbor() override
SwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
bool MakeNeighbor() override
TSPLns(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int tsp_size)
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
std::string DebugString() const override
bool MakeNeighbor() override
TSPOpt(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int chain_length)
std::string DebugString() const override
int64_t GetBaseNodeRestartPosition(int base_index) override
Returns the index of the node to which the base node of index base_index must be set to when it reach...
bool MakeNeighbor() override
bool IsIncremental() const override
TwoOpt(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
bool OnSamePathAsPreviousBase(int64_t base_index) override
Returns true if a base node has to be on the same path as the "previous" base node (base node of inde...
std::string DebugString() const override
SharedBoundsManager * bounds
GurobiMPCallbackContext * context
static const int64_t kint64max
static const int64_t kint64min
ABSL_FLAG(int, cp_local_search_sync_frequency, 16, "Frequency of checks for better solutions in the solution pool.")
std::vector< int64_t > synchronized_costs_
int64_t synchronized_sum_
const int primary_vars_size_
#define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass)
std::vector< int64_t > delta_costs_
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
ReverseView< Container > reversed_view(const Container &c)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
std::function< CallbackResult(const CallbackData &)> Callback
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
LinearExpr operator+(LinearExpr lhs, const LinearExpr &rhs)
LocalSearchOperator * MakeLocalSearchOperator(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
Operator Factories.
void InstallLocalSearchProfiler(LocalSearchProfiler *monitor)
int64_t CapSub(int64_t x, int64_t y)
void DeleteLocalSearchProfiler(LocalSearchProfiler *monitor)
LocalSearchFilter * MakeDimensionFilter(Solver *solver, std::unique_ptr< DimensionChecker > checker, const std::string &dimension_name)
LinearExpr operator-(LinearExpr lhs, const LinearExpr &rhs)
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.
#define VLOG(verboselevel)