ITK 6.0.0
Insight Toolkit
 
Loading...
Searching...
No Matches
itkMathLDLT.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 itkMathLDLT_h
19#define itkMathLDLT_h
20
21#include "itkMacro.h"
22#include "itkArray.h"
23#include "itkArray2D.h"
24#include "itkMatrix.h"
25#include "itkVector.h"
26#include "vnl/vnl_matrix.h"
27#include "vnl/vnl_matrix_fixed.h"
28#include "vnl/vnl_vector.h"
29#include "vnl/vnl_vector_fixed.h"
30
31#include "itk_eigen.h"
32#include ITK_EIGEN(Dense)
33
34#include <limits>
35
49#define ITK_MATH_HAS_SOLVE_SYMMETRIC 1
50
51namespace itk
52{
53namespace Math
54{
55namespace detail
56{
57// Solve the symmetric system A x = b via Eigen's robust LDLT (Bunch-Kaufman
58// pivoting). A is assumed symmetric; column-major mapping of ITK's row-major
59// storage is exact for a symmetric matrix (A == A^T). Throws on a non-finite
60// input.
61template <typename TReal>
62void
63SolveSymmetricLDLTEigen(const TReal * aData, const TReal * bData, unsigned int n, TReal * xData)
64{
65 using ColMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic>;
66 using Vector = Eigen::Matrix<TReal, Eigen::Dynamic, 1>;
67
68 const Eigen::Map<const ColMajor> aMap(aData, n, n);
69 const Eigen::Map<const Vector> bMap(bData, n);
70
71 const Eigen::LDLT<ColMajor> ldlt(aMap);
72 if (ldlt.info() != Eigen::Success)
73 {
74 itkGenericExceptionMacro("itk::Math::SolveSymmetric failed; input is likely non-finite (NaN/Inf).");
75 }
76 Eigen::Map<Vector>(xData, n) = ldlt.solve(bMap);
77}
78
79// Solve A X = B for symmetric A (n x n) and B (n x m) with a SINGLE LDLT
80// factorization (all columns solved together). A is symmetric so its col-major
81// map of ITK's row-major storage is exact; B and X are mapped row-major. Throws
82// on a non-finite input.
83template <typename TReal>
84void
85SolveSymmetricMatrixLDLTEigen(const TReal * aData, const TReal * bData, unsigned int n, unsigned int m, TReal * xData)
86{
87 using ColMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic>;
88 using RowMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
89
90 const Eigen::Map<const ColMajor> aMap(aData, n, n);
91 const Eigen::Map<const RowMajor> bMap(bData, n, m);
92
93 const Eigen::LDLT<ColMajor> ldlt(aMap);
94 if (ldlt.info() != Eigen::Success)
95 {
96 itkGenericExceptionMacro("itk::Math::SolveSymmetric failed; input is likely non-finite (NaN/Inf).");
97 }
98 Eigen::Map<RowMajor>(xData, n, m) = ldlt.solve(bMap);
99}
100
101// Invert symmetric A via LDLT (solves A X = I). Eigen's LDLT::solve silently
102// pseudo-inverts zero pivots, so singularity is rejected here from vectorD().
103template <typename TReal>
104void
105InverseSymmetricLDLTEigen(const TReal * aData, unsigned int n, TReal * invData)
106{
107 using ColMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic>;
108 using RowMajor = Eigen::Matrix<TReal, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
109
110 const Eigen::Map<const ColMajor> aMap(aData, n, n);
111
112 const Eigen::LDLT<ColMajor> ldlt(aMap);
113 if (ldlt.info() != Eigen::Success)
114 {
115 itkGenericExceptionMacro("itk::Math::InverseSymmetric failed; input is likely non-finite (NaN/Inf).");
116 }
117 const auto dAbs = ldlt.vectorD().cwiseAbs().eval();
118 const TReal dMax = dAbs.maxCoeff();
119 if (dMax == TReal{ 0 } || dAbs.minCoeff() <= dMax * static_cast<TReal>(n) * std::numeric_limits<TReal>::epsilon())
120 {
121 itkGenericExceptionMacro("itk::Math::InverseSymmetric failed; input matrix is singular.");
122 }
123 Eigen::Map<RowMajor>(invData, n, n) = ldlt.solve(ColMajor::Identity(n, n));
124}
125} // namespace detail
126
144template <typename TReal>
147{
148 const unsigned int n = A.rows();
149 if (n == 0 || A.cols() != n || b.size() != n)
150 {
151 itkGenericExceptionMacro("itk::Math::SolveSymmetric requires a non-empty square A and a matching b.");
152 }
153 Array<TReal> x(n);
154 detail::SolveSymmetricLDLTEigen<TReal>(A.data_block(), b.data_block(), n, x.data_block());
155 return x;
156}
157
159template <typename TReal, unsigned int VDim>
167
168// --- vnl convenience overloads ---------------------------------------------
169// Provided so consumers that still hold vnl types (e.g. legacy filters) can call
170// directly without wrapping. The ITK-typed overloads above are the forward-
171// looking interface; the very-long-term goal is to eliminate vnl from the ITK
172// API, so prefer the ITK-typed signatures in new code.
173
175template <typename TReal>
176vnl_vector<TReal>
177SolveSymmetric(const vnl_matrix<TReal> & A, const vnl_vector<TReal> & b)
178{
179 const unsigned int n = A.rows();
180 if (n == 0 || A.cols() != n || b.size() != n)
181 {
182 itkGenericExceptionMacro("itk::Math::SolveSymmetric requires a non-empty square A and a matching b.");
183 }
184 vnl_vector<TReal> x(n);
185 detail::SolveSymmetricLDLTEigen<TReal>(A.data_block(), b.data_block(), n, x.data_block());
186 return x;
187}
188
190template <typename TReal, unsigned int VDim>
191vnl_vector_fixed<TReal, VDim>
192SolveSymmetric(const vnl_matrix_fixed<TReal, VDim, VDim> & A, const vnl_vector_fixed<TReal, VDim> & b)
193{
194 vnl_vector_fixed<TReal, VDim> x;
195 detail::SolveSymmetricLDLTEigen<TReal>(A.data_block(), b.data_block(), VDim, x.data_block());
196 return x;
197}
198
199// --- multi-RHS solve and symmetric inverse ---------------------------------
200
204template <typename TReal>
207{
208 const unsigned int n = A.rows();
209 if (n == 0 || A.cols() != n || B.rows() != n)
210 {
211 itkGenericExceptionMacro("itk::Math::SolveSymmetric requires a non-empty square A and a matching B.");
212 }
213 Array2D<TReal> X(n, B.cols());
214 detail::SolveSymmetricMatrixLDLTEigen<TReal>(A.data_block(), B.data_block(), n, B.cols(), X.data_block());
215 return X;
216}
217
227template <typename TReal>
230{
231 const unsigned int n = A.rows();
232 if (n == 0 || A.cols() != n)
233 {
234 itkGenericExceptionMacro("itk::Math::InverseSymmetric requires a non-empty square A.");
235 }
236 Array2D<TReal> inv(n, n);
237 detail::InverseSymmetricLDLTEigen<TReal>(A.data_block(), n, inv.data_block());
238 return inv;
239}
240
242template <typename TReal>
243vnl_matrix<TReal>
244SolveSymmetric(const vnl_matrix<TReal> & A, const vnl_matrix<TReal> & B)
245{
246 const unsigned int n = A.rows();
247 if (n == 0 || A.cols() != n || B.rows() != n)
248 {
249 itkGenericExceptionMacro("itk::Math::SolveSymmetric requires a non-empty square A and a matching B.");
250 }
251 vnl_matrix<TReal> X(n, B.cols());
252 detail::SolveSymmetricMatrixLDLTEigen<TReal>(A.data_block(), B.data_block(), n, B.cols(), X.data_block());
253 return X;
254}
255
257template <typename TReal>
258vnl_matrix<TReal>
259InverseSymmetric(const vnl_matrix<TReal> & A)
260{
261 const unsigned int n = A.rows();
262 if (n == 0 || A.cols() != n)
263 {
264 itkGenericExceptionMacro("itk::Math::InverseSymmetric requires a non-empty square A.");
265 }
266 vnl_matrix<TReal> inv(n, n);
267 detail::InverseSymmetricLDLTEigen<TReal>(A.data_block(), n, inv.data_block());
268 return inv;
269}
270
271} // namespace Math
272} // namespace itk
273
274#endif // itkMathLDLT_h
Array2D class representing a 2D array.
Definition itkArray2D.h:43
Array class with size defined at construction time.
Definition itkArray.h:48
ValueType * GetDataPointer()
A templated class holding a M x N size Matrix.
Definition itkMatrix.h:53
InternalMatrixType & GetVnlMatrix()
Definition itkMatrix.h:214
A templated class holding a n-Dimensional vector.
Definition itkVector.h:63
Array2D< TReal > InverseSymmetric(const Array2D< TReal > &A)
Inverse of a symmetric matrix, backed by Eigen LDLT.
Array< TReal > SolveSymmetric(const Array2D< TReal > &A, const Array< TReal > &b)
Solve the symmetric linear system A x = b via LDLT, backed by Eigen.
void InverseSymmetricLDLTEigen(const TReal *aData, unsigned int n, TReal *invData)
void SolveSymmetricMatrixLDLTEigen(const TReal *aData, const TReal *bData, unsigned int n, unsigned int m, TReal *xData)
Definition itkMathLDLT.h:85
void SolveSymmetricLDLTEigen(const TReal *aData, const TReal *bData, unsigned int n, TReal *xData)
Definition itkMathLDLT.h:63
The "itk" namespace contains all Insight Segmentation and Registration Toolkit (ITK) classes....