25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
34#include "ortools/constraint_solver/routing_parameters.pb.h"
43template <
typename Disjunctions>
44void AddDisjunctionsFromNodes(
const RoutingModel&
model,
45 const std::vector<int64_t>&
nodes,
46 Disjunctions* disjunctions) {
47 for (int64_t node :
nodes) {
48 for (
const auto disjunction :
model.GetDisjunctionIndices(node)) {
49 disjunctions->insert(disjunction);
58 absl::flat_hash_set<int> disjunction_nodes;
62 if (!disjunction_nodes.insert(node).second)
return false;
66 absl::flat_hash_set<DisjunctionIndex> disjunctions;
67 AddDisjunctionsFromNodes(*
this, pd_pairs.first, &disjunctions);
68 AddDisjunctionsFromNodes(*
this, pd_pairs.second, &disjunctions);
70 if (disjunctions.size() > 2)
return false;
78 if (dimension->class_evaluators_.size() != 1) {
83 if (transit ==
nullptr) {
86 int64_t max_vehicle_capacity = 0;
87 for (int64_t vehicle_capacity : dimension->vehicle_capacities()) {
88 max_vehicle_capacity =
std::max(max_vehicle_capacity, vehicle_capacity);
90 std::vector<int64_t> transits(nexts_.size(),
92 for (
int i = 0; i < nexts_.size(); ++i) {
94 transits[i] =
std::min(transits[i], transit(i));
101 const auto transit_cmp = [&transits](
int i,
int j) {
102 return transits[i] < transits[j];
107 transits[*std::min_element(pd_pairs.first.begin(),
108 pd_pairs.first.end(), transit_cmp)] +
110 transits[*std::min_element(pd_pairs.second.begin(),
111 pd_pairs.second.end(), transit_cmp)]);
115 for (
int i = 0; i < transits.size(); ++i) {
117 min_transit =
std::min(min_transit, transits[i]);
122 if (
CapProd(min_transit, 2) > max_vehicle_capacity)
return true;
156bool RoutingModel::SolveMatchingModel(
157 Assignment* assignment,
const RoutingSearchParameters&
parameters) {
158 if (
parameters.disable_scheduling_beware_this_may_degrade_performance()) {
163 VLOG(2) <<
"Solving with flow";
169 const std::vector<RoutingDimension*>
dimensions =
171 std::vector<LocalDimensionCumulOptimizer> optimizers;
174 optimizers.emplace_back(dimension,
178 int num_flow_nodes = 0;
179 std::vector<std::vector<int64_t>> disjunction_to_flow_nodes;
180 std::vector<int64_t> disjunction_penalties;
181 std::vector<bool> in_disjunction(
Size(),
false);
185 absl::flat_hash_map<int, std::pair<int64_t, int64_t>> flow_to_pd;
187 disjunction_to_flow_nodes.push_back({});
188 absl::flat_hash_set<DisjunctionIndex> disjunctions;
189 AddDisjunctionsFromNodes(*
this, pd_pairs.first, &disjunctions);
190 AddDisjunctionsFromNodes(*
this, pd_pairs.second, &disjunctions);
191 for (int64_t pickup : pd_pairs.first) {
192 in_disjunction[pickup] =
true;
193 for (int64_t delivery : pd_pairs.second) {
194 in_disjunction[delivery] =
true;
195 flow_to_pd[num_flow_nodes] = {pickup, delivery};
196 disjunction_to_flow_nodes.back().push_back(num_flow_nodes);
202 if (disjunctions.size() < 2) {
211 penalty =
CapAdd(penalty, d_penalty);
214 disjunction_penalties.push_back(penalty);
217 absl::flat_hash_map<int, int64_t> flow_to_non_pd;
218 for (
int node = 0; node <
Size(); ++node) {
219 if (
IsStart(node) || in_disjunction[node])
continue;
220 const std::vector<DisjunctionIndex>& disjunctions =
223 disjunction_to_flow_nodes.push_back({});
224 disjunction_penalties.push_back(
227 if (disjunctions.empty()) {
228 in_disjunction[node] =
true;
229 flow_to_non_pd[num_flow_nodes] = node;
230 disjunction_to_flow_nodes.back().push_back(num_flow_nodes);
234 in_disjunction[n] =
true;
235 flow_to_non_pd[num_flow_nodes] = n;
236 disjunction_to_flow_nodes.back().push_back(num_flow_nodes);
242 std::vector<FlowArc> arcs;
247 absl::flat_hash_map<int, int> flow_to_disjunction;
248 for (
int i = 0; i < disjunction_to_flow_nodes.size(); ++i) {
249 const std::vector<int64_t>& flow_nodes = disjunction_to_flow_nodes[i];
250 if (flow_nodes.size() == 1) {
251 flow_to_disjunction[flow_nodes.back()] = i;
253 flow_to_disjunction[num_flow_nodes] = i;
254 for (int64_t flow_node : flow_nodes) {
255 arcs.push_back({flow_node, num_flow_nodes, 1, 0});
266 std::vector<int> vehicle_to_flow;
267 absl::flat_hash_map<int, int> flow_to_vehicle;
268 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
270 const int64_t
end =
End(vehicle);
271 for (
const std::vector<int64_t>& flow_nodes : disjunction_to_flow_nodes) {
272 for (int64_t flow_node : flow_nodes) {
273 std::pair<int64_t, int64_t> pd_pair;
276 bool add_arc =
false;
278 const int64_t pickup = pd_pair.first;
279 const int64_t delivery = pd_pair.second;
287 const absl::flat_hash_map<int64_t, int64_t> nexts = {
288 {
start, pickup}, {pickup, delivery}, {delivery,
end}};
289 for (LocalDimensionCumulOptimizer& optimizer : optimizers) {
290 int64_t cumul_cost_value = 0;
293 if (optimizer.ComputeRouteCumulCostWithoutFixedTransits(
295 [&nexts](int64_t node) {
296 return nexts.find(node)->second;
298 &cumul_cost_value) !=
307 }
else if (
gtl::FindCopy(flow_to_non_pd, flow_node, &node)) {
312 const absl::flat_hash_map<int64_t, int64_t> nexts = {{
start, node},
314 for (LocalDimensionCumulOptimizer& optimizer : optimizers) {
315 int64_t cumul_cost_value = 0;
318 if (optimizer.ComputeRouteCumulCostWithoutFixedTransits(
320 [&nexts](int64_t node) {
321 return nexts.find(node)->second;
323 &cumul_cost_value) !=
336 arcs.push_back({num_flow_nodes, flow_node, 1,
cost});
340 flow_to_vehicle[num_flow_nodes] = vehicle;
341 vehicle_to_flow.push_back(num_flow_nodes);
345 const int source = num_flow_nodes + 1;
346 const int sink = source + 1;
348 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
349 arcs.push_back({source, vehicle_to_flow[vehicle], 1, 0});
353 const int unperformed = num_flow_nodes;
354 const int64_t flow_supply = disjunction_to_flow_nodes.size();
355 arcs.push_back({source, unperformed, flow_supply, 0});
356 for (
const auto& flow_disjunction_element : flow_to_disjunction) {
357 const int flow_node = flow_disjunction_element.first;
358 const int64_t penalty =
359 disjunction_penalties[flow_disjunction_element.second];
361 arcs.push_back({unperformed, flow_node, 1, penalty});
364 arcs.push_back({flow_node, sink, 1, 0});
371 int64_t scale_factor = 1;
372 const FlowArc& arc_with_max_cost = *std::max_element(
373 arcs.begin(), arcs.end(),
374 [](
const FlowArc&
a,
const FlowArc&
b) { return a.cost < b.cost; });
377 const int actual_flow_num_nodes = num_flow_nodes + 3;
378 if (log(
static_cast<double>(arc_with_max_cost.cost) + 1) +
379 2 * log(actual_flow_num_nodes) >
381 scale_factor =
CapProd(actual_flow_num_nodes, actual_flow_num_nodes);
384 SimpleMinCostFlow flow;
386 for (
const FlowArc&
arc : arcs) {
387 flow.AddArcWithCapacityAndUnitCost(
arc.tail,
arc.head,
arc.capacity,
388 arc.cost / scale_factor);
392 flow.SetNodeSupply(source, flow_supply);
393 flow.SetNodeSupply(sink, -flow_supply);
401 std::vector<bool> used_vehicles(
vehicles(),
false);
402 absl::flat_hash_set<int> used_nodes;
403 for (
int i = 0; i < flow.NumArcs(); ++i) {
404 if (flow.Flow(i) > 0 && flow.Tail(i) != source && flow.Head(i) != sink) {
405 std::vector<int>
nodes;
406 std::pair<int64_t, int64_t> pd_pair;
410 nodes.push_back(pd_pair.first);
411 nodes.push_back(pd_pair.second);
412 }
else if (
gtl::FindCopy(flow_to_non_pd, flow.Head(i), &node)) {
413 nodes.push_back(node);
415 for (int64_t flow_node : disjunction_to_flow_nodes[
index]) {
417 nodes.push_back(pd_pair.first);
418 nodes.push_back(pd_pair.second);
419 }
else if (
gtl::FindCopy(flow_to_non_pd, flow_node, &node)) {
420 nodes.push_back(node);
425 if (flow.Tail(i) == unperformed) {
427 for (
int node :
nodes) {
428 assignment->Add(
NextVar(node))->SetValue(node);
429 used_nodes.insert(node);
431 }
else if (
gtl::FindCopy(flow_to_vehicle, flow.Tail(i), &vehicle)) {
433 used_vehicles[vehicle] =
true;
434 int current =
Start(vehicle);
435 for (
int node :
nodes) {
436 assignment->Add(
NextVar(current))->SetValue(node);
437 used_nodes.insert(node);
440 assignment->Add(
NextVar(current))->SetValue(
End(vehicle));
445 for (
int node = 0; node <
Size(); ++node) {
446 if (!
IsStart(node) && used_nodes.count(node) == 0) {
447 assignment->Add(
NextVar(node))->SetValue(node);
451 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
452 if (!used_vehicles[vehicle]) {
std::vector< int > dimensions
#define DCHECK_LE(val1, val2)
#define DCHECK(condition)
#define VLOG(verboselevel)
Dimensions represent quantities accumulated at nodes along the routes.
int nodes() const
Sizes and indices Returns the number of nodes in the model.
RoutingTransitCallback1 TransitCallback1
const IndexPairs & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
bool IsStart(int64_t index) const
Returns true if 'index' represents the first node of a route.
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64_t node_index) const
Same as above for deliveries.
friend class RoutingDimension
int64_t Size() const
Returns the number of next variables in the model.
IntVar * NextVar(int64_t index) const
!defined(SWIGPYTHON)
static const int64_t kNoPenalty
Constant used to express a hard constraint instead of a soft penalty.
int64_t GetDisjunctionMaxCardinality(DisjunctionIndex index) const
Returns the maximum number of possible active nodes of the node disjunction of index 'index'.
std::vector< RoutingDimension * > GetDimensionsWithSoftOrSpanCosts() const
Returns dimensions with soft or vehicle span costs.
int64_t GetDisjunctionPenalty(DisjunctionIndex index) const
Returns the penalty of the node disjunction of index 'index'.
const TransitCallback1 & UnaryTransitCallbackOrNull(int callback_index) const
bool IsVehicleAllowedForIndex(int vehicle, int64_t index)
Returns true if a vehicle is allowed to visit a given node.
int64_t GetArcCostForVehicle(int64_t from_index, int64_t to_index, int64_t vehicle) const
Returns the cost of the transit arc between two nodes for a given vehicle.
const std::vector< DisjunctionIndex > & GetDisjunctionIndices(int64_t index) const
Returns the indices of the disjunctions to which an index belongs.
int64_t Start(int vehicle) const
Model inspection.
int vehicles() const
Returns the number of vehicle routes in the model.
int GetNumberOfDisjunctions() const
Returns the number of node disjunctions in the model.
const std::vector< std::pair< int, int > > & GetPickupIndexPairs(int64_t node_index) const
Returns pairs for which the node is a pickup; the first element of each pair is the index in the pick...
bool IsMatchingModel() const
Returns true if a vehicle/node matching problem is detected.
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
RoutingDisjunctionIndex DisjunctionIndex
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
const std::vector< int64_t > & GetDisjunctionNodeIndices(DisjunctionIndex index) const
Returns the variable indices of the nodes in the disjunction of index 'index'.
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
std::optional< int64_t > end