[Relax][ONNX] Make ReduceMax/ReduceMin NaN propagation order-independent(numpy semantics)#19755
Draft
cchung100m wants to merge 4 commits into
Draft
[Relax][ONNX] Make ReduceMax/ReduceMin NaN propagation order-independent(numpy semantics)#19755cchung100m wants to merge 4 commits into
cchung100m wants to merge 4 commits into
Conversation
Contributor
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hi Committers,
This PR addresses the
ReduceMax/ReduceMinpart of issue #19572. Any suggestions would be appreciated if you are available.Root cause:
The ONNX frontend ReduceMax / ReduceMin converters return relax.op.max / relax.op.min. After legalization these map to topi.max / topi.min, which fold with a commutative reducer whose combiner is Max(x, y) / Min(x, y). In codegen, Max(a, b) lowers to select(a > b, a, b) using an ordered float comparison (fcmp ogt), which is false for NaN. As a left-fold (acc = Max(acc, elem)), NaN propagation becomes position-dependent - a later non-NaN element silently overwrites an earlier NaN.
Solution:
Adopt the well-defined, order-independent numpy/IEEE convention (matching numpy.max/min and torch.amax/amin): the reduction yields NaN whenever any reduced element is NaN. Minimal, ONNX-frontend-only change:
sum(astype(isnan(data), dtype), axes, keepdims) > 0and force those outputs toNaNwithwhere(has_nan, nan, reduce(data)). The mask reduces over the same axes/keepdims, so it aligns in shape with the reduced result._impl_v11and both reduce branches of_impl_v18) through the helper; thenoop_with_empty_axespassthrough is left untouched since it performs no reduction.Note on scope (re: #19589 ):
The underlying NaN behavior of Max/Min is the same family of ops discussed in #19589. Per review comments there, enforcing NaN semantics at the IR / LLVM-IR level is undesirable(backward-compat with older LLVM, and portability to CUDA/OpenCL/Vulkan), and a dedicated portable nanmin/nanmax TIRx intrinsic(like
nearbyint) would be the preferred long-term mechanism. This PR deliberately:sum(isnan) > 0, fully sidestepping Max/Min NaN behavior.