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