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"
40template <
typename Disjunctions>
42 absl::Span<const int64_t> nodes,
43 Disjunctions* disjunctions) {
44 for (int64_t node : nodes) {
45 for (
const auto disjunction : model.GetDisjunctionIndices(node)) {
46 disjunctions->insert(disjunction);
55 absl::flat_hash_set<int> disjunction_nodes;
59 if (!disjunction_nodes.insert(node).second)
return false;
63 absl::flat_hash_set<DisjunctionIndex> disjunctions;
64 AddDisjunctionsFromNodes(*
this, pickups, &disjunctions);
65 AddDisjunctionsFromNodes(*
this, deliveries, &disjunctions);
67 if (disjunctions.size() > 2)
return false;
75 if (dimension->class_evaluators_.size() != 1) {
80 if (transit ==
nullptr) {
83 int64_t max_vehicle_capacity = 0;
84 for (int64_t vehicle_capacity : dimension->vehicle_capacities()) {
85 max_vehicle_capacity = std::max(max_vehicle_capacity, vehicle_capacity);
87 std::vector<int64_t> transits(nexts_.size(),
88 std::numeric_limits<int64_t>::max());
89 for (
int i = 0; i < nexts_.size(); ++i) {
91 transits[i] = std::min(transits[i], transit(i));
94 int64_t min_transit = std::numeric_limits<int64_t>::max();
98 const auto transit_cmp = [&transits](
int i,
int j) {
99 return transits[i] < transits[j];
102 std::min(min_transit,
104 transits[*absl::c_min_element(pickups, transit_cmp)] +
106 transits[*absl::c_min_element(deliveries, transit_cmp)]);
110 for (
int i = 0; i < transits.size(); ++i) {
112 min_transit = std::min(min_transit, transits[i]);
117 if (
CapProd(min_transit, 2) > max_vehicle_capacity)
return true;
151bool RoutingModel::SolveMatchingModel(
158 VLOG(2) <<
"Solving with flow";
164 const std::vector<RoutingDimension*> dimensions =
166 std::vector<LocalDimensionCumulOptimizer> optimizers;
167 optimizers.reserve(dimensions.size());
169 optimizers.emplace_back(
173 int num_flow_nodes = 0;
174 std::vector<std::vector<int64_t>> disjunction_to_flow_nodes;
175 std::vector<int64_t> disjunction_penalties;
176 std::vector<bool> in_disjunction(
Size(),
false);
180 absl::flat_hash_map<int, std::pair<int64_t, int64_t>> flow_to_pd;
182 disjunction_to_flow_nodes.push_back({});
183 absl::flat_hash_set<DisjunctionIndex> disjunctions;
184 AddDisjunctionsFromNodes(*
this, pickups, &disjunctions);
185 AddDisjunctionsFromNodes(*
this, deliveries, &disjunctions);
186 for (int64_t pickup : pickups) {
187 in_disjunction[pickup] =
true;
188 for (int64_t delivery : deliveries) {
189 in_disjunction[delivery] =
true;
190 flow_to_pd[num_flow_nodes] = {pickup, delivery};
191 disjunction_to_flow_nodes.back().push_back(num_flow_nodes);
195 DCHECK_LE(disjunctions.size(), 2);
197 if (disjunctions.size() < 2) {
206 penalty =
CapAdd(penalty, d_penalty);
209 disjunction_penalties.push_back(penalty);
212 absl::flat_hash_map<int, int64_t> flow_to_non_pd;
213 for (
int node = 0; node <
Size(); ++node) {
214 if (
IsStart(node) || in_disjunction[node])
continue;
215 const std::vector<DisjunctionIndex>& disjunctions =
217 DCHECK_LE(disjunctions.size(), 1);
218 disjunction_to_flow_nodes.push_back({});
219 disjunction_penalties.push_back(
222 if (disjunctions.empty()) {
223 in_disjunction[node] =
true;
224 flow_to_non_pd[num_flow_nodes] = node;
225 disjunction_to_flow_nodes.back().push_back(num_flow_nodes);
229 in_disjunction[n] =
true;
230 flow_to_non_pd[num_flow_nodes] = n;
231 disjunction_to_flow_nodes.back().push_back(num_flow_nodes);
237 std::vector<FlowArc> arcs;
242 absl::flat_hash_map<int, int> flow_to_disjunction;
243 for (
int i = 0;
i < disjunction_to_flow_nodes.size(); ++
i) {
244 const std::vector<int64_t>& flow_nodes = disjunction_to_flow_nodes[
i];
245 if (flow_nodes.size() == 1) {
246 flow_to_disjunction[flow_nodes.back()] =
i;
248 flow_to_disjunction[num_flow_nodes] =
i;
249 for (int64_t flow_node : flow_nodes) {
250 arcs.push_back({flow_node, num_flow_nodes, 1, 0});
261 std::vector<int> vehicle_to_flow;
262 absl::flat_hash_map<int, int> flow_to_vehicle;
263 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
264 const int64_t start =
Start(vehicle);
265 const int64_t
end =
End(vehicle);
266 for (
const std::vector<int64_t>& flow_nodes : disjunction_to_flow_nodes) {
267 for (int64_t flow_node : flow_nodes) {
268 std::pair<int64_t, int64_t> pd_pair;
271 bool add_arc =
false;
273 const int64_t pickup = pd_pair.first;
274 const int64_t delivery = pd_pair.second;
282 const absl::flat_hash_map<int64_t, int64_t> nexts = {
283 {start, pickup}, {pickup, delivery}, {delivery,
end}};
284 for (LocalDimensionCumulOptimizer& optimizer : optimizers) {
285 int64_t cumul_cost_value = 0;
288 if (optimizer.ComputeRouteCumulCostWithoutFixedTransits(
290 [&nexts](int64_t node) {
291 return nexts.find(node)->second;
293 nullptr, &cumul_cost_value) !=
295 cost =
CapAdd(cost, cumul_cost_value);
302 }
else if (
gtl::FindCopy(flow_to_non_pd, flow_node, &node)) {
307 const absl::flat_hash_map<int64_t, int64_t> nexts = {{start, node},
309 for (LocalDimensionCumulOptimizer& optimizer : optimizers) {
310 int64_t cumul_cost_value = 0;
313 if (optimizer.ComputeRouteCumulCostWithoutFixedTransits(
315 [&nexts](int64_t node) {
316 return nexts.find(node)->second;
318 nullptr, &cumul_cost_value) !=
320 cost =
CapAdd(cost, cumul_cost_value);
331 arcs.push_back({num_flow_nodes, flow_node, 1, cost});
335 flow_to_vehicle[num_flow_nodes] = vehicle;
336 vehicle_to_flow.push_back(num_flow_nodes);
340 const int source = num_flow_nodes + 1;
341 const int sink = source + 1;
343 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
344 arcs.push_back({source, vehicle_to_flow[vehicle], 1, 0});
348 const int unperformed = num_flow_nodes;
349 const int64_t flow_supply = disjunction_to_flow_nodes.size();
350 arcs.push_back({source, unperformed, flow_supply, 0});
351 for (
const auto& flow_disjunction_element : flow_to_disjunction) {
352 const int flow_node = flow_disjunction_element.first;
353 const int64_t penalty =
354 disjunction_penalties[flow_disjunction_element.second];
356 arcs.push_back({unperformed, flow_node, 1, penalty});
359 arcs.push_back({flow_node, sink, 1, 0});
366 int64_t scale_factor = 1;
367 const FlowArc& arc_with_max_cost = *std::max_element(
368 arcs.begin(), arcs.end(),
369 [](
const FlowArc& a,
const FlowArc& b) { return a.cost < b.cost; });
372 const int actual_flow_num_nodes = num_flow_nodes + 3;
373 if (log(
static_cast<double>(arc_with_max_cost.cost) + 1) +
374 2 * log(actual_flow_num_nodes) >
375 log(std::numeric_limits<int64_t>::max())) {
376 scale_factor =
CapProd(actual_flow_num_nodes, actual_flow_num_nodes);
379 SimpleMinCostFlow flow;
381 for (
const FlowArc& arc : arcs) {
382 flow.AddArcWithCapacityAndUnitCost(arc.tail, arc.head, arc.capacity,
383 arc.cost / scale_factor);
387 flow.SetNodeSupply(source, flow_supply);
388 flow.SetNodeSupply(sink, -flow_supply);
391 search_stats_.num_min_cost_flow_calls++;
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);
410 }
else if (
gtl::FindCopy(flow_to_disjunction, flow.Head(i), &index)) {
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);
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]) {
IntVarElement * Add(IntVar *var)
int64_t Size() const
Returns the number of next variables in the model.
int GetNumberOfDisjunctions() const
Returns the number of node disjunctions in the model.
int64_t Start(int vehicle) const
RoutingDisjunctionIndex DisjunctionIndex
operations_research::IntVar * NextVar(int64_t index) const
int64_t GetDisjunctionPenalty(DisjunctionIndex index) const
Returns the penalty of the node disjunction of index 'index'.
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
const std::vector< PickupDeliveryPair > & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
friend class RoutingDimension
bool IsMatchingModel() const
Returns true if a vehicle/node matching problem is detected.
bool IsStart(int64_t index) const
Returns true if 'index' represents the first node of a route.
bool IsPickup(int64_t node_index) const
Returns whether the node is a pickup (resp. delivery).
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
bool IsDelivery(int64_t node_index) const
const std::vector< int64_t > & GetDisjunctionNodeIndices(DisjunctionIndex index) const
int vehicles() const
Returns the number of vehicle routes in the model.
static const int64_t kNoPenalty
Constant used to express a hard constraint instead of a soft penalty.
const TransitCallback1 & UnaryTransitCallbackOrNull(int callback_index) const
int64_t GetArcCostForVehicle(int64_t from_index, int64_t to_index, int64_t vehicle) const
std::vector< RoutingDimension * > GetDimensionsWithSoftOrSpanCosts() const
Returns dimensions with soft or vehicle span costs.
bool IsVehicleAllowedForIndex(int vehicle, int64_t index) const
Returns true if a vehicle is allowed to visit a given node.
RoutingTransitCallback1 TransitCallback1
const std::vector< DisjunctionIndex > & GetDisjunctionIndices(int64_t index) const
Returns the indices of the disjunctions to which an index belongs.
int64_t GetDisjunctionMaxCardinality(DisjunctionIndex index) const
bool disable_scheduling_beware_this_may_degrade_performance() const
::operations_research::RoutingSearchParameters_SchedulingSolver continuous_scheduling_solver() const
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
int64_t CapAdd(int64_t x, int64_t y)
ClosedInterval::Iterator end(ClosedInterval interval)
int64_t CapProd(int64_t x, int64_t y)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...