26 #include "absl/container/flat_hash_map.h" 27 #include "absl/container/flat_hash_set.h" 28 #include "absl/memory/memory.h" 29 #include "absl/random/distributions.h" 30 #include "absl/random/random.h" 31 #include "absl/strings/str_cat.h" 44 ABSL_FLAG(
int, cp_local_search_sync_frequency, 16,
45 "Frequency of checks for better solutions in the solution pool.");
47 ABSL_FLAG(
int, cp_local_search_tsp_opt_size, 13,
48 "Size of TSPs solved in the TSPOpt operator.");
50 ABSL_FLAG(
int, cp_local_search_tsp_lns_size, 10,
51 "Size of TSPs solved in the TSPLns operator.");
53 ABSL_FLAG(
bool, cp_use_empty_path_symmetry_breaker,
true,
54 "If true, equivalent empty paths are removed from the neighborhood " 68 Assignment* deltadelta);
80 <<
delta->DebugString() <<
"), deltadelta=(" 81 << (deltadelta ? deltadelta->
DebugString() : std::string(
"nullptr"));
109 for (
int candidate : fragment_) {
123 fragment_.push_back(
index);
134 class SimpleLns :
public BaseLns {
136 SimpleLns(
const std::vector<IntVar*>& vars,
int number_of_variables)
137 :
BaseLns(vars), index_(0), number_of_variables_(number_of_variables) {
140 ~SimpleLns()
override {}
141 void InitFragments()
override { index_ = 0; }
142 bool NextFragment()
override;
143 std::string DebugString()
const override {
return "SimpleLns"; }
147 const int number_of_variables_;
150 bool SimpleLns::NextFragment() {
151 const int size = Size();
153 for (
int i = index_; i < index_ + number_of_variables_; ++i) {
154 AppendToFragment(i % size);
166 class RandomLns :
public BaseLns {
168 RandomLns(
const std::vector<IntVar*>& vars,
int number_of_variables,
170 : BaseLns(vars), rand_(seed), number_of_variables_(number_of_variables) {
172 CHECK_LE(number_of_variables_, Size());
174 ~RandomLns()
override {}
175 bool NextFragment()
override;
177 std::string DebugString()
const override {
return "RandomLns"; }
181 const int number_of_variables_;
184 bool RandomLns::NextFragment() {
186 for (
int i = 0; i < number_of_variables_; ++i) {
187 AppendToFragment(absl::Uniform<int>(rand_, 0, Size()));
194 const std::vector<IntVar*>& vars,
int number_of_variables) {
199 const std::vector<IntVar*>& vars,
int number_of_variables, int32_t seed) {
200 return RevAlloc(
new RandomLns(vars, number_of_variables, seed));
211 MoveTowardTargetLS(
const std::vector<IntVar*>& variables,
212 const std::vector<int64_t>& target_values)
214 target_(target_values),
218 variable_index_(Size() - 1) {
219 CHECK_EQ(target_values.size(), variables.size()) <<
"Illegal arguments.";
222 ~MoveTowardTargetLS()
override {}
224 std::string DebugString()
const override {
return "MoveTowardTargetLS"; }
228 bool MakeOneNeighbor()
override {
229 while (num_var_since_last_start_ < Size()) {
230 ++num_var_since_last_start_;
231 variable_index_ = (variable_index_ + 1) % Size();
232 const int64_t target_value = target_.at(variable_index_);
233 const int64_t current_value = OldValue(variable_index_);
234 if (current_value != target_value) {
235 SetValue(variable_index_, target_value);
243 void OnStart()
override {
257 num_var_since_last_start_ = 0;
261 const std::vector<int64_t> target_;
264 int64_t variable_index_;
267 int64_t num_var_since_last_start_;
273 typedef std::vector<IntVarElement> Elements;
276 std::vector<IntVar*> vars;
277 std::vector<int64_t> values;
280 for (
const auto& it : elements) {
281 vars.push_back(it.Var());
282 values.push_back(it.Value());
288 const std::vector<IntVar*>& variables,
289 const std::vector<int64_t>& target_values) {
290 return RevAlloc(
new MoveTowardTargetLS(variables, target_values));
301 const int size =
Size();
302 while (index_ < size) {
311 void ChangeValue::OnStart() { index_ = 0; }
316 class IncrementValue :
public ChangeValue {
318 explicit IncrementValue(
const std::vector<IntVar*>& vars)
319 : ChangeValue(vars) {}
320 ~IncrementValue()
override {}
321 int64_t ModifyValue(int64_t
index, int64_t
value)
override {
325 std::string DebugString()
const override {
return "IncrementValue"; }
330 class DecrementValue :
public ChangeValue {
332 explicit DecrementValue(
const std::vector<IntVar*>& vars)
333 : ChangeValue(vars) {}
334 ~DecrementValue()
override {}
335 int64_t ModifyValue(int64_t
index, int64_t
value)
override {
339 std::string DebugString()
const override {
return "DecrementValue"; }
346 const std::vector<IntVar*>& path_vars,
349 number_of_nexts_(next_vars.size()),
350 ignore_path_vars_(path_vars.empty()),
351 next_base_to_increment_(iteration_parameters.number_of_base_nodes),
352 base_nodes_(iteration_parameters.number_of_base_nodes),
353 base_alternatives_(iteration_parameters.number_of_base_nodes),
354 base_sibling_alternatives_(iteration_parameters.number_of_base_nodes),
355 end_nodes_(iteration_parameters.number_of_base_nodes),
356 base_paths_(iteration_parameters.number_of_base_nodes),
357 just_started_(false),
359 iteration_parameters_(std::move(iteration_parameters)),
360 optimal_paths_enabled_(false),
361 alternative_index_(next_vars.size(), -1) {
366 path_basis_.push_back(0);
367 for (
int i = 1; i < base_nodes_.size(); ++i) {
370 if ((path_basis_.size() > 2) ||
371 (!next_vars.empty() && !next_vars.back()
374 .skip_locally_optimal_paths())) {
381 void PathOperator::OnStart() {
382 optimal_paths_enabled_ =
false;
383 InitializeBaseNodes();
384 InitializeAlternatives();
389 while (IncrementPosition()) {
413 int64_t destination) {
414 if (destination == before_chain || destination == chain_end)
return false;
417 const int64_t destination_path =
Path(destination);
418 const int64_t after_chain =
Next(chain_end);
419 SetNext(chain_end,
Next(destination), destination_path);
421 int current = destination;
423 while (current != chain_end) {
429 SetNext(destination,
Next(before_chain), destination_path);
431 SetNext(before_chain, after_chain,
Path(before_chain));
436 int64_t* chain_last) {
438 int64_t path =
Path(before_chain);
439 int64_t current =
Next(before_chain);
440 if (current == after_chain) {
443 int64_t current_next =
Next(current);
444 SetNext(current, after_chain, path);
445 while (current_next != after_chain) {
446 const int64_t
next =
Next(current_next);
447 SetNext(current_next, current, path);
448 current = current_next;
451 SetNext(before_chain, current, path);
452 *chain_last = current;
460 int64_t destination_path =
Path(destination);
461 SetNext(node,
Next(destination), destination_path);
462 SetNext(destination, node, destination_path);
469 const int64_t kNoPath = -1;
472 const int64_t after_chain =
Next(chain_end);
473 int64_t current =
Next(before_chain);
474 while (current != after_chain) {
476 SetNext(current, current, kNoPath);
479 SetNext(before_chain, after_chain,
Path(before_chain));
486 if (active == inactive)
return false;
487 const int64_t prev =
Prev(active);
491 bool PathOperator::IncrementPosition() {
494 if (!just_started_) {
495 const int number_of_paths = path_starts_.size();
501 int last_restarted = base_node_size;
502 for (
int i = base_node_size - 1; i >= 0; --i) {
506 const int sibling_alternative_index =
508 if (sibling_alternative_index >= 0) {
509 if (base_sibling_alternatives_[i] <
510 alternative_sets_[sibling_alternative_index].size() - 1) {
511 ++base_sibling_alternatives_[i];
514 base_sibling_alternatives_[i] = 0;
517 const int alternative_index = alternative_index_[base_nodes_[i]];
518 if (alternative_index >= 0) {
519 if (base_alternatives_[i] <
520 alternative_sets_[alternative_index].size() - 1) {
521 ++base_alternatives_[i];
524 base_alternatives_[i] = 0;
525 base_sibling_alternatives_[i] = 0;
528 base_alternatives_[i] = 0;
529 base_sibling_alternatives_[i] = 0;
530 base_nodes_[i] =
OldNext(base_nodes_[i]);
535 base_alternatives_[i] = 0;
536 base_sibling_alternatives_[i] = 0;
550 for (
int i = last_restarted; i < base_node_size; ++i) {
551 base_alternatives_[i] = 0;
552 base_sibling_alternatives_[i] = 0;
555 if (last_restarted > 0) {
561 if (optimal_paths_enabled_ &&
563 if (path_basis_.size() > 1) {
564 for (
int i = 1; i < path_basis_.size(); ++i) {
574 std::vector<int> current_starts(base_node_size);
575 for (
int i = 0; i < base_node_size; ++i) {
580 optimal_paths_enabled_ =
true;
582 for (
int i = base_node_size - 1; i >= 0; --i) {
583 const int next_path_index = base_paths_[i] + 1;
584 if (next_path_index < number_of_paths) {
585 base_paths_[i] = next_path_index;
586 base_alternatives_[i] = 0;
587 base_sibling_alternatives_[i] = 0;
588 base_nodes_[i] = path_starts_[next_path_index];
594 base_alternatives_[i] = 0;
595 base_sibling_alternatives_[i] = 0;
596 base_nodes_[i] = path_starts_[0];
602 if (path_basis_.size() > 1) {
603 for (
int j = 1; j < path_basis_.size(); ++j) {
605 path_basis_[j - 1])] +
619 if (!CheckEnds())
return false;
621 for (
int i = 0; i < base_node_size; ++i) {
627 if (stop)
return false;
630 just_started_ =
false;
636 void PathOperator::InitializePathStarts() {
644 has_prevs[
next] =
true;
649 if (optimal_paths_.empty() &&
682 std::vector<int64_t> new_path_starts;
683 const bool use_empty_path_symmetry_breaker =
684 absl::GetFlag(FLAGS_cp_use_empty_path_symmetry_breaker);
694 new_path_starts.push_back(i);
702 std::vector<int> node_paths(max_next + 1, -1);
703 for (
int i = 0; i < path_starts_.size(); ++i) {
704 int node = path_starts_[i];
706 node_paths[node] = i;
709 node_paths[node] = i;
713 base_alternatives_[j] = 0;
714 base_sibling_alternatives_[j] = 0;
715 if (
IsInactive(base_nodes_[j]) || node_paths[base_nodes_[j]] == -1) {
718 base_nodes_[j] = path_starts_[base_paths_[j]];
720 base_paths_[j] = node_paths[base_nodes_[j]];
727 absl::flat_hash_set<int> found_bases;
728 for (
int i = 0; i < path_starts_.size(); ++i) {
729 int index = new_index;
731 while (
index < new_path_starts.size() &&
732 new_path_starts[
index] < path_starts_[i]) {
735 const bool found = (
index < new_path_starts.size() &&
736 new_path_starts[
index] == path_starts_[i]);
742 found_bases.insert(j);
743 base_paths_[j] = new_index;
747 base_nodes_[j] = new_path_starts[new_index];
753 path_starts_.swap(new_path_starts);
756 void PathOperator::InitializeInactives() {
759 inactives_.push_back(
OldNext(i) == i);
763 void PathOperator::InitializeBaseNodes() {
765 InitializeInactives();
766 InitializePathStarts();
772 base_nodes_[i] = path_starts_[0];
774 first_start_ =
false;
778 int64_t base_node = base_nodes_[i];
780 base_node = path_starts_[base_paths_[i]];
781 base_nodes_[i] = base_node;
783 end_nodes_[i] = base_node;
789 !OnSamePath(base_nodes_[i - 1], base_nodes_[i])) {
790 const int64_t base_node = base_nodes_[i - 1];
791 base_nodes_[i] = base_node;
792 end_nodes_[i] = base_node;
793 base_paths_[i] = base_paths_[i - 1];
797 base_alternatives_[i] = 0;
798 base_sibling_alternatives_[i] = 0;
800 just_started_ =
true;
803 void PathOperator::InitializeAlternatives() {
804 active_in_alternative_set_.resize(alternative_sets_.size(), -1);
805 for (
int i = 0; i < alternative_sets_.size(); ++i) {
806 const int64_t current_active = active_in_alternative_set_[i];
807 if (current_active >= 0 && !
IsInactive(current_active))
continue;
808 for (int64_t
index : alternative_sets_[i]) {
810 active_in_alternative_set_[i] =
index;
817 bool PathOperator::OnSamePath(int64_t node1, int64_t node2)
const {
840 int64_t exclude)
const {
841 if (before_chain == chain_end || before_chain == exclude)
return false;
842 int64_t current = before_chain;
844 while (current != chain_end) {
851 current =
Next(current);
853 if (current == exclude) {
873 const std::vector<IntVar*>& secondary_vars,
874 std::function<
int(int64_t)> start_empty_path_class)
876 std::move(start_empty_path_class)),
895 void OnNodeInitialization()
override { last_ = -1; }
903 if (last_base_ !=
BaseNode(0) || last_ == -1) {
915 && last_ != chain_last) {
921 const int64_t to_move =
Next(last_);
944 const std::vector<IntVar*>& secondary_vars,
const std::string&
name,
945 std::function<
int(int64_t)> start_empty_path_class,
946 int64_t chain_length = 1LL,
bool single_path =
false)
948 std::move(start_empty_path_class)),
949 chain_length_(chain_length),
950 single_path_(single_path),
955 const std::vector<IntVar*>& secondary_vars,
956 std::function<
int(int64_t)> start_empty_path_class,
957 int64_t chain_length = 1LL,
bool single_path =
false)
959 absl::StrCat(
"Relocate<", chain_length,
">"),
960 std::move(start_empty_path_class), chain_length, single_path) {
975 const int64_t chain_length_;
976 const bool single_path_;
977 const std::string name_;
982 const int64_t destination =
BaseNode(1);
984 const int64_t before_chain =
BaseNode(0);
985 int64_t chain_end = before_chain;
986 for (
int i = 0; i < chain_length_; ++i) {
987 if (
IsPathEnd(chain_end) || chain_end == destination) {
990 chain_end =
Next(chain_end);
993 MoveChain(before_chain, chain_end, destination);
1009 const std::vector<IntVar*>& secondary_vars,
1010 std::function<
int(int64_t)> start_empty_path_class)
1012 std::move(start_empty_path_class)) {}
1020 const int64_t prev_node0 =
BaseNode(0);
1021 const int64_t node0 =
Next(prev_node0);
1023 const int64_t prev_node1 =
BaseNode(1);
1024 const int64_t node1 =
Next(prev_node1);
1026 const bool ok =
MoveChain(prev_node0, node0, prev_node1);
1045 const std::vector<IntVar*>& secondary_vars,
1046 std::function<
int(int64_t)> start_empty_path_class)
1048 std::move(start_empty_path_class)) {}
1058 if (start1 == start0)
return false;
1060 if (node0 == start0)
return false;
1062 if (node1 == start1)
return false;
1082 const std::vector<IntVar*>& vars,
1083 const std::vector<IntVar*>& secondary_vars,
int number_of_base_nodes,
1084 std::function<
int(int64_t)> start_empty_path_class)
1085 :
PathOperator(vars, secondary_vars, number_of_base_nodes, false, false,
1086 std::move(start_empty_path_class)),
1097 void OnNodeInitialization()
override;
1102 void BaseInactiveNodeToPathOperator::OnNodeInitialization() {
1103 for (
int i = 0; i <
Size(); ++i) {
1109 inactive_node_ =
Size();
1113 while (inactive_node_ <
Size()) {
1136 const std::vector<IntVar*>& secondary_vars,
1137 std::function<
int(int64_t)> start_empty_path_class)
1139 std::move(start_empty_path_class)) {}
1143 std::string
DebugString()
const override {
return "MakeActiveOperator"; }
1162 const std::vector<IntVar*>& vars,
1163 const std::vector<IntVar*>& secondary_vars,
1164 std::function<
int(int64_t)> start_empty_path_class)
1166 std::move(start_empty_path_class)) {}
1169 const int64_t before_node_to_move =
BaseNode(1);
1170 const int64_t node =
Next(before_node_to_move);
1177 return "RelocateAndMakeActiveOpertor";
1190 const std::vector<IntVar*>& secondary_vars,
1191 std::function<
int(int64_t)> start_empty_path_class)
1193 std::move(start_empty_path_class)) {}
1198 return "MakeActiveAndRelocateOperator";
1203 const int64_t before_chain =
BaseNode(1);
1204 const int64_t chain_end =
Next(before_chain);
1205 const int64_t destination =
BaseNode(0);
1207 MoveChain(before_chain, chain_end, destination) &&
1222 const std::vector<IntVar*>& secondary_vars,
1223 std::function<
int(int64_t)> start_empty_path_class)
1225 std::move(start_empty_path_class)) {}
1232 std::string
DebugString()
const override {
return "MakeInactiveOperator"; }
1246 const std::vector<IntVar*>& vars,
1247 const std::vector<IntVar*>& secondary_vars,
1248 std::function<
int(int64_t)> start_empty_path_class)
1250 std::move(start_empty_path_class)) {}
1253 const int64_t destination =
BaseNode(1);
1254 const int64_t before_to_move =
BaseNode(0);
1255 const int64_t node_to_inactivate =
Next(destination);
1256 if (node_to_inactivate == before_to_move ||
IsPathEnd(node_to_inactivate) ||
1260 const int64_t node =
Next(before_to_move);
1265 return "RelocateAndMakeInactiveOperator";
1281 const std::vector<IntVar*>& secondary_vars,
1282 std::function<
int(int64_t)> start_empty_path_class)
1284 std::move(start_empty_path_class)) {}
1291 return "MakeChainInactiveOperator";
1318 const std::vector<IntVar*>& secondary_vars,
1319 std::function<
int(int64_t)> start_empty_path_class)
1321 std::move(start_empty_path_class)) {}
1325 std::string
DebugString()
const override {
return "SwapActiveOperator"; }
1350 const std::vector<IntVar*>& secondary_vars,
1351 std::function<
int(int64_t)> start_empty_path_class)
1353 std::move(start_empty_path_class)) {}
1358 return "ExtendedSwapActiveOperator";
1365 if (
Next(base0) == base1) {
1383 TSPOpt(
const std::vector<IntVar*>& vars,
1384 const std::vector<IntVar*>& secondary_vars,
1392 std::vector<std::vector<int64_t>> cost_;
1394 hamiltonian_path_solver_;
1396 const int chain_length_;
1400 const std::vector<IntVar*>& secondary_vars,
1402 :
PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1403 hamiltonian_path_solver_(cost_),
1405 chain_length_(chain_length) {}
1408 std::vector<int64_t>
nodes;
1410 for (
int i = 0; i < chain_length_ + 1; ++i) {
1411 nodes.push_back(chain_end);
1415 chain_end =
Next(chain_end);
1417 if (
nodes.size() <= 3) {
1421 const int size =
nodes.size() - 1;
1423 for (
int i = 0; i < size; ++i) {
1424 cost_[i].resize(size);
1425 cost_[i][0] = evaluator_(
nodes[i],
nodes[size], chain_path);
1426 for (
int j = 1; j < size; ++j) {
1427 cost_[i][j] = evaluator_(
nodes[i],
nodes[j], chain_path);
1431 std::vector<PathNodeIndex> path;
1434 for (
int i = 0; i < size - 1; ++i) {
1451 TSPLns(
const std::vector<IntVar*>& vars,
1452 const std::vector<IntVar*>& secondary_vars,
1463 void OnNodeInitialization()
override {
1465 has_long_enough_paths_ =
Size() != 0;
1468 std::vector<std::vector<int64_t>> cost_;
1469 HamiltonianPathSolver<int64_t, std::vector<std::vector<int64_t>>>
1470 hamiltonian_path_solver_;
1472 const int tsp_size_;
1474 bool has_long_enough_paths_;
1478 const std::vector<IntVar*>& secondary_vars,
1480 :
PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1481 hamiltonian_path_solver_(cost_),
1483 tsp_size_(tsp_size),
1485 has_long_enough_paths_(true) {
1487 cost_.resize(tsp_size_);
1488 for (
int i = 0; i < tsp_size_; ++i) {
1489 cost_[i].resize(tsp_size_);
1494 while (has_long_enough_paths_) {
1495 has_long_enough_paths_ =
false;
1505 const int64_t base_node =
BaseNode(0);
1506 std::vector<int64_t>
nodes;
1508 nodes.push_back(node);
1510 if (
nodes.size() <= tsp_size_) {
1513 has_long_enough_paths_ =
true;
1516 absl::flat_hash_set<int64_t> breaks_set;
1518 breaks_set.insert(base_node);
1520 while (breaks_set.size() < tsp_size_) {
1521 breaks_set.insert(
nodes[absl::Uniform<int>(rand_, 0,
nodes.size())]);
1523 CHECK_EQ(breaks_set.size(), tsp_size_);
1528 std::vector<int> breaks;
1529 std::vector<int64_t> meta_node_costs;
1532 int64_t node_path =
Path(node);
1536 breaks.push_back(node);
1537 meta_node_costs.push_back(
cost);
1544 meta_node_costs[0] +=
cost;
1545 CHECK_EQ(breaks.size(), tsp_size_);
1547 CHECK_EQ(meta_node_costs.size(), tsp_size_);
1548 for (
int i = 0; i < tsp_size_; ++i) {
1550 CapAdd(meta_node_costs[i],
1551 evaluator_(breaks[i],
Next(breaks[tsp_size_ - 1]), node_path));
1552 for (
int j = 1; j < tsp_size_; ++j) {
1554 CapAdd(meta_node_costs[i],
1555 evaluator_(breaks[i],
Next(breaks[j - 1]), node_path));
1561 std::vector<PathNodeIndex> path;
1563 bool nochange =
true;
1564 for (
int i = 0; i < path.size() - 1; ++i) {
1573 CHECK_EQ(0, path[path.size() - 1]);
1574 for (
int i = 0; i < tsp_size_ - 1; ++i) {
1575 SetNext(breaks[path[i]],
OldNext(breaks[path[i + 1] - 1]), node_path);
1577 SetNext(breaks[path[tsp_size_ - 1]],
OldNext(breaks[tsp_size_ - 1]),
1598 virtual std::string
DebugString()
const {
return "NearestNeighbors"; }
1601 void ComputeNearest(
int row);
1603 std::vector<std::vector<int>> neighbors_;
1615 path_operator_(path_operator),
1617 initialized_(false) {}
1621 if (!initialized_) {
1622 initialized_ =
true;
1624 neighbors_.push_back(std::vector<int>());
1631 return neighbors_[
index];
1634 void NearestNeighbors::ComputeNearest(
int row) {
1636 const int path = path_operator_.
Path(
row);
1638 const int64_t var_min =
var->Min();
1639 const int var_size =
var->Max() - var_min + 1;
1640 using ValuedIndex = std::pair<int64_t ,
int >;
1641 std::vector<ValuedIndex> neighbors(var_size);
1642 for (
int i = 0; i < var_size; ++i) {
1643 const int index = i + var_min;
1644 neighbors[i] = std::make_pair(evaluator_(
row,
index, path),
index);
1646 if (var_size > size_) {
1647 std::nth_element(neighbors.begin(), neighbors.begin() + size_ - 1,
1652 for (
int i = 0; i <
std::min(size_, var_size); ++i) {
1653 neighbors_[
row].push_back(neighbors[i].second);
1655 std::sort(neighbors_[
row].begin(), neighbors_[
row].end());
1661 const std::vector<IntVar*>& secondary_vars,
1669 void OnNodeInitialization()
override;
1671 static const int kNeighbors;
1673 bool InFromOut(int64_t in_i, int64_t in_j, int64_t* out, int64_t* gain);
1677 absl::flat_hash_set<int64_t> marked_;
1686 const std::vector<IntVar*>& secondary_vars,
1688 :
PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1690 neighbors_(evaluator, *this, kNeighbors),
1695 void LinKernighan::OnNodeInitialization() { neighbors_.
Initialize(); }
1700 int64_t path =
Path(node);
1701 int64_t base = node;
1706 marked_.insert(node);
1708 if (!InFromOut(node,
next, &out, &gain))
return false;
1709 marked_.insert(
next);
1710 marked_.insert(out);
1711 const int64_t node1 = out;
1713 const int64_t next1 =
Next(node1);
1715 if (!InFromOut(node1, next1, &out, &gain))
return false;
1716 marked_.insert(next1);
1717 marked_.insert(out);
1721 const int64_t next_out =
Next(out);
1722 const int64_t in_cost = evaluator_(node, next_out, path);
1723 const int64_t out_cost = evaluator_(out, next_out, path);
1724 if (
CapAdd(
CapSub(gain, in_cost), out_cost) > 0)
return true;
1731 while (InFromOut(node,
next, &out, &gain)) {
1732 marked_.insert(
next);
1733 marked_.insert(out);
1738 int64_t in_cost = evaluator_(base, chain_last, path);
1739 int64_t out_cost = evaluator_(chain_last, out, path);
1755 const int LinKernighan::kNeighbors = 5 + 1;
1757 bool LinKernighan::InFromOut(int64_t in_i, int64_t in_j, int64_t* out,
1759 const std::vector<int>& nexts = neighbors_.
Neighbors(in_j);
1761 int64_t path =
Path(in_i);
1762 int64_t out_cost = evaluator_(in_i, in_j, path);
1763 const int64_t current_gain =
CapAdd(*gain, out_cost);
1764 for (
int k = 0; k < nexts.size(); ++k) {
1765 const int64_t
next = nexts[k];
1767 int64_t in_cost = evaluator_(in_j,
next, path);
1768 int64_t new_gain =
CapSub(current_gain, in_cost);
1769 if (new_gain > 0 &&
next !=
Next(in_j) && marked_.count(in_j) == 0 &&
1770 marked_.count(
next) == 0) {
1771 if (best_gain < new_gain) {
1773 best_gain = new_gain;
1791 const std::vector<IntVar*>& secondary_vars,
int number_of_chunks,
1792 int chunk_size,
bool unactive_fragments)
1793 :
PathOperator(vars, secondary_vars, number_of_chunks, true, true,
1795 number_of_chunks_(number_of_chunks),
1796 chunk_size_(chunk_size),
1797 unactive_fragments_(unactive_fragments) {
1807 inline bool ChainsAreFullPaths()
const {
return chunk_size_ == 0; }
1808 void DeactivateChain(int64_t node);
1809 void DeactivateUnactives();
1811 const int number_of_chunks_;
1812 const int chunk_size_;
1813 const bool unactive_fragments_;
1817 if (ChainsAreFullPaths()) {
1821 for (
int i = 0; i < number_of_chunks_; ++i) {
1825 for (
int i = 0; i < number_of_chunks_; ++i) {
1828 DeactivateUnactives();
1832 void PathLns::DeactivateChain(int64_t node) {
1833 for (
int i = 0, current = node;
1834 (ChainsAreFullPaths() || i < chunk_size_) && !
IsPathEnd(current);
1835 ++i, current =
Next(current)) {
1843 void PathLns::DeactivateUnactives() {
1844 if (unactive_fragments_) {
1845 for (
int i = 0; i <
Size(); ++i) {
1861 : operator_(op), limit_(limit), next_neighborhood_calls_(0) {
1862 CHECK(op !=
nullptr);
1867 next_neighborhood_calls_ = 0;
1868 operator_->
Start(assignment);
1872 if (next_neighborhood_calls_ >= limit_) {
1875 ++next_neighborhood_calls_;
1881 std::string
DebugString()
const override {
return "NeighborhoodLimit"; }
1885 const int64_t limit_;
1886 int64_t next_neighborhood_calls_;
1899 CompoundOperator(std::vector<LocalSearchOperator*> operators,
1900 std::function<int64_t(
int,
int)> evaluator);
1901 ~CompoundOperator()
override {}
1902 void Reset()
override;
1903 void Start(
const Assignment* assignment)
override;
1904 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
1905 bool HasFragments()
const override {
return has_fragments_; }
1906 bool HoldsDelta()
const override {
return true; }
1908 std::string DebugString()
const override {
1909 return operators_.empty()
1911 : operators_[operator_indices_[index_]]->DebugString();
1913 const LocalSearchOperator* Self()
const override {
1914 return operators_.empty() ? this
1915 : operators_[operator_indices_[index_]]->Self();
1919 class OperatorComparator {
1921 OperatorComparator(std::function<int64_t(
int,
int)> evaluator,
1922 int active_operator)
1923 :
evaluator_(std::move(evaluator)), active_operator_(active_operator) {}
1924 bool operator()(
int lhs,
int rhs)
const {
1925 const int64_t lhs_value = Evaluate(lhs);
1926 const int64_t rhs_value = Evaluate(rhs);
1927 return lhs_value < rhs_value || (lhs_value == rhs_value && lhs < rhs);
1931 int64_t Evaluate(
int operator_index)
const {
1932 return evaluator_(active_operator_, operator_index);
1936 const int active_operator_;
1940 std::vector<LocalSearchOperator*> operators_;
1941 std::vector<int> operator_indices_;
1943 Bitset64<> started_;
1944 const Assignment* start_assignment_;
1945 bool has_fragments_;
1948 CompoundOperator::CompoundOperator(std::vector<LocalSearchOperator*> operators,
1949 std::function<int64_t(
int,
int)> evaluator)
1951 operators_(std::move(operators)),
1953 started_(operators_.size()),
1954 start_assignment_(nullptr),
1955 has_fragments_(false) {
1956 operators_.erase(std::remove(operators_.begin(), operators_.end(),
nullptr),
1958 operator_indices_.resize(operators_.size());
1959 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
1960 for (LocalSearchOperator*
const op : operators_) {
1961 if (op->HasFragments()) {
1962 has_fragments_ =
true;
1968 void CompoundOperator::Reset() {
1969 for (LocalSearchOperator*
const op : operators_) {
1974 void CompoundOperator::Start(
const Assignment* assignment) {
1975 start_assignment_ = assignment;
1977 if (!operators_.empty()) {
1978 OperatorComparator comparator(
evaluator_, operator_indices_[index_]);
1979 std::sort(operator_indices_.begin(), operator_indices_.end(), comparator);
1984 bool CompoundOperator::MakeNextNeighbor(Assignment*
delta,
1985 Assignment* deltadelta) {
1986 if (!operators_.empty()) {
1990 const int64_t operator_index = operator_indices_[index_];
1991 if (!started_[operator_index]) {
1992 operators_[operator_index]->Start(start_assignment_);
1993 started_.
Set(operator_index);
1995 if (!operators_[operator_index]->HoldsDelta()) {
1998 if (operators_[operator_index]->MakeNextNeighbor(
delta, deltadelta)) {
2003 if (index_ == operators_.size()) {
2006 }
while (index_ != 0);
2011 int64_t CompoundOperatorNoRestart(
int size,
int active_index,
2012 int operator_index) {
2013 return (operator_index < active_index) ? size + operator_index - active_index
2014 : operator_index - active_index;
2017 int64_t CompoundOperatorRestart(
int active_index,
int operator_index) {
2023 const std::vector<LocalSearchOperator*>& ops) {
2028 const std::vector<LocalSearchOperator*>& ops,
bool restart) {
2030 std::function<int64_t(
int,
int)> eval = CompoundOperatorRestart;
2033 const int size = ops.size();
2035 return CompoundOperatorNoRestart(size, i, j);
2040 const std::vector<LocalSearchOperator*>& ops,
2041 std::function<int64_t(
int,
int)> evaluator) {
2042 return RevAlloc(
new CompoundOperator(ops, std::move(evaluator)));
2048 explicit RandomCompoundOperator(std::vector<LocalSearchOperator*> operators);
2049 RandomCompoundOperator(std::vector<LocalSearchOperator*> operators,
2051 ~RandomCompoundOperator()
override {}
2052 void Reset()
override;
2053 void Start(
const Assignment* assignment)
override;
2054 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
2055 bool HoldsDelta()
const override {
return true; }
2057 std::string DebugString()
const override {
return "RandomCompoundOperator"; }
2062 const std::vector<LocalSearchOperator*> operators_;
2063 bool has_fragments_;
2066 void RandomCompoundOperator::Start(
const Assignment* assignment) {
2067 for (LocalSearchOperator*
const op : operators_) {
2068 op->Start(assignment);
2072 RandomCompoundOperator::RandomCompoundOperator(
2073 std::vector<LocalSearchOperator*> operators)
2074 : RandomCompoundOperator(std::move(operators),
CpRandomSeed()) {}
2076 RandomCompoundOperator::RandomCompoundOperator(
2077 std::vector<LocalSearchOperator*> operators, int32_t seed)
2078 : rand_(seed), operators_(std::move(operators)), has_fragments_(false) {
2079 for (LocalSearchOperator*
const op : operators_) {
2080 if (op->HasFragments()) {
2081 has_fragments_ =
true;
2087 void RandomCompoundOperator::Reset() {
2088 for (LocalSearchOperator*
const op : operators_) {
2093 bool RandomCompoundOperator::MakeNextNeighbor(Assignment*
delta,
2094 Assignment* deltadelta) {
2095 const int size = operators_.size();
2096 std::vector<int> indices(size);
2097 std::iota(indices.begin(), indices.end(), 0);
2098 std::shuffle(indices.begin(), indices.end(), rand_);
2099 for (
int index : indices) {
2100 if (!operators_[
index]->HoldsDelta()) {
2103 if (operators_[
index]->MakeNextNeighbor(
delta, deltadelta)) {
2113 const std::vector<LocalSearchOperator*>& ops) {
2114 return RevAlloc(
new RandomCompoundOperator(ops));
2118 const std::vector<LocalSearchOperator*>& ops, int32_t seed) {
2119 return RevAlloc(
new RandomCompoundOperator(ops, seed));
2125 explicit MultiArmedBanditCompoundOperator(
2126 std::vector<LocalSearchOperator*> operators,
double memory_coefficient,
2127 double exploration_coefficient,
bool maximize);
2128 ~MultiArmedBanditCompoundOperator()
override {}
2129 void Reset()
override;
2130 void Start(
const Assignment* assignment)
override;
2131 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
2132 bool HoldsDelta()
const override {
return true; }
2134 std::string DebugString()
const override {
2135 return operators_.empty()
2137 : operators_[operator_indices_[index_]]->DebugString();
2139 const LocalSearchOperator* Self()
const override {
2140 return operators_.empty() ? this
2141 : operators_[operator_indices_[index_]]->Self();
2145 double Score(
int index);
2147 std::vector<LocalSearchOperator*> operators_;
2148 Bitset64<> started_;
2149 const Assignment* start_assignment_;
2150 bool has_fragments_;
2151 std::vector<int> operator_indices_;
2152 int64_t last_objective_;
2153 std::vector<double> avg_improvement_;
2155 std::vector<double> num_neighbors_per_operator_;
2161 const double memory_coefficient_;
2166 const double exploration_coefficient_;
2169 MultiArmedBanditCompoundOperator::MultiArmedBanditCompoundOperator(
2170 std::vector<LocalSearchOperator*> operators,
double memory_coefficient,
2171 double exploration_coefficient,
bool maximize)
2173 operators_(std::move(operators)),
2174 started_(operators_.size()),
2175 start_assignment_(nullptr),
2176 has_fragments_(false),
2177 last_objective_(std::numeric_limits<int64_t>::
max()),
2180 memory_coefficient_(memory_coefficient),
2181 exploration_coefficient_(exploration_coefficient) {
2185 operators_.erase(std::remove(operators_.begin(), operators_.end(),
nullptr),
2187 operator_indices_.resize(operators_.size());
2188 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
2189 num_neighbors_per_operator_.resize(operators_.size(), 0);
2190 avg_improvement_.resize(operators_.size(), 0);
2191 for (LocalSearchOperator*
const op : operators_) {
2192 if (op->HasFragments()) {
2193 has_fragments_ =
true;
2199 void MultiArmedBanditCompoundOperator::Reset() {
2200 for (LocalSearchOperator*
const op : operators_) {
2205 double MultiArmedBanditCompoundOperator::Score(
int index) {
2206 return avg_improvement_[
index] +
2207 exploration_coefficient_ *
2208 sqrt(2 * log(1 + num_neighbors_) /
2209 (1 + num_neighbors_per_operator_[
index]));
2212 void MultiArmedBanditCompoundOperator::Start(
const Assignment* assignment) {
2213 start_assignment_ = assignment;
2215 if (operators_.empty())
return;
2217 const double objective = assignment->ObjectiveValue();
2219 if (objective == last_objective_)
return;
2222 last_objective_ = objective;
2226 const double improvement =
2227 maximize_ ? objective - last_objective_ : last_objective_ - objective;
2228 if (improvement < 0) {
2231 last_objective_ = objective;
2232 avg_improvement_[operator_indices_[index_]] +=
2233 memory_coefficient_ *
2234 (improvement - avg_improvement_[operator_indices_[index_]]);
2236 std::sort(operator_indices_.begin(), operator_indices_.end(),
2237 [
this](
int lhs,
int rhs) {
2238 const double lhs_score = Score(lhs);
2239 const double rhs_score = Score(rhs);
2240 return lhs_score > rhs_score ||
2241 (lhs_score == rhs_score && lhs < rhs);
2247 bool MultiArmedBanditCompoundOperator::MakeNextNeighbor(
2248 Assignment*
delta, Assignment* deltadelta) {
2249 if (operators_.empty())
return false;
2251 const int operator_index = operator_indices_[index_];
2252 if (!started_[operator_index]) {
2253 operators_[operator_index]->Start(start_assignment_);
2254 started_.
Set(operator_index);
2256 if (!operators_[operator_index]->HoldsDelta()) {
2259 if (operators_[operator_index]->MakeNextNeighbor(
delta, deltadelta)) {
2261 ++num_neighbors_per_operator_[operator_index];
2266 if (index_ == operators_.size()) {
2269 }
while (index_ != 0);
2275 const std::vector<LocalSearchOperator*>& ops,
double memory_coefficient,
2276 double exploration_coefficient,
bool maximize) {
2277 return RevAlloc(
new MultiArmedBanditCompoundOperator(
2278 ops, memory_coefficient, exploration_coefficient, maximize));
2285 Solver* solver,
const std::vector<IntVar*>& vars,
2286 const std::vector<IntVar*>& secondary_vars,
2287 std::function<
int(int64_t)> start_empty_path_class) {
2289 new T(vars, secondary_vars, std::move(start_empty_path_class)));
2292 #define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass) \ 2294 LocalSearchOperator* MakeLocalSearchOperator<OperatorClass>( \ 2295 Solver * solver, const std::vector<IntVar*>& vars, \ 2296 const std::vector<IntVar*>& secondary_vars, \ 2297 std::function<int(int64_t)> start_empty_path_class) { \ 2298 return solver->RevAlloc(new OperatorClass( \ 2299 vars, secondary_vars, std::move(start_empty_path_class))); \ 2315 #undef MAKE_LOCAL_SEARCH_OPERATOR 2323 const std::vector<IntVar*>& vars,
2324 const std::vector<IntVar*>& secondary_vars,
2333 std::vector<LocalSearchOperator*> operators;
2334 for (
int i = 1; i < 4; ++i) {
2336 new Relocate(vars, secondary_vars, absl::StrCat(
"OrOpt<", i,
">"),
2337 nullptr, i,
true)));
2343 result = MakeLocalSearchOperator<Relocate>(
this, vars, secondary_vars,
2348 result = MakeLocalSearchOperator<Exchange>(
this, vars, secondary_vars,
2354 MakeLocalSearchOperator<Cross>(
this, vars, secondary_vars,
nullptr);
2358 result = MakeLocalSearchOperator<MakeActiveOperator>(
2359 this, vars, secondary_vars,
nullptr);
2363 result = MakeLocalSearchOperator<MakeInactiveOperator>(
2364 this, vars, secondary_vars,
nullptr);
2368 result = MakeLocalSearchOperator<MakeChainInactiveOperator>(
2369 this, vars, secondary_vars,
nullptr);
2373 result = MakeLocalSearchOperator<SwapActiveOperator>(
2374 this, vars, secondary_vars,
nullptr);
2378 result = MakeLocalSearchOperator<ExtendedSwapActiveOperator>(
2379 this, vars, secondary_vars,
nullptr);
2398 if (secondary_vars.empty()) {
2399 result =
RevAlloc(
new IncrementValue(vars));
2402 <<
" does not support secondary variables";
2407 if (secondary_vars.empty()) {
2408 result =
RevAlloc(
new DecrementValue(vars));
2411 <<
" does not support secondary variables";
2416 if (secondary_vars.empty()) {
2417 result =
RevAlloc(
new SimpleLns(vars, 1));
2420 <<
" does not support secondary variables";
2425 LOG(
FATAL) <<
"Unknown operator " << op;
2433 return MakeOperator(vars, std::vector<IntVar*>(), std::move(evaluator), op);
2437 const std::vector<IntVar*>& vars,
2438 const std::vector<IntVar*>& secondary_vars,
2444 std::vector<LocalSearchOperator*> operators;
2446 new LinKernighan(vars, secondary_vars, evaluator,
false)));
2448 new LinKernighan(vars, secondary_vars, evaluator,
true)));
2454 new TSPOpt(vars, secondary_vars, evaluator,
2455 absl::GetFlag(FLAGS_cp_local_search_tsp_opt_size)));
2460 new TSPLns(vars, secondary_vars, evaluator,
2461 absl::GetFlag(FLAGS_cp_local_search_tsp_lns_size)));
2465 LOG(
FATAL) <<
"Unknown operator " << op;
2473 class SumOperation {
2475 SumOperation() : value_(0) {}
2476 void Init() { value_ = 0; }
2477 void Update(int64_t update) { value_ =
CapAdd(value_, update); }
2478 void Remove(int64_t remove) { value_ =
CapSub(value_, remove); }
2479 int64_t
value()
const {
return value_; }
2480 void set_value(int64_t new_value) { value_ = new_value; }
2486 class ProductOperation {
2488 ProductOperation() : value_(1) {}
2489 void Init() { value_ = 1; }
2490 void Update(int64_t update) { value_ *= update; }
2491 void Remove(int64_t remove) {
2496 int64_t
value()
const {
return value_; }
2497 void set_value(int64_t new_value) { value_ = new_value; }
2503 class MinOperation {
2505 void Init() { values_set_.clear(); }
2506 void Update(int64_t update) { values_set_.insert(update); }
2507 void Remove(int64_t remove) { values_set_.erase(remove); }
2508 int64_t
value()
const {
2509 return (!values_set_.empty()) ? *values_set_.begin() : 0;
2511 void set_value(int64_t new_value) {}
2514 std::set<int64_t> values_set_;
2517 class MaxOperation {
2519 void Init() { values_set_.clear(); }
2520 void Update(int64_t update) { values_set_.insert(update); }
2521 void Remove(int64_t remove) { values_set_.erase(remove); }
2522 int64_t
value()
const {
2523 return (!values_set_.empty()) ? *values_set_.rbegin() : 0;
2525 void set_value(int64_t new_value) {}
2528 std::set<int64_t> values_set_;
2532 class AcceptFilter :
public LocalSearchFilter {
2534 std::string DebugString()
const override {
return "AcceptFilter"; }
2535 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2536 int64_t obj_min, int64_t obj_max)
override {
2539 void Synchronize(
const Assignment* assignment,
2540 const Assignment*
delta)
override {}
2545 return RevAlloc(
new AcceptFilter());
2552 std::string DebugString()
const override {
return "RejectFilter"; }
2553 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2554 int64_t obj_min, int64_t obj_max)
override {
2557 void Synchronize(
const Assignment* assignment,
2558 const Assignment*
delta)
override {}
2563 return RevAlloc(
new RejectFilter());
2567 std::vector<int> path_end)
2568 : num_nodes_(num_nodes),
2569 num_paths_(path_start.size()),
2570 num_nodes_threshold_(std::
max(16, 4 * num_nodes_))
2572 DCHECK_EQ(path_start.size(), num_paths_);
2574 for (
int p = 0; p < num_paths_; ++p) {
2575 path_start_end_.push_back({path_start[p], path_end[p]});
2578 committed_index_.assign(num_nodes_, -1);
2579 committed_nodes_.assign(2 * num_paths_, {-1, -1});
2580 chains_.assign(num_paths_ + 1, {-1, -1});
2581 paths_.assign(num_paths_, {-1, -1});
2582 for (
int path = 0; path < num_paths_; ++path) {
2583 const int index = 2 * path;
2584 const PathStartEnd start_end = path_start_end_[path];
2585 committed_index_[start_end.start] =
index;
2586 committed_index_[start_end.end] =
index + 1;
2588 committed_nodes_[
index] = {start_end.start, path};
2589 committed_nodes_[
index + 1] = {start_end.end, path};
2592 paths_[path] = {path, path + 1};
2594 chains_[num_paths_] = {0, 0};
2596 for (
int node = 0; node < num_nodes_; ++node) {
2597 if (committed_index_[node] != -1)
continue;
2598 committed_index_[node] = committed_nodes_.size();
2599 committed_nodes_.push_back({node, -1});
2601 path_has_changed_.assign(num_paths_,
false);
2605 const PathBounds
bounds = paths_[path];
2607 chains_.data() +
bounds.end_index,
2608 committed_nodes_.data());
2612 const PathBounds
bounds = paths_[path];
2614 chains_.data() +
bounds.end_index,
2615 committed_nodes_.data());
2618 void PathState::MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm() {
2619 int num_visited_changed_arcs = 0;
2620 const int num_changed_arcs = tail_head_indices_.size();
2621 const int num_committed_nodes = committed_nodes_.size();
2623 for (
const int path : changed_paths_) {
2624 const int old_chain_size = chains_.size();
2625 const ChainBounds
bounds = chains_[paths_[path].begin_index];
2626 const int start_index =
bounds.begin_index;
2627 const int end_index =
bounds.end_index - 1;
2628 int current_index = start_index;
2632 int selected_arc = -1;
2633 int selected_tail_index = num_committed_nodes;
2634 for (
int i = num_visited_changed_arcs; i < num_changed_arcs; ++i) {
2635 const int tail_index = tail_head_indices_[i].tail_index;
2636 if (current_index <= tail_index && tail_index < selected_tail_index) {
2638 selected_tail_index = tail_index;
2646 if (start_index <= current_index && current_index <= end_index &&
2647 end_index < selected_tail_index) {
2648 chains_.emplace_back(current_index, end_index + 1);
2651 chains_.emplace_back(current_index, selected_tail_index + 1);
2652 current_index = tail_head_indices_[selected_arc].head_index;
2653 std::swap(tail_head_indices_[num_visited_changed_arcs],
2654 tail_head_indices_[selected_arc]);
2655 ++num_visited_changed_arcs;
2658 const int new_chain_size = chains_.size();
2659 paths_[path] = {old_chain_size, new_chain_size};
2661 chains_.emplace_back(0, 0);
2664 void PathState::MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm() {
2680 for (
const int path : changed_paths_) {
2681 const PathStartEnd start_end = path_start_end_[path];
2682 tail_head_indices_.push_back(
2683 {committed_index_[start_end.end], committed_index_[start_end.start]});
2688 const int num_arc_indices = tail_head_indices_.size();
2689 arcs_by_tail_index_.resize(num_arc_indices);
2690 arcs_by_head_index_.resize(num_arc_indices);
2691 for (
int i = 0; i < num_arc_indices; ++i) {
2692 arcs_by_tail_index_[i] = {tail_head_indices_[i].tail_index, i};
2693 arcs_by_head_index_[i] = {tail_head_indices_[i].head_index, i};
2695 std::sort(arcs_by_tail_index_.begin(), arcs_by_tail_index_.end());
2696 std::sort(arcs_by_head_index_.begin(), arcs_by_head_index_.end());
2698 next_arc_.resize(num_arc_indices);
2699 for (
int i = 0; i < num_arc_indices; ++i) {
2700 next_arc_[arcs_by_head_index_[i].arc] = arcs_by_tail_index_[i].arc;
2706 const int first_fake_arc = num_arc_indices - changed_paths_.size();
2707 for (
int fake_arc = first_fake_arc; fake_arc < num_arc_indices; ++fake_arc) {
2708 const int new_path_begin = chains_.size();
2709 int32_t arc = fake_arc;
2711 const int chain_begin = tail_head_indices_[arc].head_index;
2712 arc = next_arc_[arc];
2713 const int chain_end = tail_head_indices_[arc].tail_index + 1;
2714 chains_.emplace_back(chain_begin, chain_end);
2715 }
while (arc != fake_arc);
2716 const int path = changed_paths_[fake_arc - first_fake_arc];
2717 const int new_path_end = chains_.size();
2718 paths_[path] = {new_path_begin, new_path_end};
2720 chains_.emplace_back(0, 0);
2724 if (is_invalid_)
return;
2728 DCHECK_EQ(chains_.size(), num_paths_ + 1);
2729 DCHECK(changed_paths_.empty());
2730 tail_head_indices_.clear();
2731 int num_changed_arcs = 0;
2732 for (
const auto& arc : changed_arcs_) {
2734 std::tie(node,
next) = arc;
2735 const int node_index = committed_index_[node];
2736 const int next_index = committed_index_[
next];
2737 const int node_path = committed_nodes_[node_index].path;
2739 (next_index != node_index + 1 || node_path == -1)) {
2740 tail_head_indices_.push_back({node_index, next_index});
2741 changed_arcs_[num_changed_arcs++] = {node,
next};
2742 if (node_path != -1 && !path_has_changed_[node_path]) {
2743 path_has_changed_[node_path] =
true;
2744 changed_paths_.push_back(node_path);
2746 }
else if (node ==
next && node_path != -1) {
2747 changed_arcs_[num_changed_arcs++] = {node, node};
2750 changed_arcs_.resize(num_changed_arcs);
2752 if (tail_head_indices_.size() + changed_paths_.size() <= 8) {
2753 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
2755 MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
2761 if (committed_nodes_.size() < num_nodes_threshold_) {
2762 IncrementalCommit();
2769 is_invalid_ =
false;
2770 chains_.resize(num_paths_ + 1);
2771 for (
const int path : changed_paths_) {
2772 paths_[path] = {path, path + 1};
2773 path_has_changed_[path] =
false;
2775 changed_paths_.clear();
2776 changed_arcs_.clear();
2779 void PathState::CopyNewPathAtEndOfNodes(
int path) {
2781 const int new_path_begin_index = committed_nodes_.size();
2782 const PathBounds path_bounds = paths_[path];
2783 for (
int i = path_bounds.begin_index; i < path_bounds.end_index; ++i) {
2784 const ChainBounds chain_bounds = chains_[i];
2785 committed_nodes_.insert(committed_nodes_.end(),
2786 committed_nodes_.data() + chain_bounds.begin_index,
2787 committed_nodes_.data() + chain_bounds.end_index);
2789 const int new_path_end_index = committed_nodes_.size();
2791 for (
int i = new_path_begin_index; i < new_path_end_index; ++i) {
2792 committed_nodes_[i].path = path;
2798 void PathState::IncrementalCommit() {
2799 const int new_nodes_begin = committed_nodes_.size();
2801 const int chain_begin = committed_nodes_.size();
2802 CopyNewPathAtEndOfNodes(path);
2803 const int chain_end = committed_nodes_.size();
2804 chains_[path] = {chain_begin, chain_end};
2807 const int new_nodes_end = committed_nodes_.size();
2808 for (
int i = new_nodes_begin; i < new_nodes_end; ++i) {
2809 committed_index_[committed_nodes_[i].node] = i;
2815 std::tie(node,
next) = arc;
2816 if (node !=
next)
continue;
2817 const int index = committed_index_[node];
2818 committed_nodes_[
index].path = -1;
2824 void PathState::FullCommit() {
2827 const int old_num_nodes = committed_nodes_.size();
2828 for (
int path = 0; path < num_paths_; ++path) {
2829 const int new_path_begin = committed_nodes_.size() - old_num_nodes;
2830 CopyNewPathAtEndOfNodes(path);
2831 const int new_path_end = committed_nodes_.size() - old_num_nodes;
2832 chains_[path] = {new_path_begin, new_path_end};
2834 committed_nodes_.erase(committed_nodes_.begin(),
2835 committed_nodes_.begin() + old_num_nodes);
2838 constexpr
int kUnindexed = -1;
2839 committed_index_.assign(num_nodes_, kUnindexed);
2841 for (
const CommittedNode committed_node : committed_nodes_) {
2842 committed_index_[committed_node.node] =
index++;
2844 for (
int node = 0; node < num_nodes_; ++node) {
2845 if (committed_index_[node] != kUnindexed)
continue;
2846 committed_index_[node] =
index++;
2847 committed_nodes_.push_back({node, -1});
2855 class PathStateFilter :
public LocalSearchFilter {
2857 std::string DebugString()
const override {
return "PathStateFilter"; }
2858 PathStateFilter(std::unique_ptr<PathState> path_state,
2859 const std::vector<IntVar*>& nexts);
2860 void Relax(
const Assignment*
delta,
const Assignment* deltadelta)
override;
2861 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2862 int64_t objective_min, int64_t objective_max)
override {
2865 void Synchronize(
const Assignment*
delta,
2866 const Assignment* deltadelta)
override{};
2867 void Commit(
const Assignment* assignment,
const Assignment*
delta)
override;
2868 void Revert()
override;
2869 void Reset()
override;
2872 const std::unique_ptr<PathState> path_state_;
2874 std::vector<int> variable_index_to_node_;
2877 std::vector<bool> node_is_assigned_;
2880 PathStateFilter::PathStateFilter(std::unique_ptr<PathState> path_state,
2881 const std::vector<IntVar*>& nexts)
2882 : path_state_(std::move(path_state)) {
2886 for (
const IntVar*
next : nexts) {
2888 min_index = std::min<int>(min_index,
index);
2889 max_index = std::max<int>(max_index,
index);
2891 variable_index_to_node_.resize(max_index - min_index + 1, -1);
2892 index_offset_ = min_index;
2895 for (
int node = 0; node < nexts.size(); ++node) {
2896 const int index = nexts[node]->index() - index_offset_;
2897 variable_index_to_node_[
index] = node;
2901 void PathStateFilter::Relax(
const Assignment*
delta,
2902 const Assignment* deltadelta) {
2903 path_state_->Revert();
2904 for (
const IntVarElement& var_value :
delta->IntVarContainer().elements()) {
2905 if (var_value.Var() ==
nullptr)
continue;
2906 const int index = var_value.Var()->index() - index_offset_;
2907 if (
index < 0 || variable_index_to_node_.size() <=
index)
continue;
2908 const int node = variable_index_to_node_[
index];
2909 if (node == -1)
continue;
2910 if (var_value.Bound()) {
2911 path_state_->ChangeNext(node, var_value.Value());
2913 path_state_->Revert();
2914 path_state_->SetInvalid();
2918 path_state_->CutChains();
2921 void PathStateFilter::Reset() {
2922 path_state_->Revert();
2925 const int num_nodes = path_state_->NumNodes();
2926 node_is_assigned_.assign(num_nodes,
false);
2927 const int num_paths = path_state_->NumPaths();
2928 for (
int path = 0; path < num_paths; ++path) {
2929 const int start = path_state_->Start(path);
2930 const int end = path_state_->End(path);
2931 path_state_->ChangeNext(start, end);
2932 node_is_assigned_[start] =
true;
2933 node_is_assigned_[end] =
true;
2935 for (
int node = 0; node < num_nodes; ++node) {
2936 if (!node_is_assigned_[node]) path_state_->ChangeNext(node, node);
2938 path_state_->CutChains();
2939 path_state_->Commit();
2945 void PathStateFilter::Commit(
const Assignment* assignment,
2946 const Assignment*
delta) {
2947 path_state_->Revert();
2949 Relax(assignment,
nullptr);
2951 Relax(
delta,
nullptr);
2953 path_state_->Commit();
2956 void PathStateFilter::Revert() { path_state_->Revert(); }
2961 std::unique_ptr<PathState> path_state,
2962 const std::vector<IntVar*>& nexts) {
2963 PathStateFilter* filter =
new PathStateFilter(std::move(path_state), nexts);
2968 const PathState* path_state, std::vector<Interval> path_capacity,
2969 std::vector<int> path_class, std::vector<std::vector<Interval>>
demand,
2970 std::vector<Interval> node_capacity)
2971 : path_state_(path_state),
2972 path_capacity_(std::move(path_capacity)),
2973 path_class_(std::move(path_class)),
2974 demand_(std::move(
demand)),
2975 node_capacity_(std::move(node_capacity)),
2976 index_(path_state_->NumNodes(), 0),
2977 maximum_partial_demand_layer_size_(
2978 std::
max(16, 4 * path_state_->NumNodes()))
2980 const int num_nodes = path_state_->
NumNodes();
2981 const int num_paths = path_state_->
NumPaths();
2982 DCHECK_EQ(num_paths, path_capacity_.size());
2983 DCHECK_EQ(num_paths, path_class_.size());
2985 partial_demand_sums_rmq_.resize(maximum_rmq_exponent + 1);
2986 previous_nontrivial_index_.reserve(maximum_partial_demand_layer_size_);
2991 if (path_state_->
IsInvalid())
return true;
2993 const Interval path_capacity = path_capacity_[path];
2998 const int path_class = path_class_[path];
3000 Interval capacity_used = node_capacity_[path_state_->
Start(path)];
3003 if (capacity_used.
min > capacity_used.
max)
return false;
3005 for (
const auto chain : path_state_->
Chains(path)) {
3006 const int first_node = chain.First();
3007 const int last_node = chain.Last();
3009 const int first_index = index_[first_node];
3010 const int last_index = index_[last_node];
3012 const int chain_path = path_state_->
Path(first_node);
3013 const int chain_path_class =
3014 chain_path == -1 ? -1 : path_class_[chain_path];
3018 constexpr
int kMinRangeSizeForRMQ = 4;
3019 if (last_index - first_index > kMinRangeSizeForRMQ &&
3020 path_class == chain_path_class &&
3021 SubpathOnlyHasTrivialNodes(first_index, last_index)) {
3026 GetMinMaxPartialDemandSum(first_index, last_index);
3027 const Interval prev_sum = partial_demand_sums_rmq_[0][first_index - 1];
3035 if (capacity_used.
min > capacity_used.
max)
return false;
3037 const Interval last_sum = partial_demand_sums_rmq_[0][last_index];
3042 for (
const int node : chain) {
3043 const Interval node_capacity = node_capacity_[node];
3046 if (capacity_used.
min > capacity_used.
max)
return false;
3064 const int current_layer_size = partial_demand_sums_rmq_[0].size();
3067 for (
const auto chain : path_state_->
Chains(path)) {
3068 change_size += chain.NumNodes();
3071 if (current_layer_size + change_size <= maximum_partial_demand_layer_size_) {
3072 IncrementalCommit();
3078 void UnaryDimensionChecker::IncrementalCommit() {
3080 const int begin_index = partial_demand_sums_rmq_[0].size();
3081 AppendPathDemandsToSums(path);
3082 UpdateRMQStructure(begin_index, partial_demand_sums_rmq_[0].size());
3086 void UnaryDimensionChecker::FullCommit() {
3088 previous_nontrivial_index_.clear();
3089 for (
auto& sums : partial_demand_sums_rmq_) sums.clear();
3091 const int num_paths = path_state_->
NumPaths();
3092 for (
int path = 0; path < num_paths; ++path) {
3093 const int begin_index = partial_demand_sums_rmq_[0].size();
3094 AppendPathDemandsToSums(path);
3095 UpdateRMQStructure(begin_index, partial_demand_sums_rmq_[0].size());
3099 void UnaryDimensionChecker::AppendPathDemandsToSums(
int path) {
3100 const int path_class = path_class_[path];
3101 Interval demand_sum = {0, 0};
3102 int previous_nontrivial_index = -1;
3103 int index = partial_demand_sums_rmq_[0].size();
3106 partial_demand_sums_rmq_[0].push_back(demand_sum);
3107 previous_nontrivial_index_.push_back(-1);
3110 for (
const int node : path_state_->
Nodes(path)) {
3111 index_[node] =
index;
3112 const Interval
demand = demand_[path_class][node];
3115 partial_demand_sums_rmq_[0].push_back(demand_sum);
3117 const Interval node_capacity = node_capacity_[node];
3121 previous_nontrivial_index =
index;
3123 previous_nontrivial_index_.push_back(previous_nontrivial_index);
3128 void UnaryDimensionChecker::UpdateRMQStructure(
int begin_index,
int end_index) {
3131 const int maximum_rmq_exponent =
3133 for (
int layer = 1, window_size = 1; layer <= maximum_rmq_exponent;
3134 ++layer, window_size *= 2) {
3135 partial_demand_sums_rmq_[layer].resize(end_index);
3136 for (
int i = begin_index; i < end_index - window_size; ++i) {
3137 const Interval i1 = partial_demand_sums_rmq_[layer - 1][i];
3138 const Interval i2 = partial_demand_sums_rmq_[layer - 1][i + window_size];
3139 partial_demand_sums_rmq_[layer][i] = {
std::min(i1.min, i2.min),
3143 partial_demand_sums_rmq_[layer - 1].begin() + end_index - window_size,
3144 partial_demand_sums_rmq_[layer - 1].begin() + end_index,
3145 partial_demand_sums_rmq_[layer].begin() + end_index - window_size);
3155 UnaryDimensionChecker::Interval
3156 UnaryDimensionChecker::GetMinMaxPartialDemandSum(
int first_node_index,
3157 int last_node_index)
const {
3159 DCHECK_LT(first_node_index, last_node_index);
3160 DCHECK_LT(last_node_index, partial_demand_sums_rmq_[0].size());
3165 const int window_size = 1 << layer;
3167 const Interval i1 = partial_demand_sums_rmq_[layer][first_node_index];
3169 partial_demand_sums_rmq_[layer][last_node_index - window_size + 1];
3173 bool UnaryDimensionChecker::SubpathOnlyHasTrivialNodes(
3174 int first_node_index,
int last_node_index)
const {
3176 DCHECK_LT(first_node_index, last_node_index);
3177 DCHECK_LT(last_node_index, previous_nontrivial_index_.size());
3178 return first_node_index > previous_nontrivial_index_[last_node_index];
3183 class UnaryDimensionFilter :
public LocalSearchFilter {
3185 std::string DebugString()
const override {
return name_; }
3186 UnaryDimensionFilter(std::unique_ptr<UnaryDimensionChecker> checker,
3187 const std::string& dimension_name)
3188 : checker_(std::move(checker)),
3189 name_(
absl::StrCat(
"UnaryDimensionFilter(", dimension_name,
")")) {}
3191 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3192 int64_t objective_min, int64_t objective_max)
override {
3193 return checker_->Check();
3196 void Synchronize(
const Assignment* assignment,
3197 const Assignment*
delta)
override {
3202 std::unique_ptr<UnaryDimensionChecker> checker_;
3203 const std::string name_;
3209 Solver* solver, std::unique_ptr<UnaryDimensionChecker> checker,
3210 const std::string& dimension_name) {
3211 UnaryDimensionFilter* filter =
3212 new UnaryDimensionFilter(std::move(checker), dimension_name);
3220 class VariableDomainFilter :
public LocalSearchFilter {
3222 VariableDomainFilter() {}
3223 ~VariableDomainFilter()
override {}
3224 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3225 int64_t objective_min, int64_t objective_max)
override;
3226 void Synchronize(
const Assignment* assignment,
3227 const Assignment*
delta)
override {}
3229 std::string DebugString()
const override {
return "VariableDomainFilter"; }
3232 bool VariableDomainFilter::Accept(
const Assignment*
delta,
3233 const Assignment* deltadelta,
3234 int64_t objective_min,
3235 int64_t objective_max) {
3237 const int size = container.Size();
3238 for (
int i = 0; i < size; ++i) {
3239 const IntVarElement& element = container.Element(i);
3240 if (element.Activated() && !element.Var()->Contains(element.Value())) {
3249 return RevAlloc(
new VariableDomainFilter());
3254 const int IntVarLocalSearchFilter::kUnassigned = -1;
3257 const std::vector<IntVar*>& vars) {
3262 if (!vars.empty()) {
3263 for (
int i = 0; i < vars.size(); ++i) {
3264 const int index = vars[i]->index();
3265 if (
index >= var_index_to_index_.size()) {
3266 var_index_to_index_.resize(
index + 1, kUnassigned);
3268 var_index_to_index_[
index] = i + vars_.size();
3270 vars_.insert(vars_.end(), vars.begin(), vars.end());
3271 values_.resize(vars_.size(), 0);
3272 var_synced_.resize(vars_.size(),
false);
3281 var_synced_.assign(var_synced_.size(),
false);
3292 const int size = container.
Size();
3293 for (
int i = 0; i < size; ++i) {
3296 if (
var !=
nullptr) {
3297 if (i < vars_.size() && vars_[i] ==
var) {
3298 values_[i] = element.
Value();
3299 var_synced_[i] =
true;
3301 const int64_t kUnallocated = -1;
3302 int64_t
index = kUnallocated;
3305 var_synced_[
index] =
true;
3324 SumObjectiveFilter(
const std::vector<IntVar*>& vars,
3334 for (
int i = 0; i < vars.size(); ++i) {
3339 ~SumObjectiveFilter()
override {
3343 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
3344 int64_t objective_min, int64_t objective_max)
override {
3345 if (
delta ==
nullptr) {
3348 if (deltadelta->Empty()) {
3379 LOG(
ERROR) <<
"Unknown local search filter enum value";
3386 virtual int64_t CostOfSynchronizedVariable(int64_t
index) = 0;
3390 virtual bool FillCostOfBoundDeltaVariable(
3392 int* container_index, int64_t* new_cost) = 0;
3393 bool IsIncremental()
const override {
return true; }
3395 std::string DebugString()
const override {
return "SumObjectiveFilter"; }
3397 int64_t GetSynchronizedObjectiveValue()
const override {
3400 int64_t GetAcceptedObjectiveValue()
const override {
return delta_sum_; }
3412 void OnSynchronize(
const Assignment*
delta)
override {
3415 const int64_t
cost = CostOfSynchronizedVariable(i);
3423 int64_t CostOfChanges(
const Assignment* changes,
3424 const int64_t*
const old_costs,
3425 bool cache_delta_values) {
3426 int64_t total_cost = 0;
3428 const int size = container.
Size();
3429 for (
int i = 0; i < size; ++i) {
3430 const IntVarElement& new_element = container.Element(i);
3431 IntVar*
const var = new_element.Var();
3434 total_cost =
CapSub(total_cost, old_costs[
index]);
3435 int64_t new_cost = 0LL;
3436 if (FillCostOfBoundDeltaVariable(container,
index, &i, &new_cost)) {
3437 total_cost =
CapAdd(total_cost, new_cost);
3439 if (cache_delta_values) {
3448 class BinaryObjectiveFilter :
public SumObjectiveFilter {
3450 BinaryObjectiveFilter(
const std::vector<IntVar*>& vars,
3453 : SumObjectiveFilter(vars, filter_enum),
3454 value_evaluator_(std::move(value_evaluator)) {}
3455 ~BinaryObjectiveFilter()
override {}
3456 int64_t CostOfSynchronizedVariable(int64_t
index)
override {
3462 int index,
int* container_index,
3463 int64_t* new_cost)
override {
3464 const IntVarElement& element = container.Element(*container_index);
3465 if (element.Activated()) {
3466 *new_cost = value_evaluator_(
index, element.Value());
3469 const IntVar*
var = element.Var();
3471 *new_cost = value_evaluator_(
index,
var->Min());
3482 class TernaryObjectiveFilter :
public SumObjectiveFilter {
3484 TernaryObjectiveFilter(
const std::vector<IntVar*>& vars,
3485 const std::vector<IntVar*>& secondary_vars,
3488 : SumObjectiveFilter(vars, filter_enum),
3489 secondary_vars_offset_(vars.size()),
3490 value_evaluator_(std::move(value_evaluator)) {
3494 ~TernaryObjectiveFilter()
override {}
3495 int64_t CostOfSynchronizedVariable(int64_t
index)
override {
3500 index + secondary_vars_offset_))
3504 int index,
int* container_index,
3505 int64_t* new_cost)
override {
3508 const IntVarElement& element = container.Element(*container_index);
3509 const IntVar* secondary_var =
3511 if (element.Activated()) {
3512 const int64_t
value = element.Value();
3513 int hint_index = *container_index + 1;
3514 if (hint_index < container.Size() &&
3515 secondary_var == container.Element(hint_index).Var()) {
3517 container.Element(hint_index).Value());
3518 *container_index = hint_index;
3521 container.Element(secondary_var).Value());
3525 const IntVar*
var = element.Var();
3526 if (
var->Bound() && secondary_var->Bound()) {
3527 *new_cost = value_evaluator_(
index,
var->Min(), secondary_var->Min());
3535 int secondary_vars_offset_;
3544 new BinaryObjectiveFilter(vars, std::move(values), filter_enum));
3548 const std::vector<IntVar*>& vars,
3551 return RevAlloc(
new TernaryObjectiveFilter(vars, secondary_vars,
3552 std::move(values), filter_enum));
3556 int64_t initial_max) {
3559 initial_variable_bounds_.push_back({initial_min, initial_max});
3560 variable_bounds_.push_back({initial_min, initial_max});
3561 variable_is_relaxed_.push_back(
false);
3563 const int variable_index = variable_bounds_.size() - 1;
3564 return {
this, variable_index};
3567 void LocalSearchState::RelaxVariableBounds(
int variable_index) {
3569 DCHECK(0 <= variable_index && variable_index < variable_is_relaxed_.size());
3570 if (!variable_is_relaxed_[variable_index]) {
3571 variable_is_relaxed_[variable_index] =
true;
3572 saved_variable_bounds_trail_.emplace_back(variable_bounds_[variable_index],
3574 variable_bounds_[variable_index] = initial_variable_bounds_[variable_index];
3578 int64_t LocalSearchState::VariableMin(
int variable_index)
const {
3580 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3581 return variable_bounds_[variable_index].min;
3584 int64_t LocalSearchState::VariableMax(
int variable_index)
const {
3586 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3587 return variable_bounds_[variable_index].max;
3590 bool LocalSearchState::TightenVariableMin(
int variable_index,
3591 int64_t min_value) {
3593 DCHECK(variable_is_relaxed_[variable_index]);
3594 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3595 Bounds&
bounds = variable_bounds_[variable_index];
3596 if (
bounds.max < min_value) {
3597 state_is_valid_ =
false;
3600 return state_is_valid_;
3603 bool LocalSearchState::TightenVariableMax(
int variable_index,
3604 int64_t max_value) {
3606 DCHECK(variable_is_relaxed_[variable_index]);
3607 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3608 Bounds&
bounds = variable_bounds_[variable_index];
3609 if (
bounds.min > max_value) {
3610 state_is_valid_ =
false;
3613 return state_is_valid_;
3621 saved_variable_bounds_trail_.clear();
3622 variable_is_relaxed_.assign(variable_is_relaxed_.size(),
false);
3626 for (
const auto& bounds_index : saved_variable_bounds_trail_) {
3627 DCHECK(variable_is_relaxed_[bounds_index.second]);
3628 variable_bounds_[bounds_index.second] = bounds_index.first;
3630 saved_variable_bounds_trail_.clear();
3631 variable_is_relaxed_.assign(variable_is_relaxed_.size(),
false);
3632 state_is_valid_ =
true;
3640 std::string
DebugString()
const override {
return "LocalSearchProfiler"; }
3642 operator_stats_.clear();
3643 filter_stats_.clear();
3647 if (
solver()->TopLevelSearch() ==
solver()->ActiveSearch()) {
3653 std::vector<const LocalSearchOperator*> operators;
3654 for (
const auto& stat : operator_stats_) {
3655 operators.push_back(stat.first);
3658 operators.begin(), operators.end(),
3664 const OperatorStats& stats =
gtl::FindOrDie(operator_stats_, op);
3666 local_search_operator_statistics =
3672 stats.filtered_neighbors);
3674 stats.accepted_neighbors);
3677 std::vector<const LocalSearchFilter*> filters;
3678 for (
const auto& stat : filter_stats_) {
3679 filters.push_back(stat.first);
3681 std::sort(filters.begin(), filters.end(),
3688 const FilterStats& stats =
gtl::FindOrDie(filter_stats_, filter);
3690 local_search_filter_statistics =
3693 filter->DebugString());
3700 solver()->filtered_neighbors());
3702 solver()->accepted_neighbors());
3703 return statistics_proto;
3706 size_t max_name_size = 0;
3707 std::vector<const LocalSearchOperator*> operators;
3708 for (
const auto& stat : operator_stats_) {
3709 operators.push_back(stat.first);
3711 std::max(max_name_size, stat.first->DebugString().length());
3714 operators.begin(), operators.end(),
3719 std::string overview =
"Local search operator statistics:\n";
3720 absl::StrAppendFormat(&overview,
3721 "%*s | Neighbors | Filtered | Accepted | Time (s)\n",
3723 OperatorStats total_stats;
3725 const OperatorStats& stats =
gtl::FindOrDie(operator_stats_, op);
3726 const std::string&
name = op->DebugString();
3727 absl::StrAppendFormat(&overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n",
3728 max_name_size,
name, stats.neighbors,
3729 stats.filtered_neighbors, stats.accepted_neighbors,
3731 total_stats.neighbors += stats.neighbors;
3732 total_stats.filtered_neighbors += stats.filtered_neighbors;
3733 total_stats.accepted_neighbors += stats.accepted_neighbors;
3734 total_stats.seconds += stats.seconds;
3736 absl::StrAppendFormat(&overview,
"%*s | %9ld | %8ld | %8ld | %7.2g\n",
3737 max_name_size,
"Total", total_stats.neighbors,
3738 total_stats.filtered_neighbors,
3739 total_stats.accepted_neighbors, total_stats.seconds);
3741 std::vector<const LocalSearchFilter*> filters;
3742 for (
const auto& stat : filter_stats_) {
3743 filters.push_back(stat.first);
3745 std::max(max_name_size, stat.first->DebugString().length());
3747 std::sort(filters.begin(), filters.end(),
3753 absl::StrAppendFormat(&overview,
3754 "Local search filter statistics:\n%*s | Calls | " 3755 " Rejects | Time (s) " 3758 FilterStats total_filter_stats;
3760 const FilterStats& stats =
gtl::FindOrDie(filter_stats_, filter);
3761 const std::string&
name = filter->DebugString();
3762 absl::StrAppendFormat(&overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n",
3763 max_name_size,
name, stats.calls, stats.rejects,
3764 stats.seconds, stats.rejects / stats.seconds);
3765 total_filter_stats.calls += stats.calls;
3766 total_filter_stats.rejects += stats.rejects;
3767 total_filter_stats.seconds += stats.seconds;
3769 absl::StrAppendFormat(
3770 &overview,
"%*s | %9ld | %9ld | %7.2g | %7.2g\n", max_name_size,
3771 "Total", total_filter_stats.calls, total_filter_stats.rejects,
3772 total_filter_stats.seconds,
3773 total_filter_stats.rejects / total_filter_stats.seconds);
3779 if (last_operator_ != op->
Self()) {
3781 last_operator_ = op->
Self();
3787 if (neighbor_found) {
3788 operator_stats_[op->
Self()].neighbors++;
3793 bool neighbor_found)
override {
3794 if (neighbor_found) {
3795 operator_stats_[op->
Self()].filtered_neighbors++;
3800 bool neighbor_found)
override {
3801 if (neighbor_found) {
3802 operator_stats_[op->
Self()].accepted_neighbors++;
3806 filter_stats_[filter].calls++;
3807 filter_timer_.
Start();
3810 filter_timer_.
Stop();
3811 auto& stats = filter_stats_[filter];
3812 stats.seconds += filter_timer_.
Get();
3821 if (last_operator_ !=
nullptr) {
3823 operator_stats_[last_operator_].seconds += timer_.
Get();
3828 struct OperatorStats {
3829 int64_t neighbors = 0;
3830 int64_t filtered_neighbors = 0;
3831 int64_t accepted_neighbors = 0;
3835 struct FilterStats {
3837 int64_t rejects = 0;
3842 const LocalSearchOperator* last_operator_ =
nullptr;
3843 absl::flat_hash_map<const LocalSearchOperator*, OperatorStats>
3845 absl::flat_hash_map<const LocalSearchFilter*, FilterStats> filter_stats_;
3862 if (local_search_profiler_ !=
nullptr) {
3869 if (local_search_profiler_ !=
nullptr) {
3875 void LocalSearchFilterManager::InitializeForcedEvents() {
3876 const int num_events = filter_events_.size();
3877 int next_forced_event = num_events;
3878 next_forced_events_.resize(num_events);
3879 for (
int i = num_events - 1; i >= 0; --i) {
3880 next_forced_events_[i] = next_forced_event;
3881 if (filter_events_[i].filter->IsIncremental() ||
3882 (filter_events_[i].event_type == FilterEventType::kRelax &&
3883 next_forced_event != num_events)) {
3884 next_forced_event = i;
3890 std::vector<LocalSearchFilter*> filters)
3891 : synchronized_value_(std::numeric_limits<int64_t>::
min()),
3892 accepted_value_(std::numeric_limits<int64_t>::
min()) {
3893 filter_events_.reserve(2 * filters.size());
3895 filter_events_.push_back({filter, FilterEventType::kRelax});
3898 filter_events_.push_back({filter, FilterEventType::kAccept});
3900 InitializeForcedEvents();
3904 std::vector<FilterEvent> filter_events)
3905 : filter_events_(std::move(filter_events)),
3906 synchronized_value_(std::numeric_limits<int64_t>::
min()),
3907 accepted_value_(std::numeric_limits<int64_t>::
min()) {
3908 InitializeForcedEvents();
3914 for (
int i = last_event_called_; i >= 0; --i) {
3915 auto [filter, event_type] = filter_events_[i];
3916 if (event_type == FilterEventType::kRelax) filter->Revert();
3918 last_event_called_ = -1;
3927 int64_t objective_min,
3928 int64_t objective_max) {
3930 accepted_value_ = 0;
3932 const int num_events = filter_events_.size();
3933 for (
int i = 0; i < num_events;) {
3934 last_event_called_ = i;
3935 auto [filter, event_type] = filter_events_[last_event_called_];
3936 switch (event_type) {
3937 case FilterEventType::kAccept: {
3939 const bool accept = filter->Accept(
3940 delta, deltadelta,
CapSub(objective_min, accepted_value_),
3941 CapSub(objective_max, accepted_value_));
3943 if (monitor !=
nullptr) monitor->
EndFiltering(filter, !accept);
3946 CapAdd(accepted_value_, filter->GetAcceptedObjectiveValue());
3948 ok = accepted_value_ <= objective_max;
3952 case FilterEventType::kRelax: {
3953 filter->Relax(
delta, deltadelta);
3957 LOG(
FATAL) <<
"Unknown filter event type.";
3963 i = next_forced_events_[i];
3974 const bool reset_to_assignment =
delta ==
nullptr ||
delta->Empty();
3976 for (
auto [filter, event_type] : filter_events_) {
3977 switch (event_type) {
3978 case FilterEventType::kAccept: {
3981 case FilterEventType::kRelax: {
3982 if (reset_to_assignment) {
3984 filter->Relax(assignment,
nullptr);
3986 filter->Relax(
delta,
nullptr);
3991 LOG(
FATAL) <<
"Unknown filter event type.";
3996 synchronized_value_ = 0;
3998 switch (event_type) {
3999 case FilterEventType::kAccept: {
4000 filter->Synchronize(assignment,
delta);
4001 synchronized_value_ =
CapAdd(synchronized_value_,
4002 filter->GetSynchronizedObjectiveValue());
4005 case FilterEventType::kRelax: {
4006 filter->Commit(assignment,
delta);
4010 LOG(
FATAL) <<
"Unknown filter event type.";
4027 std::string
DebugString()
const override {
return "FindOneNeighbor"; }
4031 int64_t objective_min, int64_t objective_max);
4032 void SynchronizeAll(
Solver* solver);
4035 IntVar*
const objective_;
4036 std::unique_ptr<Assignment> reference_assignment_;
4042 bool neighbor_found_;
4044 int64_t solutions_since_last_check_;
4045 int64_t check_period_;
4047 bool has_checked_assignment_ =
false;
4059 : assignment_(assignment),
4061 reference_assignment_(new
Assignment(assignment_)),
4063 ls_operator_(ls_operator),
4064 sub_decision_builder_(sub_decision_builder),
4066 original_limit_(limit),
4067 neighbor_found_(false),
4068 filter_manager_(filter_manager),
4069 solutions_since_last_check_(0),
4071 assignment_->solver()->
parameters().check_solution_period()),
4072 last_checked_assignment_(assignment) {
4073 CHECK(
nullptr != assignment);
4074 CHECK(
nullptr != ls_operator);
4078 if (
nullptr == limit) {
4086 VLOG(1) <<
"Disabling neighbor-check skipping outside of first accept.";
4093 VLOG(1) <<
"Disabling neighbor-check skipping for LNS.";
4097 if (!reference_assignment_->HasObjective()) {
4098 reference_assignment_->AddObjective(objective_);
4103 CHECK(
nullptr != solver);
4105 if (original_limit_ !=
nullptr) {
4106 limit_->
Copy(original_limit_);
4113 if (!neighbor_found_) {
4121 SynchronizeAll(solver);
4131 if (sub_decision_builder_) {
4132 restore = solver->
Compose(restore, sub_decision_builder_);
4140 delta->ClearObjective();
4141 deltadelta->
Clear();
4143 if (++counter >= absl::GetFlag(FLAGS_cp_local_search_sync_frequency) &&
4144 pool_->
SyncNeeded(reference_assignment_.get())) {
4147 SynchronizeAll(solver);
4150 bool has_neighbor =
false;
4151 if (!limit_->
Check()) {
4155 ls_operator_, has_neighbor,
delta, deltadelta);
4158 if (has_neighbor && !solver->IsUncheckedSolutionLimitReached()) {
4159 solver->neighbors_ += 1;
4166 const bool mh_filter =
4171 objective_min = objective_->
Min();
4172 objective_max = objective_->
Max();
4174 if (
delta->HasObjective() &&
delta->Objective() == objective_) {
4175 objective_min =
std::max(objective_min,
delta->ObjectiveMin());
4176 objective_max =
std::min(objective_max,
delta->ObjectiveMax());
4178 const bool move_filter = FilterAccept(solver,
delta, deltadelta,
4179 objective_min, objective_max);
4181 ls_operator_, mh_filter && move_filter);
4182 if (!mh_filter || !move_filter) {
4183 if (filter_manager_ !=
nullptr) filter_manager_->
Revert();
4186 solver->filtered_neighbors_ += 1;
4187 if (
delta->HasObjective()) {
4199 const bool check_solution = (solutions_since_last_check_ == 0) ||
4202 !
delta->AreAllElementsBound();
4203 if (has_checked_assignment_) solutions_since_last_check_++;
4204 if (solutions_since_last_check_ >= check_period_) {
4205 solutions_since_last_check_ = 0;
4207 const bool accept = !check_solution || solver->
SolveAndCommit(restore);
4211 solver->accepted_neighbors_ += 1;
4212 if (check_solution) {
4215 assignment_->
Store();
4217 neighbor_found_ =
true;
4218 has_checked_assignment_ =
true;
4232 solver->IncrementUncheckedSolutionCounter();
4234 SynchronizeAll(solver);
4237 neighbor_found_ =
true;
4239 if (filter_manager_ !=
nullptr) filter_manager_->
Revert();
4240 if (check_period_ > 1 && has_checked_assignment_) {
4246 VLOG(1) <<
"Imperfect filtering detected, backtracking to last " 4247 "checked solution and checking all solutions.";
4249 solutions_since_last_check_ = 0;
4251 SynchronizeAll(solver);
4256 if (neighbor_found_) {
4268 has_checked_assignment_ =
true;
4276 SynchronizeAll(solver);
4289 int64_t objective_min,
4290 int64_t objective_max) {
4291 if (filter_manager_ ==
nullptr)
return true;
4293 return filter_manager_->
Accept(monitor,
delta, deltadelta, objective_min,
4297 void FindOneNeighbor::SynchronizeAll(Solver* solver) {
4299 neighbor_found_ =
false;
4301 solver->GetLocalSearchMonitor()->BeginOperatorStart();
4302 ls_operator_->
Start(reference_assignment_.get());
4303 if (filter_manager_ !=
nullptr) {
4304 filter_manager_->
Synchronize(reference_assignment_.get(),
nullptr);
4306 solver->GetLocalSearchMonitor()->EndOperatorStart();
4319 solution_pool_(pool),
4326 return "LocalSearchPhaseParameters";
4333 return sub_decision_builder_;
4337 return filter_manager_;
4341 IntVar*
const objective_;
4353 ls_operator, sub_decision_builder,
4361 ls_operator, sub_decision_builder,
4370 ls_operator, sub_decision_builder,
4371 limit, filter_manager);
4379 sub_decision_builder,
nullptr,
nullptr);
4387 sub_decision_builder, limit,
nullptr);
4396 sub_decision_builder, limit,
4410 class NestedSolveDecision :
public Decision {
4413 enum StateType { DECISION_PENDING, DECISION_FAILED, DECISION_FOUND };
4415 NestedSolveDecision(DecisionBuilder*
const db,
bool restore,
4416 const std::vector<SearchMonitor*>& monitors);
4417 NestedSolveDecision(DecisionBuilder*
const db,
bool restore);
4418 ~NestedSolveDecision()
override {}
4419 void Apply(Solver*
const solver)
override;
4420 void Refute(Solver*
const solver)
override;
4421 std::string DebugString()
const override {
return "NestedSolveDecision"; }
4422 int state()
const {
return state_; }
4425 DecisionBuilder*
const db_;
4427 std::vector<SearchMonitor*> monitors_;
4431 NestedSolveDecision::NestedSolveDecision(
4432 DecisionBuilder*
const db,
bool restore,
4433 const std::vector<SearchMonitor*>& monitors)
4436 monitors_(monitors),
4437 state_(DECISION_PENDING) {
4438 CHECK(
nullptr != db);
4441 NestedSolveDecision::NestedSolveDecision(DecisionBuilder*
const db,
4443 : db_(db), restore_(restore), state_(DECISION_PENDING) {
4444 CHECK(
nullptr != db);
4447 void NestedSolveDecision::Apply(Solver*
const solver) {
4448 CHECK(
nullptr != solver);
4450 if (solver->Solve(db_, monitors_)) {
4451 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FOUND));
4453 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FAILED));
4456 if (solver->SolveAndCommit(db_, monitors_)) {
4457 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FOUND));
4459 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FAILED));
4464 void NestedSolveDecision::Refute(Solver*
const solver) {}
4475 class LocalSearch :
public DecisionBuilder {
4477 LocalSearch(Assignment*
const assignment, IntVar* objective,
4478 SolutionPool*
const pool, LocalSearchOperator*
const ls_operator,
4479 DecisionBuilder*
const sub_decision_builder,
4480 RegularLimit*
const limit,
4481 LocalSearchFilterManager* filter_manager);
4484 LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4485 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4486 LocalSearchOperator*
const ls_operator,
4487 DecisionBuilder*
const sub_decision_builder,
4488 RegularLimit*
const limit,
4489 LocalSearchFilterManager* filter_manager);
4490 LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4491 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4492 DecisionBuilder*
const first_solution_sub_decision_builder,
4493 LocalSearchOperator*
const ls_operator,
4494 DecisionBuilder*
const sub_decision_builder,
4495 RegularLimit*
const limit,
4496 LocalSearchFilterManager* filter_manager);
4497 LocalSearch(
const std::vector<SequenceVar*>& vars, IntVar* objective,
4498 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4499 LocalSearchOperator*
const ls_operator,
4500 DecisionBuilder*
const sub_decision_builder,
4501 RegularLimit*
const limit,
4502 LocalSearchFilterManager* filter_manager);
4503 ~LocalSearch()
override;
4504 Decision* Next(Solver*
const solver)
override;
4505 std::string DebugString()
const override {
return "LocalSearch"; }
4506 void Accept(ModelVisitor*
const visitor)
const override;
4509 void PushFirstSolutionDecision(DecisionBuilder* first_solution);
4510 void PushLocalSearchDecision();
4513 Assignment* assignment_;
4515 SolutionPool*
const pool_;
4516 LocalSearchOperator*
const ls_operator_;
4517 DecisionBuilder*
const first_solution_sub_decision_builder_;
4518 DecisionBuilder*
const sub_decision_builder_;
4519 std::vector<NestedSolveDecision*> nested_decisions_;
4520 int nested_decision_index_;
4521 RegularLimit*
const limit_;
4522 LocalSearchFilterManager*
const filter_manager_;
4526 LocalSearch::LocalSearch(Assignment*
const assignment, IntVar* objective,
4527 SolutionPool*
const pool,
4528 LocalSearchOperator*
const ls_operator,
4529 DecisionBuilder*
const sub_decision_builder,
4530 RegularLimit*
const limit,
4531 LocalSearchFilterManager* filter_manager)
4532 : assignment_(nullptr),
4535 ls_operator_(ls_operator),
4536 first_solution_sub_decision_builder_(sub_decision_builder),
4537 sub_decision_builder_(sub_decision_builder),
4538 nested_decision_index_(0),
4540 filter_manager_(filter_manager),
4541 has_started_(false) {
4542 CHECK(
nullptr != assignment);
4543 CHECK(
nullptr != ls_operator);
4544 Solver*
const solver = assignment->solver();
4545 assignment_ = solver->GetOrCreateLocalSearchState();
4546 assignment_->Copy(assignment);
4547 DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment);
4548 PushFirstSolutionDecision(restore);
4549 PushLocalSearchDecision();
4552 LocalSearch::LocalSearch(
const std::vector<IntVar*>& vars, IntVar* objective,
4553 SolutionPool*
const pool,
4554 DecisionBuilder*
const first_solution,
4555 LocalSearchOperator*
const ls_operator,
4556 DecisionBuilder*
const sub_decision_builder,
4557 RegularLimit*
const limit,
4558 LocalSearchFilterManager* filter_manager)
4559 : assignment_(nullptr),
4562 ls_operator_(ls_operator),
4563 first_solution_sub_decision_builder_(sub_decision_builder),
4564 sub_decision_builder_(sub_decision_builder),
4565 nested_decision_index_(0),
4567 filter_manager_(filter_manager),
4568 has_started_(false) {
4569 CHECK(
nullptr != first_solution);
4570 CHECK(
nullptr != ls_operator);
4571 CHECK(!vars.empty());
4572 Solver*
const solver = vars[0]->solver();
4573 assignment_ = solver->GetOrCreateLocalSearchState();
4574 assignment_->Add(vars);
4575 PushFirstSolutionDecision(first_solution);
4576 PushLocalSearchDecision();
4579 LocalSearch::LocalSearch(
4580 const std::vector<IntVar*>& vars, IntVar* objective,
4581 SolutionPool*
const pool, DecisionBuilder*
const first_solution,
4582 DecisionBuilder*
const first_solution_sub_decision_builder,
4583 LocalSearchOperator*
const ls_operator,
4584 DecisionBuilder*
const sub_decision_builder, RegularLimit*
const limit,
4585 LocalSearchFilterManager* filter_manager)
4586 : assignment_(nullptr),
4589 ls_operator_(ls_operator),
4590 first_solution_sub_decision_builder_(first_solution_sub_decision_builder),
4591 sub_decision_builder_(sub_decision_builder),
4592 nested_decision_index_(0),
4594 filter_manager_(filter_manager),
4595 has_started_(false) {
4596 CHECK(
nullptr != first_solution);
4597 CHECK(
nullptr != ls_operator);
4598 CHECK(!vars.empty());
4599 Solver*
const solver = vars[0]->solver();
4600 assignment_ = solver->GetOrCreateLocalSearchState();
4601 assignment_->Add(vars);
4602 PushFirstSolutionDecision(first_solution);
4603 PushLocalSearchDecision();
4606 LocalSearch::LocalSearch(
const std::vector<SequenceVar*>& vars,
4607 IntVar* objective, SolutionPool*
const pool,
4608 DecisionBuilder*
const first_solution,
4609 LocalSearchOperator*
const ls_operator,
4610 DecisionBuilder*
const sub_decision_builder,
4611 RegularLimit*
const limit,
4612 LocalSearchFilterManager* filter_manager)
4613 : assignment_(nullptr),
4616 ls_operator_(ls_operator),
4617 first_solution_sub_decision_builder_(sub_decision_builder),
4618 sub_decision_builder_(sub_decision_builder),
4619 nested_decision_index_(0),
4621 filter_manager_(filter_manager),
4622 has_started_(false) {
4623 CHECK(
nullptr != first_solution);
4624 CHECK(
nullptr != ls_operator);
4625 CHECK(!vars.empty());
4626 Solver*
const solver = vars[0]->solver();
4627 assignment_ = solver->GetOrCreateLocalSearchState();
4628 assignment_->Add(vars);
4629 PushFirstSolutionDecision(first_solution);
4630 PushLocalSearchDecision();
4633 LocalSearch::~LocalSearch() {}
4636 void LocalSearch::Accept(ModelVisitor*
const visitor)
const {
4637 DCHECK(assignment_ !=
nullptr);
4640 const std::vector<IntVarElement>& elements =
4642 if (!elements.empty()) {
4643 std::vector<IntVar*> vars;
4644 for (
const IntVarElement& elem : elements) {
4645 vars.push_back(elem.Var());
4650 const std::vector<IntervalVarElement>& interval_elements =
4652 if (!interval_elements.empty()) {
4653 std::vector<IntervalVar*> interval_vars;
4654 for (
const IntervalVarElement& elem : interval_elements) {
4655 interval_vars.push_back(elem.Var());
4668 Decision* LocalSearch::Next(Solver*
const solver) {
4669 CHECK(
nullptr != solver);
4670 CHECK_LT(0, nested_decisions_.size());
4671 if (!has_started_) {
4672 nested_decision_index_ = 0;
4673 solver->SaveAndSetValue(&has_started_,
true);
4674 }
else if (nested_decision_index_ < 0) {
4677 NestedSolveDecision* decision = nested_decisions_[nested_decision_index_];
4678 const int state = decision->state();
4680 case NestedSolveDecision::DECISION_FAILED: {
4684 ls_operator_->
Reset();
4686 nested_decision_index_ = -1;
4691 case NestedSolveDecision::DECISION_PENDING: {
4694 const int32_t kLocalSearchBalancedTreeDepth = 32;
4695 const int depth = solver->SearchDepth();
4696 if (depth < kLocalSearchBalancedTreeDepth) {
4697 return solver->balancing_decision();
4699 if (depth > kLocalSearchBalancedTreeDepth) {
4704 case NestedSolveDecision::DECISION_FOUND: {
4706 if (nested_decision_index_ + 1 < nested_decisions_.size()) {
4707 ++nested_decision_index_;
4712 LOG(
ERROR) <<
"Unknown local search state";
4719 void LocalSearch::PushFirstSolutionDecision(DecisionBuilder* first_solution) {
4720 CHECK(first_solution);
4721 Solver*
const solver = assignment_->
solver();
4723 DecisionBuilder* first_solution_and_store = solver->Compose(
4724 first_solution, first_solution_sub_decision_builder_, store);
4725 std::vector<SearchMonitor*> monitor;
4726 monitor.push_back(
limit_);
4727 nested_decisions_.push_back(solver->RevAlloc(
4728 new NestedSolveDecision(first_solution_and_store,
false, monitor)));
4731 void LocalSearch::PushLocalSearchDecision() {
4732 Solver*
const solver = assignment_->
solver();
4733 DecisionBuilder* find_neighbors = solver->
RevAlloc(
4734 new FindOneNeighbor(assignment_,
objective_, pool_, ls_operator_,
4735 sub_decision_builder_,
limit_, filter_manager_));
4736 nested_decisions_.push_back(
4737 solver->RevAlloc(
new NestedSolveDecision(find_neighbors,
false)));
4740 class DefaultSolutionPool :
public SolutionPool {
4742 DefaultSolutionPool() {}
4744 ~DefaultSolutionPool()
override {}
4746 void Initialize(Assignment*
const assignment)
override {
4747 reference_assignment_ = absl::make_unique<Assignment>(assignment);
4750 void RegisterNewSolution(Assignment*
const assignment)
override {
4751 reference_assignment_->CopyIntersection(assignment);
4754 void GetNextSolution(Assignment*
const assignment)
override {
4755 assignment->CopyIntersection(reference_assignment_.get());
4758 bool SyncNeeded(Assignment*
const local_assignment)
override {
return false; }
4760 std::string DebugString()
const override {
return "DefaultSolutionPool"; }
4763 std::unique_ptr<Assignment> reference_assignment_;
4768 return RevAlloc(
new DefaultSolutionPool());
4795 first_solution, first_solution_sub_decision_builder,
4801 const std::vector<SequenceVar*>& vars,
DecisionBuilder* first_solution,
TSPLns(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int tsp_size)
void ChangeCostMatrix(CostFunction cost)
~MakeChainInactiveOperator() override
This is the base class for building an Lns operator.
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...
bool MakeNeighbor() override
int64_t CapSub(int64_t x, int64_t y)
NeighborhoodLimit(LocalSearchOperator *const op, int64_t limit)
LocalSearchFilter * MakeUnaryDimensionFilter(Solver *solver, std::unique_ptr< UnaryDimensionChecker > checker, const std::string &dimension_name)
void EndMakeNextNeighbor(const LocalSearchOperator *op, bool neighbor_found, const Assignment *delta, const Assignment *deltadelta) override
bool FindIndex(IntVar *const var, int64_t *index) const
RegularLimit * limit() const
AssignmentContainer< IntVar, IntVarElement > IntContainer
Set of parameters used to configure how the neighnorhood is traversed.
bool MakeNeighbor() 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
virtual bool MakeNeighbor()=0
The base class for all local search operators.
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 AddVars(const std::vector< IntVar * > &vars)
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)
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...
void set_num_accepted_neighbors(::PROTOBUF_NAMESPACE_ID::int64 value)
std::string DebugString() const override
Solver(const std::string &name)
Solver API.
~SwapActiveOperator() override
RegularLimit * MakeIdenticalClone() const
#define CHECK_GE(val1, val2)
virtual bool NextFragment()=0
int GetSiblingAlternativeIndex(int node) const
Returns the index of the alternative set of the sibling of node.
void SetValue(int64_t index, const int64_t &value)
void TopPeriodicCheck()
Performs PeriodicCheck on the top-level search; for instance, can be called from a nested solve to ch...
virtual void EndAcceptNeighbor(const LocalSearchOperator *op, bool neighbor_found)=0
Base class of the local search operators dedicated to path modifications (a path is a set of nodes li...
::operations_research::LocalSearchStatistics_LocalSearchFilterStatistics * add_local_search_filter_statistics()
Lin-Kernighan local search.
bool MakeNeighbor() override
const int number_of_nexts_
Operator which defines a neighborhood to decrement values.
std::string PrintOverview() const
void set_local_search_filter(ArgT0 &&arg0, ArgT... args)
LocalSearchFilter * MakeRejectFilter()
const std::vector< E > & elements() const
void SetNext(int64_t from, int64_t to, int64_t path)
Sets 'to' to be the node after 'from' on the given path.
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...
std::string DebugString() const override
~RelocateAndMakeInactiveOperator() override
bool MakeNeighbor() override
Operator which exchanges the positions of two nodes.
RegularLimit * MakeSolutionsLimit(int64_t solutions)
Creates a search limit that constrains the number of solutions found during the search.
ABSL_FLAG(int, cp_local_search_sync_frequency, 16, "Frequency of checks for better solutions in the solution pool.")
int64_t StartNode(int i) const
Returns the start node of the ith base node.
#define CHECK_GT(val1, val2)
LocalSearchVariable AddVariable(int64_t initial_min, int64_t initial_max)
bool AcceptDelta(Search *const search, Assignment *delta, Assignment *deltadelta)
#define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass)
void RevertChanges(bool incremental)
#define VLOG(verboselevel)
std::string DebugString() const override
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...
int64_t synchronized_sum_
ChainRange Chains(int path) const
int64_t BaseNode(int i) const
Returns the ith base node of the operator.
void Synchronize(const Assignment *assignment, const Assignment *delta) override
This method should not be overridden.
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.
LocalSearchOperator * ls_operator() const
bool skip_locally_optimal_paths
Skip paths which have been proven locally optimal.
int PathClass(int i) const
Returns the class of the path of the ith base node.
bool SkipUnchanged(int index) const override
virtual void BeginMakeNextNeighbor(const LocalSearchOperator *op)=0
IntVar * Var(int index) const
bool IsIncremental() const override
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
void set_duration_seconds(double value)
LocalSearchProfiler * BuildLocalSearchProfiler(Solver *solver)
void set_duration_seconds(double value)
bool MakeNeighbor() override
virtual void OnSynchronize(const Assignment *delta)
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
Operator which makes path nodes inactive.
virtual int64_t Min() const =0
std::string DebugString() const override
TSPOpt(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int chain_length)
virtual void BeginFiltering(const LocalSearchFilter *filter)=0
const std::vector< std::pair< int, int > > & ChangedArcs() const
bool MakeNeighbor() override
std::vector< int64_t > prev_values_
IntVar * Var(int64_t index) const
Returns the variable of given index.
int64_t ObjectiveValue() const
void EndFilterNeighbor(const LocalSearchOperator *op, bool neighbor_found) override
bool IsInactive(int64_t node) const
Returns true if node is inactive.
int next_base_to_increment_
void AcceptUncheckedNeighbor(Search *const search)
#define DCHECK_GT(val1, val2)
~LocalSearchPhaseParameters() override
SolutionPool * solution_pool() const
const std::vector< int > & Neighbors(int index) const
std::string DebugString() const override
Usual limit based on wall_time, number of explored branches and number of failures in the search tree...
virtual bool HoldsDelta() const
~ExtendedSwapActiveOperator() override
void set_total_num_neighbors(::PROTOBUF_NAMESPACE_ID::int64 value)
LocalSearchStatistics ExportToLocalSearchStatistics() const
const E & Element(const V *const var) const
virtual int64_t ModifyValue(int64_t index, int64_t value)=0
void EndFiltering(const LocalSearchFilter *filter, bool reject) override
std::string DebugString() const override
std::string DebugString() const override
A DecisionBuilder is responsible for creating the search tree.
virtual void OnNodeInitialization()
Called by OnStart() after initializing node information.
void BeginFiltering(const LocalSearchFilter *filter) override
RelocateAndMakeActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
virtual std::string DebugString() const
const IntervalContainer & IntervalVarContainer() const
bool MakeNeighbor() override
ConstraintSolverParameters parameters() const
Stored Parameters.
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
int number_of_nexts() const
Number of next variables.
BaseLns(const std::vector< IntVar * > &vars)
LocalSearchPhaseParameters * MakeLocalSearchPhaseParameters(IntVar *objective, LocalSearchOperator *const ls_operator, DecisionBuilder *const sub_decision_builder)
Local Search Phase Parameters.
TwoOpt(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
#define CHECK_LT(val1, val2)
virtual void Start(const Assignment *assignment)=0
bool LocalOptimumReached(Search *const search)
void AddVars(const std::vector< IntVar * > &vars)
Add variables to "track" to the filter.
ReverseView< Container > reversed_view(const Container &c)
const bool ignore_path_vars_
virtual void BeginFilterNeighbor(const LocalSearchOperator *op)=0
Operator which relaxes all inactive nodes and one sub-chain of six consecutive arcs.
int64_t Next(int64_t node) const
Returns the node after node in the current delta.
Relocate neighborhood with length of 1 (see OROPT comment).
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...
Move is accepted when the current objective value <= objective.Max.
IntVarLocalSearchFilter(const std::vector< IntVar * > &vars)
int MostSignificantBitPosition32(uint32_t n)
Operator which defines one neighbor per variable.
virtual ~NearestNeighbors()
Operator which cross exchanges the starting chains of 2 paths, including exchanging the whole paths.
void DeleteLocalSearchProfiler(LocalSearchProfiler *monitor)
LocalSearchFilterBound
This enum is used in Solver::MakeLocalSearchObjectiveFilter.
bool HasObjective() const
Filter manager: when a move is made, filters are executed to decide whether the solution is feasible ...
void AcceptNeighbor(Search *const search)
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
std::vector< int64_t > start_to_path_
int64_t CapAdd(int64_t x, int64_t y)
DecisionBuilder * MakeStoreAssignment(Assignment *assignment)
Returns a DecisionBuilder which stores an Assignment (calls void Assignment::Store())
std::string DebugString() const override
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
LocalSearchFilter * MakeVariableDomainFilter()
~MakeInactiveOperator() override
~RelocateAndMakeActiveOperator() override
void EndOperatorStart() override
const int primary_vars_size_
void set_num_neighbors(::PROTOBUF_NAMESPACE_ID::int64 value)
const std::vector< int > & ChangedPaths() const
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 IsVarSynced(int index) const
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 ...
ExtendedSwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
void SynchronizeOnAssignment(const Assignment *assignment)
bool accept_path_end_base
True if path ends should be considered when iterating over neighbors.
bool HoldsDelta() const override
bool HasFragments() const override
static const char kVariableGroupExtension[]
bool UseFastLocalSearch() const
Returns true if fast local search is enabled.
Operator which makes a "chain" of path nodes inactive.
#define CHECK_LE(val1, val2)
int64_t GetInactiveNode() const
std::string DebugString() const override
const int64_t & Value(int64_t index) const
Returns the value in the current assignment of the variable of given index.
LocalSearchOperators
This enum is used in Solver::MakeOperator to specify the neighborhood to create.
UnaryDimensionChecker(const PathState *path_state, std::vector< Interval > path_capacity, std::vector< int > path_class, std::vector< std::vector< Interval >> demand, std::vector< Interval > node_capacity)
void AppendToFragment(int index)
bool MakeNeighbor() override
bool ApplyChanges(Assignment *delta, Assignment *deltadelta) const
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
void ExitSearch() override
End of the search.
void set_num_calls(::PROTOBUF_NAMESPACE_ID::int64 value)
void Deactivate(int64_t index)
int64_t Path(int64_t node) const
Returns the index of the path to which node belongs in the current delta.
bool MakeNeighbor() override
void RestartSearch() override
Restart the search.
virtual void RegisterNewSolution(Assignment *const assignment)=0
This method is called when a new solution has been accepted by the local search.
std::string DebugString() const override
bool ContainsKey(const Collection &collection, const Key &key)
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 ...
~BaseInactiveNodeToPathOperator() override
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
Redefines MakeNextNeighbor to export a simpler interface.
void set_num_rejects(::PROTOBUF_NAMESPACE_ID::int64 value)
NearestNeighbors(Solver::IndexEvaluator3 evaluator, const PathOperator &path_operator, int size)
Exchange(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::function< int64_t(int64_t, int64_t, int64_t)> IndexEvaluator3
virtual void EndFiltering(const LocalSearchFilter *filter, bool reject)=0
std::function< int64_t(int64_t, int64_t)> evaluator_
virtual void EndFilterNeighbor(const LocalSearchOperator *op, bool neighbor_found)=0
std::string DebugString() const override
std::string DebugString() const override
bool SwapActiveAndInactive(int64_t active, int64_t inactive)
Replaces active by inactive in the current path, making active inactive.
virtual void Install()
Registers itself on the solver such that it gets notified of the search and propagation events.
An Assignment is a variable -> domains mapping, used to report solutions to the user.
int64_t *const delta_costs_
LocalSearchStatistics GetLocalSearchStatistics() const
Returns detailed local search statistics.
void CopyIntersection(const Assignment *assignment)
Copies the intersection of the two assignments to the current assignment.
bool MakeNeighbor() override
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
A BaseObject is the root of all reversibly allocated objects.
The class IntVar is a subset of IntExpr.
LocalSearchPhaseParameters(IntVar *objective, SolutionPool *const pool, LocalSearchOperator *ls_operator, DecisionBuilder *sub_decision_builder, RegularLimit *const limit, LocalSearchFilterManager *filter_manager)
#define DCHECK_GE(val1, val2)
~IntVarLocalSearchFilter() override
int number_of_base_nodes
Number of nodes needed to define a neighbor.
int Start(int path) const
virtual bool ConsiderAlternatives(int64_t base_index) const
Indicates if alternatives should be considered when iterating over base nodes.
SwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
#define CHECK_EQ(val1, val2)
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...
static const char kVarsArgument[]
Relocate: OROPT and RELOCATE.
Move is accepted when the current objective value >= objective.Min.
void AddObjective(IntVar *const v)
void BeginFilterNeighbor(const LocalSearchOperator *op) override
std::string DebugString() const override
void EndAcceptNeighbor(const LocalSearchOperator *op, bool neighbor_found) override
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...
std::function< int64_t(int64_t, int64_t)> IndexEvaluator2
DecisionBuilder * sub_decision_builder() const
MakeActiveAndRelocate(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
T * RevAlloc(T *object)
Registers the given object as being reversible.
void Copy(const SearchLimit *const limit) override
Copy a limit.
bool MakeNeighbor() override
ChangeValue(const std::vector< IntVar * > &vars)
int64_t solutions() const
bool MakeNeighbor() override
std::string DebugString() const override
#define DCHECK(condition)
Search * ActiveSearch() const
Returns the active search, nullptr outside search.
void ResetPosition()
Reset the position of the operator to its position when Start() was last called; this can be used to ...
Operator which replaces an active node by an inactive one.
virtual bool InitPosition() const
Returns true if the operator needs to restart its initial position at each call to Start()
std::vector< int > TravelingSalesmanPath()
Assignment * MakeAssignment()
This method creates an empty assignment.
Operator which reverses a sub-chain of a path.
int64_t OldNext(int64_t node) const
virtual bool RestartAtPathStartOnSynchronize()
When the operator is being synchronized with a new solution (when Start() is called),...
bool MakeNeighbor() override
virtual std::string DebugString() const
This class is used to manage a pool of solutions.
void Init() override
This method is called when the search limit is initialized.
int64_t Prev(int64_t node) const
Returns the node before node in the current delta.
Operator which defines one neighbor per variable.
bool MakeNeighbor() override
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
LocalSearchFilter * MakeAcceptFilter()
Local Search Filters.
#define DCHECK_EQ(val1, val2)
virtual const LocalSearchOperator * Self() const
PathLns(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, int number_of_chunks, int chunk_size, bool unactive_fragments)
void SetObjectiveValue(int64_t value)
void SetSearchContext(Search *search, const std::string &search_context)
std::string DebugString() const override
Cross(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
IntVar * objective() const
bool MakeOneNeighbor() override
This method should not be overridden. Override NextFragment() instead.
A Decision represents a choice point in the search tree.
int64_t Value(int index) const
Solver::LocalSearchFilterBound filter_enum_
std::string DebugString() const override
void set_num_filtered_neighbors(::PROTOBUF_NAMESPACE_ID::int64 value)
MakeActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
LocalSearchOperator * ConcatenateOperators(const std::vector< LocalSearchOperator * > &ops)
Creates a local search operator which concatenates a vector of operators.
#define DCHECK_LE(val1, val2)
bool IsLocalSearchProfilingEnabled() const
Returns whether we are profiling local search.
~FindOneNeighbor() override
~MakeActiveOperator() override
SharedBoundsManager * bounds
void BeginMakeNextNeighbor(const LocalSearchOperator *op) override
virtual bool HasFragments() const
std::string DebugString() const override
Collection of objects used to extend the Constraint Solver library.
bool Check() override
This method is called to check the status of the limit.
std::string DebugString() const override
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.
void set_total_num_filtered_neighbors(::PROTOBUF_NAMESPACE_ID::int64 value)
int64_t GetAcceptedObjectiveValue() const
virtual void BeginAcceptNeighbor(const LocalSearchOperator *op)=0
void InstallLocalSearchProfiler(LocalSearchProfiler *monitor)
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
Operator which makes an inactive node active and an active one inactive.
LinKernighan(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, const Solver::IndexEvaluator3 &evaluator, bool topt)
Operator which relaxes one entire path and all inactive nodes, thus defining num_paths neighbors.
virtual void Initialize(Assignment *const assignment)=0
This method is called to initialize the solution pool with the assignment from the local search.
bool MakeActive(int64_t node, int64_t destination)
Insert the inactive node after destination.
bool MakeNeighbor() override
RelocateAndMakeInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
LocalSearchFilterManager *const filter_manager() const
DecisionBuilder * MakeLocalSearchPhase(Assignment *const assignment, LocalSearchPhaseParameters *const parameters)
Local Search decision builders factories.
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.
void Start(const Assignment *assignment) override
EvaluatorLocalSearchOperators
This enum is used in Solver::MakeOperator associated with an evaluator to specify the neighborhood to...
LocalSearchOperator * MakeMoveTowardTargetOperator(const Assignment &target)
Creates a local search operator that tries to move the assignment of some variables toward a target.
virtual void EndMakeNextNeighbor(const LocalSearchOperator *op, bool neighbor_found, const Assignment *delta, const Assignment *deltadelta)=0
Decision * Next(Solver *const solver) override
This is the main method of the decision builder class.
DecisionBuilder * MakeRestoreAssignment(Assignment *assignment)
Returns a DecisionBuilder which restores an Assignment (calls void Assignment::Restore())
MakeInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
NodeRange Nodes(int path) const
LocalSearchFilterManager(std::vector< FilterEvent > filter_events)
void BeginAcceptNeighbor(const LocalSearchOperator *op) override
void BeginOperatorStart() override
Local search operator events.
Move is accepted when the current objective value is in the interval objective.Min .
void set_local_search_operator(ArgT0 &&arg0, ArgT... args)
std::string DebugString() const override
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...
void Install() override
Install itself on the solver.
LocalSearchOperator * RandomConcatenateOperators(const std::vector< LocalSearchOperator * > &ops)
Randomized version of local search concatenator; calls a random operator at each call to MakeNextNeig...
Specialization of LocalSearchOperator built from an array of IntVars which specifies the scope of the...
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 ...
LocalSearchProfiler(Solver *solver)
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)
LocalSearchFilter * MakePathStateFilter(Solver *solver, std::unique_ptr< PathState > path_state, const std::vector< IntVar * > &nexts)
::operations_research::LocalSearchStatistics_LocalSearchOperatorStatistics * add_local_search_operator_statistics()
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.
static const char kIntervalsArgument[]
~MakeActiveAndRelocate() override
virtual bool MakeOneNeighbor()
Creates a new neighbor.
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...
std::function< int(int64_t)> start_empty_path_class
Callback returning an index such that if c1 = start_empty_path_class(StartNode(p1)),...
const IntContainer & IntVarContainer() const
const int64_t & OldValue(int64_t index) const
Local Search Filters are used for fast neighbor pruning.
Operator which relaxes two sub-chains of three consecutive arcs each.
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)
bool MakeOneNeighbor() override
This method should not be overridden. Override ModifyValue() instead.
virtual void GetNextSolution(Assignment *const assignment)=0
This method is called when the local search starts a new neighborhood to initialize the default assig...
DecisionBuilder * Compose(DecisionBuilder *const db1, DecisionBuilder *const db2)
Creates a decision builder which sequentially composes decision builders.
virtual void InitFragments()
void set_total_num_accepted_neighbors(::PROTOBUF_NAMESPACE_ID::int64 value)
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...
std::string LocalSearchProfile() const
Returns local search profiling information in a human readable format.
virtual int64_t Max() const =0
Operator which inserts an inactive node into a path.
LocalSearchOperator * MakeOperator(const std::vector< IntVar * > &vars, LocalSearchOperators op)
Local Search Operators.
IntVarLocalSearchFilter * MakeSumObjectiveFilter(const std::vector< IntVar * > &vars, IndexEvaluator2 values, Solver::LocalSearchFilterBound filter_enum)
int64_t *const synchronized_costs_
#define DCHECK_LT(val1, val2)
IntVar * Objective() const
SolutionPool * MakeDefaultSolutionPool()
Solution Pool.
PathState(int num_nodes, std::vector< int > path_start, std::vector< int > path_end)
MakeChainInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
virtual bool SyncNeeded(Assignment *const local_assignment)=0
This method checks if the local solution needs to be updated with an external one.