numpy
Advanced tools
| Contributors | ||
| ============ | ||
| A total of 16 people contributed to this release. People with a "+" by their | ||
| names contributed a patch for the first time. | ||
| * Abhijeetsingh Meena + | ||
| * Charalampos Stratakis | ||
| * Charles Harris | ||
| * Chris Ninham + | ||
| * David Woods | ||
| * Geonho + | ||
| * Gopu Yeshwanth Reddy + | ||
| * Iason Krommydas | ||
| * Ijtihed Kilani | ||
| * Jelle Zijlstra + | ||
| * Joren Hammudoglu | ||
| * Kumar Aditya | ||
| * Mike Boyle | ||
| * Nathan Goldbaum | ||
| * Raghuveer Devulapalli | ||
| * Sebastian Berg | ||
| Pull requests merged | ||
| ==================== | ||
| A total of 28 pull requests were merged for this release. | ||
| * `#31864 <https://github.com/numpy/numpy/pull/31864>`__: MAINT: Prepare 2.5.x for further development | ||
| * `#31889 <https://github.com/numpy/numpy/pull/31889>`__: TYP: Backport multiple static typing fixes 1. | ||
| * `#31900 <https://github.com/numpy/numpy/pull/31900>`__: TST: add tests for stable ABI numpy extensions (#31822) | ||
| * `#31901 <https://github.com/numpy/numpy/pull/31901>`__: BUG: fix ``StringDType`` coerce flag in binary ufunc promotion... | ||
| * `#31902 <https://github.com/numpy/numpy/pull/31902>`__: BLD: fix meson deprecation warnings (#31892) | ||
| * `#31921 <https://github.com/numpy/numpy/pull/31921>`__: TYP: Backport multiple typing fixes 2. | ||
| * `#31947 <https://github.com/numpy/numpy/pull/31947>`__: MAINT: Update x86-simd-sort subproject (5adb334 → fa944ef) (#31908) | ||
| * `#31949 <https://github.com/numpy/numpy/pull/31949>`__: BUG: fix crash on 32 bit systems using abi3t (#31771) | ||
| * `#31950 <https://github.com/numpy/numpy/pull/31950>`__: MNT: remove some obsolete string to bool workarounds (#31859) | ||
| * `#31952 <https://github.com/numpy/numpy/pull/31952>`__: BUG: centralized helper for output coerce and na_object in stringdtype... | ||
| * `#31953 <https://github.com/numpy/numpy/pull/31953>`__: BUG: fix CPU feature env diagnostic buffer overruns (#31905) | ||
| * `#31954 <https://github.com/numpy/numpy/pull/31954>`__: BUG: restore ndarray.conjugate() for legacy user-defined dtypes... | ||
| * `#31955 <https://github.com/numpy/numpy/pull/31955>`__: TYP: Avoid shadowed dtype annotations | ||
| * `#32077 <https://github.com/numpy/numpy/pull/32077>`__: MAINT: Update verdored-meson/meson to match main. | ||
| * `#32114 <https://github.com/numpy/numpy/pull/32114>`__: BUG: fix refcount leak on overlapping copyto with where=False | ||
| * `#32115 <https://github.com/numpy/numpy/pull/32115>`__: BUG: fix swallowed cast error in fancy indexing assignment (#31975) | ||
| * `#32116 <https://github.com/numpy/numpy/pull/32116>`__: BUG: Fix buffered iterator stride after removing multi-index | ||
| * `#32117 <https://github.com/numpy/numpy/pull/32117>`__: BUG: fix ``np.fromiter`` corruption when reusing a ``StringDType``... | ||
| * `#32119 <https://github.com/numpy/numpy/pull/32119>`__: BUG: add a special case for StringDType in np.isdtype (#32030) | ||
| * `#32121 <https://github.com/numpy/numpy/pull/32121>`__: BUG: reference leak in ``simd_sequence_from_iterable`` (#32038) | ||
| * `#32122 <https://github.com/numpy/numpy/pull/32122>`__: BUG: ensure lock is held when accessing or writing to RNG state... | ||
| * `#32123 <https://github.com/numpy/numpy/pull/32123>`__: BUG: fully reset cached RNG state for non-MT19937 RNGs (#32062) | ||
| * `#32135 <https://github.com/numpy/numpy/pull/32135>`__: TYP: type capabilities max dimensions | ||
| * `#32158 <https://github.com/numpy/numpy/pull/32158>`__: BUG: avoid possible stack overflow in arraydescr_dealloc (#32133) | ||
| * `#32206 <https://github.com/numpy/numpy/pull/32206>`__: MAINT: Update cibuildwheel to v4.2.0 | ||
| * `#32214 <https://github.com/numpy/numpy/pull/32214>`__: MAINT: Skip limited_api tests on some platforms. | ||
| * `#32220 <https://github.com/numpy/numpy/pull/32220>`__: TYP: ``isclose`` shape-typing fix for 2d array-likes (#32205) | ||
| * `#32221 <https://github.com/numpy/numpy/pull/32221>`__: BUG: avoid segfaults when legacy copyswap slot is not defined... |
| .. currentmodule:: numpy | ||
| ========================= | ||
| NumPy 2.5.2 Release Notes | ||
| ========================= | ||
| The NumPy 2.5.2 is a patch release that fixes bugs discovered after the 2.5.1 | ||
| release. The big news is that it includes wheels for the newly released | ||
| Python 3.15.0rc1. | ||
| This release supports Python versions 3.12-3.15 | ||
| C API changes | ||
| ============= | ||
| ``PyArray_StringDTypeObject`` is opaque under the abi3t stable ABI | ||
| ------------------------------------------------------------------ | ||
| The ``PyArray_StringDTypeObject`` was accidentally exposed in NumPy | ||
| 2.5 when targeting the free-threading-compatible stable ABI | ||
| (``Py_TARGET_ABI3T``). ``PyArray_StringDTypeObject`` is now an opaque | ||
| struct: extensions compiled that way cannot access its fields, since | ||
| the struct layout depends on the size of the object header. Any code | ||
| that accessed ``PyArray_StringDTypeObject`` fields in an abi3t build | ||
| would have crashed, so we are making this API change in a bugfix | ||
| release. | ||
| The ``NpyString`` allocator API remains usable by passing the | ||
| descriptor object pointer, e.g. | ||
| ``NpyString_acquire_allocator((PyArray_StringDTypeObject *)descr)``. | ||
| (`gh-31771 <https://github.com/numpy/numpy/pull/31771>`__) | ||
| Contributors | ||
| ============ | ||
| A total of 16 people contributed to this release. People with a "+" by their | ||
| names contributed a patch for the first time. | ||
| * Abhijeetsingh Meena + | ||
| * Charalampos Stratakis | ||
| * Charles Harris | ||
| * Chris Ninham + | ||
| * David Woods | ||
| * Geonho + | ||
| * Gopu Yeshwanth Reddy + | ||
| * Iason Krommydas | ||
| * Ijtihed Kilani | ||
| * Jelle Zijlstra + | ||
| * Joren Hammudoglu | ||
| * Kumar Aditya | ||
| * Mike Boyle | ||
| * Nathan Goldbaum | ||
| * Raghuveer Devulapalli | ||
| * Sebastian Berg | ||
| Pull requests merged | ||
| ==================== | ||
| A total of 28 pull requests were merged for this release. | ||
| * `#31864 <https://github.com/numpy/numpy/pull/31864>`__: MAINT: Prepare 2.5.x for further development | ||
| * `#31889 <https://github.com/numpy/numpy/pull/31889>`__: TYP: Backport multiple static typing fixes 1. | ||
| * `#31900 <https://github.com/numpy/numpy/pull/31900>`__: TST: add tests for stable ABI numpy extensions (#31822) | ||
| * `#31901 <https://github.com/numpy/numpy/pull/31901>`__: BUG: fix ``StringDType`` coerce flag in binary ufunc promotion... | ||
| * `#31902 <https://github.com/numpy/numpy/pull/31902>`__: BLD: fix meson deprecation warnings (#31892) | ||
| * `#31921 <https://github.com/numpy/numpy/pull/31921>`__: TYP: Backport multiple typing fixes 2. | ||
| * `#31947 <https://github.com/numpy/numpy/pull/31947>`__: MAINT: Update x86-simd-sort subproject (5adb334 → fa944ef) (#31908) | ||
| * `#31949 <https://github.com/numpy/numpy/pull/31949>`__: BUG: fix crash on 32 bit systems using abi3t (#31771) | ||
| * `#31950 <https://github.com/numpy/numpy/pull/31950>`__: MNT: remove some obsolete string to bool workarounds (#31859) | ||
| * `#31952 <https://github.com/numpy/numpy/pull/31952>`__: BUG: centralized helper for output coerce and na_object in stringdtype... | ||
| * `#31953 <https://github.com/numpy/numpy/pull/31953>`__: BUG: fix CPU feature env diagnostic buffer overruns (#31905) | ||
| * `#31954 <https://github.com/numpy/numpy/pull/31954>`__: BUG: restore ndarray.conjugate() for legacy user-defined dtypes... | ||
| * `#31955 <https://github.com/numpy/numpy/pull/31955>`__: TYP: Avoid shadowed dtype annotations | ||
| * `#32077 <https://github.com/numpy/numpy/pull/32077>`__: MAINT: Update verdored-meson/meson to match main. | ||
| * `#32114 <https://github.com/numpy/numpy/pull/32114>`__: BUG: fix refcount leak on overlapping copyto with where=False | ||
| * `#32115 <https://github.com/numpy/numpy/pull/32115>`__: BUG: fix swallowed cast error in fancy indexing assignment (#31975) | ||
| * `#32116 <https://github.com/numpy/numpy/pull/32116>`__: BUG: Fix buffered iterator stride after removing multi-index | ||
| * `#32117 <https://github.com/numpy/numpy/pull/32117>`__: BUG: fix ``np.fromiter`` corruption when reusing a ``StringDType``... | ||
| * `#32119 <https://github.com/numpy/numpy/pull/32119>`__: BUG: add a special case for StringDType in np.isdtype (#32030) | ||
| * `#32121 <https://github.com/numpy/numpy/pull/32121>`__: BUG: reference leak in ``simd_sequence_from_iterable`` (#32038) | ||
| * `#32122 <https://github.com/numpy/numpy/pull/32122>`__: BUG: ensure lock is held when accessing or writing to RNG state... | ||
| * `#32123 <https://github.com/numpy/numpy/pull/32123>`__: BUG: fully reset cached RNG state for non-MT19937 RNGs (#32062) | ||
| * `#32135 <https://github.com/numpy/numpy/pull/32135>`__: TYP: type capabilities max dimensions | ||
| * `#32158 <https://github.com/numpy/numpy/pull/32158>`__: BUG: avoid possible stack overflow in arraydescr_dealloc (#32133) | ||
| * `#32206 <https://github.com/numpy/numpy/pull/32206>`__: MAINT: Update cibuildwheel to v4.2.0 | ||
| * `#32214 <https://github.com/numpy/numpy/pull/32214>`__: MAINT: Skip limited_api tests on some platforms. | ||
| * `#32220 <https://github.com/numpy/numpy/pull/32220>`__: TYP: ``isclose`` shape-typing fix for 2d array-likes (#32205) | ||
| * `#32221 <https://github.com/numpy/numpy/pull/32221>`__: BUG: avoid segfaults when legacy copyswap slot is not defined... | ||
| name: 'Meson Build' | ||
| description: 'Build project using Meson and Ninja' | ||
| inputs: | ||
| compiler: | ||
| description: 'C++ compiler to use (e.g., g++-9, clang++-18, icpx)' | ||
| required: true | ||
| meson-options: | ||
| description: 'Additional Meson options' | ||
| required: false | ||
| default: '-Dbuild_tests=true --warnlevel 2 --werror --buildtype release' | ||
| cxxflags: | ||
| description: 'Additional CXXFLAGS' | ||
| required: false | ||
| default: '' | ||
| build-examples: | ||
| description: 'Whether to build examples first' | ||
| required: false | ||
| default: 'false' | ||
| pre-build-commands: | ||
| description: 'Commands to run before build (e.g., source environment)' | ||
| required: false | ||
| default: '' | ||
| runs: | ||
| using: 'composite' | ||
| steps: | ||
| - name: Build examples | ||
| if: inputs.build-examples == 'true' | ||
| shell: bash | ||
| env: | ||
| CXX: ${{ inputs.compiler }} | ||
| CXXFLAGS: ${{ inputs.cxxflags }} | ||
| run: | | ||
| ${{ inputs.pre-build-commands }} | ||
| cd examples | ||
| make all | ||
| - name: Build with Meson | ||
| shell: bash | ||
| env: | ||
| CXX: ${{ inputs.compiler }} | ||
| CXXFLAGS: ${{ inputs.cxxflags }} | ||
| run: | | ||
| ${{ inputs.pre-build-commands }} | ||
| make clean | ||
| meson setup ${{ inputs.meson-options }} builddir | ||
| cd builddir | ||
| ninja |
| name: 'Setup Build Environment' | ||
| description: 'Common setup for building and testing x86-simd-sort' | ||
| inputs: | ||
| compiler: | ||
| description: 'Compiler to install (e.g., g++-9, clang-18)' | ||
| required: true | ||
| extra-packages: | ||
| description: 'Additional apt packages to install' | ||
| required: false | ||
| default: '' | ||
| setup-sde: | ||
| description: 'Whether to setup Intel SDE' | ||
| required: false | ||
| default: 'true' | ||
| sde-version: | ||
| description: 'Intel SDE version to install' | ||
| required: false | ||
| default: '9.58.0' | ||
| runs: | ||
| using: 'composite' | ||
| steps: | ||
| - name: Install dependencies | ||
| shell: bash | ||
| run: | | ||
| sudo apt update | ||
| sudo apt -y install ${{ inputs.compiler }} libgtest-dev meson wget git ${{ inputs.extra-packages }} | ||
| - name: Setup Intel SDE | ||
| if: inputs.setup-sde == 'true' | ||
| uses: petarpetrovt/setup-sde@31aa4a8e85e109bef00f1d838613fcc6ec421271 # v5.0 | ||
| with: | ||
| environmentVariableName: SDE_PATH | ||
| sdeVersion: ${{ inputs.sde-version }} | ||
| - name: Add Intel SDE to PATH | ||
| if: inputs.setup-sde == 'true' | ||
| shell: bash | ||
| run: echo "$SDE_PATH" >> "$GITHUB_PATH" |
| #ifndef X86SIMDSORT_CPUID_H | ||
| #define X86SIMDSORT_CPUID_H | ||
| #ifdef _MSC_VER | ||
| #include <intrin.h> | ||
| #include <string> | ||
| #include <unordered_map> | ||
| static std::unordered_map<std::string, bool> xss_cpu_features; | ||
| static bool os_supports_avx() | ||
| { | ||
| int cpuInfo[4]; | ||
| __cpuid(cpuInfo, 1); | ||
| bool osxsaveSupported = (cpuInfo[2] & (1 << 27)) != 0; // OSXSAVE bit | ||
| bool avxSupported = (cpuInfo[2] & (1 << 28)) != 0; // AVX bit | ||
| if (!(avxSupported && osxsaveSupported)) return false; | ||
| // Check XCR0[2:1] (XMM and YMM state) | ||
| unsigned long long xcr0 = _xgetbv(0); | ||
| return (xcr0 & 0x6) == 0x6; | ||
| } | ||
| static bool os_supports_avx512() | ||
| { | ||
| if (!os_supports_avx()) return false; | ||
| // Need XCR0[7:5] = opmask/ZMM/YMM state enabled | ||
| unsigned long long xcr0 = _xgetbv(0); | ||
| return (xcr0 & 0xE0) == 0xE0; | ||
| } | ||
| static void xss_cpu_init() | ||
| { | ||
| int cpuInfo[4]; | ||
| __cpuid(cpuInfo, 0); | ||
| int maxLeaf = cpuInfo[0]; | ||
| bool hasAVX2 = false; | ||
| bool hasAVX512F = false, hasAVX512DQ = false, hasAVX512BW = false, | ||
| hasAVX512VL = false; | ||
| bool hasAVX512VBMI2 = false, hasAVX512FP16 = false; | ||
| if (maxLeaf >= 7) { | ||
| __cpuidex(cpuInfo, 7, 0); | ||
| // EBX bits | ||
| hasAVX2 = os_supports_avx() && (cpuInfo[1] & (1 << 5)); | ||
| hasAVX512F = os_supports_avx512() && (cpuInfo[1] & (1 << 16)); | ||
| hasAVX512DQ = os_supports_avx512() && (cpuInfo[1] & (1 << 17)); | ||
| hasAVX512BW = os_supports_avx512() && (cpuInfo[1] & (1 << 30)); | ||
| hasAVX512VL = os_supports_avx512() && (cpuInfo[1] & (1 << 31)); | ||
| // ECX bits | ||
| hasAVX512VBMI2 = os_supports_avx512() && (cpuInfo[2] & (1 << 6)); | ||
| // EDX bits | ||
| hasAVX512FP16 = os_supports_avx512() && (cpuInfo[3] & (1 << 23)); | ||
| } | ||
| xss_cpu_features["avx2"] = hasAVX2; | ||
| xss_cpu_features["avx512f"] = hasAVX512F; | ||
| xss_cpu_features["avx512dq"] = hasAVX512DQ; | ||
| xss_cpu_features["avx512bw"] = hasAVX512BW; | ||
| xss_cpu_features["avx512vl"] = hasAVX512VL; | ||
| xss_cpu_features["avx512vbmi2"] = hasAVX512VBMI2; | ||
| xss_cpu_features["avx512fp16"] = hasAVX512FP16; | ||
| } | ||
| inline bool xss_cpu_supports(const char *feature) | ||
| { | ||
| auto it = xss_cpu_features.find(feature); | ||
| return it != xss_cpu_features.end() && it->second; | ||
| } | ||
| #else | ||
| #define xss_cpu_init() __builtin_cpu_init() | ||
| #define xss_cpu_supports(feature) __builtin_cpu_supports(feature) | ||
| #endif // _MSC_VER | ||
| #endif // X86SIMDSORT_CPUID_H |
| #cython: language_level=3 | ||
| """ | ||
| Make sure cython can compile using the NumPy C-API in limited API mode | ||
| (see meson.build). | ||
| """ | ||
| cimport numpy as cnp | ||
| cnp.import_array() | ||
| def nonzero(cnp.ndarray arr): | ||
| """Count non-zero elements of a float64 array using the NpyIter API.""" | ||
| cdef cnp.NpyIter *it | ||
| cdef cnp.NpyIter_IterNextFunc iternext | ||
| cdef char **dataptr | ||
| cdef cnp.npy_intp *strideptr | ||
| cdef cnp.npy_intp *innersizeptr | ||
| cdef cnp.npy_intp nonzero_count = 0 | ||
| cdef cnp.npy_intp stride, count | ||
| cdef char *data | ||
| if cnp.PyArray_SIZE(arr) == 0: | ||
| return 0 | ||
| it = cnp.NpyIter_New( | ||
| arr, | ||
| cnp.NPY_ITER_READONLY | cnp.NPY_ITER_EXTERNAL_LOOP | cnp.NPY_ITER_REFS_OK, | ||
| cnp.NPY_KEEPORDER, cnp.NPY_NO_CASTING, <cnp.dtype>NULL) | ||
| if it == NULL: | ||
| raise RuntimeError("NpyIter_New failed") | ||
| iternext = cnp.NpyIter_GetIterNext(it, NULL) | ||
| if iternext == NULL: | ||
| cnp.NpyIter_Deallocate(it) | ||
| raise RuntimeError("NpyIter_GetIterNext failed") | ||
| dataptr = cnp.NpyIter_GetDataPtrArray(it) | ||
| strideptr = cnp.NpyIter_GetInnerStrideArray(it) | ||
| innersizeptr = cnp.NpyIter_GetInnerLoopSizePtr(it) | ||
| while True: | ||
| data = dataptr[0] | ||
| stride = strideptr[0] | ||
| count = innersizeptr[0] | ||
| while count > 0: | ||
| if (<double *>data)[0] != 0.0: | ||
| nonzero_count += 1 | ||
| data += stride | ||
| count -= 1 | ||
| if not iternext(it): | ||
| break | ||
| cnp.NpyIter_Deallocate(it) | ||
| return nonzero_count | ||
| def iter_next(cnp.ndarray arr): | ||
| """Sum float64 array elements using PyArray_ITER_NEXT / _DATA / _NOTDONE.""" | ||
| cdef cnp.flatiter it = cnp.PyArray_IterNew(arr) | ||
| cdef double total = 0.0 | ||
| while cnp.PyArray_ITER_NOTDONE(it): | ||
| total += (<double *>cnp.PyArray_ITER_DATA(it))[0] | ||
| cnp.PyArray_ITER_NEXT(it) | ||
| return total | ||
| def iter_goto1d(cnp.ndarray arr, cnp.npy_intp index): | ||
| """Get element at a flat index using PyArray_ITER_GOTO1D.""" | ||
| cdef cnp.flatiter it = cnp.PyArray_IterNew(arr) | ||
| cnp.PyArray_ITER_GOTO1D(it, index) | ||
| return (<double *>cnp.PyArray_ITER_DATA(it))[0] | ||
| def iter_reset(cnp.ndarray arr): | ||
| """Sum float64 array elements after PyArray_ITER_RESET.""" | ||
| cdef cnp.flatiter it = cnp.PyArray_IterNew(arr) | ||
| cdef double total = 0.0 | ||
| while cnp.PyArray_ITER_NOTDONE(it): | ||
| cnp.PyArray_ITER_NEXT(it) | ||
| cnp.PyArray_ITER_RESET(it) | ||
| while cnp.PyArray_ITER_NOTDONE(it): | ||
| total += (<double *>cnp.PyArray_ITER_DATA(it))[0] | ||
| cnp.PyArray_ITER_NEXT(it) | ||
| return total | ||
| def iter_goto(cnp.ndarray arr, tuple coord): | ||
| """Get element at a coordinate using PyArray_ITER_GOTO.""" | ||
| cdef int nd = cnp.PyArray_NDIM(arr) | ||
| cdef cnp.npy_intp destination[32] | ||
| cdef cnp.flatiter it | ||
| cdef int i | ||
| if len(coord) != nd: | ||
| raise ValueError("coordinate length mismatch") | ||
| for i in range(nd): | ||
| destination[i] = coord[i] | ||
| it = cnp.PyArray_IterNew(arr) | ||
| cnp.PyArray_ITER_GOTO(it, destination) | ||
| return (<double *>cnp.PyArray_ITER_DATA(it))[0] | ||
| def multi_iter_next(cnp.ndarray a, cnp.ndarray b): | ||
| """Sum broadcast (a + b) using PyArray_MultiIter_NEXT / _DATA.""" | ||
| cdef cnp.broadcast multi = cnp.PyArray_MultiIterNew2(a, b) | ||
| cdef double total = 0.0 | ||
| while cnp.PyArray_MultiIter_NOTDONE(multi): | ||
| total += (<double *>cnp.PyArray_MultiIter_DATA(multi, 0))[0] | ||
| total += (<double *>cnp.PyArray_MultiIter_DATA(multi, 1))[0] | ||
| cnp.PyArray_MultiIter_NEXT(multi) | ||
| return total | ||
| def multi_iter_goto1d(cnp.ndarray a, cnp.ndarray b, cnp.npy_intp index): | ||
| """Get (a, b) at a flat index using PyArray_MultiIter_GOTO1D.""" | ||
| cdef cnp.broadcast multi = cnp.PyArray_MultiIterNew2(a, b) | ||
| cdef double va, vb | ||
| cnp.PyArray_MultiIter_GOTO1D(multi, index) | ||
| va = (<double *>cnp.PyArray_MultiIter_DATA(multi, 0))[0] | ||
| vb = (<double *>cnp.PyArray_MultiIter_DATA(multi, 1))[0] | ||
| return va, vb | ||
| def multi_iter_nexti(cnp.ndarray a, cnp.ndarray b, int steps): | ||
| """Advance only iterator 0 by N steps using PyArray_MultiIter_NEXTi.""" | ||
| cdef cnp.broadcast multi = cnp.PyArray_MultiIterNew2(a, b) | ||
| cdef int _i | ||
| for _i in range(steps): | ||
| cnp.PyArray_MultiIter_NEXTi(multi, 0) | ||
| return (<double *>cnp.PyArray_MultiIter_DATA(multi, 0))[0] | ||
| def get_datetime_value(obj): | ||
| """Underlying int64 of a datetime64 scalar via get_datetime64_value.""" | ||
| return cnp.get_datetime64_value(obj) | ||
| def get_timedelta_value(obj): | ||
| """Underlying int64 of a timedelta64 scalar via get_timedelta64_value.""" | ||
| return cnp.get_timedelta64_value(obj) | ||
| def get_datetime_unit(obj): | ||
| """Unit (NPY_DATETIMEUNIT base) of a datetime64 scalar.""" | ||
| return <int>cnp.get_datetime64_unit(obj) | ||
| def is_datetime64(obj): | ||
| """Cython is_datetime64_object (isinstance-like check).""" | ||
| return cnp.is_datetime64_object(obj) | ||
| def is_timedelta64(obj): | ||
| """Cython is_timedelta64_object (isinstance-like check).""" | ||
| return cnp.is_timedelta64_object(obj) |
| #ifndef MODULE_NAME | ||
| #error "MODULE_NAME must be defined before including this file" | ||
| #endif | ||
| #ifndef Py_LIMITED_API | ||
| #error "Py_LIMITED_API must be defined before including this file" | ||
| #endif | ||
| #include <Python.h> | ||
| #include <numpy/ndarraytypes.h> | ||
| #include <numpy/arrayobject.h> | ||
| #include <numpy/ufuncobject.h> | ||
| #include <stddef.h> | ||
| #include <string.h> | ||
| #define MODULE_NAME_STR NPY_TOSTRING(MODULE_NAME) | ||
| #ifdef Py_TARGET_ABI3T | ||
| #define ENTRY_FUNC_NAME NPY_CAT(PyModExport_, MODULE_NAME) | ||
| #else | ||
| #define ENTRY_FUNC_NAME NPY_CAT(PyInit_, MODULE_NAME) | ||
| #endif | ||
| static PyObject *limited_api_nonzero(PyObject *mod, PyArrayObject *self) | ||
| { | ||
| PyArray_NonzeroFunc* nonzero = PyDataType_GetArrFuncs(PyArray_DESCR(self))->nonzero; | ||
| NpyIter* iter; | ||
| NpyIter_IterNextFunc *iternext; | ||
| char** dataptr; | ||
| npy_intp nonzero_count; | ||
| npy_intp* strideptr,* innersizeptr; | ||
| /* Handle zero-sized arrays specially */ | ||
| if (PyArray_SIZE(self) == 0) { | ||
| return PyLong_FromLong(0); | ||
| } | ||
| /* | ||
| * Create and use an iterator to count the nonzeros. | ||
| * flag NPY_ITER_READONLY | ||
| * - The array is never written to. | ||
| * flag NPY_ITER_EXTERNAL_LOOP | ||
| * - Inner loop is done outside the iterator for efficiency. | ||
| * flag NPY_ITER_NPY_ITER_REFS_OK | ||
| * - Reference types are acceptable. | ||
| * order NPY_KEEPORDER | ||
| * - Visit elements in memory order, regardless of strides. | ||
| * This is good for performance when the specific order | ||
| * elements are visited is unimportant. | ||
| * casting NPY_NO_CASTING | ||
| * - No casting is required for this operation. | ||
| */ | ||
| iter = NpyIter_New(self, NPY_ITER_READONLY| | ||
| NPY_ITER_EXTERNAL_LOOP| | ||
| NPY_ITER_REFS_OK, | ||
| NPY_KEEPORDER, NPY_NO_CASTING, | ||
| NULL); | ||
| if (iter == NULL) { | ||
| return NULL; | ||
| } | ||
| /* | ||
| * The iternext function gets stored in a local variable | ||
| * so it can be called repeatedly in an efficient manner. | ||
| */ | ||
| iternext = NpyIter_GetIterNext(iter, NULL); | ||
| if (iternext == NULL) { | ||
| NpyIter_Deallocate(iter); | ||
| return NULL; | ||
| } | ||
| /* The location of the data pointer which the iterator may update */ | ||
| dataptr = NpyIter_GetDataPtrArray(iter); | ||
| /* The location of the stride which the iterator may update */ | ||
| strideptr = NpyIter_GetInnerStrideArray(iter); | ||
| /* The location of the inner loop size which the iterator may update */ | ||
| innersizeptr = NpyIter_GetInnerLoopSizePtr(iter); | ||
| nonzero_count = 0; | ||
| do { | ||
| /* Get the inner loop data/stride/count values */ | ||
| char* data = *dataptr; | ||
| npy_intp stride = *strideptr; | ||
| npy_intp count = *innersizeptr; | ||
| /* This is a typical inner loop for NPY_ITER_EXTERNAL_LOOP */ | ||
| while (count--) { | ||
| if (nonzero(data, self)) { | ||
| ++nonzero_count; | ||
| } | ||
| data += stride; | ||
| } | ||
| /* Increment the iterator to the next inner loop */ | ||
| } while(iternext(iter)); | ||
| NpyIter_Deallocate(iter); | ||
| return PyLong_FromLong(nonzero_count); | ||
| } | ||
| /* | ||
| * Test PyArray_ITER_NEXT, PyArray_ITER_RESET, PyArray_ITER_DATA, | ||
| * and PyArray_ITER_NOTDONE by summing all elements using the | ||
| * legacy iterator macros. | ||
| */ | ||
| static PyObject * | ||
| limited_api_iter_next(PyObject *mod, PyArrayObject *self) | ||
| { | ||
| PyObject *iter_obj = PyArray_IterNew((PyObject *)self); | ||
| if (iter_obj == NULL) { | ||
| return NULL; | ||
| } | ||
| double sum = 0.0; | ||
| while (PyArray_ITER_NOTDONE(iter_obj)) { | ||
| sum += *(double *)PyArray_ITER_DATA(iter_obj); | ||
| PyArray_ITER_NEXT(iter_obj); | ||
| } | ||
| Py_DECREF(iter_obj); | ||
| return PyFloat_FromDouble(sum); | ||
| } | ||
| /* | ||
| * Test PyArray_ITER_GOTO1D by accessing a specific flat index. | ||
| */ | ||
| static PyObject * | ||
| limited_api_iter_goto1d(PyObject *mod, PyObject *args) | ||
| { | ||
| PyArrayObject *arr; | ||
| npy_intp index; | ||
| if (!PyArg_ParseTuple(args, "O!n", &PyArray_Type, &arr, &index)) { | ||
| return NULL; | ||
| } | ||
| PyObject *iter_obj = PyArray_IterNew((PyObject *)arr); | ||
| if (iter_obj == NULL) { | ||
| return NULL; | ||
| } | ||
| PyArray_ITER_GOTO1D(iter_obj, index); | ||
| double val = *(double *)PyArray_ITER_DATA(iter_obj); | ||
| Py_DECREF(iter_obj); | ||
| return PyFloat_FromDouble(val); | ||
| } | ||
| /* | ||
| * Test PyArray_ITER_RESET by iterating, resetting, and iterating again. | ||
| * Returns the sum from the second pass (should equal the first). | ||
| */ | ||
| static PyObject * | ||
| limited_api_iter_reset(PyObject *mod, PyArrayObject *self) | ||
| { | ||
| PyObject *iter_obj = PyArray_IterNew((PyObject *)self); | ||
| if (iter_obj == NULL) { | ||
| return NULL; | ||
| } | ||
| /* First pass: skip through */ | ||
| while (PyArray_ITER_NOTDONE(iter_obj)) { | ||
| PyArray_ITER_NEXT(iter_obj); | ||
| } | ||
| /* Reset and sum */ | ||
| PyArray_ITER_RESET(iter_obj); | ||
| double sum = 0.0; | ||
| while (PyArray_ITER_NOTDONE(iter_obj)) { | ||
| sum += *(double *)PyArray_ITER_DATA(iter_obj); | ||
| PyArray_ITER_NEXT(iter_obj); | ||
| } | ||
| Py_DECREF(iter_obj); | ||
| return PyFloat_FromDouble(sum); | ||
| } | ||
| /* | ||
| * Test PyArray_MultiIter_NEXT, PyArray_MultiIter_RESET, | ||
| * PyArray_MultiIter_DATA, and PyArray_MultiIter_NOTDONE | ||
| * by computing the element-wise sum of two broadcastable arrays. | ||
| * Returns the total sum of (a + b) for all broadcast elements. | ||
| */ | ||
| static PyObject * | ||
| limited_api_multi_iter_next(PyObject *mod, PyObject *args) | ||
| { | ||
| PyArrayObject *a, *b; | ||
| if (!PyArg_ParseTuple(args, "O!O!", &PyArray_Type, &a, | ||
| &PyArray_Type, &b)) { | ||
| return NULL; | ||
| } | ||
| PyObject *multi = PyArray_MultiIterNew(2, a, b); | ||
| if (multi == NULL) { | ||
| return NULL; | ||
| } | ||
| double sum = 0.0; | ||
| while (PyArray_MultiIter_NOTDONE(multi)) { | ||
| double va = *(double *)PyArray_MultiIter_DATA(multi, 0); | ||
| double vb = *(double *)PyArray_MultiIter_DATA(multi, 1); | ||
| sum += va + vb; | ||
| PyArray_MultiIter_NEXT(multi); | ||
| } | ||
| /* Test reset: iterate again and verify same sum */ | ||
| PyArray_MultiIter_RESET(multi); | ||
| double sum2 = 0.0; | ||
| while (PyArray_MultiIter_NOTDONE(multi)) { | ||
| double va = *(double *)PyArray_MultiIter_DATA(multi, 0); | ||
| double vb = *(double *)PyArray_MultiIter_DATA(multi, 1); | ||
| sum2 += va + vb; | ||
| PyArray_MultiIter_NEXT(multi); | ||
| } | ||
| Py_DECREF(multi); | ||
| if (sum != sum2) { | ||
| PyErr_SetString(PyExc_RuntimeError, | ||
| "MultiIter reset produced different sum"); | ||
| return NULL; | ||
| } | ||
| return PyFloat_FromDouble(sum); | ||
| } | ||
| /* | ||
| * Test PyArray_ITER_GOTO by jumping to a coordinate and reading the value. | ||
| */ | ||
| static PyObject * | ||
| limited_api_iter_goto(PyObject *mod, PyObject *args) | ||
| { | ||
| PyArrayObject *arr; | ||
| PyObject *coord_tuple; | ||
| if (!PyArg_ParseTuple(args, "O!O!", &PyArray_Type, &arr, | ||
| &PyTuple_Type, &coord_tuple)) { | ||
| return NULL; | ||
| } | ||
| int nd = PyArray_NDIM(arr); | ||
| if (PyTuple_Size(coord_tuple) != nd) { | ||
| PyErr_SetString(PyExc_ValueError, "coordinate length mismatch"); | ||
| return NULL; | ||
| } | ||
| npy_intp destination[NPY_MAXDIMS_LEGACY_ITERS]; | ||
| for (int i = 0; i < nd; i++) { | ||
| destination[i] = PyLong_AsLong(PyTuple_GetItem(coord_tuple, i)); | ||
| if (destination[i] == -1 && PyErr_Occurred()) { | ||
| return NULL; | ||
| } | ||
| } | ||
| PyObject *iter_obj = PyArray_IterNew((PyObject *)arr); | ||
| if (iter_obj == NULL) { | ||
| return NULL; | ||
| } | ||
| PyArray_ITER_GOTO(iter_obj, destination); | ||
| double val = *(double *)PyArray_ITER_DATA(iter_obj); | ||
| Py_DECREF(iter_obj); | ||
| return PyFloat_FromDouble(val); | ||
| } | ||
| /* | ||
| * Test PyArray_MultiIter_GOTO by jumping to a coordinate | ||
| * and returning (a_val, b_val) at that position. | ||
| */ | ||
| static PyObject * | ||
| limited_api_multi_iter_goto(PyObject *mod, PyObject *args) | ||
| { | ||
| PyArrayObject *a, *b; | ||
| PyObject *coord_tuple; | ||
| if (!PyArg_ParseTuple(args, "O!O!O!", &PyArray_Type, &a, | ||
| &PyArray_Type, &b, | ||
| &PyTuple_Type, &coord_tuple)) { | ||
| return NULL; | ||
| } | ||
| PyObject *multi = PyArray_MultiIterNew(2, a, b); | ||
| if (multi == NULL) { | ||
| return NULL; | ||
| } | ||
| int nd = _PyMIT(multi)->nd; | ||
| if (PyTuple_Size(coord_tuple) != nd) { | ||
| Py_DECREF(multi); | ||
| PyErr_SetString(PyExc_ValueError, "coordinate length mismatch"); | ||
| return NULL; | ||
| } | ||
| npy_intp destination[NPY_MAXDIMS_LEGACY_ITERS]; | ||
| for (int i = 0; i < nd; i++) { | ||
| destination[i] = PyLong_AsLong(PyTuple_GetItem(coord_tuple, i)); | ||
| if (destination[i] == -1 && PyErr_Occurred()) { | ||
| Py_DECREF(multi); | ||
| return NULL; | ||
| } | ||
| } | ||
| PyArray_MultiIter_GOTO(multi, destination); | ||
| double va = *(double *)PyArray_MultiIter_DATA(multi, 0); | ||
| double vb = *(double *)PyArray_MultiIter_DATA(multi, 1); | ||
| Py_DECREF(multi); | ||
| return Py_BuildValue("dd", va, vb); | ||
| } | ||
| /* | ||
| * Test PyArray_MultiIter_GOTO1D by jumping to a flat index | ||
| * and returning (a_val, b_val) at that position. | ||
| */ | ||
| static PyObject * | ||
| limited_api_multi_iter_goto1d(PyObject *mod, PyObject *args) | ||
| { | ||
| PyArrayObject *a, *b; | ||
| npy_intp index; | ||
| if (!PyArg_ParseTuple(args, "O!O!n", &PyArray_Type, &a, | ||
| &PyArray_Type, &b, &index)) { | ||
| return NULL; | ||
| } | ||
| PyObject *multi = PyArray_MultiIterNew(2, a, b); | ||
| if (multi == NULL) { | ||
| return NULL; | ||
| } | ||
| PyArray_MultiIter_GOTO1D(multi, index); | ||
| double va = *(double *)PyArray_MultiIter_DATA(multi, 0); | ||
| double vb = *(double *)PyArray_MultiIter_DATA(multi, 1); | ||
| Py_DECREF(multi); | ||
| return Py_BuildValue("dd", va, vb); | ||
| } | ||
| /* | ||
| * Test PyArray_MultiIter_NEXTi by advancing only the first iterator | ||
| * and returning its data pointer value after N steps. | ||
| */ | ||
| static PyObject * | ||
| limited_api_multi_iter_nexti(PyObject *mod, PyObject *args) | ||
| { | ||
| PyArrayObject *a, *b; | ||
| int steps; | ||
| if (!PyArg_ParseTuple(args, "O!O!i", &PyArray_Type, &a, | ||
| &PyArray_Type, &b, &steps)) { | ||
| return NULL; | ||
| } | ||
| PyObject *multi = PyArray_MultiIterNew(2, a, b); | ||
| if (multi == NULL) { | ||
| return NULL; | ||
| } | ||
| for (int i = 0; i < steps; i++) { | ||
| PyArray_MultiIter_NEXTi(multi, 0); | ||
| } | ||
| double val = *(double *)PyArray_MultiIter_DATA(multi, 0); | ||
| Py_DECREF(multi); | ||
| return PyFloat_FromDouble(val); | ||
| } | ||
| static PyObject * | ||
| limited_api_datetime_metadata(PyObject *mod, PyArrayObject *arr) | ||
| { | ||
| PyArray_Descr *descr = PyArray_DESCR(arr); | ||
| npy_uint64 flags = PyDataType_FLAGS(descr); | ||
| PyArray_DatetimeDTypeMetaData *dt_meta = | ||
| (PyArray_DatetimeDTypeMetaData *)PyDataType_C_METADATA(descr); | ||
| if (dt_meta == NULL) { | ||
| PyErr_SetString( | ||
| PyExc_RuntimeError, | ||
| "PyDataType_C_METADATA() returned NULL for a datetime descriptor"); | ||
| return NULL; | ||
| } | ||
| return Py_BuildValue("Kii", (unsigned long long)flags, | ||
| (int)dt_meta->meta.base, dt_meta->meta.num); | ||
| } | ||
| /* | ||
| * Test the NpyString allocator API. Under the abi3t stable ABI | ||
| * PyArray_StringDTypeObject is an opaque struct; the descriptor object | ||
| * pointer is passed to NpyString_acquire_allocator without accessing any | ||
| * struct fields. The API only exists when targeting NumPy 2.0+. | ||
| */ | ||
| #if NPY_FEATURE_VERSION >= NPY_2_0_API_VERSION | ||
| static PyObject * | ||
| limited_api_stringdtype_load(PyObject *mod, PyArrayObject *arr) | ||
| { | ||
| npy_string_allocator *allocator = NpyString_acquire_allocator( | ||
| (PyArray_StringDTypeObject *)PyArray_DESCR(arr)); | ||
| npy_packed_static_string *packed = | ||
| (npy_packed_static_string *)PyArray_DATA(arr); | ||
| npy_static_string s = {0, NULL}; | ||
| PyObject *res = NULL; | ||
| int is_null = NpyString_load(allocator, packed, &s); | ||
| if (is_null == -1) { | ||
| PyErr_SetString(PyExc_RuntimeError, "NpyString_load failed"); | ||
| } | ||
| else if (is_null) { | ||
| res = Py_None; | ||
| Py_INCREF(res); | ||
| } | ||
| else { | ||
| res = PyUnicode_FromStringAndSize(s.buf, (Py_ssize_t)s.size); | ||
| } | ||
| NpyString_release_allocator(allocator); | ||
| return res; | ||
| } | ||
| #endif /* NPY_FEATURE_VERSION >= NPY_2_0_API_VERSION */ | ||
| static PyMethodDef limited_api_methods[] = { | ||
| {"nonzero", (PyCFunction)limited_api_nonzero, METH_O, | ||
| "Count the number of non-zero elements in the array."}, | ||
| {"iter_next", (PyCFunction)limited_api_iter_next, METH_O, | ||
| "Sum array elements using PyArray_ITER_NEXT."}, | ||
| {"iter_goto1d", (PyCFunction)limited_api_iter_goto1d, METH_VARARGS, | ||
| "Get element at flat index using PyArray_ITER_GOTO1D."}, | ||
| {"iter_reset", (PyCFunction)limited_api_iter_reset, METH_O, | ||
| "Sum array elements after reset using PyArray_ITER_RESET."}, | ||
| {"multi_iter_next", (PyCFunction)limited_api_multi_iter_next, | ||
| METH_VARARGS, | ||
| "Sum broadcast (a+b) using PyArray_MultiIter_NEXT."}, | ||
| {"iter_goto", (PyCFunction)limited_api_iter_goto, METH_VARARGS, | ||
| "Get element at coordinate using PyArray_ITER_GOTO."}, | ||
| {"multi_iter_goto", (PyCFunction)limited_api_multi_iter_goto, | ||
| METH_VARARGS, | ||
| "Get (a, b) at coordinate using PyArray_MultiIter_GOTO."}, | ||
| {"multi_iter_goto1d", (PyCFunction)limited_api_multi_iter_goto1d, | ||
| METH_VARARGS, | ||
| "Get (a, b) at flat index using PyArray_MultiIter_GOTO1D."}, | ||
| {"multi_iter_nexti", (PyCFunction)limited_api_multi_iter_nexti, | ||
| METH_VARARGS, | ||
| "Advance only iter 0 N steps using PyArray_MultiIter_NEXTi."}, | ||
| {"datetime_metadata", (PyCFunction)limited_api_datetime_metadata, | ||
| METH_O, | ||
| "Get (flags, unit base, unit num) from a datetime64/timedelta64 array."}, | ||
| #if NPY_FEATURE_VERSION >= NPY_2_0_API_VERSION | ||
| {"stringdtype_load", (PyCFunction)limited_api_stringdtype_load, METH_O, | ||
| "Load the first element of a StringDType array via the NpyString API."}, | ||
| #endif | ||
| {NULL, NULL, 0, NULL} /* Sentinel */ | ||
| }; | ||
| #ifdef Py_TARGET_ABI3T | ||
| PyABIInfo_VAR(abi_info); | ||
| static PySlot limited_api_slots[] = { | ||
| PySlot_STATIC_DATA(Py_mod_abi, &abi_info), | ||
| PySlot_STATIC_DATA(Py_mod_name, MODULE_NAME_STR), | ||
| PySlot_STATIC_DATA(Py_mod_methods, limited_api_methods), | ||
| PySlot_STATIC_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), | ||
| PySlot_END, | ||
| }; | ||
| PyMODEXPORT_FUNC | ||
| ENTRY_FUNC_NAME(void) | ||
| { | ||
| import_array(); | ||
| import_umath(); | ||
| return limited_api_slots; | ||
| } | ||
| #else | ||
| static PyModuleDef moduledef = { | ||
| .m_base = PyModuleDef_HEAD_INIT, | ||
| .m_name = MODULE_NAME_STR, | ||
| .m_size = -1, | ||
| .m_methods = limited_api_methods, | ||
| }; | ||
| PyMODINIT_FUNC | ||
| ENTRY_FUNC_NAME(void) | ||
| { | ||
| import_array(); | ||
| import_umath(); | ||
| return PyModule_Create(&moduledef); | ||
| } | ||
| #endif |
@@ -38,3 +38,3 @@ name: Test Emscripten/Pyodide build | ||
| persist-credentials: false | ||
| - uses: pypa/cibuildwheel@294735312765b09d24a2fbec22660ce817587d55 # v4.1.0 | ||
| - uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 | ||
| env: | ||
@@ -41,0 +41,0 @@ CIBW_PLATFORM: pyodide |
@@ -105,5 +105,5 @@ # Workflow to build and test wheels, similarly to numpy/numpy-release. | ||
| - name: Build wheels | ||
| uses: pypa/cibuildwheel@294735312765b09d24a2fbec22660ce817587d55 # v4.1.0 | ||
| env: | ||
| CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }} | ||
| uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 | ||
| with: | ||
| only: ${{ matrix.python }}-${{ matrix.buildplat[1] }} | ||
@@ -110,0 +110,0 @@ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 |
+6
-0
@@ -103,2 +103,5 @@ # Prevent git from showing duplicate names with commands like "git shortlog" | ||
| Abdul Muneer <abdulmuneer@gmail.com> | ||
| Abhijeetsingh Meena <abhijeet040403@gmail.com> | ||
| Abhijeetsingh Meena <abhijeet040403@gmail.com> <amethhac@gmail.com> | ||
| Abhijeetsingh Meena <abhijeet040403@gmail.com> NumPy Bugfix <bugfix@localhost> | ||
| Abhilash Barigidad <abhilash.ub@gmail.com> | ||
@@ -341,2 +344,3 @@ Abhilash Barigidad <abhilash.ub@gmail.com> <64172584+abhilash42@users.noreply.github.com> | ||
| Gavin Zhang <zhanggan@cn.ibm.com> | ||
| Geonho <mnb0968@naver.com> | ||
| Gerrit Holl <gerrit.holl@gmail.com> <gerrit.holl@utoronto.ca> | ||
@@ -347,2 +351,4 @@ Gerrit Holl <gerrit.holl@gmail.com> <g.holl@reading.ac.uk> | ||
| Gopal Singh Meena <gopalmeena94@gmail.com> | ||
| Gopu Yeshwanth Reddy <Gopu.Reddy@deshaw.com> | ||
| Gopu Yeshwanth Reddy <Gopu.Reddy@deshaw.com> <87797634+Yeshwanth-G@users.noreply.github.com> | ||
| Greg Knoll <gregory@bccn-berlin.de> | ||
@@ -349,0 +355,0 @@ Greg Yang <sorcererofdm@gmail.com> |
@@ -8,2 +8,3 @@ ************* | ||
| 2.5.2 <release/2.5.2-notes> | ||
| 2.5.1 <release/2.5.1-notes> | ||
@@ -10,0 +11,0 @@ 2.5.0 <release/2.5.0-notes> |
@@ -71,3 +71,3 @@ # This file is generated by numpy's build process | ||
| }, | ||
| "cross-compiled": bool("@CROSS_COMPILED@".lower().replace("false", "")), | ||
| "cross-compiled": @CROSS_COMPILED@, | ||
| }, | ||
@@ -77,3 +77,3 @@ "Build Dependencies": { | ||
| "name": "@BLAS_NAME@", | ||
| "found": bool("@BLAS_FOUND@".lower().replace("false", "")), | ||
| "found": @BLAS_FOUND@, | ||
| "version": "@BLAS_VERSION@", | ||
@@ -88,3 +88,3 @@ "detection method": "@BLAS_TYPE_NAME@", | ||
| "name": "@LAPACK_NAME@", | ||
| "found": bool("@LAPACK_FOUND@".lower().replace("false", "")), | ||
| "found": @LAPACK_FOUND@, | ||
| "version": "@LAPACK_VERSION@", | ||
@@ -91,0 +91,0 @@ "detection method": "@LAPACK_TYPE_NAME@", |
@@ -76,2 +76,5 @@ """ | ||
| - **"max dimensions"**: integer indicating maximum number of supported | ||
| dimensions. Always ``64`` for NumPy. | ||
| See | ||
@@ -78,0 +81,0 @@ https://data-apis.org/array-api/latest/API_specification/generated/array_api.info.capabilities.html |
@@ -13,2 +13,3 @@ from typing import Literal, Never, TypedDict, final, overload, type_check_only | ||
| "data-dependent shapes": Literal[True], | ||
| "max dimensions": Literal[64], | ||
| }, | ||
@@ -15,0 +16,0 @@ ) |
@@ -6,2 +6,3 @@ #ifndef NUMPY_CORE_INCLUDE_NUMPY_UFUNCOBJECT_H_ | ||
| #include <numpy/npy_common.h> | ||
| #include <numpy/utils.h> | ||
@@ -112,3 +113,4 @@ #ifdef __cplusplus | ||
| */ | ||
| int nin, nout, nargs; | ||
| _NPY_OPAQUE_FIRST_FIELD int nin; | ||
| int nout, nargs; | ||
@@ -115,0 +117,0 @@ /* |
@@ -16,2 +16,6 @@ #ifndef NUMPY_CORE_INCLUDE_NUMPY_UTILS_H_ | ||
| // Older compilers may name `_Alignas` differently; to allow compilation on such | ||
| // unsupported platforms, we don't redefine NPY_DECL_ALIGNED if it's already | ||
| // defined similar to CPython's _Py_ALIGNED_DEF. | ||
| #ifndef NPY_DECL_ALIGNED | ||
| #if defined(__GNUC__) || defined(__ICC) || defined(__clang__) | ||
@@ -21,5 +25,63 @@ #define NPY_DECL_ALIGNED(x) __attribute__ ((aligned (x))) | ||
| #define NPY_DECL_ALIGNED(x) __declspec(align(x)) | ||
| #elif defined(__cplusplus) | ||
| #define NPY_DECL_ALIGNED(x) alignas(x) | ||
| #else | ||
| #define NPY_DECL_ALIGNED(x) | ||
| #define NPY_DECL_ALIGNED(x) _Alignas(x) | ||
| #endif | ||
| #endif | ||
| /* | ||
| * Force the first field of a struct to be 8-byte aligned on Python 3.15+ to achieve | ||
| * ABI compatibility of `_fields` structs with and without `PyObject_HEAD`. | ||
| * | ||
| * When we expose (or may want to) expose an object structs contents to the Python | ||
| * stable ABI we define it as: | ||
| * | ||
| * struct _PyArray_Descr { | ||
| * #if !defined(Py_TARGET_ABI3T) | ||
| * PyObject_HEAD; | ||
| * #endif | ||
| * _NPY_OPAQUE_FIRST_FIELD T first_field; | ||
| * ... | ||
| * } fields; | ||
| * | ||
| * Effectively creating two versions, one `fields_obj` and `fields_no_obj` depending | ||
| * on the build target. These builds must be ABI compatible and for that: | ||
| * | ||
| * offsetof(fields_obj, first_field) % alignof(fields_no_obj) == 0 | ||
| * | ||
| * must hold. If it does not hold, then `fields_obj` (minus the PyObject_HEAD) will | ||
| * be padded differently from `fields_no_obj`. Concretely, this happened for 32bit | ||
| * free-threaded builds (PyObject_HEAD having a size of 20 bytes) where the uint64 | ||
| * flags required padding to achieve 8 byte alignment, but with the `PyObject_HEAD`. | ||
| * | ||
| * The only way to ensure the above holds always is to pad the first field so that | ||
| * it's offset is a multiple of the alignment. The simplest path (e.g. what PEP 697 | ||
| * also does) may be to always pad to `alignof(max_align_t)`. | ||
| * We do _not_ do this, because we have to remain ABI compatible to abi3 builds and | ||
| * Python GIL enabled builds have a PyObject_HEAD size that is a multiple of | ||
| * 8/16 (32bit/64bit systems) alignment. | ||
| * So, instead we use 8 bytes. We could choose 16 for 64bit systems but it currently | ||
| * makes no difference (there is a static_assert to notify us if it might matter). | ||
| * | ||
| * Because the ABI3T target was added in Python 3.15 we _can_ freely add padding for | ||
| * Python 3.15+ builds for them. And this is where it matters, because 32bit | ||
| * free-threaded builds have a `sizeof(PyObject) == 20` which gives 4 byte max | ||
| * alignment which breaks the requirement above. | ||
| * | ||
| * As an actual implementation we add the padding by explicitly aligning the first | ||
| * field to 8 bytes. | ||
| * In theory this might guarantee an alignment larger than `alignof(max_align_t)`, | ||
| * we assume that we would notice this first and in practice malloc alignment is >=8. | ||
| * | ||
| * One may be able to expose >8 byte aligned fields, but this requires thoughts on | ||
| * how it affects abi3, abi3t builds and compatibility with older Python/NumPy builds. | ||
| * | ||
| * Note that assertions concerning these assumptions are paired with the | ||
| * `*_GET_ITEM_DATA` function definitions (in `arrayobject.c`). | ||
| */ | ||
| #if PY_VERSION_HEX >= 0x030f0000 | ||
| #define _NPY_OPAQUE_FIRST_FIELD NPY_DECL_ALIGNED(8) | ||
| #else | ||
| #define _NPY_OPAQUE_FIRST_FIELD | ||
| #endif | ||
@@ -26,0 +88,0 @@ /* Use this to tag a variable as not used. It will remove unused variable |
@@ -810,3 +810,6 @@ import datetime as dt | ||
| # | ||
| def bincount(x: _ArrayLikeInt_co, /, weights: ArrayLike | None = None, minlength: SupportsIndex = 0) -> _Array1D[intp]: ... | ||
| @overload | ||
| def bincount(x: _ArrayLikeInt_co, /, weights: None = None, minlength: SupportsIndex = 0) -> _Array1D[intp]: ... | ||
| @overload | ||
| def bincount(x: _ArrayLikeInt_co, /, weights: _ArrayLikeFloat_co, minlength: SupportsIndex = 0) -> _Array1D[np.float64]: ... | ||
@@ -1793,3 +1796,3 @@ # | ||
| flags: Sequence[_NDIterFlagsKind] | None = None, | ||
| op_flags: Sequence[_NDIterFlagsOp] | None = None, | ||
| op_flags: Sequence[_NDIterFlagsOp] | Sequence[Sequence[_NDIterFlagsOp]] | None = None, | ||
| op_dtypes: DTypeLike | None = None, | ||
@@ -1796,0 +1799,0 @@ order: _OrderKACF = "K", |
+16
-16
@@ -1283,4 +1283,4 @@ from _typeshed import Incomplete | ||
| b: _NumberLike_co, | ||
| rtol: ArrayLike = 1e-5, | ||
| atol: ArrayLike = 1e-8, | ||
| rtol: _ScalarLike_co = 1e-5, | ||
| atol: _ScalarLike_co = 1e-8, | ||
| equal_nan: py_bool = False, | ||
@@ -1292,4 +1292,4 @@ ) -> np.bool: ... | ||
| b: np.ndarray[ShapeT] | _NumberLike_co, | ||
| rtol: ArrayLike = 1e-5, | ||
| atol: ArrayLike = 1e-8, | ||
| rtol: _ScalarLike_co = 1e-5, | ||
| atol: _ScalarLike_co = 1e-8, | ||
| equal_nan: py_bool = False, | ||
@@ -1301,4 +1301,4 @@ ) -> np.ndarray[ShapeT, np.dtype[np.bool]]: ... | ||
| b: np.ndarray[ShapeT], | ||
| rtol: ArrayLike = 1e-5, | ||
| atol: ArrayLike = 1e-8, | ||
| rtol: _ScalarLike_co = 1e-5, | ||
| atol: _ScalarLike_co = 1e-8, | ||
| equal_nan: py_bool = False, | ||
@@ -1310,4 +1310,4 @@ ) -> np.ndarray[ShapeT, np.dtype[np.bool]]: ... | ||
| b: Sequence[_NumberLike_co] | _NumberLike_co | np.ndarray[tuple[int]], | ||
| rtol: ArrayLike = 1e-5, | ||
| atol: ArrayLike = 1e-8, | ||
| rtol: _ScalarLike_co = 1e-5, | ||
| atol: _ScalarLike_co = 1e-8, | ||
| equal_nan: py_bool = False, | ||
@@ -1319,4 +1319,4 @@ ) -> np.ndarray[tuple[int], np.dtype[np.bool]]: ... | ||
| b: Sequence[_NumberLike_co], | ||
| rtol: ArrayLike = 1e-5, | ||
| atol: ArrayLike = 1e-8, | ||
| rtol: _ScalarLike_co = 1e-5, | ||
| atol: _ScalarLike_co = 1e-8, | ||
| equal_nan: py_bool = False, | ||
@@ -1328,6 +1328,6 @@ ) -> np.ndarray[tuple[int], np.dtype[np.bool]]: ... | ||
| b: Sequence[Sequence[_NumberLike_co]] | Sequence[_NumberLike_co] | _NumberLike_co | np.ndarray[tuple[int] | tuple[int, int]], | ||
| rtol: ArrayLike = 1e-5, | ||
| atol: ArrayLike = 1e-8, | ||
| rtol: _ScalarLike_co = 1e-5, | ||
| atol: _ScalarLike_co = 1e-8, | ||
| equal_nan: py_bool = False, | ||
| ) -> np.ndarray[tuple[int], np.dtype[np.bool]]: ... | ||
| ) -> np.ndarray[tuple[int, int], np.dtype[np.bool]]: ... | ||
| @overload # <=2d array-like, 2d sequence | ||
@@ -1337,6 +1337,6 @@ def isclose( | ||
| a: Sequence[Sequence[_NumberLike_co]], | ||
| rtol: ArrayLike = 1e-5, | ||
| atol: ArrayLike = 1e-8, | ||
| rtol: _ScalarLike_co = 1e-5, | ||
| atol: _ScalarLike_co = 1e-8, | ||
| equal_nan: py_bool = False, | ||
| ) -> np.ndarray[tuple[int], np.dtype[np.bool]]: ... | ||
| ) -> np.ndarray[tuple[int, int], np.dtype[np.bool]]: ... | ||
| @overload # unknown shape, unknown shape | ||
@@ -1343,0 +1343,0 @@ def isclose( |
@@ -84,2 +84,3 @@ """ | ||
| from . import multiarray as ma | ||
| from ._multiarray_umath import StringDType | ||
| from .multiarray import ( | ||
@@ -315,5 +316,11 @@ busday_count, | ||
| """ | ||
| orig_dtype = dtype | ||
| if isinstance(dtype, ma.dtype): | ||
| dtype = dtype.type | ||
| if isinstance(dtype, ndarray) or dtype not in allTypes.values(): | ||
| if isinstance(orig_dtype, StringDType) or orig_dtype is StringDType: | ||
| # StringDType's scalar type is the builtin `str`, which is not | ||
| # a NumPy scalar type, so use the DType class to identify it. | ||
| # if we ever fix gh-28165, this should be deleted | ||
| return StringDType | ||
| raise _PreprocessDTypeError | ||
@@ -320,0 +327,0 @@ return dtype |
@@ -1,2 +0,1 @@ | ||
| # pyright: reportSelfClsParameterName=false | ||
| from _typeshed import Incomplete, StrOrBytesPath | ||
@@ -148,4 +147,3 @@ from collections.abc import Buffer, Iterable, Sequence | ||
| shape: _ShapeLike | None = None, | ||
| *, | ||
| formats: DTypeLike | None, | ||
| formats: DTypeLike | None = None, | ||
| names: str | Sequence[str] | None = None, | ||
@@ -157,2 +155,3 @@ titles: str | Sequence[str] | None = None, | ||
| # exported in `numpy.rec` | ||
| @overload | ||
@@ -174,4 +173,3 @@ def fromrecords( | ||
| shape: _ShapeLike | None = None, | ||
| *, | ||
| formats: DTypeLike | None, | ||
| formats: DTypeLike | None = None, | ||
| names: str | Sequence[str] | None = None, | ||
@@ -178,0 +176,0 @@ titles: str | Sequence[str] | None = None, |
@@ -108,2 +108,3 @@ /** | ||
| if (dst == NULL) { | ||
| Py_DECREF(seq_obj); | ||
| return NULL; | ||
@@ -110,0 +111,0 @@ } |
@@ -277,4 +277,4 @@ #include "npy_cpu_features.h" | ||
| PyErr_Format(PyExc_RuntimeError, | ||
| "Length of environment variable '%s' is %d, only %d accepted", | ||
| env_name, var_len, NPY__MAX_VAR_LEN | ||
| "Length of environment variable '%s' is %zu, only %zu accepted", | ||
| env_name, var_len, (size_t)NPY__MAX_VAR_LEN | ||
| ); | ||
@@ -289,3 +289,3 @@ return -1; | ||
| char notsupp[sizeof(NPY_WITH_CPU_DISPATCH) + 1]; | ||
| char notsupp[NPY__MAX_VAR_LEN]; | ||
| char *notsupp_cur = ¬supp[0]; | ||
@@ -313,4 +313,7 @@ | ||
| int flen = strlen(feature); | ||
| if (nexist_cur != nexist) { | ||
| *nexist_cur++ = ' '; | ||
| } | ||
| memcpy(nexist_cur, feature, flen); | ||
| nexist_cur[flen] = ' '; nexist_cur += flen + 1; | ||
| nexist_cur += flen; | ||
| goto next; | ||
@@ -321,4 +324,7 @@ } | ||
| int flen = strlen(feature); | ||
| if (notsupp_cur != notsupp) { | ||
| *notsupp_cur++ = ' '; | ||
| } | ||
| memcpy(notsupp_cur, feature, flen); | ||
| notsupp_cur[flen] = ' '; notsupp_cur += flen + 1; | ||
| notsupp_cur += flen; | ||
| goto next; | ||
@@ -342,3 +348,2 @@ } | ||
| if (nexist[0] != '\0') { | ||
| *(nexist_cur-1) = '\0'; // trim the last space | ||
| if (PyErr_WarnFormat(PyExc_ImportWarning, 1, | ||
@@ -362,3 +367,2 @@ "%sYou cannot %s CPU features (%s), since " | ||
| if (notsupp[0] != '\0') { | ||
| *(notsupp_cur-1) = '\0'; // trim the last space | ||
| if (!disable){ | ||
@@ -365,0 +369,0 @@ PyErr_Format(PyExc_RuntimeError, NOTSUPP_BODY); |
@@ -444,2 +444,5 @@ /* | ||
| /* where=False copies nothing */ | ||
| if (copied_src) { | ||
| Py_DECREF(src); | ||
| } | ||
| return 0; | ||
@@ -446,0 +449,0 @@ } |
@@ -468,4 +468,4 @@ /* | ||
| } | ||
| memset((char *)(res->method) + sizeof(PyObject), 0, | ||
| sizeof(PyArrayMethodObject) - sizeof(PyObject)); | ||
| memset((char *)(res->method) + offsetof(PyArrayMethodObject, name), 0, | ||
| sizeof(PyArrayMethodObject) - offsetof(PyArrayMethodObject, name)); | ||
@@ -472,0 +472,0 @@ res->method->nin = spec->nin; |
@@ -1280,8 +1280,37 @@ /* | ||
| /* | ||
| The following *_GET_ITEM_DATA functions are used to get the pointer to the fields of the | ||
| corresponding struct from the given object. It is technically undefined behaviour | ||
| to access the fields of the struct through a pointer that is not of the same type, | ||
| but in our case it is not a problem in practice because this is used only in stable ABI | ||
| extensions where the original object layout is opaque. | ||
| */ | ||
| * Python stable ABI compatible object field accessor functions. | ||
| * | ||
| * The following *_GET_ITEM_DATA functions are used to get the pointer to the fields of the | ||
| * corresponding struct from the given object. It is technically undefined behaviour | ||
| * to access the fields of the struct through a pointer that is not of the same type, | ||
| * but in our case it is not a problem in practice because this is used only in stable ABI | ||
| * extensions where the original object layout is opaque. | ||
| * | ||
| * To expose the struct this way alignment guarantees must be met, see `utils.h` and the | ||
| * definition of `_NPY_OPAQUE_FIRST_FIELD`. | ||
| */ | ||
| #if SIZEOF_VOID_P != 4 // not a 32bit build | ||
| /* | ||
| * If this assert fails then Python changed the sizeof(PyObject). If we simply remove the | ||
| * assert we lose flexibility to add 16byte aligned fields to the stable ABI fields. | ||
| * We can choose that this is fine or increase the padding to 16/max_align_t when it happens. | ||
| * (See comments in `ndarraytypes.h` for more details.) | ||
| */ | ||
| static_assert(sizeof(PyObject) % 16 == 0, | ||
| "Expected sizeof(PyObject) to be multiple of 16 on 64bit builds."); | ||
| #endif | ||
| static_assert(NPY_ALIGNOF(PyArray_Descr_fields) <= 8, | ||
| "PyArray_Descr must not require more than 8-byte alignment"); | ||
| static_assert(NPY_ALIGNOF(_PyArray_LegacyDescr_fields) <= 8, | ||
| "_PyArray_LegacyDescr must not require more than 8-byte alignment"); | ||
| static_assert(NPY_ALIGNOF(PyArrayObject_fields) <= 8, | ||
| "PyArrayObject must not require more than 8-byte alignment"); | ||
| static_assert(NPY_ALIGNOF(PyArrayMultiIterObject_fields) <= 8, | ||
| "PyArrayMultiIterObject must not require more than 8-byte alignment"); | ||
| static_assert(NPY_ALIGNOF(PyArrayIterObject_fields) <= 8, | ||
| "PyArrayIterObject must not require more than 8-byte alignment"); | ||
| static_assert(NPY_ALIGNOF(PyArrayNeighborhoodIterObject_fields) <= 8, | ||
| "PyArrayNeighborhoodIterObject must not require more than 8-byte alignment"); | ||
| #undef _PyDataType_GET_ITEM_DATA | ||
@@ -1292,3 +1321,3 @@ /*NUMPY_API*/ | ||
| { | ||
| return (PyArray_Descr_fields *)(((char *)dtype) + sizeof(PyObject)); | ||
| return (PyArray_Descr_fields *)(((char *)dtype) + offsetof(PyArray_Descr, typeobj)); | ||
| } | ||
@@ -1300,3 +1329,3 @@ #undef _PyArray_LegacyDescr_GET_ITEM_DATA | ||
| { | ||
| return (_PyArray_LegacyDescr_fields *)(((char *)dtype) + sizeof(PyObject)); | ||
| return (_PyArray_LegacyDescr_fields *)(((char *)dtype) + offsetof(_PyArray_LegacyDescr, typeobj)); | ||
| } | ||
@@ -1308,3 +1337,3 @@ #undef _PyArray_GET_ITEM_DATA | ||
| { | ||
| return (PyArrayObject_fields *)(((char *)arr) + sizeof(PyObject)); | ||
| return (PyArrayObject_fields *)(((char *)arr) + offsetof(PyArrayObject_fields, data)); | ||
| } | ||
@@ -1316,3 +1345,3 @@ #undef _PyArrayMultiIter_GET_ITEM_DATA | ||
| { | ||
| return (PyArrayMultiIterObject_fields *)(((char *)multi) + sizeof(PyObject)); | ||
| return (PyArrayMultiIterObject_fields *)(((char *)multi) + offsetof(PyArrayMultiIterObject_fields, numiter)); | ||
| } | ||
@@ -1324,3 +1353,3 @@ #undef _PyArrayIter_GET_ITEM_DATA | ||
| { | ||
| return (PyArrayIterObject_fields *)(((char *)iter) + sizeof(PyObject)); | ||
| return (PyArrayIterObject_fields *)(((char *)iter) + offsetof(PyArrayIterObject_fields, nd_m1)); | ||
| } | ||
@@ -1332,3 +1361,3 @@ #undef _PyArrayNeighborhoodIter_GET_ITEM_DATA | ||
| { | ||
| return (PyArrayNeighborhoodIterObject_fields *)(((char *)iter) + sizeof(PyObject)); | ||
| return (PyArrayNeighborhoodIterObject_fields *)(((char *)iter) + offsetof(PyArrayNeighborhoodIterObject_fields, nd_m1)); | ||
| } | ||
@@ -1335,0 +1364,0 @@ #undef _PyDatetimeScalarObject_GetMetadata |
@@ -831,4 +831,12 @@ #define NPY_NO_DEPRECATED_API NPY_API_VERSION | ||
| { | ||
| if (NPY_DT_SLOTS(NPY_DTYPE(PyArray_DTYPE(self)))->imag_meth != NULL) { | ||
| /* The dtype has `arr.imag` so `conjugate` must exist (or error) */ | ||
| PyArray_DTypeMeta *dtype = NPY_DTYPE(PyArray_DESCR(self)); | ||
| /* | ||
| * If a dtype doesn't define `imag_meth` and is numeric, we assume it isn't | ||
| * a complex dtype (`conjugate()` does nothing). | ||
| * For user defined legacy dtypes we always try the ufunc for backwards | ||
| * compatibility (could be deprecated). Unless they flag "numeric" because if | ||
| * they do they live in a future where they could set `imag_meth` as well. | ||
| */ | ||
| if (NPY_DT_SLOTS(dtype)->imag_meth != NULL | ||
| || (PyArray_ISUSERDEF(self) && !NPY_DT_is_numeric(dtype))) { | ||
| if (out == NULL) { | ||
@@ -845,3 +853,3 @@ return PyArray_GenericUnaryFunction(self, | ||
| else { | ||
| if (!NPY_DT_is_numeric(NPY_DTYPE(PyArray_DTYPE(self)))) { | ||
| if (!NPY_DT_is_numeric(dtype)) { | ||
| PyErr_SetString(PyExc_TypeError, | ||
@@ -848,0 +856,0 @@ "cannot conjugate non-numeric dtype"); |
@@ -62,2 +62,26 @@ #ifndef NUMPY_CORE_SRC_MULTIARRAY_COMMON_H_ | ||
| /* | ||
| * Raise a TypeError for a dtype whose legacy copyswap slot is missing | ||
| * (e.g. a dtype written using the new DType API). This never succeeds: | ||
| * it always returns -1, so that it can be chained onto the NULL test at | ||
| * the call site. `inplace_swap` marks a request that only swaps bytes | ||
| * in place (no data copy); it is unused for now, but is passed so that | ||
| * such requests can become a no-op (returning 0) when byte order does | ||
| * not apply to the dtype (see gh-32150). | ||
| * | ||
| * WARNING: every call site is written as | ||
| * | ||
| * if (copyswap == NULL && raise_missing_copyswap(...) < 0) { | ||
| * <error return>; | ||
| * } | ||
| * copyswap(...); | ||
| * | ||
| * which calls through the NULL slot if this function returns 0. This is | ||
| * safe only while it unconditionally returns -1: before changing it to | ||
| * return 0, every call site must first be restructured to skip the | ||
| * copyswap call on success. | ||
| */ | ||
| NPY_NO_EXPORT int | ||
| raise_missing_copyswap(PyArray_Descr *dtype, int inplace_swap); | ||
| NPY_NO_EXPORT PyObject * | ||
@@ -64,0 +88,0 @@ convert_shape_to_string(npy_intp n, npy_intp const *vals, char *ending); |
@@ -716,2 +716,7 @@ /* Array Descr Object */ | ||
| copyswap(selfit->dataptr, arrit->dataptr, swap, self); | ||
| if (PyErr_Occurred()) { | ||
| /* e.g. a structured dtype field that does not support copyswap; | ||
| stop writing as soon as the error is visible */ | ||
| goto exit; | ||
| } | ||
| PyArray_ITER_NEXT(selfit); | ||
@@ -718,0 +723,0 @@ PyArray_ITER_NEXT(arrit); |
@@ -214,2 +214,58 @@ /* The implementation of the StringDType class */ | ||
| // Combine the na_object and coerce attributes of the string descriptors in | ||
| // descrs, ignoring descriptors of other dtypes. The na_objects must be | ||
| // compatible, and coercion is only enabled if it is enabled for all string | ||
| // descriptors. Returns -1 with an error set if two descriptors have | ||
| // incompatible na_objects. out_na_object (a borrowed reference) and | ||
| // out_coerce may be NULL if only the compatibility check is needed. | ||
| NPY_NO_EXPORT int | ||
| stringdtype_common_na_coerce(int ndescrs, PyArray_Descr *const descrs[], | ||
| PyObject **out_na_object, int *out_coerce) | ||
| { | ||
| PyObject *na_object = NULL; | ||
| int coerce = 1; | ||
| for (int i = 0; i < ndescrs; i++) { | ||
| if (NPY_DTYPE(descrs[i]) != &PyArray_StringDType) { | ||
| continue; | ||
| } | ||
| PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)descrs[i]; | ||
| if (stringdtype_compatible_na(na_object, descr->na_object, &na_object) == -1) { | ||
| return -1; | ||
| } | ||
| coerce = coerce && descr->coerce; | ||
| } | ||
| if (out_na_object != NULL) { | ||
| *out_na_object = na_object; | ||
| } | ||
| if (out_coerce != NULL) { | ||
| *out_coerce = coerce; | ||
| } | ||
| return 0; | ||
| } | ||
| // Select the descriptor that determines how nulls are handled in an | ||
| // operation accepting several string operands whose descriptors may differ, | ||
| // ignoring descriptors of other dtypes. The caller has already checked that | ||
| // the na_objects are compatible, so the first string descriptor with a set | ||
| // na_object determines the null-handling behavior for the whole operation. | ||
| // Returns the first string descriptor if none has an na_object set. | ||
| NPY_NO_EXPORT PyArray_StringDTypeObject * | ||
| stringdtype_effective_na_descr(int ndescrs, PyArray_Descr *const descrs[]) | ||
| { | ||
| PyArray_StringDTypeObject *first = NULL; | ||
| for (int i = 0; i < ndescrs; i++) { | ||
| if (NPY_DTYPE(descrs[i]) != &PyArray_StringDType) { | ||
| continue; | ||
| } | ||
| PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)descrs[i]; | ||
| if (descr->na_object != NULL) { | ||
| return descr; | ||
| } | ||
| if (first == NULL) { | ||
| first = descr; | ||
| } | ||
| } | ||
| return first; | ||
| } | ||
| /* | ||
@@ -223,6 +279,7 @@ * This is used to determine the correct dtype to return when dealing | ||
| { | ||
| PyArray_Descr *descrs[2] = {(PyArray_Descr *)dtype1, (PyArray_Descr *)dtype2}; | ||
| PyObject *out_na_object = NULL; | ||
| int out_coerce = 1; | ||
| if (stringdtype_compatible_na( | ||
| dtype1->na_object, dtype2->na_object, &out_na_object) == -1) { | ||
| if (stringdtype_common_na_coerce(2, descrs, &out_na_object, &out_coerce) == -1) { | ||
| PyErr_Format(PyExc_TypeError, | ||
@@ -235,3 +292,3 @@ "Cannot find common instance for incompatible dtypes " | ||
| return (PyArray_StringDTypeObject *)new_stringdtype_instance( | ||
| out_na_object, dtype1->coerce && dtype2->coerce); | ||
| out_na_object, out_coerce); | ||
| } | ||
@@ -467,13 +524,8 @@ | ||
| npy_string_allocator *allocator_b = descr_b->allocator; | ||
| // descr_a and descr_b are either the same object or objects | ||
| // that are equal, so we can safely refer only to descr_a. | ||
| // This is enforced in the resolve_descriptors for comparisons | ||
| // | ||
| // Note that even though the default_string isn't checked in comparisons, | ||
| // it will still be the same for both descrs because the value of | ||
| // default_string is always the empty string unless na_object is a string. | ||
| int has_null = descr_a->na_object != NULL; | ||
| int has_string_na = descr_a->has_string_na; | ||
| int has_nan_na = descr_a->has_nan_na; | ||
| npy_static_string *default_string = &descr_a->default_string; | ||
| PyArray_Descr *descrs[2] = {(PyArray_Descr *)descr_a, (PyArray_Descr *)descr_b}; | ||
| PyArray_StringDTypeObject *nadescr = stringdtype_effective_na_descr(2, descrs); | ||
| int has_null = nadescr->na_object != NULL; | ||
| int has_string_na = nadescr->has_string_na; | ||
| int has_nan_na = nadescr->has_nan_na; | ||
| npy_static_string *default_string = &nadescr->default_string; | ||
| const npy_packed_static_string *ps_a = (npy_packed_static_string *)a; | ||
@@ -480,0 +532,0 @@ npy_static_string s_a = {0, NULL}; |
@@ -60,2 +60,9 @@ #ifndef _NPY_CORE_SRC_MULTIARRAY_STRINGDTYPE_DTYPE_H_ | ||
| NPY_NO_EXPORT int | ||
| stringdtype_common_na_coerce(int ndescrs, PyArray_Descr *const descrs[], | ||
| PyObject **out_na_object, int *out_coerce); | ||
| NPY_NO_EXPORT PyArray_StringDTypeObject * | ||
| stringdtype_effective_na_descr(int ndescrs, PyArray_Descr *const descrs[]); | ||
| NPY_NO_EXPORT int | ||
| na_eq_cmp(PyObject *a, PyObject *b); | ||
@@ -62,0 +69,0 @@ |
@@ -48,8 +48,11 @@ name: NumPy | ||
| - name: Install Intel SDE | ||
| run: | | ||
| curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz | ||
| mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ | ||
| sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde | ||
| - name: Setup Intel SDE | ||
| uses: petarpetrovt/setup-sde@31aa4a8e85e109bef00f1d838613fcc6ec421271 # v5.0 | ||
| with: | ||
| environmentVariableName: SDE_PATH | ||
| sdeVersion: 9.58.0 | ||
| - name: Add Intel SDE to PATH | ||
| run: echo "$SDE_PATH" >> "$GITHUB_PATH" | ||
| - name: Install NumPy dependencies | ||
@@ -102,8 +105,11 @@ working-directory: ${{ github.workspace }}/numpy | ||
| - name: Install Intel SDE | ||
| run: | | ||
| curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz | ||
| mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ | ||
| sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde | ||
| - name: Setup Intel SDE | ||
| uses: petarpetrovt/setup-sde@31aa4a8e85e109bef00f1d838613fcc6ec421271 # v5.0 | ||
| with: | ||
| environmentVariableName: SDE_PATH | ||
| sdeVersion: 9.58.0 | ||
| - name: Add Intel SDE to PATH | ||
| run: echo "$SDE_PATH" >> "$GITHUB_PATH" | ||
| - name: Checkout NumPy main | ||
@@ -110,0 +116,0 @@ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 |
@@ -18,3 +18,3 @@ #!/bin/bash | ||
| ## Install Intel SDE | ||
| curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz | ||
| wget -O /tmp/sde.tar.xz https://downloadmirror.intel.com/915934/sde-external-10.8.0-2026-03-15-lin.tar.xz | ||
| mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ | ||
@@ -21,0 +21,0 @@ mv /tmp/sde/* /opt/sde && ln -s /opt/sde/sde /usr/bin/sde |
@@ -12,2 +12,50 @@ name: Build and run tests | ||
| jobs: | ||
| build-windows-msvc: | ||
| runs-on: windows-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| # Set up MSVC environment | ||
| - name: Set up MSVC Developer Command Prompt | ||
| uses: ilammy/msvc-dev-cmd@v1 | ||
| with: | ||
| arch: x64 | ||
| # Install Python (Meson requires it) | ||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.x' | ||
| # Install Meson and Ninja | ||
| - name: Install Meson + Ninja | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install meson ninja | ||
| - name: Setup environment | ||
| run: | | ||
| echo "PKG_CONFIG=${{ github.workspace }}/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe" >> $env:GITHUB_ENV | ||
| echo "PKG_CONFIG_PATH=${{ github.workspace }}/vcpkg/installed/x64-windows/lib/pkgconfig" >> $env:GITHUB_ENV | ||
| echo "LIB=${{ github.workspace }}/vcpkg/installed/x64-windows/lib;$env:LIB" >> $env:GITHUB_ENV | ||
| echo "INCLUDE=${{ github.workspace }}/vcpkg/installed/x64-windows/include;$env:INCLUDE" >> $env:GITHUB_ENV | ||
| shell: pwsh | ||
| - name: Setup vcpkg and install pkg-config and gtest | ||
| run: | | ||
| git clone https://github.com/Microsoft/vcpkg.git | ||
| .\vcpkg\bootstrap-vcpkg.bat | ||
| .\vcpkg\vcpkg install gtest:x64-windows pkgconf:x64-windows | ||
| # Configure and build with Meson (MSVC will be used automatically) | ||
| - name: Configure (Meson) | ||
| run: meson setup -Dbuild_tests=true --warnlevel 2 --buildtype release builddir --backend=ninja | ||
| - name: Build (Ninja) | ||
| run: ninja -C builddir | ||
| - name: Run tests | ||
| run: meson test -C builddir --test-args "\-\-gtest_filter=*qsort*" -v | ||
| SKL-gcc9: | ||
@@ -20,21 +68,11 @@ | ||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt update | ||
| sudo apt -y install g++-9 libgtest-dev meson curl git | ||
| - name: Setup build environment | ||
| uses: ./.github/actions/setup-build-env | ||
| with: | ||
| compiler: g++-9 | ||
| - name: Install Intel SDE | ||
| run: | | ||
| curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz | ||
| mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ | ||
| sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde | ||
| - name: Build | ||
| env: | ||
| CXX: g++-9 | ||
| run: | | ||
| make clean | ||
| meson setup -Dbuild_tests=true --warnlevel 2 --werror --buildtype release builddir | ||
| cd builddir | ||
| ninja | ||
| uses: ./.github/actions/meson-build | ||
| with: | ||
| compiler: g++-9 | ||
@@ -51,21 +89,11 @@ - name: Run test suite on SKL | ||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt update | ||
| sudo apt -y install g++-10 libgtest-dev meson curl git | ||
| - name: Setup build environment | ||
| uses: ./.github/actions/setup-build-env | ||
| with: | ||
| compiler: g++-10 | ||
| - name: Install Intel SDE | ||
| run: | | ||
| curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz | ||
| mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ | ||
| sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde | ||
| - name: Build | ||
| env: | ||
| CXX: g++-10 | ||
| run: | | ||
| make clean | ||
| meson setup -Dbuild_tests=true --warnlevel 2 --werror --buildtype release builddir | ||
| cd builddir | ||
| ninja | ||
| uses: ./.github/actions/meson-build | ||
| with: | ||
| compiler: g++-10 | ||
@@ -82,21 +110,11 @@ - name: Run test suite on SKX | ||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt update | ||
| sudo apt -y install g++-11 libgtest-dev meson curl git | ||
| - name: Setup build environment | ||
| uses: ./.github/actions/setup-build-env | ||
| with: | ||
| compiler: g++-11 | ||
| - name: Install Intel SDE | ||
| run: | | ||
| curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz | ||
| mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ | ||
| sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde | ||
| - name: Build | ||
| env: | ||
| CXX: g++-11 | ||
| run: | | ||
| make clean | ||
| meson setup -Dbuild_tests=true --warnlevel 2 --werror --buildtype release builddir | ||
| cd builddir | ||
| ninja | ||
| uses: ./.github/actions/meson-build | ||
| with: | ||
| compiler: g++-11 | ||
| - name: Run test suite on TGL | ||
@@ -112,28 +130,12 @@ run: sde -tgl -- ./builddir/testexe | ||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt update | ||
| sudo apt -y install g++-13 libgtest-dev meson curl git | ||
| - name: Setup build environment | ||
| uses: ./.github/actions/setup-build-env | ||
| with: | ||
| compiler: g++-13 | ||
| - name: Install Intel SDE | ||
| run: | | ||
| curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz | ||
| mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ | ||
| sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde | ||
| - name: Build examples | ||
| env: | ||
| CXX: g++-13 | ||
| run: | | ||
| cd examples | ||
| make all | ||
| - name: Build | ||
| env: | ||
| CXX: g++-13 | ||
| run: | | ||
| make clean | ||
| meson setup -Dbuild_tests=true --warnlevel 2 --werror --buildtype release builddir | ||
| cd builddir | ||
| ninja | ||
| uses: ./.github/actions/meson-build | ||
| with: | ||
| compiler: g++-13 | ||
| build-examples: 'true' | ||
@@ -143,3 +145,3 @@ - name: Run test suite on SPR | ||
| ADL-ASAN-clang18: | ||
| ASAN-clang18: | ||
@@ -151,72 +153,19 @@ runs-on: ubuntu-24.04 | ||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt update | ||
| sudo apt -y install clang-18 libomp-18-dev libgtest-dev meson curl git | ||
| - name: Setup build environment | ||
| uses: ./.github/actions/setup-build-env | ||
| with: | ||
| compiler: clang-18 | ||
| extra-packages: libomp-18-dev | ||
| - name: Install Intel SDE | ||
| run: | | ||
| curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz | ||
| mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ | ||
| sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde | ||
| - name: Build examples | ||
| env: | ||
| CXX: clang++-18 | ||
| run: | | ||
| cd examples | ||
| make all | ||
| - name: Build | ||
| env: | ||
| CXX: clang++-18 | ||
| run: | | ||
| make clean | ||
| meson setup -Dbuild_tests=true -Duse_openmp=true -Db_sanitize=address,undefined -Dfatal_sanitizers=true -Dasan_ci_dont_validate=true -Db_lundef=false --warnlevel 0 --buildtype release builddir | ||
| cd builddir | ||
| ninja | ||
| uses: ./.github/actions/meson-build | ||
| with: | ||
| compiler: clang++-18 | ||
| meson-options: '-Dbuild_tests=true -Duse_openmp=true -Db_sanitize=address,undefined -Dfatal_sanitizers=true -Dasan_ci_dont_validate=true -Db_lundef=false --warnlevel 0 --buildtype release' | ||
| build-examples: 'true' | ||
| - name: Run test suite on ADL | ||
| run: sde -adl -- ./builddir/testexe --gtest_filter="*simdsort*" | ||
| - name: Run test suite | ||
| run: ./builddir/testexe --gtest_filter="*simdsort*" | ||
| SPR-ASAN-clang18: | ||
| runs-on: intel-ubuntu-24.04 | ||
| steps: | ||
| - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 | ||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt update | ||
| sudo apt -y install clang-18 libomp-18-dev libgtest-dev meson curl git | ||
| - name: Install Intel SDE | ||
| run: | | ||
| curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz | ||
| mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ | ||
| sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde | ||
| - name: Build examples | ||
| env: | ||
| CXX: clang++-18 | ||
| run: | | ||
| cd examples | ||
| make all | ||
| - name: Build | ||
| env: | ||
| CXX: clang++-18 | ||
| run: | | ||
| make clean | ||
| meson setup -Dbuild_tests=true -Duse_openmp=true -Db_sanitize=address,undefined -Dfatal_sanitizers=true -Dasan_ci_dont_validate=true -Db_lundef=false --warnlevel 0 --buildtype release builddir | ||
| cd builddir | ||
| ninja | ||
| - name: Run test suite on SPR | ||
| run: sde -spr -- ./builddir/testexe | ||
| - name: Run ICL fp16 tests | ||
| # Note: This filters for the _Float16 tests based on the number assigned to it, which could change in the future | ||
| run: sde -icx -- ./builddir/testexe --gtest_filter="*/simdsort/2*" | ||
| SKX-SKL-openmp: | ||
@@ -229,21 +178,12 @@ | ||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt update | ||
| sudo apt -y install g++-10 libgtest-dev meson curl git | ||
| - name: Setup build environment | ||
| uses: ./.github/actions/setup-build-env | ||
| with: | ||
| compiler: g++-10 | ||
| - name: Install Intel SDE | ||
| run: | | ||
| curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz | ||
| mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ | ||
| sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde | ||
| - name: Build | ||
| env: | ||
| CXX: g++-10 | ||
| run: | | ||
| make clean | ||
| meson setup -Dbuild_tests=true -Duse_openmp=true --warnlevel 2 --werror --buildtype release builddir | ||
| cd builddir | ||
| ninja | ||
| uses: ./.github/actions/meson-build | ||
| with: | ||
| compiler: g++-10 | ||
| meson-options: '-Dbuild_tests=true -Duse_openmp=true --warnlevel 2 --werror --buildtype release' | ||
@@ -262,22 +202,12 @@ - name: Run test suite on SKX and SKL | ||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt update | ||
| sudo apt -y install g++-13 libgtest-dev meson curl git | ||
| - name: Setup build environment | ||
| uses: ./.github/actions/setup-build-env | ||
| with: | ||
| compiler: g++-13 | ||
| - name: Install Intel SDE | ||
| run: | | ||
| curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz | ||
| mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ | ||
| sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde | ||
| - name: Build | ||
| env: | ||
| CXX: g++-13 | ||
| CXXFLAGS: "-DXSS_MINIMAL_NETWORK_SORT -DXSS_TEST_KEYVALUE_BASE_CASE" | ||
| run: | | ||
| make clean | ||
| meson setup -Dbuild_tests=true --warnlevel 2 --werror --buildtype release builddir | ||
| cd builddir | ||
| ninja | ||
| uses: ./.github/actions/meson-build | ||
| with: | ||
| compiler: g++-13 | ||
| cxxflags: '-DXSS_MINIMAL_NETWORK_SORT -DXSS_TEST_KEYVALUE_BASE_CASE' | ||
@@ -310,3 +240,3 @@ - name: List exported symbols | ||
| - name: Install dependencies | ||
| - name: Install Intel oneAPI repository and dependencies | ||
| run: | | ||
@@ -316,32 +246,20 @@ echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list | ||
| sudo apt update --allow-insecure-repositories | ||
| sudo apt --allow-unauthenticated -y install intel-oneapi-compiler-dpcpp-cpp libgtest-dev curl git python3-pip meson | ||
| sudo apt --allow-unauthenticated -y install intel-oneapi-compiler-dpcpp-cpp libgtest-dev wget git python3-pip meson | ||
| - name: Install Intel SDE | ||
| run: | | ||
| #INTEL_SDE_URL=$(curl -s https://www.intel.com/content/www/us/en/download/684897/813591/intel-software-development-emulator.html | grep -Po 'https://downloadmirror.intel.com/.*lin.tar.xz(?=")') | ||
| #curl -o /tmp/sde.tar.xz $INTEL_SDE_URL | ||
| curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz | ||
| mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ | ||
| sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde | ||
| - name: Setup Intel SDE | ||
| uses: petarpetrovt/setup-sde@31aa4a8e85e109bef00f1d838613fcc6ec421271 # v5.0 | ||
| with: | ||
| environmentVariableName: SDE_PATH | ||
| sdeVersion: 9.58.0 | ||
| - name: Build examples | ||
| env: | ||
| CXX: icpx | ||
| CXXFLAGS: -fp-model=precise | ||
| run: | | ||
| source /opt/intel/oneapi/setvars.sh | ||
| cd examples | ||
| make all | ||
| - name: Add Intel SDE to PATH | ||
| run: echo "$SDE_PATH" >> "$GITHUB_PATH" | ||
| - name: Build | ||
| env: | ||
| CXX: icpx | ||
| CXXFLAGS: -fp-model=precise | ||
| run: | | ||
| make clean | ||
| source /opt/intel/oneapi/setvars.sh | ||
| icpx --version | ||
| meson setup -Dbuild_tests=true --warnlevel 2 --werror --buildtype release builddir | ||
| cd builddir | ||
| ninja | ||
| uses: ./.github/actions/meson-build | ||
| with: | ||
| compiler: icpx | ||
| cxxflags: '-fp-model=precise' | ||
| build-examples: 'true' | ||
| pre-build-commands: 'source /opt/intel/oneapi/setvars.sh && icpx --version' | ||
@@ -348,0 +266,0 @@ - name: Run test suite on SPR |
@@ -8,29 +8,53 @@ // AVX2 specific routines: | ||
| template <> \ | ||
| void qsort(type *arr, size_t arrsize, bool hasnan, bool descending) \ | ||
| void qsort(type *arr, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| x86simdsortStatic::qsort(arr, arrsize, hasnan, descending); \ | ||
| x86simdsortStatic::qsort( \ | ||
| arr, arrsize, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <> \ | ||
| void qselect( \ | ||
| type *arr, size_t k, size_t arrsize, bool hasnan, bool descending) \ | ||
| void qselect(type *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| x86simdsortStatic::qselect(arr, k, arrsize, hasnan, descending); \ | ||
| x86simdsortStatic::qselect( \ | ||
| arr, k, arrsize, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <> \ | ||
| void partial_qsort( \ | ||
| type *arr, size_t k, size_t arrsize, bool hasnan, bool descending) \ | ||
| void partial_qsort(type *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| x86simdsortStatic::partial_qsort(arr, k, arrsize, hasnan, descending); \ | ||
| x86simdsortStatic::partial_qsort( \ | ||
| arr, k, arrsize, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <> \ | ||
| std::vector<size_t> argsort( \ | ||
| type *arr, size_t arrsize, bool hasnan, bool descending) \ | ||
| std::vector<size_t> argsort(const type *arr, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| return x86simdsortStatic::argsort(arr, arrsize, hasnan, descending); \ | ||
| return x86simdsortStatic::argsort( \ | ||
| arr, arrsize, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <> \ | ||
| std::vector<size_t> argselect( \ | ||
| type *arr, size_t k, size_t arrsize, bool hasnan) \ | ||
| std::vector<size_t> argselect(const type *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| return x86simdsortStatic::argselect(arr, k, arrsize, hasnan); \ | ||
| return x86simdsortStatic::argselect( \ | ||
| arr, k, arrsize, hasnan, descending, nans_last); \ | ||
| } | ||
@@ -37,0 +61,0 @@ |
| // ICL specific routines: | ||
| #include "x86simdsort-static-incl.h" | ||
| #include "x86simdsort-internal.h" | ||
| #ifdef _MSC_VER | ||
| #include "avx512-16bit-qsort.hpp" | ||
| #endif | ||
@@ -8,5 +11,9 @@ namespace xss { | ||
| template <> | ||
| void qsort(uint16_t *arr, size_t size, bool hasnan, bool descending) | ||
| void qsort(uint16_t *arr, | ||
| size_t size, | ||
| bool hasnan, | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| x86simdsortStatic::qsort(arr, size, hasnan, descending); | ||
| x86simdsortStatic::qsort(arr, size, hasnan, descending, nans_last); | ||
| } | ||
@@ -18,5 +25,7 @@ template <> | ||
| bool hasnan, | ||
| bool descending) | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| x86simdsortStatic::qselect(arr, k, arrsize, hasnan, descending); | ||
| x86simdsortStatic::qselect( | ||
| arr, k, arrsize, hasnan, descending, nans_last); | ||
| } | ||
@@ -28,10 +37,16 @@ template <> | ||
| bool hasnan, | ||
| bool descending) | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| x86simdsortStatic::partial_qsort(arr, k, arrsize, hasnan, descending); | ||
| x86simdsortStatic::partial_qsort( | ||
| arr, k, arrsize, hasnan, descending, nans_last); | ||
| } | ||
| template <> | ||
| void qsort(int16_t *arr, size_t size, bool hasnan, bool descending) | ||
| void qsort(int16_t *arr, | ||
| size_t size, | ||
| bool hasnan, | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| x86simdsortStatic::qsort(arr, size, hasnan, descending); | ||
| x86simdsortStatic::qsort(arr, size, hasnan, descending, nans_last); | ||
| } | ||
@@ -43,5 +58,7 @@ template <> | ||
| bool hasnan, | ||
| bool descending) | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| x86simdsortStatic::qselect(arr, k, arrsize, hasnan, descending); | ||
| x86simdsortStatic::qselect( | ||
| arr, k, arrsize, hasnan, descending, nans_last); | ||
| } | ||
@@ -53,5 +70,7 @@ template <> | ||
| bool hasnan, | ||
| bool descending) | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| x86simdsortStatic::partial_qsort(arr, k, arrsize, hasnan, descending); | ||
| x86simdsortStatic::partial_qsort( | ||
| arr, k, arrsize, hasnan, descending, nans_last); | ||
| } | ||
@@ -62,5 +81,9 @@ } // namespace avx512 | ||
| template <> | ||
| void qsort(_Float16 *arr, size_t size, bool hasnan, bool descending) | ||
| void qsort(_Float16 *arr, | ||
| size_t size, | ||
| bool hasnan, | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| x86simdsortStatic::qsort(arr, size, hasnan, descending); | ||
| x86simdsortStatic::qsort(arr, size, hasnan, descending, nans_last); | ||
| } | ||
@@ -72,5 +95,7 @@ template <> | ||
| bool hasnan, | ||
| bool descending) | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| x86simdsortStatic::qselect(arr, k, arrsize, hasnan, descending); | ||
| x86simdsortStatic::qselect( | ||
| arr, k, arrsize, hasnan, descending, nans_last); | ||
| } | ||
@@ -82,5 +107,7 @@ template <> | ||
| bool hasnan, | ||
| bool descending) | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| x86simdsortStatic::partial_qsort(arr, k, arrsize, hasnan, descending); | ||
| x86simdsortStatic::partial_qsort( | ||
| arr, k, arrsize, hasnan, descending, nans_last); | ||
| } | ||
@@ -87,0 +114,0 @@ #endif |
@@ -13,3 +13,4 @@ #ifndef XSS_INTERNAL_METHODS | ||
| bool hasnan = false, \ | ||
| bool descending = false); \ | ||
| bool descending = false, \ | ||
| bool nans_last = true); \ | ||
| template <typename T1, typename T2> \ | ||
@@ -26,3 +27,4 @@ XSS_HIDE_SYMBOL void keyvalue_qsort(T1 *key, \ | ||
| bool hasnan = false, \ | ||
| bool descending = false); \ | ||
| bool descending = false, \ | ||
| bool nans_last = true); \ | ||
| template <typename T1, typename T2> \ | ||
@@ -40,3 +42,4 @@ XSS_HIDE_SYMBOL void keyvalue_select(T1 *key, \ | ||
| bool hasnan = false, \ | ||
| bool descending = false); \ | ||
| bool descending = false, \ | ||
| bool nans_last = true); \ | ||
| template <typename T1, typename T2> \ | ||
@@ -50,9 +53,14 @@ XSS_HIDE_SYMBOL void keyvalue_partial_sort(T1 *key, \ | ||
| template <typename T> \ | ||
| XSS_HIDE_SYMBOL std::vector<size_t> argsort(T *arr, \ | ||
| XSS_HIDE_SYMBOL std::vector<size_t> argsort(const T *arr, \ | ||
| size_t arrsize, \ | ||
| bool hasnan = false, \ | ||
| bool descending = false); \ | ||
| bool descending = false, \ | ||
| bool nans_last = true); \ | ||
| template <typename T> \ | ||
| XSS_HIDE_SYMBOL std::vector<size_t> \ | ||
| argselect(T *arr, size_t k, size_t arrsize, bool hasnan = false); \ | ||
| XSS_HIDE_SYMBOL std::vector<size_t> argselect(const T *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan = false, \ | ||
| bool descending = false, \ | ||
| bool nans_last = true); \ | ||
| } | ||
@@ -59,0 +67,0 @@ |
@@ -27,9 +27,20 @@ #include "custom-compare.h" | ||
| template <typename T> | ||
| decltype(auto) get_cmp_func(bool hasnan, bool reverse) | ||
| decltype(auto) | ||
| get_cmp_func(bool hasnan, bool reverse, bool nans_last = true) | ||
| { | ||
| std::function<bool(T, T)> cmp; | ||
| if (hasnan) { | ||
| if (reverse == true) { cmp = compare<T, std::greater<T>>(); } | ||
| if (nans_last) { | ||
| if (reverse == true) { | ||
| cmp = compare_nan_end<T, std::greater<T>>(); | ||
| } | ||
| else { | ||
| cmp = compare<T, std::less<T>>(); | ||
| } | ||
| } | ||
| else { | ||
| cmp = compare<T, std::less<T>>(); | ||
| if (reverse == true) { cmp = compare<T, std::greater<T>>(); } | ||
| else { | ||
| cmp = compare_nan_begin<T, std::less<T>>(); | ||
| } | ||
| } | ||
@@ -49,29 +60,47 @@ } | ||
| template <typename T> | ||
| void qsort(T *arr, size_t arrsize, bool hasnan, bool reversed) | ||
| void qsort(T *arr, | ||
| size_t arrsize, | ||
| bool hasnan, | ||
| bool reversed, | ||
| bool nans_last) | ||
| { | ||
| std::sort(arr, | ||
| arr + arrsize, | ||
| xss::utils::get_cmp_func<T>(hasnan, reversed)); | ||
| xss::utils::get_cmp_func<T>(hasnan, reversed, nans_last)); | ||
| } | ||
| template <typename T> | ||
| void qselect(T *arr, size_t k, size_t arrsize, bool hasnan, bool reversed) | ||
| void qselect(T *arr, | ||
| size_t k, | ||
| size_t arrsize, | ||
| bool hasnan, | ||
| bool reversed, | ||
| bool nans_last) | ||
| { | ||
| std::nth_element(arr, | ||
| arr + k, | ||
| arr + arrsize, | ||
| xss::utils::get_cmp_func<T>(hasnan, reversed)); | ||
| std::nth_element( | ||
| arr, | ||
| arr + k, | ||
| arr + arrsize, | ||
| xss::utils::get_cmp_func<T>(hasnan, reversed, nans_last)); | ||
| } | ||
| template <typename T> | ||
| void | ||
| partial_qsort(T *arr, size_t k, size_t arrsize, bool hasnan, bool reversed) | ||
| void partial_qsort(T *arr, | ||
| size_t k, | ||
| size_t arrsize, | ||
| bool hasnan, | ||
| bool reversed, | ||
| bool nans_last) | ||
| { | ||
| std::partial_sort(arr, | ||
| arr + k, | ||
| arr + arrsize, | ||
| xss::utils::get_cmp_func<T>(hasnan, reversed)); | ||
| std::partial_sort( | ||
| arr, | ||
| arr + k, | ||
| arr + arrsize, | ||
| xss::utils::get_cmp_func<T>(hasnan, reversed, nans_last)); | ||
| } | ||
| template <typename T> | ||
| std::vector<size_t> | ||
| argsort(T *arr, size_t arrsize, bool hasnan, bool reversed) | ||
| std::vector<size_t> argsort(const T *arr, | ||
| size_t arrsize, | ||
| bool hasnan, | ||
| bool reversed, | ||
| bool nans_last) | ||
| { | ||
@@ -81,10 +110,25 @@ UNUSED(hasnan); | ||
| std::iota(arg.begin(), arg.end(), 0); | ||
| if (reversed) { | ||
| std::sort(arg.begin(), | ||
| arg.end(), | ||
| compare_arg<T, std::greater<T>>(arr)); | ||
| if (nans_last) { | ||
| if (reversed) { | ||
| std::sort(arg.begin(), | ||
| arg.end(), | ||
| compare_arg_nan_end<T, std::greater<T>>(arr)); | ||
| } | ||
| else { | ||
| std::sort(arg.begin(), | ||
| arg.end(), | ||
| compare_arg<T, std::less<T>>(arr)); | ||
| } | ||
| } | ||
| else { | ||
| std::sort( | ||
| arg.begin(), arg.end(), compare_arg<T, std::less<T>>(arr)); | ||
| if (reversed) { | ||
| std::sort(arg.begin(), | ||
| arg.end(), | ||
| compare_arg<T, std::greater<T>>(arr)); | ||
| } | ||
| else { | ||
| std::sort(arg.begin(), | ||
| arg.end(), | ||
| compare_arg_nan_begin<T, std::less<T>>(arr)); | ||
| } | ||
| } | ||
@@ -94,3 +138,8 @@ return arg; | ||
| template <typename T> | ||
| std::vector<size_t> argselect(T *arr, size_t k, size_t arrsize, bool hasnan) | ||
| std::vector<size_t> argselect(const T *arr, | ||
| size_t k, | ||
| size_t arrsize, | ||
| bool hasnan, | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
@@ -100,6 +149,18 @@ UNUSED(hasnan); | ||
| std::iota(arg.begin(), arg.end(), 0); | ||
| std::nth_element(arg.begin(), | ||
| arg.begin() + k, | ||
| arg.end(), | ||
| compare_arg<T, std::less<T>>(arr)); | ||
| std::function<bool(size_t, size_t)> cmp; | ||
| if (nans_last) { | ||
| if (descending) { | ||
| cmp = compare_arg_nan_end<T, std::greater<T>>(arr); | ||
| } | ||
| else { | ||
| cmp = compare_arg<T, std::less<T>>(arr); | ||
| } | ||
| } | ||
| else { | ||
| if (descending) { cmp = compare_arg<T, std::greater<T>>(arr); } | ||
| else { | ||
| cmp = compare_arg_nan_begin<T, std::less<T>>(arr); | ||
| } | ||
| } | ||
| std::nth_element(arg.begin(), arg.begin() + k, arg.end(), cmp); | ||
| return arg; | ||
@@ -111,3 +172,4 @@ } | ||
| { | ||
| std::vector<size_t> arg = argsort(key, arrsize, hasnan, descending); | ||
| std::vector<size_t> arg | ||
| = argsort(key, arrsize, hasnan, descending, true); | ||
| utils::apply_permutation_in_place(key, arg); | ||
@@ -114,0 +176,0 @@ utils::apply_permutation_in_place(val, arg); |
@@ -8,29 +8,53 @@ // SKX specific routines: | ||
| template <> \ | ||
| void qsort(type *arr, size_t arrsize, bool hasnan, bool descending) \ | ||
| void qsort(type *arr, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| x86simdsortStatic::qsort(arr, arrsize, hasnan, descending); \ | ||
| x86simdsortStatic::qsort( \ | ||
| arr, arrsize, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <> \ | ||
| void qselect( \ | ||
| type *arr, size_t k, size_t arrsize, bool hasnan, bool descending) \ | ||
| void qselect(type *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| x86simdsortStatic::qselect(arr, k, arrsize, hasnan, descending); \ | ||
| x86simdsortStatic::qselect( \ | ||
| arr, k, arrsize, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <> \ | ||
| void partial_qsort( \ | ||
| type *arr, size_t k, size_t arrsize, bool hasnan, bool descending) \ | ||
| void partial_qsort(type *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| x86simdsortStatic::partial_qsort(arr, k, arrsize, hasnan, descending); \ | ||
| x86simdsortStatic::partial_qsort( \ | ||
| arr, k, arrsize, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <> \ | ||
| std::vector<size_t> argsort( \ | ||
| type *arr, size_t arrsize, bool hasnan, bool descending) \ | ||
| std::vector<size_t> argsort(const type *arr, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| return x86simdsortStatic::argsort(arr, arrsize, hasnan, descending); \ | ||
| return x86simdsortStatic::argsort( \ | ||
| arr, arrsize, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <> \ | ||
| std::vector<size_t> argselect( \ | ||
| type *arr, size_t k, size_t arrsize, bool hasnan) \ | ||
| std::vector<size_t> argselect(const type *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| return x86simdsortStatic::argselect(arr, k, arrsize, hasnan); \ | ||
| return x86simdsortStatic::argselect( \ | ||
| arr, k, arrsize, hasnan, descending, nans_last); \ | ||
| } | ||
@@ -37,0 +61,0 @@ |
@@ -8,5 +8,9 @@ // SPR specific routines: | ||
| template <> | ||
| void qsort(_Float16 *arr, size_t size, bool hasnan, bool descending) | ||
| void qsort(_Float16 *arr, | ||
| size_t size, | ||
| bool hasnan, | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| x86simdsortStatic::qsort(arr, size, hasnan, descending); | ||
| x86simdsortStatic::qsort(arr, size, hasnan, descending, nans_last); | ||
| } | ||
@@ -18,5 +22,7 @@ template <> | ||
| bool hasnan, | ||
| bool descending) | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| x86simdsortStatic::qselect(arr, k, arrsize, hasnan, descending); | ||
| x86simdsortStatic::qselect( | ||
| arr, k, arrsize, hasnan, descending, nans_last); | ||
| } | ||
@@ -28,7 +34,9 @@ template <> | ||
| bool hasnan, | ||
| bool descending) | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| x86simdsortStatic::partial_qsort(arr, k, arrsize, hasnan, descending); | ||
| x86simdsortStatic::partial_qsort( | ||
| arr, k, arrsize, hasnan, descending, nans_last); | ||
| } | ||
| } // namespace fp16_spr | ||
| } // namespace xss |
@@ -0,4 +1,10 @@ | ||
| #if defined(_MSC_VER) | ||
| #define XSS_ATTRIBUTE_CONSTRUCTOR | ||
| #else | ||
| #define XSS_ATTRIBUTE_CONSTRUCTOR __attribute__((constructor)) | ||
| #endif | ||
| #include "x86simdsort.h" | ||
| #include "x86simdsort-internal.h" | ||
| #include "x86simdsort-scalar.h" | ||
| #include "x86simdsortcpuid.h" | ||
| #include <algorithm> | ||
@@ -15,5 +21,4 @@ #include <iostream> | ||
| && (!defined(__clang_major__) || __clang_major__ >= 18) | ||
| return __builtin_cpu_supports("avx512f") | ||
| && __builtin_cpu_supports("avx512fp16") | ||
| && __builtin_cpu_supports("avx512vbmi2"); | ||
| return xss_cpu_supports("avx512f") && xss_cpu_supports("avx512fp16") | ||
| && xss_cpu_supports("avx512vbmi2"); | ||
| #else | ||
@@ -23,12 +28,9 @@ return 0; | ||
| else if ((cpufeature == "avx512_icl") && (!disable_avx512)) | ||
| return __builtin_cpu_supports("avx512f") | ||
| && __builtin_cpu_supports("avx512vbmi2") | ||
| && __builtin_cpu_supports("avx512bw") | ||
| && __builtin_cpu_supports("avx512vl"); | ||
| return xss_cpu_supports("avx512f") && xss_cpu_supports("avx512vbmi2") | ||
| && xss_cpu_supports("avx512bw") && xss_cpu_supports("avx512vl"); | ||
| else if ((cpufeature == "avx512_skx") && (!disable_avx512)) | ||
| return __builtin_cpu_supports("avx512f") | ||
| && __builtin_cpu_supports("avx512dq") | ||
| && __builtin_cpu_supports("avx512vl"); | ||
| return xss_cpu_supports("avx512f") && xss_cpu_supports("avx512dq") | ||
| && xss_cpu_supports("avx512vl"); | ||
| else if (cpufeature == "avx2") | ||
| return __builtin_cpu_supports("avx2"); | ||
| return xss_cpu_supports("avx2"); | ||
@@ -62,53 +64,214 @@ return 0; | ||
| /* runtime dispatch mechanism */ | ||
| #define DISPATCH(func, TYPE, ISA) \ | ||
| DECLARE_INTERNAL_##func(TYPE) static XSS_ATTRIBUTE_CONSTRUCTOR void CAT( \ | ||
| CAT(resolve_, func), TYPE)(void) \ | ||
| { \ | ||
| CAT(CAT(internal_, func), TYPE) = &xss::scalar::func<TYPE>; \ | ||
| xss_cpu_init(); \ | ||
| std::string_view preferred_cpu = find_preferred_cpu(ISA); \ | ||
| if constexpr (dispatch_requested("avx512", ISA)) { \ | ||
| if (preferred_cpu.find("avx512") != std::string_view::npos) { \ | ||
| if constexpr (IS_TYPE_FLOAT16<TYPE>()) { \ | ||
| if (preferred_cpu.find("avx512_spr") \ | ||
| != std::string_view::npos) { \ | ||
| CAT(CAT(internal_, func), TYPE) \ | ||
| = &xss::fp16_spr::func<TYPE>; \ | ||
| return; \ | ||
| } \ | ||
| if (preferred_cpu.find("avx512_icl") \ | ||
| != std::string_view::npos) { \ | ||
| CAT(CAT(internal_, func), TYPE) \ | ||
| = &xss::fp16_icl::func<TYPE>; \ | ||
| return; \ | ||
| } \ | ||
| } \ | ||
| else { \ | ||
| CAT(CAT(internal_, func), TYPE) \ | ||
| = &xss::avx512::func<TYPE>; \ | ||
| } \ | ||
| return; \ | ||
| } \ | ||
| } \ | ||
| if constexpr (dispatch_requested("avx2", ISA)) { \ | ||
| if (preferred_cpu.find("avx2") != std::string_view::npos) { \ | ||
| CAT(CAT(internal_, func), TYPE) = &xss::avx2::func<TYPE>; \ | ||
| return; \ | ||
| } \ | ||
| } \ | ||
| } | ||
| #ifdef _MSC_VER | ||
| #define DECLARE_INTERNAL_qsort(TYPE) \ | ||
| static void (*internal_qsort##TYPE)(TYPE *, size_t, bool, bool) = NULL; \ | ||
| static void CAT(resolve_qsort, TYPE)(void); \ | ||
| static void (*internal_qsort##TYPE)(TYPE *, size_t, bool, bool, bool) \ | ||
| = NULL; \ | ||
| template <> \ | ||
| void qsort(TYPE *arr, size_t arrsize, bool hasnan, bool descending) \ | ||
| void XSS_EXPORT_SYMBOL qsort(TYPE *arr, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| (*internal_qsort##TYPE)(arr, arrsize, hasnan, descending); \ | ||
| if (internal_qsort##TYPE == NULL) { CAT(resolve_qsort, TYPE)(); } \ | ||
| (*internal_qsort##TYPE)( \ | ||
| arr, arrsize, hasnan, descending, nans_last); \ | ||
| } | ||
| #define DECLARE_INTERNAL_qselect(TYPE) \ | ||
| static void (*internal_qselect##TYPE)(TYPE *, size_t, size_t, bool, bool) \ | ||
| static void CAT(resolve_qselect, TYPE)(void); \ | ||
| static void (*internal_qselect##TYPE)( \ | ||
| TYPE *, size_t, size_t, bool, bool, bool) \ | ||
| = NULL; \ | ||
| template <> \ | ||
| void qselect( \ | ||
| TYPE *arr, size_t k, size_t arrsize, bool hasnan, bool descending) \ | ||
| void XSS_EXPORT_SYMBOL qselect(TYPE *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| (*internal_qselect##TYPE)(arr, k, arrsize, hasnan, descending); \ | ||
| if (internal_qselect##TYPE == NULL) { CAT(resolve_qselect, TYPE)(); } \ | ||
| (*internal_qselect##TYPE)( \ | ||
| arr, k, arrsize, hasnan, descending, nans_last); \ | ||
| } | ||
| #define DECLARE_INTERNAL_partial_qsort(TYPE) \ | ||
| static void CAT(resolve_partial_qsort, TYPE)(void); \ | ||
| static void (*internal_partial_qsort##TYPE)( \ | ||
| TYPE *, size_t, size_t, bool, bool) \ | ||
| TYPE *, size_t, size_t, bool, bool, bool) \ | ||
| = NULL; \ | ||
| template <> \ | ||
| void partial_qsort( \ | ||
| TYPE *arr, size_t k, size_t arrsize, bool hasnan, bool descending) \ | ||
| void XSS_EXPORT_SYMBOL partial_qsort(TYPE *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| (*internal_partial_qsort##TYPE)(arr, k, arrsize, hasnan, descending); \ | ||
| if (internal_partial_qsort##TYPE == NULL) { \ | ||
| CAT(resolve_partial_qsort, TYPE)(); \ | ||
| } \ | ||
| (*internal_partial_qsort##TYPE)( \ | ||
| arr, k, arrsize, hasnan, descending, nans_last); \ | ||
| } | ||
| #define DECLARE_INTERNAL_argsort(TYPE) \ | ||
| static void CAT(resolve_argsort, TYPE)(void); \ | ||
| static std::vector<size_t> (*internal_argsort##TYPE)( \ | ||
| TYPE *, size_t, bool, bool) \ | ||
| const TYPE *, size_t, bool, bool, bool) \ | ||
| = NULL; \ | ||
| template <> \ | ||
| std::vector<size_t> argsort( \ | ||
| TYPE *arr, size_t arrsize, bool hasnan, bool descending) \ | ||
| std::vector<size_t> XSS_EXPORT_SYMBOL argsort(const TYPE *arr, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| return (*internal_argsort##TYPE)(arr, arrsize, hasnan, descending); \ | ||
| if (internal_argsort##TYPE == NULL) { CAT(resolve_argsort, TYPE)(); } \ | ||
| return (*internal_argsort##TYPE)( \ | ||
| arr, arrsize, hasnan, descending, nans_last); \ | ||
| } | ||
| #define DECLARE_INTERNAL_argselect(TYPE) \ | ||
| static void CAT(resolve_argselect, TYPE)(void); \ | ||
| static std::vector<size_t> (*internal_argselect##TYPE)( \ | ||
| TYPE *, size_t, size_t, bool) \ | ||
| const TYPE *, size_t, size_t, bool, bool, bool) \ | ||
| = NULL; \ | ||
| template <> \ | ||
| std::vector<size_t> argselect( \ | ||
| TYPE *arr, size_t k, size_t arrsize, bool hasnan) \ | ||
| std::vector<size_t> XSS_EXPORT_SYMBOL argselect(const TYPE *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| return (*internal_argselect##TYPE)(arr, k, arrsize, hasnan); \ | ||
| if (internal_argselect##TYPE == NULL) { \ | ||
| CAT(resolve_argselect, TYPE)(); \ | ||
| } \ | ||
| return (*internal_argselect##TYPE)( \ | ||
| arr, k, arrsize, hasnan, descending, nans_last); \ | ||
| } | ||
| #else | ||
| #define DECLARE_INTERNAL_qsort(TYPE) \ | ||
| static void (*internal_qsort##TYPE)(TYPE *, size_t, bool, bool, bool) \ | ||
| = NULL; \ | ||
| template <> \ | ||
| void XSS_EXPORT_SYMBOL qsort(TYPE *arr, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| (*internal_qsort##TYPE)( \ | ||
| arr, arrsize, hasnan, descending, nans_last); \ | ||
| } | ||
| #define DECLARE_INTERNAL_qselect(TYPE) \ | ||
| static void (*internal_qselect##TYPE)( \ | ||
| TYPE *, size_t, size_t, bool, bool, bool) \ | ||
| = NULL; \ | ||
| template <> \ | ||
| void XSS_EXPORT_SYMBOL qselect(TYPE *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| (*internal_qselect##TYPE)( \ | ||
| arr, k, arrsize, hasnan, descending, nans_last); \ | ||
| } | ||
| #define DECLARE_INTERNAL_partial_qsort(TYPE) \ | ||
| static void (*internal_partial_qsort##TYPE)( \ | ||
| TYPE *, size_t, size_t, bool, bool, bool) \ | ||
| = NULL; \ | ||
| template <> \ | ||
| void XSS_EXPORT_SYMBOL partial_qsort(TYPE *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| (*internal_partial_qsort##TYPE)( \ | ||
| arr, k, arrsize, hasnan, descending, nans_last); \ | ||
| } | ||
| #define DECLARE_INTERNAL_argsort(TYPE) \ | ||
| static std::vector<size_t> (*internal_argsort##TYPE)( \ | ||
| const TYPE *, size_t, bool, bool, bool) \ | ||
| = NULL; \ | ||
| template <> \ | ||
| std::vector<size_t> XSS_EXPORT_SYMBOL argsort(const TYPE *arr, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| return (*internal_argsort##TYPE)( \ | ||
| arr, arrsize, hasnan, descending, nans_last); \ | ||
| } | ||
| #define DECLARE_INTERNAL_argselect(TYPE) \ | ||
| static std::vector<size_t> (*internal_argselect##TYPE)( \ | ||
| const TYPE *, size_t, size_t, bool, bool, bool) \ | ||
| = NULL; \ | ||
| template <> \ | ||
| std::vector<size_t> XSS_EXPORT_SYMBOL argselect(const TYPE *arr, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| return (*internal_argselect##TYPE)( \ | ||
| arr, k, arrsize, hasnan, descending, nans_last); \ | ||
| } | ||
| #endif // _MSC_VER | ||
| /* simple constexpr function as a way around having #ifdef __FLT16_MAX__ block | ||
@@ -125,41 +288,2 @@ * within the DISPATCH macro */ | ||
| /* runtime dispatch mechanism */ | ||
| #define DISPATCH(func, TYPE, ISA) \ | ||
| DECLARE_INTERNAL_##func(TYPE) static __attribute__((constructor)) void \ | ||
| CAT(CAT(resolve_, func), TYPE)(void) \ | ||
| { \ | ||
| CAT(CAT(internal_, func), TYPE) = &xss::scalar::func<TYPE>; \ | ||
| __builtin_cpu_init(); \ | ||
| std::string_view preferred_cpu = find_preferred_cpu(ISA); \ | ||
| if constexpr (dispatch_requested("avx512", ISA)) { \ | ||
| if (preferred_cpu.find("avx512") != std::string_view::npos) { \ | ||
| if constexpr (IS_TYPE_FLOAT16<TYPE>()) { \ | ||
| if (preferred_cpu.find("avx512_spr") \ | ||
| != std::string_view::npos) { \ | ||
| CAT(CAT(internal_, func), TYPE) \ | ||
| = &xss::fp16_spr::func<TYPE>; \ | ||
| return; \ | ||
| } \ | ||
| if (preferred_cpu.find("avx512_icl") \ | ||
| != std::string_view::npos) { \ | ||
| CAT(CAT(internal_, func), TYPE) \ | ||
| = &xss::fp16_icl::func<TYPE>; \ | ||
| return; \ | ||
| } \ | ||
| } \ | ||
| else { \ | ||
| CAT(CAT(internal_, func), TYPE) \ | ||
| = &xss::avx512::func<TYPE>; \ | ||
| } \ | ||
| return; \ | ||
| } \ | ||
| } \ | ||
| if constexpr (dispatch_requested("avx2", ISA)) { \ | ||
| if (preferred_cpu.find("avx2") != std::string_view::npos) { \ | ||
| CAT(CAT(internal_, func), TYPE) = &xss::avx2::func<TYPE>; \ | ||
| return; \ | ||
| } \ | ||
| } \ | ||
| } | ||
| #define ISA_LIST(...) \ | ||
@@ -211,3 +335,32 @@ std::initializer_list<std::string_view> \ | ||
| /* Key-Value methods */ | ||
| #define DISPATCH_KV_FUNC(func, TYPE1, TYPE2, ISA) \ | ||
| static XSS_ATTRIBUTE_CONSTRUCTOR void CAT( \ | ||
| CAT(CAT(CAT(resolve_, func), _), TYPE1), TYPE2)(void) \ | ||
| { \ | ||
| CAT(CAT(CAT(CAT(internal_, func), _), TYPE1), TYPE2) \ | ||
| = &xss::scalar::func<TYPE1, TYPE2>; \ | ||
| xss_cpu_init(); \ | ||
| std::string_view preferred_cpu = find_preferred_cpu(ISA); \ | ||
| if constexpr (dispatch_requested("avx512", ISA)) { \ | ||
| if (preferred_cpu.find("avx512") != std::string_view::npos) { \ | ||
| CAT(CAT(CAT(CAT(internal_, func), _), TYPE1), TYPE2) \ | ||
| = &xss::avx512::func<TYPE1, TYPE2>; \ | ||
| return; \ | ||
| } \ | ||
| } \ | ||
| if constexpr (dispatch_requested("avx2", ISA)) { \ | ||
| if (preferred_cpu.find("avx2") != std::string_view::npos) { \ | ||
| CAT(CAT(CAT(CAT(internal_, func), _), TYPE1), TYPE2) \ | ||
| = &xss::avx2::func<TYPE1, TYPE2>; \ | ||
| return; \ | ||
| } \ | ||
| } \ | ||
| } | ||
| #ifdef _MSC_VER | ||
| #define DECLARE_ALL_KEYVALUE_METHODS(TYPE1, TYPE2) \ | ||
| static void CAT(CAT(resolve_keyvalue_select_, TYPE1), TYPE2)(void); \ | ||
| static void CAT(CAT(resolve_keyvalue_partial_sort_, TYPE1), TYPE2)(void); \ | ||
| static void CAT(CAT(resolve_keyvalue_qsort_, TYPE1), TYPE2)(void); \ | ||
| static void(CAT(CAT(*internal_keyvalue_qsort_, TYPE1), TYPE2))( \ | ||
@@ -223,8 +376,11 @@ TYPE1 *, TYPE2 *, size_t, bool, bool) \ | ||
| template <> \ | ||
| void keyvalue_qsort(TYPE1 *key, \ | ||
| TYPE2 *val, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending) \ | ||
| void XSS_EXPORT_SYMBOL keyvalue_qsort(TYPE1 *key, \ | ||
| TYPE2 *val, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending) \ | ||
| { \ | ||
| if ((CAT(CAT(*internal_keyvalue_qsort_, TYPE1), TYPE2)) == NULL) { \ | ||
| CAT(CAT(resolve_keyvalue_qsort_, TYPE1), TYPE2)(); \ | ||
| } \ | ||
| (CAT(CAT(*internal_keyvalue_qsort_, TYPE1), TYPE2))( \ | ||
@@ -234,9 +390,12 @@ key, val, arrsize, hasnan, descending); \ | ||
| template <> \ | ||
| void keyvalue_select(TYPE1 *key, \ | ||
| TYPE2 *val, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending) \ | ||
| void XSS_EXPORT_SYMBOL keyvalue_select(TYPE1 *key, \ | ||
| TYPE2 *val, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending) \ | ||
| { \ | ||
| if ((CAT(CAT(*internal_keyvalue_select_, TYPE1), TYPE2)) == NULL) { \ | ||
| CAT(CAT(resolve_keyvalue_select_, TYPE1), TYPE2)(); \ | ||
| } \ | ||
| (CAT(CAT(*internal_keyvalue_select_, TYPE1), TYPE2))( \ | ||
@@ -246,36 +405,60 @@ key, val, k, arrsize, hasnan, descending); \ | ||
| template <> \ | ||
| void keyvalue_partial_sort(TYPE1 *key, \ | ||
| TYPE2 *val, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending) \ | ||
| void XSS_EXPORT_SYMBOL keyvalue_partial_sort(TYPE1 *key, \ | ||
| TYPE2 *val, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending) \ | ||
| { \ | ||
| if ((CAT(CAT(*internal_keyvalue_partial_sort_, TYPE1), TYPE2)) \ | ||
| == NULL) { \ | ||
| CAT(CAT(resolve_keyvalue_partial_sort_, TYPE1), TYPE2)(); \ | ||
| } \ | ||
| (CAT(CAT(*internal_keyvalue_partial_sort_, TYPE1), TYPE2))( \ | ||
| key, val, k, arrsize, hasnan, descending); \ | ||
| } | ||
| #define DISPATCH_KV_FUNC(func, TYPE1, TYPE2, ISA) \ | ||
| static __attribute__((constructor)) void CAT( \ | ||
| CAT(CAT(CAT(resolve_, func), _), TYPE1), TYPE2)(void) \ | ||
| #else | ||
| #define DECLARE_ALL_KEYVALUE_METHODS(TYPE1, TYPE2) \ | ||
| static void(CAT(CAT(*internal_keyvalue_qsort_, TYPE1), TYPE2))( \ | ||
| TYPE1 *, TYPE2 *, size_t, bool, bool) \ | ||
| = NULL; \ | ||
| static void(CAT(CAT(*internal_keyvalue_select_, TYPE1), TYPE2))( \ | ||
| TYPE1 *, TYPE2 *, size_t, size_t, bool, bool) \ | ||
| = NULL; \ | ||
| static void(CAT(CAT(*internal_keyvalue_partial_sort_, TYPE1), TYPE2))( \ | ||
| TYPE1 *, TYPE2 *, size_t, size_t, bool, bool) \ | ||
| = NULL; \ | ||
| template <> \ | ||
| void XSS_EXPORT_SYMBOL keyvalue_qsort(TYPE1 *key, \ | ||
| TYPE2 *val, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending) \ | ||
| { \ | ||
| CAT(CAT(CAT(CAT(internal_, func), _), TYPE1), TYPE2) \ | ||
| = &xss::scalar::func<TYPE1, TYPE2>; \ | ||
| __builtin_cpu_init(); \ | ||
| std::string_view preferred_cpu = find_preferred_cpu(ISA); \ | ||
| if constexpr (dispatch_requested("avx512", ISA)) { \ | ||
| if (preferred_cpu.find("avx512") != std::string_view::npos) { \ | ||
| CAT(CAT(CAT(CAT(internal_, func), _), TYPE1), TYPE2) \ | ||
| = &xss::avx512::func<TYPE1, TYPE2>; \ | ||
| return; \ | ||
| } \ | ||
| } \ | ||
| if constexpr (dispatch_requested("avx2", ISA)) { \ | ||
| if (preferred_cpu.find("avx2") != std::string_view::npos) { \ | ||
| CAT(CAT(CAT(CAT(internal_, func), _), TYPE1), TYPE2) \ | ||
| = &xss::avx2::func<TYPE1, TYPE2>; \ | ||
| return; \ | ||
| } \ | ||
| } \ | ||
| (CAT(CAT(*internal_keyvalue_qsort_, TYPE1), TYPE2))( \ | ||
| key, val, arrsize, hasnan, descending); \ | ||
| } \ | ||
| template <> \ | ||
| void XSS_EXPORT_SYMBOL keyvalue_select(TYPE1 *key, \ | ||
| TYPE2 *val, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending) \ | ||
| { \ | ||
| (CAT(CAT(*internal_keyvalue_select_, TYPE1), TYPE2))( \ | ||
| key, val, k, arrsize, hasnan, descending); \ | ||
| } \ | ||
| template <> \ | ||
| void XSS_EXPORT_SYMBOL keyvalue_partial_sort(TYPE1 *key, \ | ||
| TYPE2 *val, \ | ||
| size_t k, \ | ||
| size_t arrsize, \ | ||
| bool hasnan, \ | ||
| bool descending) \ | ||
| { \ | ||
| (CAT(CAT(*internal_keyvalue_partial_sort_, TYPE1), TYPE2))( \ | ||
| key, val, k, arrsize, hasnan, descending); \ | ||
| } | ||
| #endif // _MSC_VER | ||
@@ -282,0 +465,0 @@ #define DISPATCH_KEYVALUE_SORT(TYPE1, TYPE2, ISA) \ |
@@ -9,4 +9,9 @@ #ifndef X86_SIMD_SORT | ||
| #if defined(_MSC_VER) | ||
| #define XSS_EXPORT_SYMBOL __declspec(dllexport) | ||
| #define XSS_HIDE_SYMBOL | ||
| #else | ||
| #define XSS_EXPORT_SYMBOL __attribute__((visibility("default"))) | ||
| #define XSS_HIDE_SYMBOL __attribute__((visibility("hidden"))) | ||
| #endif | ||
| #define UNUSED(x) (void)(x) | ||
@@ -18,4 +23,7 @@ | ||
| template <typename T> | ||
| XSS_EXPORT_SYMBOL void | ||
| qsort(T *arr, size_t arrsize, bool hasnan = false, bool descending = false); | ||
| XSS_EXPORT_SYMBOL void qsort(T *arr, | ||
| size_t arrsize, | ||
| bool hasnan = false, | ||
| bool descending = false, | ||
| bool nans_last = true); | ||
@@ -28,3 +36,4 @@ // quickselect | ||
| bool hasnan = false, | ||
| bool descending = false); | ||
| bool descending = false, | ||
| bool nans_last = true); | ||
@@ -37,13 +46,21 @@ // partial sort | ||
| bool hasnan = false, | ||
| bool descending = false); | ||
| bool descending = false, | ||
| bool nans_last = true); | ||
| // argsort | ||
| template <typename T> | ||
| XSS_EXPORT_SYMBOL std::vector<size_t> | ||
| argsort(T *arr, size_t arrsize, bool hasnan = false, bool descending = false); | ||
| XSS_EXPORT_SYMBOL std::vector<size_t> argsort(const T *arr, | ||
| size_t arrsize, | ||
| bool hasnan = false, | ||
| bool descending = false, | ||
| bool nans_last = true); | ||
| // argselect | ||
| template <typename T> | ||
| XSS_EXPORT_SYMBOL std::vector<size_t> | ||
| argselect(T *arr, size_t k, size_t arrsize, bool hasnan = false); | ||
| XSS_EXPORT_SYMBOL std::vector<size_t> argselect(const T *arr, | ||
| size_t k, | ||
| size_t arrsize, | ||
| bool hasnan = false, | ||
| bool descending = false, | ||
| bool nans_last = true); | ||
@@ -50,0 +67,0 @@ // keyvalue sort |
@@ -1,2 +0,2 @@ | ||
| # x86-simd-sort | ||
| # x86-simd-sort [](https://deepwiki.com/numpy/x86-simd-sort) | ||
@@ -40,5 +40,5 @@ C++ template library for high performance SIMD based sorting routines for | ||
| ```cpp | ||
| void x86simdsort::qsort(T* arr, size_t size, bool hasnan, bool descending); | ||
| void x86simdsort::qselect(T* arr, size_t k, size_t size, bool hasnan, bool descending); | ||
| void x86simdsort::partial_qsort(T* arr, size_t k, size_t size, bool hasnan, bool descending); | ||
| void x86simdsort::qsort(T* arr, size_t size, bool hasnan, bool descending, bool trailing_nans); | ||
| void x86simdsort::qselect(T* arr, size_t k, size_t size, bool hasnan, bool descending, bool trailing_nans); | ||
| void x86simdsort::partial_qsort(T* arr, size_t k, size_t size, bool hasnan, bool descending, bool trailing_nans); | ||
| ``` | ||
@@ -60,4 +60,4 @@ Supported datatypes: `T` $\in$ `[_Float16, uint16_t, int16_t, float, uint32_t, | ||
| ```cpp | ||
| std::vector<size_t> arg = x86simdsort::argsort(T* arr, size_t size, bool hasnan, bool descending); | ||
| std::vector<size_t> arg = x86simdsort::argselect(T* arr, size_t k, size_t size, bool hasnan); | ||
| std::vector<size_t> arg = x86simdsort::argsort(const T* arr, size_t size, bool hasnan, bool descending, bool trailing_nans); | ||
| std::vector<size_t> arg = x86simdsort::argselect(const T* arr, size_t k, size_t size, bool hasnan, bool descending, bool trailing_nans); | ||
| ``` | ||
@@ -87,2 +87,12 @@ Supported datatypes: `T` $\in$ `[_Float16, uint16_t, int16_t, float, uint32_t, int32_t, double, | ||
| ## Supported Compilers | ||
| This library requires a **C++17** compatible compiler. While most modern compilers work, specific versions are required to support advanced SIMD instruction sets like AVX-512 and AVX-512FP16: | ||
| * **GCC**: Version **8.x** or higher is required for general builds. | ||
| * *Note: To support `_Float16` (AVX-512FP16), **GCC 12** or higher is mandatory.* | ||
| * **Clang**: Version **8.0** or higher is generally recommended for robust AVX-512 support. | ||
| * **MSVC**: **Visual Studio 2019 (version 16.8)** or newer is required for C++17 and initial AVX-512 intrinsic support. | ||
| * **Intel C++ Compiler (icx)**: All modern versions of the LLVM-based Intel compiler are fully supported and often provide optimal performance for these routines. | ||
| ## Build using OpenMP | ||
@@ -170,9 +180,18 @@ | ||
| hasnan` set to `false` by default (these are relevant to floating point data | ||
| types only). If your array has NAN's, the the behaviour of the sorting routine | ||
| is undefined. If `hasnan` is set to true, NAN's are always sorted to the end of | ||
| the array. In addition to that, qsort will replace all your NAN's with | ||
| `std::numeric_limits<T>::quiet_NaN`. The original bit-exact NaNs in | ||
| the input are not preserved. Also note that the arg methods (argsort and | ||
| argselect) will not use the SIMD based algorithms if they detect NAN's in the | ||
| array. You can read details of all the implementations | ||
| types only). If your array has NaN values, the behaviour of the sorting routine | ||
| is undefined unless `hasnan` is set to `true`. When `hasnan=true`, NaN placement | ||
| is controlled by the optional `bool trailing_nans` parameter (default `true`): | ||
| - `trailing_nans=true` (default): NaN values are placed at the **end** of the | ||
| result, regardless of sort direction. | ||
| - `trailing_nans=false`: NaN values are placed at the **beginning** of the | ||
| result, regardless of sort direction. | ||
| All routines accept an optional `bool descending` parameter (default `false`). | ||
| When `descending=true`, results are in descending order. For `argselect`, the | ||
| k-th element becomes the k-th **largest**, with all elements before index k | ||
| being greater than or equal to it. | ||
| Note that the arg methods (argsort and argselect) will not use the SIMD based | ||
| algorithms if they detect NaN values in the array. You can read details of all the implementations | ||
| [here](https://github.com/intel/x86-simd-sort/blob/main/src/README.md). | ||
@@ -179,0 +198,0 @@ |
@@ -87,3 +87,3 @@ /******************************************************************* | ||
| } | ||
| static reg_t i64gather(type_t *arr, arrsize_t *ind) | ||
| static reg_t i64gather(const type_t *arr, arrsize_t *ind) | ||
| { | ||
@@ -241,3 +241,3 @@ return set(arr[ind[3]], arr[ind[2]], arr[ind[1]], arr[ind[0]]); | ||
| } | ||
| static reg_t i64gather(type_t *arr, arrsize_t *ind) | ||
| static reg_t i64gather(const type_t *arr, arrsize_t *ind) | ||
| { | ||
@@ -426,3 +426,3 @@ return set(arr[ind[3]], arr[ind[2]], arr[ind[1]], arr[ind[0]]); | ||
| } | ||
| static reg_t i64gather(type_t *arr, arrsize_t *ind) | ||
| static reg_t i64gather(const type_t *arr, arrsize_t *ind) | ||
| { | ||
@@ -429,0 +429,0 @@ return set(arr[ind[3]], arr[ind[2]], arr[ind[1]], arr[ind[0]]); |
@@ -102,3 +102,3 @@ /******************************************************************* | ||
| } | ||
| static reg_t i64gather(type_t *arr, arrsize_t *ind) | ||
| static reg_t i64gather(const type_t *arr, arrsize_t *ind) | ||
| { | ||
@@ -273,3 +273,3 @@ return set(arr[ind[3]], arr[ind[2]], arr[ind[1]], arr[ind[0]]); | ||
| } | ||
| static reg_t i64gather(type_t *arr, arrsize_t *ind) | ||
| static reg_t i64gather(const type_t *arr, arrsize_t *ind) | ||
| { | ||
@@ -504,3 +504,3 @@ return set(arr[ind[3]], arr[ind[2]], arr[ind[1]], arr[ind[0]]); | ||
| } | ||
| static reg_t i64gather(type_t *arr, arrsize_t *ind) | ||
| static reg_t i64gather(const type_t *arr, arrsize_t *ind) | ||
| { | ||
@@ -507,0 +507,0 @@ return set(arr[ind[3]], arr[ind[2]], arr[ind[1]], arr[ind[0]]); |
@@ -522,27 +522,8 @@ /******************************************************************* | ||
| template <> | ||
| X86_SIMD_SORT_INLINE_ONLY arrsize_t | ||
| replace_nan_with_inf<zmm_vector<float16>>(uint16_t *arr, arrsize_t arrsize) | ||
| { | ||
| arrsize_t nan_count = 0; | ||
| __mmask16 loadmask = 0xFFFF; | ||
| for (arrsize_t ii = 0; ii < arrsize; | ||
| ii = ii + zmm_vector<float16>::numlanes / 2) { | ||
| if (arrsize - ii < 16) { | ||
| loadmask = (0x0001 << (arrsize - ii)) - 0x0001; | ||
| } | ||
| __m256i in_zmm = _mm256_maskz_loadu_epi16(loadmask, arr); | ||
| __m512 in_zmm_asfloat = _mm512_cvtph_ps(in_zmm); | ||
| __mmask16 nanmask = _mm512_cmp_ps_mask( | ||
| in_zmm_asfloat, in_zmm_asfloat, _CMP_NEQ_UQ); | ||
| nan_count += _mm_popcnt_u32((int32_t)nanmask); | ||
| _mm256_mask_storeu_epi16(arr, nanmask, YMM_MAX_HALF); | ||
| arr += 16; | ||
| } | ||
| return nan_count; | ||
| } | ||
| template <typename comparator> | ||
| [[maybe_unused]] X86_SIMD_SORT_INLINE void | ||
| avx512_qsort_fp16_helper(uint16_t *arr, arrsize_t arrsize) | ||
| avx512_qsort_fp16_helper(uint16_t *arr, | ||
| arrsize_t arrsize, | ||
| arrsize_t index_first_elem, | ||
| arrsize_t index_last_elem) | ||
| { | ||
@@ -553,7 +534,9 @@ using T = uint16_t; | ||
| #ifdef XSS_COMPILE_OPENMP | ||
| bool use_parallel = arrsize > 100000; | ||
| bool use_parallel = (index_last_elem - index_first_elem + 1) > 100000; | ||
| if (use_parallel) { | ||
| int thread_count = xss_get_num_threads(); | ||
| arrsize_t task_threshold = std::max((arrsize_t)100000, arrsize / 100); | ||
| arrsize_t task_threshold | ||
| = std::max((arrsize_t)100000, | ||
| (index_last_elem - index_first_elem + 1) / 100); | ||
@@ -566,4 +549,4 @@ // We use omp parallel and then omp single to setup the threads that will run the omp task calls in qsort_ | ||
| qsort_<vtype, comparator, T>(arr, | ||
| 0, | ||
| arrsize - 1, | ||
| index_first_elem, | ||
| index_last_elem, | ||
| 2 * (arrsize_t)log2(arrsize), | ||
@@ -574,4 +557,4 @@ task_threshold); | ||
| qsort_<vtype, comparator, T>(arr, | ||
| 0, | ||
| arrsize - 1, | ||
| index_first_elem, | ||
| index_last_elem, | ||
| 2 * (arrsize_t)log2(arrsize), | ||
@@ -582,4 +565,7 @@ std::numeric_limits<arrsize_t>::max()); | ||
| #else | ||
| qsort_<vtype, comparator, T>( | ||
| arr, 0, arrsize - 1, 2 * (arrsize_t)log2(arrsize), 0); | ||
| qsort_<vtype, comparator, T>(arr, | ||
| index_first_elem, | ||
| index_last_elem, | ||
| 2 * (arrsize_t)log2(arrsize), | ||
| 0); | ||
| #endif | ||
@@ -592,3 +578,4 @@ } | ||
| bool hasnan = false, | ||
| bool descending = false) | ||
| bool descending = false, | ||
| bool nans_last = true) | ||
| { | ||
@@ -598,13 +585,22 @@ using vtype = zmm_vector<float16>; | ||
| if (arrsize > 1) { | ||
| arrsize_t nan_count = 0; | ||
| arrsize_t index_first_elem = 0; | ||
| arrsize_t index_last_elem = arrsize - 1; | ||
| if (UNLIKELY(hasnan)) { | ||
| nan_count = replace_nan_with_inf<vtype, uint16_t>(arr, arrsize); | ||
| if (!nans_last) { | ||
| index_first_elem = move_nans_to_start_of_array(arr, arrsize); | ||
| } | ||
| else { | ||
| index_last_elem = move_nans_to_end_of_array(arr, arrsize); | ||
| } | ||
| } | ||
| if (descending) { | ||
| avx512_qsort_fp16_helper<Comparator<vtype, true>>(arr, arrsize); | ||
| if (index_first_elem <= index_last_elem && index_last_elem < arrsize) { | ||
| if (descending) { | ||
| avx512_qsort_fp16_helper<Comparator<vtype, true>>( | ||
| arr, arrsize, index_first_elem, index_last_elem); | ||
| } | ||
| else { | ||
| avx512_qsort_fp16_helper<Comparator<vtype, false>>( | ||
| arr, arrsize, index_first_elem, index_last_elem); | ||
| } | ||
| } | ||
| else { | ||
| avx512_qsort_fp16_helper<Comparator<vtype, false>>(arr, arrsize); | ||
| } | ||
| replace_inf_with_nan(arr, arrsize, nan_count, descending); | ||
| } | ||
@@ -623,3 +619,4 @@ | ||
| bool hasnan = false, | ||
| bool descending = false) | ||
| bool descending = false, | ||
| bool nans_last = true) | ||
| { | ||
@@ -635,3 +632,3 @@ using vtype = zmm_vector<float16>; | ||
| if (UNLIKELY(hasnan)) { | ||
| if (descending) { | ||
| if (!nans_last) { | ||
| index_first_elem = move_nans_to_start_of_array(arr, arrsize); | ||
@@ -674,8 +671,9 @@ } | ||
| bool hasnan = false, | ||
| bool descending = false) | ||
| bool descending = false, | ||
| bool nans_last = true) | ||
| { | ||
| if (k == 0) return; | ||
| avx512_qselect_fp16(arr, k - 1, arrsize, hasnan, descending); | ||
| avx512_qsort_fp16(arr, k - 1, hasnan, descending); | ||
| avx512_qselect_fp16(arr, k - 1, arrsize, hasnan, descending, nans_last); | ||
| avx512_qsort_fp16(arr, k - 1, hasnan, descending, nans_last); | ||
| } | ||
| #endif // AVX512_QSORT_16BIT |
@@ -102,3 +102,3 @@ /******************************************************************* | ||
| } | ||
| static reg_t i64gather(type_t *arr, arrsize_t *ind) | ||
| static reg_t i64gather(const type_t *arr, arrsize_t *ind) | ||
| { | ||
@@ -297,3 +297,3 @@ return set(arr[ind[7]], | ||
| } | ||
| static reg_t i64gather(type_t *arr, arrsize_t *ind) | ||
| static reg_t i64gather(const type_t *arr, arrsize_t *ind) | ||
| { | ||
@@ -486,3 +486,3 @@ return set(arr[ind[7]], | ||
| } | ||
| static reg_t i64gather(type_t *arr, arrsize_t *ind) | ||
| static reg_t i64gather(const type_t *arr, arrsize_t *ind) | ||
| { | ||
@@ -686,3 +686,3 @@ return set(arr[ind[7]], | ||
| } | ||
| static reg_t i64gather(type_t *arr, arrsize_t *ind) | ||
| static reg_t i64gather(const type_t *arr, arrsize_t *ind) | ||
| { | ||
@@ -850,3 +850,3 @@ return set(arr[ind[7]], | ||
| } | ||
| static reg_t i64gather(type_t *arr, arrsize_t *ind) | ||
| static reg_t i64gather(const type_t *arr, arrsize_t *ind) | ||
| { | ||
@@ -1070,3 +1070,3 @@ return set(arr[ind[7]], | ||
| } | ||
| static reg_t i64gather(type_t *arr, arrsize_t *ind) | ||
| static reg_t i64gather(const type_t *arr, arrsize_t *ind) | ||
| { | ||
@@ -1073,0 +1073,0 @@ return set(arr[ind[7]], |
@@ -178,24 +178,2 @@ /******************************************************************* | ||
| template <> | ||
| X86_SIMD_SORT_INLINE_ONLY void replace_inf_with_nan(_Float16 *arr, | ||
| arrsize_t size, | ||
| arrsize_t nan_count, | ||
| bool descending) | ||
| { | ||
| Fp16Bits val; | ||
| val.i_ = 0x7c01; | ||
| if (descending) { | ||
| for (arrsize_t ii = 0; nan_count > 0; ++ii) { | ||
| arr[ii] = val.f_; | ||
| nan_count -= 1; | ||
| } | ||
| } | ||
| else { | ||
| for (arrsize_t ii = size - 1; nan_count > 0; --ii) { | ||
| arr[ii] = val.f_; | ||
| nan_count -= 1; | ||
| } | ||
| } | ||
| } | ||
| #endif // AVX512FP16_QSORT_16BIT |
@@ -21,3 +21,3 @@ # x86-simd-sort | ||
| ```cpp | ||
| void x86simdsortStatic::qsort<T>(T* arr, size_t arrsize, bool hasnan = false, bool descending = false); | ||
| void x86simdsortStatic::qsort<T>(T* arr, size_t arrsize, bool hasnan = false, bool descending = false, bool nans_last = true); | ||
| ``` | ||
@@ -27,4 +27,5 @@ Supported datatypes: `uint16_t`, `int16_t`, `_Float16`, `uint32_t`, `int32_t`, | ||
| 32-bit and 64-bit dtypes only. For floating-point types, if `arr` contains | ||
| NaNs, they are moved to the end and replaced with a quiet NaN. That is, the | ||
| original, bit-exact NaNs in the input are not preserved. | ||
| NaNs, their placement is controlled by `nans_last`: `true` (default) places | ||
| NaNs at the end; `false` places them at the beginning. Bit-exact NaN payloads | ||
| are preserved. | ||
@@ -39,3 +40,3 @@ #### Quickselect | ||
| ```cpp | ||
| void x86simdsortStatic::qselect<T>(T* arr, size_t k, size_t arrsize, bool hasnan = false, bool descending = false); | ||
| void x86simdsortStatic::qselect<T>(T* arr, size_t k, size_t arrsize, bool hasnan = false, bool descending = false, bool nans_last = true); | ||
| ``` | ||
@@ -45,4 +46,5 @@ Supported datatypes: `uint16_t`, `int16_t`, `_Float16`, `uint32_t`, `int32_t`, | ||
| 32-bit and 64-bit dtypes only. For floating-point types, if `bool hasnan` is | ||
| set, NaNs are moved to the end of the array, preserving the bit-exact NaNs in | ||
| the input. If NaNs are present but `hasnan` is `false`, the behavior is | ||
| set, NaN placement is controlled by `nans_last`: `true` (default) places | ||
| NaNs at the end; `false` places them at the beginning. Bit-exact NaN payloads | ||
| are preserved. If NaNs are present but `hasnan` is `false`, the behavior is | ||
| undefined. | ||
@@ -56,3 +58,3 @@ | ||
| ```cpp | ||
| void x86simdsortStatic::partial_qsort<T>(T* arr, size_t k, size_t arrsize, bool hasnan = false, bool descending = false) | ||
| void x86simdsortStatic::partial_qsort<T>(T* arr, size_t k, size_t arrsize, bool hasnan = false, bool descending = false, bool nans_last = true); | ||
| ``` | ||
@@ -62,4 +64,5 @@ Supported datatypes: `uint16_t`, `int16_t`, `_Float16`, `uint32_t`, `int32_t`, | ||
| 32-bit and 64-bit dtypes only. For floating-point types, if `bool hasnan` is | ||
| set, NaNs are moved to the end of the array, preserving the bit-exact NaNs in | ||
| the input. If NaNs are present but `hasnan` is `false`, the behavior is | ||
| set, NaN placement is controlled by `nans_last`: `true` (default) places | ||
| NaNs at the end; `false` places them at the beginning. Bit-exact NaN payloads | ||
| are preserved. If NaNs are present but `hasnan` is `false`, the behavior is | ||
| undefined. | ||
@@ -72,3 +75,3 @@ | ||
| ```cpp | ||
| void x86simdsortStatic::argsort<T>(T* arr, size_t *arg, size_t arrsize, bool hasnan = false, bool descending = false); | ||
| void x86simdsortStatic::argsort<T>(const T* arr, size_t *arg, size_t arrsize, bool hasnan = false, bool descending = false, bool nans_last = true); | ||
| ``` | ||
@@ -85,3 +88,3 @@ Supported datatypes: `uint32_t`, `int32_t`, `float`, `uint64_t`, `int64_t` and | ||
| ```cpp | ||
| void x86simdsortStatic::argselect<T>(T* arr, size_t *arg, size_t k, size_t arrsize, bool hasnan = false); | ||
| void x86simdsortStatic::argselect<T>(const T* arr, size_t *arg, size_t k, size_t arrsize, bool hasnan = false, bool descending = false, bool nans_last = true); | ||
| ``` | ||
@@ -91,4 +94,9 @@ Supported datatypes: `uint32_t`, `int32_t`, `float`, `uint64_t`, `int64_t` and | ||
| The algorithm resorts to scalar `std::sort` if the array contains NaNs. | ||
| When `descending=true`, the k-th element is the k-th **largest** and elements | ||
| before index k are all greater than or equal to it. For floating-point types, | ||
| if `bool hasnan` is set, NaN placement is controlled by `nans_last`: | ||
| `true` (default) places NaNs at the end; `false` places them at the beginning. | ||
| The algorithm resorts to scalar `std::nth_element` if the array contains NaNs. | ||
| #### Key-value sort | ||
@@ -95,0 +103,0 @@ ```cpp |
@@ -10,4 +10,7 @@ #ifndef X86_SIMD_SORT_STATIC_METHODS | ||
| template <typename T> | ||
| X86_SIMD_SORT_FINLINE void | ||
| qsort(T *arr, size_t size, bool hasnan = false, bool descending = false); | ||
| X86_SIMD_SORT_FINLINE void qsort(T *arr, | ||
| size_t size, | ||
| bool hasnan = false, | ||
| bool descending = false, | ||
| bool nans_last = true); | ||
@@ -19,3 +22,4 @@ template <typename T> | ||
| bool hasnan = false, | ||
| bool descending = false); | ||
| bool descending = false, | ||
| bool nans_last = true); | ||
@@ -27,24 +31,38 @@ template <typename T> | ||
| bool hasnan = false, | ||
| bool descending = false); | ||
| bool descending = false, | ||
| bool nans_last = true); | ||
| template <typename T> | ||
| X86_SIMD_SORT_FINLINE std::vector<size_t> | ||
| argsort(T *arr, size_t size, bool hasnan = false, bool descending = false); | ||
| X86_SIMD_SORT_FINLINE std::vector<size_t> argsort(const T *arr, | ||
| size_t size, | ||
| bool hasnan = false, | ||
| bool descending = false, | ||
| bool nans_last = true); | ||
| /* argsort API required by NumPy: */ | ||
| template <typename T> | ||
| X86_SIMD_SORT_FINLINE void argsort(T *arr, | ||
| X86_SIMD_SORT_FINLINE void argsort(const T *arr, | ||
| size_t *arg, | ||
| size_t size, | ||
| bool hasnan = false, | ||
| bool descending = false); | ||
| bool descending = false, | ||
| bool nans_last = true); | ||
| template <typename T> | ||
| X86_SIMD_SORT_FINLINE std::vector<size_t> | ||
| argselect(T *arr, size_t k, size_t size, bool hasnan = false); | ||
| X86_SIMD_SORT_FINLINE std::vector<size_t> argselect(const T *arr, | ||
| size_t k, | ||
| size_t size, | ||
| bool hasnan = false, | ||
| bool descending = false, | ||
| bool nans_last = true); | ||
| /* argselect API required by NumPy: */ | ||
| template <typename T> | ||
| void X86_SIMD_SORT_FINLINE | ||
| argselect(T *arr, size_t *arg, size_t k, size_t size, bool hasnan = false); | ||
| void X86_SIMD_SORT_FINLINE argselect(const T *arr, | ||
| size_t *arg, | ||
| size_t k, | ||
| size_t size, | ||
| bool hasnan = false, | ||
| bool descending = false, | ||
| bool nans_last = true); | ||
@@ -78,28 +96,48 @@ template <typename T1, typename T2> | ||
| template <typename T> \ | ||
| X86_SIMD_SORT_FINLINE void x86simdsortStatic::qsort( \ | ||
| T *arr, size_t size, bool hasnan, bool descending) \ | ||
| X86_SIMD_SORT_FINLINE void x86simdsortStatic::qsort(T *arr, \ | ||
| size_t size, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| ISA##_qsort(arr, size, hasnan, descending); \ | ||
| ISA##_qsort(arr, size, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <typename T> \ | ||
| X86_SIMD_SORT_FINLINE void x86simdsortStatic::qselect( \ | ||
| T *arr, size_t k, size_t size, bool hasnan, bool descending) \ | ||
| X86_SIMD_SORT_FINLINE void x86simdsortStatic::qselect(T *arr, \ | ||
| size_t k, \ | ||
| size_t size, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| ISA##_qselect(arr, k, size, hasnan, descending); \ | ||
| ISA##_qselect(arr, k, size, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <typename T> \ | ||
| X86_SIMD_SORT_FINLINE void x86simdsortStatic::partial_qsort( \ | ||
| T *arr, size_t k, size_t size, bool hasnan, bool descending) \ | ||
| T *arr, \ | ||
| size_t k, \ | ||
| size_t size, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| ISA##_partial_qsort(arr, k, size, hasnan, descending); \ | ||
| ISA##_partial_qsort(arr, k, size, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <typename T> \ | ||
| X86_SIMD_SORT_FINLINE void x86simdsortStatic::argsort( \ | ||
| T *arr, size_t *arg, size_t size, bool hasnan, bool descending) \ | ||
| X86_SIMD_SORT_FINLINE void x86simdsortStatic::argsort(const T *arr, \ | ||
| size_t *arg, \ | ||
| size_t size, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| ISA##_argsort(arr, arg, size, hasnan, descending); \ | ||
| ISA##_argsort(arr, arg, size, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <typename T> \ | ||
| X86_SIMD_SORT_FINLINE std::vector<size_t> x86simdsortStatic::argsort( \ | ||
| T *arr, size_t size, bool hasnan, bool descending) \ | ||
| const T *arr, \ | ||
| size_t size, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
@@ -109,3 +147,3 @@ std::vector<size_t> indices(size); \ | ||
| x86simdsortStatic::argsort( \ | ||
| arr, indices.data(), size, hasnan, descending); \ | ||
| arr, indices.data(), size, hasnan, descending, nans_last); \ | ||
| return indices; \ | ||
@@ -115,13 +153,30 @@ } \ | ||
| X86_SIMD_SORT_FINLINE void x86simdsortStatic::argselect( \ | ||
| T *arr, size_t *arg, size_t k, size_t size, bool hasnan) \ | ||
| const T *arr, \ | ||
| size_t *arg, \ | ||
| size_t k, \ | ||
| size_t size, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| ISA##_argselect(arr, arg, k, size, hasnan); \ | ||
| ISA##_argselect(arr, arg, k, size, hasnan, descending, nans_last); \ | ||
| } \ | ||
| template <typename T> \ | ||
| X86_SIMD_SORT_FINLINE std::vector<size_t> x86simdsortStatic::argselect( \ | ||
| T *arr, size_t k, size_t size, bool hasnan) \ | ||
| const T *arr, \ | ||
| size_t k, \ | ||
| size_t size, \ | ||
| bool hasnan, \ | ||
| bool descending, \ | ||
| bool nans_last) \ | ||
| { \ | ||
| std::vector<size_t> indices(size); \ | ||
| std::iota(indices.begin(), indices.end(), 0); \ | ||
| x86simdsortStatic::argselect(arr, indices.data(), k, size, hasnan); \ | ||
| x86simdsortStatic::argselect(arr, \ | ||
| indices.data(), \ | ||
| k, \ | ||
| size, \ | ||
| hasnan, \ | ||
| descending, \ | ||
| nans_last); \ | ||
| return indices; \ | ||
@@ -189,19 +244,30 @@ } \ | ||
| bool hasnan, | ||
| bool descending) | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| avx512_qsort_fp16((uint16_t *)arr, size, hasnan, descending); | ||
| avx512_qsort_fp16((uint16_t *)arr, size, hasnan, descending, nans_last); | ||
| } | ||
| template <> | ||
| [[maybe_unused]] | ||
| void x86simdsortStatic::qselect<_Float16>( | ||
| _Float16 *arr, size_t k, size_t size, bool hasnan, bool descending) | ||
| void x86simdsortStatic::qselect<_Float16>(_Float16 *arr, | ||
| size_t k, | ||
| size_t size, | ||
| bool hasnan, | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| avx512_qselect_fp16((uint16_t *)arr, k, size, hasnan, descending); | ||
| avx512_qselect_fp16( | ||
| (uint16_t *)arr, k, size, hasnan, descending, nans_last); | ||
| } | ||
| template <> | ||
| [[maybe_unused]] | ||
| void x86simdsortStatic::partial_qsort<_Float16>( | ||
| _Float16 *arr, size_t k, size_t size, bool hasnan, bool descending) | ||
| void x86simdsortStatic::partial_qsort<_Float16>(_Float16 *arr, | ||
| size_t k, | ||
| size_t size, | ||
| bool hasnan, | ||
| bool descending, | ||
| bool nans_last) | ||
| { | ||
| avx512_partial_qsort_fp16((uint16_t *)arr, k, size, hasnan, descending); | ||
| avx512_partial_qsort_fp16( | ||
| (uint16_t *)arr, k, size, hasnan, descending, nans_last); | ||
| } | ||
@@ -221,2 +287,2 @@ #endif | ||
| #endif // X86_SIMD_SORT_STATIC_METHODS | ||
| #endif // X86_SIMD_SORT_STATIC_METHODS |
@@ -14,39 +14,46 @@ /******************************************************************* | ||
| template <typename T> | ||
| X86_SIMD_SORT_INLINE void std_argselect_withnan( | ||
| T *arr, arrsize_t *arg, arrsize_t k, arrsize_t left, arrsize_t right) | ||
| X86_SIMD_SORT_INLINE void std_argselect_withnan(const T *arr, | ||
| arrsize_t *arg, | ||
| arrsize_t k, | ||
| arrsize_t left, | ||
| arrsize_t right, | ||
| bool nans_last = true, | ||
| bool descending = false) | ||
| { | ||
| std::nth_element(arg + left, | ||
| arg + k, | ||
| arg + right, | ||
| [arr](arrsize_t a, arrsize_t b) -> bool { | ||
| if ((!std::isnan(arr[a])) && (!std::isnan(arr[b]))) { | ||
| return arr[a] < arr[b]; | ||
| } | ||
| else if (std::isnan(arr[a])) { | ||
| return false; | ||
| } | ||
| else { | ||
| return true; | ||
| } | ||
| }); | ||
| std::nth_element( | ||
| arg + left, | ||
| arg + k, | ||
| arg + right, | ||
| [arr, nans_last, descending](arrsize_t a, arrsize_t b) -> bool { | ||
| bool a_nan = std::isnan(arr[a]); | ||
| bool b_nan = std::isnan(arr[b]); | ||
| if (!a_nan && !b_nan) { | ||
| return descending ? arr[a] > arr[b] : arr[a] < arr[b]; | ||
| } | ||
| if (a_nan && b_nan) { return false; } | ||
| return nans_last ? !a_nan : a_nan; | ||
| }); | ||
| } | ||
| /* argsort using std::sort */ | ||
| /* argsort using std::sort, handles NaN placement and descending order */ | ||
| template <typename T> | ||
| X86_SIMD_SORT_INLINE void | ||
| std_argsort_withnan(T *arr, arrsize_t *arg, arrsize_t left, arrsize_t right) | ||
| X86_SIMD_SORT_INLINE void std_argsort_withnan(const T *arr, | ||
| arrsize_t *arg, | ||
| arrsize_t left, | ||
| arrsize_t right, | ||
| bool nans_last = true, | ||
| bool descending = false) | ||
| { | ||
| std::sort(arg + left, | ||
| arg + right, | ||
| [arr](arrsize_t left, arrsize_t right) -> bool { | ||
| if ((!std::isnan(arr[left])) && (!std::isnan(arr[right]))) { | ||
| return arr[left] < arr[right]; | ||
| } | ||
| else if (std::isnan(arr[left])) { | ||
| return false; | ||
| } | ||
| else { | ||
| return true; | ||
| } | ||
| }); | ||
| std::sort( | ||
| arg + left, | ||
| arg + right, | ||
| [arr, nans_last, descending](arrsize_t a, arrsize_t b) -> bool { | ||
| bool a_nan = std::isnan(arr[a]); | ||
| bool b_nan = std::isnan(arr[b]); | ||
| if (!a_nan && !b_nan) { | ||
| return descending ? arr[a] > arr[b] : arr[a] < arr[b]; | ||
| } | ||
| if (a_nan && b_nan) { return false; } | ||
| return nans_last ? !a_nan : a_nan; | ||
| }); | ||
| } | ||
@@ -57,3 +64,3 @@ | ||
| X86_SIMD_SORT_INLINE void | ||
| std_argsort(T *arr, arrsize_t *arg, arrsize_t left, arrsize_t right) | ||
| std_argsort(const T *arr, arrsize_t *arg, arrsize_t left, arrsize_t right) | ||
| { | ||
@@ -177,3 +184,3 @@ std::sort(arg + left, | ||
| template <typename vtype, typename argtype, typename type_t> | ||
| X86_SIMD_SORT_INLINE arrsize_t argpartition(type_t *arr, | ||
| X86_SIMD_SORT_INLINE arrsize_t argpartition(const type_t *arr, | ||
| arrsize_t *arg, | ||
@@ -297,3 +304,3 @@ arrsize_t left, | ||
| typename type_t = typename vtype::type_t> | ||
| X86_SIMD_SORT_INLINE arrsize_t argpartition_unrolled(type_t *arr, | ||
| X86_SIMD_SORT_INLINE arrsize_t argpartition_unrolled(const type_t *arr, | ||
| arrsize_t *arg, | ||
@@ -429,3 +436,3 @@ arrsize_t left, | ||
| template <typename vtype, typename type_t> | ||
| X86_SIMD_SORT_INLINE type_t get_pivot_64bit(type_t *arr, | ||
| X86_SIMD_SORT_INLINE type_t get_pivot_64bit(const type_t *arr, | ||
| arrsize_t *arg, | ||
@@ -476,3 +483,3 @@ const arrsize_t left, | ||
| template <typename vtype, typename argtype, typename type_t> | ||
| X86_SIMD_SORT_INLINE void argsort_(type_t *arr, | ||
| X86_SIMD_SORT_INLINE void argsort_(const type_t *arr, | ||
| arrsize_t *arg, | ||
@@ -558,3 +565,3 @@ arrsize_t left, | ||
| template <typename vtype, typename argtype, typename type_t> | ||
| X86_SIMD_SORT_INLINE void argselect_(type_t *arr, | ||
| X86_SIMD_SORT_INLINE void argselect_(const type_t *arr, | ||
| arrsize_t *arg, | ||
@@ -600,7 +607,8 @@ arrsize_t pos, | ||
| typename half_vector> | ||
| X86_SIMD_SORT_INLINE void xss_argsort(T *arr, | ||
| X86_SIMD_SORT_INLINE void xss_argsort(const T *arr, | ||
| arrsize_t *arg, | ||
| arrsize_t arrsize, | ||
| bool hasnan = false, | ||
| bool descending = false) | ||
| bool descending = false, | ||
| bool nans_last = true) | ||
| { | ||
@@ -621,6 +629,4 @@ | ||
| if ((hasnan) && (array_has_nan<vectype>(arr, arrsize))) { | ||
| std_argsort_withnan(arr, arg, 0, arrsize); | ||
| if (descending) { std::reverse(arg, arg + arrsize); } | ||
| std_argsort_withnan( | ||
| arr, arg, 0, arrsize, nans_last, descending); | ||
| return; | ||
@@ -630,2 +636,3 @@ } | ||
| UNUSED(hasnan); | ||
| UNUSED(nans_last); | ||
@@ -682,21 +689,23 @@ /* early exit for already sorted arrays: float/double with nan never reach here*/ | ||
| template <typename T> | ||
| X86_SIMD_SORT_INLINE void avx512_argsort(T *arr, | ||
| X86_SIMD_SORT_INLINE void avx512_argsort(const T *arr, | ||
| arrsize_t *arg, | ||
| arrsize_t arrsize, | ||
| bool hasnan = false, | ||
| bool descending = false) | ||
| bool descending = false, | ||
| bool nans_last = true) | ||
| { | ||
| xss_argsort<T, zmm_vector, ymm_vector>( | ||
| arr, arg, arrsize, hasnan, descending); | ||
| arr, arg, arrsize, hasnan, descending, nans_last); | ||
| } | ||
| template <typename T> | ||
| X86_SIMD_SORT_INLINE void avx2_argsort(T *arr, | ||
| X86_SIMD_SORT_INLINE void avx2_argsort(const T *arr, | ||
| arrsize_t *arg, | ||
| arrsize_t arrsize, | ||
| bool hasnan = false, | ||
| bool descending = false) | ||
| bool descending = false, | ||
| bool nans_last = true) | ||
| { | ||
| xss_argsort<T, avx2_vector, avx2_half_vector>( | ||
| arr, arg, arrsize, hasnan, descending); | ||
| arr, arg, arrsize, hasnan, descending, nans_last); | ||
| } | ||
@@ -710,7 +719,9 @@ | ||
| typename half_vector> | ||
| X86_SIMD_SORT_INLINE void xss_argselect(T *arr, | ||
| X86_SIMD_SORT_INLINE void xss_argselect(const T *arr, | ||
| arrsize_t *arg, | ||
| arrsize_t k, | ||
| arrsize_t arrsize, | ||
| bool hasnan = false) | ||
| bool hasnan = false, | ||
| bool descending = false, | ||
| bool nans_last = true) | ||
| { | ||
@@ -730,3 +741,4 @@ /* TODO optimization: on 32-bit, use full_vector for 32-bit dtype */ | ||
| if ((hasnan) && (array_has_nan<vectype>(arr, arrsize))) { | ||
| std_argselect_withnan(arr, arg, k, 0, arrsize); | ||
| std_argselect_withnan( | ||
| arr, arg, k, 0, arrsize, nans_last, descending); | ||
| return; | ||
@@ -736,4 +748,10 @@ } | ||
| UNUSED(hasnan); | ||
| UNUSED(nans_last); | ||
| /* For descending, partition at the mirror position so the k-th | ||
| * largest lands at arrsize-1-k; reversal then moves it to k. */ | ||
| arrsize_t pos = descending ? arrsize - 1 - k : k; | ||
| argselect_<vectype, argtype>( | ||
| arr, arg, k, 0, arrsize - 1, 2 * (arrsize_t)log2(arrsize)); | ||
| arr, arg, pos, 0, arrsize - 1, 2 * (arrsize_t)log2(arrsize)); | ||
| if (descending) { std::reverse(arg, arg + arrsize); } | ||
| } | ||
@@ -748,22 +766,27 @@ | ||
| template <typename T> | ||
| X86_SIMD_SORT_INLINE void avx512_argselect(T *arr, | ||
| X86_SIMD_SORT_INLINE void avx512_argselect(const T *arr, | ||
| arrsize_t *arg, | ||
| arrsize_t k, | ||
| arrsize_t arrsize, | ||
| bool hasnan = false) | ||
| bool hasnan = false, | ||
| bool descending = false, | ||
| bool nans_last = true) | ||
| { | ||
| xss_argselect<T, zmm_vector, ymm_vector>(arr, arg, k, arrsize, hasnan); | ||
| xss_argselect<T, zmm_vector, ymm_vector>( | ||
| arr, arg, k, arrsize, hasnan, descending, nans_last); | ||
| } | ||
| template <typename T> | ||
| X86_SIMD_SORT_INLINE void avx2_argselect(T *arr, | ||
| X86_SIMD_SORT_INLINE void avx2_argselect(const T *arr, | ||
| arrsize_t *arg, | ||
| arrsize_t k, | ||
| arrsize_t arrsize, | ||
| bool hasnan = false) | ||
| bool hasnan = false, | ||
| bool descending = false, | ||
| bool nans_last = true) | ||
| { | ||
| xss_argselect<T, avx2_vector, avx2_half_vector>( | ||
| arr, arg, k, arrsize, hasnan); | ||
| arr, arg, k, arrsize, hasnan, descending, nans_last); | ||
| } | ||
| #endif // XSS_COMMON_ARGSORT |
@@ -644,4 +644,5 @@ /******************************************************************* | ||
| if (descending) { | ||
| std::reverse(keys, keys + arrsize); | ||
| std::reverse(indexes, indexes + arrsize); | ||
| // Only reverse the real portion; NaN at the end stays in place | ||
| std::reverse(keys, keys + index_last_elem + 1); | ||
| std::reverse(indexes, indexes + index_last_elem + 1); | ||
| } | ||
@@ -692,4 +693,2 @@ } | ||
| if (minarrsize) { | ||
| if (descending) { k = arrsize - 1 - k; } | ||
| arrsize_t index_last_elem = arrsize - 1; | ||
@@ -704,2 +703,5 @@ if constexpr (xss::fp::is_floating_point_v<T1>) { | ||
| // For descending: map k to ascending position within real portion | ||
| if (descending) { k = index_last_elem - k; } | ||
| UNUSED(hasnan); | ||
@@ -712,4 +714,5 @@ if (index_last_elem >= k) { | ||
| if (descending) { | ||
| std::reverse(keys, keys + arrsize); | ||
| std::reverse(indexes, indexes + arrsize); | ||
| // Only reverse the real portion; NaN at the end stays in place | ||
| std::reverse(keys, keys + index_last_elem + 1); | ||
| std::reverse(indexes, indexes + index_last_elem + 1); | ||
| } | ||
@@ -716,0 +719,0 @@ } |
@@ -54,31 +54,4 @@ /******************************************************************* | ||
| template <typename vtype, typename T> | ||
| X86_SIMD_SORT_INLINE arrsize_t replace_nan_with_inf(T *arr, arrsize_t size) | ||
| { | ||
| arrsize_t nan_count = 0; | ||
| using opmask_t = typename vtype::opmask_t; | ||
| using reg_t = typename vtype::reg_t; | ||
| opmask_t loadmask; | ||
| reg_t in; | ||
| /* | ||
| * (ii + numlanes) can never overflow: max val of size is 2**63 on 64-bit | ||
| * and 2**31 on 32-bit systems | ||
| */ | ||
| for (arrsize_t ii = 0; ii < size; ii = ii + vtype::numlanes) { | ||
| if (size - ii < vtype::numlanes) { | ||
| loadmask = vtype::get_partial_loadmask(size - ii); | ||
| in = vtype::maskz_loadu(loadmask, arr + ii); | ||
| } | ||
| else { | ||
| in = vtype::loadu(arr + ii); | ||
| } | ||
| opmask_t nanmask = vtype::template fpclass<0x01 | 0x80>(in); | ||
| nan_count += _mm_popcnt_u32(vtype::convert_mask_to_int(nanmask)); | ||
| vtype::mask_storeu(arr + ii, nanmask, vtype::zmm_max()); | ||
| } | ||
| return nan_count; | ||
| } | ||
| template <typename vtype, typename type_t> | ||
| X86_SIMD_SORT_INLINE bool array_has_nan(type_t *arr, arrsize_t size) | ||
| X86_SIMD_SORT_INLINE bool array_has_nan(const type_t *arr, arrsize_t size) | ||
| { | ||
@@ -108,32 +81,2 @@ using opmask_t = typename vtype::opmask_t; | ||
| template <typename type_t> | ||
| X86_SIMD_SORT_INLINE void replace_inf_with_nan(type_t *arr, | ||
| arrsize_t size, | ||
| arrsize_t nan_count, | ||
| bool descending = false) | ||
| { | ||
| if (descending) { | ||
| for (arrsize_t ii = 0; nan_count > 0; ++ii) { | ||
| if constexpr (xss::fp::is_floating_point_v<type_t>) { | ||
| arr[ii] = xss::fp::quiet_NaN<type_t>(); | ||
| } | ||
| else { | ||
| arr[ii] = 0x7c01; // std::quiet_nan | ||
| } | ||
| nan_count -= 1; | ||
| } | ||
| } | ||
| else { | ||
| for (arrsize_t ii = size - 1; nan_count > 0; --ii) { | ||
| if constexpr (xss::fp::is_floating_point_v<type_t>) { | ||
| arr[ii] = xss::fp::quiet_NaN<type_t>(); | ||
| } | ||
| else { | ||
| arr[ii] = 0x7c01; // std::quiet_nan | ||
| } | ||
| nan_count -= 1; | ||
| } | ||
| } | ||
| } | ||
| /* | ||
@@ -655,3 +598,4 @@ * Sort all the NAN's to end of the array and return the index of the last elem | ||
| template <typename vtype, typename T, bool descending = false> | ||
| X86_SIMD_SORT_INLINE void xss_qsort(T *arr, arrsize_t arrsize, bool hasnan) | ||
| X86_SIMD_SORT_INLINE void | ||
| xss_qsort(T *arr, arrsize_t arrsize, bool hasnan, bool nans_last = true) | ||
| { | ||
@@ -664,6 +608,13 @@ using comparator = | ||
| if (arrsize > 1) { | ||
| arrsize_t nan_count = 0; | ||
| arrsize_t index_first_elem = 0; | ||
| arrsize_t index_last_elem = arrsize - 1; | ||
| if constexpr (xss::fp::is_floating_point_v<T>) { | ||
| if (UNLIKELY(hasnan)) { | ||
| nan_count = replace_nan_with_inf<vtype>(arr, arrsize); | ||
| if (!nans_last) { | ||
| index_first_elem | ||
| = move_nans_to_start_of_array(arr, arrsize); | ||
| } | ||
| else { | ||
| index_last_elem = move_nans_to_end_of_array(arr, arrsize); | ||
| } | ||
| } | ||
@@ -674,36 +625,42 @@ } | ||
| if (index_first_elem <= index_last_elem && index_last_elem < arrsize) { | ||
| #ifdef XSS_COMPILE_OPENMP | ||
| bool use_parallel = arrsize > 100000; | ||
| bool use_parallel | ||
| = (index_last_elem - index_first_elem + 1) > 100000; | ||
| if (use_parallel) { | ||
| int thread_count = xss_get_num_threads(); | ||
| arrsize_t task_threshold | ||
| = std::max((arrsize_t)100000, arrsize / 100); | ||
| if (use_parallel) { | ||
| int thread_count = xss_get_num_threads(); | ||
| arrsize_t task_threshold = std::max( | ||
| (arrsize_t)100000, | ||
| (index_last_elem - index_first_elem + 1) / 100); | ||
| // We use omp parallel and then omp single to setup the threads that will run the omp task calls in qsort_ | ||
| // The omp single prevents multiple threads from running the initial qsort_ simultaneously and causing problems | ||
| // Note that we do not use the if(...) clause built into OpenMP, because it causes a performance regression for small arrays | ||
| // We use omp parallel and then omp single to setup the threads that will run the omp task calls in qsort_ | ||
| // The omp single prevents multiple threads from running the initial qsort_ simultaneously and causing problems | ||
| // Note that we do not use the if(...) clause built into OpenMP, because it causes a performance regression for small arrays | ||
| #pragma omp parallel num_threads(thread_count) | ||
| #pragma omp single | ||
| qsort_<vtype, comparator, T>(arr, | ||
| 0, | ||
| arrsize - 1, | ||
| 2 * (arrsize_t)log2(arrsize), | ||
| task_threshold); | ||
| qsort_<vtype, comparator, T>(arr, | ||
| index_first_elem, | ||
| index_last_elem, | ||
| 2 * (arrsize_t)log2(arrsize), | ||
| task_threshold); | ||
| #pragma omp taskwait | ||
| } | ||
| else { | ||
| } | ||
| else { | ||
| qsort_<vtype, comparator, T>( | ||
| arr, | ||
| index_first_elem, | ||
| index_last_elem, | ||
| 2 * (arrsize_t)log2(arrsize), | ||
| std::numeric_limits<arrsize_t>::max()); | ||
| } | ||
| #else | ||
| qsort_<vtype, comparator, T>(arr, | ||
| 0, | ||
| arrsize - 1, | ||
| index_first_elem, | ||
| index_last_elem, | ||
| 2 * (arrsize_t)log2(arrsize), | ||
| std::numeric_limits<arrsize_t>::max()); | ||
| 0); | ||
| #endif | ||
| } | ||
| #else | ||
| qsort_<vtype, comparator, T>( | ||
| arr, 0, arrsize - 1, 2 * (arrsize_t)log2(arrsize), 0); | ||
| #endif | ||
| replace_inf_with_nan(arr, arrsize, nan_count, descending); | ||
| } | ||
@@ -719,4 +676,7 @@ | ||
| template <typename vtype, typename T, bool descending = false> | ||
| X86_SIMD_SORT_INLINE void | ||
| xss_qselect(T *arr, arrsize_t k, arrsize_t arrsize, bool hasnan) | ||
| X86_SIMD_SORT_INLINE void xss_qselect(T *arr, | ||
| arrsize_t k, | ||
| arrsize_t arrsize, | ||
| bool hasnan, | ||
| bool nans_last = true) | ||
| { | ||
@@ -736,3 +696,3 @@ using comparator = | ||
| if (UNLIKELY(hasnan)) { | ||
| if constexpr (descending) { | ||
| if (!nans_last) { | ||
| index_first_elem = move_nans_to_start_of_array(arr, arrsize); | ||
@@ -763,8 +723,12 @@ } | ||
| template <typename vtype, typename T, bool descending = false> | ||
| X86_SIMD_SORT_INLINE void | ||
| xss_partial_qsort(T *arr, arrsize_t k, arrsize_t arrsize, bool hasnan) | ||
| X86_SIMD_SORT_INLINE void xss_partial_qsort(T *arr, | ||
| arrsize_t k, | ||
| arrsize_t arrsize, | ||
| bool hasnan, | ||
| bool nans_last = true) | ||
| { | ||
| if (k == 0) return; | ||
| xss_qselect<vtype, T, descending>(arr, k - 1, arrsize, hasnan); | ||
| xss_qsort<vtype, T, descending>(arr, k - 1, hasnan); | ||
| xss_qselect<vtype, T, descending>( | ||
| arr, k - 1, arrsize, hasnan, nans_last); | ||
| xss_qsort<vtype, T, descending>(arr, k - 1, hasnan, nans_last); | ||
| } | ||
@@ -777,7 +741,10 @@ | ||
| bool hasnan = false, \ | ||
| bool descending = false) \ | ||
| bool descending = false, \ | ||
| bool nans_last = true) \ | ||
| { \ | ||
| if (descending) { xss_qsort<VTYPE, T, true>(arr, size, hasnan); } \ | ||
| if (descending) { \ | ||
| xss_qsort<VTYPE, T, true>(arr, size, hasnan, nans_last); \ | ||
| } \ | ||
| else { \ | ||
| xss_qsort<VTYPE, T, false>(arr, size, hasnan); \ | ||
| xss_qsort<VTYPE, T, false>(arr, size, hasnan, nans_last); \ | ||
| } \ | ||
@@ -790,7 +757,10 @@ } \ | ||
| bool hasnan = false, \ | ||
| bool descending = false) \ | ||
| bool descending = false, \ | ||
| bool nans_last = true) \ | ||
| { \ | ||
| if (descending) { xss_qselect<VTYPE, T, true>(arr, k, size, hasnan); } \ | ||
| if (descending) { \ | ||
| xss_qselect<VTYPE, T, true>(arr, k, size, hasnan, nans_last); \ | ||
| } \ | ||
| else { \ | ||
| xss_qselect<VTYPE, T, false>(arr, k, size, hasnan); \ | ||
| xss_qselect<VTYPE, T, false>(arr, k, size, hasnan, nans_last); \ | ||
| } \ | ||
@@ -803,9 +773,12 @@ } \ | ||
| bool hasnan = false, \ | ||
| bool descending = false) \ | ||
| bool descending = false, \ | ||
| bool nans_last = true) \ | ||
| { \ | ||
| if (descending) { \ | ||
| xss_partial_qsort<VTYPE, T, true>(arr, k, size, hasnan); \ | ||
| xss_partial_qsort<VTYPE, T, true>( \ | ||
| arr, k, size, hasnan, nans_last); \ | ||
| } \ | ||
| else { \ | ||
| xss_partial_qsort<VTYPE, T, false>(arr, k, size, hasnan); \ | ||
| xss_partial_qsort<VTYPE, T, false>( \ | ||
| arr, k, size, hasnan, nans_last); \ | ||
| } \ | ||
@@ -812,0 +785,0 @@ } |
@@ -211,3 +211,3 @@ #ifndef XSS_KEYVALUE_NETWORKS | ||
| X86_SIMD_SORT_INLINE void | ||
| argsort_n_vec(typename keyType::type_t *keys, arrsize_t *indices, int N) | ||
| argsort_n_vec(const typename keyType::type_t *keys, arrsize_t *indices, int N) | ||
| { | ||
@@ -358,3 +358,3 @@ using kreg_t = typename keyType::reg_t; | ||
| X86_SIMD_SORT_INLINE void | ||
| argsort_n(typename keyType::type_t *keys, arrsize_t *indices, int N) | ||
| argsort_n(const typename keyType::type_t *keys, arrsize_t *indices, int N) | ||
| { | ||
@@ -361,0 +361,0 @@ static_assert(keyType::numlanes == indexType::numlanes, |
@@ -63,10 +63,10 @@ #ifndef AVX512_TEST_COMMON | ||
| if (!descending) { | ||
| cmp_less = compare<T, std::less<T>>(); | ||
| cmp_leq = compare<T, std::less_equal<T>>(); | ||
| cmp_geq = compare<T, std::greater_equal<T>>(); | ||
| cmp_less = compare_nan_end<T, std::less<T>>(); | ||
| cmp_leq = compare_nan_end<T, std::less_equal<T>>(); | ||
| cmp_geq = compare_nan_end<T, std::greater_equal<T>>(); | ||
| } | ||
| else { | ||
| cmp_less = compare<T, std::greater<T>>(); | ||
| cmp_leq = compare<T, std::greater_equal<T>>(); | ||
| cmp_geq = compare<T, std::less_equal<T>>(); | ||
| cmp_less = compare_nan_end<T, std::greater<T>>(); | ||
| cmp_leq = compare_nan_end<T, std::greater_equal<T>>(); | ||
| cmp_geq = compare_nan_end<T, std::less_equal<T>>(); | ||
| } | ||
@@ -78,2 +78,7 @@ | ||
| } | ||
| // If arr[k] is NaN, k is in the trailing NaN block; value comparisons are | ||
| // not meaningful, so skip the left/right partition checks. | ||
| if constexpr (xss::fp::is_floating_point_v<T>) { | ||
| if (xss::fp::isnan(arr[k])) return; | ||
| } | ||
| // ( 2) Elements to the left of k should be atmost arr[k] | ||
@@ -113,3 +118,4 @@ if (k >= 1) { | ||
| size_t k, | ||
| std::string type) | ||
| std::string type, | ||
| bool descending = false) | ||
| { | ||
@@ -121,4 +127,4 @@ EXPECT_UNIQUE(arg) | ||
| } | ||
| IS_ARR_PARTITIONED(part_arr, k, true_kth, type); | ||
| IS_ARR_PARTITIONED(part_arr, k, true_kth, type, descending); | ||
| } | ||
| #endif |
@@ -79,3 +79,3 @@ /******************************************* | ||
| sortedarr.end(), | ||
| compare<TypeParam, std::greater<TypeParam>>()); | ||
| compare_nan_end<TypeParam, std::greater<TypeParam>>()); | ||
| IS_SORTED(sortedarr, arr, type); | ||
@@ -123,3 +123,3 @@ #endif | ||
| sortedarr.end(), | ||
| compare<TypeParam, std::greater<TypeParam>>()); | ||
| compare_nan_end<TypeParam, std::greater<TypeParam>>()); | ||
| IS_ARG_SORTED(sortedarr, arr, arg, type); | ||
@@ -133,2 +133,88 @@ #endif | ||
| TYPED_TEST_P(simdsort, test_argsort_nans_last) | ||
| { | ||
| if constexpr (xss::fp::is_floating_point_v<TypeParam>) { | ||
| std::vector<std::string> nan_types | ||
| = {"rand_with_nan", "rand_with_max_and_nan"}; | ||
| for (auto type : nan_types) { | ||
| for (auto size : this->arrsize_long) { | ||
| std::vector<TypeParam> base = get_array<TypeParam>(type, size); | ||
| // ascending, NaNs at end | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| auto arg = x86simdsort::argsort( | ||
| arr.data(), arr.size(), true, false, true); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::sort( | ||
| sortedarr.begin(), | ||
| sortedarr.end(), | ||
| compare_nan_end<TypeParam, std::less<TypeParam>>()); | ||
| IS_ARG_SORTED(sortedarr, arr, arg, type); | ||
| #endif | ||
| } | ||
| // descending, NaNs at end | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| auto arg = x86simdsort::argsort( | ||
| arr.data(), arr.size(), true, true, true); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::sort(sortedarr.begin(), | ||
| sortedarr.end(), | ||
| compare_nan_end<TypeParam, | ||
| std::greater<TypeParam>>()); | ||
| IS_ARG_SORTED(sortedarr, arr, arg, type); | ||
| #endif | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| TYPED_TEST_P(simdsort, test_argsort_leading_nans) | ||
| { | ||
| if constexpr (xss::fp::is_floating_point_v<TypeParam>) { | ||
| std::vector<std::string> nan_types | ||
| = {"rand_with_nan", "rand_with_max_and_nan"}; | ||
| for (auto type : nan_types) { | ||
| for (auto size : this->arrsize_long) { | ||
| std::vector<TypeParam> base = get_array<TypeParam>(type, size); | ||
| // ascending, NaNs at start | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| auto arg = x86simdsort::argsort( | ||
| arr.data(), arr.size(), true, false, false); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::sort(sortedarr.begin(), | ||
| sortedarr.end(), | ||
| compare_nan_begin<TypeParam, | ||
| std::less<TypeParam>>()); | ||
| IS_ARG_SORTED(sortedarr, arr, arg, type); | ||
| #endif | ||
| } | ||
| // descending, NaNs at start | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| auto arg = x86simdsort::argsort( | ||
| arr.data(), arr.size(), true, true, false); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::sort(sortedarr.begin(), | ||
| sortedarr.end(), | ||
| compare_nan_begin<TypeParam, | ||
| std::greater<TypeParam>>()); | ||
| IS_ARG_SORTED(sortedarr, arr, arg, type); | ||
| #endif | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| TYPED_TEST_P(simdsort, test_qselect_ascending) | ||
@@ -175,6 +261,7 @@ { | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::nth_element(sortedarr.begin(), | ||
| sortedarr.begin() + k, | ||
| sortedarr.end(), | ||
| compare<TypeParam, std::greater<TypeParam>>()); | ||
| std::nth_element( | ||
| sortedarr.begin(), | ||
| sortedarr.begin() + k, | ||
| sortedarr.end(), | ||
| compare_nan_end<TypeParam, std::greater<TypeParam>>()); | ||
| if (size == 0) continue; | ||
@@ -189,4 +276,102 @@ IS_ARR_PARTITIONED(arr, k, sortedarr[k], type, true); | ||
| TYPED_TEST_P(simdsort, test_argselect) | ||
| TYPED_TEST_P(simdsort, test_qselect_nans_last) | ||
| { | ||
| if constexpr (xss::fp::is_floating_point_v<TypeParam>) { | ||
| std::vector<std::string> nan_types | ||
| = {"rand_with_nan", "rand_with_max_and_nan"}; | ||
| for (auto type : nan_types) { | ||
| for (auto size : this->arrsize) { | ||
| size_t k = size != 0 ? rand() % size : 0; | ||
| std::vector<TypeParam> base = get_array<TypeParam>(type, size); | ||
| // ascending, NaNs at end | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| x86simdsort::qselect( | ||
| arr.data(), k, arr.size(), true, false, true); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::nth_element( | ||
| sortedarr.begin(), | ||
| sortedarr.begin() + k, | ||
| sortedarr.end(), | ||
| compare_nan_end<TypeParam, std::less<TypeParam>>()); | ||
| if (size == 0) continue; | ||
| IS_ARR_PARTITIONED(arr, k, sortedarr[k], type); | ||
| #endif | ||
| } | ||
| // descending, NaNs at end | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| x86simdsort::qselect( | ||
| arr.data(), k, arr.size(), true, true, true); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::nth_element( | ||
| sortedarr.begin(), | ||
| sortedarr.begin() + k, | ||
| sortedarr.end(), | ||
| compare_nan_end<TypeParam, | ||
| std::greater<TypeParam>>()); | ||
| if (size == 0) continue; | ||
| IS_ARR_PARTITIONED(arr, k, sortedarr[k], type, true); | ||
| #endif | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| TYPED_TEST_P(simdsort, test_qselect_leading_nans) | ||
| { | ||
| if constexpr (xss::fp::is_floating_point_v<TypeParam>) { | ||
| std::vector<std::string> nan_types | ||
| = {"rand_with_nan", "rand_with_max_and_nan"}; | ||
| for (auto type : nan_types) { | ||
| for (auto size : this->arrsize) { | ||
| size_t k = size != 0 ? rand() % size : 0; | ||
| std::vector<TypeParam> base = get_array<TypeParam>(type, size); | ||
| // ascending, NaNs at start | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| x86simdsort::qselect( | ||
| arr.data(), k, arr.size(), true, false, false); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::nth_element(sortedarr.begin(), | ||
| sortedarr.begin() + k, | ||
| sortedarr.end(), | ||
| compare_nan_begin<TypeParam, | ||
| std::less<TypeParam>>()); | ||
| if (size == 0) continue; | ||
| IS_ARR_PARTITIONED(arr, k, sortedarr[k], type); | ||
| #endif | ||
| } | ||
| // descending, NaNs at start | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| x86simdsort::qselect( | ||
| arr.data(), k, arr.size(), true, true, false); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::nth_element( | ||
| sortedarr.begin(), | ||
| sortedarr.begin() + k, | ||
| sortedarr.end(), | ||
| compare_nan_begin<TypeParam, | ||
| std::greater<TypeParam>>()); | ||
| if (size == 0) continue; | ||
| IS_ARR_PARTITIONED(arr, k, sortedarr[k], type, true); | ||
| #endif | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| TYPED_TEST_P(simdsort, test_argselect_ascending) | ||
| { | ||
| for (auto type : this->arrtype) { | ||
@@ -214,2 +399,126 @@ bool hasnan = is_nan_test(type); | ||
| TYPED_TEST_P(simdsort, test_argselect_descending) | ||
| { | ||
| for (auto type : this->arrtype) { | ||
| bool hasnan = is_nan_test(type); | ||
| for (auto size : this->arrsize) { | ||
| size_t k = size != 0 ? rand() % size : 0; | ||
| std::vector<TypeParam> arr = get_array<TypeParam>(type, size); | ||
| std::vector<TypeParam> sortedarr = arr; | ||
| auto arg = x86simdsort::argselect( | ||
| arr.data(), k, arr.size(), hasnan, true); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::nth_element( | ||
| sortedarr.begin(), | ||
| sortedarr.begin() + k, | ||
| sortedarr.end(), | ||
| compare_nan_end<TypeParam, std::greater<TypeParam>>()); | ||
| if (size == 0) continue; | ||
| IS_ARG_PARTITIONED(arr, arg, sortedarr[k], k, type, true); | ||
| #endif | ||
| arr.clear(); | ||
| sortedarr.clear(); | ||
| } | ||
| } | ||
| } | ||
| TYPED_TEST_P(simdsort, test_argselect_nans_last) | ||
| { | ||
| if constexpr (xss::fp::is_floating_point_v<TypeParam>) { | ||
| std::vector<std::string> nan_types | ||
| = {"rand_with_nan", "rand_with_max_and_nan"}; | ||
| for (auto type : nan_types) { | ||
| for (auto size : this->arrsize) { | ||
| size_t k = size != 0 ? rand() % size : 0; | ||
| std::vector<TypeParam> base = get_array<TypeParam>(type, size); | ||
| // ascending, NaNs at end | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| auto arg = x86simdsort::argselect( | ||
| arr.data(), k, arr.size(), true, false, true); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::nth_element( | ||
| sortedarr.begin(), | ||
| sortedarr.begin() + k, | ||
| sortedarr.end(), | ||
| compare_nan_end<TypeParam, std::less<TypeParam>>()); | ||
| if (size == 0) continue; | ||
| IS_ARG_PARTITIONED(arr, arg, sortedarr[k], k, type); | ||
| #endif | ||
| } | ||
| // descending, NaNs at end | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| auto arg = x86simdsort::argselect( | ||
| arr.data(), k, arr.size(), true, true, true); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::nth_element( | ||
| sortedarr.begin(), | ||
| sortedarr.begin() + k, | ||
| sortedarr.end(), | ||
| compare_nan_end<TypeParam, | ||
| std::greater<TypeParam>>()); | ||
| if (size == 0) continue; | ||
| IS_ARG_PARTITIONED(arr, arg, sortedarr[k], k, type, true); | ||
| #endif | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| TYPED_TEST_P(simdsort, test_argselect_leading_nans) | ||
| { | ||
| if constexpr (xss::fp::is_floating_point_v<TypeParam>) { | ||
| std::vector<std::string> nan_types | ||
| = {"rand_with_nan", "rand_with_max_and_nan"}; | ||
| for (auto type : nan_types) { | ||
| for (auto size : this->arrsize) { | ||
| size_t k = size != 0 ? rand() % size : 0; | ||
| std::vector<TypeParam> base = get_array<TypeParam>(type, size); | ||
| // ascending, NaNs at start | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| auto arg = x86simdsort::argselect( | ||
| arr.data(), k, arr.size(), true, false, false); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::nth_element(sortedarr.begin(), | ||
| sortedarr.begin() + k, | ||
| sortedarr.end(), | ||
| compare_nan_begin<TypeParam, | ||
| std::less<TypeParam>>()); | ||
| if (size == 0) continue; | ||
| IS_ARG_PARTITIONED(arr, arg, sortedarr[k], k, type); | ||
| #endif | ||
| } | ||
| // descending, NaNs at start | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| auto arg = x86simdsort::argselect( | ||
| arr.data(), k, arr.size(), true, true, false); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::nth_element( | ||
| sortedarr.begin(), | ||
| sortedarr.begin() + k, | ||
| sortedarr.end(), | ||
| compare_nan_begin<TypeParam, | ||
| std::greater<TypeParam>>()); | ||
| if (size == 0) continue; | ||
| IS_ARG_PARTITIONED(arr, arg, sortedarr[k], k, type, true); | ||
| #endif | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| TYPED_TEST_P(simdsort, test_partial_qsort_ascending) | ||
@@ -257,3 +566,3 @@ { | ||
| sortedarr.end(), | ||
| compare<TypeParam, std::greater<TypeParam>>()); | ||
| compare_nan_end<TypeParam, std::greater<TypeParam>>()); | ||
| if (size == 0) continue; | ||
@@ -301,10 +610,105 @@ IS_ARR_PARTIALSORTED(arr, k, sortedarr, type); | ||
| TYPED_TEST_P(simdsort, test_qsort_nans_last) | ||
| { | ||
| if constexpr (xss::fp::is_floating_point_v<TypeParam>) { | ||
| std::vector<std::string> nan_types | ||
| = {"rand_with_nan", "rand_with_max_and_nan"}; | ||
| for (auto type : nan_types) { | ||
| for (auto size : this->arrsize_long) { | ||
| std::vector<TypeParam> base = get_array<TypeParam>(type, size); | ||
| // ascending, NaNs at end | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| x86simdsort::qsort( | ||
| arr.data(), arr.size(), true, false, true); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::sort( | ||
| sortedarr.begin(), | ||
| sortedarr.end(), | ||
| compare_nan_end<TypeParam, std::less<TypeParam>>()); | ||
| IS_SORTED(sortedarr, arr, type); | ||
| #endif | ||
| } | ||
| // descending, NaNs at end | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| x86simdsort::qsort( | ||
| arr.data(), arr.size(), true, true, true); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::sort(sortedarr.begin(), | ||
| sortedarr.end(), | ||
| compare_nan_end<TypeParam, | ||
| std::greater<TypeParam>>()); | ||
| IS_SORTED(sortedarr, arr, type); | ||
| #endif | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| TYPED_TEST_P(simdsort, test_qsort_leading_nans) | ||
| { | ||
| if constexpr (xss::fp::is_floating_point_v<TypeParam>) { | ||
| std::vector<std::string> nan_types | ||
| = {"rand_with_nan", "rand_with_max_and_nan"}; | ||
| for (auto type : nan_types) { | ||
| for (auto size : this->arrsize_long) { | ||
| std::vector<TypeParam> base = get_array<TypeParam>(type, size); | ||
| // ascending, NaNs at start | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| x86simdsort::qsort( | ||
| arr.data(), arr.size(), true, false, false); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::sort(sortedarr.begin(), | ||
| sortedarr.end(), | ||
| compare_nan_begin<TypeParam, | ||
| std::less<TypeParam>>()); | ||
| IS_SORTED(sortedarr, arr, type); | ||
| #endif | ||
| } | ||
| // descending, NaNs at start | ||
| { | ||
| std::vector<TypeParam> arr = base; | ||
| std::vector<TypeParam> sortedarr = base; | ||
| x86simdsort::qsort( | ||
| arr.data(), arr.size(), true, true, false); | ||
| #ifndef XSS_ASAN_CI_NOCHECK | ||
| std::sort(sortedarr.begin(), | ||
| sortedarr.end(), | ||
| compare_nan_begin<TypeParam, | ||
| std::greater<TypeParam>>()); | ||
| IS_SORTED(sortedarr, arr, type); | ||
| #endif | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| REGISTER_TYPED_TEST_SUITE_P(simdsort, | ||
| test_qsort_ascending, | ||
| test_qsort_descending, | ||
| test_qsort_nans_last, | ||
| test_qsort_leading_nans, | ||
| test_argsort_ascending, | ||
| test_argsort_descending, | ||
| test_argselect, | ||
| test_argsort_nans_last, | ||
| test_argsort_leading_nans, | ||
| test_argselect_ascending, | ||
| test_argselect_descending, | ||
| test_argselect_nans_last, | ||
| test_argselect_leading_nans, | ||
| test_qselect_ascending, | ||
| test_qselect_descending, | ||
| test_qselect_nans_last, | ||
| test_qselect_leading_nans, | ||
| test_partial_qsort_ascending, | ||
@@ -311,0 +715,0 @@ test_partial_qsort_descending, |
@@ -49,2 +49,66 @@ #ifndef UTILS_CUSTOM_COMPARE | ||
| /* | ||
| * Comparator that always places NaN at the end of the sorted array, | ||
| * regardless of whether Comparator is ascending or descending. | ||
| */ | ||
| template <typename T, typename Comparator> | ||
| struct compare_nan_end { | ||
| static constexpr auto op = Comparator {}; | ||
| bool operator()(const T a, const T b) const | ||
| { | ||
| if constexpr (xss::fp::is_floating_point_v<T>) { | ||
| bool a_nan = xss::fp::isnan(a); | ||
| bool b_nan = xss::fp::isnan(b); | ||
| if (!a_nan && !b_nan) { return op(a, b); } | ||
| if (a_nan && b_nan) { return false; } | ||
| return !a_nan; // b is NaN → a before b → NaN at end | ||
| } | ||
| else { | ||
| return op(a, b); | ||
| } | ||
| } | ||
| }; | ||
| /* | ||
| * Comparator that always places NaN at the beginning of the sorted array, | ||
| * regardless of whether Comparator is ascending or descending. | ||
| */ | ||
| template <typename T, typename Comparator> | ||
| struct compare_nan_begin { | ||
| static constexpr auto op = Comparator {}; | ||
| bool operator()(const T a, const T b) const | ||
| { | ||
| if constexpr (xss::fp::is_floating_point_v<T>) { | ||
| bool a_nan = xss::fp::isnan(a); | ||
| bool b_nan = xss::fp::isnan(b); | ||
| if (!a_nan && !b_nan) { return op(a, b); } | ||
| if (a_nan && b_nan) { return false; } | ||
| return a_nan; // a is NaN → a before b → NaN at beginning | ||
| } | ||
| else { | ||
| return op(a, b); | ||
| } | ||
| } | ||
| }; | ||
| template <typename T, typename Comparator> | ||
| struct compare_arg_nan_end { | ||
| compare_arg_nan_end(const T *arr) : arr(arr) {} | ||
| bool operator()(const int64_t a, const int64_t b) const | ||
| { | ||
| return compare_nan_end<T, Comparator>()(arr[a], arr[b]); | ||
| } | ||
| const T *arr; | ||
| }; | ||
| template <typename T, typename Comparator> | ||
| struct compare_arg_nan_begin { | ||
| compare_arg_nan_begin(const T *arr) : arr(arr) {} | ||
| bool operator()(const int64_t a, const int64_t b) const | ||
| { | ||
| return compare_nan_begin<T, Comparator>()(arr[a], arr[b]); | ||
| } | ||
| const T *arr; | ||
| }; | ||
| #endif // UTILS_CUSTOM_COMPARE |
@@ -161,5 +161,5 @@ /* | ||
| /* Don't copy PyObject_HEAD part */ | ||
| memcpy((char *)new + sizeof(PyObject), | ||
| (char *)self + sizeof(PyObject), | ||
| sizeof(PyArray_SFloatDescr) - sizeof(PyObject)); | ||
| memcpy((char *)new + offsetof(PyArray_Descr, typeobj), | ||
| (char *)self + offsetof(PyArray_Descr, typeobj), | ||
| sizeof(PyArray_SFloatDescr) - offsetof(PyArray_Descr, typeobj)); | ||
@@ -166,0 +166,0 @@ new->scaling = new->scaling * factor; |
@@ -404,19 +404,21 @@ from typing import overload | ||
| type _tuple3[T] = tuple[T, T, T] | ||
| @overload | ||
| def partition(a: U_co, sep: U_co) -> NDArray[np.str_]: ... | ||
| def partition(a: U_co, sep: U_co) -> _tuple3[NDArray[np.str_]]: ... | ||
| @overload | ||
| def partition(a: S_co, sep: S_co) -> NDArray[np.bytes_]: ... | ||
| def partition(a: S_co, sep: S_co) -> _tuple3[NDArray[np.bytes_]]: ... | ||
| @overload | ||
| def partition(a: _StringDTypeSupportsArray, sep: _StringDTypeSupportsArray) -> _StringDTypeArray: ... | ||
| def partition(a: _StringDTypeSupportsArray, sep: _StringDTypeSupportsArray) -> _tuple3[_StringDTypeArray]: ... | ||
| @overload | ||
| def partition(a: T_co, sep: T_co) -> _StringDTypeOrUnicodeArray: ... | ||
| def partition(a: T_co, sep: T_co) -> _tuple3[_StringDTypeOrUnicodeArray]: ... | ||
| @overload | ||
| def rpartition(a: U_co, sep: U_co) -> NDArray[np.str_]: ... | ||
| def rpartition(a: U_co, sep: U_co) -> _tuple3[NDArray[np.str_]]: ... | ||
| @overload | ||
| def rpartition(a: S_co, sep: S_co) -> NDArray[np.bytes_]: ... | ||
| def rpartition(a: S_co, sep: S_co) -> _tuple3[NDArray[np.bytes_]]: ... | ||
| @overload | ||
| def rpartition(a: _StringDTypeSupportsArray, sep: _StringDTypeSupportsArray) -> _StringDTypeArray: ... | ||
| def rpartition(a: _StringDTypeSupportsArray, sep: _StringDTypeSupportsArray) -> _tuple3[_StringDTypeArray]: ... | ||
| @overload | ||
| def rpartition(a: T_co, sep: T_co) -> _StringDTypeOrUnicodeArray: ... | ||
| def rpartition(a: T_co, sep: T_co) -> _tuple3[_StringDTypeOrUnicodeArray]: ... | ||
@@ -423,0 +425,0 @@ @overload |
@@ -449,2 +449,14 @@ import sys | ||
| def test_copyto_overlapping_where_false_no_leak(): | ||
| # gh-31968: overlapping copyto with scalar where=False used to leak the | ||
| # overlap temp (missing Py_DECREF on the early-return path). No refcount | ||
| # assertion is made here; a leak sanitizer build is expected to catch | ||
| # the regression. | ||
| a = np.arange(36, dtype=object).reshape(6, 6) | ||
| original = a.copy() | ||
| np.copyto(a, a[::-1, :], where=False) | ||
| # where=False must write nothing | ||
| assert_array_equal(a, original) | ||
| def test_copyto_cast_safety(): | ||
@@ -451,0 +463,0 @@ with pytest.raises(TypeError): |
@@ -98,5 +98,3 @@ """ | ||
| # Bool: | ||
| # XFAIL: Bool should be added, but has some bad properties when it | ||
| # comes to strings, see also gh-9875 | ||
| # yield param(np.bool(0), id="bool") | ||
| yield param(np.bool(0), id="bool") | ||
@@ -103,0 +101,0 @@ # Integers: |
@@ -284,2 +284,19 @@ import os | ||
| ) | ||
| @pytest.mark.parametrize("action", ["ENABLE", "DISABLE"]) | ||
| def test_max_length_nonexistent_feature(self, action): | ||
| """ | ||
| Test that the maximum accepted environment variable length can be | ||
| processed even if the whole value is an invalid feature name. | ||
| """ | ||
| MAX_VAR_LENGTH = 1024 | ||
| self.env[f'NPY_{action}_CPU_FEATURES'] = "t" * (MAX_VAR_LENGTH - 1) | ||
| self._run() | ||
| @pytest.mark.skipif( | ||
| not __cpu_dispatch__, | ||
| reason=( | ||
| "NPY_*_CPU_FEATURES only parsed if " | ||
| "`__cpu_dispatch__` is non-empty" | ||
| ) | ||
| ) | ||
| def test_impossible_feature_disable(self): | ||
@@ -339,3 +356,25 @@ """ | ||
| @pytest.mark.parametrize("action", ["ENABLE", "DISABLE"]) | ||
| def test_repeated_unavailable_feature(self, action): | ||
| """ | ||
| Test that repeated unavailable features can be processed without | ||
| overflowing the diagnostic buffer. | ||
| """ | ||
| if self.UNAVAILABLE_FEAT is None: | ||
| pytest.skip("There are no unavailable features to test with") | ||
| bad_feature = self.UNAVAILABLE_FEAT | ||
| feature_repeats = 1024 // (len(bad_feature) + 1) | ||
| self.env[f'NPY_{action}_CPU_FEATURES'] = ",".join( | ||
| [bad_feature] * feature_repeats | ||
| ) | ||
| if action == "ENABLE": | ||
| msg = "You cannot enable CPU features" | ||
| err_type = "RuntimeError" | ||
| self._expect_error(msg, err_type) | ||
| else: | ||
| self._run() | ||
| is_linux = sys.platform.startswith('linux') | ||
@@ -342,0 +381,0 @@ is_cygwin = sys.platform.startswith('cygwin') |
@@ -47,2 +47,94 @@ from tempfile import NamedTemporaryFile | ||
| def test_structured_field_byteswap_raises(self): | ||
| # a field (or subarray field) whose dtype lacks the legacy copyswap | ||
| # slot cannot be byteswapped; previously this segfaulted via the | ||
| # unguarded field copyswap calls in VOID_copyswapn | ||
| arr = np.zeros(2, dtype=[("a", "i4"), ("v", SF(1.))]) | ||
| with pytest.raises(TypeError, match="does not implement copyswap"): | ||
| arr.byteswap() | ||
| subarr = np.zeros(2, dtype=[("v", SF(1.), (2,))]) | ||
| with pytest.raises(TypeError, match="does not implement copyswap"): | ||
| subarr.byteswap() | ||
| def test_byteswap_raises(self): | ||
| # a top-level DType without the legacy copyswap slot does not | ||
| # support byteswapping | ||
| arr = np.zeros(2, dtype=SF(1.)) | ||
| with pytest.raises(TypeError, match="does not implement copyswap"): | ||
| arr.byteswap() | ||
| # the in-place writeability check fires before the missing-slot | ||
| # check, matching the behavior for dtypes that fill the slot | ||
| arr.flags.writeable = False | ||
| with pytest.raises(ValueError, match="array to be byte-swapped"): | ||
| arr.byteswap(inplace=True) | ||
| def test_structured_field_place_and_flat_raise(self): | ||
| # requests that need to copy through the missing legacy copyswap | ||
| # slot raise instead of crashing | ||
| arr = np.zeros(2, dtype=[("v", SF(1.))]) | ||
| with pytest.raises(TypeError, match="does not implement copyswap"): | ||
| np.place(arr, [True, False], arr[:1]) | ||
| with pytest.raises(TypeError, match="does not implement copyswap"): | ||
| arr.flat = arr[:1] | ||
| # both stop copying as soon as the error is detected, so elements | ||
| # after the first (failing) one keep their values, even for fields | ||
| # that could have been copied | ||
| marr = np.zeros(3, dtype=[("a", "i4"), ("v", SF(1.))]) | ||
| marr["a"] = [5, 6, 7] | ||
| src = marr[:1].copy() | ||
| src["a"] = 1 | ||
| with pytest.raises(TypeError, match="does not implement copyswap"): | ||
| marr.flat = src | ||
| assert marr["a"].tolist()[1:] == [6, 7] | ||
| with pytest.raises(TypeError, match="does not implement copyswap"): | ||
| np.place(marr, [True, True, True], src) | ||
| assert marr["a"].tolist()[1:] == [6, 7] | ||
| def test_structured_field_sort_raises(self): | ||
| # VOID_compare called the field's legacy compare slot, which | ||
| # new-style DTypes do not fill | ||
| arr = np.zeros(3, dtype=[("v", SF(1.))]) | ||
| with pytest.raises(TypeError, match="does not support comparison"): | ||
| np.sort(arr, order="v") | ||
| # packed fields are misaligned; the compare == NULL check fires | ||
| # before VOID_compare's alignment handling, so this exits through | ||
| # the same guard as the aligned case above and never reaches the | ||
| # misaligned buffer path (which would call the missing copyswap | ||
| # slot); it checks that the guard keeps covering that layout | ||
| packed_dt = np.dtype({"names": ["a", "v"], | ||
| "formats": ["u1", SF(1.)], | ||
| "offsets": [0, 1], "itemsize": 17}) | ||
| with pytest.raises(TypeError, match="does not support comparison"): | ||
| np.sort(np.zeros(3, dtype=packed_dt), order="v") | ||
| def test_structured_setitem_uses_cast_path(self): | ||
| # scalar assignment between equivalent structured dtypes used to | ||
| # segfault in the copyswap fast path; it now falls back to casting | ||
| arr = np.zeros(2, dtype=[("v", SF(1.))]) | ||
| arr["v"] = np.array([1., 2.]).view(SF(1.)) | ||
| arr[0] = arr[1] | ||
| assert arr["v"].view(np.float64).tolist() == [2., 2.] | ||
| def test_structured_setitem_nested_uses_cast_path(self): | ||
| # for a nested field the copyswap slot is VOID_copyswap, which | ||
| # fails on the inner sfloat field only after it is called; the | ||
| # error must not leak out of a successful-looking assignment and | ||
| # the copy falls back to the casting path like the flat case | ||
| arr = np.zeros(2, dtype=[("a", "i4"), ("nested", [("v", SF(1.))])]) | ||
| arr["a"] = [1, 2] | ||
| arr["nested"]["v"] = np.array([1., 2.]).view(SF(1.)) | ||
| arr[0] = arr[1] | ||
| assert arr["a"].tolist() == [2, 2] | ||
| assert arr["nested"]["v"].view(np.float64).tolist() == [2., 2.] | ||
| # same for a subarray field of an sfloat dtype | ||
| arr = np.zeros(2, dtype=[("sub", SF(1.), (2,))]) | ||
| arr["sub"] = np.array([[1., 2.], [3., 4.]]).view(SF(1.)) | ||
| arr[0] = arr[1] | ||
| assert arr["sub"].view(np.float64).tolist() == [[3., 4.], [3., 4.]] | ||
| def test_repr(self): | ||
@@ -49,0 +141,0 @@ # Check the repr, mainly to cover the code paths: |
@@ -24,3 +24,3 @@ import os | ||
| # Note: keep in sync with the one in pyproject.toml | ||
| required_version = "3.0.6" | ||
| required_version = "3.1.0" | ||
| if _pep440.parse(cython_version) < _pep440.Version(required_version): | ||
@@ -27,0 +27,0 @@ # too old or wrong cython, skip the test |
@@ -0,1 +1,2 @@ | ||
| import importlib | ||
| import os | ||
@@ -9,2 +10,3 @@ import shutil | ||
| import numpy as np | ||
| from numpy.testing import IS_EDITABLE, IS_WASM, NOGIL_BUILD | ||
@@ -23,3 +25,3 @@ from numpy.testing._private.utils import run_subprocess | ||
| # Note: keep in sync with the one in pyproject.toml | ||
| required_version = "3.0.6" | ||
| required_version = "3.1.0" | ||
| if _pep440.parse(cython_version) < _pep440.Version(required_version): | ||
@@ -87,4 +89,122 @@ # too old or wrong cython, skip the test | ||
| @pytest.mark.skipif(IS_WASM, reason="Can't start subprocess") | ||
| @pytest.mark.xfail( | ||
| def _check_api_module(mod, cython=False): | ||
| arr = np.ones((200, 200)) | ||
| assert mod.nonzero(arr) == 200 * 200 | ||
| # Legacy single-array iterator: PyArray_ITER_NEXT / _DATA / _NOTDONE. | ||
| arr = np.arange(12.0).reshape(3, 4) | ||
| assert mod.iter_next(arr) == 66.0 | ||
| assert mod.iter_goto1d(arr, 5) == 5.0 | ||
| assert mod.iter_goto1d(arr, -1) == 11.0 | ||
| assert mod.iter_reset(arr) == 66.0 | ||
| assert mod.iter_goto(arr, (1, 2)) == 6.0 | ||
| assert mod.iter_goto(arr, (2, 3)) == 11.0 | ||
| # Broadcasting multi-iterator. | ||
| a = np.arange(3.0).reshape(3, 1) | ||
| b = np.arange(4.0).reshape(1, 4) | ||
| assert mod.multi_iter_next(a, b) == float(np.sum(a + b)) | ||
| if not cython: | ||
| va, vb = mod.multi_iter_goto(a, b, (1, 2)) | ||
| assert va == 1.0 and vb == 2.0 | ||
| va, vb = mod.multi_iter_goto1d(a, b, 6) | ||
| assert va == 1.0 and vb == 2.0 | ||
| a = np.arange(6.0).reshape(2, 3) | ||
| b = np.zeros((2, 3)) | ||
| assert mod.multi_iter_nexti(a, b, 3) == 3.0 | ||
| if cython: | ||
| # Datetime / timedelta scalar accessors (.pxd helpers). | ||
| dt = np.datetime64("2021-01-01", "D") | ||
| assert mod.get_datetime_value(dt) == dt.astype("int64") | ||
| assert mod.get_datetime_unit(dt) == 4 | ||
| assert mod.is_datetime64(dt) | ||
| assert not mod.is_timedelta64(dt) | ||
| # A plain seconds timedelta: base NPY_FR_s, unit multiplier 1. | ||
| td = np.timedelta64(5, "s") | ||
| assert mod.get_timedelta_value(td) == 5 | ||
| assert mod.is_timedelta64(td) | ||
| assert not mod.is_datetime64(td) | ||
| # A non-unit multiplier exercises the metadata `num` field. | ||
| td = np.timedelta64(1000, "ms").astype("timedelta64[10ms]") | ||
| assert mod.get_timedelta_value(td) == td.astype("int64") | ||
| else: | ||
| # PyDataType_FLAGS / PyDataType_C_METADATA on datetime descriptors. | ||
| dt = np.array(["2021-01-01"], dtype="datetime64[D]") | ||
| flags, base, num = mod.datetime_metadata(dt) | ||
| assert flags == dt.dtype.flags | ||
| assert base == 4 # NPY_FR_D | ||
| assert num == 1 | ||
| # A plain seconds timedelta: base NPY_FR_s, unit multiplier 1. | ||
| td = np.array([5], dtype="timedelta64[s]") | ||
| flags, base, num = mod.datetime_metadata(td) | ||
| assert flags == td.dtype.flags | ||
| assert base == 7 # NPY_FR_s | ||
| assert num == 1 | ||
| # A non-unit multiplier exercises the metadata `num` field. | ||
| td = np.array([1000], dtype="timedelta64[ms]").astype( | ||
| "timedelta64[10ms]") | ||
| flags, base, num = mod.datetime_metadata(td) | ||
| assert flags == td.dtype.flags | ||
| assert base == 8 # NPY_FR_ms | ||
| assert num == 10 | ||
| # Non-datetime descriptors have no c_metadata and are rejected. | ||
| with pytest.raises(RuntimeError): | ||
| mod.datetime_metadata(np.arange(3)) | ||
| # NpyString allocator API; under abi3t PyArray_StringDTypeObject is | ||
| # opaque and only the descriptor object pointer is passed. Absent | ||
| # when the module targets NumPy < 2.0 (the "default" target). | ||
| if hasattr(mod, "stringdtype_load"): | ||
| arr = np.array(["hello", "world"], dtype=np.dtypes.StringDType()) | ||
| assert mod.stringdtype_load(arr) == "hello" | ||
| # A long string is stored on the heap, so loading it | ||
| # dereferences the allocator acquired from the descriptor. | ||
| text = "numpy" * 20 | ||
| arr = np.array([text, "world"], dtype=np.dtypes.StringDType()) | ||
| assert mod.stringdtype_load(arr) == text | ||
| arr = np.array([None, "world"], | ||
| dtype=np.dtypes.StringDType(na_object=None)) | ||
| assert mod.stringdtype_load(arr) is None | ||
| # Test limited API extension modules for all supported Python and NumPy versions | ||
| # The _PY_ABI3_VERSIONS and _NPY_TARGET_VERSIONS lists should be kept in sync | ||
| # with the lists defined in meson.build, and the test should be updated | ||
| # if new versions are added here. | ||
| # The special "default" entry builds without defining NPY_TARGET_VERSION at all, | ||
| # which exercises the path where numpyconfig.h picks the current API version. | ||
| _PY_ABI3_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] | ||
| _NPY_TARGET_VERSIONS = ["2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "default"] | ||
| def _module_names(prefix, abi3_versions): | ||
| names = [] | ||
| for py_ver in abi3_versions: | ||
| if sys.version_info < tuple(map(int, py_ver.split('.'))): | ||
| continue | ||
| py = py_ver.replace('.', '_') | ||
| for npy_ver in _NPY_TARGET_VERSIONS: | ||
| npy = npy_ver.replace('.', '_') | ||
| names.append(f"{prefix}_{py}_npy{npy}") | ||
| return names | ||
| def limited_api_module_names(): | ||
| return _module_names("limited_api", _PY_ABI3_VERSIONS) | ||
| def limited_api_cython_module_names(): | ||
| return _module_names("limited_api_cython", _PY_ABI3_VERSIONS) | ||
| @pytest.mark.skipif( | ||
| IS_WASM, reason="platform cannot start subprocesses" | ||
| ) | ||
| @pytest.mark.skipif( | ||
| sysconfig.get_config_var("Py_DEBUG"), | ||
@@ -96,63 +216,43 @@ reason=( | ||
| ) | ||
| @pytest.mark.xfail( | ||
| @pytest.mark.skipif( | ||
| NOGIL_BUILD, | ||
| reason="Py_GIL_DISABLED builds do not currently support the limited API", | ||
| reason="Py_GIL_DISABLED builds do not support abi3", | ||
| ) | ||
| def test_limited_api(install_temp): | ||
| """Test building a third-party C extension with the limited API | ||
| and building a cython extension with the limited API | ||
| """ | ||
| @pytest.mark.parametrize("module_name", limited_api_module_names()) | ||
| def test_limited_api_abi3(install_temp, module_name): | ||
| mod = importlib.import_module(module_name) | ||
| _check_api_module(mod) | ||
| import limited_api1 # Earliest (3.6) # noqa: F401 | ||
| import limited_api2 # cython # noqa: F401 | ||
| import limited_api_latest # Latest version (current Python) # noqa: F401 | ||
| @pytest.mark.skipif( | ||
| IS_WASM, reason="platform cannot start subprocesses" | ||
| ) | ||
| @pytest.mark.skipif( | ||
| sysconfig.get_config_var("Py_DEBUG"), | ||
| reason=( | ||
| "Py_LIMITED_API is incompatible with Py_DEBUG, Py_TRACE_REFS, " | ||
| "and Py_REF_DEBUG" | ||
| ), | ||
| ) | ||
| @pytest.mark.skipif( | ||
| NOGIL_BUILD, | ||
| reason="Py_GIL_DISABLED builds do not support abi3", | ||
| ) | ||
| @pytest.mark.parametrize("module_name", limited_api_cython_module_names()) | ||
| def test_limited_api_cython(install_temp, module_name): | ||
| mod = importlib.import_module(module_name) | ||
| _check_api_module(mod, cython=True) | ||
| @pytest.mark.skipif( | ||
| sys.version_info < (3, 15), reason="opaque PyObject requires Python 3.15+" | ||
| ) | ||
| @pytest.mark.skipif( | ||
| sys.platform == "win32" and not sysconfig.get_config_var('Py_GIL_DISABLED'), | ||
| reason=("Meson does not yet support building abi3t extensions on the " | ||
| "GIL-enabled build") | ||
| ) | ||
| def test_limited_opaque(install_temp): | ||
| import limited_api_opaque | ||
| import numpy as np | ||
| arr = np.ones((200, 200)) | ||
| assert limited_api_opaque.nonzero(arr) == 200 * 200 | ||
| # Test PyArray_ITER_NEXT / PyArray_ITER_DATA / PyArray_ITER_NOTDONE | ||
| arr = np.arange(12.0).reshape(3, 4) | ||
| assert limited_api_opaque.iter_next(arr) == 66.0 | ||
| # Test PyArray_ITER_GOTO1D | ||
| assert limited_api_opaque.iter_goto1d(arr, 5) == 5.0 | ||
| assert limited_api_opaque.iter_goto1d(arr, -1) == 11.0 | ||
| # Test PyArray_ITER_RESET | ||
| assert limited_api_opaque.iter_reset(arr) == 66.0 | ||
| # Test PyArray_MultiIter_NEXT / RESET / DATA with broadcasting | ||
| a = np.arange(3.0).reshape(3, 1) # shape (3, 1) | ||
| b = np.arange(4.0).reshape(1, 4) # shape (1, 4) | ||
| # Each broadcast element is a[i] + b[j], total sum: | ||
| expected = float(np.sum(a + b)) | ||
| assert limited_api_opaque.multi_iter_next(a, b) == expected | ||
| # Test PyArray_ITER_GOTO | ||
| arr = np.arange(12.0).reshape(3, 4) | ||
| assert limited_api_opaque.iter_goto(arr, (1, 2)) == 6.0 | ||
| assert limited_api_opaque.iter_goto(arr, (2, 3)) == 11.0 | ||
| # Test PyArray_MultiIter_GOTO | ||
| a = np.arange(3.0).reshape(3, 1) | ||
| b = np.arange(4.0).reshape(1, 4) | ||
| va, vb = limited_api_opaque.multi_iter_goto(a, b, (1, 2)) | ||
| assert va == 1.0 and vb == 2.0 | ||
| # Test PyArray_MultiIter_GOTO1D | ||
| # flat index 6 in (3,4) broadcast → row 1, col 2 | ||
| va, vb = limited_api_opaque.multi_iter_goto1d(a, b, 6) | ||
| assert va == 1.0 and vb == 2.0 | ||
| # Test PyArray_MultiIter_NEXTi | ||
| a = np.arange(6.0).reshape(2, 3) | ||
| b = np.zeros((2, 3)) | ||
| # Advance iter 0 by 3 steps → flat index 3 → value 3.0 | ||
| assert limited_api_opaque.multi_iter_nexti(a, b, 3) == 3.0 | ||
| _check_api_module(limited_api_opaque) |
| import concurrent.futures | ||
| import inspect | ||
| import random | ||
| import subprocess | ||
@@ -43,2 +44,44 @@ import sys | ||
| def test_parallel_seed_get_state(): | ||
| seeds = [12345, 67890] | ||
| rng = np.random.RandomState() | ||
| expected = [] | ||
| for seed in seeds: | ||
| rng.seed(seed) | ||
| expected.append(rng.get_state()[1]) | ||
| def func(i, barrier): | ||
| rnd = random.Random(i) | ||
| barrier.wait() | ||
| for _ in range(100): | ||
| if rnd.randrange(2): | ||
| rng.seed(rnd.choice(seeds)) | ||
| else: | ||
| key = rng.get_state()[1] | ||
| assert any((key == exp).all() for exp in expected) | ||
| run_threaded(func, pass_count=True, pass_barrier=True) | ||
| @pytest.mark.parametrize( | ||
| "bitgen_name", ["MT19937", "PCG64", "PCG64DXSM", "Philox", "SFC64"]) | ||
| def test_parallel_bit_generator_state_access(bitgen_name): | ||
| bg = getattr(np.random, bitgen_name)(12345) | ||
| gen = np.random.Generator(bg) | ||
| def func(i, barrier): | ||
| rnd = random.Random(i) | ||
| barrier.wait() | ||
| for _ in range(100): | ||
| branch = rnd.randrange(3) | ||
| if branch == 0: | ||
| bg.state = bg.state | ||
| elif branch == 1: | ||
| gen.random(10) | ||
| elif hasattr(bg, "advance"): | ||
| bg.advance(10) | ||
| else: | ||
| _ = bg.state | ||
| run_threaded(func, pass_count=True, pass_barrier=True) | ||
| def test_parallel_ufunc_execution(): | ||
@@ -45,0 +88,0 @@ # if the loop data cache or dispatch cache are not thread-safe |
@@ -473,2 +473,28 @@ import itertools | ||
| def test_isdtype_string_dtype(self): | ||
| # gh-27545: StringDType has no NumPy scalar type, but it is a | ||
| # built-in dtype, so `isdtype` must accept it rather than raise | ||
| dt = np.dtypes.StringDType() | ||
| assert not np.isdtype(dt, "bool") | ||
| for kind in self.dtype_group_dict: | ||
| assert not np.isdtype(dt, kind) | ||
| # matches itself and the StringDType class, which stands in for | ||
| # the scalar type that identifies the other dtypes | ||
| assert np.isdtype(dt, dt) | ||
| assert np.isdtype(dt, np.dtypes.StringDType) | ||
| assert np.isdtype(np.dtypes.StringDType, dt) | ||
| assert np.isdtype(dt, ("numeric", np.dtypes.StringDType)) | ||
| assert not np.isdtype(np.int64, dt) | ||
| assert not np.isdtype(dt, ("integral", np.int64)) | ||
| # dtype parameters are ignored, like datetime64 units | ||
| assert np.isdtype(np.dtypes.StringDType(na_object=None), dt) | ||
| assert np.isdtype(dt, np.dtypes.StringDType(na_object=np.nan, | ||
| coerce=False)) | ||
| # fixed-width unicode strings are a different kind | ||
| assert not np.isdtype(dt, np.str_) | ||
| assert not np.isdtype(np.dtype("U8"), dt) | ||
| def test_sctypes_complete(self): | ||
@@ -475,0 +501,0 @@ # issue 26439: int32/intc were masking each other on 32-bit builds |
@@ -249,3 +249,3 @@ from _typeshed import Incomplete | ||
| weights: _ArrayLikeFloat64_co | None = None, | ||
| ) -> tuple[NDArray[np.float64], tuple[_Array1D[np.float64], ...]]: ... | ||
| ) -> tuple[NDArray[np.float64], list[_Array1D[np.float64]]]: ... | ||
| @overload # dtype ~complex | ||
@@ -258,3 +258,3 @@ def histogramdd( | ||
| weights: _ArrayLikeFloat64_co | None = None, | ||
| ) -> tuple[NDArray[np.float64], tuple[_Array1D[np.complex128], ...]]: ... | ||
| ) -> tuple[NDArray[np.float64], list[_Array1D[np.complex128]]]: ... | ||
| @overload # dtype known | ||
@@ -267,3 +267,3 @@ def histogramdd[ScalarT: np.inexact]( | ||
| weights: _ArrayLikeFloat64_co | None = None, | ||
| ) -> tuple[NDArray[np.float64], tuple[_Array1D[ScalarT], ...]]: ... | ||
| ) -> tuple[NDArray[np.float64], list[_Array1D[ScalarT]]]: ... | ||
| @overload # dtype unknown | ||
@@ -276,2 +276,2 @@ def histogramdd( | ||
| weights: _ArrayLikeFloat64_co | None = None, | ||
| ) -> tuple[NDArray[np.float64], tuple[_Array1D[Any], ...]]: ... | ||
| ) -> tuple[NDArray[np.float64], list[_Array1D[Any]]]: ... |
@@ -43,2 +43,4 @@ from _typeshed import Incomplete | ||
| type _AtLeast2D = tuple[int, int, *tuple[int, ...]] # input only | ||
| type _Array1D[ScalarT: np.generic] = np.ndarray[tuple[int], np.dtype[ScalarT]] | ||
@@ -196,4 +198,6 @@ type _Array2D[ScalarT: np.generic] = np.ndarray[tuple[int, int], np.dtype[ScalarT]] | ||
| @overload | ||
| def tril[ArrayT: np.ndarray](m: ArrayT, k: int = 0) -> ArrayT: ... | ||
| def tril[ArrayT: np.ndarray[_AtLeast2D]](m: ArrayT, k: int = 0) -> ArrayT: ... | ||
| @overload | ||
| def tril[DTypeT: np.dtype](m: np.ndarray[tuple[int], DTypeT], k: int = 0) -> np.ndarray[tuple[int, int], DTypeT]: ... | ||
| @overload | ||
| def tril[ScalarT: np.generic](m: _ArrayLike[ScalarT], k: int = 0) -> NDArray[ScalarT]: ... | ||
@@ -205,4 +209,6 @@ @overload | ||
| @overload | ||
| def triu[ArrayT: np.ndarray](m: ArrayT, k: int = 0) -> ArrayT: ... | ||
| def triu[ArrayT: np.ndarray[_AtLeast2D]](m: ArrayT, k: int = 0) -> ArrayT: ... | ||
| @overload | ||
| def triu[DTypeT: np.dtype](m: np.ndarray[tuple[int], DTypeT], k: int = 0) -> np.ndarray[tuple[int, int], DTypeT]: ... | ||
| @overload | ||
| def triu[ScalarT: np.generic](m: _ArrayLike[ScalarT], k: int = 0) -> NDArray[ScalarT]: ... | ||
@@ -209,0 +215,0 @@ @overload |
@@ -207,2 +207,12 @@ import pytest | ||
| def test_unravel_index_buffer_boundary(self): | ||
| indices = np.arange(10_000, dtype=np.intp)[:, None] | ||
| coords = np.unravel_index(indices, (100, 100)) | ||
| assert_array_equal( | ||
| np.ravel_multi_index(coords, (100, 100)), | ||
| indices, | ||
| ) | ||
| class TestGrid: | ||
@@ -209,0 +219,0 @@ def test_basic(self): |
+31
-25
@@ -199,4 +199,4 @@ from collections.abc import Iterable, Sequence | ||
| class SlogdetResult(NamedTuple, Generic[_FloatingOrArrayT_co, _InexactOrArrayT_co]): | ||
| sign: _FloatingOrArrayT_co | ||
| logabsdet: _InexactOrArrayT_co | ||
| sign: _InexactOrArrayT_co | ||
| logabsdet: _FloatingOrArrayT_co | ||
@@ -966,39 +966,45 @@ # keep in sync with `solve` | ||
| ) -> Any: ... | ||
| @overload # 2d known dtype, dtype=None | ||
| def trace[ScalarT: _to_complex](x: _ArrayLike2D[ScalarT], /, *, offset: SupportsIndex = 0, dtype: None = None) -> ScalarT: ... | ||
| @overload # 2d known dtype | ||
| def trace[ScalarT: np.inexact](x: _ArrayLike2D[ScalarT], /, *, offset: SupportsIndex = 0, dtype: None = None) -> ScalarT: ... | ||
| @overload # 2d, dtype=<given> | ||
| def trace[ScalarT: _to_complex]( | ||
| x: _ToArrayComplex_2d, /, *, offset: SupportsIndex = 0, dtype: _DTypeLike[ScalarT] | ||
| ) -> ScalarT: ... | ||
| @overload # 2d bool | ||
| def trace(x: _Sequence2D[bool], /, *, offset: SupportsIndex = 0, dtype: None = None) -> np.bool: ... | ||
| @overload # 2d int | ||
| def trace(x: Sequence[list[int]], /, *, offset: SupportsIndex = 0, dtype: None = None) -> np.int_: ... | ||
| @overload # 2d float | ||
| def trace[ScalarT: np.number](x: _ToArrayComplex_2d, /, *, offset: SupportsIndex = 0, dtype: _DTypeLike[ScalarT]) -> ScalarT: ... | ||
| @overload # 2d +int | ||
| def trace( | ||
| x: _ArrayLike2D[np.integer | np.bool] | Sequence[Sequence[int]], /, *, offset: SupportsIndex = 0, dtype: None = None | ||
| ) -> np.int_: ... | ||
| @overload # 2d ~float | ||
| def trace(x: Sequence[list[float]], /, *, offset: SupportsIndex = 0, dtype: None = None) -> np.float64: ... | ||
| @overload # 2d complex | ||
| @overload # 2d ~complex | ||
| def trace(x: Sequence[list[complex]], /, *, offset: SupportsIndex = 0, dtype: None = None) -> np.complex128: ... | ||
| @overload # 3d known dtype, dtype=None | ||
| def trace[DTypeT: np.dtype[_to_complex]]( | ||
| @overload # 3d known dtype | ||
| def trace[DTypeT: np.dtype[np.inexact]]( | ||
| x: _SupportsArray[tuple[int, int, int], DTypeT], /, *, offset: SupportsIndex = 0, dtype: None = None | ||
| ) -> np.ndarray[tuple[int], DTypeT]: ... | ||
| @overload # 3d +int | ||
| def trace( | ||
| x: _SupportsArray[tuple[int, int, int], np.dtype[np.integer | np.bool]], /, *, offset: SupportsIndex = 0, dtype: None = None | ||
| ) -> np.ndarray[tuple[int], np.dtype[np.int_]]: ... | ||
| @overload # 3d, dtype=<given> | ||
| def trace[ScalarT: _to_complex]( | ||
| def trace[ScalarT: np.number]( | ||
| x: _ToArrayComplex_3d, /, *, offset: SupportsIndex = 0, dtype: _DTypeLike[ScalarT] | ||
| ) -> _Array1D[ScalarT]: ... | ||
| @overload # 3d+ known dtype, dtype=None | ||
| def trace[DTypeT: np.dtype[_to_complex]]( | ||
| @overload # 3d+ known dtype | ||
| def trace[DTypeT: np.dtype[np.inexact]]( | ||
| x: _SupportsArray[_AtLeast3D, DTypeT], /, *, offset: SupportsIndex = 0, dtype: None = None | ||
| ) -> np.ndarray[tuple[int, *tuple[Any, ...]], DTypeT]: ... | ||
| @overload # 3d+, dtype=<given> | ||
| def trace[ScalarT: _to_complex]( | ||
| def trace[ScalarT: np.inexact]( | ||
| x: _ArrayLike3ND[_to_complex] | _Sequence3ND[complex], /, *, offset: SupportsIndex = 0, dtype: _DTypeLike[ScalarT] | ||
| ) -> np.ndarray[tuple[int, *tuple[Any, ...]], np.dtype[ScalarT]]: ... | ||
| @overload # 3d+ bool | ||
| def trace(x: _Sequence3ND[bool], /, *, offset: SupportsIndex = 0, dtype: None = None) -> NDArray[np.bool]: ... | ||
| @overload # 3d+ int | ||
| def trace(x: _Sequence2ND[list[int]], /, *, offset: SupportsIndex = 0, dtype: None = None) -> NDArray[np.int_]: ... | ||
| @overload # 3d+ float | ||
| @overload # 3d+ +integer | ||
| def trace( | ||
| x: _SupportsArray[_AtLeast3D, np.dtype[np.integer | np.bool]] | _Sequence2ND[Sequence[int]], | ||
| /, | ||
| *, | ||
| offset: SupportsIndex = 0, | ||
| dtype: None = None, | ||
| ) -> NDArray[np.int_]: ... | ||
| @overload # 3d+ ~float | ||
| def trace(x: _Sequence2ND[list[float]], /, *, offset: SupportsIndex = 0, dtype: None = None) -> NDArray[np.float64]: ... | ||
| @overload # 3d+ complex | ||
| @overload # 3d+ ~complex | ||
| def trace(x: _Sequence2ND[list[complex]], /, *, offset: SupportsIndex = 0, dtype: None = None) -> NDArray[np.complex128]: ... | ||
@@ -1005,0 +1011,0 @@ @overload # fallback |
@@ -295,3 +295,6 @@ from _typeshed import Incomplete, SupportsLenAndGetItem | ||
| # | ||
| def count_masked(arr: ArrayLike, axis: SupportsIndex | None = None) -> NDArray[np.intp]: ... | ||
| @overload | ||
| def count_masked(arr: ArrayLike, axis: None = None) -> np.intp: ... | ||
| @overload | ||
| def count_masked(arr: ArrayLike, axis: SupportsIndex) -> NDArray[np.intp]: ... | ||
@@ -298,0 +301,0 @@ # |
@@ -691,7 +691,7 @@ # Aliases for builtins shadowed by classes to avoid annotations resolving to class members by ty | ||
| # | ||
| @overload # >=0d int, size=None (default) | ||
| @overload # 0d int, size=None (default) | ||
| def choice( | ||
| self, | ||
| /, | ||
| a: int | _NestedSequence[int], | ||
| a: int, | ||
| size: None = None, | ||
@@ -703,2 +703,13 @@ replace: bool = True, | ||
| ) -> int: ... | ||
| @overload # >0d int, size=None (default) | ||
| def choice( | ||
| self, | ||
| /, | ||
| a: _NestedSequence[int], | ||
| size: None = None, | ||
| replace: bool = True, | ||
| p: _ArrayLikeFloat_co | None = None, | ||
| axis: int = 0, | ||
| shuffle: bool = True, | ||
| ) -> np.int_: ... | ||
| @overload # >=0d known, size=None (default) | ||
@@ -705,0 +716,0 @@ def choice[ScalarT: np.generic]( |
@@ -195,3 +195,3 @@ #cython: binding=True | ||
| mt19937_init_by_array(&self.rng_state, <uint32_t*> obj.data, np.PyArray_DIM(obj, 0)) | ||
| self._seed_seq = None | ||
| self._seed_seq = None | ||
@@ -270,7 +270,8 @@ cdef jump_inplace(self, iter): | ||
| key = np.zeros(624, dtype=np.uint32) | ||
| for i in range(624): | ||
| key[i] = self.rng_state.key[i] | ||
| with self.lock: | ||
| for i in range(624): | ||
| key[i] = self.rng_state.key[i] | ||
| return {'bit_generator': self.__class__.__name__, | ||
| 'state': {'key': key, 'pos': self.rng_state.pos}} | ||
| return {'bit_generator': self.__class__.__name__, | ||
| 'state': {'key': key, 'pos': self.rng_state.pos}} | ||
@@ -291,4 +292,5 @@ @state.setter | ||
| key = value['state']['key'] | ||
| for i in range(624): | ||
| self.rng_state.key[i] = key[i] | ||
| self.rng_state.pos = value['state']['pos'] | ||
| with self.lock: | ||
| for i in range(624): | ||
| self.rng_state.key[i] = key[i] | ||
| self.rng_state.pos = value['state']['pos'] |
+22
-16
@@ -209,5 +209,6 @@ #cython: binding=True | ||
| state_vec = <np.ndarray>np.empty(4, dtype=np.uint64) | ||
| pcg64_get_state(&self.rng_state, | ||
| <uint64_t *>np.PyArray_DATA(state_vec), | ||
| &has_uint32, &uinteger) | ||
| with self.lock: | ||
| pcg64_get_state(&self.rng_state, | ||
| <uint64_t *>np.PyArray_DATA(state_vec), | ||
| &has_uint32, &uinteger) | ||
| state = int(state_vec[0]) * 2**64 + int(state_vec[1]) | ||
@@ -237,5 +238,6 @@ inc = int(state_vec[2]) * 2**64 + int(state_vec[3]) | ||
| uinteger = value['uinteger'] | ||
| pcg64_set_state(&self.rng_state, | ||
| <uint64_t *>np.PyArray_DATA(state_vec), | ||
| has_uint32, uinteger) | ||
| with self.lock: | ||
| pcg64_set_state(&self.rng_state, | ||
| <uint64_t *>np.PyArray_DATA(state_vec), | ||
| has_uint32, uinteger) | ||
@@ -283,4 +285,5 @@ def advance(self, delta): | ||
| d[1] = delta % 2**64 | ||
| pcg64_advance(&self.rng_state, <uint64_t *>np.PyArray_DATA(d)) | ||
| self._reset_state_variables() | ||
| with self.lock: | ||
| pcg64_advance(&self.rng_state, <uint64_t *>np.PyArray_DATA(d)) | ||
| self._reset_state_variables() | ||
| return self | ||
@@ -446,5 +449,6 @@ | ||
| state_vec = <np.ndarray>np.empty(4, dtype=np.uint64) | ||
| pcg64_get_state(&self.rng_state, | ||
| <uint64_t *>np.PyArray_DATA(state_vec), | ||
| &has_uint32, &uinteger) | ||
| with self.lock: | ||
| pcg64_get_state(&self.rng_state, | ||
| <uint64_t *>np.PyArray_DATA(state_vec), | ||
| &has_uint32, &uinteger) | ||
| state = int(state_vec[0]) * 2**64 + int(state_vec[1]) | ||
@@ -474,5 +478,6 @@ inc = int(state_vec[2]) * 2**64 + int(state_vec[3]) | ||
| uinteger = value['uinteger'] | ||
| pcg64_set_state(&self.rng_state, | ||
| <uint64_t *>np.PyArray_DATA(state_vec), | ||
| has_uint32, uinteger) | ||
| with self.lock: | ||
| pcg64_set_state(&self.rng_state, | ||
| <uint64_t *>np.PyArray_DATA(state_vec), | ||
| has_uint32, uinteger) | ||
@@ -520,4 +525,5 @@ def advance(self, delta): | ||
| d[1] = delta % 2**64 | ||
| pcg64_cm_advance(&self.rng_state, <uint64_t *>np.PyArray_DATA(d)) | ||
| self._reset_state_variables() | ||
| with self.lock: | ||
| pcg64_cm_advance(&self.rng_state, <uint64_t *>np.PyArray_DATA(d)) | ||
| self._reset_state_variables() | ||
| return self |
+27
-24
@@ -218,16 +218,17 @@ #cython: binding=True | ||
| buffer = np.empty(PHILOX_BUFFER_SIZE, dtype=np.uint64) | ||
| for i in range(4): | ||
| ctr[i] = self.rng_state.ctr.v[i] | ||
| if i < 2: | ||
| key[i] = self.rng_state.key.v[i] | ||
| for i in range(PHILOX_BUFFER_SIZE): | ||
| buffer[i] = self.rng_state.buffer[i] | ||
| with self.lock: | ||
| for i in range(4): | ||
| ctr[i] = self.rng_state.ctr.v[i] | ||
| if i < 2: | ||
| key[i] = self.rng_state.key.v[i] | ||
| for i in range(PHILOX_BUFFER_SIZE): | ||
| buffer[i] = self.rng_state.buffer[i] | ||
| state = {'counter': ctr, 'key': key} | ||
| return {'bit_generator': self.__class__.__name__, | ||
| 'state': state, | ||
| 'buffer': buffer, | ||
| 'buffer_pos': self.rng_state.buffer_pos, | ||
| 'has_uint32': self.rng_state.has_uint32, | ||
| 'uinteger': self.rng_state.uinteger} | ||
| state = {'counter': ctr, 'key': key} | ||
| return {'bit_generator': self.__class__.__name__, | ||
| 'state': state, | ||
| 'buffer': buffer, | ||
| 'buffer_pos': self.rng_state.buffer_pos, | ||
| 'has_uint32': self.rng_state.has_uint32, | ||
| 'uinteger': self.rng_state.uinteger} | ||
@@ -241,12 +242,13 @@ @state.setter | ||
| raise ValueError(f'state must be for a {self.__class__.__name__} PRNG') | ||
| for i in range(4): | ||
| self.rng_state.ctr.v[i] = <uint64_t> value['state']['counter'][i] | ||
| if i < 2: | ||
| self.rng_state.key.v[i] = <uint64_t> value['state']['key'][i] | ||
| for i in range(PHILOX_BUFFER_SIZE): | ||
| self.rng_state.buffer[i] = <uint64_t> value['buffer'][i] | ||
| with self.lock: | ||
| for i in range(4): | ||
| self.rng_state.ctr.v[i] = <uint64_t> value['state']['counter'][i] | ||
| if i < 2: | ||
| self.rng_state.key.v[i] = <uint64_t> value['state']['key'][i] | ||
| for i in range(PHILOX_BUFFER_SIZE): | ||
| self.rng_state.buffer[i] = <uint64_t> value['buffer'][i] | ||
| self.rng_state.has_uint32 = value['has_uint32'] | ||
| self.rng_state.uinteger = value['uinteger'] | ||
| self.rng_state.buffer_pos = value['buffer_pos'] | ||
| self.rng_state.has_uint32 = value['has_uint32'] | ||
| self.rng_state.uinteger = value['uinteger'] | ||
| self.rng_state.buffer_pos = value['buffer_pos'] | ||
@@ -333,4 +335,5 @@ cdef jump_inplace(self, iter): | ||
| delta_a = int_to_array(delta, 'step', 256, 64) | ||
| philox_advance(<uint64_t *> delta_a.data, &self.rng_state) | ||
| self._reset_state_variables() | ||
| with self.lock: | ||
| philox_advance(<uint64_t *> delta_a.data, &self.rng_state) | ||
| self._reset_state_variables() | ||
| return self |
@@ -121,5 +121,6 @@ #cython: binding=True | ||
| state_vec = <np.ndarray>np.empty(4, dtype=np.uint64) | ||
| sfc64_get_state(&self.rng_state, | ||
| <uint64_t *>np.PyArray_DATA(state_vec), | ||
| &has_uint32, &uinteger) | ||
| with self.lock: | ||
| sfc64_get_state(&self.rng_state, | ||
| <uint64_t *>np.PyArray_DATA(state_vec), | ||
| &has_uint32, &uinteger) | ||
| return {'bit_generator': self.__class__.__name__, | ||
@@ -144,4 +145,5 @@ 'state': {'state': state_vec}, | ||
| uinteger = value['uinteger'] | ||
| sfc64_set_state(&self.rng_state, | ||
| <uint64_t *>np.PyArray_DATA(state_vec), | ||
| has_uint32, uinteger) | ||
| with self.lock: | ||
| sfc64_set_state(&self.rng_state, | ||
| <uint64_t *>np.PyArray_DATA(state_vec), | ||
| has_uint32, uinteger) |
@@ -100,5 +100,5 @@ from builtins import bytes as py_bytes | ||
| @overload | ||
| def get_state(self, legacy: Literal[False] = False) -> dict[str, Any]: ... | ||
| def get_state(self, legacy: Literal[False]) -> dict[str, Any]: ... | ||
| @overload | ||
| def get_state(self, legacy: Literal[True] = True) -> dict[str, Any] | tuple[str, NDArray[np.uint32], int, int, float]: ... | ||
| def get_state(self, legacy: Literal[True] = True) -> tuple[str, NDArray[np.uint32], int, int, float]: ... | ||
@@ -105,0 +105,0 @@ # |
@@ -41,3 +41,3 @@ import os | ||
| # Note: keep in sync with the one in pyproject.toml | ||
| required_version = '3.0.6' | ||
| required_version = '3.1.0' | ||
| if _pep440.parse(cython_version) < _pep440.Version(required_version): | ||
@@ -44,0 +44,0 @@ # too old or wrong cython, skip the test |
@@ -27,4 +27,4 @@ from typing import Any | ||
| np.piecewise(AR_f8, True, [fn_ar_i], "wrong") # type: ignore[call-overload] | ||
| np.piecewise(AR_f8, AR_b_list, [fn_none_i]) # type: ignore[call-overload] | ||
| np.piecewise(AR_f8, AR_b_list, [fn_ar_i]) # type: ignore[call-overload] | ||
| np.piecewise(AR_f8, AR_b_list, [fn_none_i]) # type: ignore[list-item] | ||
| np.piecewise(AR_f8, AR_b_list, [fn_ar_i]) # type: ignore[list-item] | ||
| np.piecewise(AR_f8, AR_b_list, [fn_ar_i], 3.14) # type: ignore[call-overload] | ||
@@ -31,0 +31,0 @@ np.piecewise(AR_f8, AR_b_list, [fn_ar_i], 42, None) # type: ignore[call-overload] |
@@ -6,4 +6,3 @@ import numpy as np | ||
| np.nditer([0, 1], flags=["test"]) # type: ignore[list-item] | ||
| np.nditer([0, 1], op_flags=[["test"]]) # type: ignore[list-item] | ||
| np.nditer([0, 1], itershape=(1.0,)) # type: ignore[arg-type] | ||
| np.nditer([0, 1], buffersize=1.0) # type: ignore[call-overload] |
@@ -16,3 +16,5 @@ """Tests for :mod:`_core.fromnumeric`.""" | ||
| AR_f4_3d: np.ndarray[tuple[int, int, int], np.dtype[np.float32]] | ||
| AR_c8: npt.NDArray[np.complex64] | ||
| AR_c16: npt.NDArray[np.complex128] | ||
| AR_i1: npt.NDArray[np.int8] | ||
| AR_u8: npt.NDArray[np.uint64] | ||
@@ -246,7 +248,9 @@ AR_i8: npt.NDArray[np.int64] | ||
| assert_type(np.cumsum(b), np.ndarray[tuple[int], np.dtype[np.bool]]) | ||
| assert_type(np.cumsum(b), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(np.cumsum(f4), np.ndarray[tuple[int], np.dtype[np.float32]]) | ||
| assert_type(np.cumsum(f), np.ndarray[tuple[int]]) | ||
| assert_type(np.cumsum(AR_b), np.ndarray[tuple[int], np.dtype[np.bool]]) | ||
| assert_type(np.cumsum(AR_b, axis=0), npt.NDArray[np.bool]) | ||
| assert_type(np.cumsum(AR_b), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(np.cumsum(AR_b, axis=0), npt.NDArray[np.int_]) | ||
| assert_type(np.cumsum(AR_i1), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(np.cumsum(AR_i1, axis=0), npt.NDArray[np.int_]) | ||
| assert_type(np.cumsum(AR_f4), np.ndarray[tuple[int], np.dtype[np.float32]]) | ||
@@ -258,7 +262,9 @@ assert_type(np.cumsum(AR_f4, axis=0), npt.NDArray[np.float32]) | ||
| assert_type(np.cumulative_sum(b), np.ndarray[tuple[int], np.dtype[np.bool]]) | ||
| assert_type(np.cumulative_sum(b), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(np.cumulative_sum(f4), np.ndarray[tuple[int], np.dtype[np.float32]]) | ||
| assert_type(np.cumulative_sum(f), np.ndarray[tuple[int]]) | ||
| assert_type(np.cumulative_sum(AR_b), np.ndarray[tuple[int], np.dtype[np.bool]]) | ||
| assert_type(np.cumulative_sum(AR_b, axis=0), npt.NDArray[np.bool]) | ||
| assert_type(np.cumulative_sum(AR_b), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(np.cumulative_sum(AR_b, axis=0), npt.NDArray[np.int_]) | ||
| assert_type(np.cumulative_sum(AR_i1), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(np.cumulative_sum(AR_i1, axis=0), npt.NDArray[np.int_]) | ||
| assert_type(np.cumulative_sum(AR_f4), np.ndarray[tuple[int], np.dtype[np.float32]]) | ||
@@ -348,7 +354,9 @@ assert_type(np.cumulative_sum(AR_f4, axis=0), npt.NDArray[np.float32]) | ||
| assert_type(np.cumprod(b), np.ndarray[tuple[int], np.dtype[np.bool]]) | ||
| assert_type(np.cumprod(b), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(np.cumprod(f4), np.ndarray[tuple[int], np.dtype[np.float32]]) | ||
| assert_type(np.cumprod(f), np.ndarray[tuple[int]]) | ||
| assert_type(np.cumprod(AR_b), np.ndarray[tuple[int], np.dtype[np.bool]]) | ||
| assert_type(np.cumprod(AR_b, axis=0), npt.NDArray[np.bool]) | ||
| assert_type(np.cumprod(AR_b), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(np.cumprod(AR_b, axis=0), npt.NDArray[np.int_]) | ||
| assert_type(np.cumprod(AR_i1), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(np.cumprod(AR_i1, axis=0), npt.NDArray[np.int_]) | ||
| assert_type(np.cumprod(AR_f4), np.ndarray[tuple[int], np.dtype[np.float32]]) | ||
@@ -360,7 +368,9 @@ assert_type(np.cumprod(AR_f4, axis=0), npt.NDArray[np.float32]) | ||
| assert_type(np.cumulative_prod(b), np.ndarray[tuple[int], np.dtype[np.bool]]) | ||
| assert_type(np.cumulative_prod(b), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(np.cumulative_prod(f4), np.ndarray[tuple[int], np.dtype[np.float32]]) | ||
| assert_type(np.cumulative_prod(f), np.ndarray[tuple[int]]) | ||
| assert_type(np.cumulative_prod(AR_b), np.ndarray[tuple[int], np.dtype[np.bool]]) | ||
| assert_type(np.cumulative_prod(AR_b, axis=0), npt.NDArray[np.bool]) | ||
| assert_type(np.cumulative_prod(AR_b), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(np.cumulative_prod(AR_b, axis=0), npt.NDArray[np.int_]) | ||
| assert_type(np.cumulative_prod(AR_i1), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(np.cumulative_prod(AR_i1, axis=0), npt.NDArray[np.int_]) | ||
| assert_type(np.cumulative_prod(AR_f4), np.ndarray[tuple[int], np.dtype[np.float32]]) | ||
@@ -397,6 +407,10 @@ assert_type(np.cumulative_prod(AR_f4, axis=0), npt.NDArray[np.float32]) | ||
| assert_type(np.prod(AR_b), np.int_) | ||
| assert_type(np.prod(AR_i8), np.int64) | ||
| assert_type(np.prod(AR_i8, axis=0), npt.NDArray[np.int64]) | ||
| assert_type(np.prod(AR_i8, keepdims=True), npt.NDArray[np.int64]) | ||
| assert_type(np.prod(AR_i8, axis=0, keepdims=True), npt.NDArray[np.int64]) | ||
| assert_type(np.prod(AR_i1), np.int_) | ||
| assert_type(np.prod(AR_i1, axis=0), npt.NDArray[np.int_]) | ||
| assert_type(np.prod(AR_i1, keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(np.prod(AR_i1, axis=0, keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(np.prod(AR_i8), np.int_) | ||
| assert_type(np.prod(AR_i8, axis=0), npt.NDArray[np.int_]) | ||
| assert_type(np.prod(AR_i8, keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(np.prod(AR_i8, axis=0, keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(np.prod(AR_f4), np.float32) | ||
@@ -427,6 +441,10 @@ assert_type(np.prod(AR_c16), np.complex128) | ||
| assert_type(np.sum(AR_b), np.int_) | ||
| assert_type(np.sum(AR_i8), np.int64) | ||
| assert_type(np.sum(AR_i8, axis=0), npt.NDArray[np.int64]) | ||
| assert_type(np.sum(AR_i8, keepdims=True), npt.NDArray[np.int64]) | ||
| assert_type(np.sum(AR_i8, axis=0, keepdims=True), npt.NDArray[np.int64]) | ||
| assert_type(np.sum(AR_i1), np.int_) | ||
| assert_type(np.sum(AR_i1, axis=0), npt.NDArray[np.int_]) | ||
| assert_type(np.sum(AR_i1, keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(np.sum(AR_i1, axis=0, keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(np.sum(AR_i8), np.int_) | ||
| assert_type(np.sum(AR_i8, axis=0), npt.NDArray[np.int_]) | ||
| assert_type(np.sum(AR_i8, keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(np.sum(AR_i8, axis=0, keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(np.sum(AR_f4), np.float32) | ||
@@ -495,3 +513,8 @@ assert_type(np.sum(AR_c16), np.complex128) | ||
| assert_type(np.std(AR_f4), np.float32) | ||
| assert_type(np.std(AR_c16), np.complex128) | ||
| assert_type(np.std(AR_c8), Any) | ||
| assert_type(np.std(AR_c8, axis=0), npt.NDArray[Any]) | ||
| assert_type(np.std(AR_c8, keepdims=True), npt.NDArray[Any]) | ||
| assert_type(np.std(AR_c16), np.float64) | ||
| assert_type(np.std(AR_c16, axis=0), npt.NDArray[np.float64]) | ||
| assert_type(np.std(AR_c16, keepdims=True), npt.NDArray[np.float64]) | ||
| assert_type(np.std(AR_O), np.float64) | ||
@@ -527,3 +550,8 @@ assert_type(np.std(AR_O, axis=0), npt.NDArray[np.object_]) | ||
| assert_type(np.var(AR_f4), np.float32) | ||
| assert_type(np.var(AR_c16), np.complex128) | ||
| assert_type(np.var(AR_c8), Any) | ||
| assert_type(np.var(AR_c8, axis=0), npt.NDArray[Any]) | ||
| assert_type(np.var(AR_c8, keepdims=True), npt.NDArray[Any]) | ||
| assert_type(np.var(AR_c16), np.float64) | ||
| assert_type(np.var(AR_c16, axis=0), npt.NDArray[np.float64]) | ||
| assert_type(np.var(AR_c16, keepdims=True), npt.NDArray[np.float64]) | ||
| assert_type(np.var(AR_O), np.float64) | ||
@@ -530,0 +558,0 @@ assert_type(np.var(AR_O, axis=0), npt.NDArray[np.object_]) |
@@ -61,10 +61,10 @@ from typing import Any, assert_type | ||
| assert_type(np.histogramdd(AR_i8, bins=[1]), tuple[npt.NDArray[np.float64], tuple[_Array1D[np.float64], ...]]) | ||
| assert_type(np.histogramdd(AR_i8, range=[(0, 3)]), tuple[npt.NDArray[np.float64], tuple[_Array1D[np.float64], ...]]) | ||
| assert_type(np.histogramdd(AR_i8, weights=AR_f8), tuple[npt.NDArray[np.float64], tuple[_Array1D[np.float64], ...]]) | ||
| assert_type(np.histogramdd(AR_f8, density=True), tuple[npt.NDArray[np.float64], tuple[_Array1D[np.float64], ...]]) | ||
| assert_type(np.histogramdd(AR_i4), tuple[npt.NDArray[np.float64], tuple[_Array1D[np.float64], ...]]) | ||
| assert_type(np.histogramdd(AR_i8), tuple[npt.NDArray[np.float64], tuple[_Array1D[np.float64], ...]]) | ||
| assert_type(np.histogramdd(AR_f4), tuple[npt.NDArray[np.float64], tuple[_Array1D[np.float32], ...]]) | ||
| assert_type(np.histogramdd(AR_c8), tuple[npt.NDArray[np.float64], tuple[_Array1D[np.complex64], ...]]) | ||
| assert_type(np.histogramdd(AR_c16), tuple[npt.NDArray[np.float64], tuple[_Array1D[np.complex128], ...]]) | ||
| assert_type(np.histogramdd(AR_i8, bins=[1]), tuple[npt.NDArray[np.float64], list[_Array1D[np.float64]]]) | ||
| assert_type(np.histogramdd(AR_i8, range=[(0, 3)]), tuple[npt.NDArray[np.float64], list[_Array1D[np.float64]]]) | ||
| assert_type(np.histogramdd(AR_i8, weights=AR_f8), tuple[npt.NDArray[np.float64], list[_Array1D[np.float64]]]) | ||
| assert_type(np.histogramdd(AR_f8, density=True), tuple[npt.NDArray[np.float64], list[_Array1D[np.float64]]]) | ||
| assert_type(np.histogramdd(AR_i4), tuple[npt.NDArray[np.float64], list[_Array1D[np.float64]]]) | ||
| assert_type(np.histogramdd(AR_i8), tuple[npt.NDArray[np.float64], list[_Array1D[np.float64]]]) | ||
| assert_type(np.histogramdd(AR_f4), tuple[npt.NDArray[np.float64], list[_Array1D[np.float32]]]) | ||
| assert_type(np.histogramdd(AR_c8), tuple[npt.NDArray[np.float64], list[_Array1D[np.complex64]]]) | ||
| assert_type(np.histogramdd(AR_c16), tuple[npt.NDArray[np.float64], list[_Array1D[np.complex128]]]) |
@@ -106,2 +106,3 @@ from collections.abc import Callable | ||
| # piecewise | ||
| assert_type(np.piecewise(AR_f8_1d, AR_b, [-1.0, 1.0]), np.ndarray[tuple[int], np.dtype[np.float64]]) | ||
| assert_type(np.piecewise(AR_f8_1d, AR_b, [func]), np.ndarray[tuple[int], np.dtype[np.float64]]) | ||
@@ -220,2 +221,3 @@ assert_type(np.piecewise(AR_f8, AR_b, [func]), npt.NDArray[np.float64]) | ||
| assert_type(np.sort_complex(AR_c16_1d), np.ndarray[tuple[int], np.dtype[np.complex128]]) | ||
| assert_type(np.sort_complex(AR_LIKE_f8), npt.NDArray[np.complex128]) | ||
@@ -222,0 +224,0 @@ # trim_zeros |
@@ -19,4 +19,6 @@ from typing import Any, Literal, assert_type | ||
| bool_list_2d: list[list[bool]] | ||
| bool_list_3d: list[list[list[bool]]] | ||
| int_list_1d: list[int] | ||
| int_list_2d: list[list[int]] | ||
| int_list_3d: list[list[list[int]]] | ||
| float_list_1d: list[float] | ||
@@ -63,2 +65,4 @@ float_list_2d: list[list[float]] | ||
| AR_i1_2d: _Array2D[np.int8] | ||
| AR_i1_3d: _Array3D[np.int8] | ||
| AR_f2_2d: _Array2D[np.float16] | ||
@@ -393,2 +397,4 @@ AR_f4_1d: _Array1D[np.float32] | ||
| assert_type(np.linalg.trace(AR_f4), Any) | ||
| assert_type(np.linalg.trace(AR_i1_2d), np.int_) | ||
| assert_type(np.linalg.trace(AR_i1_3d), _Array1D[np.int_]) | ||
| assert_type(np.linalg.trace(AR_f4_2d), np.float32) | ||
@@ -399,6 +405,8 @@ assert_type(np.linalg.trace(AR_f8_2d), np.float64) | ||
| assert_type(np.linalg.trace(AR_f8_4d), np.ndarray[tuple[int, *tuple[Any, ...]], np.dtype[np.float64]]) | ||
| assert_type(np.linalg.trace(bool_list_2d), np.bool) | ||
| assert_type(np.linalg.trace(bool_list_2d), np.int_) | ||
| assert_type(np.linalg.trace(int_list_2d), np.int_) | ||
| assert_type(np.linalg.trace(float_list_2d), np.float64) | ||
| assert_type(np.linalg.trace(complex_list_2d), np.complex128) | ||
| assert_type(np.linalg.trace(bool_list_3d), npt.NDArray[np.int_]) | ||
| assert_type(np.linalg.trace(int_list_3d), npt.NDArray[np.int_]) | ||
| assert_type(np.linalg.trace(float_list_3d), npt.NDArray[np.float64]) | ||
@@ -405,0 +413,0 @@ |
@@ -241,2 +241,5 @@ from typing import Any, Literal, NoReturn, assert_type | ||
| assert_type(np.ma.where(MAR_b), tuple[np.ndarray[tuple[int], np.dtype[np.intp]], ...]) | ||
| assert_type(np.ma.where(MAR_b, 0, 1), MaskedArray[Any]) | ||
| assert_type(MAR_f4 >= 3, MaskedArray[np.bool]) | ||
@@ -243,0 +246,0 @@ assert_type(MAR_i8 >= AR_td64, MaskedArray[np.bool]) |
@@ -224,2 +224,3 @@ import datetime as dt | ||
| assert_type(np.bincount(AR_i8), np.ndarray[tuple[int], np.dtype[np.intp]]) | ||
| assert_type(np.bincount(AR_i8, weights=AR_i8), np.ndarray[tuple[int], np.dtype[np.float64]]) | ||
@@ -226,0 +227,0 @@ assert_type(np.copyto(AR_f8, [1., 1.5, 1.6]), None) |
@@ -35,2 +35,3 @@ """ | ||
| AR_c8: npt.NDArray[np.complex64] | ||
| AR_c16: npt.NDArray[np.complex128] | ||
| AR_m: npt.NDArray[np.timedelta64] | ||
@@ -123,4 +124,6 @@ AR_U: npt.NDArray[np.str_] | ||
| assert_type(AR_i8.cumprod(), np.ndarray[tuple[int], np.dtype[np.int64]]) | ||
| assert_type(AR_i8.cumprod(axis=0), npt.NDArray[np.int64]) | ||
| assert_type(AR_u1.cumprod(), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(AR_u1.cumprod(axis=0), npt.NDArray[np.int_]) | ||
| assert_type(AR_i8.cumprod(), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(AR_i8.cumprod(axis=0), npt.NDArray[np.int_]) | ||
| assert_type(AR_f8.cumprod(), np.ndarray[tuple[int], np.dtype[np.float64]]) | ||
@@ -138,4 +141,6 @@ assert_type(AR_f8.cumprod(axis=0), npt.NDArray[np.float64]) | ||
| assert_type(AR_i8.cumsum(), np.ndarray[tuple[int], np.dtype[np.int64]]) | ||
| assert_type(AR_i8.cumsum(axis=0), npt.NDArray[np.int64]) | ||
| assert_type(AR_u1.cumsum(), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(AR_u1.cumsum(axis=0), npt.NDArray[np.int_]) | ||
| assert_type(AR_i8.cumsum(), np.ndarray[tuple[int], np.dtype[np.int_]]) | ||
| assert_type(AR_i8.cumsum(axis=0), npt.NDArray[np.int_]) | ||
| assert_type(AR_f8.cumsum(), np.ndarray[tuple[int], np.dtype[np.float64]]) | ||
@@ -157,6 +162,10 @@ assert_type(AR_f8.cumsum(axis=0), npt.NDArray[np.float64]) | ||
| assert_type(f8.prod(), np.float64) | ||
| assert_type(AR_i8.prod(), np.int64) | ||
| assert_type(AR_i8.prod(keepdims=True), npt.NDArray[np.int64]) | ||
| assert_type(AR_i8.prod(axis=0), npt.NDArray[np.int64]) | ||
| assert_type(AR_i8.prod(axis=0, keepdims=True), npt.NDArray[np.int64]) | ||
| assert_type(AR_u1.prod(), np.int_) | ||
| assert_type(AR_u1.prod(keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(AR_u1.prod(axis=0), npt.NDArray[np.int_]) | ||
| assert_type(AR_u1.prod(axis=0, keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(AR_i8.prod(), np.int_) | ||
| assert_type(AR_i8.prod(keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(AR_i8.prod(axis=0), npt.NDArray[np.int_]) | ||
| assert_type(AR_i8.prod(axis=0, keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(AR_f8.prod(), np.float64) | ||
@@ -188,6 +197,10 @@ assert_type(AR_f8.prod(keepdims=True), npt.NDArray[np.float64]) | ||
| assert_type(m8_na.sum(), np.timedelta64[None]) | ||
| assert_type(AR_i8.sum(), np.int64) | ||
| assert_type(AR_i8.sum(keepdims=True), npt.NDArray[np.int64]) | ||
| assert_type(AR_i8.sum(axis=0), npt.NDArray[np.int64]) | ||
| assert_type(AR_i8.sum(axis=0, keepdims=True), npt.NDArray[np.int64]) | ||
| assert_type(AR_u1.sum(), np.int_) | ||
| assert_type(AR_u1.sum(keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(AR_u1.sum(axis=0), npt.NDArray[np.int_]) | ||
| assert_type(AR_u1.sum(axis=0, keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(AR_i8.sum(), np.int_) | ||
| assert_type(AR_i8.sum(keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(AR_i8.sum(axis=0), npt.NDArray[np.int_]) | ||
| assert_type(AR_i8.sum(axis=0, keepdims=True), npt.NDArray[np.int_]) | ||
| assert_type(AR_f8.sum(), np.float64) | ||
@@ -232,3 +245,3 @@ assert_type(AR_f8.sum(keepdims=True), npt.NDArray[np.float64]) | ||
| # same as above | ||
| # as above, but returns floating for complexfloating input | ||
| assert_type(f8.std(), Any) | ||
@@ -239,2 +252,6 @@ assert_type(AR_f8.std(), np.float64) | ||
| assert_type(AR_f8.std(axis=0, keepdims=True), npt.NDArray[np.float64]) | ||
| assert_type(AR_c16.std(), np.float64) | ||
| assert_type(AR_c16.std(keepdims=True), npt.NDArray[np.float64]) | ||
| assert_type(AR_c16.std(axis=0), npt.NDArray[np.float64]) | ||
| assert_type(AR_c16.std(axis=0, keepdims=True), npt.NDArray[np.float64]) | ||
| assert_type(AR_f8.std(dtype=np.float32), np.float32) | ||
@@ -260,2 +277,6 @@ assert_type(AR_f8.std(dtype=np.float32, axis=0), npt.NDArray[np.float32]) | ||
| assert_type(AR_f8.var(axis=0, keepdims=True), npt.NDArray[np.float64]) | ||
| assert_type(AR_c16.var(), np.float64) | ||
| assert_type(AR_c16.var(keepdims=True), npt.NDArray[np.float64]) | ||
| assert_type(AR_c16.var(axis=0), npt.NDArray[np.float64]) | ||
| assert_type(AR_c16.var(axis=0, keepdims=True), npt.NDArray[np.float64]) | ||
| assert_type(AR_f8.var(dtype=np.float32), np.float32) | ||
@@ -262,0 +283,0 @@ assert_type(AR_f8.var(dtype=np.float32, axis=0), npt.NDArray[np.float32]) |
@@ -30,2 +30,3 @@ """ | ||
| _to_1d_float: list[float] | ||
| _to_2d_float: list[list[float]] | ||
| _to_1d_complex: list[complex] | ||
@@ -165,2 +166,5 @@ | ||
| assert_type(np.isclose(_to_1d_int, _to_1d_int), np.ndarray[tuple[int], np.dtype[np.bool]]) | ||
| assert_type(np.isclose(_to_1d_int, _to_2d_float), np.ndarray[tuple[int, int], np.dtype[np.bool]]) | ||
| assert_type(np.isclose(_to_2d_float, _to_1d_int), np.ndarray[tuple[int, int], np.dtype[np.bool]]) | ||
| assert_type(np.isclose(_to_2d_float, _to_2d_float), np.ndarray[tuple[int, int], np.dtype[np.bool]]) | ||
| assert_type(np.isclose(AR_i8, AR_i8), npt.NDArray[np.bool]) | ||
@@ -167,0 +171,0 @@ |
@@ -64,2 +64,6 @@ import io | ||
| assert_type( | ||
| np.rec.fromarrays(AR_LIST, names=["i8", "f8"]), | ||
| _RecArray, | ||
| ) | ||
| assert_type( | ||
| np.rec.fromarrays( | ||
@@ -77,3 +81,2 @@ AR_LIST, | ||
| ) | ||
| assert_type( | ||
@@ -86,3 +89,2 @@ np.rec.fromrecords( | ||
| ) | ||
| assert_type( | ||
@@ -96,2 +98,6 @@ np.rec.fromrecords( | ||
| ) | ||
| assert_type( | ||
| np.rec.fromrecords(REC_AR_V, names=["i8", "f8"]), | ||
| _RecArray, | ||
| ) | ||
@@ -98,0 +104,0 @@ assert_type( |
@@ -0,1 +1,2 @@ | ||
| import datetime as dt | ||
| from typing import Any, Literal, assert_type | ||
@@ -52,4 +53,2 @@ | ||
| assert_type(c16[()], np.complex128) | ||
| assert_type(U[()], np.str_) | ||
| assert_type(S[()], np.bytes_) | ||
| assert_type(V[()], np.void) | ||
@@ -69,6 +68,2 @@ | ||
| assert_type(c16[(...,)], np.ndarray[tuple[()], np.dtype[np.complex128]]) | ||
| assert_type(U[...], np.ndarray[tuple[()], np.dtype[np.str_]]) | ||
| assert_type(U[(...,)], np.ndarray[tuple[()], np.dtype[np.str_]]) | ||
| assert_type(S[...], np.ndarray[tuple[()], np.dtype[np.bytes_]]) | ||
| assert_type(S[(...,)], np.ndarray[tuple[()], np.dtype[np.bytes_]]) | ||
| assert_type(V[...], np.ndarray[tuple[()], np.dtype[np.void]]) | ||
@@ -118,13 +113,11 @@ assert_type(V[(...,)], np.ndarray[tuple[()], np.dtype[np.void]]) | ||
| assert_type(U[None], np.ndarray[tuple[int], np.dtype[np.str_]]) | ||
| assert_type(U[None1], np.ndarray[tuple[int], np.dtype[np.str_]]) | ||
| assert_type(U[None2], np.ndarray[tuple[int, int], np.dtype[np.str_]]) | ||
| assert_type(U[None3], np.ndarray[tuple[int, int, int], np.dtype[np.str_]]) | ||
| assert_type(U[None4], np.ndarray[tuple[Any, ...], np.dtype[np.str_]]) | ||
| assert_type(U[0], str) | ||
| assert_type(U[:1], str) | ||
| assert_type(U[1:], str) | ||
| assert_type(U[:], str) | ||
| assert_type(S[None], np.ndarray[tuple[int], np.dtype[np.bytes_]]) | ||
| assert_type(S[None1], np.ndarray[tuple[int], np.dtype[np.bytes_]]) | ||
| assert_type(S[None2], np.ndarray[tuple[int, int], np.dtype[np.bytes_]]) | ||
| assert_type(S[None3], np.ndarray[tuple[int, int, int], np.dtype[np.bytes_]]) | ||
| assert_type(S[None4], np.ndarray[tuple[Any, ...], np.dtype[np.bytes_]]) | ||
| assert_type(S[0], int) | ||
| assert_type(S[:1], bytes) | ||
| assert_type(S[1:], bytes) | ||
| assert_type(S[:], bytes) | ||
@@ -264,2 +257,20 @@ assert_type(V[None], np.ndarray[tuple[int], np.dtype[np.void]]) | ||
| assert_type(np.datetime64(), np.datetime64[None]) | ||
| assert_type(np.datetime64("now", "ns"), np.datetime64[int]) | ||
| assert_type(np.datetime64("now", "s"), np.datetime64[dt.datetime]) | ||
| assert_type(np.datetime64("now", "Y"), np.datetime64[dt.date]) | ||
| # unit-swapping | ||
| assert_type(np.datetime64(np.datetime64(), "ns"), np.datetime64[int]) | ||
| assert_type(np.datetime64(np.datetime64("now", "ns"), "ns"), np.datetime64[int]) | ||
| assert_type(np.datetime64(np.datetime64("now", "s"), "ns"), np.datetime64[int]) | ||
| assert_type(np.datetime64(np.datetime64("now", "Y"), "ns"), np.datetime64[int]) | ||
| assert_type(np.datetime64(np.datetime64(), "s"), np.datetime64[dt.datetime]) | ||
| assert_type(np.datetime64(np.datetime64("now", "ns"), "s"), np.datetime64[dt.datetime]) | ||
| assert_type(np.datetime64(np.datetime64("now", "s"), "s"), np.datetime64[dt.datetime]) | ||
| assert_type(np.datetime64(np.datetime64("now", "Y"), "s"), np.datetime64[dt.datetime]) | ||
| assert_type(np.datetime64(np.datetime64(), "Y"), np.datetime64[dt.date]) | ||
| assert_type(np.datetime64(np.datetime64("now", "ns"), "Y"), np.datetime64[dt.date]) | ||
| assert_type(np.datetime64(np.datetime64("now", "s"), "Y"), np.datetime64[dt.date]) | ||
| assert_type(np.datetime64(np.datetime64("now", "Y"), "Y"), np.datetime64[dt.date]) | ||
| # These fail fail because of a mypy __new__ bug: | ||
@@ -266,0 +277,0 @@ # https://github.com/python/mypy/issues/15182 |
@@ -96,10 +96,12 @@ from typing import assert_type | ||
| assert_type(np.strings.partition(AR_U, "\n"), npt.NDArray[np.str_]) | ||
| assert_type(np.strings.partition(AR_S, [b"a", b"b", b"c"]), npt.NDArray[np.bytes_]) | ||
| assert_type(np.strings.partition(AR_T, "\n"), AR_TU_alias) | ||
| type _tuple3[T] = tuple[T, T, T] | ||
| assert_type(np.strings.rpartition(AR_U, "\n"), npt.NDArray[np.str_]) | ||
| assert_type(np.strings.rpartition(AR_S, [b"a", b"b", b"c"]), npt.NDArray[np.bytes_]) | ||
| assert_type(np.strings.rpartition(AR_T, "\n"), AR_TU_alias) | ||
| assert_type(np.strings.partition(AR_U, "\n"), _tuple3[npt.NDArray[np.str_]]) | ||
| assert_type(np.strings.partition(AR_S, [b"a", b"b", b"c"]), _tuple3[npt.NDArray[np.bytes_]]) | ||
| assert_type(np.strings.partition(AR_T, "\n"), _tuple3[AR_TU_alias]) | ||
| assert_type(np.strings.rpartition(AR_U, "\n"), _tuple3[npt.NDArray[np.str_]]) | ||
| assert_type(np.strings.rpartition(AR_S, [b"a", b"b", b"c"]), _tuple3[npt.NDArray[np.bytes_]]) | ||
| assert_type(np.strings.rpartition(AR_T, "\n"), _tuple3[AR_TU_alias]) | ||
| assert_type(np.strings.replace(AR_U, "_", "-"), npt.NDArray[np.str_]) | ||
@@ -106,0 +108,0 @@ assert_type(np.strings.replace(AR_S, [b"_", b""], [b"a", b"b"]), npt.NDArray[np.bytes_]) |
@@ -8,2 +8,3 @@ from typing import Any, assert_type, type_check_only | ||
| type _2D = tuple[int, int] | ||
| type _3D = tuple[int, int, int] | ||
| type _ND = tuple[Any, ...] | ||
@@ -21,2 +22,3 @@ | ||
| _2d_bool: np.ndarray[_2D, np.dtype[np.bool]] | ||
| _3d_bool: np.ndarray[_3D, np.dtype[np.bool]] | ||
| _nd_u64: np.ndarray[_ND, np.dtype[np.uint64]] | ||
@@ -94,2 +96,5 @@ _nd_i64: np.ndarray[_ND, np.dtype[np.int64]] | ||
| assert_type(np.tril(_to_2d_bool, k=0), np.ndarray) | ||
| assert_type(np.tril(_1d_bool, k=0), np.ndarray[_2D, np.dtype[np.bool]]) | ||
| assert_type(np.tril(_2d_bool, k=0), np.ndarray[_2D, np.dtype[np.bool]]) | ||
| assert_type(np.tril(_3d_bool, k=0), np.ndarray[_3D, np.dtype[np.bool]]) | ||
@@ -101,2 +106,5 @@ # triu | ||
| assert_type(np.triu(_to_2d_bool, k=0), np.ndarray) | ||
| assert_type(np.triu(_1d_bool, k=0), np.ndarray[_2D, np.dtype[np.bool]]) | ||
| assert_type(np.triu(_2d_bool, k=0), np.ndarray[_2D, np.dtype[np.bool]]) | ||
| assert_type(np.triu(_3d_bool, k=0), np.ndarray[_3D, np.dtype[np.bool]]) | ||
@@ -103,0 +111,0 @@ # vander |
+2
-2
@@ -5,8 +5,8 @@ | ||
| """ | ||
| version = "2.5.1" | ||
| version = "2.5.2" | ||
| __version__ = version | ||
| full_version = version | ||
| git_revision = "5e1d03ffac5f2c0a9c39bfcaa9fc853b2b83151e" | ||
| git_revision = "48fecee5453aa1d31e6b79dcb3969dc1a6d1a891" | ||
| release = 'dev' not in version and '+' not in version | ||
| short_version = version.split("+")[0] |
+2
-1
| Metadata-Version: 2.4 | ||
| Name: numpy | ||
| Version: 2.5.1 | ||
| Version: 2.5.2 | ||
| Summary: Fundamental package for array computing in Python | ||
@@ -34,2 +34,3 @@ Author: Travis E. Oliphant et al. | ||
| Classifier: Programming Language :: Python :: 3.14 | ||
| Classifier: Programming Language :: Python :: 3.15 | ||
| Classifier: Programming Language :: Python :: 3 :: Only | ||
@@ -36,0 +37,0 @@ Classifier: Programming Language :: Python :: Implementation :: CPython |
+3
-2
@@ -5,3 +5,3 @@ [build-system] | ||
| "meson-python>=0.18.0", | ||
| "Cython>=3.0.6", # keep in sync with version check in meson.build | ||
| "Cython>=3.1.0", # keep in sync with version check in meson.build | ||
| ] | ||
@@ -11,3 +11,3 @@ | ||
| name = "numpy" | ||
| version = "2.5.1" | ||
| version = "2.5.2" | ||
| description = "Fundamental package for array computing in Python" | ||
@@ -30,2 +30,3 @@ authors = [{name = "Travis E. Oliphant et al."}] | ||
| 'Programming Language :: Python :: 3.14', | ||
| 'Programming Language :: Python :: 3.15', | ||
| 'Programming Language :: Python :: 3 :: Only', | ||
@@ -32,0 +33,0 @@ 'Programming Language :: Python :: Implementation :: CPython', |
@@ -1,5 +0,5 @@ | ||
| meson-python>=0.19.0 | ||
| Cython>=3.2.4 | ||
| meson-python>=0.18.0 | ||
| Cython>=3.1.0 | ||
| ninja | ||
| spin | ||
| build |
@@ -346,3 +346,8 @@ # SPDX-License-Identifier: Apache-2.0 | ||
| def language_stdlib_only_link_flags(self) -> T.List[str]: | ||
| # No need to add search paths here, because LCC ships everything | ||
| # (C, C++, Fortran) and always knows where to look for its stuff | ||
| return ['-lgfortran', '-lm'] | ||
| class G95FortranCompiler(FortranCompiler): | ||
@@ -349,0 +354,0 @@ |
@@ -99,1 +99,5 @@ # SPDX-License-Identifier: Apache-2.0 | ||
| return ['-fopenmp'] | ||
| @classmethod | ||
| def use_linker_args(cls, linker: str, version: str) -> T.List[str]: | ||
| return [] |
@@ -774,2 +774,6 @@ # Copyright 2013-2020 The Meson development team | ||
| self.parse_modules(kwargs) | ||
| # In addition to checking the LP64/ILP64 interface, we need to | ||
| # differentiate between the original Accelerate, and the | ||
| # ACCELERATE_NEW_LAPACK variant. | ||
| self.use_new_lapack = True | ||
@@ -795,2 +799,9 @@ for_machine = MachineChoice.BUILD if kwargs.get('native', False) else MachineChoice.HOST | ||
| def check_iOS_recent_enough(self) -> bool: | ||
| # The ILP64 interface is only available as part of | ||
| # ACCELERATE_NEW_LAPACK, which requires iOS 16.4 and later. | ||
| if self.interface == "ilp64": | ||
| required_version = ">=16.4" | ||
| else: | ||
| required_version = ">=13.0" | ||
| # platform.ios_ver() is only available since Python 3.13; use getattr | ||
@@ -801,12 +812,17 @@ # so this type-checks across Python versions. | ||
| return False | ||
| ios_version = ios_ver().system | ||
| deploy_target = os.environ.get('IPHONEOS_DEPLOYMENT_TARGET', ios_version) | ||
| if not mesonlib.version_compare(deploy_target, '>=16.4'): | ||
| deploy_target = os.environ.get('IPHONEOS_DEPLOYMENT_TARGET', ios_ver().release) | ||
| if not mesonlib.version_compare(deploy_target, required_version): | ||
| return False | ||
| # We also need the SDK to be >=16.4 | ||
| # If we're targeting iOS < 16.4, we can't use ACCELERATE_NEW_LAPACK | ||
| if mesonlib.version_compare(deploy_target, "<16.4"): | ||
| self.use_new_lapack = False | ||
| # We also need the SDK to be the right version | ||
| sdk = "iphonesimulator" if ios_ver().is_simulator else "iphoneos" | ||
| cmd = ['xcrun', '-sdk', sdk, '--show-sdk-version'] | ||
| sdk_version = subprocess.run(cmd, capture_output=True, check=True, encoding='utf-8').stdout.strip() | ||
| return mesonlib.version_compare(sdk_version, '>=16.4') | ||
| return mesonlib.version_compare(sdk_version, required_version) | ||
@@ -820,3 +836,4 @@ def detect(self, kwargs: 'DependencyObjectKWs') -> None: | ||
| self.link_args = dep.link_args | ||
| self.compile_args += ['-DACCELERATE_NEW_LAPACK'] | ||
| if self.use_new_lapack: | ||
| self.compile_args += ['-DACCELERATE_NEW_LAPACK'] | ||
| if self.interface == 'ilp64': | ||
@@ -829,3 +846,6 @@ self.compile_args += ['-DACCELERATE_LAPACK_ILP64'] | ||
| def get_symbol_suffix(self) -> str: | ||
| return '$NEWLAPACK' if self.interface == 'lp64' else '$NEWLAPACK$ILP64' | ||
| if self.use_new_lapack: | ||
| return '$NEWLAPACK' if self.interface == 'lp64' else '$NEWLAPACK$ILP64' | ||
| else: | ||
| return '' | ||
@@ -832,0 +852,0 @@ |
| #include <Python.h> | ||
| #include <numpy/arrayobject.h> | ||
| #include <numpy/ufuncobject.h> | ||
| #if Py_LIMITED_API != PY_VERSION_HEX & 0xffff0000 | ||
| # error "Py_LIMITED_API not defined to Python major+minor version" | ||
| #endif | ||
| static PyModuleDef moduledef = { | ||
| .m_base = PyModuleDef_HEAD_INIT, | ||
| .m_name = "limited_api_latest" | ||
| }; | ||
| PyMODINIT_FUNC PyInit_limited_api_latest(void) | ||
| { | ||
| import_array(); | ||
| import_umath(); | ||
| return PyModule_Create(&moduledef); | ||
| } |
| #ifndef Py_TARGET_ABI3T | ||
| #error "This file must be compiled with -DPy_TARGET_ABI3T" | ||
| #endif | ||
| #include <Python.h> | ||
| #include <numpy/ndarraytypes.h> | ||
| #include <numpy/arrayobject.h> | ||
| #include <numpy/ufuncobject.h> | ||
| static PyObject *limited_api_opaque_nonzero(PyObject *mod, PyArrayObject *self) | ||
| { | ||
| PyArray_NonzeroFunc* nonzero = PyDataType_GetArrFuncs(PyArray_DESCR(self))->nonzero; | ||
| NpyIter* iter; | ||
| NpyIter_IterNextFunc *iternext; | ||
| char** dataptr; | ||
| npy_intp nonzero_count; | ||
| npy_intp* strideptr,* innersizeptr; | ||
| /* Handle zero-sized arrays specially */ | ||
| if (PyArray_SIZE(self) == 0) { | ||
| return PyLong_FromLong(0); | ||
| } | ||
| /* | ||
| * Create and use an iterator to count the nonzeros. | ||
| * flag NPY_ITER_READONLY | ||
| * - The array is never written to. | ||
| * flag NPY_ITER_EXTERNAL_LOOP | ||
| * - Inner loop is done outside the iterator for efficiency. | ||
| * flag NPY_ITER_NPY_ITER_REFS_OK | ||
| * - Reference types are acceptable. | ||
| * order NPY_KEEPORDER | ||
| * - Visit elements in memory order, regardless of strides. | ||
| * This is good for performance when the specific order | ||
| * elements are visited is unimportant. | ||
| * casting NPY_NO_CASTING | ||
| * - No casting is required for this operation. | ||
| */ | ||
| iter = NpyIter_New(self, NPY_ITER_READONLY| | ||
| NPY_ITER_EXTERNAL_LOOP| | ||
| NPY_ITER_REFS_OK, | ||
| NPY_KEEPORDER, NPY_NO_CASTING, | ||
| NULL); | ||
| if (iter == NULL) { | ||
| return NULL; | ||
| } | ||
| /* | ||
| * The iternext function gets stored in a local variable | ||
| * so it can be called repeatedly in an efficient manner. | ||
| */ | ||
| iternext = NpyIter_GetIterNext(iter, NULL); | ||
| if (iternext == NULL) { | ||
| NpyIter_Deallocate(iter); | ||
| return NULL; | ||
| } | ||
| /* The location of the data pointer which the iterator may update */ | ||
| dataptr = NpyIter_GetDataPtrArray(iter); | ||
| /* The location of the stride which the iterator may update */ | ||
| strideptr = NpyIter_GetInnerStrideArray(iter); | ||
| /* The location of the inner loop size which the iterator may update */ | ||
| innersizeptr = NpyIter_GetInnerLoopSizePtr(iter); | ||
| nonzero_count = 0; | ||
| do { | ||
| /* Get the inner loop data/stride/count values */ | ||
| char* data = *dataptr; | ||
| npy_intp stride = *strideptr; | ||
| npy_intp count = *innersizeptr; | ||
| /* This is a typical inner loop for NPY_ITER_EXTERNAL_LOOP */ | ||
| while (count--) { | ||
| if (nonzero(data, self)) { | ||
| ++nonzero_count; | ||
| } | ||
| data += stride; | ||
| } | ||
| /* Increment the iterator to the next inner loop */ | ||
| } while(iternext(iter)); | ||
| NpyIter_Deallocate(iter); | ||
| return PyLong_FromLong(nonzero_count); | ||
| } | ||
| /* | ||
| * Test PyArray_ITER_NEXT, PyArray_ITER_RESET, PyArray_ITER_DATA, | ||
| * and PyArray_ITER_NOTDONE by summing all elements using the | ||
| * legacy iterator macros. | ||
| */ | ||
| static PyObject * | ||
| limited_api_opaque_iter_next(PyObject *mod, PyArrayObject *self) | ||
| { | ||
| PyObject *iter_obj = PyArray_IterNew((PyObject *)self); | ||
| if (iter_obj == NULL) { | ||
| return NULL; | ||
| } | ||
| double sum = 0.0; | ||
| while (PyArray_ITER_NOTDONE(iter_obj)) { | ||
| sum += *(double *)PyArray_ITER_DATA(iter_obj); | ||
| PyArray_ITER_NEXT(iter_obj); | ||
| } | ||
| Py_DECREF(iter_obj); | ||
| return PyFloat_FromDouble(sum); | ||
| } | ||
| /* | ||
| * Test PyArray_ITER_GOTO1D by accessing a specific flat index. | ||
| */ | ||
| static PyObject * | ||
| limited_api_opaque_iter_goto1d(PyObject *mod, PyObject *args) | ||
| { | ||
| PyArrayObject *arr; | ||
| npy_intp index; | ||
| if (!PyArg_ParseTuple(args, "O!n", &PyArray_Type, &arr, &index)) { | ||
| return NULL; | ||
| } | ||
| PyObject *iter_obj = PyArray_IterNew((PyObject *)arr); | ||
| if (iter_obj == NULL) { | ||
| return NULL; | ||
| } | ||
| PyArray_ITER_GOTO1D(iter_obj, index); | ||
| double val = *(double *)PyArray_ITER_DATA(iter_obj); | ||
| Py_DECREF(iter_obj); | ||
| return PyFloat_FromDouble(val); | ||
| } | ||
| /* | ||
| * Test PyArray_ITER_RESET by iterating, resetting, and iterating again. | ||
| * Returns the sum from the second pass (should equal the first). | ||
| */ | ||
| static PyObject * | ||
| limited_api_opaque_iter_reset(PyObject *mod, PyArrayObject *self) | ||
| { | ||
| PyObject *iter_obj = PyArray_IterNew((PyObject *)self); | ||
| if (iter_obj == NULL) { | ||
| return NULL; | ||
| } | ||
| /* First pass: skip through */ | ||
| while (PyArray_ITER_NOTDONE(iter_obj)) { | ||
| PyArray_ITER_NEXT(iter_obj); | ||
| } | ||
| /* Reset and sum */ | ||
| PyArray_ITER_RESET(iter_obj); | ||
| double sum = 0.0; | ||
| while (PyArray_ITER_NOTDONE(iter_obj)) { | ||
| sum += *(double *)PyArray_ITER_DATA(iter_obj); | ||
| PyArray_ITER_NEXT(iter_obj); | ||
| } | ||
| Py_DECREF(iter_obj); | ||
| return PyFloat_FromDouble(sum); | ||
| } | ||
| /* | ||
| * Test PyArray_MultiIter_NEXT, PyArray_MultiIter_RESET, | ||
| * PyArray_MultiIter_DATA, and PyArray_MultiIter_NOTDONE | ||
| * by computing the element-wise sum of two broadcastable arrays. | ||
| * Returns the total sum of (a + b) for all broadcast elements. | ||
| */ | ||
| static PyObject * | ||
| limited_api_opaque_multi_iter_next(PyObject *mod, PyObject *args) | ||
| { | ||
| PyArrayObject *a, *b; | ||
| if (!PyArg_ParseTuple(args, "O!O!", &PyArray_Type, &a, | ||
| &PyArray_Type, &b)) { | ||
| return NULL; | ||
| } | ||
| PyObject *multi = PyArray_MultiIterNew(2, a, b); | ||
| if (multi == NULL) { | ||
| return NULL; | ||
| } | ||
| double sum = 0.0; | ||
| while (PyArray_MultiIter_NOTDONE(multi)) { | ||
| double va = *(double *)PyArray_MultiIter_DATA(multi, 0); | ||
| double vb = *(double *)PyArray_MultiIter_DATA(multi, 1); | ||
| sum += va + vb; | ||
| PyArray_MultiIter_NEXT(multi); | ||
| } | ||
| /* Test reset: iterate again and verify same sum */ | ||
| PyArray_MultiIter_RESET(multi); | ||
| double sum2 = 0.0; | ||
| while (PyArray_MultiIter_NOTDONE(multi)) { | ||
| double va = *(double *)PyArray_MultiIter_DATA(multi, 0); | ||
| double vb = *(double *)PyArray_MultiIter_DATA(multi, 1); | ||
| sum2 += va + vb; | ||
| PyArray_MultiIter_NEXT(multi); | ||
| } | ||
| Py_DECREF(multi); | ||
| if (sum != sum2) { | ||
| PyErr_SetString(PyExc_RuntimeError, | ||
| "MultiIter reset produced different sum"); | ||
| return NULL; | ||
| } | ||
| return PyFloat_FromDouble(sum); | ||
| } | ||
| /* | ||
| * Test PyArray_ITER_GOTO by jumping to a coordinate and reading the value. | ||
| */ | ||
| static PyObject * | ||
| limited_api_opaque_iter_goto(PyObject *mod, PyObject *args) | ||
| { | ||
| PyArrayObject *arr; | ||
| PyObject *coord_tuple; | ||
| if (!PyArg_ParseTuple(args, "O!O!", &PyArray_Type, &arr, | ||
| &PyTuple_Type, &coord_tuple)) { | ||
| return NULL; | ||
| } | ||
| int nd = PyArray_NDIM(arr); | ||
| if (PyTuple_Size(coord_tuple) != nd) { | ||
| PyErr_SetString(PyExc_ValueError, "coordinate length mismatch"); | ||
| return NULL; | ||
| } | ||
| npy_intp destination[NPY_MAXDIMS_LEGACY_ITERS]; | ||
| for (int i = 0; i < nd; i++) { | ||
| destination[i] = PyLong_AsLong(PyTuple_GetItem(coord_tuple, i)); | ||
| if (destination[i] == -1 && PyErr_Occurred()) { | ||
| return NULL; | ||
| } | ||
| } | ||
| PyObject *iter_obj = PyArray_IterNew((PyObject *)arr); | ||
| if (iter_obj == NULL) { | ||
| return NULL; | ||
| } | ||
| PyArray_ITER_GOTO(iter_obj, destination); | ||
| double val = *(double *)PyArray_ITER_DATA(iter_obj); | ||
| Py_DECREF(iter_obj); | ||
| return PyFloat_FromDouble(val); | ||
| } | ||
| /* | ||
| * Test PyArray_MultiIter_GOTO by jumping to a coordinate | ||
| * and returning (a_val, b_val) at that position. | ||
| */ | ||
| static PyObject * | ||
| limited_api_opaque_multi_iter_goto(PyObject *mod, PyObject *args) | ||
| { | ||
| PyArrayObject *a, *b; | ||
| PyObject *coord_tuple; | ||
| if (!PyArg_ParseTuple(args, "O!O!O!", &PyArray_Type, &a, | ||
| &PyArray_Type, &b, | ||
| &PyTuple_Type, &coord_tuple)) { | ||
| return NULL; | ||
| } | ||
| PyObject *multi = PyArray_MultiIterNew(2, a, b); | ||
| if (multi == NULL) { | ||
| return NULL; | ||
| } | ||
| int nd = _PyMIT(multi)->nd; | ||
| if (PyTuple_Size(coord_tuple) != nd) { | ||
| Py_DECREF(multi); | ||
| PyErr_SetString(PyExc_ValueError, "coordinate length mismatch"); | ||
| return NULL; | ||
| } | ||
| npy_intp destination[NPY_MAXDIMS_LEGACY_ITERS]; | ||
| for (int i = 0; i < nd; i++) { | ||
| destination[i] = PyLong_AsLong(PyTuple_GetItem(coord_tuple, i)); | ||
| if (destination[i] == -1 && PyErr_Occurred()) { | ||
| Py_DECREF(multi); | ||
| return NULL; | ||
| } | ||
| } | ||
| PyArray_MultiIter_GOTO(multi, destination); | ||
| double va = *(double *)PyArray_MultiIter_DATA(multi, 0); | ||
| double vb = *(double *)PyArray_MultiIter_DATA(multi, 1); | ||
| Py_DECREF(multi); | ||
| return Py_BuildValue("dd", va, vb); | ||
| } | ||
| /* | ||
| * Test PyArray_MultiIter_GOTO1D by jumping to a flat index | ||
| * and returning (a_val, b_val) at that position. | ||
| */ | ||
| static PyObject * | ||
| limited_api_opaque_multi_iter_goto1d(PyObject *mod, PyObject *args) | ||
| { | ||
| PyArrayObject *a, *b; | ||
| npy_intp index; | ||
| if (!PyArg_ParseTuple(args, "O!O!n", &PyArray_Type, &a, | ||
| &PyArray_Type, &b, &index)) { | ||
| return NULL; | ||
| } | ||
| PyObject *multi = PyArray_MultiIterNew(2, a, b); | ||
| if (multi == NULL) { | ||
| return NULL; | ||
| } | ||
| PyArray_MultiIter_GOTO1D(multi, index); | ||
| double va = *(double *)PyArray_MultiIter_DATA(multi, 0); | ||
| double vb = *(double *)PyArray_MultiIter_DATA(multi, 1); | ||
| Py_DECREF(multi); | ||
| return Py_BuildValue("dd", va, vb); | ||
| } | ||
| /* | ||
| * Test PyArray_MultiIter_NEXTi by advancing only the first iterator | ||
| * and returning its data pointer value after N steps. | ||
| */ | ||
| static PyObject * | ||
| limited_api_opaque_multi_iter_nexti(PyObject *mod, PyObject *args) | ||
| { | ||
| PyArrayObject *a, *b; | ||
| int steps; | ||
| if (!PyArg_ParseTuple(args, "O!O!i", &PyArray_Type, &a, | ||
| &PyArray_Type, &b, &steps)) { | ||
| return NULL; | ||
| } | ||
| PyObject *multi = PyArray_MultiIterNew(2, a, b); | ||
| if (multi == NULL) { | ||
| return NULL; | ||
| } | ||
| for (int i = 0; i < steps; i++) { | ||
| PyArray_MultiIter_NEXTi(multi, 0); | ||
| } | ||
| double val = *(double *)PyArray_MultiIter_DATA(multi, 0); | ||
| Py_DECREF(multi); | ||
| return PyFloat_FromDouble(val); | ||
| } | ||
| static PyMethodDef limited_api_opaque_methods[] = { | ||
| {"nonzero", (PyCFunction)limited_api_opaque_nonzero, METH_O, | ||
| "Count the number of non-zero elements in the array."}, | ||
| {"iter_next", (PyCFunction)limited_api_opaque_iter_next, METH_O, | ||
| "Sum array elements using PyArray_ITER_NEXT."}, | ||
| {"iter_goto1d", (PyCFunction)limited_api_opaque_iter_goto1d, METH_VARARGS, | ||
| "Get element at flat index using PyArray_ITER_GOTO1D."}, | ||
| {"iter_reset", (PyCFunction)limited_api_opaque_iter_reset, METH_O, | ||
| "Sum array elements after reset using PyArray_ITER_RESET."}, | ||
| {"multi_iter_next", (PyCFunction)limited_api_opaque_multi_iter_next, | ||
| METH_VARARGS, | ||
| "Sum broadcast (a+b) using PyArray_MultiIter_NEXT."}, | ||
| {"iter_goto", (PyCFunction)limited_api_opaque_iter_goto, METH_VARARGS, | ||
| "Get element at coordinate using PyArray_ITER_GOTO."}, | ||
| {"multi_iter_goto", (PyCFunction)limited_api_opaque_multi_iter_goto, | ||
| METH_VARARGS, | ||
| "Get (a, b) at coordinate using PyArray_MultiIter_GOTO."}, | ||
| {"multi_iter_goto1d", (PyCFunction)limited_api_opaque_multi_iter_goto1d, | ||
| METH_VARARGS, | ||
| "Get (a, b) at flat index using PyArray_MultiIter_GOTO1D."}, | ||
| {"multi_iter_nexti", (PyCFunction)limited_api_opaque_multi_iter_nexti, | ||
| METH_VARARGS, | ||
| "Advance only iter 0 N steps using PyArray_MultiIter_NEXTi."}, | ||
| {NULL, NULL, 0, NULL} /* Sentinel */ | ||
| }; | ||
| PyABIInfo_VAR(abi_info); | ||
| static PySlot limited_api_opaque_slots[] = { | ||
| PySlot_STATIC_DATA(Py_mod_abi, &abi_info), | ||
| PySlot_STATIC_DATA(Py_mod_name, "limited_api_opaque"), | ||
| PySlot_STATIC_DATA(Py_mod_methods, limited_api_opaque_methods), | ||
| PySlot_STATIC_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), | ||
| PySlot_END, | ||
| }; | ||
| PyMODEXPORT_FUNC | ||
| PyModExport_limited_api_opaque(void) | ||
| { | ||
| import_array(); | ||
| import_umath(); | ||
| return limited_api_opaque_slots; | ||
| } |
| #include <Python.h> | ||
| #include <numpy/arrayobject.h> | ||
| #include <numpy/ufuncobject.h> | ||
| static PyModuleDef moduledef = { | ||
| .m_base = PyModuleDef_HEAD_INIT, | ||
| .m_name = "limited_api1" | ||
| }; | ||
| PyMODINIT_FUNC PyInit_limited_api1(void) | ||
| { | ||
| import_array(); | ||
| import_umath(); | ||
| return PyModule_Create(&moduledef); | ||
| } |
| #cython: language_level=3 | ||
| """ | ||
| Make sure cython can compile in limited API mode (see meson.build) | ||
| """ | ||
| cdef extern from "numpy/arrayobject.h": | ||
| pass | ||
| cdef extern from "numpy/arrayscalars.h": | ||
| pass | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
67549968
0.15%8365
0.04%438665
0.15%