Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
lp_data_utils.cc
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
15
16#include <algorithm>
17#include <cstdlib>
18#include <limits>
19
20#include "absl/log/check.h"
21#include "absl/types/span.h"
22#include "ortools/glop/parameters.pb.h"
28
29namespace operations_research {
30namespace glop {
31
32void ComputeSlackVariablesValues(const LinearProgram& linear_program,
33 DenseRow* values) {
34 DCHECK(values);
35 DCHECK_EQ(linear_program.num_variables(), values->size());
36
37 // If there are no slack variable, we can give up.
38 if (linear_program.GetFirstSlackVariable() == kInvalidCol) return;
39
40 const auto& transposed_matrix = linear_program.GetTransposeSparseMatrix();
41 for (RowIndex row(0); row < linear_program.num_constraints(); row++) {
42 const ColIndex slack_variable = linear_program.GetSlackVariable(row);
43
44 if (slack_variable == kInvalidCol) continue;
45
46 DCHECK_EQ(0.0, linear_program.constraint_lower_bounds()[row]);
47 DCHECK_EQ(0.0, linear_program.constraint_upper_bounds()[row]);
48
49 const RowIndex transposed_slack = ColToRowIndex(slack_variable);
50 Fractional activation = 0.0;
51 // Row in the initial matrix (column in the transposed).
52 const SparseColumn& sparse_row =
53 transposed_matrix.column(RowToColIndex(row));
54 for (const auto& entry : sparse_row) {
55 if (transposed_slack == entry.index()) continue;
56 activation +=
57 (*values)[RowToColIndex(entry.index())] * entry.coefficient();
58 }
59 (*values)[slack_variable] = -activation;
60 }
61}
62
63// This is separated from the LinearProgram class because of a cyclic dependency
64// when scaling as an LP.
66 // Create GlopParameters proto to get default scaling algorithm.
67 GlopParameters params;
68 Scale(lp, scaler, params.scaling_method());
69}
70
71// This is separated from LinearProgram class because of a cyclic dependency
72// when scaling as an LP.
74 GlopParameters::ScalingAlgorithm scaling_method) {
75 scaler->Init(&lp->matrix_);
76 scaler->Scale(
77 scaling_method); // Compute R and C, and replace the matrix A by R.A.C
78 scaler->ScaleRowVector(false,
79 &lp->objective_coefficients_); // oc = oc.C
80 scaler->ScaleRowVector(true,
81 &lp->variable_upper_bounds_); // cl = cl.C^-1
82 scaler->ScaleRowVector(true,
83 &lp->variable_lower_bounds_); // cu = cu.C^-1
84 scaler->ScaleColumnVector(false, &lp->constraint_upper_bounds_); // rl = R.rl
85 scaler->ScaleColumnVector(false, &lp->constraint_lower_bounds_); // ru = R.ru
86 lp->transpose_matrix_is_consistent_ = false;
87}
88
89void LpScalingHelper::Scale(LinearProgram* lp) { Scale(GlopParameters(), lp); }
90
91void LpScalingHelper::Scale(const GlopParameters& params, LinearProgram* lp) {
92 SparseMatrixScaler scaler;
93 ::operations_research::glop::Scale(lp, &scaler, params.scaling_method());
94 bound_scaling_factor_ = 1.0 / lp->ScaleBounds();
95 objective_scaling_factor_ = 1.0 / lp->ScaleObjective(params.cost_scaling());
96
97 matrix_is_scaled_ = true;
98 row_unscaling_factors_ = scaler.row_scales();
99 col_unscaling_factors_ = scaler.col_scales();
100
101 // It is possible the scaler didn't do anything.
102 // we still allocate the vector though since we don't test that below.
103 row_unscaling_factors_.resize(lp->num_constraints(), 1.0);
104 col_unscaling_factors_.resize(lp->num_variables(), 1.0);
105}
106
108 absl::Span<const double> row_factors,
109 absl::Span<const double> col_factors) {
110 matrix_is_scaled_ = true;
111 const RowIndex num_rows(row_factors.size());
112 row_unscaling_factors_.resize(num_rows, 1.0);
113 for (RowIndex row(0); row < num_rows; ++row) {
114 row_unscaling_factors_[row] = 1.0 / row_factors[row.value()];
115 }
116
117 const ColIndex num_cols(col_factors.size());
118 col_unscaling_factors_.resize(num_cols, 1.0);
119 for (ColIndex col(0); col < num_cols; ++col) {
120 col_unscaling_factors_[col] = 1.0 / col_factors[col.value()];
121 }
122}
123
125 matrix_is_scaled_ = false;
126 bound_scaling_factor_ = 1.0;
127 objective_scaling_factor_ = 1.0;
128}
129
131 // During scaling a col was multiplied by ColScalingFactor() and the variable
132 // bounds divided by it.
133 return ColUnscalingFactor(col) * bound_scaling_factor_;
134}
135
137 if (!matrix_is_scaled_) return bound_scaling_factor_;
138 const ColIndex num_cols = col_unscaling_factors_.size();
139 if (col < num_cols) {
140 return col_unscaling_factors_[col] * bound_scaling_factor_;
141 }
142 return row_unscaling_factors_[ColToRowIndex(col - num_cols)] *
143 bound_scaling_factor_;
144}
145
147 Fractional value) const {
148 return value * ColUnscalingFactor(col) * bound_scaling_factor_;
149}
150
152 Fractional value) const {
153 // The reduced cost move like the objective and the col scale.
154 return value / ColUnscalingFactor(col) * objective_scaling_factor_;
155}
156
158 Fractional value) const {
159 // The dual value move like the objective and the inverse of the row scale.
160 return value * (RowUnscalingFactor(row) * objective_scaling_factor_);
161}
162
164 Fractional value) const {
165 // The activity move with the row_scale and the bound_scaling_factor.
166 return value / RowUnscalingFactor(row) * bound_scaling_factor_;
167}
168
170 Fractional value) const {
171 // Just the opposite of ScaleVariableValue().
172 return value / (ColUnscalingFactor(col) * bound_scaling_factor_);
173}
174
176 Fractional value) const {
177 // The reduced cost move like the objective and the col scale.
178 return value * ColUnscalingFactor(col) / objective_scaling_factor_;
179}
180
182 Fractional value) const {
183 // The dual value move like the objective and the inverse of the row scale.
184 return value / (RowUnscalingFactor(row) * objective_scaling_factor_);
185}
186
188 Fractional value) const {
189 // In the scaled domain, we are takeing a sum coeff * scaling * row,
190 // so to get the same effect in the unscaled domain, we want to multiply by
191 // (coeff * scaling).
192 return value / RowUnscalingFactor(row);
193}
194
196 Fractional value) const {
197 // The activity move with the row_scale and the bound_scaling_factor.
198 return value * RowUnscalingFactor(row) / bound_scaling_factor_;
199}
200
202 ColIndex basis_col, ScatteredRow* left_inverse) const {
203 const Fractional global_factor = ColUnscalingFactor(basis_col);
204
205 // We have left_inverse * [RowScale * B * ColScale] = unit_row.
206 if (left_inverse->non_zeros.empty()) {
207 const ColIndex num_rows = left_inverse->values.size();
208 for (ColIndex col(0); col < num_rows; ++col) {
209 left_inverse->values[col] /=
210 RowUnscalingFactor(ColToRowIndex(col)) * global_factor;
211 }
212 } else {
213 for (const ColIndex col : left_inverse->non_zeros) {
214 left_inverse->values[col] /=
215 RowUnscalingFactor(ColToRowIndex(col)) * global_factor;
216 }
217 }
218}
219
221 const RowToColMapping& basis, ColIndex col,
222 ScatteredColumn* right_inverse) const {
223 const Fractional global_factor = 1.0 / ColUnscalingFactor(col);
224
225 // [RowScale * B * BColScale] * inverse = RowScale * column * ColScale.
226 // That is B * (BColScale * inverse) = column * ColScale[col].
227 if (right_inverse->non_zeros.empty()) {
228 const RowIndex num_rows = right_inverse->values.size();
229 for (RowIndex row(0); row < num_rows; ++row) {
230 right_inverse->values[row] /=
231 ColUnscalingFactor(basis[row]) * global_factor;
232 }
233 } else {
234 for (const RowIndex row : right_inverse->non_zeros) {
235 right_inverse->values[row] /=
236 ColUnscalingFactor(basis[row]) * global_factor;
237 }
238 }
239}
240
242 Fractional sum = 0.0;
243 int num_terms = 0;
244 for (const Fractional f : *objective) {
245 if (f == 0) continue;
246 ++num_terms;
247 sum += std::abs(f);
248 }
249 if (num_terms == 0) {
250 objective_scaling_factor_ = 1.0;
251 return;
252 }
253
254 const Fractional average = sum / static_cast<double>(num_terms);
255 objective_scaling_factor_ = 1.0 / average;
256 for (Fractional& f : *objective) {
257 f *= objective_scaling_factor_;
258 }
259}
260
262 DenseRow* lower_bounds) {
263 const double infinity = std::numeric_limits<double>::infinity();
264 Fractional min_magnitude = infinity;
265 Fractional max_magnitude = 0.0;
266 for (const Fractional f : *lower_bounds) {
267 const Fractional m = std::abs(f);
268 if (m == 0 || m == infinity) continue;
269 min_magnitude = std::min(min_magnitude, m);
270 max_magnitude = std::max(max_magnitude, m);
271 }
272 for (const Fractional f : *upper_bounds) {
273 const Fractional m = std::abs(f);
274 if (m == 0 || m == infinity) continue;
275 min_magnitude = std::min(min_magnitude, m);
276 max_magnitude = std::max(max_magnitude, m);
277 }
278
279 bound_scaling_factor_ = 1.0;
280 if (min_magnitude != infinity) {
281 CHECK_LE(min_magnitude, max_magnitude);
282 if (min_magnitude > 1.0) {
283 bound_scaling_factor_ = 1.0 / min_magnitude;
284 } else if (max_magnitude < 1.0) {
285 bound_scaling_factor_ = 1.0 / max_magnitude;
286 }
287 }
288
289 if (bound_scaling_factor_ == 1.0) return;
290 for (Fractional& f : *lower_bounds) {
291 f *= bound_scaling_factor_;
292 }
293 for (Fractional& f : *upper_bounds) {
294 f *= bound_scaling_factor_;
295 }
296}
297
298} // namespace glop
299} // namespace operations_research
const DenseColumn & constraint_lower_bounds() const
Definition lp_data.h:223
const SparseMatrix & GetTransposeSparseMatrix() const
Definition lp_data.cc:385
ColIndex GetSlackVariable(RowIndex row) const
Definition lp_data.cc:766
Fractional ScaleObjective(GlopParameters::CostScalingAlgorithm method)
Definition lp_data.cc:1199
const DenseColumn & constraint_upper_bounds() const
Definition lp_data.h:226
ColIndex num_variables() const
Returns the number of variables.
Definition lp_data.h:213
RowIndex num_constraints() const
Returns the number of constraints.
Definition lp_data.h:216
void UnscaleUnitRowLeftSolve(ColIndex basis_col, ScatteredRow *left_inverse) const
void AverageCostScaling(DenseRow *objective)
Extra scaling function, to scale objective/bounds.
void ConfigureFromFactors(absl::Span< const double > row_factors, absl::Span< const double > col_factors)
Fractional VariableScalingFactorWithSlack(ColIndex col) const
Fractional ScaleVariableValue(ColIndex col, Fractional value) const
Transforms value from unscaled domain to the scaled one.
Fractional UnscaleDualValue(RowIndex row, Fractional value) const
void ContainOneBoundScaling(DenseRow *upper_bounds, DenseRow *lower_bounds)
Fractional UnscaleConstraintActivity(RowIndex row, Fractional value) const
void Clear()
Clear all scaling coefficients.
Fractional UnscaleLeftSolveValue(RowIndex row, Fractional value) const
Fractional ScaleDualValue(RowIndex row, Fractional value) const
void Scale(LinearProgram *lp)
Scale the given LP.
Fractional ScaleConstraintActivity(RowIndex row, Fractional value) const
Fractional UnscaleReducedCost(ColIndex col, Fractional value) const
Fractional VariableScalingFactor(ColIndex col) const
Fractional UnscaleVariableValue(ColIndex col, Fractional value) const
Transforms corresponding value from the scaled domain to the original one.
Fractional ScaleReducedCost(ColIndex col, Fractional value) const
void UnscaleColumnRightSolve(const RowToColMapping &basis, ColIndex col, ScatteredColumn *right_inverse) const
Unscale a col vector v such that B.c = matrix_column_col.
const DenseRow & col_scales() const
These are unscaling factor, same as [Col|Row]UnscalingFactor().
void ScaleColumnVector(bool up, DenseColumn *column_vector) const
void ScaleRowVector(bool up, DenseRow *row_vector) const
void Scale(GlopParameters::ScalingAlgorithm method)
Scales the matrix.
constexpr ColIndex kInvalidCol(-1)
StrictITIVector< RowIndex, ColIndex > RowToColMapping
Definition lp_types.h:394
void ComputeSlackVariablesValues(const LinearProgram &linear_program, DenseRow *values)
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
Definition lp_types.h:54
RowIndex ColToRowIndex(ColIndex col)
Get the RowIndex corresponding to the row # col.
Definition lp_types.h:57
void Scale(LinearProgram *lp, SparseMatrixScaler *scaler)
StrictITIVector< ColIndex, Fractional > DenseRow
Row-vector types. Row-vector types are indexed by a column index.
Definition lp_types.h:351
In SWIG mode, we don't want anything besides these top-level includes.
StrictITIVector< Index, Fractional > values