OR-Tools  9.6
dual_edge_norms.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 
17 
18 namespace operations_research {
19 namespace glop {
20 
22  : basis_factorization_(basis_factorization),
23  recompute_edge_squared_norms_(true) {}
24 
26  return recompute_edge_squared_norms_;
27 }
28 
29 void DualEdgeNorms::Clear() { recompute_edge_squared_norms_ = true; }
30 
31 void DualEdgeNorms::ResizeOnNewRows(RowIndex new_size) {
32  edge_squared_norms_.resize(new_size, 1.0);
33 }
34 
36  if (recompute_edge_squared_norms_) ComputeEdgeSquaredNorms();
37  return edge_squared_norms_;
38 }
39 
41  const ColumnPermutation& col_perm) {
42  if (recompute_edge_squared_norms_) return;
43  ApplyColumnPermutationToRowIndexedVector(col_perm, &edge_squared_norms_,
44  &tmp_edge_squared_norms_);
45 }
46 
47 bool DualEdgeNorms::TestPrecision(RowIndex leaving_row,
48  const ScatteredRow& unit_row_left_inverse) {
49  // This should only be true if we do not use steepest edge.
50  if (recompute_edge_squared_norms_) return true;
51 
52  // ||unit_row_left_inverse||^2 is the same as
53  // edge_squared_norms_[leaving_row], but with a better precision. If the
54  // difference between the two is too large, we trigger a full recomputation.
55  const Fractional leaving_squared_norm =
56  SquaredNorm(TransposedView(unit_row_left_inverse));
57  const Fractional old_squared_norm = edge_squared_norms_[leaving_row];
58  const Fractional estimated_edge_norms_accuracy =
59  (sqrt(leaving_squared_norm) - sqrt(old_squared_norm)) /
60  sqrt(leaving_squared_norm);
61  stats_.edge_norms_accuracy.Add(estimated_edge_norms_accuracy);
62 
63  if (std::abs(estimated_edge_norms_accuracy) >
64  parameters_.recompute_edges_norm_threshold()) {
65  VLOG(1) << "Recomputing edge norms: " << sqrt(leaving_squared_norm)
66  << " vs " << sqrt(old_squared_norm);
67  recompute_edge_squared_norms_ = true;
68  }
69 
70  // We just do not want to pivot on a position with an under-estimated norm.
71  edge_squared_norms_[leaving_row] = leaving_squared_norm;
72  const bool result = old_squared_norm > 0.25 * leaving_squared_norm;
73  if (!result) {
74  VLOG(1) << "Recomputing leaving row. Norm was " << sqrt(old_squared_norm)
75  << " vs precise version " << sqrt(leaving_squared_norm);
76  }
77  return result;
78 }
79 
81  ColIndex entering_col, RowIndex leaving_row,
82  const ScatteredColumn& direction,
83  const ScatteredRow& unit_row_left_inverse) {
84  // No need to update if we will recompute it from scratch later.
85  if (recompute_edge_squared_norms_) return;
86  const DenseColumn& tau = ComputeTau(TransposedView(unit_row_left_inverse));
87  SCOPED_TIME_STAT(&stats_);
88 
89  const Fractional pivot = direction[leaving_row];
90  const Fractional new_leaving_squared_norm =
91  edge_squared_norms_[leaving_row] / Square(pivot);
92 
93  // Update the norm.
94  int stat_lower_bounded_norms = 0;
95  auto output = edge_squared_norms_.view();
96  for (const auto e : direction) {
97  // Note that the update formula used is important to maximize the precision.
98  // See Koberstein's PhD section 8.2.2.1.
99  output[e.row()] +=
100  e.coefficient() * (e.coefficient() * new_leaving_squared_norm -
101  2.0 / pivot * tau[e.row()]);
102 
103  // Avoid 0.0 norms (The 1e-4 is the value used by Koberstein).
104  // TODO(user): use a more precise lower bound depending on the column norm?
105  // We can do that with Cauchy-Swartz inequality:
106  // (edge . leaving_column)^2 = 1.0 < ||edge||^2 * ||leaving_column||^2
107  const Fractional kLowerBound = 1e-4;
108  if (output[e.row()] < kLowerBound) {
109  if (e.row() == leaving_row) continue;
110  output[e.row()] = kLowerBound;
111  ++stat_lower_bounded_norms;
112  }
113  }
114  output[leaving_row] = new_leaving_squared_norm;
115  IF_STATS_ENABLED(stats_.lower_bounded_norms.Add(stat_lower_bounded_norms));
116 }
117 
118 void DualEdgeNorms::ComputeEdgeSquaredNorms() {
119  SCOPED_TIME_STAT(&stats_);
120 
121  // Since we will do a lot of inversions, it is better to be as efficient and
122  // precise as possible by having a refactorized basis.
123  DCHECK(basis_factorization_.IsRefactorized());
124  const RowIndex num_rows = basis_factorization_.GetNumberOfRows();
125  edge_squared_norms_.resize(num_rows, 0.0);
126  for (RowIndex row(0); row < num_rows; ++row) {
127  edge_squared_norms_[row] = basis_factorization_.DualEdgeSquaredNorm(row);
128  }
129  recompute_edge_squared_norms_ = false;
130 }
131 
132 const DenseColumn& DualEdgeNorms::ComputeTau(
133  const ScatteredColumn& unit_row_left_inverse) {
134  SCOPED_TIME_STAT(&stats_);
135  const DenseColumn& result =
136  basis_factorization_.RightSolveForTau(unit_row_left_inverse);
137  IF_STATS_ENABLED(stats_.tau_density.Add(Density(Transpose(result))));
138  return result;
139 }
140 
141 } // namespace glop
142 } // namespace operations_research
const DenseColumn & RightSolveForTau(const ScatteredColumn &a) const
Fractional DualEdgeSquaredNorm(RowIndex row) const
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, const ScatteredRow &unit_row_left_inverse)
void UpdateDataOnBasisPermutation(const ColumnPermutation &col_perm)
bool TestPrecision(RowIndex leaving_row, const ScatteredRow &unit_row_left_inverse)
DualEdgeNorms(const BasisFactorization &basis_factorization)
RowIndex row
Definition: markowitz.cc:185
Fractional Square(Fractional f)
Fractional SquaredNorm(const SparseColumn &v)
double Density(const DenseRow &row)
const DenseRow & Transpose(const DenseColumn &col)
void ApplyColumnPermutationToRowIndexedVector(const Permutation< ColIndex > &col_perm, RowIndexedVector *v)
StrictITIVector< RowIndex, Fractional > DenseColumn
Definition: lp_types.h:370
const ScatteredRow & TransposedView(const ScatteredColumn &c)
Collection of objects used to extend the Constraint Solver library.
#define IF_STATS_ENABLED(instructions)
Definition: stats.h:438
#define SCOPED_TIME_STAT(stats)
Definition: stats.h:439
#define VLOG(verboselevel)
Definition: vlog.h:39