Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
variable_values.cc
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
15
16#include <algorithm>
17#include <cstdlib>
18#include <vector>
19
20#include "absl/base/attributes.h"
21#include "absl/log/check.h"
22#include "absl/types/span.h"
26#include "ortools/glop/parameters.pb.h"
33#include "ortools/util/stats.h"
34
35namespace operations_research {
36namespace glop {
37
39 const CompactSparseMatrix& matrix,
40 const RowToColMapping& basis,
41 const VariablesInfo& variables_info,
42 const BasisFactorization& basis_factorization,
43 DualEdgeNorms* dual_edge_norms,
44 DynamicMaximum<RowIndex>* dual_prices)
45 : parameters_(parameters),
46 matrix_(matrix),
47 basis_(basis),
48 variables_info_(variables_info),
49 basis_factorization_(basis_factorization),
50 dual_edge_norms_(dual_edge_norms),
51 dual_prices_(dual_prices),
52 stats_("VariableValues") {}
53
55 SCOPED_TIME_STAT(&stats_);
56 const DenseRow& lower_bounds = variables_info_.GetVariableLowerBounds();
57 const DenseRow& upper_bounds = variables_info_.GetVariableUpperBounds();
58 variable_values_.resize(matrix_.num_cols(), 0.0);
59 switch (variables_info_.GetStatusRow()[col]) {
61 DCHECK_NE(-kInfinity, lower_bounds[col]);
62 DCHECK_EQ(lower_bounds[col], upper_bounds[col]);
63 variable_values_[col] = lower_bounds[col];
64 break;
66 DCHECK_NE(-kInfinity, lower_bounds[col]);
67 variable_values_[col] = lower_bounds[col];
68 break;
70 DCHECK_NE(kInfinity, upper_bounds[col]);
71 variable_values_[col] = upper_bounds[col];
72 break;
74 LOG(DFATAL) << "SetNonBasicVariableValueFromStatus() shouldn't "
75 << "be called on a FREE variable.";
76 break;
78 LOG(DFATAL) << "SetNonBasicVariableValueFromStatus() shouldn't "
79 << "be called on a BASIC variable.";
80 break;
81 }
82 // Note that there is no default value in the switch() statement above to
83 // get a compile-time error if a value is missing.
84}
85
87 const DenseRow& free_initial_value) {
88 const DenseRow& lower_bounds = variables_info_.GetVariableLowerBounds();
89 const DenseRow& upper_bounds = variables_info_.GetVariableUpperBounds();
90 const VariableStatusRow& statuses = variables_info_.GetStatusRow();
91 const ColIndex num_cols = matrix_.num_cols();
92 variable_values_.resize(num_cols, 0.0);
93 for (ColIndex col(0); col < num_cols; ++col) {
94 switch (statuses[col]) {
96 ABSL_FALLTHROUGH_INTENDED;
98 variable_values_[col] = lower_bounds[col];
99 break;
101 variable_values_[col] = upper_bounds[col];
102 break;
104 variable_values_[col] =
105 col < free_initial_value.size() ? free_initial_value[col] : 0.0;
106 break;
108 break;
109 }
110 }
111}
112
114 SCOPED_TIME_STAT(&stats_);
115 DCHECK(basis_factorization_.IsRefactorized());
116 const RowIndex num_rows = matrix_.num_rows();
117 scratchpad_.non_zeros.clear();
118 scratchpad_.values.AssignToZero(num_rows);
119 for (const ColIndex col : variables_info_.GetNotBasicBitRow()) {
120 const Fractional value = variable_values_[col];
121 matrix_.ColumnAddMultipleToDenseColumn(col, -value, &scratchpad_.values);
122 }
123 basis_factorization_.RightSolve(&scratchpad_);
124 for (RowIndex row(0); row < num_rows; ++row) {
125 variable_values_[basis_[row]] = scratchpad_[row];
126 }
127
128 // This makes sure that they will be recomputed if needed.
129 dual_prices_->Clear();
130}
131
133 SCOPED_TIME_STAT(&stats_);
134 scratchpad_.non_zeros.clear();
135 scratchpad_.values.AssignToZero(matrix_.num_rows());
136 const ColIndex num_cols = matrix_.num_cols();
137 for (ColIndex col(0); col < num_cols; ++col) {
138 const Fractional value = variable_values_[col];
139 matrix_.ColumnAddMultipleToDenseColumn(col, value, &scratchpad_.values);
140 }
141 return InfinityNorm(scratchpad_.values);
142}
143
145 SCOPED_TIME_STAT(&stats_);
146 Fractional primal_infeasibility = 0.0;
147 const ColIndex num_cols = matrix_.num_cols();
148 const DenseRow::ConstView values = variable_values_.const_view();
150 variables_info_.GetVariableLowerBounds().const_view();
152 variables_info_.GetVariableUpperBounds().const_view();
153 for (ColIndex col(0); col < num_cols; ++col) {
154 const Fractional infeasibility =
155 GetColInfeasibility(col, values, lower_bounds, upper_bounds);
156 primal_infeasibility = std::max(primal_infeasibility, infeasibility);
157 }
158 return primal_infeasibility;
159}
160
162 SCOPED_TIME_STAT(&stats_);
163 Fractional sum = 0.0;
164 const ColIndex num_cols = matrix_.num_cols();
165 const DenseRow::ConstView values = variable_values_.const_view();
167 variables_info_.GetVariableLowerBounds().const_view();
169 variables_info_.GetVariableUpperBounds().const_view();
170 for (ColIndex col(0); col < num_cols; ++col) {
171 const Fractional infeasibility =
172 GetColInfeasibility(col, values, lower_bounds, upper_bounds);
173 sum += std::max(0.0, infeasibility);
174 }
175 return sum;
176}
177
179 ColIndex entering_col, Fractional step) {
180 SCOPED_TIME_STAT(&stats_);
181 DCHECK(IsFinite(step));
182
183 // Note(user): Some positions are ignored during the primal ratio test:
184 // - The rows for which direction_[row] < tolerance.
185 // - The non-zeros of direction_ignored_position_ in case of degeneracy.
186 // Such positions may result in basic variables going out of their bounds by
187 // more than the allowed tolerance. We could choose not to update these
188 // variables or not make them take out-of-bound values, but this would
189 // introduce artificial errors.
190
191 // Note that there is no need to call variables_info_.Update() on basic
192 // variables when they change values. Note also that the status of
193 // entering_col will be updated later.
194 auto basis = basis_.const_view();
195 auto values = variable_values_.view();
196 for (const auto e : direction) {
197 const ColIndex col = basis[e.row()];
198 values[col] -= e.coefficient() * step;
199 }
200 values[entering_col] += step;
201}
202
204 absl::Span<const ColIndex> cols_to_update, bool update_basic_variables) {
205 SCOPED_TIME_STAT(&stats_);
206 if (!update_basic_variables) {
207 for (ColIndex col : cols_to_update) {
209 }
210 return;
211 }
212
213 const RowIndex num_rows = matrix_.num_rows();
214 initially_all_zero_scratchpad_.values.resize(num_rows, 0.0);
215 DCHECK(IsAllZero(initially_all_zero_scratchpad_.values));
216 initially_all_zero_scratchpad_.ClearSparseMask();
217 bool use_dense = false;
218 for (ColIndex col : cols_to_update) {
219 const Fractional old_value = variable_values_[col];
221 if (use_dense) {
223 col, variable_values_[col] - old_value,
224 &initially_all_zero_scratchpad_.values);
225 } else {
227 col, variable_values_[col] - old_value,
228 &initially_all_zero_scratchpad_);
229 use_dense = initially_all_zero_scratchpad_.ShouldUseDenseIteration();
230 }
231 }
232 initially_all_zero_scratchpad_.ClearSparseMask();
233 initially_all_zero_scratchpad_.ClearNonZerosIfTooDense();
234
235 basis_factorization_.RightSolve(&initially_all_zero_scratchpad_);
236 if (initially_all_zero_scratchpad_.non_zeros.empty()) {
237 for (RowIndex row(0); row < num_rows; ++row) {
238 variable_values_[basis_[row]] -= initially_all_zero_scratchpad_[row];
239 }
240 initially_all_zero_scratchpad_.values.AssignToZero(num_rows);
242 return;
243 }
244
245 for (const auto e : initially_all_zero_scratchpad_) {
246 variable_values_[basis_[e.row()]] -= e.coefficient();
247 initially_all_zero_scratchpad_[e.row()] = 0.0;
248 }
249 UpdateDualPrices(initially_all_zero_scratchpad_.non_zeros);
250 initially_all_zero_scratchpad_.non_zeros.clear();
251}
252
253void VariableValues::RecomputeDualPrices(bool put_more_importance_on_norm) {
254 SCOPED_TIME_STAT(&stats_);
255 const RowIndex num_rows = matrix_.num_rows();
256 dual_prices_->ClearAndResize(num_rows);
257 dual_prices_->StartDenseUpdates();
258
259 put_more_importance_on_norm_ = put_more_importance_on_norm;
260 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
261 const DenseColumn::ConstView squared_norms =
262 dual_edge_norms_->GetEdgeSquaredNorms();
263 const RowToColMapping::ConstView basis = basis_.const_view();
264 const DenseRow::ConstView values = variable_values_.const_view();
266 variables_info_.GetVariableLowerBounds().const_view();
268 variables_info_.GetVariableUpperBounds().const_view();
269 if (put_more_importance_on_norm) {
270 for (RowIndex row(0); row < num_rows; ++row) {
271 const ColIndex col = basis[row];
272 const Fractional infeasibility =
273 GetColInfeasibility(col, values, lower_bounds, upper_bounds);
274 if (infeasibility > tolerance) {
275 dual_prices_->DenseAddOrUpdate(
276 row, std::abs(infeasibility) / squared_norms[row]);
277 }
278 }
279 } else {
280 for (RowIndex row(0); row < num_rows; ++row) {
281 const ColIndex col = basis[row];
282 const Fractional infeasibility =
283 GetColInfeasibility(col, values, lower_bounds, upper_bounds);
284 if (infeasibility > tolerance) {
285 dual_prices_->DenseAddOrUpdate(
286 row, Square(infeasibility) / squared_norms[row]);
287 }
288 }
289 }
290}
291
292void VariableValues::UpdateDualPrices(absl::Span<const RowIndex> rows) {
293 if (dual_prices_->Size() != matrix_.num_rows()) {
294 RecomputeDualPrices(put_more_importance_on_norm_);
295 return;
296 }
297
298 // Note(user): this is the same as the code in RecomputeDualPrices(), but we
299 // do need the clear part.
300 SCOPED_TIME_STAT(&stats_);
301 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
302 const RowToColMapping::ConstView basis = basis_.const_view();
303 const DenseColumn::ConstView squared_norms =
304 dual_edge_norms_->GetEdgeSquaredNorms();
305 const DenseRow::ConstView values = variable_values_.const_view();
307 variables_info_.GetVariableLowerBounds().const_view();
309 variables_info_.GetVariableUpperBounds().const_view();
310 if (put_more_importance_on_norm_) {
311 for (const RowIndex row : rows) {
312 const ColIndex col = basis[row];
313 const Fractional infeasibility =
314 GetColInfeasibility(col, values, lower_bounds, upper_bounds);
315 if (infeasibility > tolerance) {
316 dual_prices_->AddOrUpdate(row,
317 std::abs(infeasibility) / squared_norms[row]);
318 } else {
319 dual_prices_->Remove(row);
320 }
321 }
322 } else {
323 for (const RowIndex row : rows) {
324 const ColIndex col = basis[row];
325 const Fractional infeasibility =
326 GetColInfeasibility(col, values, lower_bounds, upper_bounds);
327 if (infeasibility > tolerance) {
328 dual_prices_->AddOrUpdate(row,
329 Square(infeasibility) / squared_norms[row]);
330 } else {
331 dual_prices_->Remove(row);
332 }
333 }
334 }
335}
336
337} // namespace glop
338} // namespace operations_research
void RightSolve(ScatteredColumn *d) const
Right solves the system B.d = a where the input is the initial value of d.
bool IsRefactorized() const
Returns true if the factorization was just recomputed.
void ColumnAddMultipleToDenseColumn(ColIndex col, Fractional multiplier, DenseColumn::View dense_column) const
Definition sparse.h:441
void ColumnAddMultipleToSparseScatteredColumn(ColIndex col, Fractional multiplier, ScatteredColumn *column) const
Definition sparse.h:459
DenseColumn::ConstView GetEdgeSquaredNorms()
void DenseAddOrUpdate(Index position, Fractional value)
Definition pricing.h:181
void Remove(Index position)
Removes the given index from the set of candidates.
Definition pricing.h:169
void Clear()
Returns the current size n that was used in the last ClearAndResize().
Definition pricing.h:94
void AddOrUpdate(Index position, Fractional value)
Definition pricing.h:190
void UpdateOnPivoting(const ScatteredColumn &direction, ColIndex entering_col, Fractional step)
void UpdateGivenNonBasicVariables(absl::Span< const ColIndex > cols_to_update, bool update_basic_variables)
VariableValues(const GlopParameters &parameters, const CompactSparseMatrix &matrix, const RowToColMapping &basis, const VariablesInfo &variables_info, const BasisFactorization &basis_factorization, DualEdgeNorms *dual_edge_norms, DynamicMaximum< RowIndex > *dual_prices)
void ResetAllNonBasicVariableValues(const DenseRow &free_initial_values)
void RecomputeDualPrices(bool put_more_importance_on_norm=false)
void UpdateDualPrices(absl::Span< const RowIndex > row)
const VariableStatusRow & GetStatusRow() const
const DenseRow & GetVariableLowerBounds() const
Returns the variable bounds.
const DenseRow & GetVariableUpperBounds() const
const DenseBitRow & GetNotBasicBitRow() const
SatParameters parameters
int64_t value
ColIndex col
Definition markowitz.cc:187
RowIndex row
Definition markowitz.cc:186
Fractional Square(Fractional f)
Definition lp_utils.h:41
bool IsAllZero(const Container &input)
Returns true if the given Fractional container is all zeros.
Definition lp_utils.h:229
bool IsFinite(Fractional value)
Definition lp_types.h:96
Fractional InfinityNorm(const DenseColumn &v)
Returns the maximum of the coefficients of 'v'.
Definition lp_utils.cc:151
In SWIG mode, we don't want anything besides these top-level includes.
std::vector< double > lower_bounds
Definition lp_utils.cc:746
std::vector< double > upper_bounds
Definition lp_utils.cc:747
#define SCOPED_TIME_STAT(stats)
Definition stats.h:418
void ClearNonZerosIfTooDense(double ratio_for_using_dense_representation)
void ClearSparseMask()
Efficiently clears the is_non_zero vector.
StrictITIVector< Index, Fractional > values
bool ShouldUseDenseIteration(double ratio_for_using_dense_representation) const