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"
29#include ITK_EIGEN(Dense)
39template <
typename T,
unsigned int VRows,
unsigned int VColumns>
48template <
typename TReal>
52 return rcond < TReal{ 0 } ?
static_cast<TReal
>(n) * std::numeric_limits<TReal>::epsilon() : rcond;
57template <
typename TMatrixU,
typename TVector,
typename TMatrixV,
typename TReal>
59PseudoInverse(
const TMatrixU & U,
const TVector & W,
const TMatrixV & V, TReal rcond)
61 const unsigned int k = W.size();
65 for (
unsigned int col = 0; col < k; ++col)
67 const TReal s = (W[col] > tol) ? TReal{ 1 } / W[col] : TReal{ 0 };
68 for (
unsigned int i = 0; i < scaledV.rows(); ++i)
73 return scaledV * U.transpose();
77template <
typename TMatrixU,
typename TVector,
typename TMatrixV,
typename TVectorB,
typename TReal>
79SolveLinear(
const TMatrixU & U,
const TVector & W,
const TMatrixV & V,
const TVectorB & b, TReal rcond)
81 const unsigned int n = W.size();
83 auto utb = U.transpose() * b;
84 for (
unsigned int k = 0; k < n; ++k)
86 utb[k] = (W[k] > tol) ? utb[k] / W[k] : TReal{ 0 };
91template <
typename TVector,
typename TReal>
95 const unsigned int n = W.size();
97 unsigned int count = 0;
98 for (
unsigned int k = 0; k < n; ++k)
110template <
typename TMatrixU,
typename TVector,
typename TMatrixV,
typename TReal>
112Recompose(
const TMatrixU & U,
const TVector & W,
const TMatrixV & V, TReal rcond)
114 const unsigned int k = W.size();
116 TMatrixU scaledU = U;
117 for (
unsigned int col = 0; col < k; ++col)
119 const TReal s = (W[col] > tol) ? W[col] : TReal{ 0 };
120 for (
unsigned int i = 0; i < scaledU.rows(); ++i)
122 scaledU(i, col) *= s;
125 return scaledU * V.transpose();
130template <
typename TMatrixU,
typename TVector,
typename TMatrixV>
132RecomposeWith(
const TMatrixU & U,
const TVector & modifiedW,
const TMatrixV & V)
134 TMatrixU scaledU = U;
135 for (
unsigned int col = 0; col < modifiedW.size(); ++col)
137 for (
unsigned int i = 0; i < scaledU.rows(); ++i)
139 scaledU(i, col) *= modifiedW[col];
142 return scaledU * V.transpose();
146template <
typename TVector>
150 const unsigned int k = W.size();
151 if (k == 0 || W[0] == 0)
155 return W[k - 1] / W[0];
159template <
typename TVector>
163 std::decay_t<
decltype(W[0])> product{ 1 };
164 for (
unsigned int k = 0; k < W.size(); ++k)
174template <
typename TReal,
unsigned int VDim>
177 vnl_matrix_fixed<TReal, VDim, VDim>
U{};
178 vnl_vector_fixed<TReal, VDim>
W{};
179 vnl_matrix_fixed<TReal, VDim, VDim>
V{};
182 vnl_matrix_fixed<TReal, VDim, VDim>
189 vnl_vector_fixed<TReal, VDim>
190 Solve(
const vnl_vector_fixed<TReal, VDim> & b, TReal rcond = TReal{ -1 })
const
197 Rank(TReal rcond = TReal{ -1 })
const
203 vnl_matrix_fixed<TReal, VDim, VDim>
210 vnl_matrix_fixed<TReal, VDim, VDim>
218 vnl_vector_fixed<TReal, VDim>
221 return V.get_column(VDim - 1);
242template <
typename TReal>
245 vnl_matrix<TReal>
U{};
246 vnl_vector<TReal>
W{};
247 vnl_matrix<TReal>
V{};
258 Solve(
const vnl_vector<TReal> & b, TReal rcond = TReal{ -1 })
const
265 Rank(TReal rcond = TReal{ -1 })
const
291 if (
U.rows() <
V.rows())
293 itkGenericExceptionMacro(
294 "NullVector() requires rows >= cols; the thin V of an underdetermined input does not span the nullspace.");
296 return V.get_column(
V.cols() - 1);
317template <
typename TReal,
unsigned int VRows,
unsigned int VCols>
320 static constexpr unsigned int K = (VRows < VCols) ? VRows : VCols;
322 vnl_matrix_fixed<TReal, VRows, K>
U{};
323 vnl_vector_fixed<TReal, K>
W{};
324 vnl_matrix_fixed<TReal, VCols, K>
V{};
327 vnl_matrix_fixed<TReal, VCols, VRows>
334 vnl_vector_fixed<TReal, VCols>
335 Solve(
const vnl_vector_fixed<TReal, VRows> & b, TReal rcond = TReal{ -1 })
const
342 Rank(TReal rcond = TReal{ -1 })
const
348 vnl_matrix_fixed<TReal, VRows, VCols>
355 vnl_matrix_fixed<TReal, VRows, VCols>
364 vnl_vector_fixed<TReal, VCols>
367 static_assert(VRows >= VCols,
"NullVector() requires VRows >= VCols (thin V cannot span the nullspace).");
368 return V.get_column(
K - 1);
398template <
typename TReal>
402 using RowMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
403 using ColMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic>;
404 constexpr int full = Eigen::ComputeFullU | Eigen::ComputeFullV;
406 const Eigen::Map<const RowMajor> inMap(inData, n, n);
407 Eigen::Map<RowMajor> uMap(uData, n, n);
408 Eigen::Map<RowMajor> vMap(vData, n, n);
410 const auto extract = [&](
const auto & svd) {
411 if (svd.info() != Eigen::Success)
413 itkGenericExceptionMacro(
"itk::Math::SVD failed; input is likely non-finite (NaN/Inf).");
415 uMap = svd.matrixU();
416 vMap = svd.matrixV();
417 for (
unsigned int i = 0; i < n; ++i)
419 wData[i] = svd.singularValues()[i];
425 extract(Eigen::JacobiSVD < ColMajor, full | Eigen::NoQRPreconditioner > (inMap));
429 extract(Eigen::BDCSVD<ColMajor, full>(inMap));
435template <
unsigned int VDim,
typename TReal>
448 using RowMajor = Eigen::Matrix<TReal, VDim, VDim, Eigen::RowMajor>;
449 using ColMajor = Eigen::Matrix<TReal, VDim, VDim>;
450 constexpr int options = Eigen::ComputeFullU | Eigen::ComputeFullV | Eigen::NoQRPreconditioner;
452 const Eigen::Map<const RowMajor> inMap(inData);
453 const Eigen::JacobiSVD<ColMajor, options> svd(inMap);
454 if (svd.info() != Eigen::Success)
456 itkGenericExceptionMacro(
"itk::Math::SVD failed; input is likely non-finite (NaN/Inf).");
459 Eigen::Map<RowMajor> uMap(uData);
460 Eigen::Map<RowMajor> vMap(vData);
461 uMap = svd.matrixU();
462 vMap = svd.matrixV();
463 for (
unsigned int i = 0; i < VDim; ++i)
465 wData[i] = svd.singularValues()[i];
473template <
typename TReal>
482 using RowMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
483 using ColMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic>;
484 constexpr int thin = Eigen::ComputeThinU | Eigen::ComputeThinV;
485 const unsigned int k = (rows < cols) ? rows : cols;
487 const Eigen::Map<const RowMajor> inMap(inData, rows, cols);
488 const Eigen::BDCSVD<ColMajor, thin> svd(inMap);
489 if (svd.info() != Eigen::Success)
491 itkGenericExceptionMacro(
"itk::Math::SVD failed; input is likely non-finite (NaN/Inf).");
493 Eigen::Map<RowMajor> uMap(uData, rows, k);
494 Eigen::Map<RowMajor> vMap(vData, cols, k);
495 uMap = svd.matrixU();
496 vMap = svd.matrixV();
497 for (
unsigned int i = 0; i < k; ++i)
499 wData[i] = svd.singularValues()[i];
508#if defined(ITKCommon_EXPORTS)
509# define ITKCommon_EXPORT_EXPLICIT ITK_TEMPLATE_EXPORT
511# define ITKCommon_EXPORT_EXPLICIT ITKCommon_EXPORT
514#define ITK_MATH_SVD_FIXED_DIMS(F) F(1) F(2) F(3) F(4) F(5) F(6)
518ITK_GCC_PRAGMA_DIAG_PUSH()
519ITK_GCC_PRAGMA_DIAG(ignored
"-Wattributes")
521#define ITK_MATH_SVD_EXTERN_FIXED(D) \
522 extern template ITKCommon_EXPORT_EXPLICIT void SquareSVDEigen<D, float>(const float *, float *, float *, float *); \
523 extern template ITKCommon_EXPORT_EXPLICIT void SquareSVDEigen<D, double>( \
524 const double *, double *, double *, double *);
526#undef ITK_MATH_SVD_EXTERN_FIXED
537ITK_GCC_PRAGMA_DIAG_POP()
540#undef ITKCommon_EXPORT_EXPLICIT
572template <
typename TReal,
unsigned int VDim>
574SVD(
const vnl_matrix_fixed<TReal, VDim, VDim> & A,
bool canonicalizeSigns =
true)
578 if (canonicalizeSigns)
586template <
typename TReal,
unsigned int VDim>
587FixedSquareSVDResult<TReal, VDim>
596template <
typename TReal,
unsigned int VRows,
unsigned int VCols,
typename = std::enable_if_t<VRows != VCols>>
597FixedRectangularSVDResult<TReal, VRows, VCols>
598SVD(
const vnl_matrix_fixed<TReal, VRows, VCols> & A,
bool canonicalizeSigns =
true)
602 A.data_block(), VRows, VCols, result.
U.data_block(), result.
W.data_block(), result.
V.data_block());
603 if (canonicalizeSigns)
611template <
typename TReal,
unsigned int VRows,
unsigned int VCols,
typename = std::enable_if_t<VRows != VCols>>
612FixedRectangularSVDResult<TReal, VRows, VCols>
621template <
typename TReal>
623SVD(
const vnl_matrix<TReal> & A,
bool canonicalizeSigns =
true)
625 const unsigned int rows = A.rows();
626 const unsigned int cols = A.cols();
627 if (rows == 0 || cols == 0)
629 itkGenericExceptionMacro(
"itk::Math::SVD requires a non-empty matrix.");
631 const unsigned int k = (rows < cols) ? rows : cols;
633 result.
U.set_size(rows, k);
634 result.
V.set_size(cols, k);
635 result.
W.set_size(k);
639 A.data_block(), rows, result.
U.data_block(), result.
W.data_block(), result.
V.data_block());
644 A.data_block(), rows, cols, result.
U.data_block(), result.
W.data_block(), result.
V.data_block());
646 if (canonicalizeSigns)
A templated class holding a M x N size Matrix.
InternalMatrixType & GetVnlMatrix()
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.
void DynamicSquareSVDEigen(const TReal *inData, unsigned int n, TReal *uData, TReal *wData, TReal *vData)
void RectangularSVDEigen(const TReal *inData, unsigned int rows, unsigned int cols, TReal *uData, TReal *wData, TReal *vData)
template void RectangularSVDEigen< double >(const double *, unsigned int, unsigned int, double *, double *, double *)
TReal ResolveRcond(TReal rcond, unsigned int n)
auto PseudoInverse(const TMatrixU &U, const TVector &W, const TMatrixV &V, TReal rcond)
auto RecomposeWith(const TMatrixU &U, const TVector &modifiedW, const TMatrixV &V)
auto WellCondition(const TVector &W) -> std::decay_t< decltype(W[0])>
auto DeterminantMagnitude(const TVector &W) -> std::decay_t< decltype(W[0])>
template void DynamicSquareSVDEigen< double >(const double *, unsigned int, double *, double *, double *)
constexpr unsigned int kFixedSVDMaxDim
constexpr unsigned int kJacobiMaxDim
template void RectangularSVDEigen< float >(const float *, unsigned int, unsigned int, float *, float *, float *)
auto Recompose(const TMatrixU &U, const TVector &W, const TMatrixV &V, TReal rcond)
void SquareSVDEigen(const TReal *inData, TReal *uData, TReal *wData, TReal *vData)
unsigned int NumericalRank(const TVector &W, TReal rcond)
template void DynamicSquareSVDEigen< float >(const float *, unsigned int, float *, float *, float *)
auto SolveLinear(const TMatrixU &U, const TVector &W, const TMatrixV &V, const TVectorB &b, TReal rcond)
void CanonicalizeColumnSignsPaired(TMatrixU &u, TMatrixV &paired)
The "itk" namespace contains all Insight Segmentation and Registration Toolkit (ITK) classes....
vnl_vector_fixed< TReal, K > W
TReal WellCondition() const
vnl_matrix_fixed< TReal, VCols, VRows > PseudoInverse(TReal rcond=TReal{ -1 }) const
vnl_matrix_fixed< TReal, VRows, K > U
vnl_matrix_fixed< TReal, VCols, K > V
vnl_matrix_fixed< TReal, VRows, VCols > Recompose(TReal rcond=TReal{ -1 }) const
vnl_matrix_fixed< TReal, VRows, VCols > RecomposeWith(const vnl_vector_fixed< TReal, K > &modifiedW) const
unsigned int Rank(TReal rcond=TReal{ -1 }) const
vnl_vector_fixed< TReal, VCols > NullVector() const
static constexpr unsigned int K
vnl_vector_fixed< TReal, VCols > Solve(const vnl_vector_fixed< TReal, VRows > &b, TReal rcond=TReal{ -1 }) const
TReal DeterminantMagnitude() const
vnl_matrix_fixed< TReal, VDim, VDim > PseudoInverse(TReal rcond=TReal{ -1 }) const
TReal DeterminantMagnitude() const
vnl_vector_fixed< TReal, VDim > Solve(const vnl_vector_fixed< TReal, VDim > &b, TReal rcond=TReal{ -1 }) const
vnl_matrix_fixed< TReal, VDim, VDim > Recompose(TReal rcond=TReal{ -1 }) const
vnl_vector_fixed< TReal, VDim > NullVector() const
vnl_vector_fixed< TReal, VDim > W
TReal WellCondition() const
vnl_matrix_fixed< TReal, VDim, VDim > U
vnl_matrix_fixed< TReal, VDim, VDim > V
unsigned int Rank(TReal rcond=TReal{ -1 }) const
vnl_matrix_fixed< TReal, VDim, VDim > RecomposeWith(const vnl_vector_fixed< TReal, VDim > &modifiedW) const
vnl_matrix< TReal > Recompose(TReal rcond=TReal{ -1 }) const
TReal WellCondition() const
vnl_matrix< TReal > PseudoInverse(TReal rcond=TReal{ -1 }) const
unsigned int Rank(TReal rcond=TReal{ -1 }) const
TReal DeterminantMagnitude() const
vnl_vector< TReal > NullVector() const
vnl_matrix< TReal > RecomposeWith(const vnl_vector< TReal > &modifiedW) const
vnl_vector< TReal > Solve(const vnl_vector< TReal > &b, TReal rcond=TReal{ -1 }) const