Skip to content

Match NumPy in percentile() for the midpoint, nearest and plotting-position methods - #1286

Merged
cliffburdick merged 2 commits into
NVIDIA:mainfrom
0z5a:percentile-match-numpy
Nov 10, 2026
Merged

cliffburdick merged 2 commits into
NVIDIA:mainfrom
0z5a:percentile-match-numpy

Conversation

@0z5a

@0z5a 0z5a commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1285.

Problem

percentile_impl (include/matx/transforms/percentile.h):

  • MIDPOINT averaged x[ceil(i)] and x[ceil(i)+1] instead of x[floor(i)] and x[ceil(i)] (or x[i] when the index is whole), so it was wrong for every input.
  • HAZEN, WEIBULL, MEDIAN_UNBIASED and NORMAL_UNBIASED did not clamp the virtual index to [0, n-1], and read one element past the sorted buffer at high q. LINEAR with n=1 also read x[1] (with weight 0).
  • NEAREST rounded ties away from zero; NumPy rounds half to even. The initial nearbyint fix also depended on the calling thread's rounding mode.

Fix

Clamp the virtual index as NumPy does, use floor and ceil for MIDPOINT, and select the nearest index explicitly with ties to even for NEAREST using cuda::std::floor, independent of the host rounding mode. percentile.rst said that q=0 is the mean; it is the minimum.

Tests

PercentileMethodsAndBounds in test/00_operators/ReductionTests.cu expected 15.0 for MIDPOINT at q=25; it now expects NumPy's 10.0. New cases cover both tails for each method, n=4 and n=6 MIDPOINT/NEAREST, and n=1.

On the previous PR head 4eccd75, a standalone B300 repro (sm_103, CUDA 13.3) used cudaExecutor and SingleThreadedHostExecutor:

main (26f6df6) previous PR head (4eccd75)
results vs numpy.percentile 14 mismatches all match
compute-sanitizer --tool memcheck 8 errors (invalid global reads) 0 errors

For LOWER, HIGHER and MIDPOINT with (n-1)*q a multiple of 100, a random-q comparison against NumPy can still differ: NumPy's floating-point q/100 moves the index there, while MatX's integer path is exact. LOWER and HIGHER already behaved this way.

The latest rounding-mode test and change have not been built or run on CUDA. A standalone host-only harness using the index expressions matched an integer reference for 198,396 cases across four rounding modes. The full MatX test suite was not built.

…sition methods

MIDPOINT averaged x[ceil(i)] and x[ceil(i)+1] instead of x[floor(i)]
and x[ceil(i)], so it was wrong for every input (q=25 of [0,10,20,30,40]
gave 15 instead of 10). HAZEN, WEIBULL, MEDIAN_UNBIASED and
NORMAL_UNBIASED did not clamp the virtual index to [0, n-1] and read one
element past the sorted buffer at high q, and NEAREST rounded ties away
from zero instead of to even. Clamp the index as NumPy does, use floor
and ceil for MIDPOINT, and round half to even for NEAREST. The existing
test expected the old MIDPOINT result; it now expects NumPy's, and new
cases cover both tails, n=1 and the NEAREST tie. The docs said that
q=0 is the mean; it is the minimum.

Signed-off-by: 0z5a <[email protected]>
@copy-pr-bot

copy-pr-bot Bot commented Nov 3, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@greptile-apps

greptile-apps Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; the previous rounding-mode issue is fixed and no new blocking issues were found.

Summary

This PR corrects percentile() results and prevents reads past the sorted input.

  • Interpolation methods stay within the sorted input’s endpoints.
  • The midpoint method averages the values around its position.
  • The nearest method sends halfway positions to an even index.

The previous finding is fixed. No new actionable issues were found. Tests were not run during this review.

Reviews (2) · Last reviewed commit: "fix(percentile): make nearest rounding m..." · Reviewed by Greptile

Comment thread include/matx/transforms/percentile.h Resolved
@cliffburdick

Copy link
Copy Markdown
Collaborator

/build

Comment thread include/matx/transforms/percentile.h Resolved
Select the nearest index explicitly with ties to even, using CUDA standard math without changing the caller rounding mode.

Signed-off-by: 0z5a <[email protected]>
@coveralls

Copy link
Copy Markdown

Coverage Status

Coverage is 93.745% — 0z5a:percentile-match-numpy into NVIDIA:main. No base build found for NVIDIA:main.

@cliffburdick

Copy link
Copy Markdown
Collaborator

/build

@cliffburdick
cliffburdick merged commit fd02cd0 into NVIDIA:main Nov 10, 2026
1 of 2 checks passed
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.

percentile() differs from NumPy for MIDPOINT, NEAREST and the plotting-position methods, and reads past the sorted buffer

3 participants