OR-Tools  9.6
lp_solver.h
Go to the documentation of this file.
1 // Copyright 2010-2022 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_GLOP_LP_SOLVER_H_
15 #define OR_TOOLS_GLOP_LP_SOLVER_H_
16 
17 #include <memory>
18 #include <string>
19 
20 #include "ortools/glop/parameters.pb.h"
24 #include "ortools/util/logging.h"
26 
27 namespace operations_research {
28 namespace glop {
29 
30 // A full-fledged linear programming solver.
31 class LPSolver {
32  public:
33  LPSolver();
34 
35  // Sets and gets the solver parameters.
36  // See the proto for an extensive documentation.
37  void SetParameters(const GlopParameters& parameters);
38  const GlopParameters& GetParameters() const;
39  GlopParameters* GetMutableParameters();
40 
41  // Returns a string that describes the version of the solver.
42  static std::string GlopVersion();
43 
44  // Solves the given linear program and returns the solve status. See the
45  // ProblemStatus documentation for a description of the different values.
46  //
47  // The solution can be retrieved afterwards using the getter functions below.
48  // Note that depending on the returned ProblemStatus the solution values may
49  // not mean much, so it is important to check the returned status.
50  //
51  // Incrementality: From one Solve() call to the next, the internal state is
52  // not cleared and the solver may take advantage of its current state if the
53  // given lp is only slightly modified. If the modification is too important,
54  // or if the solver does not see how to reuse the previous state efficiently,
55  // it will just solve the problem from scratch. On the other hand, if the lp
56  // is the same, calling Solve() again should basically resume the solve from
57  // the last position. To disable this behavior, simply call Clear() before.
58  ABSL_MUST_USE_RESULT ProblemStatus Solve(const LinearProgram& lp);
59 
60  // Same as Solve() but use the given time limit rather than constructing a new
61  // one from the current GlopParameters.
62  ABSL_MUST_USE_RESULT ProblemStatus SolveWithTimeLimit(const LinearProgram& lp,
64 
65  // Puts the solver in a clean state.
66  //
67  // Calling Solve() for the first time, or calling Clear() then Solve() on the
68  // same problem is guaranteed to be deterministic and to always give the same
69  // result, assuming that no time limit was specified.
70  void Clear();
71 
72  // Advanced usage. This should be called before calling Solve(). It will
73  // configure the solver to try to start from the given point for the next
74  // Solve() only. Note that calling Clear() will invalidate this information.
75  //
76  // If the set of variables/constraints with a BASIC status does not form a
77  // basis a warning will be logged and the code will ignore it. Otherwise, the
78  // non-basic variables will be initialized to their given status and solving
79  // will start from there (even if the solution is not primal/dual feasible).
80  //
81  // Important: There is no facility to transform this information in sync with
82  // presolve. So you should probably disable presolve when using this since
83  // otherwise there is a good chance that the matrix will change and that the
84  // given basis will make no sense. Even worse if it happens to be factorizable
85  // but doesn't correspond to what was intended.
88 
89  // This loads a given solution and computes related quantities so that the
90  // getters below will refer to it.
91  //
92  // Depending on the given solution status, this also checks the solution
93  // feasibility or optimality. The exact behavior and tolerances are controlled
94  // by the solver parameters. Because of this, the returned ProblemStatus may
95  // be changed from the one passed in the ProblemSolution to ABNORMAL or
96  // IMPRECISE. Note that this is the same logic as the one used by Solve() to
97  // verify the solver solution.
99  const ProblemSolution& solution);
100 
101  // Returns the objective value of the solution with its offset and scaling.
103 
104  // Accessors to information related to variables.
105  const DenseRow& variable_values() const { return primal_values_; }
106  const DenseRow& reduced_costs() const { return reduced_costs_; }
108  return variable_statuses_;
109  }
110 
111  // Accessors to information related to constraints. The activity of a
112  // constraint is the sum of its linear terms evaluated with variables taking
113  // their values at the current solution.
114  //
115  // Note that the dual_values() do not take into account an eventual objective
116  // scaling of the solved LinearProgram.
117  const DenseColumn& dual_values() const { return dual_values_; }
119  return constraint_activities_;
120  }
122  return constraint_statuses_;
123  }
124 
125  // Accessors to information related to unboundedness. A primal ray is returned
126  // for primal unbounded problems and a dual ray is returned for dual unbounded
127  // problems. constraints_dual_ray corresponds to dual multiplier for
128  // constraints and variable_bounds_dual_ray corresponds to dual multipliers
129  // for variable bounds (cf. reduced_costs).
130  const DenseRow& primal_ray() const { return primal_ray_; }
132  return constraints_dual_ray_;
133  }
135  return variable_bounds_dual_ray_;
136  }
137 
138  // Returns the primal maximum infeasibility of the solution.
139  // This indicates by how much the variable and constraint bounds are violated.
141 
142  // Returns the dual maximum infeasibility of the solution.
143  // This indicates by how much the variable costs (i.e. objective) should be
144  // modified for the solution to be an exact optimal solution.
146 
147  // Returns true if the solution status was OPTIMAL and it seems that there is
148  // more than one basic optimal solution. Note that this solver always returns
149  // an optimal BASIC solution and that there is only a finite number of them.
150  // Moreover, given one basic solution, since the basis is always refactorized
151  // at optimality before reporting the numerical result, then all the
152  // quantities (even the floating point ones) should be always the same.
153  //
154  // TODO(user): Test this behavior extensively if a client relies on it.
155  bool MayHaveMultipleOptimalSolutions() const;
156 
157  // Returns the number of simplex iterations used by the last Solve().
158  int GetNumberOfSimplexIterations() const;
159 
160  // Returns the "deterministic time" since the creation of the solver. Note
161  // That this time is only increased when some operations take place in this
162  // class.
163  //
164  // TODO(user): Currently, this is only modified when the simplex code is
165  // executed.
166  //
167  // TODO(user): Improve the correlation with the running time.
168  double DeterministicTime() const;
169 
170  // Returns the SolverLogger used during solves.
171  //
172  // Please note that EnableLogging() and SetLogToStdOut() are reset at the
173  // beginning of each solve based on parameters so setting them will have no
174  // effect.
176 
177  private:
178  // Resizes all the solution vectors to the given sizes.
179  // This is used in case of error to make sure all the getter functions will
180  // not crash when given row/col inside the initial linear program dimension.
181  void ResizeSolution(RowIndex num_rows, ColIndex num_cols);
182 
183  // Make sure the primal and dual values are within their bounds in order to
184  // have a strong guarantee on the optimal solution. See
185  // provide_strong_optimal_guarantee in the GlopParameters proto.
186  void MovePrimalValuesWithinBounds(const LinearProgram& lp);
187  void MoveDualValuesWithinBounds(const LinearProgram& lp);
188 
189  // Runs the revised simplex algorithm if needed (i.e. if the program was not
190  // already solved by the preprocessors).
191  void RunRevisedSimplexIfNeeded(ProblemSolution* solution,
193 
194  // Checks that the returned solution values and statuses are consistent.
195  // Returns true if this is the case. See the code for the exact check
196  // performed.
197  bool IsProblemSolutionConsistent(const LinearProgram& lp,
198  const ProblemSolution& solution) const;
199 
200  // Returns true if there may be multiple optimal solutions.
201  // The return value is true if:
202  // - a non-fixed variable, at one of its boumds, has its reduced
203  // cost close to zero.
204  // or if:
205  // - a non-equality constraint (i.e. l <= a.x <= r, with l != r), is at one of
206  // its bounds (a.x = r or a.x = l) and has its dual value close to zero.
207  bool IsOptimalSolutionOnFacet(const LinearProgram& lp);
208 
209  // Computes derived quantities from the solution.
210  void ComputeReducedCosts(const LinearProgram& lp);
211  void ComputeConstraintActivities(const LinearProgram& lp);
212 
213  // Computes the primal/dual objectives (without the offset). Note that the
214  // dual objective needs the reduced costs in addition to the dual values.
215  double ComputeObjective(const LinearProgram& lp);
216  double ComputeDualObjective(const LinearProgram& lp);
217 
218  // Given a relative precision on the primal values of up to
219  // solution_feasibility_tolerance(), this returns an upper bound on the
220  // expected precision of the objective.
221  double ComputeMaxExpectedObjectiveError(const LinearProgram& lp);
222 
223  // Returns the max absolute cost pertubation (resp. rhs perturbation) so that
224  // the pair (primal values, dual values) is an EXACT optimal solution to the
225  // perturbed problem. Note that this assumes that
226  // MovePrimalValuesWithinBounds() and MoveDualValuesWithinBounds() have
227  // already been called. The Boolean is_too_large is set to true if any of the
228  // perturbation exceed the tolerance (which depends of the coordinate).
229  //
230  // These bounds are computed using the variable and constraint statuses by
231  // enforcing the complementary slackness optimal conditions. Note that they
232  // are almost the same as ComputeActivityInfeasibility() and
233  // ComputeReducedCostInfeasibility() but looks for optimality rather than just
234  // feasibility.
235  //
236  // Note(user): We could get EXACT bounds on these perturbations by changing
237  // the rounding mode appropriately during these computations. But this is
238  // probably not needed.
239  Fractional ComputeMaxCostPerturbationToEnforceOptimality(
240  const LinearProgram& lp, bool* is_too_large);
241  Fractional ComputeMaxRhsPerturbationToEnforceOptimality(
242  const LinearProgram& lp, bool* is_too_large);
243 
244  // Computes the maximum of the infeasibilities associated with each values.
245  // The returned infeasibilities are the maximum of the "absolute" errors of
246  // each vector coefficients.
247  //
248  // These function also set is_too_large to true if any infeasibility is
249  // greater than the tolerance (which depends of the coordinate).
250  double ComputePrimalValueInfeasibility(const LinearProgram& lp,
251  bool* is_too_large);
252  double ComputeActivityInfeasibility(const LinearProgram& lp,
253  bool* is_too_large);
254  double ComputeDualValueInfeasibility(const LinearProgram& lp,
255  bool* is_too_large);
256  double ComputeReducedCostInfeasibility(const LinearProgram& lp,
257  bool* is_too_large);
258 
259  // On a call to Solve(), this is initialized to an exact copy of the given
260  // linear program. It is later modified by the preprocessors and then solved
261  // by the revised simplex.
262  //
263  // This is not efficient memory-wise but allows to check optimality with
264  // respect to the given LinearProgram that is guaranteed to not have been
265  // modified. It also allows for a nicer Solve() API with a const
266  // LinearProgram& input.
267  LinearProgram current_linear_program_;
268 
269  SolverLogger logger_;
270 
271  // The revised simplex solver.
272  std::unique_ptr<RevisedSimplex> revised_simplex_;
273 
274  // The number of revised simplex iterations used by the last Solve().
275  int num_revised_simplex_iterations_;
276 
277  // The current ProblemSolution.
278  // TODO(user): use a ProblemSolution directly? Note, that primal_ray_,
279  // constraints_dual_ray_ and variable_bounds_dual_ray_ are not currently in
280  // ProblemSolution and are filled directly by RunRevisedSimplexIfNeeded().
281  DenseRow primal_values_;
282  DenseColumn dual_values_;
283  VariableStatusRow variable_statuses_;
284  ConstraintStatusColumn constraint_statuses_;
285  DenseRow primal_ray_;
286  DenseColumn constraints_dual_ray_;
287  DenseRow variable_bounds_dual_ray_;
288 
289  // Quantities computed from the solution and the linear program.
290  DenseRow reduced_costs_;
291  DenseColumn constraint_activities_;
292  Fractional problem_objective_value_ = 0.0;
293  bool may_have_multiple_solutions_;
294  Fractional max_absolute_primal_infeasibility_;
295  Fractional max_absolute_dual_infeasibility_;
296 
297  // Proto holding all the parameters of the algorithm.
298  GlopParameters parameters_;
299 
300  // The number of times Solve() was called. Used to number dump files.
301  int num_solves_;
302 
303  DISALLOW_COPY_AND_ASSIGN(LPSolver);
304 };
305 
306 } // namespace glop
307 } // namespace operations_research
308 
309 #endif // OR_TOOLS_GLOP_LP_SOLVER_H_
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
const GlopParameters & GetParameters() const
Definition: lp_solver.cc:130
const DenseColumn & constraint_activities() const
Definition: lp_solver.h:118
static std::string GlopVersion()
Definition: lp_solver.cc:114
void SetInitialBasis(const VariableStatusRow &variable_statuses, const ConstraintStatusColumn &constraint_statuses)
Definition: lp_solver.cc:261
const ConstraintStatusColumn & constraint_statuses() const
Definition: lp_solver.h:121
const DenseColumn & constraints_dual_ray() const
Definition: lp_solver.h:131
bool MayHaveMultipleOptimalSolutions() const
Definition: lp_solver.cc:520
const VariableStatusRow & variable_statuses() const
Definition: lp_solver.h:107
const DenseColumn & dual_values() const
Definition: lp_solver.h:117
GlopParameters * GetMutableParameters()
Definition: lp_solver.cc:132
Fractional GetMaximumDualInfeasibility() const
Definition: lp_solver.cc:516
const DenseRow & variable_values() const
Definition: lp_solver.h:105
Fractional GetMaximumPrimalInfeasibility() const
Definition: lp_solver.cc:512
Fractional GetObjectiveValue() const
Definition: lp_solver.cc:508
ProblemStatus LoadAndVerifySolution(const LinearProgram &lp, const ProblemSolution &solution)
Definition: lp_solver.cc:316
const DenseRow & reduced_costs() const
Definition: lp_solver.h:106
ABSL_MUST_USE_RESULT ProblemStatus Solve(const LinearProgram &lp)
Definition: lp_solver.cc:136
ABSL_MUST_USE_RESULT ProblemStatus SolveWithTimeLimit(const LinearProgram &lp, TimeLimit *time_limit)
Definition: lp_solver.cc:142
void SetParameters(const GlopParameters &parameters)
Definition: lp_solver.cc:118
const DenseRow & primal_ray() const
Definition: lp_solver.h:130
const DenseRow & variable_bounds_dual_ray() const
Definition: lp_solver.h:134
SatParameters parameters
ModelSharedTimeLimit * time_limit
Collection of objects used to extend the Constraint Solver library.