18#ifndef itkBridgeMathSVD_h
19#define itkBridgeMathSVD_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"
29#include ITK_EIGEN(Dense)
39template <
typename T,
unsigned int VRows,
unsigned int VColumns>
51template <
typename TReal>
55 return rcond < TReal{ 0 } ?
static_cast<TReal
>(n) * std::numeric_limits<TReal>::epsilon() : rcond;
60template <
typename TMatrixU,
typename TVector,
typename TMatrixV,
typename TReal>
62PseudoInverse(
const TMatrixU & U,
const TVector & W,
const TMatrixV & V, TReal rcond)
64 const unsigned int k = W.size();
68 for (
unsigned int col = 0; col < k; ++col)
70 const TReal s = (W[col] > tol) ? TReal{ 1 } / W[col] : TReal{ 0 };
71 for (
unsigned int i = 0; i < scaledV.rows(); ++i)
76 return scaledV * U.transpose();
80template <
typename TMatrixU,
typename TVector,
typename TMatrixV,
typename TVectorB,
typename TReal>
82SolveLinear(
const TMatrixU & U,
const TVector & W,
const TMatrixV & V,
const TVectorB & b, TReal rcond)
84 const unsigned int n = W.size();
86 auto utb = U.transpose() * b;
87 for (
unsigned int k = 0; k < n; ++k)
89 utb[k] = (W[k] > tol) ? utb[k] / W[k] : TReal{ 0 };
94template <
typename TVector,
typename TReal>
98 const unsigned int n = W.size();
100 unsigned int count = 0;
101 for (
unsigned int k = 0; k < n; ++k)
113template <
typename TMatrixU,
typename TVector,
typename TMatrixV,
typename TReal>
115Recompose(
const TMatrixU & U,
const TVector & W,
const TMatrixV & V, TReal rcond)
117 const unsigned int k = W.size();
119 TMatrixU scaledU = U;
120 for (
unsigned int col = 0; col < k; ++col)
122 const TReal s = (W[col] > tol) ? W[col] : TReal{ 0 };
123 for (
unsigned int i = 0; i < scaledU.rows(); ++i)
125 scaledU(i, col) *= s;
128 return scaledU * V.transpose();
133template <
typename TMatrixU,
typename TVector,
typename TMatrixV>
135RecomposeWith(
const TMatrixU & U,
const TVector & modifiedW,
const TMatrixV & V)
137 TMatrixU scaledU = U;
138 for (
unsigned int col = 0; col < modifiedW.size(); ++col)
140 for (
unsigned int i = 0; i < scaledU.rows(); ++i)
142 scaledU(i, col) *= modifiedW[col];
145 return scaledU * V.transpose();
149template <
typename TVector>
153 const unsigned int k = W.size();
154 if (k == 0 || W[0] == 0)
158 return W[k - 1] / W[0];
162template <
typename TVector>
166 std::decay_t<
decltype(W[0])> product{ 1 };
167 for (
unsigned int k = 0; k < W.size(); ++k)
177template <
typename TReal,
unsigned int VDim>
180 vnl_matrix_fixed<TReal, VDim, VDim>
U{};
181 vnl_vector_fixed<TReal, VDim>
W{};
182 vnl_matrix_fixed<TReal, VDim, VDim>
V{};
185 vnl_matrix_fixed<TReal, VDim, VDim>
192 vnl_vector_fixed<TReal, VDim>
193 Solve(
const vnl_vector_fixed<TReal, VDim> & b, TReal rcond = TReal{ -1 })
const
200 Rank(TReal rcond = TReal{ -1 })
const
206 vnl_matrix_fixed<TReal, VDim, VDim>
213 vnl_matrix_fixed<TReal, VDim, VDim>
221 vnl_vector_fixed<TReal, VDim>
224 return V.get_column(VDim - 1);
245template <
typename TReal>
248 vnl_matrix<TReal>
U{};
249 vnl_vector<TReal>
W{};
250 vnl_matrix<TReal>
V{};
261 Solve(
const vnl_vector<TReal> & b, TReal rcond = TReal{ -1 })
const
268 Rank(TReal rcond = TReal{ -1 })
const
294 if (
U.rows() <
V.rows())
296 itkGenericExceptionMacro(
297 "NullVector() requires rows >= cols; the thin V of an underdetermined input does not span the nullspace.");
299 return V.get_column(
V.cols() - 1);
320template <
typename TReal,
unsigned int VRows,
unsigned int VCols>
323 static constexpr unsigned int K = (VRows < VCols) ? VRows : VCols;
325 vnl_matrix_fixed<TReal, VRows, K>
U{};
326 vnl_vector_fixed<TReal, K>
W{};
327 vnl_matrix_fixed<TReal, VCols, K>
V{};
330 vnl_matrix_fixed<TReal, VCols, VRows>
337 vnl_vector_fixed<TReal, VCols>
338 Solve(
const vnl_vector_fixed<TReal, VRows> & b, TReal rcond = TReal{ -1 })
const
345 Rank(TReal rcond = TReal{ -1 })
const
351 vnl_matrix_fixed<TReal, VRows, VCols>
358 vnl_matrix_fixed<TReal, VRows, VCols>
367 vnl_vector_fixed<TReal, VCols>
370 static_assert(VRows >= VCols,
"NullVector() requires VRows >= VCols (thin V cannot span the nullspace).");
371 return V.get_column(
K - 1);
401template <
typename TReal>
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;
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);
413 const auto extract = [&](
const auto & svd) {
414 if (svd.info() != Eigen::Success)
416 itkGenericExceptionMacro(
"itk::bridge::Math::SVD failed; input is likely non-finite (NaN/Inf).");
418 uMap = svd.matrixU();
419 vMap = svd.matrixV();
420 for (
unsigned int i = 0; i < n; ++i)
422 wData[i] = svd.singularValues()[i];
428 extract(Eigen::JacobiSVD < ColMajor, full | Eigen::NoQRPreconditioner > (inMap));
432 extract(Eigen::BDCSVD<ColMajor, full>(inMap));
438template <
unsigned int VDim,
typename TReal>
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;
455 const Eigen::Map<const RowMajor> inMap(inData);
456 const Eigen::JacobiSVD<ColMajor, options> svd(inMap);
457 if (svd.info() != Eigen::Success)
459 itkGenericExceptionMacro(
"itk::bridge::Math::SVD failed; input is likely non-finite (NaN/Inf).");
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)
468 wData[i] = svd.singularValues()[i];
476template <
typename TReal>
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;
490 const Eigen::Map<const RowMajor> inMap(inData, rows, cols);
491 const Eigen::BDCSVD<ColMajor, thin> svd(inMap);
492 if (svd.info() != Eigen::Success)
494 itkGenericExceptionMacro(
"itk::bridge::Math::SVD failed; input is likely non-finite (NaN/Inf).");
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)
502 wData[i] = svd.singularValues()[i];
511#if defined(ITKCommon_EXPORTS)
512# define ITKCommon_EXPORT_EXPLICIT ITK_TEMPLATE_EXPORT
514# define ITKCommon_EXPORT_EXPLICIT ITKCommon_EXPORT
517#define ITK_MATH_SVD_FIXED_DIMS(F) F(1) F(2) F(3) F(4) F(5) F(6)
521ITK_GCC_PRAGMA_DIAG_PUSH()
522ITK_GCC_PRAGMA_DIAG(ignored
"-Wattributes")
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
540ITK_GCC_PRAGMA_DIAG_POP()
543#undef ITKCommon_EXPORT_EXPLICIT
575template <
typename TReal,
unsigned int VDim>
577SVD(
const vnl_matrix_fixed<TReal, VDim, VDim> & A,
bool canonicalizeSigns =
true)
581 if (canonicalizeSigns)
589template <
typename TReal,
unsigned int VDim>
590FixedSquareSVDResult<TReal, VDim>
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)
605 A.data_block(), VRows, VCols, result.
U.data_block(), result.
W.data_block(), result.
V.data_block());
606 if (canonicalizeSigns)
614template <
typename TReal,
unsigned int VRows,
unsigned int VCols,
typename = std::enable_if_t<VRows != VCols>>
615FixedRectangularSVDResult<TReal, VRows, VCols>
624template <
typename TReal>
626SVD(
const vnl_matrix<TReal> & A,
bool canonicalizeSigns =
true)
628 const unsigned int rows = A.rows();
629 const unsigned int cols = A.cols();
630 if (rows == 0 || cols == 0)
632 itkGenericExceptionMacro(
"itk::bridge::Math::SVD requires a non-empty matrix.");
634 const unsigned int k = (rows < cols) ? rows : cols;
636 result.
U.set_size(rows, k);
637 result.
V.set_size(cols, k);
638 result.
W.set_size(k);
642 A.data_block(), rows, result.
U.data_block(), result.
W.data_block(), result.
V.data_block());
647 A.data_block(), rows, cols, result.
U.data_block(), result.
W.data_block(), result.
V.data_block());
649 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.
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....
TReal WellCondition() const
vnl_vector_fixed< TReal, VCols > NullVector() const
TReal DeterminantMagnitude() 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
static constexpr unsigned int K
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_vector_fixed< TReal, K > W
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
TReal DeterminantMagnitude() const
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
TReal WellCondition() const
unsigned int Rank(TReal rcond=TReal{ -1 }) const
vnl_vector< TReal > NullVector() const
vnl_matrix< TReal > RecomposeWith(const vnl_vector< TReal > &modifiedW) const
TReal WellCondition() const
vnl_matrix< TReal > Recompose(TReal rcond=TReal{ -1 }) const
TReal DeterminantMagnitude() const
vnl_matrix< TReal > PseudoInverse(TReal rcond=TReal{ -1 }) const
vnl_vector< TReal > Solve(const vnl_vector< TReal > &b, TReal rcond=TReal{ -1 }) const