25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.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 VLOG(2) <<
"Solving with flow";
164 const std::vector<RoutingDimension*>
dimensions =
166 std::vector<LocalDimensionCumulOptimizer> optimizers;
169 optimizers.emplace_back(dimension,
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, pd_pairs.first, &disjunctions);
185 AddDisjunctionsFromNodes(*
this, pd_pairs.second, &disjunctions);
186 for (int64_t pickup : pd_pairs.first) {
187 in_disjunction[pickup] =
true;
188 for (int64_t delivery : pd_pairs.second) {
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);
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 =
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) {
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 &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 &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) >
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);
396 std::vector<bool> used_vehicles(
vehicles(),
false);
397 absl::flat_hash_set<int> used_nodes;
398 for (
int i = 0; i < flow.NumArcs(); ++i) {
399 if (flow.Flow(i) > 0 && flow.Tail(i) != source && flow.Head(i) != sink) {
400 std::vector<int>
nodes;
401 std::pair<int64_t, int64_t> pd_pair;
405 nodes.push_back(pd_pair.first);
406 nodes.push_back(pd_pair.second);
407 }
else if (
gtl::FindCopy(flow_to_non_pd, flow.Head(i), &node)) {
408 nodes.push_back(node);
410 for (int64_t flow_node : disjunction_to_flow_nodes[
index]) {
412 nodes.push_back(pd_pair.first);
413 nodes.push_back(pd_pair.second);
414 }
else if (
gtl::FindCopy(flow_to_non_pd, flow_node, &node)) {
415 nodes.push_back(node);
420 if (flow.Tail(i) == unperformed) {
422 for (
int node :
nodes) {
423 assignment->Add(
NextVar(node))->SetValue(node);
424 used_nodes.insert(node);
426 }
else if (
gtl::FindCopy(flow_to_vehicle, flow.Tail(i), &vehicle)) {
428 used_vehicles[vehicle] =
true;
429 int current =
Start(vehicle);
430 for (
int node :
nodes) {
431 assignment->Add(
NextVar(current))->SetValue(node);
432 used_nodes.insert(node);
435 assignment->Add(
NextVar(current))->SetValue(
End(vehicle));
440 for (
int node = 0; node <
Size(); ++node) {
441 if (!
IsStart(node) && used_nodes.count(node) == 0) {
442 assignment->Add(
NextVar(node))->SetValue(node);
446 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
447 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