Google OR-Tools v9.9
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
dual_edge_norms.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 <cstdlib>
17
19
20namespace operations_research {
21namespace glop {
22
24 : basis_factorization_(basis_factorization),
25 recompute_edge_squared_norms_(true) {}
26
28 return recompute_edge_squared_norms_;
29}
30
31void DualEdgeNorms::Clear() { recompute_edge_squared_norms_ = true; }
32
33void DualEdgeNorms::ResizeOnNewRows(RowIndex new_size) {
34 edge_squared_norms_.resize(new_size, 1.0);
35}
36
38 if (recompute_edge_squared_norms_) ComputeEdgeSquaredNorms();
39 return edge_squared_norms_.const_view();
40}
41
43 const ColumnPermutation& col_perm) {
44 if (recompute_edge_squared_norms_) return;
45 ApplyColumnPermutationToRowIndexedVector(col_perm, &edge_squared_norms_,
46 &tmp_edge_squared_norms_);
47}
48
49bool DualEdgeNorms::TestPrecision(RowIndex leaving_row,
50 const ScatteredRow& unit_row_left_inverse) {
51 // This should only be true if we do not use steepest edge.
52 if (recompute_edge_squared_norms_) return true;
53
54 // ||unit_row_left_inverse||^2 is the same as
55 // edge_squared_norms_[leaving_row], but with a better precision. If the
56 // difference between the two is too large, we trigger a full recomputation.
57 const Fractional leaving_squared_norm =
58 SquaredNorm(TransposedView(unit_row_left_inverse));
59 const Fractional old_squared_norm = edge_squared_norms_[leaving_row];
60 const Fractional estimated_edge_norms_accuracy =
61 (sqrt(leaving_squared_norm) - sqrt(old_squared_norm)) /
62 sqrt(leaving_squared_norm);
63 stats_.edge_norms_accuracy.Add(estimated_edge_norms_accuracy);
64
65 if (std::abs(estimated_edge_norms_accuracy) >
66 parameters_.recompute_edges_norm_threshold()) {
67 VLOG(1) << "Recomputing edge norms: " << sqrt(leaving_squared_norm)
68 << " vs " << sqrt(old_squared_norm);
69 recompute_edge_squared_norms_ = true;
70 }
71
72 // We just do not want to pivot on a position with an under-estimated norm.
73 edge_squared_norms_[leaving_row] = leaving_squared_norm;
74 const bool result = old_squared_norm > 0.25 * leaving_squared_norm;
75 if (!result) {
76 VLOG(1) << "Recomputing leaving row. Norm was " << sqrt(old_squared_norm)
77 << " vs precise version " << sqrt(leaving_squared_norm);
78 }
79 return result;
80}
81
83 ColIndex entering_col, RowIndex leaving_row,
84 const ScatteredColumn& direction,
85 const ScatteredRow& unit_row_left_inverse) {
86 // No need to update if we will recompute it from scratch later.
87 if (recompute_edge_squared_norms_) return;
88 const DenseColumn& tau = ComputeTau(TransposedView(unit_row_left_inverse));
89 SCOPED_TIME_STAT(&stats_);
90
91 const Fractional pivot = direction[leaving_row];
92 const Fractional new_leaving_squared_norm =
93 edge_squared_norms_[leaving_row] / Square(pivot);
94
95 // Update the norm.
96 int stat_lower_bounded_norms = 0;
97 auto output = edge_squared_norms_.view();
98 for (const auto e : direction) {
99 // Note that the update formula used is important to maximize the precision.
100 // See Koberstein's PhD section 8.2.2.1.
101 output[e.row()] +=
102 e.coefficient() * (e.coefficient() * new_leaving_squared_norm -
103 2.0 / pivot * tau[e.row()]);
104
105 // Avoid 0.0 norms (The 1e-4 is the value used by Koberstein).
106 // TODO(user): use a more precise lower bound depending on the column norm?
107 // We can do that with Cauchy-Swartz inequality:
108 // (edge . leaving_column)^2 = 1.0 < ||edge||^2 * ||leaving_column||^2
109 const Fractional kLowerBound = 1e-4;
110 if (output[e.row()] < kLowerBound) {
111 if (e.row() == leaving_row) continue;
112 output[e.row()] = kLowerBound;
113 ++stat_lower_bounded_norms;
114 }
115 }
116 output[leaving_row] = new_leaving_squared_norm;
117 IF_STATS_ENABLED(stats_.lower_bounded_norms.Add(stat_lower_bounded_norms));
118}
119
120void DualEdgeNorms::ComputeEdgeSquaredNorms() {
121 SCOPED_TIME_STAT(&stats_);
122
123 // Since we will do a lot of inversions, it is better to be as efficient and
124 // precise as possible by having a refactorized basis.
125 DCHECK(basis_factorization_.IsRefactorized());
126 const RowIndex num_rows = basis_factorization_.GetNumberOfRows();
127 edge_squared_norms_.resize(num_rows, 0.0);
128 for (RowIndex row(0); row < num_rows; ++row) {
129 edge_squared_norms_[row] = basis_factorization_.DualEdgeSquaredNorm(row);
130 }
131 recompute_edge_squared_norms_ = false;
132}
133
134const DenseColumn& DualEdgeNorms::ComputeTau(
135 const ScatteredColumn& unit_row_left_inverse) {
136 SCOPED_TIME_STAT(&stats_);
137 const DenseColumn& result =
138 basis_factorization_.RightSolveForTau(unit_row_left_inverse);
139 IF_STATS_ENABLED(stats_.tau_density.Add(Density(Transpose(result))));
140 return result;
141}
142
143} // namespace glop
144} // namespace operations_research
const DenseColumn & RightSolveForTau(const ScatteredColumn &a) const
bool IsRefactorized() const
Returns true if the factorization was just recomputed.
RowIndex GetNumberOfRows() const
Return the number of rows in the basis.
DenseColumn::ConstView GetEdgeSquaredNorms()
DualEdgeNorms(const BasisFactorization &basis_factorization)
Takes references to the linear program data we need.
void UpdateDataOnBasisPermutation(const ColumnPermutation &col_perm)
Updates the norms if the columns of the basis where permuted.
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, const ScatteredRow &unit_row_left_inverse)
bool TestPrecision(RowIndex leaving_row, const ScatteredRow &unit_row_left_inverse)
RowIndex row
Definition markowitz.cc:186
double Density(const DenseRow &row)
Definition lp_utils.cc:122
Fractional Square(Fractional f)
Definition lp_utils.h:41
const DenseRow & Transpose(const DenseColumn &col)
Similar comment as the other Transpose() implementation above.
Definition lp_utils.h:197
Fractional SquaredNorm(const SparseColumn &v)
Definition lp_utils.cc:35
StrictITIVector< RowIndex, Fractional > DenseColumn
Column-vector types. Column-vector types are indexed by a row index.
Definition lp_types.h:376
void ApplyColumnPermutationToRowIndexedVector(const Permutation< ColIndex > &col_perm, RowIndexedVector *v)
const ScatteredRow & TransposedView(const ScatteredColumn &c)
In SWIG mode, we don't want anything besides these top-level includes.
#define IF_STATS_ENABLED(instructions)
Definition stats.h:416
#define SCOPED_TIME_STAT(stats)
Definition stats.h:417