20 #include "absl/strings/str_format.h"
31 : matrix_(nullptr), row_scale_(), col_scale_() {}
48 return row < row_scale_.
size() ? row_scale_[
row] : 1.0;
53 return col < col_scale_.
size() ? col_scale_[
col] : 1.0;
64 std::string SparseMatrixScaler::DebugInformationString()
const {
73 const Fractional dynamic_range = max_magnitude / min_magnitude;
74 std::string output = absl::StrFormat(
75 "Min magnitude = %g, max magnitude = %g\n"
76 "Dynamic range = %g\n"
78 "Minimum row scale = %g, maximum row scale = %g\n"
79 "Minimum col scale = %g, maximum col scale = %g\n",
80 min_magnitude, max_magnitude, dynamic_range,
82 *std::min_element(row_scale_.
begin(), row_scale_.
end()),
83 *std::max_element(row_scale_.
begin(), row_scale_.
end()),
84 *std::min_element(col_scale_.
begin(), col_scale_.
end()),
85 *std::max_element(col_scale_.
begin(), col_scale_.
end()));
96 DCHECK(matrix_ !=
nullptr);
100 if (min_magnitude == 0.0) {
104 VLOG(1) <<
"Before scaling:\n" << DebugInformationString();
105 if (method == GlopParameters::LINEAR_PROGRAM) {
108 if (lp_status.
ok()) {
116 const Fractional dynamic_range = max_magnitude / min_magnitude;
117 const Fractional kMaxDynamicRangeForGeometricScaling = 1e20;
118 if (dynamic_range < kMaxDynamicRangeForGeometricScaling) {
119 const int kScalingIterations = 4;
121 for (
int iteration = 0; iteration < kScalingIterations; ++iteration) {
125 VLOG(1) <<
"Geometric scaling iteration " << iteration
126 <<
". Rows scaled = " << num_rows_scaled
127 <<
", columns scaled = " << num_cols_scaled <<
"\n";
128 VLOG(1) << DebugInformationString();
129 if (variance < kVarianceThreshold ||
130 (num_cols_scaled == 0 && num_rows_scaled == 0)) {
137 VLOG(1) <<
"Equilibration step: Rows scaled = " << rows_equilibrated
138 <<
", columns scaled = " << cols_equilibrated <<
"\n";
139 VLOG(1) << DebugInformationString();
149 for (I i(0); i < size; ++i) {
150 (*vector_to_scale)[i] *= scale[i];
153 for (I i(0); i < size; ++i) {
154 (*vector_to_scale)[i] /= scale[i];
159 template <
typename InputIndexType>
160 ColIndex CreateOrGetScaleIndex(
161 InputIndexType num, LinearProgram* lp,
163 if ((*scale_var_indices)[num] == -1) {
164 (*scale_var_indices)[num] = lp->CreateNewVariable();
166 return (*scale_var_indices)[num];
171 DCHECK(row_vector !=
nullptr);
172 ScaleVector(col_scale_, up, row_vector);
177 DCHECK(column_vector !=
nullptr);
178 ScaleVector(row_scale_, up, column_vector);
182 DCHECK(matrix_ !=
nullptr);
186 const ColIndex num_cols = matrix_->
num_cols();
187 for (ColIndex
col(0);
col < num_cols; ++
col) {
189 const Fractional magnitude = fabs(e.coefficient());
190 if (magnitude != 0.0) {
191 sigma_square += magnitude * magnitude;
192 sigma_abs += magnitude;
197 if (n == 0.0)
return 0.0;
203 return (sigma_square - sigma_abs * sigma_abs / n) / n;
212 DCHECK(matrix_ !=
nullptr);
215 const ColIndex num_cols = matrix_->
num_cols();
216 for (ColIndex
col(0);
col < num_cols; ++
col) {
218 const Fractional magnitude = fabs(e.coefficient());
219 const RowIndex
row = e.row();
220 if (magnitude != 0.0) {
226 const RowIndex num_rows = matrix_->
num_rows();
228 for (RowIndex
row(0);
row < num_rows; ++
row) {
229 if (max_in_row[
row] == 0.0) {
230 scaling_factor[
row] = 1.0;
233 scaling_factor[
row] = sqrt(max_in_row[
row] * min_in_row[
row]);
236 return ScaleMatrixRows(scaling_factor);
240 DCHECK(matrix_ !=
nullptr);
241 ColIndex num_cols_scaled(0);
242 const ColIndex num_cols = matrix_->
num_cols();
243 for (ColIndex
col(0);
col < num_cols; ++
col) {
247 const Fractional magnitude = fabs(e.coefficient());
248 if (magnitude != 0.0) {
249 max_in_col =
std::max(max_in_col, magnitude);
250 min_in_col =
std::min(min_in_col, magnitude);
253 if (max_in_col != 0.0) {
255 ScaleMatrixColumn(
col, factor);
259 return num_cols_scaled;
268 DCHECK(matrix_ !=
nullptr);
269 const RowIndex num_rows = matrix_->
num_rows();
271 const ColIndex num_cols = matrix_->
num_cols();
272 for (ColIndex
col(0);
col < num_cols; ++
col) {
274 const Fractional magnitude = fabs(e.coefficient());
275 if (magnitude != 0.0) {
276 const RowIndex
row = e.row();
281 for (RowIndex
row(0);
row < num_rows; ++
row) {
282 if (max_magnitude[
row] == 0.0) {
283 max_magnitude[
row] = 1.0;
286 return ScaleMatrixRows(max_magnitude);
290 DCHECK(matrix_ !=
nullptr);
291 ColIndex num_cols_scaled(0);
292 const ColIndex num_cols = matrix_->
num_cols();
293 for (ColIndex
col(0);
col < num_cols; ++
col) {
295 if (max_magnitude != 0.0) {
296 ScaleMatrixColumn(
col, max_magnitude);
300 return num_cols_scaled;
303 RowIndex SparseMatrixScaler::ScaleMatrixRows(
const DenseColumn& factors) {
305 DCHECK(matrix_ !=
nullptr);
306 const RowIndex num_rows = matrix_->
num_rows();
308 RowIndex num_rows_scaled(0);
309 for (RowIndex
row(0);
row < num_rows; ++
row) {
314 row_scale_[
row] *= factor;
318 const ColIndex num_cols = matrix_->
num_cols();
319 for (ColIndex
col(0);
col < num_cols; ++
col) {
321 if (column !=
nullptr) {
326 return num_rows_scaled;
329 void SparseMatrixScaler::ScaleMatrixColumn(ColIndex
col,
Fractional factor) {
331 DCHECK(matrix_ !=
nullptr);
332 col_scale_[
col] *= factor;
336 if (column !=
nullptr) {
343 DCHECK(matrix_ !=
nullptr);
344 const ColIndex num_cols = matrix_->
num_cols();
345 for (ColIndex
col(0);
col < num_cols; ++
col) {
350 if (column !=
nullptr) {
358 DCHECK(matrix_ !=
nullptr);
360 auto linear_program = absl::make_unique<LinearProgram>();
361 GlopParameters params;
362 auto simplex = absl::make_unique<RevisedSimplex>();
363 simplex->SetParameters(params);
387 const ColIndex beta = linear_program->CreateNewVariable();
390 linear_program->SetObjectiveCoefficient(beta, 1);
392 const ColIndex num_cols = matrix_->
num_cols();
393 for (ColIndex
col(0);
col < num_cols; ++
col) {
396 const ColIndex column_scale = CreateOrGetScaleIndex<ColIndex>(
397 col, linear_program.get(), &col_scale_var_indices);
404 const ColIndex row_scale = CreateOrGetScaleIndex<RowIndex>(
405 row, linear_program.get(), &row_scale_var_indices);
416 const RowIndex positive_constraint =
417 linear_program->CreateNewConstraint();
419 linear_program->SetConstraintBounds(positive_constraint, -log_magnitude,
422 linear_program->SetCoefficient(positive_constraint, row_scale, 1);
424 linear_program->SetCoefficient(positive_constraint, column_scale, 1);
426 linear_program->SetCoefficient(positive_constraint, beta, 1);
429 const RowIndex negative_constraint =
430 linear_program->CreateNewConstraint();
432 linear_program->SetConstraintBounds(negative_constraint, -
kInfinity,
435 linear_program->SetCoefficient(negative_constraint, row_scale, 1);
437 linear_program->SetCoefficient(negative_constraint, column_scale, 1);
439 linear_program->SetCoefficient(negative_constraint, beta, -1);
444 linear_program->AddSlackVariablesWhereNecessary(
false);
445 const Status simplex_status =
447 if (!simplex_status.
ok()) {
448 return simplex_status;
454 const ColIndex num_cols = matrix_->
num_cols();
455 for (ColIndex
col(0);
col < num_cols; ++
col) {
457 exp2(-simplex->GetVariableValue(CreateOrGetScaleIndex<ColIndex>(
458 col, linear_program.get(), &col_scale_var_indices)));
459 ScaleMatrixColumn(
col, column_scale);
461 const RowIndex num_rows = matrix_->
num_rows();
463 for (RowIndex
row(0);
row < num_rows; ++
row) {
465 exp2(-simplex->GetVariableValue(CreateOrGetScaleIndex<RowIndex>(
466 row, linear_program.get(), &row_scale_var_indices)));
468 ScaleMatrixRows(row_scale);