Skip to content

Fix Upsample align_corners=False for fractional scale factors#3908

Open
v-code01 wants to merge 1 commit into
ml-explore:mainfrom
v-code01:fix/upsample-align-corners-false
Open

Fix Upsample align_corners=False for fractional scale factors#3908
v-code01 wants to merge 1 commit into
ml-explore:mainfrom
v-code01:fix/upsample-align-corners-false

Conversation

@v-code01

Copy link
Copy Markdown

Problem

_scaled_indices computes the align_corners=False start offset as

start = ((M - 1) * step - N + 1) / 2   # M = int(scale * N), step = 1 / scale

This equals the half-pixel offset (1 - step) / 2 only when M == scale * N exactly. For a fractional scale factor M = int(scale * N) is floored, so the grid is re-centered to the corner-aligned (align_corners=True) grid. The result: align_corners=False output becomes bit-identical to align_corners=True, i.e. the flag is silently ignored.

x = mx.arange(5).reshape(1, 5, 1).astype(mx.float32)
f = nn.Upsample(scale_factor=1.5, mode="linear", align_corners=False)(x)
t = nn.Upsample(scale_factor=1.5, mode="linear", align_corners=True)(x)
# f == t == [0, 0.667, 1.333, 2.0, 2.667, 3.333, 4.0]
# correct half-pixel: [0, 0.5, 1.167, 1.833, 2.5, 3.167, 3.833]

This affects linear and cubic modes (both route through _scaled_indices) at any fractional scale (1.5, 1.25, ...). All existing upsample tests use integer scale factors, where the two expressions coincide, so the regime was untested.

Fix

Use the constant half-pixel offset start = (1 - step) / 2, so the sampling positions are (i + 0.5) / scale - 0.5 for every scale. Integer scale factors are unchanged.

Test

Added test_upsample_align_corners_false_fractional_scale pinning the half-pixel positions at scale=1.5 and asserting align_corners=False now differs from True. It fails on the old code and passes with the fix; the existing upsample tests stay green.

_scaled_indices computed the align_corners=False start offset as
((M - 1) * step - N + 1) / 2, which equals the half-pixel offset (1 - step) / 2
only when M == scale * N exactly. For a fractional scale factor M = int(scale *
N) is floored, so the sampling grid was re-centered to the corner-aligned
(align_corners=True) grid: the output became bit-identical to align_corners=True
and the flag had no effect (e.g. scale=1.5, mode=linear/cubic).

Use the constant half-pixel offset (1 - step) / 2 so the sampling positions are
(i + 0.5) / scale - 0.5 for every scale. Integer scale factors are unchanged
(the two expressions already coincide there), so the existing tests still pass.

Adds a regression test at a fractional scale that pins the half-pixel positions
and checks align_corners False differs from True.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant