forked from google/or-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dual_edge_norms.cc
128 lines (110 loc) · 4.92 KB
/
dual_edge_norms.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright 2010-2021 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ortools/glop/dual_edge_norms.h"
#include "ortools/lp_data/lp_utils.h"
namespace operations_research {
namespace glop {
DualEdgeNorms::DualEdgeNorms(const BasisFactorization& basis_factorization)
: basis_factorization_(basis_factorization),
recompute_edge_squared_norms_(true) {}
bool DualEdgeNorms::NeedsBasisRefactorization() const {
return recompute_edge_squared_norms_;
}
void DualEdgeNorms::Clear() { recompute_edge_squared_norms_ = true; }
void DualEdgeNorms::ResizeOnNewRows(RowIndex new_size) {
edge_squared_norms_.resize(new_size, 1.0);
}
const DenseColumn& DualEdgeNorms::GetEdgeSquaredNorms() {
if (recompute_edge_squared_norms_) ComputeEdgeSquaredNorms();
return edge_squared_norms_;
}
void DualEdgeNorms::UpdateDataOnBasisPermutation(
const ColumnPermutation& col_perm) {
if (recompute_edge_squared_norms_) return;
ApplyColumnPermutationToRowIndexedVector(col_perm, &edge_squared_norms_);
}
void DualEdgeNorms::UpdateBeforeBasisPivot(
ColIndex entering_col, RowIndex leaving_row,
const ScatteredColumn& direction,
const ScatteredRow& unit_row_left_inverse) {
// No need to update if we will recompute it from scratch later.
if (recompute_edge_squared_norms_) return;
const DenseColumn& tau = ComputeTau(TransposedView(unit_row_left_inverse));
SCOPED_TIME_STAT(&stats_);
// ||unit_row_left_inverse||^2 is the same as
// edge_squared_norms_[leaving_row], but with a better precision. If the
// difference between the two is too large, we trigger a full recomputation.
//
// Note that we use the PreciseSquaredNorms() because it is a small price to
// pay for more precise update below.
const Fractional leaving_squared_norm =
PreciseSquaredNorm(TransposedView(unit_row_left_inverse));
const Fractional old_squared_norm = edge_squared_norms_[leaving_row];
const Fractional estimated_edge_norms_accuracy =
(sqrt(leaving_squared_norm) - sqrt(old_squared_norm)) /
sqrt(leaving_squared_norm);
stats_.edge_norms_accuracy.Add(estimated_edge_norms_accuracy);
if (std::abs(estimated_edge_norms_accuracy) >
parameters_.recompute_edges_norm_threshold()) {
VLOG(1) << "Recomputing edge norms: " << sqrt(leaving_squared_norm)
<< " vs " << sqrt(old_squared_norm);
recompute_edge_squared_norms_ = true;
return;
}
const Fractional pivot = direction[leaving_row];
const Fractional new_leaving_squared_norm =
leaving_squared_norm / Square(pivot);
// Update the norm.
int stat_lower_bounded_norms = 0;
for (const auto e : direction) {
// Note that the update formula used is important to maximize the precision.
// See Koberstein's PhD section 8.2.2.1.
edge_squared_norms_[e.row()] +=
e.coefficient() * (e.coefficient() * new_leaving_squared_norm -
2.0 / pivot * tau[e.row()]);
// Avoid 0.0 norms (The 1e-4 is the value used by Koberstein).
// TODO(user): use a more precise lower bound depending on the column norm?
// We can do that with Cauchy-Swartz inequality:
// (edge . leaving_column)^2 = 1.0 < ||edge||^2 * ||leaving_column||^2
const Fractional kLowerBound = 1e-4;
if (edge_squared_norms_[e.row()] < kLowerBound) {
if (e.row() == leaving_row) continue;
edge_squared_norms_[e.row()] = kLowerBound;
++stat_lower_bounded_norms;
}
}
edge_squared_norms_[leaving_row] = new_leaving_squared_norm;
IF_STATS_ENABLED(stats_.lower_bounded_norms.Add(stat_lower_bounded_norms));
}
void DualEdgeNorms::ComputeEdgeSquaredNorms() {
SCOPED_TIME_STAT(&stats_);
// Since we will do a lot of inversions, it is better to be as efficient and
// precise as possible by having a refactorized basis.
DCHECK(basis_factorization_.IsRefactorized());
const RowIndex num_rows = basis_factorization_.GetNumberOfRows();
edge_squared_norms_.resize(num_rows, 0.0);
for (RowIndex row(0); row < num_rows; ++row) {
edge_squared_norms_[row] = basis_factorization_.DualEdgeSquaredNorm(row);
}
recompute_edge_squared_norms_ = false;
}
const DenseColumn& DualEdgeNorms::ComputeTau(
const ScatteredColumn& unit_row_left_inverse) {
SCOPED_TIME_STAT(&stats_);
const DenseColumn& result =
basis_factorization_.RightSolveForTau(unit_row_left_inverse);
IF_STATS_ENABLED(stats_.tau_density.Add(Density(Transpose(result))));
return result;
}
} // namespace glop
} // namespace operations_research