Skip to content

ENH, API: New sorting mechanism for DType API #28516

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 54 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
87f410b
ENH: Allocate lock only once in StringDType quicksort
MaanasArora Mar 14, 2025
ecd3ee1
ENH: Add dtype slots for sorting and begin integration
MaanasArora Mar 26, 2025
7e0bf44
MAINT: Rename sort compare slot access function
MaanasArora Mar 26, 2025
45f5008
ENH: Add dtype slot sorting functionality support to all sort kinds
MaanasArora Mar 26, 2025
d235dc9
ENH: Add descending flag to internal sorting functions
MaanasArora Mar 26, 2025
6a65c07
MAINT: Improve get dtype sort compare function name
MaanasArora Mar 27, 2025
379022f
MAINT: Fix doc typo
MaanasArora Mar 27, 2025
36fa05f
MAINT: Error out when non-legacy dtype has no sort_compare function
MaanasArora Mar 27, 2025
a7c6792
DOC: Add release notes for new dtype sorting API
MaanasArora Mar 28, 2025
84e7421
DOC: Add doc for sort compare slot in release notes
MaanasArora Mar 28, 2025
5caea7f
DOC: Add note for potential deprecation of sort arrfuncs in release note
MaanasArora Mar 30, 2025
f700725
MAINT: Reorder dtype slots to prevent changing existing slot numbers
MaanasArora Mar 30, 2025
e7cf5c2
BUG: Error on missing `sort_compare` slot only when dtype is privatel…
MaanasArora Mar 30, 2025
21fb7e7
DOC: Add C-API documentation for new sorting slots
MaanasArora Apr 1, 2025
9244ea3
ENH: Replace array object with context and auxdata in sortfunc signat…
MaanasArora Apr 5, 2025
9f09b13
BUG: Fix unnecessarily private function call due to underscore typo
MaanasArora Apr 5, 2025
7aeba26
MAINT: Fix whitespace typos
MaanasArora Apr 5, 2025
c7481b8
ENH: Allow flexible sorting compare for arr or descr in npy_sort func…
MaanasArora Apr 11, 2025
aa3415a
ENH: Add new sort func implementations and use in stringdtype
MaanasArora Apr 11, 2025
e725ed5
DOC: Fix missing newline in ctype doc
MaanasArora Apr 11, 2025
6d0ba21
DOC: Add sortfunc typedef docs
MaanasArora Apr 12, 2025
23204c5
DOC: Fix missing newline in ctype doc
MaanasArora Apr 12, 2025
ede3462
ENH: Define SortCompareFunc type
MaanasArora Apr 12, 2025
889ee11
Update dtype sorting signatures: move context, move out auxdata to ge…
MaanasArora May 13, 2025
beba242
MAINT: Check error in Get(Arg)SortFunc using return value
MaanasArora May 13, 2025
61e6f16
DOC: Add missing newlines to c-types in array.rst
MaanasArora May 14, 2025
434005e
MAINT: Rename new sort funcs and restore older names for existing pub…
MaanasArora May 24, 2025
3c168e4
MAINT: Rename start pointer in new sort func documentation to data
MaanasArora May 24, 2025
d9d9872
ENH: Add flags to new get_(arg)sort_function
MaanasArora May 28, 2025
a448475
DOC: Mention new sort func buffers to be contiguous
MaanasArora May 28, 2025
ab8f394
ENH: Elaborate new sort context, add reverse sorting and non position…
MaanasArora Jun 1, 2025
cd38b15
MAINT: Remove array from new sort context
MaanasArora Jun 1, 2025
dcc465a
DOC: Add `PyArrayMethod_SortContext` to docs
MaanasArora Jun 1, 2025
30b6c9c
DOC: Move sort nan position enum to array.rst
MaanasArora Jun 1, 2025
60d7f80
DOC: Add accidentally removed documentation for finalize_descr slot
MaanasArora Jun 1, 2025
38e29e0
BUG: Fix context initiation in _new_(arg)sortlike
MaanasArora Jun 1, 2025
bbbd274
ENH: Add descending flag to SortContext
MaanasArora Jun 1, 2025
178445d
MAINT: Pass descr in sort compare func and remove compare slot
MaanasArora Jul 1, 2025
29cd764
MAINT: Remove name for only one parameter in typedef
MaanasArora Jul 1, 2025
852783f
ENH: Add NumPy 2.4 API version and use for new sorting slots
MaanasArora Jul 1, 2025
5aaa15f
ENH: Add error result to `NPY_COMPARE_RESULT` enum
MaanasArora Jul 1, 2025
28e8c3a
DOC: Fix incorrectly changed arrfunc docs for sort functions
MaanasArora Jul 3, 2025
6e65f8d
MAINT: Clearer names for NPY_SORT_NAN_POSITION members
MaanasArora Jul 3, 2025
73bb5f8
DOC: Remove unimplemented release note
MaanasArora Jul 3, 2025
5eca653
DOC: Document new-style sorts are always descending and nan to end
MaanasArora Jul 3, 2025
9d3ae70
MAINT: Remove descending from get_sort_function and rename in sort co…
MaanasArora Jul 3, 2025
f46a498
DOC: Add version added directives to new sorting slots
MaanasArora Jul 3, 2025
b471562
BUG: Check return value of slots in GetSortFunction and GetArgSortFun…
MaanasArora Jul 3, 2025
d022a2d
DOC: Clarify that comparison function pointer can be NULL in sorting …
MaanasArora Jul 3, 2025
b1222b2
BUG: Use NPY_MIN_INT return value in compare_from_context to indicate…
MaanasArora Jul 3, 2025
4d7d592
DOC: Add release note for new sort compare c-api change
MaanasArora Jul 5, 2025
b734527
DOC: Add `PyArray_SortCompareFunc` to array docs
MaanasArora Jul 5, 2025
7d9ac65
DOC: Update sorting documentation with NPY_COMPARE_RESULT enum
MaanasArora Jul 5, 2025
c21a164
ENH: Add descending sorts to new `compare_from_context`
MaanasArora Jul 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/release/upcoming_changes/28516.c_api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* `PyArray_GetSortFunction`, `PyArray_GetArgSortFunction`, and `PyArray_GetSortCompareFunction` have been added to the C-API. These functions return the sorting, argsorting, and sort comparison functions if provided for a given dtype in new slots.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You did not actually add them to the public C-API. Which is totally fine, though.

(I might start with adding a SortBuffer() function or so.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is still valid, I think we might as well remove this for now.

11 changes: 11 additions & 0 deletions doc/release/upcoming_changes/28516.new_feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
New sorting function slots `NPY_DT_get_sort_function`, `NPY_DT_get_argsort_function` for dtype API
---------------------------------------------------------------------------------------------------

User-defined dtypes can now provide specific sorting functions for use with NumPy's sort methods.
The new slots `NPY_DT_get_sort_function` and `NPY_DT_get_argsort_function` should be functions that
return function pointers implementing sorting functionality for the dtype, while considering the
sort-kind and order. The old arrfunc slots ``NPY_DT_PyArray_ArrFuncs_sort`` and
``NPY_DT_PyArray_ArrFuncs_argsort`` may be deprecated in the future.

Additionally, the new `NPY_DT_sort_compare` slot can be used to provide a comparison function for
sorting, which will replace the default comparison function for the dtype in sorting functions.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe a note that the old arrfuncs slots may be deprecated in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added, thanks!

71 changes: 67 additions & 4 deletions doc/source/reference/c-api/array.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1873,6 +1873,28 @@ described below.
pointer. Currently this is used for zero-filling and clearing arrays storing
embedded references.

.. c:type:: int (PyArray_SortFuncWithContext)( \
PyArrayMethod_SortContext *context, void *data, \
npy_intp num, NpyAuxData *auxdata)

A function to sort a buffer of data. The *data* is a pointer to the
beginning of the contiguous buffer containing *num* elements. A function
of this type is returned by the `get_sort_function` function in the DType
slots, where *context* is passed in containing the descriptor for the
array. Returns 0 on success, -1 on failure.

.. c:type:: int (PyArray_ArgSortFuncWithContext)( \
PyArrayMethod_SortContext *context, void *data, \
npy_intp *tosort, npy_intp num, NpyAuxData *auxdata)

A function to arg-sort a buffer of data. The *data* is a pointer to the
beginning of the buffer containing *num* elements. The *tosort* is a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@charris to confirm, even for argsorting it probably makes sense to always use a contiguous buffer for sorting?

pointer to an array of indices that will be filled in with the
indices of the sorted elements. A function of this type is returned by
the `get_argsort_function` function in the DType slots, where
*context* is passed in containing the descriptor for the array.
Returns 0 on success, -1 on failure.

API Functions and Typedefs
~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -3521,6 +3543,30 @@ member of ``PyArrayDTypeMeta_Spec`` struct.
force newly created arrays to have a newly created descriptor
instance, no matter what input descriptor is provided by a user.

.. c:macro:: NPY_DT_get_sort_function

.. c:type:: int *(PyArrayDTypeMeta_GetSortFunction)(PyArray_Descr *, \
npy_intp sort_kind, int descending, PyArray_SortFuncWithContext **out_sort, \
NpyAuxData **out_auxdata, NPY_ARRAYMETHOD_FLAGS *out_flags)

If defined, sets a custom sorting function for the DType for each of
the sort kinds numpy implements. Returns 0 on success.

.. c:macro:: NPY_DT_get_argsort_function

.. c:type:: int *(PyArrayDTypeMeta_GetArgSortFunction)(PyArray_Descr *, \
npy_intp sort_kind, int descending, PyArray_ArgSortFuncWithContext **out_argsort, \
NpyAuxData **out_auxdata, NPY_ARRAYMETHOD_FLAGS *out_flags)

If defined, sets a custom argsorting function for the DType for each of
the sort kinds numpy implements. Returns 0 on success.

.. c:macro:: NPY_DT_sort_compare

If defined, sets a custom comparison function for the DType for use in
sorting, which will replace `NPY_DT_PyArray_ArrFuncs_compare`. Implements
``PyArray_CompareFunc``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a .. versionadded:: here. We don't have to strictly hide it, since it'll at least fail gracefully on older versions (at least I hope).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks


PyArray_ArrFuncs slots
^^^^^^^^^^^^^^^^^^^^^^

Expand All @@ -3547,6 +3593,8 @@ DType API slots but for now we have exposed the legacy
.. c:macro:: NPY_DT_PyArray_ArrFuncs_compare

Computes a comparison for `numpy.sort`, implements ``PyArray_CompareFunc``.
If `NPY_DT_sort_compare` is defined, it will be used instead. This slot may
be deprecated in the future.

.. c:macro:: NPY_DT_PyArray_ArrFuncs_argmax

Expand Down Expand Up @@ -3588,15 +3636,19 @@ DType API slots but for now we have exposed the legacy

.. c:macro:: NPY_DT_PyArray_ArrFuncs_sort

An array of PyArray_SortFunc of length ``NPY_NSORTS``. If set, allows
An array of PyArray_SortFuncWithContext of length ``NPY_NSORTS``. If set, allows
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems wrong, should be unchanged?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's wrong, thanks for catching

defining custom sorting implementations for each of the sorting
algorithms numpy implements.
algorithms numpy implements. If `NPY_DT_get_sort_function` is
defined, it will be used instead. This slot may be deprecated in the
future.

.. c:macro:: NPY_DT_PyArray_ArrFuncs_argsort

An array of PyArray_ArgSortFunc of length ``NPY_NSORTS``. If set,
An array of PyArray_ArgSortFuncWithContext of length ``NPY_NSORTS``. If set,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, should remain unchanged.

allows defining custom argsorting implementations for each of the
sorting algorithms numpy implements.
sorting algorithms numpy implements. If `NPY_DT_get_argsort_function`
is defined, it will be used instead. This slot may be deprecated in
the future.

Macros and Static Inline Functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -4340,6 +4392,17 @@ Enumerated Types
:c:data:`NPY_STABLESORT` are aliased to each other and may refer to one
of several stable sorting algorithms depending on the data type.

.. c:enum:: NPY_SORT_NAN_POSITION

An enum used to indicate the position of NaN values in sorting.

.. c:enumerator:: NPY_SORT_NAN_FIRST
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer _TO_END and _TO_START or _LAST/_FIRST or _AT_END/_AT_START.

I.e. make it sort order independent. first/last to me seems slightly less clear whether it is sort order dependent or not (i.e. does "first" mean that it's value is considered lower than others?).

(The problem is, that in practice both conventions are relatively common.)

Copy link
Contributor Author

@MaanasArora MaanasArora Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've used _TO_END and _TO_START; it does make more sense, thanks!


Indicates that NaN values should be sorted first.

.. c:enumerator:: NPY_SORT_NAN_LAST

Indicates that NaN values should be sorted last.

.. c:enum:: NPY_SCALARKIND

Expand Down
38 changes: 36 additions & 2 deletions doc/source/reference/c-api/types-and-structures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,8 @@ PyArray_ArrFuncs
PyArray_NonzeroFunc *nonzero;
PyArray_FillFunc *fill;
PyArray_FillWithScalarFunc *fillwithscalar;
PyArray_SortFunc *sort[NPY_NSORTS];
PyArray_ArgSortFunc *argsort[NPY_NSORTS];
PyArray_SortFuncWithContext *sort[NPY_NSORTS];
PyArray_ArgSortFuncWithContext *argsort[NPY_NSORTS];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You really can't change this!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, reverted!

PyObject *castdict;
PyArray_ScalarKindFunc *scalarkind;
int **cancastscalarkindto;
Expand Down Expand Up @@ -792,6 +792,40 @@ PyArrayMethod_Context and PyArrayMethod_Spec
An array of slots for the method. Slot IDs must be one of the values
below.

.. _arraymethod-sort-context:

PyArrayMethod_SortContext
-------------------------

.. c:type:: PyArrayMethod_SortContext

A struct used to provide context for sorting methods.

.. code-block:: c

typedef struct {
PyArray_Descr *descriptor;
PyArray_SortCompareFunc *compare;
int reversed;
NPY_SORT_NAN_POSITION nan_position;
} PyArrayMethod_SortContext

.. c:member:: PyArray_Descr *descriptor

The descriptor for the data type being sorted.

.. c:member:: PyArray_SortCompareFunc *compare

A pointer to the comparison function used for sorting.

.. c:member:: int reversed

A flag indicating whether the sort is reversed.

.. c:member:: NPY_SORT_NAN_POSITION nan_position

The position of NaN values in the sort order.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, makes sense to pass these as information, and I guess that means it makes sense to use a custom context and not try to rely on an existing one (the ufunc/casting one is more complex, and the traverse one too simple maybe.)

Strictly speaking, we could avoid the sortcompare slot here, but I think it's maybe pragmatic to just include it and leave it NULL.


.. _dtypemeta:

PyArray_DTypeMeta and PyArrayDTypeMeta_Spec
Expand Down
47 changes: 47 additions & 0 deletions numpy/_core/include/numpy/dtype_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,10 @@ typedef int (PyArrayMethod_PromoterFunction)(PyObject *ufunc,
#define NPY_DT_get_clear_loop 9
#define NPY_DT_get_fill_zero_loop 10
#define NPY_DT_finalize_descr 11
#define NPY_DT_get_sort_function 12
#define NPY_DT_get_argsort_function 13
#define NPY_DT_compare 14
#define NPY_DT_sort_compare 15

// These PyArray_ArrFunc slots will be deprecated and replaced eventually
// getitem and setitem can be defined as a performance optimization;
Expand Down Expand Up @@ -477,4 +481,47 @@ typedef PyArray_Descr *(PyArrayDTypeMeta_FinalizeDescriptor)(PyArray_Descr *dtyp
typedef int(PyArrayDTypeMeta_SetItem)(PyArray_Descr *, PyObject *, char *);
typedef PyObject *(PyArrayDTypeMeta_GetItem)(PyArray_Descr *, char *);

typedef enum {
NPY_LESS = -1,
NPY_EQUAL = 0,
NPY_GREATER = 1,
NPY_UNORDERED_LEFT = 2,
NPY_UNORDERED_RIGHT = 3,
NPY_UNORDERED_BOTH = 4,
} NPY_COMPARE_RESULT;

typedef struct PyArrayMethod_SortContext_tag PyArrayMethod_SortContext;

typedef NPY_COMPARE_RESULT (PyArray_CompareFuncWithContext)(
const void *a, const void *b, PyArrayMethod_SortContext *context);
typedef NPY_COMPARE_RESULT (PyArray_SortCompareFunc)(
const void *a, const void *b, PyArrayMethod_SortContext *context);

typedef enum {
NPY_SORT_NAN_FIRST = 0,
NPY_SORT_NAN_LAST = 1,
} NPY_SORT_NAN_POSITION;

struct PyArrayMethod_SortContext_tag {
PyArray_Descr *descriptor;
PyArray_SortCompareFunc *compare;
int descending;
int reversed;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same paremeter, should just be descending. Could make it npy_bool (doesn't really matter).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, done and made npy_bool.

NPY_SORT_NAN_POSITION nan_position;
};

typedef int (PyArray_SortFuncWithContext)(PyArrayMethod_SortContext *,
void *, npy_intp,
NpyAuxData *);
typedef int (PyArray_ArgSortFuncWithContext)(PyArrayMethod_SortContext *,
void *, npy_intp *, npy_intp,
NpyAuxData *);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two need different names and you need to leave the original typedefs in ndarraytypes.h that had these names, since they're public API.

Copy link
Contributor Author

@MaanasArora MaanasArora May 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing! This is done.


typedef int *(PyArrayDTypeMeta_GetSortFunction)(PyArray_Descr *,
npy_intp, int, PyArray_SortFuncWithContext **, NpyAuxData **,
NPY_ARRAYMETHOD_FLAGS *out_flags);
typedef int *(PyArrayDTypeMeta_GetArgSortFunction)(PyArray_Descr *,
npy_intp, int, PyArray_ArgSortFuncWithContext **, NpyAuxData **,
NPY_ARRAYMETHOD_FLAGS *out_flags);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New stuff in the public API needs new API docs as well as a release note describing the new features.

Maybe also as a proof-of-concept, it looks like both quaddtype and mpfdtype in numpy-user-dtypes implement sorting - would you be willing to update them to use the new API in a PR to numpy-user-dtypes that depends on this PR to numpy? That should give you a feeling for whether this API is helpful for someone writing a new user dtype. It'll also be a form of documentation - we don't have great docs for writing user dtypes besides the examples in numpy-user-dtypes.

Copy link
Member

@ngoldbaum ngoldbaum Mar 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also what should we do about the flags that got added before we made the dtype API public, e.g. NPY_DT_PyArray_ArrFuncs_compare? I guess we can deprecate them although I don't know how hard it would be to generate deprecation warnings if those are used.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's easy to generate a deprecation warning during registration (a bit tedious maybe, as you need explicit check).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll add API docs and a release note, and willing to make a PR to numpy-user-dtypes! Will look into that.

Just to be clear, NPY_DT_PyArray_ArrFuncs_compare is still needed right? We can move it to a new slot rather than an arrayfunc but it's going to be different from the sort comparison for now if I'm thinking right (as it is user-facing rather than used in the sorting). Do we need to do this another way?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't change slot numbers (unless they are guarded as private)! So the numbers are fixed (until they have not been used for a bit at least).
So yeah, I think we should keep it the old slot for now, maybe easier to make the deprecation a follow up.[^depr]

So, we just have to live with the numbering we got, I half thought I asked for an offset for the NPY_DT_PyArray_ArrFuncs slots, but maybe I didn't bother.
(It's not a big issue, the only thing is the convenience if slot numbers == slot offset so you don't need to translate it.)

[^depr] I think this is as simple as asking users to compile with the new NumPy, and then adding PyArray_RUNTIME_VERSION, but this PR is complicated enough due to API decisions for the new loops, etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, we just have to live with the numbering we got, I half thought I asked for an offset for the NPY_DT_PyArray_ArrFuncs slots, but maybe I didn't bother.

There is an offset, _NPY_DT_ARRFUNCS_OFFSET:

#define NPY_DT_MAX_ARRFUNCS_SLOT \
NPY_NUM_DTYPE_PYARRAY_ARRFUNCS_SLOTS + _NPY_DT_ARRFUNCS_OFFSET

#endif /* NUMPY_CORE_INCLUDE_NUMPY___DTYPE_API_H_ */
26 changes: 26 additions & 0 deletions numpy/_core/src/common/npy_sort.h.src
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <Python.h>
#include <numpy/npy_common.h>
#include <numpy/ndarraytypes.h>
#include <numpy/dtype_api.h>

#define NPY_ENOMEM 1
#define NPY_ECOMP 2
Expand Down Expand Up @@ -97,6 +98,22 @@ NPY_NO_EXPORT int atimsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *
*****************************************************************************
*/

NPY_NO_EXPORT int npy_quicksort_with_context(PyArrayMethod_SortContext *context, void *vec,
npy_intp cnt, NpyAuxData *auxdata);
NPY_NO_EXPORT int npy_heapsort_with_context(PyArrayMethod_SortContext *context, void *vec,
npy_intp cnt, NpyAuxData *auxdata);
NPY_NO_EXPORT int npy_mergesort_with_context(PyArrayMethod_SortContext *context, void *vec,
npy_intp cnt, NpyAuxData *auxdata);
NPY_NO_EXPORT int npy_timsort_with_context(PyArrayMethod_SortContext *context, void *vec,
npy_intp cnt, NpyAuxData *auxdata);
NPY_NO_EXPORT int npy_aquicksort_with_context(PyArrayMethod_SortContext *context, void *vec,
npy_intp *ind, npy_intp cnt, NpyAuxData *auxdata);
NPY_NO_EXPORT int npy_aheapsort_with_context(PyArrayMethod_SortContext *context, void *vec,
npy_intp *ind, npy_intp cnt, NpyAuxData *auxdata);
NPY_NO_EXPORT int npy_amergesort_with_context(PyArrayMethod_SortContext *context, void *vec,
npy_intp *ind, npy_intp cnt, NpyAuxData *auxdata);
NPY_NO_EXPORT int npy_atimsort_with_context(PyArrayMethod_SortContext *context, void *vec,
npy_intp *ind, npy_intp cnt, NpyAuxData *auxdata);

NPY_NO_EXPORT int npy_quicksort(void *vec, npy_intp cnt, void *arr);
NPY_NO_EXPORT int npy_heapsort(void *vec, npy_intp cnt, void *arr);
Expand All @@ -107,6 +124,15 @@ NPY_NO_EXPORT int npy_aheapsort(void *vec, npy_intp *ind, npy_intp cnt, void *ar
NPY_NO_EXPORT int npy_amergesort(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
NPY_NO_EXPORT int npy_atimsort(void *vec, npy_intp *ind, npy_intp cnt, void *arr);

NPY_NO_EXPORT int npy_quicksort_impl(void *vec, npy_intp cnt, void *arr, PyArrayMethod_SortContext *context);
NPY_NO_EXPORT int npy_heapsort_impl(void *vec, npy_intp cnt, void *arr, PyArrayMethod_SortContext *context);
NPY_NO_EXPORT int npy_mergesort_impl(void *vec, npy_intp cnt, void *arr, PyArrayMethod_SortContext *context);
NPY_NO_EXPORT int npy_timsort_impl(void *vec, npy_intp cnt, void *arr, PyArrayMethod_SortContext *context);
NPY_NO_EXPORT int npy_aquicksort_impl(void *vec, npy_intp *ind, npy_intp cnt, void *arr, PyArrayMethod_SortContext *context);
NPY_NO_EXPORT int npy_aheapsort_impl(void *vec, npy_intp *ind, npy_intp cnt, void *arr, PyArrayMethod_SortContext *context);
NPY_NO_EXPORT int npy_amergesort_impl(void *vec, npy_intp *ind, npy_intp cnt, void *arr, PyArrayMethod_SortContext *context);
NPY_NO_EXPORT int npy_atimsort_impl(void *vec, npy_intp *ind, npy_intp cnt, void *arr, PyArrayMethod_SortContext *context);

#ifdef __cplusplus
}
#endif
Expand Down
18 changes: 18 additions & 0 deletions numpy/_core/src/multiarray/dtypemeta.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ dtypemeta_initialize_struct_from_spec(
NPY_DT_SLOTS(DType)->common_instance = NULL;
NPY_DT_SLOTS(DType)->setitem = NULL;
NPY_DT_SLOTS(DType)->getitem = NULL;
NPY_DT_SLOTS(DType)->get_sort_function = NULL;
NPY_DT_SLOTS(DType)->get_argsort_function = NULL;
NPY_DT_SLOTS(DType)->compare = NULL;
NPY_DT_SLOTS(DType)->sort_compare = NULL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must come at the end to not break ABI!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is done!

NPY_DT_SLOTS(DType)->get_clear_loop = NULL;
NPY_DT_SLOTS(DType)->get_fill_zero_loop = NULL;
NPY_DT_SLOTS(DType)->finalize_descr = NULL;
Expand Down Expand Up @@ -1230,6 +1234,20 @@ dtypemeta_wrap_legacy_descriptor(
dtype_class->flags |= NPY_DT_NUMERIC;
}

if (dt_slots->sort_compare == NULL) {
if (!NPY_DT_is_legacy(dtype_class) && !NPY_DT_is_user_defined(dtype_class)) {
PyErr_SetString(PyExc_RuntimeError,
"DType has no sort_compare function.");
Py_DECREF(dtype_class);
return -1;
}
}

/* Auto-fill compare slot with sort-compare as default */
if (dt_slots->compare == NULL && dt_slots->sort_compare != NULL) {
dt_slots->compare = dt_slots->sort_compare;
}

if (_PyArray_MapPyTypeToDType(dtype_class, descr->typeobj,
PyTypeNum_ISUSERDEF(dtype_class->type_num)) < 0) {
Py_DECREF(dtype_class);
Expand Down
48 changes: 47 additions & 1 deletion numpy/_core/src/multiarray/dtypemeta.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ typedef struct {
* parameters, if any, as the operand dtype.
*/
PyArrayDTypeMeta_FinalizeDescriptor *finalize_descr;

/* DType sorting methods. */
PyArrayDTypeMeta_GetSortFunction *get_sort_function;
PyArrayDTypeMeta_GetArgSortFunction *get_argsort_function;
PyArray_CompareFuncWithContext *compare;
PyArray_SortCompareFunc *sort_compare;

/*
* The casting implementation (ArrayMethod) to convert between two
* instances of this DType, stored explicitly for fast access:
Expand All @@ -89,7 +96,7 @@ typedef struct {

// This must be updated if new slots before within_dtype_castingimpl
// are added
#define NPY_NUM_DTYPE_SLOTS 11
#define NPY_NUM_DTYPE_SLOTS 15
#define NPY_NUM_DTYPE_PYARRAY_ARRFUNCS_SLOTS 22
#define NPY_DT_MAX_ARRFUNCS_SLOT \
NPY_NUM_DTYPE_PYARRAY_ARRFUNCS_SLOTS + _NPY_DT_ARRFUNCS_OFFSET
Expand Down Expand Up @@ -291,6 +298,45 @@ PyArray_SETITEM(PyArrayObject *arr, char *itemptr, PyObject *v)
Py_XSETREF(descr, _new_); \
} while(0)

static inline int
PyArray_GetSortFunction(PyArray_Descr *descr,
NPY_SORTKIND which, int descending, PyArray_SortFuncWithContext **out_sort,
NpyAuxData **out_auxdata, NPY_ARRAYMETHOD_FLAGS *out_flags)
{
if (NPY_DT_SLOTS(NPY_DTYPE(descr))->get_sort_function == NULL) {
return -1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to set an error (TypeError or DTypeTypeError, which is defined somewhere I think.)

(An error here will make sense after you move the fallback logic into a default slot filling. Or you could have the default slot raise an error, that is also completely fine.)

}

NPY_DT_SLOTS(NPY_DTYPE(descr))->get_sort_function(
descr, which, descending, out_sort, out_auxdata, out_flags);
return 0;
}

static inline int
PyArray_GetArgSortFunction(PyArray_Descr *descr,
NPY_SORTKIND which, int descending, PyArray_ArgSortFuncWithContext **out_argsort,
NpyAuxData **out_auxdata, NPY_ARRAYMETHOD_FLAGS *out_flags)
{
if (NPY_DT_SLOTS(NPY_DTYPE(descr))->get_argsort_function == NULL) {
return -1;
}

NPY_DT_SLOTS(NPY_DTYPE(descr))->get_argsort_function(
descr, which, descending, out_argsort, out_auxdata, out_flags);
return 0;
}

static inline PyArray_CompareFuncWithContext *
PyArray_GetCompareFunction(PyArray_Descr *descr)
{
return NPY_DT_SLOTS(NPY_DTYPE(descr))->compare;
}

static inline PyArray_SortCompareFunc *
PyArray_GetSortCompareFunction(PyArray_Descr *descr)
{
return NPY_DT_SLOTS(NPY_DTYPE(descr))->sort_compare;
}

// Get the pointer to the PyArray_DTypeMeta for the type associated with the typenum.
static inline PyArray_DTypeMeta *
Expand Down
Loading
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy