OR-Tools  9.0
variables_info.cc
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 
15 
16 namespace operations_research {
17 namespace glop {
18 
20  : matrix_(matrix) {}
21 
23  const DenseRow& new_lower_bounds, const DenseRow& new_upper_bounds) {
24  const ColIndex num_cols = matrix_.num_cols();
25  DCHECK_EQ(num_cols, new_lower_bounds.size());
26  DCHECK_EQ(num_cols, new_upper_bounds.size());
27 
28  // Optim if nothing changed.
29  if (lower_bounds_ == new_lower_bounds && upper_bounds_ == new_upper_bounds) {
30  return true;
31  }
32 
33  lower_bounds_ = new_lower_bounds;
34  upper_bounds_ = new_upper_bounds;
35  variable_type_.resize(num_cols, VariableType::UNCONSTRAINED);
36  for (ColIndex col(0); col < num_cols; ++col) {
37  variable_type_[col] = ComputeVariableType(col);
38  }
39  return false;
40 }
41 
42 void VariablesInfo::ResetStatusInfo() {
43  const ColIndex num_cols = matrix_.num_cols();
44  DCHECK_EQ(num_cols, lower_bounds_.size());
45  DCHECK_EQ(num_cols, upper_bounds_.size());
46 
47  // TODO(user): These could just be Resized() but there is a bug with the
48  // iteration and resize it seems. Investigate. I suspect the last bucket
49  // is not cleared so you can still iterate on the ones there even if it all
50  // positions before num_cols are set to zero.
51  variable_status_.resize(num_cols, VariableStatus::FREE);
52  can_increase_.ClearAndResize(num_cols);
53  can_decrease_.ClearAndResize(num_cols);
54  is_basic_.ClearAndResize(num_cols);
55  not_basic_.ClearAndResize(num_cols);
56  non_basic_boxed_variables_.ClearAndResize(num_cols);
57 
58  // This one cannot just be resized.
59  boxed_variables_are_relevant_ = true;
60  num_entries_in_relevant_columns_ = 0;
61  relevance_.ClearAndResize(num_cols);
62 }
63 
64 void VariablesInfo::InitializeFromBasisState(ColIndex first_slack_col,
65  ColIndex num_new_cols,
66  const BasisState& state) {
67  ResetStatusInfo();
68 
69  RowIndex num_basic_variables(0);
70  const ColIndex num_cols = lower_bounds_.size();
71  const RowIndex num_rows = ColToRowIndex(num_cols - first_slack_col);
72  DCHECK_LE(num_new_cols, first_slack_col);
73  const ColIndex first_new_col(first_slack_col - num_new_cols);
74 
75  // Compute the status for all the columns (note that the slack variables are
76  // already added at the end of the matrix at this stage).
77  for (ColIndex col(0); col < num_cols; ++col) {
78  // Start with the given "warm" status from the BasisState if it exists.
79  VariableStatus status;
80  if (col < first_new_col && col < state.statuses.size()) {
81  status = state.statuses[col];
82  } else if (col >= first_slack_col &&
83  col - num_new_cols < state.statuses.size()) {
84  status = state.statuses[col - num_new_cols];
85  } else {
86  UpdateToNonBasicStatus(col, DefaultVariableStatus(col));
87  continue;
88  }
89 
90  // Remove incompatibilities between the warm status and the current state.
91  switch (status) {
93  // Do not allow more than num_rows VariableStatus::BASIC variables.
94  if (num_basic_variables == num_rows) {
95  VLOG(1) << "Too many basic variables in the warm-start basis."
96  << "Only keeping the first ones as VariableStatus::BASIC.";
97  UpdateToNonBasicStatus(col, DefaultVariableStatus(col));
98  } else {
99  ++num_basic_variables;
100 
101  // Because we just called ResetStatusInfo(), we optimize the call to
102  // UpdateToNonBasicStatus(col) here. In an incremental setting with
103  // almost no work per call, the update of all the DenseBitRow are
104  // visible.
105  variable_status_[col] = VariableStatus::BASIC;
106  is_basic_.Set(col, true);
107  }
108  break;
110  if (lower_bounds_[col] == upper_bounds_[col]) {
112  } else {
113  UpdateToNonBasicStatus(col, lower_bounds_[col] == -kInfinity
114  ? DefaultVariableStatus(col)
115  : status);
116  }
117  break;
119  if (lower_bounds_[col] == upper_bounds_[col]) {
121  } else {
122  UpdateToNonBasicStatus(col, upper_bounds_[col] == kInfinity
123  ? DefaultVariableStatus(col)
124  : status);
125  }
126  break;
127  default:
128  UpdateToNonBasicStatus(col, DefaultVariableStatus(col));
129  }
130  }
131 }
132 
134  ResetStatusInfo();
135  const ColIndex num_cols = lower_bounds_.size();
136  for (ColIndex col(0); col < num_cols; ++col) {
137  UpdateToNonBasicStatus(col, DefaultVariableStatus(col));
138  }
139 }
140 
141 VariableStatus VariablesInfo::DefaultVariableStatus(ColIndex col) const {
142  DCHECK_GE(col, 0);
143  DCHECK_LT(col, lower_bounds_.size());
144  if (lower_bounds_[col] == upper_bounds_[col]) {
146  }
147  if (lower_bounds_[col] == -kInfinity && upper_bounds_[col] == kInfinity) {
148  return VariableStatus::FREE;
149  }
150 
151  // Returns the bound with the lowest magnitude. Note that it must be finite
152  // because the VariableStatus::FREE case was tested earlier.
153  DCHECK(IsFinite(lower_bounds_[col]) || IsFinite(upper_bounds_[col]));
154  return std::abs(lower_bounds_[col]) <= std::abs(upper_bounds_[col])
157 }
158 
160  if (value == boxed_variables_are_relevant_) return;
161  boxed_variables_are_relevant_ = value;
162  if (value) {
163  for (const ColIndex col : non_basic_boxed_variables_) {
164  SetRelevance(col, variable_type_[col] != VariableType::FIXED_VARIABLE);
165  }
166  } else {
167  for (const ColIndex col : non_basic_boxed_variables_) {
168  SetRelevance(col, false);
169  }
170  }
171 }
172 
174  if (in_dual_phase_one_) {
175  // TODO(user): A bit annoying that we need to test this even if we
176  // don't use the dual. But the cost is minimal.
177  if (lower_bounds_[col] != 0.0) lower_bounds_[col] = -kInfinity;
178  if (upper_bounds_[col] != 0.0) upper_bounds_[col] = +kInfinity;
179  variable_type_[col] = ComputeVariableType(col);
180  }
181  variable_status_[col] = VariableStatus::BASIC;
182  is_basic_.Set(col, true);
183  not_basic_.Set(col, false);
184  can_increase_.Set(col, false);
185  can_decrease_.Set(col, false);
186  non_basic_boxed_variables_.Set(col, false);
187  SetRelevance(col, false);
188 }
189 
191  VariableStatus status) {
193  variable_status_[col] = status;
194  is_basic_.Set(col, false);
195  not_basic_.Set(col, true);
196  can_increase_.Set(col, status == VariableStatus::AT_LOWER_BOUND ||
197  status == VariableStatus::FREE);
198  can_decrease_.Set(col, status == VariableStatus::AT_UPPER_BOUND ||
199  status == VariableStatus::FREE);
200 
201  const bool boxed =
202  variable_type_[col] == VariableType::UPPER_AND_LOWER_BOUNDED;
203  non_basic_boxed_variables_.Set(col, boxed);
204  const bool relevance = status != VariableStatus::FIXED_VALUE &&
205  (boxed_variables_are_relevant_ || !boxed);
206  SetRelevance(col, relevance);
207 }
208 
210  return variable_type_;
211 }
212 
214  return variable_status_;
215 }
216 
218  return can_increase_;
219 }
220 
222  return can_decrease_;
223 }
224 
226  return relevance_;
227 }
228 
229 const DenseBitRow& VariablesInfo::GetIsBasicBitRow() const { return is_basic_; }
230 
232  return not_basic_;
233 }
234 
236  return non_basic_boxed_variables_;
237 }
238 
240  return num_entries_in_relevant_columns_;
241 }
242 
243 VariableType VariablesInfo::ComputeVariableType(ColIndex col) const {
244  DCHECK_LE(lower_bounds_[col], upper_bounds_[col]);
245  if (lower_bounds_[col] == -kInfinity) {
246  if (upper_bounds_[col] == kInfinity) {
248  }
250  } else if (upper_bounds_[col] == kInfinity) {
252  } else if (lower_bounds_[col] == upper_bounds_[col]) {
254  } else {
256  }
257 }
258 
259 void VariablesInfo::SetRelevance(ColIndex col, bool relevance) {
260  if (relevance_.IsSet(col) == relevance) return;
261  if (relevance) {
262  relevance_.Set(col);
263  num_entries_in_relevant_columns_ += matrix_.ColumnNumEntries(col);
264  } else {
265  relevance_.Clear(col);
266  num_entries_in_relevant_columns_ -= matrix_.ColumnNumEntries(col);
267  }
268 }
269 
270 // This is really similar to InitializeFromBasisState() but there is less
271 // cases to consider for TransformToDualPhaseIProblem()/EndDualPhaseI().
272 void VariablesInfo::UpdateStatusForNewType(ColIndex col) {
273  switch (variable_status_[col]) {
276  break;
278  if (lower_bounds_[col] == upper_bounds_[col]) {
280  } else if (lower_bounds_[col] == -kInfinity) {
281  UpdateToNonBasicStatus(col, DefaultVariableStatus(col));
282  } else {
283  // TODO(user): This is only needed for boxed variable to update their
284  // relevance. It should probably be done with the type and not the
285  // status update.
286  UpdateToNonBasicStatus(col, variable_status_[col]);
287  }
288  break;
290  if (lower_bounds_[col] == upper_bounds_[col]) {
292  } else if (upper_bounds_[col] == kInfinity) {
293  UpdateToNonBasicStatus(col, DefaultVariableStatus(col));
294  } else {
295  // TODO(user): Same as in the AT_LOWER_BOUND branch above.
296  UpdateToNonBasicStatus(col, variable_status_[col]);
297  }
298  break;
299  default:
300  // TODO(user): boxed variable that become fixed in
301  // TransformToDualPhaseIProblem() will be changed status twice. Once here,
302  // and once when we make them dual feasible according to their reduced
303  // cost. We should probably just do all at once.
304  UpdateToNonBasicStatus(col, DefaultVariableStatus(col));
305  }
306 }
307 
309  Fractional dual_feasibility_tolerance, const DenseRow& reduced_costs) {
310  DCHECK(!in_dual_phase_one_);
311  in_dual_phase_one_ = true;
312  saved_lower_bounds_ = lower_bounds_;
313  saved_upper_bounds_ = upper_bounds_;
314 
315  // Transform the bound and type to get a new problem. If this problem has an
316  // optimal value of 0.0, then the problem is dual feasible. And more
317  // importantly, by keeping the same basis, we have a feasible solution of the
318  // original problem.
319  const ColIndex num_cols = matrix_.num_cols();
320  for (ColIndex col(0); col < num_cols; ++col) {
321  switch (variable_type_[col]) {
322  case VariableType::FIXED_VARIABLE: // ABSL_FALLTHROUGH_INTENDED
324  lower_bounds_[col] = 0.0;
325  upper_bounds_[col] = 0.0;
326  variable_type_[col] = VariableType::FIXED_VARIABLE;
327  break;
329  lower_bounds_[col] = 0.0;
330  upper_bounds_[col] = 1.0;
331  variable_type_[col] = VariableType::UPPER_AND_LOWER_BOUNDED;
332  break;
334  lower_bounds_[col] = -1.0;
335  upper_bounds_[col] = 0.0;
336  variable_type_[col] = VariableType::UPPER_AND_LOWER_BOUNDED;
337  break;
339  lower_bounds_[col] = -1000.0;
340  upper_bounds_[col] = 1000.0;
341  variable_type_[col] = VariableType::UPPER_AND_LOWER_BOUNDED;
342  break;
343  }
344 
345  // Make sure we start with a feasible dual solution.
346  // If the reduced cost is close to zero, we keep the "default" status.
347  if (variable_type_[col] == VariableType::UPPER_AND_LOWER_BOUNDED) {
348  if (reduced_costs[col] > dual_feasibility_tolerance) {
349  variable_status_[col] = VariableStatus::AT_LOWER_BOUND;
350  } else if (reduced_costs[col] < -dual_feasibility_tolerance) {
351  variable_status_[col] = VariableStatus::AT_UPPER_BOUND;
352  }
353  }
354 
355  UpdateStatusForNewType(col);
356  }
357 }
358 
359 void VariablesInfo::EndDualPhaseI(Fractional dual_feasibility_tolerance,
360  const DenseRow& reduced_costs) {
361  DCHECK(in_dual_phase_one_);
362  in_dual_phase_one_ = false;
363  std::swap(saved_lower_bounds_, lower_bounds_);
364  std::swap(saved_upper_bounds_, upper_bounds_);
365 
366  // This is to clear the memory of the saved bounds since it is no longer
367  // needed.
368  DenseRow empty1, empty2;
369  std::swap(empty1, saved_lower_bounds_);
370  std::swap(empty1, saved_upper_bounds_);
371 
372  // Restore the type and update all other fields.
373  const ColIndex num_cols = matrix_.num_cols();
374  for (ColIndex col(0); col < num_cols; ++col) {
375  variable_type_[col] = ComputeVariableType(col);
376 
377  // We make sure that the old fixed variables that are now boxed are dual
378  // feasible.
379  //
380  // TODO(user): When there is a choice, use the previous status that might
381  // have been warm-started ? but then this is not high priority since
382  // warm-starting with a non-dual feasible basis seems unfrequent.
383  if (variable_type_[col] == VariableType::UPPER_AND_LOWER_BOUNDED) {
384  if (reduced_costs[col] > dual_feasibility_tolerance) {
385  variable_status_[col] = VariableStatus::AT_LOWER_BOUND;
386  } else if (reduced_costs[col] < -dual_feasibility_tolerance) {
387  variable_status_[col] = VariableStatus::AT_UPPER_BOUND;
388  }
389  }
390 
391  UpdateStatusForNewType(col);
392  }
393 }
394 
395 } // namespace glop
396 } // namespace operations_research
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:895
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:894
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:897
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:896
#define DCHECK(condition)
Definition: base/logging.h:892
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:893
#define VLOG(verboselevel)
Definition: base/logging.h:986
void ClearAndResize(IndexType size)
Definition: bitset.h:440
void Clear(IndexType i)
Definition: bitset.h:457
void Set(IndexType i)
Definition: bitset.h:495
bool IsSet(IndexType i) const
Definition: bitset.h:485
EntryIndex ColumnNumEntries(ColIndex col) const
Definition: sparse.h:335
const DenseBitRow & GetIsBasicBitRow() const
const DenseBitRow & GetNonBasicBoxedVariables() const
const DenseBitRow & GetCanIncreaseBitRow() const
const DenseBitRow & GetCanDecreaseBitRow() const
const VariableTypeRow & GetTypeRow() const
void EndDualPhaseI(Fractional dual_feasibility_tolerance, const DenseRow &reduced_costs)
void UpdateToNonBasicStatus(ColIndex col, VariableStatus status)
const DenseBitRow & GetNotBasicBitRow() const
VariablesInfo(const CompactSparseMatrix &matrix)
const VariableStatusRow & GetStatusRow() const
const DenseBitRow & GetIsRelevantBitRow() const
void InitializeFromBasisState(ColIndex first_slack, ColIndex num_new_cols, const BasisState &state)
bool LoadBoundsAndReturnTrueIfUnchanged(const DenseRow &new_lower_bounds, const DenseRow &new_upper_bounds)
void TransformToDualPhaseIProblem(Fractional dual_feasibility_tolerance, const DenseRow &reduced_costs)
int64_t value
ColIndex col
Definition: markowitz.cc:183
bool IsFinite(Fractional value)
Definition: lp_types.h:91
RowIndex ColToRowIndex(ColIndex col)
Definition: lp_types.h:52
const double kInfinity
Definition: lp_types.h:84
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
Collection of objects used to extend the Constraint Solver library.