Documentation 6b54b78464
Loading...
Searching...
No Matches
CQuasiNewtonInvLeastSquares.hpp
Go to the documentation of this file.
1/*!
2 * \file CQuasiNewtonInvLeastSquares.hpp
3 * \brief Implements a method to accelerate and stabilize the convergence
4 * of fixed point iterations, the history of past iterates is used to compute
5 * a least squares approximation of the inverse of the Jacobian, which is then
6 * used to correct the natural solution of the fixed point iteration.
7 * \note Based on the IQN-ILS method, see DOI 10.1007/s11831-013-9085-5 and
8 * references therein.
9 * \author P. Gomes
10 * \version 8.5.0 "Harrier"
11 *
12 * SU2 Project Website: https://su2code.github.io
13 *
14 * The SU2 Project is maintained by the SU2 Foundation
15 * (http://su2foundation.org)
16 *
17 * Copyright 2012-2026, SU2 Contributors (cf. AUTHORS.md)
18 *
19 * SU2 is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU Lesser General Public
21 * License as published by the Free Software Foundation; either
22 * version 2.1 of the License, or (at your option) any later version.
23 *
24 * SU2 is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * Lesser General Public License for more details.
28 *
29 * You should have received a copy of the GNU Lesser General Public
30 * License along with SU2. If not, see <http://www.gnu.org/licenses/>.
31 */
32
33#pragma once
34
35#include "../parallelization/omp_structure.hpp"
36#include "../parallelization/mpi_structure.hpp"
37#include "CSymmetricMatrix.hpp"
38
39/*!
40 * \brief A quasi-Newton fixed-point (FP) accelerator based on IQN-ILS.
41 * \note The implementation prioritizes storage, the LS problem is solved
42 * via the normal equations as that is easy to make parallel over MPI, it
43 * may however be unstable with large sample sizes (compared to QR decomp).
44 * Usage: Allocate, store the initial solution (operator (i,j), default is 0),
45 * run the FP, store its result ("FPresult"), compute new solution, use it
46 * as the new input of the FP, run the FP, etc.
47 * \ingroup BLAS
48 */
49template <class Scalar_t, bool WithMPI = true>
51 public:
52 using Scalar = Scalar_t;
54 static_assert(std::is_floating_point<Scalar>::value, "");
55
56 private:
57 using MPI_Wrapper = typename SelectMPIWrapper<Scalar>::W;
58
59 enum : size_t { BLOCK_SIZE = 1024 }; /*!< \brief Loop tiling parameter. */
60 std::vector<su2matrix<Scalar> > X, R; /*!< \brief Input and residual history of the FP. */
61 su2matrix<Scalar> work; /*!< \brief Work matrix (FP result, correction, and approx solution). */
62 su2vector<Scalar> mat, rhs, sol; /*!< \brief Matrix, rhs, and solution of the normal equations. */
63 Index iSample = 0; /*!< \brief Current sample index. */
64 Index nPtDomain = 0; /*!< \brief Local size of the history, considered in dot products. */
65
66 void shiftHistoryLeft() {
67 for (Index i = 1; i < X.size(); ++i) {
68 /*--- Swap instead of moving to re-use the memory of the first sample.
69 * This is why X and R are not stored as contiguous blocks of mem. ---*/
70 std::swap(X[i - 1], X[i]);
71 std::swap(R[i - 1], R[i]);
72 }
73 }
74
75 void computeNormalEquations() {
76 /*--- Size for the dot products. ---*/
77 const auto end = std::min<Index>(nPtDomain, work.rows()) * work.cols();
78
79 mat = Scalar(0);
80 rhs = Scalar(0);
81
82 /*--- Tiled part of the loop. ---*/
83 Index begin = 0;
84 while (end - begin >= BLOCK_SIZE) {
85 computeNormalEquations<BLOCK_SIZE>(mat, rhs, begin);
86 begin += BLOCK_SIZE;
87 }
88
89 /*--- Remainder of the loop. ---*/
90 if (begin != end) {
91 computeNormalEquations<0>(mat, rhs, begin, end - begin);
92 }
93
94 /*--- MPI reduction of the dot products. ---*/
95 if (WithMPI) {
96 const auto type = (sizeof(Scalar) < sizeof(double)) ? MPI_FLOAT : MPI_DOUBLE;
97
98 su2vector<Scalar> tmp(mat.size());
99 MPI_Wrapper::Allreduce(mat.data(), tmp.data(), iSample * (iSample + 1) / 2, type, MPI_SUM, SU2_MPI::GetComm());
100 mat = std::move(tmp);
101
102 MPI_Wrapper::Allreduce(rhs.data(), sol.data(), iSample, type, MPI_SUM, SU2_MPI::GetComm());
103 std::swap(rhs, sol);
104 }
105 }
106
107 template <Index StaticSize>
108 void computeNormalEquations(su2vector<Scalar>& mat, su2vector<Scalar>& vec, Index start, Index dynSize = 0) const {
109 /*--- Select either the static or dynamic size, optimizes inner loop. ---*/
110 const auto blkSize = StaticSize ? StaticSize : dynSize;
111
112 for (Index i = 0; i < iSample; ++i) {
113 const auto ri1 = R[i + 1].data() + start;
114 const auto ri0 = R[i].data() + start;
115 /*--- Off-diagonal coefficients. ---*/
116 for (Index j = 0; j < i; ++j) {
117 const auto rj1 = R[j + 1].data() + start;
118 const auto rj0 = R[j].data() + start;
119 /*--- Sum of partial sums to reduce trunc. error. ---*/
120 Scalar sum = 0;
122 for (Index k = 0; k < blkSize; ++k) {
123 sum += (ri1[k] - ri0[k]) * (rj1[k] - rj0[k]);
124 }
125 /*--- 1D index of (i,j) in lower triangular storage. ---*/
126 const auto iCoeff = i * (i + 1) / 2 + j;
127 mat(iCoeff) += sum;
128 }
129 /*--- Diagonal coeff and residual fused. ---*/
130 Scalar diag = 0, res = 0;
131 const auto r = R[iSample].data() + start;
133 for (Index k = 0; k < blkSize; ++k) {
134 diag += pow(ri1[k] - ri0[k], 2);
135 res += (ri1[k] - ri0[k]) * r[k];
136 }
137 mat(i * (i + 3) / 2) += diag;
138 vec(i) -= res;
139 }
140 }
141
142 public:
143 /*! \brief Default construction without allocation. */
145
146 /*! \brief Construction with allocation, see "resize". */
147 CQuasiNewtonInvLeastSquares(Index nsample, Index npt, Index nvar, Index nptdomain = 0) {
148 resize(nsample, npt, nvar, nptdomain);
149 }
150
151 /*!
152 * \brief Resize the object.
153 * \param[in] nsample - Number of samples used to build the FP history.
154 * \param[in] npt - Size of the solution including any halos.
155 * \param[in] nvar - Number of solution variables.
156 * \param[in] nptdomain - Local size (<= npt), if 0 it defaults to npt.
157 */
158 void resize(Index nsample, Index npt, Index nvar, Index nptdomain = 0) {
159 if (nptdomain > npt || nsample < 2) SU2_MPI::Error("Invalid quasi-Newton parameters", CURRENT_FUNCTION);
160 iSample = 0;
161 nPtDomain = nptdomain ? nptdomain : npt;
162 work.resize(npt, nvar);
163 X.clear();
164 R.clear();
165 for (Index i = 0; i < nsample; ++i) {
166 X.emplace_back(npt, nvar);
167 R.emplace_back(npt, nvar);
168 }
169 X[0] = Scalar(0);
170 /*--- Lower triangular packed storage. ---*/
171 mat.resize(nsample * (nsample - 1) / 2);
172 rhs.resize(nsample - 1);
173 sol.resize(nsample - 1);
174 }
175
176 /*! \brief Size of the object, the number of samples. */
177 Index size() const { return X.size(); }
178
179 /*! \brief Discard all history, keeping the current solution. */
180 void reset() {
181 std::swap(X[0], X[iSample]);
182 iSample = 0;
183 }
184
185 /*!
186 * \brief Access the current fixed-point result.
187 * \note Use these to STORE the result of running the FP.
188 */
189 su2matrix<Scalar>& FPresult() { return work; }
190 const su2matrix<Scalar>& FPresult() const { return work; }
191 Scalar& FPresult(Index iPt, Index iVar) { return work(iPt, iVar); }
192 const Scalar& FPresult(Index iPt, Index iVar) const { return work(iPt, iVar); }
193
194 /*!
195 * \brief Access the current solution approximation.
196 * \note Use these, after calling compute, to GET the new FP solution estimate.
197 */
198 su2matrix<Scalar>& solution() { return X[iSample]; }
199 const su2matrix<Scalar>& solution() const { return X[iSample]; }
200 Scalar& operator()(Index iPt, Index iVar) { return X[iSample](iPt, iVar); }
201 const Scalar& operator()(Index iPt, Index iVar) const { return X[iSample](iPt, iVar); }
202
203 /*!
204 * \brief Compute and return a new approximation.
205 * \note To be used after storing the FP result.
206 */
208 /*--- Compute FP residual, clear correction. ---*/
210 for (Index i = 0; i < work.size(); ++i) {
211 R[iSample].data()[i] = work.data()[i] - X[iSample].data()[i];
212 work.data()[i] = Scalar(0);
213 }
214
215 if (iSample > 0) {
216 /*--- Solve the normal equations. ---*/
217 computeNormalEquations();
218 CSymmetricMatrix pseudoInv(iSample);
219 for (Index i = 0, k = 0; i < iSample; ++i)
220 for (Index j = 0; j <= i; ++j) pseudoInv(i, j) = mat(k++);
221 pseudoInv.Invert(true);
222 pseudoInv.MatVecMult(rhs.data(), sol.data());
223
224 /*--- Compute correction, cleared before for less trunc. error. ---*/
225 for (Index k = 0; k < iSample; ++k) {
226 const auto x1 = X[k + 1].data();
227 const auto r1 = R[k + 1].data();
228 const auto x0 = X[k].data();
229 const auto r0 = R[k].data();
231 for (Index i = 0; i < work.size(); ++i) {
232 Scalar dy = r1[i] - r0[i] + x1[i] - x0[i];
233 work.data()[i] += sol(k) * dy;
234 }
235 }
236 }
237
238 /*--- Check for need to shift left. ---*/
239 if (iSample + 1 == X.size()) {
240 shiftHistoryLeft();
241 iSample--;
242 }
243
244 /*--- Set new solution. ---*/
246 for (Index i = 0; i < work.size(); ++i) work.data()[i] += R[iSample].data()[i] + X[iSample].data()[i];
247 std::swap(X[++iSample], work);
248
249 return solution();
250 }
251};
CNewtonIntegration::Scalar Scalar
Definition CNewtonIntegration.cpp:31
Dense symmetric matrix, used for example in RBF interpolation.
A templated matrix/vector-like object.
Definition C2DContainer.hpp:374
C2DContainer & resize(const Index_t size1, const Index_t size2=1) noexcept
Request a change of size.
Definition C2DContainer.hpp:541
size_t size() const noexcept
Definition C2DContainer.hpp:222
Index_t cols() const noexcept
Definition C2DContainer.hpp:222
Index_t Index
Definition C2DContainer.hpp:387
Index_t rows() const noexcept
Definition C2DContainer.hpp:222
Definition mpi_structure.hpp:501
static Comm GetComm()
Definition mpi_structure.hpp:530
static void Error(const std::string &ErrorMsg, const std::string &FunctionName)
Definition mpi_structure.cpp:131
A quasi-Newton fixed-point (FP) accelerator based on IQN-ILS.
Definition CQuasiNewtonInvLeastSquares.hpp:50
Index size() const
Size of the object, the number of samples.
Definition CQuasiNewtonInvLeastSquares.hpp:177
Scalar_t Scalar
Definition CQuasiNewtonInvLeastSquares.hpp:52
Scalar & operator()(Index iPt, Index iVar)
Definition CQuasiNewtonInvLeastSquares.hpp:200
CQuasiNewtonInvLeastSquares(Index nsample, Index npt, Index nvar, Index nptdomain=0)
Construction with allocation, see "resize".
Definition CQuasiNewtonInvLeastSquares.hpp:147
su2matrix< Scalar > & solution()
Access the current solution approximation.
Definition CQuasiNewtonInvLeastSquares.hpp:198
const Scalar & FPresult(Index iPt, Index iVar) const
Definition CQuasiNewtonInvLeastSquares.hpp:192
void reset()
Discard all history, keeping the current solution.
Definition CQuasiNewtonInvLeastSquares.hpp:180
const su2matrix< Scalar > & FPresult() const
Definition CQuasiNewtonInvLeastSquares.hpp:190
const Scalar & operator()(Index iPt, Index iVar) const
Definition CQuasiNewtonInvLeastSquares.hpp:201
Scalar & FPresult(Index iPt, Index iVar)
Definition CQuasiNewtonInvLeastSquares.hpp:191
const su2matrix< Scalar > & solution() const
Definition CQuasiNewtonInvLeastSquares.hpp:199
su2matrix< Scalar > & FPresult()
Access the current fixed-point result.
Definition CQuasiNewtonInvLeastSquares.hpp:189
void resize(Index nsample, Index npt, Index nvar, Index nptdomain=0)
Resize the object.
Definition CQuasiNewtonInvLeastSquares.hpp:158
typename su2matrix< Scalar >::Index Index
Definition CQuasiNewtonInvLeastSquares.hpp:53
CQuasiNewtonInvLeastSquares()=default
Default construction without allocation.
const su2matrix< Scalar > & compute()
Compute and return a new approximation.
Definition CQuasiNewtonInvLeastSquares.hpp:207
The matrix is symmetric but full storage is used as that gives much better performance for some BLAS ...
Definition CSymmetricMatrix.hpp:38
void MatVecMult(ForwardIt vec_in, ForwardIt vec_out) const
Definition CSymmetricMatrix.hpp:68
void Invert(bool is_spd=false)
Definition CSymmetricMatrix.cpp:160
Scalar_t * data() noexcept
Definition C2DContainer.hpp:222
FORCEINLINE ARRAY_T pow(const ARRAY_T &a, const ARRAY_T &b)
Definition special_vectorization.hpp:179
#define MPI_FLOAT
Definition mpi_structure.hpp:483
#define CURRENT_FUNCTION
Definition mpi_structure.hpp:59
#define MPI_SUM
Definition mpi_structure.hpp:486
#define MPI_DOUBLE
Definition mpi_structure.hpp:484
#define SU2_OMP_SIMD
Definition omp_structure.hpp:125