ITK 6.0.0
Insight Toolkit
 
Loading...
Searching...
No Matches
itkBridgeMathSVD.h
Go to the documentation of this file.
1/*=========================================================================
2 *
3 * Copyright NumFOCUS
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0.txt
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *=========================================================================*/
18#ifndef itkBridgeMathSVD_h
19#define itkBridgeMathSVD_h
20
21#include "itkMacro.h"
23#include "vnl/vnl_matrix.h"
24#include "vnl/vnl_matrix_fixed.h"
25#include "vnl/vnl_vector.h"
26#include "vnl/vnl_vector_fixed.h"
27
28#include "itk_eigen.h"
29#include ITK_EIGEN(Dense)
30
31#include <limits>
32#include <type_traits>
33
34namespace itk
35{
36// Forward-declared to break the itkMatrix.h <-> itkBridgeMathSVD.h include
37// cycle; the itk::Matrix SVD overload needs the complete type only at
38// instantiation, where the caller has already included itkMatrix.h.
39template <typename T, unsigned int VRows, unsigned int VColumns>
40class Matrix;
41} // namespace itk
42
43namespace itk::bridge
44{
45namespace Math
46{
47
48namespace detail
49{
50// rcond < 0 selects an automatic relative threshold of n * epsilon.
51template <typename TReal>
52TReal
53ResolveRcond(TReal rcond, unsigned int n)
54{
55 return rcond < TReal{ 0 } ? static_cast<TReal>(n) * std::numeric_limits<TReal>::epsilon() : rcond;
56}
57
58// Moore-Penrose pseudo-inverse V diag(1/w) U^T, with singular values at or below
59// rcond*max(w) treated as zero. U and V may be distinct types (rectangular fixed).
60template <typename TMatrixU, typename TVector, typename TMatrixV, typename TReal>
61auto
62PseudoInverse(const TMatrixU & U, const TVector & W, const TMatrixV & V, TReal rcond)
63{
64 const unsigned int k = W.size();
65 // W is sorted descending (Eigen guarantee), so W[0] is the max without an O(k) scan.
66 const TReal tol = ResolveRcond(rcond, k) * W[0];
67 TMatrixV scaledV = V;
68 for (unsigned int col = 0; col < k; ++col)
69 {
70 const TReal s = (W[col] > tol) ? TReal{ 1 } / W[col] : TReal{ 0 };
71 for (unsigned int i = 0; i < scaledV.rows(); ++i)
72 {
73 scaledV(i, col) *= s;
74 }
75 }
76 return scaledV * U.transpose();
77}
78
79// Least-squares / minimum-norm solution x = V diag(1/w) U^T b of A x = b.
80template <typename TMatrixU, typename TVector, typename TMatrixV, typename TVectorB, typename TReal>
81auto
82SolveLinear(const TMatrixU & U, const TVector & W, const TMatrixV & V, const TVectorB & b, TReal rcond)
83{
84 const unsigned int n = W.size();
85 const TReal tol = ResolveRcond(rcond, n) * W[0]; // W sorted descending; W[0] == max
86 auto utb = U.transpose() * b;
87 for (unsigned int k = 0; k < n; ++k)
88 {
89 utb[k] = (W[k] > tol) ? utb[k] / W[k] : TReal{ 0 };
90 }
91 return V * utb;
92}
93
94template <typename TVector, typename TReal>
95unsigned int
96NumericalRank(const TVector & W, TReal rcond)
97{
98 const unsigned int n = W.size();
99 const TReal tol = ResolveRcond(rcond, n) * W[0]; // W sorted descending; W[0] == max
100 unsigned int count = 0;
101 for (unsigned int k = 0; k < n; ++k)
102 {
103 if (W[k] > tol)
104 {
105 ++count;
106 }
107 }
108 return count;
109}
110
111// Reconstruct U diag(w) V^T, treating singular values at or below rcond*max(w) as
112// zero (a truncated, rank-reduced reconstruction of A).
113template <typename TMatrixU, typename TVector, typename TMatrixV, typename TReal>
114auto
115Recompose(const TMatrixU & U, const TVector & W, const TMatrixV & V, TReal rcond)
116{
117 const unsigned int k = W.size();
118 const TReal tol = ResolveRcond(rcond, k) * W[0]; // W sorted descending; W[0] == max
119 TMatrixU scaledU = U;
120 for (unsigned int col = 0; col < k; ++col)
121 {
122 const TReal s = (W[col] > tol) ? W[col] : TReal{ 0 };
123 for (unsigned int i = 0; i < scaledU.rows(); ++i)
124 {
125 scaledU(i, col) *= s;
126 }
127 }
128 return scaledU * V.transpose();
129}
130
131// Reconstruct U diag(w') V^T with caller-supplied singular values (no truncation);
132// serves the modify-W-then-recompose idiom (value clamping/inversion/zeroing).
133template <typename TMatrixU, typename TVector, typename TMatrixV>
134auto
135RecomposeWith(const TMatrixU & U, const TVector & modifiedW, const TMatrixV & V)
136{
137 TMatrixU scaledU = U;
138 for (unsigned int col = 0; col < modifiedW.size(); ++col)
139 {
140 for (unsigned int i = 0; i < scaledU.rows(); ++i)
141 {
142 scaledU(i, col) *= modifiedW[col];
143 }
144 }
145 return scaledU * V.transpose();
146}
147
148// Reciprocal condition number sigma_min/sigma_max; 0 when the matrix is singular.
149template <typename TVector>
150auto
151WellCondition(const TVector & W) -> std::decay_t<decltype(W[0])>
152{
153 const unsigned int k = W.size();
154 if (k == 0 || W[0] == 0)
155 {
156 return 0;
157 }
158 return W[k - 1] / W[0]; // W sorted descending
159}
160
161// Product of the singular values (|det A| for a square A).
162template <typename TVector>
163auto
164DeterminantMagnitude(const TVector & W) -> std::decay_t<decltype(W[0])>
165{
166 std::decay_t<decltype(W[0])> product{ 1 };
167 for (unsigned int k = 0; k < W.size(); ++k)
168 {
169 product *= W[k];
170 }
171 return product;
172}
173} // namespace detail
174
177template <typename TReal, unsigned int VDim>
179{
180 vnl_matrix_fixed<TReal, VDim, VDim> U{};
181 vnl_vector_fixed<TReal, VDim> W{};
182 vnl_matrix_fixed<TReal, VDim, VDim> V{};
183
185 vnl_matrix_fixed<TReal, VDim, VDim>
186 PseudoInverse(TReal rcond = TReal{ -1 }) const
187 {
188 return detail::PseudoInverse(U, W, V, rcond);
189 }
190
192 vnl_vector_fixed<TReal, VDim>
193 Solve(const vnl_vector_fixed<TReal, VDim> & b, TReal rcond = TReal{ -1 }) const
194 {
195 return detail::SolveLinear(U, W, V, b, rcond);
196 }
197
199 unsigned int
200 Rank(TReal rcond = TReal{ -1 }) const
201 {
202 return detail::NumericalRank(W, rcond);
203 }
204
206 vnl_matrix_fixed<TReal, VDim, VDim>
207 Recompose(TReal rcond = TReal{ -1 }) const
208 {
209 return detail::Recompose(U, W, V, rcond);
210 }
211
213 vnl_matrix_fixed<TReal, VDim, VDim>
214 RecomposeWith(const vnl_vector_fixed<TReal, VDim> & modifiedW) const
215 {
216 return detail::RecomposeWith(U, modifiedW, V);
217 }
218
221 vnl_vector_fixed<TReal, VDim>
223 {
224 return V.get_column(VDim - 1);
225 }
226
228 TReal
230 {
231 return detail::WellCondition(W);
232 }
233
235 TReal
237 {
239 }
240};
241
245template <typename TReal>
247{
248 vnl_matrix<TReal> U{};
249 vnl_vector<TReal> W{};
250 vnl_matrix<TReal> V{};
251
253 vnl_matrix<TReal>
254 PseudoInverse(TReal rcond = TReal{ -1 }) const
255 {
256 return detail::PseudoInverse(U, W, V, rcond);
257 }
258
260 vnl_vector<TReal>
261 Solve(const vnl_vector<TReal> & b, TReal rcond = TReal{ -1 }) const
262 {
263 return detail::SolveLinear(U, W, V, b, rcond);
264 }
265
267 unsigned int
268 Rank(TReal rcond = TReal{ -1 }) const
269 {
270 return detail::NumericalRank(W, rcond);
271 }
272
274 vnl_matrix<TReal>
275 Recompose(TReal rcond = TReal{ -1 }) const
276 {
277 return detail::Recompose(U, W, V, rcond);
278 }
279
281 vnl_matrix<TReal>
282 RecomposeWith(const vnl_vector<TReal> & modifiedW) const
283 {
284 return detail::RecomposeWith(U, modifiedW, V);
285 }
286
291 vnl_vector<TReal>
293 {
294 if (U.rows() < V.rows())
295 {
296 itkGenericExceptionMacro(
297 "NullVector() requires rows >= cols; the thin V of an underdetermined input does not span the nullspace.");
298 }
299 return V.get_column(V.cols() - 1);
300 }
301
303 TReal
305 {
306 return detail::WellCondition(W);
307 }
308
310 TReal
312 {
314 }
315};
316
320template <typename TReal, unsigned int VRows, unsigned int VCols>
322{
323 static constexpr unsigned int K = (VRows < VCols) ? VRows : VCols;
324
325 vnl_matrix_fixed<TReal, VRows, K> U{};
326 vnl_vector_fixed<TReal, K> W{};
327 vnl_matrix_fixed<TReal, VCols, K> V{};
328
330 vnl_matrix_fixed<TReal, VCols, VRows>
331 PseudoInverse(TReal rcond = TReal{ -1 }) const
332 {
333 return detail::PseudoInverse(U, W, V, rcond);
334 }
335
337 vnl_vector_fixed<TReal, VCols>
338 Solve(const vnl_vector_fixed<TReal, VRows> & b, TReal rcond = TReal{ -1 }) const
339 {
340 return detail::SolveLinear(U, W, V, b, rcond);
341 }
342
344 unsigned int
345 Rank(TReal rcond = TReal{ -1 }) const
346 {
347 return detail::NumericalRank(W, rcond);
348 }
349
351 vnl_matrix_fixed<TReal, VRows, VCols>
352 Recompose(TReal rcond = TReal{ -1 }) const
353 {
354 return detail::Recompose(U, W, V, rcond);
355 }
356
358 vnl_matrix_fixed<TReal, VRows, VCols>
359 RecomposeWith(const vnl_vector_fixed<TReal, K> & modifiedW) const
360 {
361 return detail::RecomposeWith(U, modifiedW, V);
362 }
363
367 vnl_vector_fixed<TReal, VCols>
369 {
370 static_assert(VRows >= VCols, "NullVector() requires VRows >= VCols (thin V cannot span the nullspace).");
371 return V.get_column(K - 1);
372 }
373
375 TReal
377 {
378 return detail::WellCondition(W);
379 }
380
382 TReal
384 {
386 }
387};
388
389namespace detail
390{
391// Above this compile-time size the fixed overload delegates to the runtime path
392// (keeps a large VDim off Eigen's stack-allocation limit).
393constexpr unsigned int kFixedSVDMaxDim = 16;
394
395// JacobiSVD+NoQRPreconditioner is fastest for small square inputs; BDCSVD is
396// faster and more accurate for larger n.
397constexpr unsigned int kJacobiMaxDim = 6;
398
399// Runtime-sized square SVD: JacobiSVD + NoQRPreconditioner for small n, BDCSVD for
400// larger n where Jacobi sweeps scale poorly. Throws on a failed decomposition.
401template <typename TReal>
402void
403DynamicSquareSVDEigen(const TReal * inData, unsigned int n, TReal * uData, TReal * wData, TReal * vData)
404{
405 using RowMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
406 using ColMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic>;
407 constexpr int full = Eigen::ComputeFullU | Eigen::ComputeFullV;
408
409 const Eigen::Map<const RowMajor> inMap(inData, n, n);
410 Eigen::Map<RowMajor> uMap(uData, n, n);
411 Eigen::Map<RowMajor> vMap(vData, n, n);
412
413 const auto extract = [&](const auto & svd) {
414 if (svd.info() != Eigen::Success)
415 {
416 itkGenericExceptionMacro("itk::bridge::Math::SVD failed; input is likely non-finite (NaN/Inf).");
417 }
418 uMap = svd.matrixU();
419 vMap = svd.matrixV();
420 for (unsigned int i = 0; i < n; ++i)
421 {
422 wData[i] = svd.singularValues()[i];
423 }
424 };
425
426 if (n <= kJacobiMaxDim)
427 {
428 extract(Eigen::JacobiSVD < ColMajor, full | Eigen::NoQRPreconditioner > (inMap));
429 }
430 else
431 {
432 extract(Eigen::BDCSVD<ColMajor, full>(inMap));
433 }
434}
435
436// NoQRPreconditioner skips the column-pivot QR (wasted for a square input); large
437// VDim falls back to the runtime path. Throws on a failed/non-finite decomposition.
438template <unsigned int VDim, typename TReal>
439void
440SquareSVDEigen(const TReal * inData, TReal * uData, TReal * wData, TReal * vData)
441{
442 if constexpr (VDim > kFixedSVDMaxDim)
443 {
444 DynamicSquareSVDEigen<TReal>(inData, VDim, uData, wData, vData);
445 }
446 else
447 {
448 // Compile-time sizes ≤ kFixedSVDMaxDim always take JacobiSVD: it stack-allocates,
449 // fully unrolls, and beats BDCSVD's dynamic setup at these sizes (kJacobiMaxDim
450 // gates only the runtime path, where BDCSVD's allocation pays off for larger n).
451 using RowMajor = Eigen::Matrix<TReal, VDim, VDim, Eigen::RowMajor>;
452 using ColMajor = Eigen::Matrix<TReal, VDim, VDim>;
453 constexpr int options = Eigen::ComputeFullU | Eigen::ComputeFullV | Eigen::NoQRPreconditioner;
454
455 const Eigen::Map<const RowMajor> inMap(inData);
456 const Eigen::JacobiSVD<ColMajor, options> svd(inMap);
457 if (svd.info() != Eigen::Success)
458 {
459 itkGenericExceptionMacro("itk::bridge::Math::SVD failed; input is likely non-finite (NaN/Inf).");
460 }
461
462 Eigen::Map<RowMajor> uMap(uData);
463 Eigen::Map<RowMajor> vMap(vData);
464 uMap = svd.matrixU();
465 vMap = svd.matrixV();
466 for (unsigned int i = 0; i < VDim; ++i)
467 {
468 wData[i] = svd.singularValues()[i];
469 }
470 }
471}
472
473// Rectangular SVD via BDCSVD with thin U/V (Eigen's general engine for non-square
474// inputs). For an m x n input: U is m x k, V is n x k, W has length k = min(m, n).
475// Throws on a failed decomposition.
476template <typename TReal>
477void
478RectangularSVDEigen(const TReal * inData,
479 unsigned int rows,
480 unsigned int cols,
481 TReal * uData,
482 TReal * wData,
483 TReal * vData)
484{
485 using RowMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
486 using ColMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic>;
487 constexpr int thin = Eigen::ComputeThinU | Eigen::ComputeThinV;
488 const unsigned int k = (rows < cols) ? rows : cols;
489
490 const Eigen::Map<const RowMajor> inMap(inData, rows, cols);
491 const Eigen::BDCSVD<ColMajor, thin> svd(inMap);
492 if (svd.info() != Eigen::Success)
493 {
494 itkGenericExceptionMacro("itk::bridge::Math::SVD failed; input is likely non-finite (NaN/Inf).");
495 }
496 Eigen::Map<RowMajor> uMap(uData, rows, k);
497 Eigen::Map<RowMajor> vMap(vData, cols, k);
498 uMap = svd.matrixU();
499 vMap = svd.matrixV();
500 for (unsigned int i = 0; i < k; ++i)
501 {
502 wData[i] = svd.singularValues()[i];
503 }
504}
505} // namespace detail
506
507// The Eigen JacobiSVD/BDCSVD instantiations behind the detail engines dominate
508// compile memory: without pre-instantiation, GCC needs ~2 GB per translation unit
509// that inverts an itk::Matrix. Declare the common specializations extern and define
510// them once in itkBridgeMathSVD.cxx so consumers link instead of re-instantiating them.
511#if defined(ITKCommon_EXPORTS)
512# define ITKCommon_EXPORT_EXPLICIT ITK_TEMPLATE_EXPORT
513#else
514# define ITKCommon_EXPORT_EXPLICIT ITKCommon_EXPORT
515#endif
516
517#define ITK_MATH_SVD_FIXED_DIMS(F) F(1) F(2) F(3) F(4) F(5) F(6)
518
519namespace detail
520{
521ITK_GCC_PRAGMA_DIAG_PUSH()
522ITK_GCC_PRAGMA_DIAG(ignored "-Wattributes")
523
524#define ITK_MATH_SVD_EXTERN_FIXED(D) \
525 extern template ITKCommon_EXPORT_EXPLICIT void SquareSVDEigen<D, float>(const float *, float *, float *, float *); \
526 extern template ITKCommon_EXPORT_EXPLICIT void SquareSVDEigen<D, double>( \
527 const double *, double *, double *, double *);
529#undef ITK_MATH_SVD_EXTERN_FIXED
530
531extern template ITKCommon_EXPORT_EXPLICIT void
532DynamicSquareSVDEigen<float>(const float *, unsigned int, float *, float *, float *);
533extern template ITKCommon_EXPORT_EXPLICIT void
534DynamicSquareSVDEigen<double>(const double *, unsigned int, double *, double *, double *);
535extern template ITKCommon_EXPORT_EXPLICIT void
536RectangularSVDEigen<float>(const float *, unsigned int, unsigned int, float *, float *, float *);
537extern template ITKCommon_EXPORT_EXPLICIT void
538RectangularSVDEigen<double>(const double *, unsigned int, unsigned int, double *, double *, double *);
539
540ITK_GCC_PRAGMA_DIAG_POP()
541} // namespace detail
542
543#undef ITKCommon_EXPORT_EXPLICIT
544
575template <typename TReal, unsigned int VDim>
577SVD(const vnl_matrix_fixed<TReal, VDim, VDim> & A, bool canonicalizeSigns = true)
578{
580 detail::SquareSVDEigen<VDim>(A.data_block(), result.U.data_block(), result.W.data_block(), result.V.data_block());
581 if (canonicalizeSigns)
582 {
584 }
585 return result;
586}
587
589template <typename TReal, unsigned int VDim>
590FixedSquareSVDResult<TReal, VDim>
591SVD(const Matrix<TReal, VDim, VDim> & A, bool canonicalizeSigns = true)
592{
593 return SVD<TReal, VDim>(A.GetVnlMatrix(), canonicalizeSigns);
594}
595
599template <typename TReal, unsigned int VRows, unsigned int VCols, typename = std::enable_if_t<VRows != VCols>>
600FixedRectangularSVDResult<TReal, VRows, VCols>
601SVD(const vnl_matrix_fixed<TReal, VRows, VCols> & A, bool canonicalizeSigns = true)
602{
605 A.data_block(), VRows, VCols, result.U.data_block(), result.W.data_block(), result.V.data_block());
606 if (canonicalizeSigns)
607 {
609 }
610 return result;
611}
612
614template <typename TReal, unsigned int VRows, unsigned int VCols, typename = std::enable_if_t<VRows != VCols>>
615FixedRectangularSVDResult<TReal, VRows, VCols>
616SVD(const Matrix<TReal, VRows, VCols> & A, bool canonicalizeSigns = true)
617{
618 return SVD<TReal, VRows, VCols>(A.GetVnlMatrix(), canonicalizeSigns);
619}
620
624template <typename TReal>
625SVDResult<TReal>
626SVD(const vnl_matrix<TReal> & A, bool canonicalizeSigns = true)
627{
628 const unsigned int rows = A.rows();
629 const unsigned int cols = A.cols();
630 if (rows == 0 || cols == 0)
631 {
632 itkGenericExceptionMacro("itk::bridge::Math::SVD requires a non-empty matrix.");
633 }
634 const unsigned int k = (rows < cols) ? rows : cols;
635 SVDResult<TReal> result;
636 result.U.set_size(rows, k);
637 result.V.set_size(cols, k);
638 result.W.set_size(k);
639 if (rows == cols)
640 {
642 A.data_block(), rows, result.U.data_block(), result.W.data_block(), result.V.data_block());
643 }
644 else
645 {
647 A.data_block(), rows, cols, result.U.data_block(), result.W.data_block(), result.V.data_block());
648 }
649 if (canonicalizeSigns)
650 {
652 }
653 return result;
654}
655
656} // namespace Math
657} // namespace itk::bridge
658
659#endif // itkBridgeMathSVD_h
A templated class holding a M x N size Matrix.
Definition itkMatrix.h:71
InternalMatrixType & GetVnlMatrix()
Definition itkMatrix.h:232
FixedSquareSVDResult< TReal, VDim > SVD(const vnl_matrix_fixed< TReal, VDim, VDim > &A, bool canonicalizeSigns=true)
Singular value decomposition A = U diag(W) V^T, backed by Eigen.
#define ITKCommon_EXPORT_EXPLICIT
#define ITK_MATH_SVD_EXTERN_FIXED(D)
#define ITK_MATH_SVD_FIXED_DIMS(F)
Cholesky-based linear algebra for symmetric matrices, backed by Eigen.
template void DynamicSquareSVDEigen< float >(const float *, unsigned int, float *, float *, float *)
TReal ResolveRcond(TReal rcond, unsigned int n)
auto Recompose(const TMatrixU &U, const TVector &W, const TMatrixV &V, TReal rcond)
void RectangularSVDEigen(const TReal *inData, unsigned int rows, unsigned int cols, TReal *uData, TReal *wData, TReal *vData)
auto DeterminantMagnitude(const TVector &W) -> std::decay_t< decltype(W[0])>
auto WellCondition(const TVector &W) -> std::decay_t< decltype(W[0])>
constexpr unsigned int kFixedSVDMaxDim
auto SolveLinear(const TMatrixU &U, const TVector &W, const TMatrixV &V, const TVectorB &b, TReal rcond)
unsigned int NumericalRank(const TVector &W, TReal rcond)
constexpr unsigned int kJacobiMaxDim
void SquareSVDEigen(const TReal *inData, TReal *uData, TReal *wData, TReal *vData)
template void DynamicSquareSVDEigen< double >(const double *, unsigned int, double *, double *, double *)
void DynamicSquareSVDEigen(const TReal *inData, unsigned int n, TReal *uData, TReal *wData, TReal *vData)
auto RecomposeWith(const TMatrixU &U, const TVector &modifiedW, const TMatrixV &V)
auto PseudoInverse(const TMatrixU &U, const TVector &W, const TMatrixV &V, TReal rcond)
template void RectangularSVDEigen< double >(const double *, unsigned int, unsigned int, double *, double *, double *)
template void RectangularSVDEigen< float >(const float *, unsigned int, unsigned int, float *, float *, float *)
void CanonicalizeColumnSignsPaired(TMatrixU &u, TMatrixV &paired)
Convenience wrappers over third-party numerical backends, provided as a migration aid rather than as ...
The "itk" namespace contains all Insight Segmentation and Registration Toolkit (ITK) classes....
vnl_vector_fixed< TReal, VCols > NullVector() const
vnl_matrix_fixed< TReal, VRows, K > U
vnl_vector_fixed< TReal, VCols > Solve(const vnl_vector_fixed< TReal, VRows > &b, TReal rcond=TReal{ -1 }) const
vnl_matrix_fixed< TReal, VRows, VCols > Recompose(TReal rcond=TReal{ -1 }) const
unsigned int Rank(TReal rcond=TReal{ -1 }) const
vnl_matrix_fixed< TReal, VCols, VRows > PseudoInverse(TReal rcond=TReal{ -1 }) const
vnl_matrix_fixed< TReal, VRows, VCols > RecomposeWith(const vnl_vector_fixed< TReal, K > &modifiedW) const
vnl_matrix_fixed< TReal, VCols, K > V
vnl_vector_fixed< TReal, VDim > Solve(const vnl_vector_fixed< TReal, VDim > &b, TReal rcond=TReal{ -1 }) const
vnl_matrix_fixed< TReal, VDim, VDim > RecomposeWith(const vnl_vector_fixed< TReal, VDim > &modifiedW) const
vnl_matrix_fixed< TReal, VDim, VDim > Recompose(TReal rcond=TReal{ -1 }) const
vnl_matrix_fixed< TReal, VDim, VDim > U
vnl_matrix_fixed< TReal, VDim, VDim > V
unsigned int Rank(TReal rcond=TReal{ -1 }) const
vnl_vector_fixed< TReal, VDim > W
vnl_matrix_fixed< TReal, VDim, VDim > PseudoInverse(TReal rcond=TReal{ -1 }) const
vnl_vector_fixed< TReal, VDim > NullVector() const
unsigned int Rank(TReal rcond=TReal{ -1 }) const
vnl_vector< TReal > NullVector() const
vnl_matrix< TReal > RecomposeWith(const vnl_vector< TReal > &modifiedW) const
vnl_matrix< TReal > Recompose(TReal rcond=TReal{ -1 }) const
vnl_matrix< TReal > PseudoInverse(TReal rcond=TReal{ -1 }) const
vnl_vector< TReal > Solve(const vnl_vector< TReal > &b, TReal rcond=TReal{ -1 }) const