Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions Documentation/Doxygen/BridgeNumerics.dox
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
\page BridgeNumericsPage The itk::bridge Numerics Namespace

\section BridgeNumericsIntent Intent

\c itk::bridge holds convenience wrappers over third-party numerical
solvers, currently Eigen. They exist to ease the initial migration of code
away from the deprecated VNL algorithms: a call site that used
\c vnl_svd can be moved to \c itk::bridge::Math::SVD as a mechanical,
low-risk edit, without the caller first learning Eigen's API.

\section BridgeNumericsNotCore Not part of the core ITK mission

These are a migration aid, not a numerics library. ITK does not undertake to
maintain, validate, optimize, or preserve the API or ABI of anything in
\c itk::bridge to the standard it applies to the rest of the toolkit.
Specifically:

\li The interfaces may change or be removed between releases without the
deprecation cycle that core ITK API changes receive.
\li The numerical behavior is that of the underlying backend. ITK does not
independently validate accuracy, conditioning, or convergence, and does
not guarantee bit-for-bit stability across backend versions.
\li Coverage is limited to what ITK's own migration needed. Absent
functionality will not necessarily be added.

\section BridgeNumericsDownstream Guidance for downstream users

\b Prefer \b calling \b Eigen \b directly. Downstream projects should depend
on Eigen (or another numerical library of their choosing) rather than on the
API or ABI of these convenience wrappers. Using \c itk::bridge as a
transitional step while retiring VNL is reasonable; treating it as a
permanent dependency is not.

Code that needs a long-lived, supported numerical interface should call the
backend directly, where the API contract, versioning policy, and
documentation are those of the numerical library itself.

\sa itk::bridge::Math::SVD
\sa itk::bridge::SymmetricEigenDecomposition
*/

/**
\namespace itk::bridge
\brief Convenience wrappers over third-party numerical backends, provided as
a migration aid rather than as supported ITK API.

Anything in this namespace eases the initial move off the deprecated VNL
algorithms. It is not part of the core ITK mission to maintain or validate:
the API and ABI may change without the usual deprecation cycle, and the
numerical behavior is the backend's, not ITK's.

Downstream users should prefer calling Eigen (or another numerical library)
directly rather than depending on these wrappers.

\see \ref BridgeNumericsPage
*/
49 changes: 37 additions & 12 deletions Documentation/docs/migration_guides/itk_6_migration_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -751,26 +751,51 @@ removed. The default (non-FFTW) FFT backend is now PocketFFT
- `vnl_fft_prime_factors`, `gpfa`-family symbols, and the vxl
`vnl_algo_test_fft*`/`test_convolve` tests are gone from the vendored tree.

## The `itk::bridge` namespace: a migration aid, not supported ITK API

The Eigen-backed replacements named throughout this guide live in
`itk::bridge`. That namespace holds **convenience wrappers intended to ease the
initial burden of moving off the deprecated VNL algorithms** — a `vnl_svd` call
site can become `itk::bridge::Math::SVD` as a mechanical edit, without the
caller first learning Eigen's API.

These wrappers are **not part of the core ITK mission to maintain or validate**:

- The API and ABI may change, or be removed, without the deprecation cycle that
core ITK API changes receive.
- The numerical behavior is the backend's. ITK does not independently validate
accuracy, conditioning, or convergence, and does not guarantee bit-for-bit
stability across backend versions.
- Coverage extends only to what ITK's own migration required; missing
functionality will not necessarily be added.

**Downstream users should prefer depending on Eigen (or another numerical
library) directly** rather than on the API/ABI of these wrappers. Using
`itk::bridge` as a transitional step while retiring VNL is reasonable; treating
it as a permanent dependency is not. Code that needs a long-lived, supported
numerical interface should call the backend directly, where the API contract,
versioning policy, and documentation are the numerical library's own.

## Eigen-backed eigendecompositions replace netlib EISPACK `vnl_*_eigensystem`

`vnl_real_eigensystem`, `vnl_symmetric_eigensystem`, and
`vnl_generalized_eigensystem` (netlib EISPACK `rg`/`rs`/`rsg`), and the only
other client `vnl_scatter_3x3`, are deprecated in favor of Eigen-backed
`itk::RealEigenDecomposition`, `itk::SymmetricEigenDecomposition`, and
`itk::GeneralizedEigenDecomposition`. Under `ITK_FUTURE_LEGACY_REMOVE` these
`itk::bridge::RealEigenDecomposition`, `itk::bridge::SymmetricEigenDecomposition`, and
`itk::bridge::GeneralizedEigenDecomposition`. Under `ITK_FUTURE_LEGACY_REMOVE` these
classes and the entire `v3p/netlib/eispack/` subdirectory are excluded from the
build.

### What you need to do

- Replace `vnl_symmetric_eigensystem<T>` with `itk::SymmetricEigenDecomposition<T>`
- Replace `vnl_symmetric_eigensystem<T>` with `itk::bridge::SymmetricEigenDecomposition<T>`
(it mirrors the `V`/`D` members and `get_eigenvector`/`get_eigenvalue`
accessors, so most call sites change only the type name and the include:
`itkSymmetricEigenDecomposition.h`). Use `itk::RealEigenDecomposition` and
`itk::GeneralizedEigenDecomposition` for the non-symmetric and
`itkBridgeSymmetricEigenDecomposition.h`). Use `itk::bridge::RealEigenDecomposition` and
`itk::bridge::GeneralizedEigenDecomposition` for the non-symmetric and
symmetric-definite generalized problems.
- For the eigenvalues/eigenvectors of a 3x3 scatter matrix, build the matrix
directly and feed `itk::SymmetricEigenDecomposition`.
directly and feed `itk::bridge::SymmetricEigenDecomposition`.

### A different correct representation — not a regression

Expand Down Expand Up @@ -800,14 +825,14 @@ representation of the same result, not an indication of a failure.** Update such
expected values to the new (now platform-stable) representatives; the migrated
ITK tests have been updated this way.

## Optional Eigen-backed `itk::Math::SVD`
## Optional Eigen-backed `itk::bridge::Math::SVD`

A new opt-in, header-only `itk::Math::SVD` (`itkMathSVD.h`) provides an
A new opt-in, header-only `itk::bridge::Math::SVD` (`itkBridgeMathSVD.h`) provides an
Eigen-backed singular value decomposition. It returns `U`, `W`, `V` as vnl types
plus `PseudoInverse()`, `Solve()`, `Rank()` and `Recompose()` -- the dominant `vnl_svd`
operations. `vnl_svd` is unchanged by this addition, but the direction is to
migrate ITK and downstream code onto `itk::Math::SVD` and ultimately **deprecate
and remove** the `vnl_svd` path. Prefer `itk::Math::SVD` in new code.
migrate ITK and downstream code onto `itk::bridge::Math::SVD` and ultimately **deprecate
and remove** the `vnl_svd` path. Prefer `itk::bridge::Math::SVD` in new code.

A runtime-sized `vnl_matrix` of any shape is accepted: square inputs take a fast
JacobiSVD/BDCSVD path; rectangular inputs use BDCSVD with thin U/V (`U` is m x k,
Expand All @@ -818,7 +843,7 @@ signature.

### What you need to do

Nothing is required immediately, but prefer `itk::Math::SVD` in new code. The
Nothing is required immediately, but prefer `itk::bridge::Math::SVD` in new code. The
`vnl_svd`-only operations (`nullvector`, `well_condition`, `singularities`) have
no remaining callers in ITK, so the new API already covers every ITK use; the few
remaining `vnl_svd` call sites are slated for migration as part of the broader
Expand Down Expand Up @@ -854,7 +879,7 @@ accuracy.

### Deterministic signs -- a different correct representation

Like the eigendecomposition classes above, `itk::Math::SVD` canonicalizes the
Like the eigendecomposition classes above, `itk::bridge::Math::SVD` canonicalizes the
singular-vector signs by default (reproducible across platforms and SIMD width),
whereas `vnl_svd`'s signs depend on solver internals. Sign-invariant operations
-- `U * V^T`, `PseudoInverse()`, `Solve()` -- are stable across the swap; code that
Expand Down
6 changes: 3 additions & 3 deletions Examples/RegistrationITKv4/ImageRegistration9.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
// that will monitor the evolution of the registration process.
//
#include "itkCommand.h"
#include "itkMathSVD.h"
#include "itkBridgeMathSVD.h"
class CommandIterationUpdate : public itk::Command
{
public:
Expand Down Expand Up @@ -113,7 +113,7 @@ class CommandIterationUpdate : public itk::Command
p[0][1] = static_cast<double>(optimizer->GetCurrentPosition()[1]);
p[1][0] = static_cast<double>(optimizer->GetCurrentPosition()[2]);
p[1][1] = static_cast<double>(optimizer->GetCurrentPosition()[3]);
const auto svd = itk::Math::SVD(p);
const auto svd = itk::bridge::Math::SVD(p);
vnl_matrix<double> r(2, 2);
r = svd.U * svd.V.transpose();
const double angle = std::asin(r[1][0]);
Expand Down Expand Up @@ -412,7 +412,7 @@ main(int argc, char * argv[])
p[0][1] = static_cast<double>(finalParameters[1]);
p[1][0] = static_cast<double>(finalParameters[2]);
p[1][1] = static_cast<double>(finalParameters[3]);
const auto svd = itk::Math::SVD(p);
const auto svd = itk::bridge::Math::SVD(p);
vnl_matrix<double> r(2, 2);
r = svd.U * svd.V.transpose();
const double angle = std::asin(r[1][0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@
* limitations under the License.
*
*=========================================================================*/
#ifndef itkCholeskySolve_h
#define itkCholeskySolve_h
#ifndef itkBridgeCholeskySolve_h
#define itkBridgeCholeskySolve_h

#include "itkMacro.h"
#include "vnl/vnl_matrix.h"
#include "vnl/vnl_vector.h"
#include "itk_eigen.h"
#include ITK_EIGEN(Dense)

namespace itk
namespace itk::bridge
{
namespace Math
{
Expand Down Expand Up @@ -85,6 +85,6 @@ CholeskyLowerTriangle(const vnl_matrix<T> & A)
}

} // namespace Math
} // namespace itk
} // namespace itk::bridge

#endif // itkCholeskySolve_h
#endif // itkBridgeCholeskySolve_h
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
* limitations under the License.
*
*=========================================================================*/
#ifndef itkEigenDecompositionSignConvention_h
#define itkEigenDecompositionSignConvention_h
#ifndef itkBridgeEigenDecompositionSignConvention_h
#define itkBridgeEigenDecompositionSignConvention_h

#include "vnl/vnl_matrix.h"
#include <cmath>

namespace itk::detail
namespace itk::bridge::detail
{

/** Canonicalize the sign of every eigenvector column of \a V so that its
Expand Down Expand Up @@ -90,6 +90,6 @@ CanonicalizeColumnSignsPaired(TMatrixU & u, TMatrixV & paired)
}
}

} // namespace itk::detail
} // namespace itk::bridge::detail

#endif // itkEigenDecompositionSignConvention_h
#endif // itkBridgeEigenDecompositionSignConvention_h
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
* limitations under the License.
*
*=========================================================================*/
#ifndef itkEigenDecompositionSolverInfo_h
#define itkEigenDecompositionSolverInfo_h
#ifndef itkBridgeEigenDecompositionSolverInfo_h
#define itkBridgeEigenDecompositionSolverInfo_h

#include "itk_eigen.h"
#include ITK_EIGEN(Dense)

namespace itk::detail
namespace itk::bridge::detail
{

/** Human-readable name and likely cause for an Eigen solver status, so a
Expand All @@ -43,6 +43,6 @@ EigenComputationInfoString(Eigen::ComputationInfo info)
return "an unrecognized Eigen::ComputationInfo value";
}

} // namespace itk::detail
} // namespace itk::bridge::detail

#endif // itkEigenDecompositionSolverInfo_h
#endif // itkBridgeEigenDecompositionSolverInfo_h
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@
* limitations under the License.
*
*=========================================================================*/
#ifndef itkGeneralizedEigenDecomposition_h
#define itkGeneralizedEigenDecomposition_h
#ifndef itkBridgeGeneralizedEigenDecomposition_h
#define itkBridgeGeneralizedEigenDecomposition_h

#include "vnl/vnl_matrix.h"
#include "vnl/vnl_vector.h"
#include "itkEigenDecompositionSignConvention.h"
#include "itkEigenDecompositionSolverInfo.h"
#include "itkBridgeEigenDecompositionSignConvention.h"
#include "itkBridgeEigenDecompositionSolverInfo.h"
#include "itkMacro.h"
#include "itk_eigen.h"
#include ITK_EIGEN(Dense)

namespace itk
namespace itk::bridge
{

/** \class GeneralizedEigenDecomposition
Expand Down Expand Up @@ -105,6 +105,6 @@ class GeneralizedEigenDecomposition
MatrixType m_Eigenvectors;
};

} // namespace itk
} // namespace itk::bridge

#endif // itkGeneralizedEigenDecomposition_h
#endif // itkBridgeGeneralizedEigenDecomposition_h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
* limitations under the License.
*
*=========================================================================*/
#ifndef itkMathDeterminant_h
#define itkMathDeterminant_h
#ifndef itkBridgeMathDeterminant_h
#define itkBridgeMathDeterminant_h

#include "itkMacro.h"
#include "vnl/vnl_matrix.h"
Expand All @@ -31,7 +31,10 @@ namespace itk
// with itkMatrix.h; its body instantiates only where itk::Matrix is complete.
template <typename T, unsigned int VRows, unsigned int VColumns>
class Matrix;
} // namespace itk

namespace itk::bridge
{
namespace Math
{
namespace detail
Expand Down Expand Up @@ -104,7 +107,7 @@ Determinant(const vnl_matrix<TReal> & A)
const unsigned int rows = A.rows();
if (rows != A.cols())
{
itkGenericExceptionMacro("itk::Math::Determinant requires a square matrix.");
itkGenericExceptionMacro("itk::bridge::Math::Determinant requires a square matrix.");
}
return detail::DynamicDeterminantEigen<TReal>(A.data_block(), rows);
}
Expand All @@ -121,6 +124,6 @@ Determinant(const vnl_matrix_fixed<TReal, VRows, VColumns> & A)
}

} // namespace Math
} // namespace itk
} // namespace itk::bridge

#endif // itkMathDeterminant_h
#endif // itkBridgeMathDeterminant_h
Loading
Loading