23#include "absl/algorithm/container.h"
24#include "absl/container/flat_hash_map.h"
25#include "absl/container/flat_hash_set.h"
26#include "absl/types/span.h"
33#include "ortools/constraint_solver/routing_parameters.pb.h"
41template <
typename Disjunctions>
42void AddDisjunctionsFromNodes(
const RoutingModel&
model,
43 absl::Span<const int64_t>
nodes,
44 Disjunctions* disjunctions) {
45 for (int64_t node :
nodes) {
46 for (
const auto disjunction :
model.GetDisjunctionIndices(node)) {
47 disjunctions->insert(disjunction);
53bool RoutingModel::IsMatchingModel()
const {
56 absl::flat_hash_set<int> disjunction_nodes;
57 for (DisjunctionIndex
i(0);
i < GetNumberOfDisjunctions(); ++
i) {
58 if (GetDisjunctionMaxCardinality(i) > 1)
return false;
59 for (int64_t node : GetDisjunctionNodeIndices(i)) {
60 if (!disjunction_nodes.insert(node).second)
return false;
63 for (
const auto& [pickups, deliveries] : GetPickupAndDeliveryPairs()) {
64 absl::flat_hash_set<DisjunctionIndex> disjunctions;
65 AddDisjunctionsFromNodes(*
this, pickups, &disjunctions);
66 AddDisjunctionsFromNodes(*
this, deliveries, &disjunctions);
68 if (disjunctions.size() > 2)
return false;
74 for (
const RoutingDimension*
const dimension : dimensions_) {
76 if (dimension->class_evaluators_.size() != 1) {
79 const TransitCallback1& transit =
80 UnaryTransitCallbackOrNull(dimension->class_evaluators_[0]);
81 if (transit ==
nullptr) {
84 int64_t max_vehicle_capacity = 0;
85 for (int64_t vehicle_capacity : dimension->vehicle_capacities()) {
86 max_vehicle_capacity = std::max(max_vehicle_capacity, vehicle_capacity);
88 std::vector<int64_t> transits(nexts_.size(),
89 std::numeric_limits<int64_t>::max());
90 for (
int i = 0;
i < nexts_.size(); ++
i) {
91 if (!IsStart(i) && !IsEnd(i)) {
92 transits[
i] = std::min(transits[i], transit(i));
95 int64_t min_transit = std::numeric_limits<int64_t>::max();
98 for (
const auto& [pickups, deliveries] : GetPickupAndDeliveryPairs()) {
99 const auto transit_cmp = [&transits](
int i,
int j) {
100 return transits[
i] < transits[j];
103 std::min(min_transit,
105 transits[*absl::c_min_element(pickups, transit_cmp)] +
107 transits[*absl::c_min_element(deliveries, transit_cmp)]);
111 for (
int i = 0;
i < transits.size(); ++
i) {
112 if (!IsPickup(i) && !IsDelivery(i)) {
113 min_transit = std::min(min_transit, transits[i]);
118 if (
CapProd(min_transit, 2) > max_vehicle_capacity)
return true;
152bool RoutingModel::SolveMatchingModel(
154 if (
parameters.disable_scheduling_beware_this_may_degrade_performance()) {
159 VLOG(2) <<
"Solving with flow";
165 const std::vector<RoutingDimension*>
dimensions =
166 GetDimensionsWithSoftOrSpanCosts();
167 std::vector<LocalDimensionCumulOptimizer> optimizers;
169 for (RoutingDimension* dimension :
dimensions) {
170 optimizers.emplace_back(dimension,
174 int num_flow_nodes = 0;
175 std::vector<std::vector<int64_t>> disjunction_to_flow_nodes;
176 std::vector<int64_t> disjunction_penalties;
177 std::vector<bool> in_disjunction(Size(),
false);
181 absl::flat_hash_map<int, std::pair<int64_t, int64_t>> flow_to_pd;
182 for (
const auto& [pickups, deliveries] : GetPickupAndDeliveryPairs()) {
183 disjunction_to_flow_nodes.push_back({});
184 absl::flat_hash_set<DisjunctionIndex> disjunctions;
185 AddDisjunctionsFromNodes(*
this, pickups, &disjunctions);
186 AddDisjunctionsFromNodes(*
this, deliveries, &disjunctions);
187 for (int64_t pickup : pickups) {
188 in_disjunction[pickup] =
true;
189 for (int64_t delivery : deliveries) {
190 in_disjunction[delivery] =
true;
191 flow_to_pd[num_flow_nodes] = {pickup, delivery};
192 disjunction_to_flow_nodes.back().push_back(num_flow_nodes);
196 DCHECK_LE(disjunctions.size(), 2);
198 if (disjunctions.size() < 2) {
199 penalty = kNoPenalty;
201 for (DisjunctionIndex
index : disjunctions) {
202 const int64_t d_penalty = GetDisjunctionPenalty(
index);
203 if (d_penalty == kNoPenalty) {
204 penalty = kNoPenalty;
207 penalty =
CapAdd(penalty, d_penalty);
210 disjunction_penalties.push_back(penalty);
213 absl::flat_hash_map<int, int64_t> flow_to_non_pd;
214 for (
int node = 0; node < Size(); ++node) {
215 if (IsStart(node) || in_disjunction[node])
continue;
216 const std::vector<DisjunctionIndex>& disjunctions =
217 GetDisjunctionIndices(node);
218 DCHECK_LE(disjunctions.size(), 1);
219 disjunction_to_flow_nodes.push_back({});
220 disjunction_penalties.push_back(
221 disjunctions.empty() ? kNoPenalty
222 : GetDisjunctionPenalty(disjunctions.back()));
223 if (disjunctions.empty()) {
224 in_disjunction[node] =
true;
225 flow_to_non_pd[num_flow_nodes] = node;
226 disjunction_to_flow_nodes.back().push_back(num_flow_nodes);
229 for (
int n : GetDisjunctionNodeIndices(disjunctions.back())) {
230 in_disjunction[n] =
true;
231 flow_to_non_pd[num_flow_nodes] = n;
232 disjunction_to_flow_nodes.back().push_back(num_flow_nodes);
238 std::vector<FlowArc> arcs;
243 absl::flat_hash_map<int, int> flow_to_disjunction;
244 for (
int i = 0;
i < disjunction_to_flow_nodes.size(); ++
i) {
245 const std::vector<int64_t>& flow_nodes = disjunction_to_flow_nodes[
i];
246 if (flow_nodes.size() == 1) {
247 flow_to_disjunction[flow_nodes.back()] =
i;
249 flow_to_disjunction[num_flow_nodes] =
i;
250 for (int64_t flow_node : flow_nodes) {
251 arcs.push_back({flow_node, num_flow_nodes, 1, 0});
262 std::vector<int> vehicle_to_flow;
263 absl::flat_hash_map<int, int> flow_to_vehicle;
264 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
265 const int64_t
start = Start(vehicle);
266 const int64_t
end = End(vehicle);
267 for (
const std::vector<int64_t>& flow_nodes : disjunction_to_flow_nodes) {
268 for (int64_t flow_node : flow_nodes) {
269 std::pair<int64_t, int64_t> pd_pair;
272 bool add_arc =
false;
274 const int64_t pickup = pd_pair.first;
275 const int64_t delivery = pd_pair.second;
276 if (IsVehicleAllowedForIndex(vehicle, pickup) &&
277 IsVehicleAllowedForIndex(vehicle, delivery)) {
280 CapAdd(GetArcCostForVehicle(
start, pickup, vehicle),
281 CapAdd(GetArcCostForVehicle(pickup, delivery, vehicle),
282 GetArcCostForVehicle(delivery,
end, vehicle)));
283 const absl::flat_hash_map<int64_t, int64_t> nexts = {
284 {
start, pickup}, {pickup, delivery}, {delivery,
end}};
285 for (LocalDimensionCumulOptimizer& optimizer : optimizers) {
286 int64_t cumul_cost_value = 0;
289 if (optimizer.ComputeRouteCumulCostWithoutFixedTransits(
291 [&nexts](int64_t node) {
292 return nexts.find(node)->second;
294 &cumul_cost_value) !=
296 cost =
CapAdd(cost, cumul_cost_value);
303 }
else if (
gtl::FindCopy(flow_to_non_pd, flow_node, &node)) {
304 if (IsVehicleAllowedForIndex(vehicle, node)) {
306 cost =
CapAdd(GetArcCostForVehicle(
start, node, vehicle),
307 GetArcCostForVehicle(node,
end, vehicle));
308 const absl::flat_hash_map<int64_t, int64_t> nexts = {{
start, node},
310 for (LocalDimensionCumulOptimizer& optimizer : optimizers) {
311 int64_t cumul_cost_value = 0;
314 if (optimizer.ComputeRouteCumulCostWithoutFixedTransits(
316 [&nexts](int64_t node) {
317 return nexts.find(node)->second;
319 &cumul_cost_value) !=
321 cost =
CapAdd(cost, cumul_cost_value);
332 arcs.push_back({num_flow_nodes, flow_node, 1, cost});
336 flow_to_vehicle[num_flow_nodes] = vehicle;
337 vehicle_to_flow.push_back(num_flow_nodes);
341 const int source = num_flow_nodes + 1;
342 const int sink = source + 1;
344 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
345 arcs.push_back({source, vehicle_to_flow[vehicle], 1, 0});
349 const int unperformed = num_flow_nodes;
350 const int64_t flow_supply = disjunction_to_flow_nodes.size();
351 arcs.push_back({source, unperformed, flow_supply, 0});
352 for (
const auto& flow_disjunction_element : flow_to_disjunction) {
353 const int flow_node = flow_disjunction_element.first;
354 const int64_t penalty =
355 disjunction_penalties[flow_disjunction_element.second];
356 if (penalty != kNoPenalty) {
357 arcs.push_back({unperformed, flow_node, 1, penalty});
360 arcs.push_back({flow_node, sink, 1, 0});
367 int64_t scale_factor = 1;
368 const FlowArc& arc_with_max_cost = *std::max_element(
369 arcs.begin(), arcs.end(),
370 [](
const FlowArc&
a,
const FlowArc&
b) { return a.cost < b.cost; });
373 const int actual_flow_num_nodes = num_flow_nodes + 3;
374 if (log(
static_cast<double>(arc_with_max_cost.cost) + 1) +
375 2 * log(actual_flow_num_nodes) >
376 log(std::numeric_limits<int64_t>::max())) {
377 scale_factor =
CapProd(actual_flow_num_nodes, actual_flow_num_nodes);
380 SimpleMinCostFlow flow;
382 for (
const FlowArc&
arc : arcs) {
383 flow.AddArcWithCapacityAndUnitCost(
arc.tail,
arc.head,
arc.capacity,
384 arc.cost / scale_factor);
388 flow.SetNodeSupply(source, flow_supply);
389 flow.SetNodeSupply(sink, -flow_supply);
397 std::vector<bool> used_vehicles(vehicles(),
false);
398 absl::flat_hash_set<int> used_nodes;
399 for (
int i = 0;
i < flow.NumArcs(); ++
i) {
400 if (flow.Flow(i) > 0 && flow.Tail(i) != source && flow.Head(i) != sink) {
401 std::vector<int>
nodes;
402 std::pair<int64_t, int64_t> pd_pair;
406 nodes.push_back(pd_pair.first);
407 nodes.push_back(pd_pair.second);
408 }
else if (
gtl::FindCopy(flow_to_non_pd, flow.Head(i), &node)) {
409 nodes.push_back(node);
411 for (int64_t flow_node : disjunction_to_flow_nodes[
index]) {
413 nodes.push_back(pd_pair.first);
414 nodes.push_back(pd_pair.second);
415 }
else if (
gtl::FindCopy(flow_to_non_pd, flow_node, &node)) {
416 nodes.push_back(node);
421 if (flow.Tail(i) == unperformed) {
423 for (
int node :
nodes) {
425 used_nodes.insert(node);
427 }
else if (
gtl::FindCopy(flow_to_vehicle, flow.Tail(i), &vehicle)) {
429 used_vehicles[vehicle] =
true;
430 int current = Start(vehicle);
431 for (
int node :
nodes) {
433 used_nodes.insert(node);
436 assignment->
Add(NextVar(current))->
SetValue(End(vehicle));
441 for (
int node = 0; node < Size(); ++node) {
442 if (!IsStart(node) && used_nodes.count(node) == 0) {
447 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
448 if (!used_vehicles[vehicle]) {
449 assignment->
Add(NextVar(Start(vehicle)))->
SetValue(End(vehicle));
std::vector< int > dimensions
IntVarElement * Add(IntVar *var)
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapAdd(int64_t x, int64_t y)
@ INFEASIBLE
A solution could not be found.
int64_t CapProd(int64_t x, int64_t y)
std::optional< int64_t > end