Files
ortools-clone/ortools/constraint_solver/routing_insertion_lns.h
2025-02-10 15:53:25 +01:00

253 lines
8.7 KiB
C++

// Copyright 2010-2025 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_INSERTION_LNS_H_
#define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_INSERTION_LNS_H_
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/log/check.h"
#include "absl/strings/str_cat.h"
#include "ortools/constraint_solver/constraint_solver.h"
#include "ortools/constraint_solver/constraint_solveri.h"
#include "ortools/constraint_solver/routing.h"
#include "ortools/constraint_solver/routing_search.h"
#include "ortools/constraint_solver/routing_types.h"
#include "ortools/util/bitset.h"
namespace operations_research {
/// Class of operators using a RoutingFilteredHeuristic to insert unperformed
/// nodes after changes have been made to the current solution.
// TODO(user): Put these methods in an object with helper methods instead
// of adding a layer to the class hierarchy.
class FilteredHeuristicLocalSearchOperator : public IntVarLocalSearchOperator {
public:
explicit FilteredHeuristicLocalSearchOperator(
std::unique_ptr<RoutingFilteredHeuristic> heuristic,
bool keep_inverse_values = false);
~FilteredHeuristicLocalSearchOperator() override {}
protected:
virtual bool IncrementPosition() = 0;
/// Virtual method to return the next_accessor to be passed to the heuristic
/// to build a new solution. This method should also correctly set the
/// nodes being removed (if any) in removed_nodes_.
virtual std::function<int64_t(int64_t)> SetupNextAccessorForNeighbor() = 0;
std::string HeuristicName() const {
std::string heuristic_name = heuristic_->DebugString();
const int erase_pos = heuristic_name.find("FilteredHeuristic");
if (erase_pos != std::string::npos) {
const int expected_name_size = heuristic_name.size() - 17;
heuristic_name.erase(erase_pos);
// NOTE: Verify that the "FilteredHeuristic" string was at the end of the
// heuristic name.
DCHECK_EQ(heuristic_name.size(), expected_name_size);
}
return heuristic_name;
}
// TODO(user): Remove the dependency from RoutingModel by storing an
// IntVarFilteredHeuristic here instead and storing information on path
// start/ends like PathOperator does (instead of relying on the model).
RoutingModel* const model_;
/// Keeps track of removed nodes when making a neighbor.
SparseBitset<> removed_nodes_;
private:
bool MakeOneNeighbor() override;
bool MakeChangesAndInsertNodes();
int64_t VehicleVarIndex(int64_t node) const { return model_->Size() + node; }
const std::unique_ptr<RoutingFilteredHeuristic> heuristic_;
const bool consider_vehicle_vars_;
};
/// LNS-like operator based on a filtered first solution heuristic to rebuild
/// the solution, after the destruction phase consisting of removing one route.
class FilteredHeuristicPathLNSOperator
: public FilteredHeuristicLocalSearchOperator {
public:
explicit FilteredHeuristicPathLNSOperator(
std::unique_ptr<RoutingFilteredHeuristic> heuristic);
~FilteredHeuristicPathLNSOperator() override {}
std::string DebugString() const override {
return absl::StrCat("HeuristicPathLNS(", HeuristicName(), ")");
}
private:
void OnStart() override;
bool IncrementPosition() override;
bool RouteIsEmpty(int route) const;
int GetNextRoute(int route) const {
return route + 1 < num_routes_ ? route + 1 : 0;
}
int GetFirstNonEmptyRouteAfterCurrentRoute() const;
std::function<int64_t(int64_t)> SetupNextAccessorForNeighbor() override;
const int num_routes_;
int current_route_;
int last_route_;
bool just_started_;
};
/// Heuristic-based local search operator which relocates an entire route to
/// an empty vehicle of different vehicle class and then tries to insert
/// unperformed nodes using the heuristic.
class RelocatePathAndHeuristicInsertUnperformedOperator
: public FilteredHeuristicLocalSearchOperator {
public:
explicit RelocatePathAndHeuristicInsertUnperformedOperator(
std::unique_ptr<RoutingFilteredHeuristic> heuristic);
~RelocatePathAndHeuristicInsertUnperformedOperator() override {}
std::string DebugString() const override {
return absl::StrCat("RelocatePathAndHeuristicInsertUnperformed(",
HeuristicName(), ")");
}
private:
void OnStart() override;
bool IncrementPosition() override;
bool IncrementRoutes();
std::function<int64_t(int64_t)> SetupNextAccessorForNeighbor() override;
int route_to_relocate_index_;
int last_route_to_relocate_index_;
int empty_route_index_;
int last_empty_route_index_;
std::vector<int> routes_to_relocate_;
std::vector<int> empty_routes_;
std::vector<int64_t> last_node_on_route_;
bool has_unperformed_nodes_;
bool just_started_;
};
/// Similar to the heuristic path LNS above, but instead of removing one route
/// entirely, the destruction phase consists of removing all nodes on an
/// "expensive" chain from a route.
class FilteredHeuristicExpensiveChainLNSOperator
: public FilteredHeuristicLocalSearchOperator {
public:
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() override {}
std::string DebugString() const override {
return absl::StrCat("HeuristicExpensiveChainLNS(", HeuristicName(), ")");
}
private:
void OnStart() override;
bool IncrementPosition() override;
bool IncrementRoute();
bool IncrementCurrentArcIndices();
bool FindMostExpensiveChainsOnRemainingRoutes();
std::function<int64_t(int64_t)> SetupNextAccessorForNeighbor() override;
int current_route_;
int last_route_;
const int num_arcs_to_consider_;
std::vector<std::pair<int64_t, int>> most_expensive_arc_starts_and_ranks_;
/// Indices in most_expensive_arc_starts_and_ranks_ corresponding to the first
/// and second arcs currently being considered for removal.
std::pair</*first_arc_index*/ int, /*second_arc_index*/ int>
current_expensive_arc_indices_;
std::function<int64_t(/*before_node*/ int64_t, /*after_node*/ int64_t,
/*path_start*/ int64_t)>
arc_cost_for_route_start_;
bool just_started_;
};
/// Filtered heuristic LNS operator, where the destruction phase consists of
/// removing a node and the 'num_close_nodes' nodes closest to it, along with
/// each of their corresponding sibling pickup/deliveries that are performed.
class FilteredHeuristicCloseNodesLNSOperator
: public FilteredHeuristicLocalSearchOperator {
public:
FilteredHeuristicCloseNodesLNSOperator(
std::unique_ptr<RoutingFilteredHeuristic> heuristic, int num_close_nodes);
~FilteredHeuristicCloseNodesLNSOperator() override {}
std::string DebugString() const override {
return absl::StrCat("HeuristicCloseNodesLNS(", HeuristicName(), ")");
}
private:
void Initialize();
void OnStart() override;
bool IncrementPosition() override;
std::function<int64_t(int64_t)> SetupNextAccessorForNeighbor() override;
void RemoveNode(int64_t node);
void RemoveNodeAndActiveSibling(int64_t node);
bool IsActive(int64_t node) const {
DCHECK_LT(node, model_->Size());
return Value(node) != node && !removed_nodes_[node];
}
int64_t Prev(int64_t node) const {
DCHECK_EQ(Value(InverseValue(node)), node);
DCHECK_LT(node, new_prevs_.size());
return changed_prevs_[node] ? new_prevs_[node] : InverseValue(node);
}
int64_t Next(int64_t node) const {
DCHECK(!model_->IsEnd(node));
return changed_nexts_[node] ? new_nexts_[node] : Value(node);
}
std::vector<int64_t> GetActiveSiblings(int64_t node) const;
const std::vector<PickupDeliveryPair>& pickup_delivery_pairs_;
int current_node_;
int last_node_;
bool just_started_;
bool initialized_;
std::vector<std::vector<int64_t>> close_nodes_;
const int num_close_nodes_;
/// Keep track of changes when making a neighbor.
std::vector<int64_t> new_nexts_;
SparseBitset<> changed_nexts_;
std::vector<int64_t> new_prevs_;
SparseBitset<> changed_prevs_;
};
} // namespace operations_research
#endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_INSERTION_LNS_H_