24#include "absl/log/check.h"
38 std::unique_ptr<RoutingFilteredHeuristic> heuristic,
39 bool keep_inverse_values)
42 model_(heuristic->model()),
44 heuristic_(
std::move(heuristic)),
45 consider_vehicle_vars_(!
model_->CostsAreHomogeneousAcrossVehicles()) {
46 if (consider_vehicle_vars_) {
51bool FilteredHeuristicLocalSearchOperator::MakeOneNeighbor() {
53 if (
model_->CheckLimit()) {
62 if (MakeChangesAndInsertNodes()) {
69bool FilteredHeuristicLocalSearchOperator::MakeChangesAndInsertNodes() {
72 const std::function<int64_t(int64_t)> next_accessor =
74 if (next_accessor ==
nullptr) {
78 const Assignment*
const result_assignment =
79 heuristic_->BuildSolutionFromRoutes(next_accessor);
80 model_->solver()->set_context(
"");
82 if (result_assignment ==
nullptr) {
86 bool has_change =
false;
87 const std::vector<IntVarElement>& elements =
88 result_assignment->IntVarContainer().elements();
89 for (
int vehicle = 0; vehicle <
model_->vehicles(); vehicle++) {
90 int64_t node_index =
model_->Start(vehicle);
91 while (!
model_->IsEnd(node_index)) {
94 const IntVarElement& node_element = elements[node_index];
95 DCHECK_EQ(node_element.Var(),
model_->NextVar(node_index));
97 const int64_t new_node_value = node_element.Value();
98 DCHECK_NE(new_node_value, node_index);
100 const int64_t vehicle_var_index = VehicleVarIndex(node_index);
101 if (
OldValue(node_index) != new_node_value ||
102 (consider_vehicle_vars_ &&
OldValue(vehicle_var_index) != vehicle)) {
104 SetValue(node_index, new_node_value);
105 if (consider_vehicle_vars_) {
106 SetValue(vehicle_var_index, vehicle);
109 node_index = new_node_value;
115 const IntVarElement& node_element = elements[node];
116 DCHECK_EQ(node_element.Var(),
model_->NextVar(node));
117 if (node_element.Value() == node) {
121 if (consider_vehicle_vars_) {
122 const int64_t vehicle_var_index = VehicleVarIndex(node);
123 DCHECK_NE(
OldValue(vehicle_var_index), -1);
134 std::unique_ptr<RoutingFilteredHeuristic> heuristic)
136 num_routes_(
model_->vehicles()),
139 just_started_(false) {}
141void FilteredHeuristicPathLNSOperator::OnStart() {
144 last_route_ = current_route_;
145 if (RouteIsEmpty(current_route_)) {
146 current_route_ = GetFirstNonEmptyRouteAfterCurrentRoute();
148 just_started_ =
true;
151bool FilteredHeuristicPathLNSOperator::IncrementPosition() {
153 just_started_ =
false;
157 return !RouteIsEmpty(current_route_) &&
158 GetFirstNonEmptyRouteAfterCurrentRoute() != last_route_;
160 current_route_ = GetFirstNonEmptyRouteAfterCurrentRoute();
161 return current_route_ != last_route_;
164bool FilteredHeuristicPathLNSOperator::RouteIsEmpty(
int route)
const {
168int FilteredHeuristicPathLNSOperator::GetFirstNonEmptyRouteAfterCurrentRoute()
170 int route = GetNextRoute(current_route_);
171 while (route != last_route_ && RouteIsEmpty(route)) {
172 route = GetNextRoute(route);
177std::function<int64_t(int64_t)>
178FilteredHeuristicPathLNSOperator::SetupNextAccessorForNeighbor() {
179 const int64_t start_node =
model_->Start(current_route_);
180 const int64_t end_node =
model_->End(current_route_);
182 int64_t node =
Value(start_node);
183 while (node != end_node) {
188 return [
this, start_node, end_node](int64_t node) {
189 if (node == start_node)
return end_node;
198 std::unique_ptr<RoutingFilteredHeuristic> heuristic)
200 route_to_relocate_index_(0),
201 empty_route_index_(0),
202 just_started_(false) {}
204void RelocatePathAndHeuristicInsertUnperformedOperator::OnStart() {
205 has_unperformed_nodes_ =
false;
206 last_node_on_route_.resize(
model_->vehicles());
207 routes_to_relocate_.clear();
208 empty_routes_.clear();
209 std::vector<bool> empty_vehicle_of_vehicle_class_added(
210 model_->GetVehicleClassesCount(),
false);
211 for (int64_t node = 0; node <
model_->Size(); node++) {
212 const int64_t next =
OldValue(node);
214 has_unperformed_nodes_ =
true;
217 if (
model_->IsEnd(next)) {
218 last_node_on_route_[
model_->VehicleIndex(next)] = node;
222 for (
int vehicle = 0; vehicle <
model_->vehicles(); vehicle++) {
224 if (!
model_->IsEnd(next)) {
225 routes_to_relocate_.push_back(vehicle);
228 const int vehicle_class =
229 model_->GetVehicleClassIndexOfVehicle(vehicle).value();
230 if (!empty_vehicle_of_vehicle_class_added[vehicle_class]) {
231 empty_routes_.push_back(vehicle);
232 empty_vehicle_of_vehicle_class_added[vehicle_class] =
true;
236 if (empty_route_index_ >= empty_routes_.size()) {
237 empty_route_index_ = 0;
239 if (route_to_relocate_index_ >= routes_to_relocate_.size()) {
240 route_to_relocate_index_ = 0;
242 last_empty_route_index_ = empty_route_index_;
243 last_route_to_relocate_index_ = route_to_relocate_index_;
245 just_started_ =
true;
248bool RelocatePathAndHeuristicInsertUnperformedOperator::IncrementPosition() {
249 if (!has_unperformed_nodes_ || empty_routes_.empty() ||
250 routes_to_relocate_.empty()) {
254 just_started_ =
false;
257 return IncrementRoutes();
260bool RelocatePathAndHeuristicInsertUnperformedOperator::IncrementRoutes() {
261 ++empty_route_index_ %= empty_routes_.size();
262 if (empty_route_index_ != last_empty_route_index_) {
265 ++route_to_relocate_index_ %= routes_to_relocate_.size();
266 return route_to_relocate_index_ != last_route_to_relocate_index_;
269std::function<int64_t(int64_t)>
270RelocatePathAndHeuristicInsertUnperformedOperator::
271 SetupNextAccessorForNeighbor() {
272 const int empty_route = empty_routes_[empty_route_index_];
273 const int relocated_route = routes_to_relocate_[route_to_relocate_index_];
274 if (
model_->GetVehicleClassIndexOfVehicle(empty_route) ==
275 model_->GetVehicleClassIndexOfVehicle(relocated_route)) {
280 const int64_t empty_start_node =
model_->Start(empty_route);
281 const int64_t empty_end_node =
model_->End(empty_route);
283 const int64_t relocated_route_start =
model_->Start(relocated_route);
284 const int64_t first_relocated_node =
OldValue(relocated_route_start);
285 const int64_t last_relocated_node = last_node_on_route_[relocated_route];
286 const int64_t relocated_route_end =
model_->End(relocated_route);
288 return [
this, empty_start_node, empty_end_node, first_relocated_node,
289 last_relocated_node, relocated_route_start,
290 relocated_route_end](int64_t node) {
291 if (node == relocated_route_start)
return relocated_route_end;
292 if (node == empty_start_node)
return first_relocated_node;
293 if (node == last_relocated_node)
return empty_end_node;
301 std::unique_ptr<RoutingFilteredHeuristic> heuristic,
int num_close_nodes)
304 pickup_delivery_pairs_(
model_->GetPickupAndDeliveryPairs()),
307 just_started_(false),
310 num_close_nodes_(num_close_nodes),
316void FilteredHeuristicCloseNodesLNSOperator::Initialize() {
317 if (initialized_)
return;
319 const int64_t size =
model_->Size();
320 const int64_t max_num_neighbors =
321 std::max<int64_t>(0, size - 1 -
model_->vehicles());
322 const int64_t num_closest_neighbors =
323 std::min<int64_t>(num_close_nodes_, max_num_neighbors);
324 DCHECK_GE(num_closest_neighbors, 0);
326 if (num_closest_neighbors == 0)
return;
328 const int64_t num_cost_classes =
model_->GetCostClassesCount();
330 for (int64_t node = 0; node < size; node++) {
331 if (
model_->IsStart(node) ||
model_->IsEnd(node))
continue;
333 std::vector<std::pair< double, int64_t>>
335 costed_after_nodes.reserve(size);
336 for (int64_t after_node = 0; after_node < size; after_node++) {
337 if (
model_->IsStart(after_node) ||
model_->IsEnd(after_node) ||
338 after_node == node) {
341 double total_cost = 0.0;
344 for (
int cost_class = 1; cost_class < num_cost_classes; cost_class++) {
345 total_cost +=
model_->GetArcCostForClass(node, after_node, cost_class);
347 costed_after_nodes.emplace_back(total_cost, after_node);
350 std::nth_element(costed_after_nodes.begin(),
351 costed_after_nodes.begin() + num_closest_neighbors - 1,
352 costed_after_nodes.end());
353 std::vector<int64_t>& neighbors = close_nodes_[node];
354 neighbors.reserve(num_closest_neighbors);
355 for (
int index = 0; index < num_closest_neighbors; index++) {
356 neighbors.push_back(costed_after_nodes[index].second);
361void FilteredHeuristicCloseNodesLNSOperator::OnStart() {
363 last_node_ = current_node_;
364 just_started_ =
true;
367bool FilteredHeuristicCloseNodesLNSOperator::IncrementPosition() {
368 DCHECK(initialized_);
370 just_started_ =
false;
373 ++current_node_ %=
model_->Size();
374 return current_node_ != last_node_;
377void FilteredHeuristicCloseNodesLNSOperator::RemoveNode(int64_t node) {
379 DCHECK_NE(
Value(node), node);
380 DCHECK(IsActive(node));
383 const int64_t prev = Prev(node);
384 const int64_t next =
Next(node);
385 changed_nexts_.Set(prev);
386 new_nexts_[prev] = next;
387 if (next < model_->
Size()) {
388 changed_prevs_.Set(next);
389 new_prevs_[next] = prev;
393void FilteredHeuristicCloseNodesLNSOperator::RemoveNodeAndActiveSibling(
395 if (!IsActive(node))
return;
398 if (
const std::optional<int64_t> sibling_node =
399 model_->GetFirstMatchingPickupDeliverySibling(
401 [
this](int64_t node) {
402 return IsActive(node) && !model_->IsStart(node) &&
403 !model_->IsEnd(node);
405 sibling_node.has_value()) {
406 RemoveNode(sibling_node.value());
410std::function<int64_t(int64_t)>
411FilteredHeuristicCloseNodesLNSOperator::SetupNextAccessorForNeighbor() {
412 DCHECK(initialized_);
413 if (
model_->IsStart(current_node_)) {
416 DCHECK(!
model_->IsEnd(current_node_));
418 changed_nexts_.SparseClearAll();
419 changed_prevs_.SparseClearAll();
421 RemoveNodeAndActiveSibling(current_node_);
423 for (int64_t neighbor : close_nodes_[current_node_]) {
424 RemoveNodeAndActiveSibling(neighbor);
427 return [
this](int64_t node) {
return Next(node); };
434 std::unique_ptr<RoutingFilteredHeuristic> heuristic,
435 int num_arcs_to_consider,
436 std::function<int64_t(int64_t, int64_t, int64_t)>
437 arc_cost_for_route_start)
441 num_arcs_to_consider_(num_arcs_to_consider),
442 current_expensive_arc_indices_({-1, -1}),
443 arc_cost_for_route_start_(std::move(arc_cost_for_route_start)),
444 just_started_(
false) {
445 DCHECK_GE(num_arcs_to_consider_, 2);
448void FilteredHeuristicExpensiveChainLNSOperator::OnStart() {
449 last_route_ = current_route_;
450 just_started_ =
true;
453bool FilteredHeuristicExpensiveChainLNSOperator::IncrementPosition() {
455 just_started_ =
false;
456 return FindMostExpensiveChainsOnRemainingRoutes();
459 if (IncrementCurrentArcIndices())
return true;
461 return IncrementRoute() && FindMostExpensiveChainsOnRemainingRoutes();
464std::function<int64_t(int64_t)>
465FilteredHeuristicExpensiveChainLNSOperator::SetupNextAccessorForNeighbor() {
466 const int first_arc_index = current_expensive_arc_indices_.first;
467 const int second_arc_index = current_expensive_arc_indices_.second;
468 DCHECK_LE(0, first_arc_index);
469 DCHECK_LT(first_arc_index, second_arc_index);
470 DCHECK_LT(second_arc_index, most_expensive_arc_starts_and_ranks_.size());
472 const std::pair<int, int>& first_start_and_rank =
473 most_expensive_arc_starts_and_ranks_[first_arc_index];
474 const std::pair<int, int>& second_start_and_rank =
475 most_expensive_arc_starts_and_ranks_[second_arc_index];
476 int64_t before_chain, after_chain;
477 if (first_start_and_rank.second < second_start_and_rank.second) {
478 before_chain = first_start_and_rank.first;
479 after_chain =
OldValue(second_start_and_rank.first);
481 before_chain = second_start_and_rank.first;
482 after_chain =
OldValue(first_start_and_rank.first);
485 int node =
Value(before_chain);
486 while (node != after_chain) {
491 return [
this, before_chain, after_chain](int64_t node) {
492 if (node == before_chain)
return after_chain;
497bool FilteredHeuristicExpensiveChainLNSOperator::IncrementRoute() {
498 ++current_route_ %=
model_->vehicles();
499 return current_route_ != last_route_;
502bool FilteredHeuristicExpensiveChainLNSOperator::IncrementCurrentArcIndices() {
503 int& second_index = current_expensive_arc_indices_.second;
504 if (++second_index < most_expensive_arc_starts_and_ranks_.size()) {
507 int& first_index = current_expensive_arc_indices_.first;
508 if (first_index + 2 < most_expensive_arc_starts_and_ranks_.size()) {
510 second_index = first_index + 1;
516bool FilteredHeuristicExpensiveChainLNSOperator::
517 FindMostExpensiveChainsOnRemainingRoutes() {
520 num_arcs_to_consider_,
model_->Start(current_route_),
521 [
this](int64_t i) { return OldValue(i); },
522 [
this](int64_t node) { return model_->IsEnd(node); },
523 arc_cost_for_route_start_, &most_expensive_arc_starts_and_ranks_,
524 ¤t_expensive_arc_indices_)) {
527 }
while (IncrementRoute());
virtual std::string DebugString() const
FilteredHeuristicCloseNodesLNSOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic, int num_close_nodes)
FilteredHeuristicCloseNodesLNSOperator.
FilteredHeuristicExpensiveChainLNSOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic, int num_arcs_to_consider, std::function< int64_t(int64_t, int64_t, int64_t)> arc_cost_for_route_start)
FilteredHeuristicExpensiveChainLNSOperator.
RoutingModel *const model_
SparseBitset removed_nodes_
Keeps track of removed nodes when making a neighbor.
virtual std::function< int64_t(int64_t)> SetupNextAccessorForNeighbor()=0
virtual bool IncrementPosition()=0
FilteredHeuristicLocalSearchOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic, bool keep_inverse_values=false)
FilteredHeuristicLocalSearchOperator.
FilteredHeuristicPathLNSOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic)
FilteredHeuristicPathLNSOperator.
void SetValue(int64_t index, int64_t value)
void AddVars(const std::vector< IntVar * > &vars)
int64_t Value(int64_t index) const
IntVarLocalSearchOperator(const std::vector< IntVar * > &vars, bool keep_inverse_values=false)
int64_t OldValue(int64_t index) const
In SWIG mode, we don't want anything besides these top-level includes.
bool FindMostExpensiveArcsOnRoute(int num_arcs, int64_t start, const std::function< int64_t(int64_t)> &next_accessor, const std::function< bool(int64_t)> &is_end, const std::function< int64_t(int64_t, int64_t, int64_t)> &arc_cost_for_route_start, std::vector< std::pair< int64_t, int > > *most_expensive_arc_starts_and_ranks, std::pair< int, int > *first_expensive_arc_indices)