Skip to content

ENH: Support single-precision-only (ITK_USE_FFTWF) FFT builds#6330

Open
hjmjohnson wants to merge 7 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:wip/fftwf-only-ci-build
Open

ENH: Support single-precision-only (ITK_USE_FFTWF) FFT builds#6330
hjmjohnson wants to merge 7 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:wip/fftwf-only-ci-build

Conversation

@hjmjohnson
Copy link
Copy Markdown
Member

@hjmjohnson hjmjohnson commented May 22, 2026

WIP — do not merge yet. Fixes the FFTWF-only (single-precision FFTW) build on main, and temporarily carries a CI-only commit (db1b8d5) forcing the Pixi-Cxx leg to build single-precision-only so the matrix actually exercises the fix. The WIP commit will be dropped once CI is green. Companion to #6326 (same fixes on release-5.4).

The two defects (masked by the default dual-precision build)
  1. CMake/itkExternal_FFTW.cmake set FFTW_INCLUDE from the double-precision FFTW3_INCLUDE_DIRS inside the ITK_USE_FFTWF block, leaving it empty in single-precision-only builds (fftw3.h not found).
  2. The FFTW factory instantiated both precisions. FFTImageFilterFactory<FFTWxxx> now consults per-precision predicates (FFTImageFilterEnableFloat/Double) guarded by if constexpr. The FFTW filter headers specialize the predicate to false_type for the absent precision, so the factory does not instantiate fftw::ComplexToComplexProxy<double> (which is undefined when ITK_USE_FFTWD=OFF).

The specializations live in each FFTW filter header (next to FFTImageFilterTraits<FFTWxxx>), not in a .cxx, so every translation unit that instantiates the factory — including itkTestDriverIncludeRequiredFactories.cxx — sees them.

Local verification

Built ITKFFT and ITKTestKernel on macOS arm64 with ITK_USE_FFTWF=ON / ITK_USE_FFTWD=OFF / ITK_USE_SYSTEM_FFTW=OFF: staged FFTW built its own fftw3.h, and itkTestDriverIncludeRequiredFactories.cxx (the TU that failed in CI) now compiles and links.

@github-actions github-actions Bot added type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots area:Filtering Issues affecting the Filtering module labels May 22, 2026
@hjmjohnson hjmjohnson force-pushed the wip/fftwf-only-ci-build branch from 4e48df0 to d4bbf4e Compare May 22, 2026 18:19
@hjmjohnson hjmjohnson changed the title WIP: Validate FFTWF-only build path in CI (cherry-picks #6326) BUG: Fix FFTWF-only (single-precision FFTW) build on main May 22, 2026
@hjmjohnson hjmjohnson marked this pull request as ready for review May 22, 2026 18:20
@greptile-apps

This comment was marked as low quality.

Comment thread Modules/Filtering/FFT/src/itkFFTWFFTImageFilterInitFactory.cxx Outdated
@hjmjohnson hjmjohnson force-pushed the wip/fftwf-only-ci-build branch from d4bbf4e to db1b8d5 Compare May 22, 2026 18:41
@github-actions github-actions Bot added the type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances label May 22, 2026
@hjmjohnson hjmjohnson changed the title BUG: Fix FFTWF-only (single-precision FFTW) build on main WIP: Fix FFTWF-only build on main (CI-validation commit temporary) May 22, 2026
@hjmjohnson hjmjohnson marked this pull request as draft May 22, 2026 18:41
@hjmjohnson hjmjohnson force-pushed the wip/fftwf-only-ci-build branch from db1b8d5 to 1705087 Compare May 22, 2026 19:21
@github-actions github-actions Bot removed the type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances label May 22, 2026
@hjmjohnson hjmjohnson force-pushed the wip/fftwf-only-ci-build branch from 1705087 to 6ef430d Compare May 22, 2026 20:07
@github-actions github-actions Bot added the type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct label May 22, 2026
@hjmjohnson hjmjohnson force-pushed the wip/fftwf-only-ci-build branch 2 times, most recently from 38e673f to ed398b3 Compare May 23, 2026 13:42
@github-actions github-actions Bot added the area:Registration Issues affecting the Registration module label May 23, 2026
@hjmjohnson hjmjohnson force-pushed the wip/fftwf-only-ci-build branch from ed398b3 to 4f13120 Compare May 23, 2026 13:50
FFTWFFTImageFilterInitFactory unconditionally registered every FFTW
filter for both float and double via FFTImageFilterFactory, whose
constructor instantiated both precisions. The single-precision-only
1-D FFTW filters (e.g. FFTWForward1DFFTImageFilter) form member type
aliases from fftw::ComplexToComplexProxy<PixelType>, which is defined
only for the configured precision. Building with ITK_USE_FFTWF=ON and
ITK_USE_FFTWD=OFF therefore failed to compile ITKFFT
("no type named 'PlanType' in 'itk::fftw::ComplexToComplexProxy<double>'").

Add FFTImageFilterEnableFloat / FFTImageFilterEnableDouble predicates
(both default true) and guard the factory constructor with if constexpr.
The FFTW InitFactory specializes the predicate to false_type for the
precision whose backend is absent, so only the configured precision is
registered. Vnl and other factories are unaffected because the
predicates default to enabling both precisions.
In the ITK_USE_FFTWF block, FFTW_INCLUDE was assigned from
FFTW3_INCLUDE_DIRS (the double-precision variable), which is only set
inside the ITK_USE_FFTWD block. When ITK is configured with
ITK_USE_FFTWF=ON and ITK_USE_FFTWD=OFF, FFTW_INCLUDE is left empty, so
the staged fftw3.h include directory is never added and ITKFFT fails to
compile with "fatal error: 'fftw3.h' file not found".

The defect is masked whenever both precisions are enabled because the
FFTWD block reassigns FFTW_INCLUDE afterward. Use the single-precision
FFTW3f_INCLUDE_DIRS in the FFTWF block so single-precision-only builds
resolve the header.
The FFTW filter headers reference FFTW_FORWARD/FFTW_BACKWARD/FFTW_ESTIMATE
and other fftw3.h symbols but obtained them only transitively through
itkFFTWCommon.h / itkFFTWCommonExtended.h. Those proxy headers guard the
fftw3.h include with `#if defined(ITK_USE_FFTWF) || defined(ITK_USE_FFTWD)`
without first including itkConfigure.h. When a proxy header is first
included before ITK_USE_FFTW* is defined, the guard is false, fftw3.h is
skipped, and the proxy's include guard is set so later inclusions are
no-ops, leaving the FFTW symbols undeclared at parse time (observed in
ITKFFTHeaderTest1 with GCC for single-precision-only builds).

Include fftw3.h directly, guarded, in each consuming header so the
symbols are visible regardless of header include order.
@hjmjohnson hjmjohnson force-pushed the wip/fftwf-only-ci-build branch from 4f13120 to 2f576cc Compare May 23, 2026 13:55
The 1D FFT tests hardcoded double, which fails to compile in an
ITK_USE_FFTWF=ON, ITK_USE_FFTWD=OFF build because only
ComplexToComplexProxy<float> defines PlanType/ComplexType. Select the
pixel type from the configured precision so the FFTW backend
instantiates a proxy that exists. In a single-precision FFTW build the
default 1D backend for the float pixel type is FFTW rather than Vnl, so
the default-backend check expects FFTW accordingly.
Add a precision-dispatching Plan_r2r wrapper to itk::fftw::Proxy (float and
double) so real-to-real (DCT) transforms use the same precision-aware
interface as the c2c/r2c/c2r plans. VariationalRegistrationCurvatureRegularizer
now creates its forward/backward DCT plans via FFTWProxyType::Plan_r2r instead
of the bare double-only fftw_plan_r2r API, so the curvature regularizer builds
and runs in single-precision (ITK_USE_FFTWF-only) configurations.

PDEDeformable's itkCurvatureRegistrationFilter still calls the double-only FFTW
r2r API directly and its test is gated on ITK_USE_FFTWD, so narrow its compile
guards to require ITK_USE_FFTWD (matching the test). Also drop a stale
single-precision #warning in the elastic regularizer, which already supports
both precisions through the proxy.
The Elastic/Curvature registration tests compared bit-exact against stale
baselines (ITK InsightSoftwareConsortium#6305) and failed in every FFTW configuration (float, double,
both) by large amounts. The registration itself converges correctly; the
output is the same solution offset by sub-pixel discretization shifts, so a
neighborhood (radius) match with a modest intensity tolerance and a small
differing-pixel cushion is the appropriate comparison.

Wire --compareRadiusTolerance / --compareIntensityTolerance /
--compareNumberOfPixelsTolerance into the 2D/3D test macros via the
VR_RADIUS_TOL / VR_INTENSITY_TOL / VR_NPIXELS_TOL cache variables and set
defaults of 2 / 20 / 200.

Measured differing-pixel counts (float-only build) while tuning:

  radius/intensity | Elastic2D | Curvature2D | Elastic3D
  -----------------+-----------+-------------+----------
  0 / 0  (bit-exact)|  390 FAIL |  2500 FAIL  | 11953 FAIL
  1 / 10            |    0 pass |     8 FAIL  |   632 FAIL
  2 / 20            |    0 pass |     0 pass  |    25 FAIL
  2 / 40            |    0 pass |     0 pass  |     0 pass

Radius matching alone collapses the divergence ~500x (11953 -> 25),
confirming a valid spatially-shifted solution rather than intensity error.
The chosen 2 / 20 keeps intensity moderate; the 200-pixel cushion absorbs
cross-platform variation (Elastic3D had 25 differing pixels at intensity 20).
@hjmjohnson hjmjohnson force-pushed the wip/fftwf-only-ci-build branch from c7b1b35 to 80471a4 Compare May 23, 2026 16:58
itkFrequencyExpand/ShrinkTest used double-precision images, so on an
odd-sized input (21x21x9, prime factor 7) the ND FFT fell back to the Vnl
backend, which only supports sizes factored by 2, 3, and 5, and threw. The
odd-input tests were gated on ITK_USE_FFTWF although they require an FFTW
backend for the double pixel type (ITK_USE_FFTWD), so an FFTWF-only build ran
them against the Vnl double fallback and failed.

Select the test pixel type from the configured FFTW precision (double when
ITK_USE_FFTWD, else float) so the matching FFTW backend handles the odd size,
and run the odd-input tests whenever any FFTW backend is configured. No
baseline is published for these cases (the .tiff content links never existed,
matching the already-commented-out Even tests), so drop the dead --compare and
let the test exercise the odd-size FFTW path, failing only on an exception.
@hjmjohnson hjmjohnson force-pushed the wip/fftwf-only-ci-build branch from 80471a4 to 8301a2b Compare May 23, 2026 18:19
hjmjohnson added a commit that referenced this pull request May 23, 2026
The 1D FFT tests hardcoded double, which fails to compile in an
ITK_USE_FFTWF=ON, ITK_USE_FFTWD=OFF build because only
ComplexToComplexProxy<float> defines PlanType/ComplexType. Select the
pixel type from the configured precision so the FFTW backend
instantiates a proxy that exists.

Backport of the single-precision FFTW 1D-test pixel-type fix from main
(PR #6330). The default-backend (Vnl) check is unchanged on release-5.4.
@hjmjohnson
Copy link
Copy Markdown
Member Author

Validated the single-precision FFTW work by configuring, building, and testing all four FFTW precision configurations locally (full build per config; VariationalRegistration + ITKFFT test labels shown, all green):

Configuration flags build tests
No FFTW FFTWF=OFF FFTWD=OFF 46/46
FFTWF-only FFTWF=ON FFTWD=OFF 59/59
FFTWD-only FFTWF=OFF FFTWD=ON 59/59
Both FFTWF=ON FFTWD=ON 65/65
Notes per configuration
  • All four compile and link cleanly — the FFTWF-only build was the original failure this PR fixes (no type named 'PlanType' in ComplexToComplexProxy<double>).
  • FFTWF-only: 1D FFT tests use float; the DCT curvature regularizer uses the new itk::fftw::Proxy::Plan_r2r single-precision path; IsotropicWavelets frequency tests use float.
  • Default 1D backend resolves to FFTW when any FFTW precision is configured, else Vnl.
  • The differing test counts reflect which FFTW-gated tests register per configuration (e.g. odd-size IsotropicWavelets and the DCT regularizer tests register only when an FFTW backend is present).
  • The full FFTWF-only Pixi-Cxx CI suite (~4.5k tests) also passed once the IsotropicWavelets frequency-test precision fix landed.

@hjmjohnson hjmjohnson changed the title WIP: Fix FFTWF-only build on main (CI-validation commit temporary) ENH: Support single-precision-only (ITK_USE_FFTWF) FFT builds May 23, 2026
@hjmjohnson hjmjohnson force-pushed the wip/fftwf-only-ci-build branch from 8301a2b to 52f5a11 Compare May 23, 2026 22:45
@github-actions github-actions Bot added the type:Enhancement Improvement of existing methods or implementation label May 23, 2026
@hjmjohnson hjmjohnson marked this pull request as ready for review May 24, 2026 01:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Filtering Issues affecting the Filtering module area:Registration Issues affecting the Registration module type:Enhancement Improvement of existing methods or implementation type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant