preCICE v3.2.0
Loading...
Searching...
No Matches
SVDFactorization.cpp
Go to the documentation of this file.
1#ifndef PRECICE_NO_MPI
2
4#include <Eigen/Core>
5#include <limits>
6#include <utility>
7
8#include "utils/IntraComm.hpp"
9
11
13 double eps,
14 PtrPreconditioner preconditioner)
15 : _preconditioner(std::move(preconditioner)),
17{
18}
19
21 PtrParMatrixOps parOps,
22 int globalRowsA,
23 int globalRowsB)
24{
25 _parMatrixOps = std::move(parOps);
26 _globalRowsA = globalRowsA;
27 _globalRowsB = globalRowsB;
28 _initialized = true;
29}
30
31/*
32void SVDFactorization::applyPreconditioner()
33{
34 PRECICE_TRACE();
35
36 if(_psi.size() > 0 && _phi.size() > 0){
37 // apply preconditioner: \psi_i * P_i, corresponds to Wtil_i * P_i, local!
38 _preconditioner->apply(_psi);
39 // apply preconditioner: \phi_i^T * P_i^{-1}, corresponds to Z_i * P_i^{-1}, local!
40 // here, \phi^T should be preconditioned from right with inv_weights, i.e., the columns
41 // of \phi^T are scaled. This is identical to scaling the rows of \phi, i.e., applying
42 // P^{-1} * \phi
43 _preconditioner->revert(_phi);
44 }
45 _preconditionerApplied = true;
46}
47
48void SVDFactorization::revertPreconditioner()
49{
50 PRECICE_TRACE();
51
52 if(_psi.size() > 0 && _phi.size() > 0){
53 // revert preconditioner: \psi_i * P_i^{-1}, corresponds to Wtil_i * P_i^{-1}, local!
54 _preconditioner->revert(_psi);
55 // revert preconditioner: \phi_i^T * P_i, corresponds to Z_i * P_i, local!
56 // here, \phi^T should be preconditioned from right with _weights, i.e., the columns
57 // of \phi^T are scaled. This is identical to scaling the rows of \phi, i.e., applying
58 // P * \phi
59 _preconditioner->apply(_phi);
60 }
61 _preconditionerApplied = false;
62}
63*/
64
66{
67 _psi.resize(0, 0);
68 _phi.resize(0, 0);
69 _sigma.resize(0);
71 _initialSVD = false;
72 _applyFilterQR = false;
73 _epsQR2 = 1e-3;
74}
75
77 Matrix const &A,
78 int globalRows,
79 Matrix &Q,
80 Matrix &R)
81{
83
84 // if nothing is linear dependent, the dimensions stay like this
85 Q = Matrix::Zero(A.rows(), A.cols());
86 R = Matrix::Zero(A.cols(), A.cols());
87 int colsR = 0;
88 int rowsR = 0;
89 int colsQ = 0;
90
91 // magic params:
92 double omega = 0.;
93 double theta = std::sqrt(2);
94
95 // columns need to be inserted at the back, otherwise we would have to perform
96 // givens rotations, to re-establish the upper diagonal form of R
97 for (int colIndex = 0; colIndex < A.cols(); colIndex++) {
98
99 // invariants:
100 PRECICE_ASSERT(colsQ == rowsR, colsQ, rowsR);
101 PRECICE_ASSERT(colsQ <= colIndex, colsQ, colIndex);
102
103 Vector col = A.col(colIndex);
104
105 // if system is quadratic; discard
106 if (globalRows == colIndex) {
107 PRECICE_WARN("The matrix that is about to be factorized is quadratic, i.e., the new column cannot be orthogonalized; discard.");
108 return;
109 }
110
114 Vector r = Vector::Zero(R.rows()); // gram-schmidt coefficients orthogonalization
115 Vector s = Vector::Zero(R.rows()); // gram-schmidt coefficients re-orthogonalization
116 Vector u = Vector::Zero(A.rows()); // sum of projections
117 double rho_orth = 0.;
118 double rho0 = utils::IntraComm::l2norm(col); // distributed l2norm;
119 double rho00 = rho0; // save norm of col for QR2 filter crit.
120
121 int its = 0;
122 bool termination = false;
123 bool orthogonalized = true;
124 // while col is not sufficiently orthogonal to Q
125 while (!termination) {
126
127 // take a gram-schmidt iteration
128 u = Vector::Zero(A.rows());
129 for (int j = 0; j < colsQ; j++) {
130
131 // dot-product <_Q(:,j), v >
132 Vector Qc = Q.col(j);
133 // dot product <_Q(:,j), v> =: r_ij
134 double r_ij = utils::IntraComm::dot(Qc, col);
135 // save r_ij in s(j) = column of R
136 s(j) = r_ij;
137 // u is the sum of projections r_ij * _Q(:,j) = _Q(:,j) * <_Q(:,j), v>
138 u += Q.col(j) * r_ij;
139 }
140 // add the gram-schmidt coefficients over all iterations of possible re-orthogonalizations
141 r += s;
142 // subtract projections from v, v is now orthogonal to columns of Q
143 col -= u;
144
145 // rho1 = norm of orthogonalized new column v_tilde (though not normalized)
146 rho_orth = utils::IntraComm::l2norm(col);
147 // t = norm of _r(:,j) with j = colNum-1
148 double norm_coefficients = utils::IntraComm::l2norm(s); // distributed l2norm
149
150 its++;
151
152 // if ||v_orth|| is nearly zero, col is not well orthogonalized; discard
153 if (rho_orth <= std::numeric_limits<double>::min()) {
154 PRECICE_DEBUG("The norm of v_orthogonal is almost zero, i.e., failed to orthogonalize column v; discard.");
155 orthogonalized = false;
156 termination = true;
157 }
158
171 if (rho_orth * theta <= rho0 + omega * norm_coefficients) {
172 // exit to fail if too many iterations
173 if (its >= 4) {
174 PRECICE_WARN("Matrix Q is not sufficiently orthogonal. Failed to orthogonalize new column after 4 iterations. New column will be discarded.");
175 orthogonalized = false;
176 termination = true;
177 }
178
179 // for re-orthogonalization
180 rho0 = rho_orth;
181
182 } else {
183 termination = true;
184 }
185 }
186
187 // if the QR2-filter crit. kicks in with threshold eps.
188 if (_applyFilterQR && orthogonalized && rho_orth <= _epsQR2 * rho00) {
189 orthogonalized = false;
190 }
191
192 // normalize col
193 double rho = orthogonalized ? rho_orth : 1.0;
194 col /= rho;
195 r(rowsR) = rho;
196
197 // as we always insert at the rightmost position, no need to shift
198 // entries of R or apply givens rotations on the QR-dec to maintain
199 // the upper triangular structure of R
200 Q.col(colsQ) = col; // insert orthogonalized column to the right in Q
201 R.col(colsR) = r; // insert gram-schmidt coefficients to the right in R
202
203 colsR++;
204 PRECICE_ASSERT(colsR <= R.cols(), colsR, R.cols());
205 rowsR++;
206 PRECICE_ASSERT(rowsR <= R.rows(), rowsR, R.rows());
207 colsQ++;
208
209 // failed to orthogonalize the column, i.e., it is linear dependent;
210 // modify the QR-dec such that it stays valid (column deleted) while
211 // also staying aligned with the dimension of A, MUST have the same
212 // number of cols (cannot delete from A)
213 if (not orthogonalized) {
214
215 colsQ--;
216 PRECICE_ASSERT(colsQ >= 0, colsQ);
217 rowsR--;
218 PRECICE_ASSERT(rowsR >= 0, rowsR);
219 // delete column that was just inserted (as it is not orthogonal to Q)
220 Q.col(colsQ) = Vector::Zero(A.rows());
221 // delete line in R that corresponds to the just inserted but not orthogonal column
222 // as we always insert to the right, no shifting/ application of givens roatations is
223 // necessary.
224 // Note: The corresponding column from R with index colIndex is not deleted: dimensions must align with A.
225 PRECICE_ASSERT(R(rowsR, colsR - 1) == 1.0, R(rowsR, colsR - 1));
226 R.row(rowsR) = Vector::Zero(A.cols());
227 }
228 }
229 // shrink matrices Q, R to actual size
230 Q.conservativeResize(A.rows(), colsQ);
231 R.conservativeResize(rowsR, colsR);
232}
233
238
243
248
253
255{
256 _applyFilterQR = b;
257 _epsQR2 = eps;
258}
259
260/*
261bool SVDFactorization::isPrecondApplied()
262{
263 return _preconditionerApplied;
264}
265*/
266
271
273{
274 _truncationEps = eps;
275}
276
278{
279 return _truncationEps;
280}
281
283{
284 int r = _waste;
285 _waste = 0;
286 return r;
287}
288
290{
291 return _cols;
292}
293
295{
296 return _cols;
297}
298
299} // namespace precice::acceleration::impl
300
301#endif // PRECICE_NO_MPI
#define PRECICE_WARN(...)
Definition LogMacros.hpp:12
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:61
#define PRECICE_TRACE(...)
Definition LogMacros.hpp:92
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
int _cols
Number of columns, i.e., rank of the truncated svd.
PtrParMatrixOps _parMatrixOps
: object for parallel matrix operations, i.e., parallel mat-mat/ mat-vec multiplications
void setApplyFilterQR(bool b, double eps=1e-3)
: enables or disables an additional QR-2 filter for the QR-decomposition
double _truncationEps
Truncation parameter for the updated SVD decomposition.
bool _initialSVD
true, if at least one update has been made, i.e., the number of rows is known and a initial rank is g...
void initialize(PtrParMatrixOps parMatOps, int globalRowsA, int globalRowsB)
: initializes the updated SVD factorization, i.e., sets the object for parallel matrix-matrix operati...
double getThreshold()
: returns the truncation threshold for the SVD
bool _preconditionerApplied
true if the preconditioner has been applied appropriate to the updated SVD decomposition
void setThreshold(double eps)
: sets the threshold for the truncation of the SVD factorization
Matrix _psi
: SVD factorization of the matrix J = _psi * _sigma * _phi^T
void setPrecondApplied(bool b)
: applies the preconditioner to the factorized and truncated representation of the Jacobian matrix
Matrix & matrixPhi()
: returns a matrix representation of the orthogonal matrix Phi, A = Psi * Sigma * Phi^T
Vector & singularValues()
: returns a matrix representation of the orthogonal matrix Sigma, A = Psi * Sigma * Phi^T
void reset()
: resets the SVD factorization
Rank rank()
: returns the rank of the truncated SVD factorization
int _globalRowsB
Number of global rows of second multiplicator, i.e., sum of _rows for all procs.
double _epsQR2
Threshold for the QR2 filter for the QR decomposition.
int _globalRowsA
Number of global rows of first multiplicator, i.e., sum of _rows for all procs.
Matrix & matrixPsi()
: returns a matrix representation of the orthogonal matrix Psi, A = Psi * Sigma * Phi^bs
bool _initialized
true, if ParallelMatrixOperations object is set, i.e., initialized
int cols()
: returns the number of columns in the QR-decomposition
SVDFactorization(double eps, PtrPreconditioner preconditioner)
Constructor.
void computeQRdecomposition(Matrix const &A, int globalRows, Matrix &Q, Matrix &R)
: computes the QR decomposition of a matrix A of type A = PSI^T*A \in R^(rank x n)
PtrPreconditioner _preconditioner
: preconditioner for least-squares system if vectorial system is used.
int getWaste()
: returns the total number of truncated modes since last call to this method
static double l2norm(const Eigen::VectorXd &vec)
The l2 norm of a vector is calculated on distributed data.
Definition IntraComm.cpp:67
static double dot(const Eigen::VectorXd &vec1, const Eigen::VectorXd &vec2)
T min(T... args)
std::shared_ptr< Preconditioner > PtrPreconditioner
std::shared_ptr< ParallelMatrixOperations > PtrParMatrixOps
int Rank
Definition Types.hpp:37
STL namespace.
T sqrt(T... args)