Repository navigation
Add dpnp.minmax with a fused min/max reduction kernel - #3135
Open
antonwolfy wants to merge 4 commits into
Open
antonwolfy wants to merge 4 commits into
antonwolfy wants to merge 4 commits into
Conversation
Cover float16, NaN at the first, middle and last position (also a complex NaN in the imaginary part only), integer limits of all integer types, multi-pass reductions of several rows, axis=() with out, and read-only and other-queue out errors. Link dpnp.minmax from dpnp.min and dpnp.max.
antonwolfy
requested review from
ndgrigorian and
vlad-perevezentsev
as code owners
October 30, 2026 18:04
There was a problem hiding this comment.
🟡 Changes recommended
The public docstring omits two intentional out incompatibilities: narrowing integer outputs can produce different extrema, and even disjoint interleaved output views are rejected.
1 open issue
What changed in this PR
Adds dpnp.minmax to compute minimum and maximum together using a fused SYCL reduction, extending dpnp’s NumPy-compatible statistics API.
Changes:
- Adds fused kernels for contiguous and strided inputs, including multi-pass reductions.
- Integrates the public API, output handling, bindings, exports, and documentation.
- Adds tests for values, layouts, output buffers, errors, queues, and USM allocation types.
| File | Description |
|---|---|
dpnp/tests/test_usm_type.py |
Checks output USM allocation types. |
dpnp/tests/test_sycl_queue.py |
Checks output queue placement. |
dpnp/tests/test_statistics.py |
Adds functional and edge-case tests for minmax. |
dpnp/tensor/libtensor/source/reductions/reduction_common.cpp |
Registers the new reduction binding. |
dpnp/tensor/libtensor/source/reductions/minmax.hpp |
Declares binding initialization. |
dpnp/tensor/libtensor/source/reductions/minmax.cpp |
Adds validation, dispatch, and Python binding. |
dpnp/tensor/libtensor/include/kernels/reductions.hpp |
Implements fused minimum/maximum kernels. |
dpnp/tensor/CMakeLists.txt |
Includes the new binding source in the build. |
dpnp/tensor/_reduction.py |
Adds private axis and output handling. |
dpnp/dpnp_iface_statistics.py |
Adds the public API and docstring. |
dpnp/__init__.py |
Exports dpnp.minmax. |
doc/reference/math.rst |
Lists minmax among extrema functions. |
CHANGELOG.md |
Records the new functionality. |
Review effort: Balanced
Contributor
|
Array API standard conformance tests for dpnp=0.22.0dev0=np2py314h8d9cdd5_7 ran successfully. |
Contributor
|
View rendered docs @ https://intelpython.github.io/dpnp/pull/3135/index.html |
Collaborator
This branch has not been deployed
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.

NumPy 2.6 adds
numpy.minmax, which returns the(min, max)tuple of an array in a single pass. This PR addsdpnp.minmaxwith the same signature. It is backed by a new fused reduction kernel that reads the input once and writes both extrema, instead of runningdpnp.minanddpnp.maxone after another.Changes
kernels/reductions.hpp: a new tree reduction (no atomics) that keeps the running minimum and maximum per work-item and reduces both over the work-group. It uses native group algorithms for integers,all_of/any_offor bool, and NaN-aware native reductions for real floats: if any work-item holds a NaN, the NaN of the first such work-item is broadcast. Complex types use the custom SLM reduction. There are sequential, one-work-group and multi-pass variants with strided, axis-0 and axis-1 contiguous entry points, and the two outputs have independent strides.source/reductions/minmax.cpp: new binding_tensor_reductions_impl._minmax_over_axis(src, trailing_dims_to_reduce, dst_min, dst_max, sycl_queue, depends).dpnp/tensor/_reduction.py: private_minmaxhelper, modelled on_comparison_over_axis.dpnp.minmax(a, axis=None, out=None, keepdims=False, initial=None, where=True):outis a tuple of two arrays, and an entry may beNone.initialandwhereare only supported with their default values, as indpnp.minanddpnp.max.test_statistics.py,test_sycl_queue.pyandtest_usm_type.py.Two behaviours follow
dpnp.min,dpnp.maxanddpnp.modfrather than NumPy, and are not changed here:outof another dtype receives the result cast after the reduction. NumPy casts the input first.outentries that overlap in memory are rejected, including interleaved views such as(r[:, 0], r[:, 1]).Performance
Measured on an Intel Core Ultra 7 366H (Panther Lake H) with its integrated GPU on Level Zero and its CPU on OpenCL. The timing is the end-to-end time of
dpnp.minmax(a)againstdpnp.min(a), dpnp.max(a), each followed byqueue.wait(). Runs are interleaved and the median is reported. The speedup is the separate time divided by the fused time; the results of both were checked to be equal in every case.dpnp.minanddpnp.maxuse the single-kernel atomic path, and complex128 with 1e3 elements (0.96x).Cost
Build times are for
-j6on 16 CPUs._tensor_impllink remains the last step to finish._tensor_reductions_impltarget alone goes from 488 s to 511 s (+23 s), measured branch/master/master/branch. The device link takes +20.6 s andminmax.cppcompiles in about 12 s. Compile times of the other reduction sources are unchanged._tensor_reductions_implshared object grows from 35.38 MB to 37.27 MB (+1.89 MB, +5.4%). Of that, 1.0 MB is SPIR-V and 0.45 MB is host code. Compressed with zstd -19 it grows by 166 KB, with gzip -9 by 282 KB, and all dpnp shared objects together grow by 1.0%.import dpnpgrows by about 1.6 MB. GPU memory is unchanged, and so is the first-call JIT time of the existingdpnp.minanddpnp.max.Checklist