Google OR-Tools: ortools/constraint_solver/routing_flow.cc Source File
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>
41void AddDisjunctionsFromNodes(const RoutingModel& model,
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) {
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];
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";
159 assignment->Clear();
164 const std::vector<RoutingDimension*> dimensions =
166 std::vector<LocalDimensionCumulOptimizer> optimizers;
167 optimizers.reserve(dimensions.size());
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);
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;
272 if (gtl::FindCopy(flow_to_pd, flow_node, &pd_pair)) {
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(
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},
308 {node, end}};
309 for (LocalDimensionCumulOptimizer& optimizer : optimizers) {
310 int64_t cumul_cost_value = 0;
313 if (optimizer.ComputeRouteCumulCostWithoutFixedTransits(
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;
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});
367 const FlowArc& arc_with_max_cost = *std::max_element(
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);
381 for (const FlowArc& arc : arcs) {
382 flow.AddArcWithCapacityAndUnitCost(arc.tail, arc.head, arc.capacity,
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;
405 if (gtl::FindCopy(flow_to_pd, flow.Head(i), &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]) {
412 if (gtl::FindCopy(flow_to_pd, flow_node, &pd_pair)) {
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) {
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) {
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.
Definition routing_flow.cc:52
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...
Definition routing_flow.cc:143
int64_t cost
Definition routing_flow.cc:147
int64_t tail
Definition routing_flow.cc:144
int64_t head
Definition routing_flow.cc:145
int64_t capacity
Definition routing_flow.cc:146