Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
revised_simplex.h
Go to the documentation of this file.
1// Copyright 2010-2025 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// Solves a Linear Programming problem using the Revised Simplex algorithm
15// as described by G.B. Dantzig.
16// The general form is:
17// min c.x where c and x are n-vectors,
18// subject to Ax = b where A is an mxn-matrix, b an m-vector,
19// with l <= x <= u, i.e.
20// l_i <= x_i <= u_i for all i in {1 .. m}.
21//
22// c.x is called the objective function.
23// Each row a_i of A is an n-vector, and a_i.x = b_i is a linear constraint.
24// A is called the constraint matrix.
25// b is called the right hand side (rhs) of the problem.
26// The constraints l_i <= x_i <= u_i are called the generalized bounds
27// of the problem (most introductory textbooks only deal with x_i >= 0, as
28// did the first version of the Simplex algorithm). Note that l_i and u_i
29// can be -infinity and +infinity, respectively.
30//
31// To simplify the entry of data, this code actually handles problems in the
32// form:
33// min c.x where c and x are n-vectors,
34// subject to:
35// A1 x <= b1
36// A2 x >= b2
37// A3 x = b3
38// l <= x <= u
39//
40// It transforms the above problem into
41// min c.x where c and x are n-vectors,
42// subject to:
43// A1 x + s1 = b1
44// A2 x - s2 = b2
45// A3 x = b3
46// l <= x <= u
47// s1 >= 0, s2 >= 0
48// where xT = (x1, x2, x3),
49// s1 is an m1-vector (m1 being the height of A1),
50// s2 is an m2-vector (m2 being the height of A2).
51//
52// The following are very good references for terminology, data structures,
53// and algorithms. They all contain a wealth of references.
54//
55// Vasek Chvátal, "Linear Programming," W.H. Freeman, 1983. ISBN 978-0716715870.
56// http://www.amazon.com/dp/0716715872
57//
58// Robert J. Vanderbei, "Linear Programming: Foundations and Extensions,"
59// Springer, 2010, ISBN-13: 978-1441944979
60// http://www.amazon.com/dp/1441944974
61//
62// Istvan Maros, "Computational Techniques of the Simplex Method.", Springer,
63// 2002, ISBN 978-1402073328
64// http://www.amazon.com/dp/1402073321
65//
66// ===============================================
67// Short description of the dual simplex algorithm.
68//
69// The dual simplex algorithm uses the same data structure as the primal, but
70// progresses towards the optimal solution in a different way:
71// * It tries to keep the dual values dual-feasible at all time which means that
72// the reduced costs are of the correct sign depending on the bounds of the
73// non-basic variables. As a consequence the values of the basic variable are
74// out of bound until the optimal is reached.
75// * A basic leaving variable is selected first (dual pricing) and then a
76// corresponding entering variable is selected. This is done in such a way
77// that the dual objective value increases (lower bound on the optimal
78// solution).
79// * Once the basis pivot is chosen, the variable values and the reduced costs
80// are updated the same way as in the primal algorithm.
81//
82// Good references on the Dual simplex algorithm are:
83//
84// Robert Fourer, "Notes on the Dual simplex Method", March 14, 1994.
85// http://users.iems.northwestern.edu/~4er/WRITINGS/dual.pdf
86//
87// Achim Koberstein, "The dual simplex method, techniques for a fast and stable
88// implementation", PhD, Paderborn, Univ., 2005.
89// http://digital.ub.uni-paderborn.de/hs/download/pdf/3885?originalFilename=true
90
91#ifndef OR_TOOLS_GLOP_REVISED_SIMPLEX_H_
92#define OR_TOOLS_GLOP_REVISED_SIMPLEX_H_
93
94#include <cstdint>
95#include <string>
96#include <vector>
97
98#include "absl/base/attributes.h"
99#include "absl/log/die_if_null.h"
100#include "absl/random/bit_gen_ref.h"
101#include "absl/random/random.h"
102#include "ortools/base/types.h"
107#include "ortools/glop/parameters.pb.h"
108#include "ortools/glop/pricing.h"
111#include "ortools/glop/status.h"
122#include "ortools/util/logging.h"
124#include "ortools/util/stats.h"
126
127namespace operations_research {
128namespace glop {
129
130// Entry point of the revised simplex algorithm implementation.
131class RevisedSimplex {
132 public:
134
135 // This type is neither copyable nor movable.
136 RevisedSimplex(const RevisedSimplex&) = delete;
139 // Sets or gets the algorithm parameters to be used on the next Solve().
140 void SetParameters(const GlopParameters& parameters);
141 const GlopParameters& GetParameters() const { return parameters_; }
143 // Solves the given linear program.
144 //
145 // We accept two forms of LinearProgram:
146 // - The lp can be in the equations form Ax = 0 created by
147 // LinearProgram::AddSlackVariablesForAllRows(), i.e. the rightmost square
148 // submatrix of A is an identity matrix, all its columns have been marked as
149 // slack variables, and the bounds of all constraints have been set to 0.
150 // - If not, we will convert it internally while copying it to the internal
151 // structure used.
152 //
153 // By default, the algorithm tries to exploit the computation done during the
154 // last Solve() call. It will analyze the difference of the new linear program
155 // and try to use the previously computed solution as a warm-start. To disable
156 // this behavior or give explicit warm-start data, use one of the State*()
157 // functions below.
158 ABSL_MUST_USE_RESULT Status Solve(const LinearProgram& lp,
160
161 // Do not use the current solution as a warm-start for the next Solve(). The
162 // next Solve() will behave as if the class just got created.
164
165 // Uses the given state as a warm-start for the next Solve() call.
166 void LoadStateForNextSolve(const BasisState& state);
167
168 // Advanced usage. While constructing the initial basis, if this is called
169 // then we will use these values as the initial starting value for the FREE
170 // variables.
172
173 // Getters to retrieve all the information computed by the last Solve().
174 RowIndex GetProblemNumRows() const;
175 ColIndex GetProblemNumCols() const;
178 int64_t GetNumberOfIterations() const;
179 Fractional GetVariableValue(ColIndex col) const;
180 Fractional GetReducedCost(ColIndex col) const;
181 const DenseRow& GetReducedCosts() const;
182 Fractional GetDualValue(RowIndex row) const;
183 Fractional GetConstraintActivity(RowIndex row) const;
184 VariableStatus GetVariableStatus(ColIndex col) const;
185 ConstraintStatus GetConstraintStatus(RowIndex row) const;
186 const BasisState& GetState() const;
187 double DeterministicTime() const;
188 bool objective_limit_reached() const { return objective_limit_reached_; }
191 return dual_edge_norms_.GetEdgeSquaredNorms();
192 }
193
194 const DenseBitRow& GetNotBasicBitRow() const {
195 return variables_info_.GetNotBasicBitRow();
196 }
197
198 // If the problem status is PRIMAL_UNBOUNDED (respectively DUAL_UNBOUNDED),
199 // then the solver has a corresponding primal (respectively dual) ray to show
200 // the unboundness. From a primal (respectively dual) feasible solution any
201 // positive multiple of this ray can be added to the solution and keep it
202 // feasible. Moreover, by doing so, the objective of the problem will improve
203 // and its magnitude will go to infinity.
204 //
205 // Note that when the problem is DUAL_UNBOUNDED, the dual ray is also known as
206 // the Farkas proof of infeasibility of the problem.
207 const DenseRow& GetPrimalRay() const;
208 const DenseColumn& GetDualRay() const;
209
210 // This is the "dual ray" linear combination of the matrix rows.
211 const DenseRow& GetDualRayRowCombination() const;
212
213 // Returns the index of the column in the basis and the basis factorization.
214 // Note that the order of the column in the basis is important since it is the
215 // one used by the various solve functions provided by the BasisFactorization
216 // class.
217 ColIndex GetBasis(RowIndex row) const;
218
219 const ScatteredRow& GetUnitRowLeftInverse(RowIndex row) {
220 return update_row_.ComputeAndGetUnitRowLeftInverse(row);
221 }
222
223 // Returns a copy of basis_ vector for outside applications (like cuts) to
224 // have the correspondence between rows and columns of the dictionary.
225 RowToColMapping GetBasisVector() const { return basis_; }
228
229 // Returns statistics about this class as a string.
230 std::string StatString();
231
232 // Computes the dictionary B^-1*N on-the-fly row by row. Returns the resulting
233 // matrix as a vector of sparse rows so that it is easy to use it on the left
234 // side in the matrix multiplication. Runs in O(num_non_zeros_in_matrix).
235 // TODO(user): Use row scales as well.
236 RowMajorSparseMatrix ComputeDictionary(const DenseRow* column_scales);
237
238 // Initializes the matrix for the given 'linear_program' and 'state' and
239 // computes the variable values for basic variables using non-basic variables.
240 void ComputeBasicVariablesForState(const LinearProgram& linear_program,
241 const BasisState& state);
242
243 // This is used in a MIP context to polish the final basis. We assume that the
244 // columns for which SetIntegralityScale() has been called correspond to
245 // integral variable once multiplied by the given factor.
246 void ClearIntegralityScales() { integrality_scale_.clear(); }
247 void SetIntegralityScale(ColIndex col, Fractional scale);
248
249 void SetLogger(SolverLogger* logger) { logger_ = logger; }
251 // Note: SetParameters() calls SetRandom() on its implementation, so if you
252 // want to set the parameters and then set the random generator, you should
253 // call SetRandom() after SetParameters().
254 void SetRandom(absl::BitGenRef random);
255
256 // Advanced usage. For fast incremental call to the solver, it is better not
257 // to use LinearProgram at all. This api allows to directly modify the
258 // internal data of glop and then call solve.
259 const CompactSparseMatrix& MatrixWithSlack() const { return compact_matrix_; }
261 transpose_was_changed_ = true;
262 return &transposed_matrix_;
263 }
265 return variables_info_.MutableLowerBounds();
266 }
268 return variables_info_.MutableUpperBounds();
269 }
271 const DenseRow& objective, Fractional objective_scaling_factor,
272 Fractional objective_offset, TimeLimit* time_limit);
273
274 private:
275 struct IterationStats : public StatsGroup {
276 IterationStats()
277 : StatsGroup("IterationStats"),
278 total("total", this),
279 normal("normal", this),
280 bound_flip("bound_flip", this),
281 refactorize("refactorize", this),
282 degenerate("degenerate", this),
283 num_dual_flips("num_dual_flips", this),
284 degenerate_run_size("degenerate_run_size", this) {}
285 TimeDistribution total;
286 TimeDistribution normal;
287 TimeDistribution bound_flip;
288 TimeDistribution refactorize;
289 TimeDistribution degenerate;
290 IntegerDistribution num_dual_flips;
291 IntegerDistribution degenerate_run_size;
292 };
293
294 struct RatioTestStats : public StatsGroup {
295 RatioTestStats()
296 : StatsGroup("RatioTestStats"),
297 bound_shift("bound_shift", this),
298 abs_used_pivot("abs_used_pivot", this),
299 abs_tested_pivot("abs_tested_pivot", this),
300 abs_skipped_pivot("abs_skipped_pivot", this),
301 direction_density("direction_density", this),
302 leaving_choices("leaving_choices", this),
303 num_perfect_ties("num_perfect_ties", this) {}
304 DoubleDistribution bound_shift;
305 DoubleDistribution abs_used_pivot;
306 DoubleDistribution abs_tested_pivot;
307 DoubleDistribution abs_skipped_pivot;
308 RatioDistribution direction_density;
309 IntegerDistribution leaving_choices;
310 IntegerDistribution num_perfect_ties;
311 };
312
313 enum class Phase { FEASIBILITY, OPTIMIZATION, PUSH };
314
315 enum class RefactorizationReason {
316 DEFAULT,
317 SMALL_PIVOT,
318 IMPRECISE_PIVOT,
319 NORM,
320 RC,
321 VAR_VALUES,
322 FINAL_CHECK
323 };
324
325 ABSL_MUST_USE_RESULT Status SolveInternal(double start_time, bool maximize,
326 const DenseRow& objective,
327 TimeLimit* time_limit);
328
329 // Propagates parameters_ to all the other classes that need it.
330 //
331 // TODO(user): Maybe a better design is for them to have a reference to a
332 // unique parameters object? It will clutter a bit more these classes'
333 // constructor though.
334 void PropagateParameters();
335
336 // Returns a string containing the same information as with GetSolverStats,
337 // but in a much more human-readable format. For example:
338 // Problem status : Optimal
339 // Solving time : 1.843
340 // Number of iterations : 12345
341 // Time for solvability (first phase) : 1.343
342 // Number of iterations for solvability : 10000
343 // Time for optimization : 0.5
344 // Number of iterations for optimization : 2345
345 // Maximum time allowed in seconds : 6000
346 // Maximum number of iterations : 1000000
347 // Stop after first basis : 0
348 std::string GetPrettySolverStats() const;
349
350 // Returns a string containing formatted information about the variable
351 // corresponding to column col.
352 std::string SimpleVariableInfo(ColIndex col) const;
353
354 // Displays a short string with the current iteration and objective value.
355 void DisplayIterationInfo(bool primal, RefactorizationReason reason =
356 RefactorizationReason::DEFAULT);
357
358 // Displays the error bounds of the current solution.
359 void DisplayErrors();
360
361 // Displays the status of the variables.
362 void DisplayInfoOnVariables() const;
363
364 // Displays the bounds of the variables.
365 void DisplayVariableBounds();
366
367 // Displays the following information:
368 // * Linear Programming problem as a dictionary, taking into
369 // account the iterations that have been made;
370 // * Variable info;
371 // * Reduced costs;
372 // * Variable bounds.
373 // A dictionary is in the form:
374 // xB = value + sum_{j in N} pa_ij x_j
375 // z = objective_value + sum_{i in N} rc_i x_i
376 // where the pa's are the coefficients of the matrix after the pivotings
377 // and the rc's are the reduced costs, i.e. the coefficients of the objective
378 // after the pivotings.
379 // Dictionaries are the modern way of presenting the result of an iteration
380 // of the Simplex algorithm in the literature.
381 void DisplayRevisedSimplexDebugInfo();
382
383 // Displays the Linear Programming problem as it was input.
384 void DisplayProblem();
385
386 // Returns the current objective value. This is just the sum of the current
387 // variable values times their current cost.
388 Fractional ComputeObjectiveValue() const;
389
390 // Returns the current objective of the linear program given to Solve() using
391 // the initial costs, maximization direction, objective offset and objective
392 // scaling factor.
393 Fractional ComputeInitialProblemObjectiveValue() const;
394
395 // Assigns names to variables. Variables in the input will be named
396 // x1..., slack variables will be s1... .
397 void SetVariableNames();
398
399 // Sets the variable status and derives the variable value according to the
400 // exact status definition. This can only be called for non-basic variables
401 // because the value of a basic variable is computed from the values of the
402 // non-basic variables.
403 void SetNonBasicVariableStatusAndDeriveValue(ColIndex col,
404 VariableStatus status);
405
406 // Checks if the basis_ and is_basic_ arrays are well formed. Also checks that
407 // the variable statuses are consistent with this basis. Returns true if this
408 // is the case. This is meant to be used in debug mode only.
409 bool BasisIsConsistent() const;
410
411 // Moves the column entering_col into the basis at position basis_row. Removes
412 // the current basis column at position basis_row from the basis and sets its
413 // status to leaving_variable_status.
414 void UpdateBasis(ColIndex entering_col, RowIndex basis_row,
415 VariableStatus leaving_variable_status);
416
417 // Initializes matrix-related internal data. Returns true if this data was
418 // unchanged. If not, also sets only_change_is_new_rows to true if compared
419 // to the current matrix, the only difference is that new rows have been
420 // added (with their corresponding extra slack variables). Similarly, sets
421 // only_change_is_new_cols to true if the only difference is that new columns
422 // have been added, in which case also sets num_new_cols to the number of
423 // new columns.
424 bool InitializeMatrixAndTestIfUnchanged(const LinearProgram& lp,
425 bool lp_is_in_equation_form,
426 bool* only_change_is_new_rows,
427 bool* only_change_is_new_cols,
428 ColIndex* num_new_cols);
429
430 // Checks if the only change to the bounds is the addition of new columns,
431 // and that the new columns have at least one bound equal to zero.
432 bool OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
433 const LinearProgram& lp, bool lp_is_in_equation_form,
434 ColIndex num_new_cols);
435
436 // Initializes objective-related internal data. Returns true if unchanged.
437 bool InitializeObjectiveAndTestIfUnchanged(const LinearProgram& lp);
438
439 // Computes the stopping criterion on the problem objective value.
440 void InitializeObjectiveLimit();
441
442 // Initializes the starting basis. In most cases it starts by the all slack
443 // basis and tries to apply some heuristics to replace fixed variables.
444 ABSL_MUST_USE_RESULT Status CreateInitialBasis();
445
446 // Sets the initial basis to the given columns, try to factorize it and
447 // recompute the basic variable values.
448 ABSL_MUST_USE_RESULT Status
449 InitializeFirstBasis(const RowToColMapping& initial_basis);
450
451 // Entry point for the solver initialization.
452 ABSL_MUST_USE_RESULT Status Initialize(const LinearProgram& lp);
453 ABSL_MUST_USE_RESULT Status FinishInitialization(bool solve_from_scratch);
454
455 // Saves the current variable statuses in solution_state_.
456 void SaveState();
457
458 // Displays statistics on what kinds of variables are in the current basis.
459 void DisplayBasicVariableStatistics();
460
461 // Tries to reduce the initial infeasibility (stored in error_) by using the
462 // singleton columns present in the problem. A singleton column is a column
463 // with only one non-zero. This is used by CreateInitialBasis().
464 void UseSingletonColumnInInitialBasis(RowToColMapping* basis);
465
466 // Returns the number of empty rows in the matrix, i.e. rows where all
467 // the coefficients are zero.
468 RowIndex ComputeNumberOfEmptyRows();
469
470 // Returns the number of empty columns in the matrix, i.e. columns where all
471 // the coefficients are zero.
472 ColIndex ComputeNumberOfEmptyColumns();
473
474 // Returns the number of super-basic variables. These are non-basic variables
475 // that are not at their bounds (if they have bounds), or non-basic free
476 // variables that are not at zero.
477 int ComputeNumberOfSuperBasicVariables() const;
478
479 // This method transforms a basis for the first phase, with the optimal
480 // value at zero, into a feasible basis for the initial problem, thus
481 // preparing the execution of phase-II of the algorithm.
482 void CleanUpBasis();
483
484 // If the primal maximum residual is too large, recomputes the basic variable
485 // value from the non-basic ones. This function also perturbs the bounds
486 // during the primal simplex if too many iterations are degenerate.
487 //
488 // Only call this on a refactorized basis to have the best precision.
489 void CorrectErrorsOnVariableValues();
490
491 // Computes b - A.x in error_
492 void ComputeVariableValuesError();
493
494 // Solves the system B.d = a where a is the entering column (given by col).
495 // Known as FTRAN (Forward transformation) in FORTRAN codes.
496 // See Chvatal's book for more detail (Chapter 7).
497 void ComputeDirection(ColIndex col);
498
499 // Computes a - B.d in error_ and return the maximum std::abs() of its coeffs.
500 Fractional ComputeDirectionError(ColIndex col);
501
502 // Computes the ratio of the basic variable corresponding to 'row'. A target
503 // bound (upper or lower) is chosen depending on the sign of the entering
504 // reduced cost and the sign of the direction 'd_[row]'. The ratio is such
505 // that adding 'ratio * d_[row]' to the variable value changes it to its
506 // target bound.
507 template <bool is_entering_reduced_cost_positive>
508 Fractional GetRatio(const DenseRow& lower_bounds,
509 const DenseRow& upper_bounds, RowIndex row) const;
510
511 // First pass of the Harris ratio test. Returns the harris ratio value which
512 // is an upper bound on the ratio value that the leaving variable can take.
513 // Fills leaving_candidates with the ratio and row index of a super-set of the
514 // columns with a ratio <= harris_ratio.
515 template <bool is_entering_reduced_cost_positive>
516 Fractional ComputeHarrisRatioAndLeavingCandidates(
517 Fractional bound_flip_ratio, SparseColumn* leaving_candidates) const;
518
519 // Chooses the leaving variable, considering the entering column and its
520 // associated reduced cost. If there was a precision issue and the basis is
521 // not refactorized, set refactorize to true. Otherwise, the row number of the
522 // leaving variable is written in *leaving_row, and the step length
523 // is written in *step_length.
524 Status ChooseLeavingVariableRow(ColIndex entering_col,
525 Fractional reduced_cost, bool* refactorize,
526 RowIndex* leaving_row,
527 Fractional* step_length,
528 Fractional* target_bound);
529
530 // Chooses the leaving variable for the primal phase-I algorithm. The
531 // algorithm follows more or less what is described in Istvan Maros's book in
532 // chapter 9.6 and what is done for the dual phase-I algorithm which was
533 // derived from Koberstein's PhD. Both references can be found at the top of
534 // this file.
535 void PrimalPhaseIChooseLeavingVariableRow(ColIndex entering_col,
536 Fractional reduced_cost,
537 bool* refactorize,
538 RowIndex* leaving_row,
539 Fractional* step_length,
540 Fractional* target_bound) const;
541
542 // Chooses an infeasible basic variable. The returned values are:
543 // - leaving_row: the basic index of the infeasible leaving variable
544 // or kNoLeavingVariable if no such row exists: the dual simplex algorithm
545 // has terminated and the optimal has been reached.
546 // - cost_variation: how much do we improve the objective by moving one unit
547 // along this dual edge.
548 // - target_bound: the bound at which the leaving variable should go when
549 // leaving the basis.
550 ABSL_MUST_USE_RESULT Status DualChooseLeavingVariableRow(
551 RowIndex* leaving_row, Fractional* cost_variation,
552 Fractional* target_bound);
553
554 // Updates the prices used by DualChooseLeavingVariableRow() after a simplex
555 // iteration by using direction_. The prices are stored in
556 // dual_pricing_vector_. Note that this function only takes care of the
557 // entering and leaving column dual feasibility status change and that other
558 // changes will be dealt with by DualPhaseIUpdatePriceOnReducedCostsChange().
559 void DualPhaseIUpdatePrice(RowIndex leaving_row, ColIndex entering_col);
560
561 // This must be called each time the dual_pricing_vector_ is changed at
562 // position row.
563 template <bool use_dense_update = false>
564 void OnDualPriceChange(DenseColumn::ConstView squared_norms, RowIndex row,
565 VariableType type, Fractional threshold);
566
567 // Updates the prices used by DualChooseLeavingVariableRow() when the reduced
568 // costs of the given columns have changed.
569 template <typename Cols>
570 void DualPhaseIUpdatePriceOnReducedCostChange(const Cols& cols);
571
572 // Same as DualChooseLeavingVariableRow() but for the phase I of the dual
573 // simplex. Here the objective is not to minimize the primal infeasibility,
574 // but the dual one, so the variable is not chosen in the same way. See
575 // "Notes on the Dual simplex Method" or Istvan Maros, "A Piecewise Linear
576 // Dual Phase-1 Algorithm for the Simplex Method", Computational Optimization
577 // and Applications, October 2003, Volume 26, Issue 1, pp 63-81.
578 // http://rd.springer.com/article/10.1023%2FA%3A1025102305440
579 ABSL_MUST_USE_RESULT Status DualPhaseIChooseLeavingVariableRow(
580 RowIndex* leaving_row, Fractional* cost_variation,
581 Fractional* target_bound);
582
583 // Makes sure the boxed variable are dual-feasible by setting them to the
584 // correct bound according to their reduced costs. This is called
585 // Dual feasibility correction in the literature.
586 //
587 // Note that this function is also used as a part of the bound flipping ratio
588 // test by flipping the boxed dual-infeasible variables at each iteration.
589 //
590 // If update_basic_values is true, the basic variable values are updated.
591 template <typename BoxedVariableCols>
592 void MakeBoxedVariableDualFeasible(const BoxedVariableCols& cols,
593 bool update_basic_values);
594
595 // Computes the step needed to move the leaving_row basic variable to the
596 // given target bound.
597 Fractional ComputeStepToMoveBasicVariableToBound(RowIndex leaving_row,
598 Fractional target_bound);
599
600 // Returns true if the basis obtained after the given pivot can be factorized.
601 bool TestPivot(ColIndex entering_col, RowIndex leaving_row);
602
603 // Gets the current LU column permutation from basis_representation,
604 // applies it to basis_ and then sets it to the identity permutation since
605 // it will no longer be needed during solves. This function also updates all
606 // the data that depends on the column order in basis_.
607 void PermuteBasis();
608
609 // Updates the system state according to the given basis pivot.
610 // Returns an error if the update could not be done because of some precision
611 // issue.
612 ABSL_MUST_USE_RESULT Status UpdateAndPivot(ColIndex entering_col,
613 RowIndex leaving_row,
614 Fractional target_bound);
615
616 // Displays all the timing stats related to the calling object.
617 void DisplayAllStats();
618
619 // Calls basis_factorization_.Refactorize() if refactorize is true, and
620 // returns its status. This also sets refactorize to false and invalidates any
621 // data structure that depends on the current factorization.
622 //
623 // The general idea is that if a refactorization is going to be needed during
624 // a simplex iteration, it is better to do it as soon as possible so that
625 // every component can take advantage of it.
626 Status RefactorizeBasisIfNeeded(bool* refactorize);
627
628 // Main iteration loop of the primal simplex.
629 ABSL_MUST_USE_RESULT Status PrimalMinimize(TimeLimit* time_limit);
630
631 // Main iteration loop of the dual simplex.
632 ABSL_MUST_USE_RESULT Status DualMinimize(bool feasibility_phase,
633 TimeLimit* time_limit);
634
635 // Pushes all super-basic variables to bounds (if applicable) or to zero (if
636 // unconstrained). This is part of a "crossover" procedure to find a vertex
637 // solution given a (near) optimal solution. Assumes that Minimize() or
638 // DualMinimize() has already run, i.e., that we are at an optimal solution
639 // within numerical tolerances.
640 ABSL_MUST_USE_RESULT Status PrimalPush(TimeLimit* time_limit);
641
642 // Experimental. This is useful in a MIP context. It performs a few degenerate
643 // pivot to try to mimize the fractionality of the optimal basis.
644 //
645 // We assume that the columns for which SetIntegralityScale() has been called
646 // correspond to integral variable once scaled by the given factor.
647 //
648 // I could only find slides for the reference of this "LP Solution Polishing
649 // to improve MIP Performance", Matthias Miltenberger, Zuse Institute Berlin.
650 ABSL_MUST_USE_RESULT Status Polish(TimeLimit* time_limit);
651
652 // Utility functions to return the current ColIndex of the slack column with
653 // given number. Note that currently, such columns are always present in the
654 // internal representation of a linear program.
655 ColIndex SlackColIndex(RowIndex row) const;
656
657 // Advances the deterministic time in time_limit with the difference between
658 // the current internal deterministic time and the internal deterministic time
659 // during the last call to this method.
660 // TODO(user): Update the internals of revised simplex so that the time
661 // limit is updated at the source and remove this method.
662 void AdvanceDeterministicTime(TimeLimit* time_limit);
663
664 // Problem status
665 ProblemStatus problem_status_;
666
667 // Current number of rows in the problem.
668 RowIndex num_rows_ = RowIndex(0);
669
670 // Current number of columns in the problem.
671 ColIndex num_cols_ = ColIndex(0);
672
673 // Index of the first slack variable in the input problem. We assume that all
674 // variables with index greater or equal to first_slack_col_ are slack
675 // variables.
676 ColIndex first_slack_col_ = ColIndex(0);
677
678 // We're using vectors after profiling and looking at the generated assembly
679 // it's as fast as std::unique_ptr as long as the size is properly reserved
680 // beforehand.
681
682 // Compact version of the matrix given to Solve().
683 CompactSparseMatrix compact_matrix_;
684
685 // The transpose of compact_matrix_, it may be empty if it is not needed.
686 CompactSparseMatrix transposed_matrix_;
687
688 // Stop the algorithm and report feasibility if:
689 // - The primal simplex is used, the problem is primal-feasible and the
690 // current objective value is strictly lower than primal_objective_limit_.
691 // - The dual simplex is used, the problem is dual-feasible and the current
692 // objective value is strictly greater than dual_objective_limit_.
693 Fractional primal_objective_limit_;
694 Fractional dual_objective_limit_;
695
696 // Current objective (feasibility for Phase-I, user-provided for Phase-II).
697 DenseRow current_objective_;
698
699 // Array of coefficients for the user-defined objective.
700 // Indexed by column number. Used in Phase-II.
701 DenseRow objective_;
702
703 // Objective offset and scaling factor of the linear program given to Solve().
704 // This is used to display the correct objective values in the logs with
705 // ComputeInitialProblemObjectiveValue().
706 Fractional objective_offset_;
707 Fractional objective_scaling_factor_;
708
709 // Used in dual phase I to keep track of the non-basic dual infeasible
710 // columns and their sign of infeasibility (+1 or -1).
711 DenseRow dual_infeasibility_improvement_direction_;
712 int num_dual_infeasible_positions_;
713
714 // A temporary scattered column that is always reset to all zero after use.
715 ScatteredColumn initially_all_zero_scratchpad_;
716
717 // Array of column index, giving the column number corresponding
718 // to a given basis row.
719 RowToColMapping basis_;
720 RowToColMapping tmp_basis_;
721
722 // Vector of strings containing the names of variables.
723 // Indexed by column number.
724 StrictITIVector<ColIndex, std::string> variable_name_;
725
726 // Only used for logging. What triggered a refactorization.
727 RefactorizationReason last_refactorization_reason_;
728
729 // Information about the solution computed by the last Solve().
730 Fractional solution_objective_value_;
731 DenseColumn solution_dual_values_;
732 DenseRow solution_reduced_costs_;
733 DenseRow solution_primal_ray_;
734 DenseColumn solution_dual_ray_;
735 DenseRow solution_dual_ray_row_combination_;
736 BasisState solution_state_;
737 bool solution_state_has_been_set_externally_;
738
739 // If this is cleared, we assume they are none.
740 DenseRow variable_starting_values_;
741
742 // See MutableTransposedMatrixWithSlack().
743 bool transpose_was_changed_ = false;
744
745 // This is known as 'd' in the literature and is set during each pivot to the
746 // right inverse of the basic entering column of A by ComputeDirection().
747 // ComputeDirection() also fills direction_.non_zeros with the position of the
748 // non-zero.
749 ScatteredColumn direction_;
750 Fractional direction_infinity_norm_;
751
752 // Used to compute the error 'b - A.x' or 'a - B.d'.
753 DenseColumn error_;
754
755 // A random number generator. In test we use absl_random_ to have a
756 // non-deterministic behavior and avoid client depending on a golden optimal
757 // solution which prevent us from easily changing the solver.
758 random_engine_t deterministic_random_;
759 absl::BitGen absl_random_;
760
761 // A reference to one of the above random generators. Fixed at construction.
762 absl::BitGenRef random_;
763
764 // Helpers for logging the solve progress.
765 SolverLogger default_logger_;
766 SolverLogger* logger_ = &default_logger_;
767
768 // Representation of matrix B using eta matrices and LU decomposition.
769 BasisFactorization basis_factorization_;
770
771 // Classes responsible for maintaining the data of the corresponding names.
772 VariablesInfo variables_info_;
773 PrimalEdgeNorms primal_edge_norms_;
774 DualEdgeNorms dual_edge_norms_;
775 DynamicMaximum<RowIndex> dual_prices_;
776 VariableValues variable_values_;
777 UpdateRow update_row_;
778 ReducedCosts reduced_costs_;
779 EnteringVariable entering_variable_;
780 PrimalPrices primal_prices_;
781
782 // Used in dual phase I to hold the price of each possible leaving choices.
783 DenseColumn dual_pricing_vector_;
784 DenseColumn tmp_dual_pricing_vector_;
785
786 // Temporary memory used by DualMinimize().
787 std::vector<ColIndex> bound_flip_candidates_;
788
789 // Total number of iterations performed.
790 uint64_t num_iterations_ = 0;
791
792 // Number of iterations performed during the first (feasibility) phase.
793 uint64_t num_feasibility_iterations_ = 0;
794
795 // Number of iterations performed during the second (optimization) phase.
796 uint64_t num_optimization_iterations_ = 0;
797
798 // Number of iterations performed during the push/crossover phase.
799 uint64_t num_push_iterations_ = 0;
800
801 // Deterministic time for DualPhaseIUpdatePriceOnReducedCostChange().
802 int64_t num_update_price_operations_ = 0;
803
804 // Total time spent in Solve().
805 double total_time_ = 0.0;
806
807 // Time spent in the first (feasibility) phase.
808 double feasibility_time_ = 0.0;
809
810 // Time spent in the second (optimization) phase.
811 double optimization_time_ = 0.0;
812
813 // Time spent in the push/crossover phase.
814 double push_time_ = 0.0;
815
816 // The internal deterministic time during the most recent call to
817 // RevisedSimplex::AdvanceDeterministicTime.
818 double last_deterministic_time_update_ = 0.0;
819
820 // Statistics about the iterations done by PrimalMinimize().
821 IterationStats iteration_stats_;
822
823 mutable RatioTestStats ratio_test_stats_;
824
825 // Placeholder for all the function timing stats.
826 // Mutable because we time const functions like ChooseLeavingVariableRow().
827 mutable StatsGroup function_stats_;
828
829 // Proto holding all the parameters of this algorithm.
830 //
831 // Note that parameters_ may actually change during a solve as the solver may
832 // dynamically adapt some values. It is why we store the argument of the last
833 // SetParameters() call in initial_parameters_ so the next Solve() can reset
834 // it correctly.
835 GlopParameters parameters_;
836 GlopParameters initial_parameters_;
837
838 // LuFactorization used to test if a pivot will cause the new basis to
839 // not be factorizable.
840 LuFactorization test_lu_;
841
842 // Number of degenerate iterations made just before the current iteration.
843 int num_consecutive_degenerate_iterations_;
844
845 // Indicate the current phase of the solve.
846 Phase phase_ = Phase::FEASIBILITY;
847
848 // Indicates whether simplex ended due to the objective limit being reached.
849 // Note that it's not enough to compare the final objective value with the
850 // limit due to numerical issues (i.e., the limit which is reached within
851 // given tolerance on the internal objective may no longer be reached when the
852 // objective scaling and offset are taken into account).
853 bool objective_limit_reached_;
854
855 // Temporary SparseColumn used by ChooseLeavingVariableRow().
856 SparseColumn leaving_candidates_;
857
858 // Temporary vector used to hold the best leaving column candidates that are
859 // tied using the current choosing criteria. We actually only store the tied
860 // candidate #2, #3, ...; because the first tied candidate is remembered
861 // anyway.
862 std::vector<RowIndex> equivalent_leaving_choices_;
863
864 // This is used by Polish().
865 DenseRow integrality_scale_;
866};
867
868// Hides the details of the dictionary matrix implementation. In the future,
869// GLOP will support generating the dictionary one row at a time without having
870// to store the whole matrix in memory.
872 public:
875 // RevisedSimplex cannot be passed const because we have to call a non-const
876 // method ComputeDictionary.
877 // TODO(user): Overload this to take RevisedSimplex* alone when the
878 // caller would normally pass a nullptr for col_scales so this and
879 // ComputeDictionary can take a const& argument.
880 RevisedSimplexDictionary(const DenseRow* col_scales,
881 RevisedSimplex* revised_simplex)
882 : dictionary_(
883 ABSL_DIE_IF_NULL(revised_simplex)->ComputeDictionary(col_scales)),
884 basis_vars_(ABSL_DIE_IF_NULL(revised_simplex)->GetBasisVector()) {}
885
886 // This type is neither copyable nor movable.
890 ConstIterator begin() const { return dictionary_.begin(); }
891 ConstIterator end() const { return dictionary_.end(); }
893 size_t NumRows() const { return dictionary_.size(); }
895 // TODO(user): This function is a better fit for the future custom iterator.
896 ColIndex GetBasicColumnForRow(RowIndex r) const { return basis_vars_[r]; }
897 SparseRow GetRow(RowIndex r) const { return dictionary_[r]; }
899 private:
900 const RowMajorSparseMatrix dictionary_;
901 const RowToColMapping basis_vars_;
902};
903
904// TODO(user): When a row-by-row generation of the dictionary is supported,
905// implement DictionaryIterator class that would call it inside operator*().
906
907} // namespace glop
908} // namespace operations_research
909
910#endif // OR_TOOLS_GLOP_REVISED_SIMPLEX_H_
Statistic on the distribution of a sequence of integers.
Definition stats.h:292
Base class to print a nice summary of a group of statistics.
Definition stats.h:131
StatsGroup(absl::string_view name)
Definition stats.h:138
RevisedSimplexDictionary(const DenseRow *col_scales, RevisedSimplex *revised_simplex)
RevisedSimplexDictionary & operator=(const RevisedSimplexDictionary &)=delete
RowMajorSparseMatrix::const_iterator ConstIterator
Entry point of the revised simplex algorithm implementation.
Fractional GetVariableValue(ColIndex col) const
void SetIntegralityScale(ColIndex col, Fractional scale)
ConstraintStatus GetConstraintStatus(RowIndex row) const
void LoadStateForNextSolve(const BasisState &state)
Uses the given state as a warm-start for the next Solve() call.
const GlopParameters & GetParameters() const
Fractional GetDualValue(RowIndex row) const
Fractional GetReducedCost(ColIndex col) const
const CompactSparseMatrix & MatrixWithSlack() const
const ScatteredRow & GetUnitRowLeftInverse(RowIndex row)
Fractional GetConstraintActivity(RowIndex row) const
const DenseBitRow & GetNotBasicBitRow() const
RowMajorSparseMatrix ComputeDictionary(const DenseRow *column_scales)
const DenseRow & GetDualRayRowCombination() const
This is the "dual ray" linear combination of the matrix rows.
RowIndex GetProblemNumRows() const
Getters to retrieve all the information computed by the last Solve().
void ComputeBasicVariablesForState(const LinearProgram &linear_program, const BasisState &state)
std::string StatString()
Returns statistics about this class as a string.
const BasisFactorization & GetBasisFactorization() const
CompactSparseMatrix * MutableTransposedMatrixWithSlack()
VariableStatus GetVariableStatus(ColIndex col) const
void SetParameters(const GlopParameters &parameters)
Sets or gets the algorithm parameters to be used on the next Solve().
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
ABSL_MUST_USE_RESULT Status MinimizeFromTransposedMatrixWithSlack(const DenseRow &objective, Fractional objective_scaling_factor, Fractional objective_offset, TimeLimit *time_limit)
void SetStartingVariableValuesForNextSolve(const DenseRow &values)
RevisedSimplex & operator=(const RevisedSimplex &)=delete
StrictITISpan< RowIndex, const Fractional > ConstView
Definition lp_types.h:291
time_limit
Definition solve.cc:22
StrictITIVector< RowIndex, ColIndex > RowToColMapping
Definition lp_types.h:394
Bitset64< ColIndex > DenseBitRow
Row of bits.
Definition lp_types.h:375
util_intops::StrongVector< RowIndex, SparseRow > RowMajorSparseMatrix
A matrix stored by rows.
Definition sparse_row.h:61
VariableType
Different types of variables.
Definition lp_types.h:178
StrictITIVector< RowIndex, Fractional > DenseColumn
Column-vector types. Column-vector types are indexed by a row index.
Definition lp_types.h:380
StrictITIVector< ColIndex, Fractional > DenseRow
Row-vector types. Row-vector types are indexed by a column index.
Definition lp_types.h:351
ProblemStatus
Different statuses for a given problem.
Definition lp_types.h:105
VectorXd ReducedCosts(const PrimalDualHybridGradientParams &params, const ShardedQuadraticProgram &sharded_qp, const VectorXd &primal_solution, const VectorXd &dual_solution, bool use_zero_primal_objective)
In SWIG mode, we don't want anything besides these top-level includes.
std::mt19937_64 random_engine_t
util_intops::StrongVector< ColumnEntryIndex, ElementIndex > SparseColumn
Definition base_types.h:61