OR-Tools  9.1
variables_info.h
Go to the documentation of this file.
1 // Copyright 2010-2021 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_VARIABLES_INFO_H_
15 #define OR_TOOLS_GLOP_VARIABLES_INFO_H_
16 
18 #include "ortools/lp_data/sparse.h"
19 
20 namespace operations_research {
21 namespace glop {
22 
23 // Holds the statuses of all the variables, including slack variables. There
24 // is no point storing constraint statuses since internally all constraints are
25 // always fixed to zero.
26 //
27 // Note that this is the minimal amount of information needed to perform a "warm
28 // start". Using this information and the original linear program, the basis can
29 // be refactorized and all the needed quantities derived.
30 //
31 // TODO(user): Introduce another state class to store a complete state of the
32 // solver. Using this state and the original linear program, the solver can be
33 // restarted with as little time overhead as possible. This is especially useful
34 // for strong branching in a MIP context.
35 struct BasisState {
36  // TODO(user): A MIP solver will potentially store a lot of BasisStates so
37  // memory usage is important. It is possible to use only 2 bits for one
38  // VariableStatus enum. To achieve this, the FIXED_VALUE status can be
39  // converted to either AT_LOWER_BOUND or AT_UPPER_BOUND and decoded properly
40  // later since this will be used with a given linear program. This way we can
41  // even encode more information by using the reduced cost sign to choose to
42  // which bound the fixed status correspond.
44 
45  // Returns true if this state is empty.
46  bool IsEmpty() const { return statuses.empty(); }
47 };
48 
49 // Class responsible for maintaining diverse information for each variable that
50 // depend on its bounds and status.
51 //
52 // Note(user): Not all information is needed at all time, but it is cheap to
53 // maintain it since it only requires a few calls to Update() per simplex
54 // iteration.
56  public:
57  // Takes references to the linear program data we need.
58  explicit VariablesInfo(const CompactSparseMatrix& matrix);
59 
60  // Updates the internal bounds and recomputes the variable types from the
61  // bounds (this is the only function that changes them).
62  //
63  // Returns true iff the existing bounds didn't change. Except if the bounds
64  // AND underlying matrix didn't change, one will need to call one of the two
65  // Initialize*() methods below before using this class.
66  bool LoadBoundsAndReturnTrueIfUnchanged(const DenseRow& new_lower_bounds,
67  const DenseRow& new_upper_bounds);
68 
69  // Same for an LP not in equation form.
71  const DenseRow& variable_lower_bounds,
72  const DenseRow& variable_upper_bounds,
73  const DenseColumn& constraint_lower_bounds,
74  const DenseColumn& constraint_upper_bounds);
75 
76  // Initializes the status according to the given BasisState. Incompatible
77  // statuses will be corrected, and we transform the state correctly if new
78  // columns / rows were added. Note however that one will need to update the
79  // BasisState with deletions to preserve the status of unchanged columns.
80  void InitializeFromBasisState(ColIndex first_slack, ColIndex num_new_cols,
81  const BasisState& state);
82 
83  // Changes to the FREE status any column with a BASIC status not listed in
84  // the basis. Returns their number. Also makes sure all the columns listed in
85  // basis are marked as basic. Note that if a variable is fixed, we set its
86  // status to FIXED_VALUE not FREE.
88 
89  // Loops over all the free variables, and if such a variable has bounds and
90  // its starting value is closer to its closest bound than the given distance,
91  // change the status to move this variable to that bound. Returns the number
92  // of changes. The variable for which starting_values is not provided are
93  // considered at zero.
94  //
95  // This is mainly useful if non-zero starting values are provided. It allows
96  // to move all the variables close to their bounds at once instead of having
97  // to move them one by one with simplex pivots later. Of course, by doing that
98  // we usually introduce a small primal infeasibility that might need
99  // correction.
100  //
101  // If one uses a large distance, then all such variables will start at their
102  // bound if they have one.
104  const DenseRow& starting_values);
105 
106  // Sets all variables status to their lowest magnitude bounds. Note that there
107  // will be no basic variable after this is called.
109 
110  // Updates the information of the given variable. Note that it is not needed
111  // to call this if the status or the bound of a variable didn't change.
112  void UpdateToBasicStatus(ColIndex col);
113  void UpdateToNonBasicStatus(ColIndex col, VariableStatus status);
114 
115  // Various getter, see the corresponding member declaration below for more
116  // information.
117  const VariableTypeRow& GetTypeRow() const;
118  const VariableStatusRow& GetStatusRow() const;
119  const DenseBitRow& GetCanIncreaseBitRow() const;
120  const DenseBitRow& GetCanDecreaseBitRow() const;
121  const DenseBitRow& GetIsRelevantBitRow() const;
122  const DenseBitRow& GetIsBasicBitRow() const;
123  const DenseBitRow& GetNotBasicBitRow() const;
124  const DenseBitRow& GetNonBasicBoxedVariables() const;
125 
126  // Returns the variable bounds.
127  const DenseRow& GetVariableLowerBounds() const { return lower_bounds_; }
128  const DenseRow& GetVariableUpperBounds() const { return upper_bounds_; }
129 
130  const ColIndex GetNumberOfColumns() const { return matrix_.num_cols(); }
131 
132  // Changes whether or not a non-basic boxed variable is 'relevant' and will be
133  // returned as such by GetIsRelevantBitRow().
134  void MakeBoxedVariableRelevant(bool value);
135 
136  // This is used in UpdateRow to decide whether to compute it using the
137  // row-wise or column-wise representation.
138  EntryIndex GetNumEntriesInRelevantColumns() const;
139 
140  // Returns the distance between the upper and lower bound of the given column.
142  return upper_bounds_[col] - lower_bounds_[col];
143  }
144 
145  // This is used for the (SP) method of "Progress in the dual simplex method
146  // for large scale LP problems: practical dual phase I algorithms". Achim
147  // Koberstein & Uwe H. Suhl.
148  //
149  // This just set the bounds according to the variable types:
150  // - Boxed variables get fixed at [0,0].
151  // - Upper bounded variables get [-1, 0] bounds
152  // - Lower bounded variables get [0, 1] bounds
153  // - Free variables get [-1000, 1000] to heuristically move them to the basis.
154  // I.e. they cost in the dual infeasibility minimization problem is
155  // multiplied by 1000.
156  //
157  // It then update the status to get an initial dual feasible solution, and
158  // then one just have to apply the phase II algo on this problem to try to
159  // find a feasible solution to the original problem.
160  //
161  // Optimization: When a variable become basic, its non-zero bounds are
162  // relaxed. This is a bit hacky as it requires that the status is updated
163  // before the bounds are read (which is the case). It is however an important
164  // optimization.
165  //
166  // TODO(user): Shall we re-add the bound when the variable is moved out of
167  // the base? it is not needed, but might allow for more bound flips?
168  void TransformToDualPhaseIProblem(Fractional dual_feasibility_tolerance,
169  const DenseRow& reduced_costs);
170  void EndDualPhaseI(Fractional dual_feasibility_tolerance,
171  const DenseRow& reduced_costs);
172 
173  private:
174  // Computes the initial/default variable status from its type. A constrained
175  // variable is set to the lowest of its 2 bounds in absolute value.
176  VariableStatus DefaultVariableStatus(ColIndex col) const;
177 
178  // Resizes all status related vectors.
179  void ResetStatusInfo();
180 
181  // Computes the variable type from its lower and upper bound.
182  VariableType ComputeVariableType(ColIndex col) const;
183 
184  // Sets the column relevance and updates num_entries_in_relevant_columns_.
185  void SetRelevance(ColIndex col, bool relevance);
186 
187  // Used by TransformToDualPhaseIProblem()/EndDualPhaseI().
188  void UpdateStatusForNewType(ColIndex col);
189 
190  // Problem data that should be updated from outside.
191  const CompactSparseMatrix& matrix_;
192 
193  // The variables bounds of the current problem. Like everything here, it
194  // include the slacks.
195  DenseRow lower_bounds_;
196  DenseRow upper_bounds_;
197 
198  // This is just used temporarily by the dual phase I algo to save the original
199  // bounds.
200  DenseRow saved_lower_bounds_;
201  DenseRow saved_upper_bounds_;
202 
203  // Array of variable statuses, indexed by column index.
204  VariableStatusRow variable_status_;
205 
206  // Array of variable types, indexed by column index.
207  VariableTypeRow variable_type_;
208 
209  // Indicates if a non-basic variable can move up or down while not increasing
210  // the primal infeasibility. Note that all combinaisons are possible for a
211  // variable according to its status: fixed, free, upper or lower bounded. This
212  // is always false for basic variable.
213  DenseBitRow can_increase_;
214  DenseBitRow can_decrease_;
215 
216  // Indicates if we should consider this variable for entering the basis during
217  // the simplex algorithm. We never consider fixed variables and in the dual
218  // feasibility phase, we don't consider boxed variable.
219  DenseBitRow relevance_;
220 
221  // Indicates if a variable is BASIC or not. There are currently two members
222  // because the DenseBitRow class only supports a nice range-based iteration on
223  // the non-zero positions and not on the others.
224  DenseBitRow is_basic_;
225  DenseBitRow not_basic_;
226 
227  // Set of boxed variables that are non-basic.
228  DenseBitRow non_basic_boxed_variables_;
229 
230  // Number of entries for the relevant matrix columns (see relevance_).
231  EntryIndex num_entries_in_relevant_columns_;
232 
233  // Whether or not a boxed variable should be considered relevant.
234  bool boxed_variables_are_relevant_ = true;
235 
236  // Whether we are between the calls TransformToDualPhaseIProblem() and
237  // EndDualPhaseI().
238  bool in_dual_phase_one_ = false;
239 
240  DISALLOW_COPY_AND_ASSIGN(VariablesInfo);
241 };
242 
243 } // namespace glop
244 } // namespace operations_research
245 
246 #endif // OR_TOOLS_GLOP_VARIABLES_INFO_H_
void InitializeFromBasisState(ColIndex first_slack, ColIndex num_new_cols, const BasisState &state)
const DenseBitRow & GetIsBasicBitRow() const
int ChangeUnusedBasicVariablesToFree(const RowToColMapping &basis)
const DenseBitRow & GetCanDecreaseBitRow() const
const DenseBitRow & GetNotBasicBitRow() const
const DenseBitRow & GetCanIncreaseBitRow() const
ColIndex col
Definition: markowitz.cc:183
int SnapFreeVariablesToBound(Fractional distance, const DenseRow &starting_values)
bool empty() const
const VariableTypeRow & GetTypeRow() const
const DenseRow & GetVariableUpperBounds() const
const DenseBitRow & GetNonBasicBoxedVariables() const
const DenseRow & GetVariableLowerBounds() const
void EndDualPhaseI(Fractional dual_feasibility_tolerance, const DenseRow &reduced_costs)
void TransformToDualPhaseIProblem(Fractional dual_feasibility_tolerance, const DenseRow &reduced_costs)
Collection of objects used to extend the Constraint Solver library.
const VariableStatusRow & GetStatusRow() const
Fractional GetBoundDifference(ColIndex col) const
int64_t value
VariablesInfo(const CompactSparseMatrix &matrix)
double distance
const DenseBitRow & GetIsRelevantBitRow() const
bool LoadBoundsAndReturnTrueIfUnchanged(const DenseRow &new_lower_bounds, const DenseRow &new_upper_bounds)
void UpdateToNonBasicStatus(ColIndex col, VariableStatus status)