Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
lmpar.h
1// IWYU pragma: private
2// SPDX-FileCopyrightText: The Eigen Authors
3// SPDX-License-Identifier: MPL-2.0
4
5#ifndef EIGEN_NONLINEAROPTIMIZATION_LMPAR_H
6#define EIGEN_NONLINEAROPTIMIZATION_LMPAR_H
7
8#include "./InternalHeaderCheck.h"
9
10namespace Eigen {
11
12namespace internal {
13
14template <typename Scalar>
15void lmpar(Matrix<Scalar, Dynamic, Dynamic> &r, const VectorXi &ipvt, const Matrix<Scalar, Dynamic, 1> &diag,
16 const Matrix<Scalar, Dynamic, 1> &qtb, Scalar delta, Scalar &par, Matrix<Scalar, Dynamic, 1> &x) {
17 using std::abs;
18 using std::sqrt;
19 typedef DenseIndex Index;
20
21 /* Local variables */
22 Index i, j, l;
23 Scalar fp;
24 Scalar parc, parl;
25 Index iter;
26 Scalar temp, paru;
27 Scalar gnorm;
28 Scalar dxnorm;
29
30 /* Function Body */
31 const Scalar dwarf = (std::numeric_limits<Scalar>::min)();
32 const Index n = r.cols();
33 eigen_assert(n == diag.size());
34 eigen_assert(n == qtb.size());
35 eigen_assert(n == x.size());
36
37 Matrix<Scalar, Dynamic, 1> wa1, wa2;
38
39 /* compute and store in x the gauss-newton direction. if the */
40 /* jacobian is rank-deficient, obtain a least squares solution. */
41 Index nsing = n - 1;
42 wa1 = qtb;
43 for (j = 0; j < n; ++j) {
44 if (r(j, j) == 0. && nsing == n - 1) nsing = j - 1;
45 if (nsing < n - 1) wa1[j] = 0.;
46 }
47 for (j = nsing; j >= 0; --j) {
48 wa1[j] /= r(j, j);
49 temp = wa1[j];
50 for (i = 0; i < j; ++i) wa1[i] -= r(i, j) * temp;
51 }
52
53 for (j = 0; j < n; ++j) x[ipvt[j]] = wa1[j];
54
55 /* initialize the iteration counter. */
56 /* evaluate the function at the origin, and test */
57 /* for acceptance of the gauss-newton direction. */
58 iter = 0;
59 wa2 = diag.cwiseProduct(x);
60 dxnorm = wa2.blueNorm();
61 fp = dxnorm - delta;
62 if (fp <= Scalar(0.1) * delta) {
63 par = 0;
64 return;
65 }
66
67 /* if the jacobian is not rank deficient, the newton */
68 /* step provides a lower bound, parl, for the zero of */
69 /* the function. otherwise set this bound to zero. */
70 parl = 0.;
71 if (nsing >= n - 1) {
72 for (j = 0; j < n; ++j) {
73 l = ipvt[j];
74 wa1[j] = diag[l] * (wa2[l] / dxnorm);
75 }
76 // Triangular solve (forward substitution):
77 for (j = 0; j < n; ++j) {
78 Scalar sum = 0.;
79 for (i = 0; i < j; ++i) sum += r(i, j) * wa1[i];
80 wa1[j] = (wa1[j] - sum) / r(j, j);
81 }
82 temp = wa1.blueNorm();
83 parl = fp / delta / temp / temp;
84 }
85
86 /* calculate an upper bound, paru, for the zero of the function. */
87 for (j = 0; j < n; ++j) wa1[j] = r.col(j).head(j + 1).dot(qtb.head(j + 1)) / diag[ipvt[j]];
88
89 gnorm = wa1.stableNorm();
90 paru = gnorm / delta;
91 if (paru == 0.) paru = dwarf / (std::min)(delta, Scalar(0.1));
92
93 /* if the input par lies outside of the interval (parl,paru), */
94 /* set par to the closer endpoint. */
95 par = (std::max)(par, parl);
96 par = (std::min)(par, paru);
97 if (par == 0.) par = gnorm / dxnorm;
98
99 /* beginning of an iteration. */
100 while (true) {
101 ++iter;
102
103 /* evaluate the function at the current value of par. */
104 if (par == 0.) par = (std::max)(dwarf, Scalar(.001) * paru); /* Computing MAX */
105 wa1 = sqrt(par) * diag;
106
107 Matrix<Scalar, Dynamic, 1> sdiag(n);
108 qrsolv<Scalar>(r, ipvt, wa1, qtb, x, sdiag);
109
110 wa2 = diag.cwiseProduct(x);
111 dxnorm = wa2.blueNorm();
112 temp = fp;
113 fp = dxnorm - delta;
114
115 /* if the function is small enough, accept the current value */
116 /* of par. also test for the exceptional cases where parl */
117 /* is zero or the number of iterations has reached 10. */
118 if (abs(fp) <= Scalar(0.1) * delta || (parl == 0. && fp <= temp && temp < 0.) || iter == 10) break;
119
120 /* compute the newton correction. */
121 for (j = 0; j < n; ++j) {
122 l = ipvt[j];
123 wa1[j] = diag[l] * (wa2[l] / dxnorm);
124 }
125 for (j = 0; j < n; ++j) {
126 wa1[j] /= sdiag[j];
127 temp = wa1[j];
128 for (i = j + 1; i < n; ++i) wa1[i] -= r(i, j) * temp;
129 }
130 temp = wa1.blueNorm();
131 parc = fp / delta / temp / temp;
132
133 /* depending on the sign of the function, update parl or paru. */
134 if (fp > 0.) parl = (std::max)(parl, par);
135 if (fp < 0.) paru = (std::min)(paru, par);
136
137 /* compute an improved estimate for par. */
138 /* Computing MAX */
139 par = (std::max)(parl, par + parc);
140
141 /* end of an iteration. */
142 }
143
144 /* termination. */
145 if (iter == 0) par = 0.;
146 return;
147}
148
149template <typename Scalar>
150void lmpar2(const ColPivHouseholderQR<Matrix<Scalar, Dynamic, Dynamic> > &qr, const Matrix<Scalar, Dynamic, 1> &diag,
151 const Matrix<Scalar, Dynamic, 1> &qtb, Scalar delta, Scalar &par, Matrix<Scalar, Dynamic, 1> &x)
152
153{
154 using std::abs;
155 using std::sqrt;
156 typedef DenseIndex Index;
157
158 /* Local variables */
159 Index j;
160 Scalar fp;
161 Scalar parc, parl;
162 Index iter;
163 Scalar temp, paru;
164 Scalar gnorm;
165 Scalar dxnorm;
166
167 /* Function Body */
168 const Scalar dwarf = (std::numeric_limits<Scalar>::min)();
169 const Index n = qr.matrixQR().cols();
170 eigen_assert(n == diag.size());
171 eigen_assert(n == qtb.size());
172
173 Matrix<Scalar, Dynamic, 1> wa1, wa2;
174
175 /* compute and store in x the gauss-newton direction. if the */
176 /* jacobian is rank-deficient, obtain a least squares solution. */
177
178 const Index rank = qr.rank(); // use a threshold
179 wa1 = qtb;
180 wa1.tail(n - rank).setZero();
181 qr.matrixQR().topLeftCorner(rank, rank).template triangularView<Upper>().solveInPlace(wa1.head(rank));
182
183 x = qr.colsPermutation() * wa1;
184
185 /* initialize the iteration counter. */
186 /* evaluate the function at the origin, and test */
187 /* for acceptance of the gauss-newton direction. */
188 iter = 0;
189 wa2 = diag.cwiseProduct(x);
190 dxnorm = wa2.blueNorm();
191 fp = dxnorm - delta;
192 if (fp <= Scalar(0.1) * delta) {
193 par = 0;
194 return;
195 }
196
197 /* if the jacobian is not rank deficient, the newton */
198 /* step provides a lower bound, parl, for the zero of */
199 /* the function. otherwise set this bound to zero. */
200 parl = 0.;
201 if (rank == n) {
202 wa1 = qr.colsPermutation().inverse() * diag.cwiseProduct(wa2) / dxnorm;
203 qr.matrixQR().topLeftCorner(n, n).transpose().template triangularView<Lower>().solveInPlace(wa1);
204 temp = wa1.blueNorm();
205 parl = fp / delta / temp / temp;
206 }
207
208 /* calculate an upper bound, paru, for the zero of the function. */
209 for (j = 0; j < n; ++j)
210 wa1[j] = qr.matrixQR().col(j).head(j + 1).dot(qtb.head(j + 1)) / diag[qr.colsPermutation().indices()(j)];
211
212 gnorm = wa1.stableNorm();
213 paru = gnorm / delta;
214 if (paru == 0.) paru = dwarf / (std::min)(delta, Scalar(0.1));
215
216 /* if the input par lies outside of the interval (parl,paru), */
217 /* set par to the closer endpoint. */
218 par = (std::max)(par, parl);
219 par = (std::min)(par, paru);
220 if (par == 0.) par = gnorm / dxnorm;
221
222 /* beginning of an iteration. */
223 Matrix<Scalar, Dynamic, Dynamic> s = qr.matrixQR();
224 while (true) {
225 ++iter;
226
227 /* evaluate the function at the current value of par. */
228 if (par == 0.) par = (std::max)(dwarf, Scalar(.001) * paru); /* Computing MAX */
229 wa1 = sqrt(par) * diag;
230
231 Matrix<Scalar, Dynamic, 1> sdiag(n);
232 qrsolv<Scalar>(s, qr.colsPermutation().indices(), wa1, qtb, x, sdiag);
233
234 wa2 = diag.cwiseProduct(x);
235 dxnorm = wa2.blueNorm();
236 temp = fp;
237 fp = dxnorm - delta;
238
239 /* if the function is small enough, accept the current value */
240 /* of par. also test for the exceptional cases where parl */
241 /* is zero or the number of iterations has reached 10. */
242 if (abs(fp) <= Scalar(0.1) * delta || (parl == 0. && fp <= temp && temp < 0.) || iter == 10) break;
243
244 /* compute the newton correction. */
245 wa1 = qr.colsPermutation().inverse() * diag.cwiseProduct(wa2 / dxnorm);
246 // we could almost use this here, but the diagonal is outside qr, in sdiag[]
247 // qr.matrixQR().topLeftCorner(n, n).transpose().template triangularView<Lower>().solveInPlace(wa1);
248 for (j = 0; j < n; ++j) {
249 wa1[j] /= sdiag[j];
250 temp = wa1[j];
251 for (Index i = j + 1; i < n; ++i) wa1[i] -= s(i, j) * temp;
252 }
253 temp = wa1.blueNorm();
254 parc = fp / delta / temp / temp;
255
256 /* depending on the sign of the function, update parl or paru. */
257 if (fp > 0.) parl = (std::max)(parl, par);
258 if (fp < 0.) paru = (std::min)(paru, par);
259
260 /* compute an improved estimate for par. */
261 par = (std::max)(parl, par + parc);
262 }
263 if (iter == 0) par = 0.;
264 return;
265}
266
267} // end namespace internal
268
269} // end namespace Eigen
270
271#endif // EIGEN_NONLINEAROPTIMIZATION_LMPAR_H
Matrix< int, Dynamic, 1 > VectorXi
Namespace containing all symbols from the Eigen library.