Skip to content

BUG: Compute displacement exponential scale without undefined shift (B35, split of #6669) - #6673

Merged
hjmjohnson merged 1 commit into
InsightSoftwareConsortium:mainfrom
hjmjohnson:split6669-b35-exp-scale
Jul 23, 2026
Merged

hjmjohnson merged 1 commit into
InsightSoftwareConsortium:mainfrom
hjmjohnson:split6669-b35-exp-scale

Conversation

@hjmjohnson

Copy link
Copy Markdown
Member

B35 — ExponentialDisplacementFieldImageFilter divided the field by 1 << numiter, undefined behavior for numiter >= 31. Split out of #6669 as an independent bug fix (#6669 closed in favor of focused PRs).

Unreachable by default (automatic cap 20), but an explicit SetMaximumNumberOfIterations(>= 31) made it live. The scale is now computed in floating point.

New GTests ThirtyOneIterationsScaleCorrectly and ZeroFieldStaysZero.

Local testing

Cherry-picked onto current upstream/main (37ecafc); built ITKDisplacementFieldGTestDriver and ran both new tests — PASSED. pre-commit run --all-files clean.

Fixes item B35 from the #6575 audit. Companion PRs (split from #6669): STYLE/DOC dispositions and the WarpImageFilter B34 fix.

The first-order scale 2^numiter was computed as 1 << numiter, which is
undefined behavior for 31 or more iterations and reachable through
SetMaximumNumberOfIterations with automatic iterations off. Use ldexp.

Also select zero squaring iterations for a zero field: the previous
sentinel, NumericTraits::min(), is a tiny positive value, so the
keep-zero branch was unreachable, and integral iteration estimates
overshot the documented ceil by one.

Issue InsightSoftwareConsortium#6575, items B35 and Q31.
@github-actions github-actions Bot added type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances 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 area:Filtering Issues affecting the Filtering module labels Jul 21, 2026
@hjmjohnson
hjmjohnson marked this pull request as ready for review July 21, 2026 15:40
@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes displacement-field exponential scaling for high iteration counts. The main changes are:

  • Replaces the undefined integer shift scale calculation with floating-point std::ldexp.
  • Keeps zero-field automatic iteration handling on the zero-iteration path.
  • Adds GTests for explicit 31-iteration scaling and zero-field output.
  • Registers the new GTest source in the displacement-field test driver.

Confidence Score: 5/5

Safe to merge with minimal risk.

The production change is narrow and replaces a known undefined integer operation with the floating-point scale calculation used by the algorithm. The added tests cover the high-iteration case and the zero-field automatic path. No functional or security issues were identified.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • The environment was inspected and initial tooling availability checked, revealing that cmake/ctest/ninja were missing while make and g++ were available.
  • The configure step was blocked because cmake was not found in the environment.
  • Fallback checks confirmed GTest registration, updated test names, the 31-iteration setup, zero-field setup, and the ldexp implementation were recognized as valid.
  • A real compiled C++17 run of the 31-iteration floating-point scale computation completed, demonstrating end-to-end execution.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
Modules/Filtering/DisplacementField/include/itkExponentialDisplacementFieldImageFilter.hxx Replaces undefined integer-shift scaling with floating-point std::ldexp and preserves the zero-field fast path.
Modules/Filtering/DisplacementField/test/CMakeLists.txt Adds the new exponential displacement GTest source to the module GoogleTest driver.
Modules/Filtering/DisplacementField/test/itkExponentialDisplacementFieldImageFilterGTest.cxx Adds focused GTests for 31-iteration scaling and automatic zero-field behavior.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Caller
participant Filter as ExponentialDisplacementFieldImageFilter
participant Divider as DivideImageFilter
participant Warper as WarpVectorImageFilter
participant Adder as AddImageFilter

Caller->>Filter: SetInput(field), configure iterations
Caller->>Filter: UpdateLargestPossibleRegion()
Filter->>Filter: Determine numiter (automatic or maximum)
alt "numiter == 0"
    Filter->>Filter: Cast input or divide by -1 for inverse
else "numiter > 0"
    Filter->>Filter: "scale = ldexp(1.0, numiter)"
    Filter->>Divider: Divide input by +/- scale
    loop numiter squaring steps
        Filter->>Warper: Warp current output by itself
        Filter->>Adder: Compose output + warped field
    end
end
Filter-->>Caller: Output displacement field
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Caller
participant Filter as ExponentialDisplacementFieldImageFilter
participant Divider as DivideImageFilter
participant Warper as WarpVectorImageFilter
participant Adder as AddImageFilter

Caller->>Filter: SetInput(field), configure iterations
Caller->>Filter: UpdateLargestPossibleRegion()
Filter->>Filter: Determine numiter (automatic or maximum)
alt "numiter == 0"
    Filter->>Filter: Cast input or divide by -1 for inverse
else "numiter > 0"
    Filter->>Filter: "scale = ldexp(1.0, numiter)"
    Filter->>Divider: Divide input by +/- scale
    loop numiter squaring steps
        Filter->>Warper: Warp current output by itself
        Filter->>Adder: Compose output + warped field
    end
end
Filter-->>Caller: Output displacement field
Loading

Reviews (1): Last reviewed commit: "BUG: Compute displacement exponential sc..." | Re-trigger Greptile

@hjmjohnson
hjmjohnson merged commit a89807e into InsightSoftwareConsortium:main Jul 23, 2026
21 checks passed
@hjmjohnson
hjmjohnson deleted the split6669-b35-exp-scale branch August 22, 2026 15:15
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 type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances 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.

2 participants