C++ Reference

C++ Reference: Routing

routing_lp_scheduling.h
Go to the documentation of this file.
1 // Copyright 2010-2018 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 #ifndef OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_LP_SCHEDULING_H_
15 #define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_LP_SCHEDULING_H_
16 
17 #include "absl/memory/memory.h"
19 #include "ortools/glop/lp_solver.h"
20 
21 namespace operations_research {
22 
23 // Classes to solve dimension cumul placement (aka scheduling) problems using
24 // linear programming.
25 
26 // Utility class used in the core optimizer to tighten the cumul bounds as much
27 // as possible based on the model precedences.
29  public:
31 
32  // Tightens the cumul bounds starting from the current cumul var min/max,
33  // and propagating the precedences resulting from the next_accessor, and the
34  // dimension's precedence rules.
35  // Returns false iff the precedences are infeasible with the given routes.
36  // Otherwise, the user can call CumulMin() and CumulMax() to retrieve the new
37  // bounds of an index.
38  bool PropagateCumulBounds(const std::function<int64(int64)>& next_accessor,
39  int64 cumul_offset);
40 
41  int64 CumulMin(int index) const {
42  return propagated_bounds_[PositiveNode(index)];
43  }
44 
45  int64 CumulMax(int index) const {
46  const int64 negated_upper_bound = propagated_bounds_[NegativeNode(index)];
47  return negated_upper_bound == kint64min ? kint64max : -negated_upper_bound;
48  }
49 
50  const RoutingDimension& dimension() const { return dimension_; }
51 
52  private:
53  // An arc "tail --offset--> head" represents the relation
54  // tail + offset <= head.
55  // As arcs are stored by tail, we don't store it in the struct.
56  struct ArcInfo {
57  int head;
58  int64 offset;
59  };
60  static const int kNoParent;
61  static const int kParentToBePropagated;
62 
63  // Return the node corresponding to the lower bound of the cumul of index and
64  // -index respectively.
65  int PositiveNode(int index) const { return 2 * index; }
66  int NegativeNode(int index) const { return 2 * index + 1; }
67 
68  void AddNodeToQueue(int node) {
69  if (!node_in_queue_[node]) {
70  bf_queue_.push_back(node);
71  node_in_queue_[node] = true;
72  }
73  }
74 
75  // Adds the relation first_index + offset <= second_index, by adding arcs
76  // first_index --offset--> second_index and
77  // -second_index --offset--> -first_index.
78  void AddArcs(int first_index, int second_index, int64 offset);
79 
80  bool InitializeArcsAndBounds(const std::function<int64(int64)>& next_accessor,
81  int64 cumul_offset);
82 
83  bool UpdateCurrentLowerBoundOfNode(int node, int64 new_lb);
84 
85  bool DisassembleSubtree(int source, int target);
86 
87  bool CleanupAndReturnFalse() {
88  // We clean-up node_in_queue_ for future calls, and return false.
89  for (int node_to_cleanup : bf_queue_) {
90  node_in_queue_[node_to_cleanup] = false;
91  }
92  bf_queue_.clear();
93  return false;
94  }
95 
96  const RoutingDimension& dimension_;
97  const int64 num_nodes_;
98 
99  // TODO(user): Investigate if all arcs for a given tail can be created
100  // at the same time, in which case outgoing_arcs_ could point to an absl::Span
101  // for each tail index.
102  std::vector<std::vector<ArcInfo>> outgoing_arcs_;
103 
104  std::deque<int> bf_queue_;
105  std::vector<bool> node_in_queue_;
106  std::vector<int> tree_parent_node_of_;
107  // After calling PropagateCumulBounds(), for each node index n,
108  // propagated_bounds_[2*n] and -propagated_bounds_[2*n+1] respectively contain
109  // the propagated lower and upper bounds of n's cumul variable.
110  std::vector<int64> propagated_bounds_;
111 
112  // Vector used in DisassembleSubtree() to avoid memory reallocation.
113  std::vector<int> tmp_dfs_stack_;
114 
115  // Used to store the pickup/delivery pairs encountered on the routes.
116  std::vector<std::pair<int64, int64>>
117  visited_pickup_delivery_indices_for_pair_;
118 };
119 
120 // Utility class used in Local/GlobalDimensionCumulOptimizer to set the LP
121 // constraints and solve the problem.
123  public:
125  bool use_precedence_propagator)
126  : dimension_(dimension),
127  visited_pickup_delivery_indices_for_pair_(
128  dimension->model()->GetPickupAndDeliveryPairs().size(), {-1, -1}) {
129  if (use_precedence_propagator) {
130  propagator_ = absl::make_unique<CumulBoundsPropagator>(dimension);
131  }
132  }
133 
134  // In the OptimizeSingleRoute() and Optimize() methods, if both "cumul_values"
135  // and "cost" parameters are null, we don't optimize the cost and stop at the
136  // first feasible solution in the LP (since in this case only feasibility is
137  // of interest).
138  bool OptimizeSingleRoute(int vehicle,
139  const std::function<int64(int64)>& next_accessor,
140  glop::LinearProgram* linear_program,
141  glop::LPSolver* lp_solver,
142  std::vector<int64>* cumul_values, int64* cost,
143  int64* transit_cost, bool clear_lp = true);
144 
145  bool Optimize(const std::function<int64(int64)>& next_accessor,
146  glop::LinearProgram* linear_program, glop::LPSolver* lp_solver,
147  std::vector<int64>* cumul_values, int64* cost,
148  int64* transit_cost, bool clear_lp = true);
149 
150  bool OptimizeAndPack(const std::function<int64(int64)>& next_accessor,
151  glop::LinearProgram* linear_program,
152  glop::LPSolver* lp_solver,
153  std::vector<int64>* cumul_values);
154 
156  int vehicle, const std::function<int64(int64)>& next_accessor,
157  glop::LinearProgram* linear_program, glop::LPSolver* lp_solver,
158  std::vector<int64>* cumul_values);
159 
160  const RoutingDimension* dimension() const { return dimension_; }
161 
162  private:
163  // Initializes the containers and given linear program. Must be called prior
164  // to setting any contraints and solving.
165  void InitOptimizer(glop::LinearProgram* linear_program);
166 
167  // Computes the minimum/maximum of cumuls for nodes on "route", and sets them
168  // in current_route_[min|max]_cumuls_ respectively.
169  // If the propagator_ is not null, uses the bounds tightened by the
170  // propagator.
171  // Otherwise, the bounds are computed by going over the nodes on the route
172  // using the CP bounds, and the fixed transits are used to tighten them.
173  bool ComputeRouteCumulBounds(const std::vector<int64>& route,
174  const std::vector<int64>& fixed_transits,
175  int64 cumul_offset);
176 
177  // Sets the constraints for all nodes on "vehicle"'s route according to
178  // "next_accessor". If optimize_costs is true, also sets the objective
179  // coefficients for the LP.
180  // Returns false if some infeasibility was detected, true otherwise.
181  bool SetRouteCumulConstraints(
182  int vehicle, const std::function<int64(int64)>& next_accessor,
183  int64 cumul_offset, bool optimize_costs,
184  glop::LinearProgram* linear_program, int64* route_transit_cost,
185  int64* route_cost_offset);
186 
187  // Sets the global constraints on the dimension, and adds global objective
188  // cost coefficients if optimize_costs is true.
189  // NOTE: When called, the call to this function MUST come after
190  // SetRouteCumulConstraints() has been called on all routes, so that
191  // index_to_cumul_variable_ and min_start/max_end_cumul_ are correctly
192  // initialized.
193  void SetGlobalConstraints(bool optimize_costs,
194  glop::LinearProgram* linear_program);
195 
196  bool FinalizeAndSolve(glop::LinearProgram* linear_program,
197  glop::LPSolver* lp_solver);
198 
199  void SetCumulValuesFromLP(const std::vector<glop::ColIndex>& cumul_variables,
200  int64 offset, const glop::LPSolver& lp_solver,
201  std::vector<int64>* cumul_values);
202 
203  // This function packs the routes of the given vehicles while keeping the cost
204  // of the LP lower than its current (supposed optimal) objective value.
205  // It does so by setting the current objective variables' coefficient to 0 and
206  // setting the coefficient of the route ends to 1, to first minimize the route
207  // ends' cumuls, and then maximizes the starts' cumuls without increasing the
208  // ends.
209  bool PackRoutes(std::vector<int> vehicles,
210  glop::LinearProgram* linear_program,
211  glop::LPSolver* lp_solver);
212 
213  std::unique_ptr<CumulBoundsPropagator> propagator_;
214  std::vector<int64> current_route_min_cumuls_;
215  std::vector<int64> current_route_max_cumuls_;
216  const RoutingDimension* const dimension_;
217  std::vector<glop::ColIndex> current_route_cumul_variables_;
218  std::vector<glop::ColIndex> index_to_cumul_variable_;
219  glop::ColIndex max_end_cumul_;
220  glop::ColIndex min_start_cumul_;
221  std::vector<std::pair<int64, int64>>
222  visited_pickup_delivery_indices_for_pair_;
223 };
224 
225 // Class used to compute optimal values for dimension cumuls of routes,
226 // minimizing cumul soft lower and upper bound costs, and vehicle span costs of
227 // a route.
228 // In its methods, next_accessor is a callback returning the next node of a
229 // given node on a route.
231  public:
233 
234  // If feasible, computes the optimal cost of the route performed by a vehicle,
235  // minimizing cumul soft lower and upper bound costs and vehicle span costs,
236  // and stores it in "optimal_cost" (if not null).
237  // Returns true iff the route respects all constraints.
238  bool ComputeRouteCumulCost(int vehicle,
239  const std::function<int64(int64)>& next_accessor,
240  int64* optimal_cost);
241 
242  // Same as ComputeRouteCumulCost, but the cost computed does not contain
243  // the part of the vehicle span cost due to fixed transits.
245  int vehicle, const std::function<int64(int64)>& next_accessor,
246  int64* optimal_cost_without_transits);
247 
248  // If feasible, computes the optimal cumul values of the route performed by a
249  // vehicle, minimizing cumul soft lower and upper bound costs and vehicle span
250  // costs, stores them in "optimal_cumuls" (if not null), and returns true.
251  // Returns false if the route is not feasible.
252  bool ComputeRouteCumuls(int vehicle,
253  const std::function<int64(int64)>& next_accessor,
254  std::vector<int64>* optimal_cumuls);
255 
256  // Similar to ComputeRouteCumuls, but also tries to pack the cumul values on
257  // the route, such that the cost remains the same, the cumul of route end is
258  // minimized, and then the cumul of the start of the route is maximized.
260  int vehicle, const std::function<int64(int64)>& next_accessor,
261  std::vector<int64>* packed_cumuls);
262 
263  const RoutingDimension* dimension() const {
264  return optimizer_core_.dimension();
265  }
266 
267  private:
268  std::vector<std::unique_ptr<glop::LPSolver>> lp_solver_;
269  std::vector<std::unique_ptr<glop::LinearProgram>> linear_program_;
270  DimensionCumulOptimizerCore optimizer_core_;
271 };
272 
274  public:
276  // If feasible, computes the optimal cost of the entire model with regards to
277  // the optimizer_core_'s dimension costs, minimizing cumul soft lower/upper
278  // bound costs and vehicle/global span costs, and stores it in "optimal_cost"
279  // (if not null).
280  // Returns true iff all the constraints can be respected.
282  const std::function<int64(int64)>& next_accessor,
283  int64* optimal_cost_without_transits);
284  // If feasible, computes the optimal cumul values, minimizing cumul soft
285  // lower/upper bound costs and vehicle/global span costs, stores them in
286  // "optimal_cumuls" (if not null), and returns true.
287  // Returns false if the routes are not feasible.
288  bool ComputeCumuls(const std::function<int64(int64)>& next_accessor,
289  std::vector<int64>* optimal_cumuls);
290 
291  // Returns true iff the routes resulting from the next_accessor are feasible
292  // wrt the constraints on the optimizer_core_.dimension()'s cumuls.
293  bool IsFeasible(const std::function<int64(int64)>& next_accessor);
294 
295  // Similar to ComputeCumuls, but also tries to pack the cumul values on all
296  // routes, such that the cost remains the same, the cumuls of route ends are
297  // minimized, and then the cumuls of the starts of the routes are maximized.
298  bool ComputePackedCumuls(const std::function<int64(int64)>& next_accessor,
299  std::vector<int64>* packed_cumuls);
300 
301  const RoutingDimension* dimension() const {
302  return optimizer_core_.dimension();
303  }
304 
305  private:
306  glop::LPSolver lp_solver_;
307  glop::LinearProgram linear_program_;
308  DimensionCumulOptimizerCore optimizer_core_;
309 };
310 
311 } // namespace operations_research
312 
313 #endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_LP_SCHEDULING_H_
bool Optimize(const std::function< int64(int64)> &next_accessor, glop::LinearProgram *linear_program, glop::LPSolver *lp_solver, std::vector< int64 > *cumul_values, int64 *cost, int64 *transit_cost, bool clear_lp=true)
bool OptimizeSingleRoute(int vehicle, const std::function< int64(int64)> &next_accessor, glop::LinearProgram *linear_program, glop::LPSolver *lp_solver, std::vector< int64 > *cumul_values, int64 *cost, int64 *transit_cost, bool clear_lp=true)
bool ComputeCumuls(const std::function< int64(int64)> &next_accessor, std::vector< int64 > *optimal_cumuls)
GlobalDimensionCumulOptimizer(const RoutingDimension *dimension)
bool PropagateCumulBounds(const std::function< int64(int64)> &next_accessor, int64 cumul_offset)
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2046
bool ComputeCumulCostWithoutFixedTransits(const std::function< int64(int64)> &next_accessor, int64 *optimal_cost_without_transits)
bool ComputePackedCumuls(const std::function< int64(int64)> &next_accessor, std::vector< int64 > *packed_cumuls)
bool ComputeRouteCumulCostWithoutFixedTransits(int vehicle, const std::function< int64(int64)> &next_accessor, int64 *optimal_cost_without_transits)
bool ComputeRouteCumuls(int vehicle, const std::function< int64(int64)> &next_accessor, std::vector< int64 > *optimal_cumuls)
bool OptimizeAndPackSingleRoute(int vehicle, const std::function< int64(int64)> &next_accessor, glop::LinearProgram *linear_program, glop::LPSolver *lp_solver, std::vector< int64 > *cumul_values)
DimensionCumulOptimizerCore(const RoutingDimension *dimension, bool use_precedence_propagator)
bool OptimizeAndPack(const std::function< int64(int64)> &next_accessor, glop::LinearProgram *linear_program, glop::LPSolver *lp_solver, std::vector< int64 > *cumul_values)
bool IsFeasible(const std::function< int64(int64)> &next_accessor)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
CumulBoundsPropagator(const RoutingDimension *dimension)
bool ComputePackedRouteCumuls(int vehicle, const std::function< int64(int64)> &next_accessor, std::vector< int64 > *packed_cumuls)
bool ComputeRouteCumulCost(int vehicle, const std::function< int64(int64)> &next_accessor, int64 *optimal_cost)
const RoutingDimension & dimension() const
LocalDimensionCumulOptimizer(const RoutingDimension *dimension)