24#include "absl/container/flat_hash_set.h"
25#include "absl/log/check.h"
39 std::unique_ptr<RoutingFilteredHeuristic> heuristic,
40 bool keep_inverse_values)
43 model_(heuristic->model()),
45 heuristic_(
std::move(heuristic)),
46 consider_vehicle_vars_(!
model_->CostsAreHomogeneousAcrossVehicles()) {
47 if (consider_vehicle_vars_) {
63 if (MakeChangesAndInsertNodes()) {
70bool FilteredHeuristicLocalSearchOperator::MakeChangesAndInsertNodes() {
76 const std::function<int64_t(int64_t)> next_accessor =
78 if (next_accessor ==
nullptr) {
82 const Assignment*
const result_assignment =
83 heuristic_->BuildSolutionFromRoutes(next_accessor);
84 model_->solver()->set_context(
"");
86 if (result_assignment ==
nullptr) {
90 bool has_change =
false;
91 const std::vector<IntVarElement>& elements =
92 result_assignment->IntVarContainer().elements();
93 for (
int vehicle = 0; vehicle <
model_->vehicles(); vehicle++) {
94 int64_t node_index =
model_->Start(vehicle);
95 while (!
model_->IsEnd(node_index)) {
98 const IntVarElement& node_element = elements[node_index];
99 DCHECK_EQ(node_element.Var(),
model_->NextVar(node_index));
101 const int64_t new_node_value = node_element.Value();
102 DCHECK_NE(new_node_value, node_index);
104 const int64_t vehicle_var_index = VehicleVarIndex(node_index);
105 if (
OldValue(node_index) != new_node_value ||
106 (consider_vehicle_vars_ &&
OldValue(vehicle_var_index) != vehicle)) {
108 SetValue(node_index, new_node_value);
109 if (consider_vehicle_vars_) {
110 SetValue(vehicle_var_index, vehicle);
113 node_index = new_node_value;
119 const IntVarElement& node_element = elements[node];
120 DCHECK_EQ(node_element.Var(),
model_->NextVar(node));
121 if (node_element.Value() == node) {
125 if (consider_vehicle_vars_) {
126 const int64_t vehicle_var_index = VehicleVarIndex(node);
127 DCHECK_NE(
OldValue(vehicle_var_index), -1);
138 std::unique_ptr<RoutingFilteredHeuristic> heuristic)
140 num_routes_(
model_->vehicles()),
143 just_started_(false) {}
148 last_route_ = current_route_;
149 if (RouteIsEmpty(current_route_)) {
150 current_route_ = GetFirstNonEmptyRouteAfterCurrentRoute();
152 just_started_ =
true;
157 just_started_ =
false;
161 return !RouteIsEmpty(current_route_) &&
162 GetFirstNonEmptyRouteAfterCurrentRoute() != last_route_;
164 current_route_ = GetFirstNonEmptyRouteAfterCurrentRoute();
165 return current_route_ != last_route_;
168bool FilteredHeuristicPathLNSOperator::RouteIsEmpty(
int route)
const {
172int FilteredHeuristicPathLNSOperator::GetFirstNonEmptyRouteAfterCurrentRoute()
174 int route = GetNextRoute(current_route_);
175 while (route != last_route_ && RouteIsEmpty(route)) {
176 route = GetNextRoute(route);
181std::function<int64_t(int64_t)>
183 const int64_t start_node =
model_->Start(current_route_);
184 const int64_t end_node =
model_->End(current_route_);
186 int64_t node =
Value(start_node);
187 while (node != end_node) {
192 return [
this, start_node, end_node](int64_t node) {
193 if (node == start_node)
return end_node;
202 std::unique_ptr<RoutingFilteredHeuristic> heuristic)
204 route_to_relocate_index_(0),
205 empty_route_index_(0),
206 just_started_(false) {}
209 has_unperformed_nodes_ =
false;
211 routes_to_relocate_.clear();
212 empty_routes_.clear();
213 std::vector<bool> empty_vehicle_of_vehicle_class_added(
215 for (int64_t node = 0; node <
model_->
Size(); node++) {
216 const int64_t next =
OldValue(node);
218 has_unperformed_nodes_ =
true;
226 for (
int vehicle = 0; vehicle <
model_->vehicles(); vehicle++) {
228 if (!
model_->IsEnd(next)) {
229 routes_to_relocate_.push_back(vehicle);
232 const int vehicle_class =
233 model_->GetVehicleClassIndexOfVehicle(vehicle).value();
234 if (!empty_vehicle_of_vehicle_class_added[vehicle_class]) {
235 empty_routes_.push_back(vehicle);
236 empty_vehicle_of_vehicle_class_added[vehicle_class] =
true;
240 if (empty_route_index_ >= empty_routes_.size()) {
241 empty_route_index_ = 0;
243 if (route_to_relocate_index_ >= routes_to_relocate_.size()) {
244 route_to_relocate_index_ = 0;
246 last_empty_route_index_ = empty_route_index_;
247 last_route_to_relocate_index_ = route_to_relocate_index_;
249 just_started_ =
true;
253 if (!has_unperformed_nodes_ || empty_routes_.empty() ||
254 routes_to_relocate_.empty()) {
258 just_started_ =
false;
261 return IncrementRoutes();
264bool RelocatePathAndHeuristicInsertUnperformedOperator::IncrementRoutes() {
265 ++empty_route_index_ %= empty_routes_.size();
266 if (empty_route_index_ != last_empty_route_index_) {
269 ++route_to_relocate_index_ %= routes_to_relocate_.size();
270 return route_to_relocate_index_ != last_route_to_relocate_index_;
273std::function<int64_t(int64_t)>
276 const int empty_route = empty_routes_[empty_route_index_];
277 const int relocated_route = routes_to_relocate_[route_to_relocate_index_];
278 if (
model_->GetVehicleClassIndexOfVehicle(empty_route) ==
279 model_->GetVehicleClassIndexOfVehicle(relocated_route)) {
284 const int64_t empty_start_node =
model_->Start(empty_route);
285 const int64_t empty_end_node =
model_->End(empty_route);
287 const int64_t relocated_route_start =
model_->Start(relocated_route);
288 const int64_t first_relocated_node =
OldValue(relocated_route_start);
289 const int64_t last_relocated_node = last_node_on_route_[relocated_route];
290 const int64_t relocated_route_end =
model_->End(relocated_route);
292 return [
this, empty_start_node, empty_end_node, first_relocated_node,
293 last_relocated_node, relocated_route_start,
294 relocated_route_end](int64_t node) {
295 if (node == relocated_route_start)
return relocated_route_end;
296 if (node == empty_start_node)
return first_relocated_node;
297 if (node == last_relocated_node)
return empty_end_node;
305 std::unique_ptr<RoutingFilteredHeuristic> heuristic,
int num_close_nodes)
308 pickup_delivery_pairs_(
model_->GetPickupAndDeliveryPairs()),
311 just_started_(false),
314 num_close_nodes_(num_close_nodes),
320void FilteredHeuristicCloseNodesLNSOperator::Initialize() {
321 if (initialized_)
return;
324 const int64_t max_num_neighbors =
326 const int64_t num_closest_neighbors =
327 std::min<int64_t>(num_close_nodes_, max_num_neighbors);
328 DCHECK_GE(num_closest_neighbors, 0);
330 if (num_closest_neighbors == 0)
return;
334 for (int64_t node = 0; node < size; node++) {
337 std::vector<std::pair< double, int64_t>>
339 costed_after_nodes.reserve(size);
340 for (int64_t after_node = 0; after_node < size; after_node++) {
342 after_node == node) {
345 double total_cost = 0.0;
348 for (
int cost_class = 1; cost_class < num_cost_classes; cost_class++) {
351 costed_after_nodes.emplace_back(total_cost, after_node);
354 std::nth_element(costed_after_nodes.begin(),
355 costed_after_nodes.begin() + num_closest_neighbors - 1,
356 costed_after_nodes.end());
357 std::vector<int64_t>& neighbors = close_nodes_[node];
358 neighbors.reserve(num_closest_neighbors);
359 for (
int index = 0; index < num_closest_neighbors; index++) {
360 neighbors.push_back(costed_after_nodes[index].second);
367 last_node_ = current_node_;
368 just_started_ =
true;
372 DCHECK(initialized_);
374 just_started_ =
false;
377 ++current_node_ %=
model_->Size();
378 return current_node_ != last_node_;
381void FilteredHeuristicCloseNodesLNSOperator::RemoveNode(int64_t node) {
383 DCHECK_NE(
Value(node), node);
384 DCHECK(IsActive(node));
387 const int64_t prev = Prev(node);
388 const int64_t next =
Next(node);
389 changed_nexts_.Set(prev);
390 new_nexts_[prev] = next;
391 if (next < model_->
Size()) {
392 changed_prevs_.Set(next);
393 new_prevs_[next] = prev;
397void FilteredHeuristicCloseNodesLNSOperator::RemoveNodeAndActiveSibling(
399 if (!IsActive(node))
return;
402 if (
const std::optional<int64_t> sibling_node =
403 model_->GetFirstMatchingPickupDeliverySibling(
405 [
this](int64_t node) {
406 return IsActive(node) && !model_->IsStart(node) &&
407 !model_->IsEnd(node);
409 sibling_node.has_value()) {
410 RemoveNode(sibling_node.value());
414std::function<int64_t(int64_t)>
416 DCHECK(initialized_);
417 if (
model_->IsStart(current_node_)) {
420 DCHECK(!
model_->IsEnd(current_node_));
422 changed_nexts_.ResetAllToFalse();
423 changed_prevs_.ResetAllToFalse();
425 RemoveNodeAndActiveSibling(current_node_);
427 for (int64_t neighbor : close_nodes_[current_node_]) {
428 RemoveNodeAndActiveSibling(neighbor);
431 return [
this](int64_t node) {
return Next(node); };
438 std::unique_ptr<RoutingFilteredHeuristic> heuristic,
439 int num_arcs_to_consider,
440 std::function<int64_t(int64_t, int64_t, int64_t)>
441 arc_cost_for_route_start)
445 num_arcs_to_consider_(num_arcs_to_consider),
446 current_expensive_arc_indices_({-1, -1}),
447 arc_cost_for_route_start_(std::move(arc_cost_for_route_start)),
448 just_started_(
false) {
449 DCHECK_GE(num_arcs_to_consider_, 2);
453 last_route_ = current_route_;
454 just_started_ =
true;
459 just_started_ =
false;
460 return FindMostExpensiveChainsOnRemainingRoutes();
463 if (IncrementCurrentArcIndices())
return true;
465 return IncrementRoute() && FindMostExpensiveChainsOnRemainingRoutes();
468std::function<int64_t(int64_t)>
470 const int first_arc_index = current_expensive_arc_indices_.first;
471 const int second_arc_index = current_expensive_arc_indices_.second;
472 DCHECK_LE(0, first_arc_index);
473 DCHECK_LT(first_arc_index, second_arc_index);
474 DCHECK_LT(second_arc_index, most_expensive_arc_starts_and_ranks_.size());
476 const std::pair<int, int>& first_start_and_rank =
477 most_expensive_arc_starts_and_ranks_[first_arc_index];
478 const std::pair<int, int>& second_start_and_rank =
479 most_expensive_arc_starts_and_ranks_[second_arc_index];
480 int64_t before_chain, after_chain;
481 if (first_start_and_rank.second < second_start_and_rank.second) {
482 before_chain = first_start_and_rank.first;
483 after_chain =
OldValue(second_start_and_rank.first);
485 before_chain = second_start_and_rank.first;
486 after_chain =
OldValue(first_start_and_rank.first);
489 int node =
Value(before_chain);
490 while (node != after_chain) {
495 return [
this, before_chain, after_chain](int64_t node) {
496 if (node == before_chain)
return after_chain;
501bool FilteredHeuristicExpensiveChainLNSOperator::IncrementRoute() {
502 ++current_route_ %=
model_->vehicles();
503 return current_route_ != last_route_;
506bool FilteredHeuristicExpensiveChainLNSOperator::IncrementCurrentArcIndices() {
507 int& second_index = current_expensive_arc_indices_.second;
508 if (++second_index < most_expensive_arc_starts_and_ranks_.size()) {
511 int& first_index = current_expensive_arc_indices_.first;
512 if (first_index + 2 < most_expensive_arc_starts_and_ranks_.size()) {
514 second_index = first_index + 1;
520bool FilteredHeuristicExpensiveChainLNSOperator::
521 FindMostExpensiveChainsOnRemainingRoutes() {
524 num_arcs_to_consider_,
model_->Start(current_route_),
525 [
this](int64_t i) { return OldValue(i); },
526 [
this](int64_t node) { return model_->IsEnd(node); },
527 arc_cost_for_route_start_, &most_expensive_arc_starts_and_ranks_,
528 ¤t_expensive_arc_indices_)) {
531 }
while (IncrementRoute());
538 std::unique_ptr<RoutingFilteredHeuristic> heuristic)
541 current_visit_type_component_index_(0),
542 last_visit_type_component_index_(0),
543 empty_route_index_(0),
548 just_started_(false) {}
551 const std::vector<std::vector<int>>& visit_type_components =
553 while (current_visit_type_component_index_ < visit_type_components.size() &&
554 visit_type_components[current_visit_type_component_index_].empty()) {
555 ++current_visit_type_component_index_;
557 last_visit_type_component_index_ = current_visit_type_component_index_;
558 empty_routes_.clear();
559 std::vector<bool> empty_vehicle_of_vehicle_class_added(
565 const int vehicle_class =
566 model_->GetVehicleClassIndexOfVehicle(vehicle).value();
567 if (!empty_vehicle_of_vehicle_class_added[vehicle_class]) {
568 empty_routes_.push_back(vehicle);
569 empty_vehicle_of_vehicle_class_added[vehicle_class] =
true;
572 if (empty_route_index_ >= empty_routes_.size()) {
573 empty_route_index_ = 0;
575 last_empty_route_index_ = empty_route_index_;
576 just_started_ =
true;
580 const std::vector<std::vector<int>>& visit_type_components =
581 model_->GetVisitTypeComponents();
582 if (visit_type_components.empty() || empty_routes_.empty())
return false;
584 just_started_ =
false;
587 ++empty_route_index_ %= empty_routes_.size();
588 if (empty_route_index_ != last_empty_route_index_)
return true;
590 ++current_visit_type_component_index_ %= visit_type_components.size();
591 }
while (current_visit_type_component_index_ !=
592 last_visit_type_component_index_ &&
593 visit_type_components[current_visit_type_component_index_].empty());
594 return current_visit_type_component_index_ !=
595 last_visit_type_component_index_;
598void RelocateVisitTypeOperator::RemoveNode(int64_t node) {
603 changed_prevs_[node] ? new_prevs_[node] :
InverseValue(node);
604 const int64_t next = changed_nexts_[node] ? new_nexts_[node] :
Value(node);
605 changed_nexts_.Set(prev);
606 new_nexts_[prev] = next;
607 if (next < model_->
Size()) {
608 changed_prevs_.Set(next);
609 new_prevs_[next] = prev;
613std::function<int64_t(int64_t)>
615 changed_nexts_.ResetAllToFalse();
616 changed_prevs_.ResetAllToFalse();
617 const std::vector<std::vector<int>>& visit_type_components =
618 model_->GetVisitTypeComponents();
619 if (visit_type_components.empty() ||
620 visit_type_components[current_visit_type_component_index_].empty()) {
624 absl::flat_hash_set<int64_t> visited_types;
625 const std::vector<PickupDeliveryPair>& pairs =
626 model_->GetPickupAndDeliveryPairs();
627 for (
const int type :
628 visit_type_components[current_visit_type_component_index_]) {
629 visited_types.insert(type);
630 for (int64_t pair_index :
model_->GetPairIndicesOfType(type)) {
631 const PickupDeliveryPair& pair = pairs[pair_index];
632 for (int64_t pickup : pair.pickup_alternatives) RemoveNode(pickup);
633 for (int64_t delivery : pair.delivery_alternatives) RemoveNode(delivery);
635 for (int64_t node :
model_->GetSingleNodesOfType(type)) RemoveNode(node);
642 const int empty_route = empty_routes_[empty_route_index_];
643 const int64_t empty_start_node =
model_->Start(empty_route);
644 const int64_t empty_end_node =
model_->End(empty_route);
645 for (
const std::vector<int>& sorted_types :
646 model_->GetTopologicallySortedVisitTypes()) {
647 for (
const int type : sorted_types) {
648 if (!visited_types.contains(type))
continue;
649 const std::vector<int>& pair_indices =
model_->GetPairIndicesOfType(type);
650 if (!pair_indices.empty()) {
651 const int pair_index = pair_indices.front();
654 const int64_t pickup = pairs[pair_index].pickup_alternatives[0];
655 const int64_t delivery = pairs[pair_index].delivery_alternatives[0];
656 return [
this, empty_start_node, empty_end_node, pickup,
657 delivery](int64_t node) {
658 if (node == empty_start_node)
return pickup;
659 if (node == pickup)
return delivery;
660 if (node == delivery)
return empty_end_node;
661 return changed_nexts_[node] ? new_nexts_[node] :
OldValue(node);
664 const std::vector<int>& single_nodes =
model_->GetSingleNodesOfType(type);
665 if (!single_nodes.empty()) {
666 const int64_t single_node = single_nodes.front();
667 return [
this, empty_start_node, empty_end_node,
668 single_node](int64_t node) {
669 if (node == empty_start_node)
return single_node;
670 if (node == single_node)
return empty_end_node;
671 return changed_nexts_[node] ? new_nexts_[node] :
OldValue(node);
virtual std::string DebugString() const
FilteredHeuristicCloseNodesLNSOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic, int num_close_nodes)
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)
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)
FilteredHeuristicPathLNSOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic)
void SetValue(int64_t index, int64_t value)
void AddVars(const std::vector< IntVar * > &vars)
int64_t Value(int64_t index) const
int64_t InverseValue(int64_t index) const
IntVarLocalSearchOperator(const std::vector< IntVar * > &vars, bool keep_inverse_values=false)
int64_t OldValue(int64_t index) const
virtual bool MakeOneNeighbor()
MakeNextNeighbor() in a subclass of IntVarLocalSearchOperator.
RelocateVisitTypeOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic)
int64_t Size() const
Returns the number of next variables in the model.
int64_t Start(int vehicle) const
int64_t GetArcCostForClass(int64_t from_index, int64_t to_index, int64_t cost_class_index) const
int GetVehicleClassesCount() const
Returns the number of different vehicle classes in the model.
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
bool IsStart(int64_t index) const
Returns true if 'index' represents the first node of a route.
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
int vehicles() const
Returns the number of vehicle routes in the model.
bool CheckLimit(absl::Duration offset=absl::ZeroDuration())
int VehicleIndex(int64_t index) const
const std::vector< std::vector< int > > & GetVisitTypeComponents() const
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)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...