-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathdiagonal_precond.cc
161 lines (142 loc) · 5.42 KB
/
diagonal_precond.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright (c) 2018 ERGO-Code. See license.txt for license.
#include "diagonal_precond.h"
#include <cassert>
#include <cmath>
#include <vector>
#include "lapack.h"
#include "timer.h"
namespace ipx {
DiagonalPrecond::DiagonalPrecond(const Model& model) : model_(model) {
const Int m = model_.rows();
diagonal_.resize(m);
}
void DiagonalPrecond::Factorize(const double* W, bool precond_dense_cols,
Info* info) {
const Int m = model_.rows();
const Int n = model_.cols();
const Int num_dense = model_.num_dense_cols();
const SparseMatrix& AI = model_.AI();
factorized_ = false;
// Build diagonal of normal matrix, excluding dense columns if
// precond_dense_cols is true.
if (W) {
for (Int i = 0; i < m; i++)
diagonal_[i] = W[n+i];
for (Int j = 0; j < n; j++) {
if (precond_dense_cols && model_.IsDenseColumn(j))
continue;
double w = W[j];
for (Int p = AI.begin(j); p < AI.end(j); p++)
diagonal_[AI.index(p)] += AI.value(p) * w * AI.value(p);
}
} else {
diagonal_ = 0.0; // rightmost m columns have weight zero
for (Int j = 0; j < n; j++) {
if (precond_dense_cols && model_.IsDenseColumn(j))
continue;
for (Int p = AI.begin(j); p < AI.end(j); p++)
diagonal_[AI.index(p)] += AI.value(p) * AI.value(p);
}
}
if (precond_dense_cols && num_dense > 0) {
// Compute a representation of the inverse of the preconditioner (2)
// from the Sherman-Morrison-Woodbury (SMW) formula. Let P denote the
// preconditioner and E = P - Ad*Wd*Ad'. Then E is diagonal matrix and
//
// inv(P) = inv(E) - inv(E)*Ad * inv(S) * Ad'*inv(E),
//
// where S = inv(Wd)+Ad'*inv(E)*Ad is a Schur complement of dimension
// ndense-by-ndense. This representation requires Wd to be invertible.
// Build dense part of A rowwise.
std::vector<Int> dense_cols;
for (Int j = 0; j < n; j++)
if (model_.IsDenseColumn(j))
dense_cols.push_back(j);
assert(dense_cols.size() == num_dense);
Atdense_ = Transpose(CopyColumns(AI, dense_cols));
assert(Atdense_.rows() == num_dense);
// Build Schur complement for SMW formula.
chol_factor_.resize(num_dense*num_dense);
chol_factor_ = 0.0;
Int k = 0;
for (Int j : dense_cols) {
// Compute column k of S:
// S[:,k] = inv(Wd)[:,k] + Ad'*inv(E)*Ad[:,k]
double* col = &chol_factor_[k*num_dense];
for (Int p = AI.begin(j); p < AI.end(j); p++) {
Int i = AI.index(p);
double alpha = AI.value(p) / diagonal_[i];
assert(std::isfinite(alpha));
for (Int pp = Atdense_.begin(i); pp < Atdense_.end(i); pp++)
col[Atdense_.index(pp)] += alpha * Atdense_.value(pp);
}
double w = W ? W[j] : 1.0;
col[k] += 1.0 / w; // add to diagonal
k++;
}
// Cholesky factorization of Schur complement.
Int err = Lapack_dpotrf('L', num_dense, &chol_factor_[0], num_dense);
if (err) {
info->errflag = IPX_ERROR_lapack_chol;
return;
}
// double rcondest = Lapack_dtrcon('1', 'L', 'N', num_dense,
// &chol_factor_[0], num_dense);
// if (rcondest < 1e-6) {
// std::cout << " Cholesky factor condest = "
// << sci2(1./rcondest) << '\n';
// }
// Allocate workspace required to apply preconditioner.
work_.resize(num_dense);
} else {
// At least we have to resize Atdense to zero rows, so that Apply()
// knows that no dense column preconditioning is used. We also free
// memory that might have been allocated in a previous factorization.
Atdense_.clear();
chol_factor_.resize(0);
work_.resize(0);
}
factorized_ = true;
}
double DiagonalPrecond::time() const {
return time_;
}
void DiagonalPrecond::reset_time() {
time_ = 0.0;
}
void DiagonalPrecond::_Apply(const Vector& rhs, Vector& lhs,
double* rhs_dot_lhs) {
const Int m = model_.rows();
const Int ndense = Atdense_.rows();
double rldot = 0.0;
Timer timer;
assert(factorized_);
assert(lhs.size() == m);
assert(rhs.size() == m);
assert(work_.size() >= ndense);
if (ndense > 0) {
// Compute Ad'*inv(E)*rhs in workspace.
work_ = 0.0;
for (Int i = 0; i < m; i++)
ScatterColumn(Atdense_, i, rhs[i]/diagonal_[i], work_);
// Solve with Cholesky factorization of Schur complement.
Int err = Lapack_dpotrs('L', ndense, 1, &chol_factor_[0], ndense,
&work_[0], ndense);
assert(err == 0);
// Compute lhs = inv(E)*rhs - inv(E)*Ad*work.
for (Int i = 0; i < m; i++) {
double d = rhs[i] - DotColumn(Atdense_, i, work_);
lhs[i] = d / diagonal_[i];
rldot += lhs[i] * rhs[i];
}
} else {
for (Int i = 0; i < m; i++) {
lhs[i] = rhs[i] / diagonal_[i];
rldot += lhs[i] * rhs[i];
}
}
if (rhs_dot_lhs)
*rhs_dot_lhs = rldot;
time_ += timer.Elapsed();
}
} // namespace ipx