Skip to content

Commit 56dab50

Browse files
mattipseberglysnikolaou
authored
BUG: adapt cython files to new complex declarations (numpy#26080)
Fixes numpy#26029 declare npy_cfloat and friends as empty structs declare cfloat_t and friends as ctypedef float complex cfloat_t, without being directly connected to npy_cfloat. This allows still using .imag and .real attributes, which Cython wraps with macros add a test for BUG: changing the complex struct has implications for Cython numpy#26029, it passes since we use C compilation for cython. The inplace += will fail on C++. resync the two cython pxd files. At what point do we declare NumPy requires Cython3? I checked that scipy and cython can use the pxd file. --------- Co-authored-by: Sebastian Berg <[email protected]> Co-authored-by: Lysandros Nikolaou <[email protected]>
1 parent 6a94599 commit 56dab50

File tree

5 files changed

+216
-58
lines changed

5 files changed

+216
-58
lines changed

doc/source/numpy_2_0_migration_guide.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ added for setting the real or imaginary part.
214214
The underlying type remains a struct under C++ (all of the above still remains
215215
valid).
216216

217+
This has implications for Cython. It is recommened to always use the native
218+
typedefs ``cfloat_t``, ``cdouble_t``, ``clongdouble_t`` rather than the NumPy
219+
types ``npy_cfloat``, etc, unless you have to interface with C code written
220+
using the NumPy types. You can still write cython code using the ``c.real`` and
221+
``c.imag`` attributes (using the native typedefs), but you can no longer use
222+
in-place operators ``c.imag += 1`` in Cython's c++ mode.
223+
217224

218225
Changes to namespaces
219226
=====================

numpy/__init__.cython-30.pxd

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -68,36 +68,28 @@ cdef extern from "numpy/arrayobject.h":
6868
ctypedef long double npy_float128
6969

7070
ctypedef struct npy_cfloat:
71-
float real
72-
float imag
71+
pass
7372

7473
ctypedef struct npy_cdouble:
75-
double real
76-
double imag
74+
pass
7775

7876
ctypedef struct npy_clongdouble:
79-
long double real
80-
long double imag
77+
pass
8178

8279
ctypedef struct npy_complex64:
83-
float real
84-
float imag
80+
pass
8581

8682
ctypedef struct npy_complex128:
87-
double real
88-
double imag
83+
pass
8984

9085
ctypedef struct npy_complex160:
91-
long double real
92-
long double imag
86+
pass
9387

9488
ctypedef struct npy_complex192:
95-
long double real
96-
long double imag
89+
pass
9790

9891
ctypedef struct npy_complex256:
99-
long double real
100-
long double imag
92+
pass
10193

10294
ctypedef struct PyArray_Dims:
10395
npy_intp *ptr
@@ -562,7 +554,6 @@ cdef extern from "numpy/arrayobject.h":
562554
object PyArray_ZEROS(int nd, npy_intp* dims, int type, int fortran)
563555
object PyArray_EMPTY(int nd, npy_intp* dims, int type, int fortran)
564556
void PyArray_FILLWBYTE(ndarray, int val)
565-
npy_intp PyArray_REFCOUNT(object)
566557
object PyArray_ContiguousFromAny(op, int, int min_depth, int max_depth)
567558
unsigned char PyArray_EquivArrTypes(ndarray a1, ndarray a2)
568559
bint PyArray_EquivByteorders(int b1, int b2) nogil
@@ -808,11 +799,10 @@ ctypedef npy_double float_t
808799
ctypedef npy_double double_t
809800
ctypedef npy_longdouble longdouble_t
810801

811-
ctypedef npy_cfloat cfloat_t
812-
ctypedef npy_cdouble cdouble_t
813-
ctypedef npy_clongdouble clongdouble_t
814-
815-
ctypedef npy_cdouble complex_t
802+
ctypedef float complex cfloat_t
803+
ctypedef double complex cdouble_t
804+
ctypedef double complex complex_t
805+
ctypedef long double complex clongdouble_t
816806

817807
cdef inline object PyArray_MultiIterNew1(a):
818808
return PyArray_MultiIterNew(1, <void*>a)
@@ -851,6 +841,7 @@ cdef extern from "numpy/ndarraytypes.h":
851841
int64_t year
852842
int32_t month, day, hour, min, sec, us, ps, as
853843

844+
854845
cdef extern from "numpy/arrayscalars.h":
855846

856847
# abstract types

numpy/__init__.pxd

Lines changed: 172 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,27 @@ from cpython.buffer cimport PyObject_GetBuffer
1616
from cpython.type cimport type
1717
cimport libc.stdio as stdio
1818

19+
20+
cdef extern from *:
21+
# Leave a marker that the NumPy declarations came from NumPy itself and not from Cython.
22+
# See https://github.com/cython/cython/issues/3573
23+
"""
24+
/* Using NumPy API declarations from "numpy/__init__.pxd" */
25+
"""
26+
27+
1928
cdef extern from "Python.h":
2029
ctypedef int Py_intptr_t
2130
bint PyObject_TypeCheck(object obj, PyTypeObject* type)
2231

2332
cdef extern from "numpy/arrayobject.h":
24-
ctypedef Py_intptr_t npy_intp
25-
ctypedef size_t npy_uintp
33+
# It would be nice to use size_t and ssize_t, but ssize_t has special
34+
# implicit conversion rules, so just use "long".
35+
# Note: The actual type only matters for Cython promotion, so long
36+
# is closer than int, but could lead to incorrect promotion.
37+
# (Not to worrying, and always the status-quo.)
38+
ctypedef signed long npy_intp
39+
ctypedef unsigned long npy_uintp
2640

2741
ctypedef unsigned char npy_bool
2842

@@ -63,36 +77,28 @@ cdef extern from "numpy/arrayobject.h":
6377
ctypedef long double npy_float128
6478

6579
ctypedef struct npy_cfloat:
66-
float real
67-
float imag
80+
pass
6881

6982
ctypedef struct npy_cdouble:
70-
double real
71-
double imag
83+
pass
7284

7385
ctypedef struct npy_clongdouble:
74-
long double real
75-
long double imag
86+
pass
7687

7788
ctypedef struct npy_complex64:
78-
float real
79-
float imag
89+
pass
8090

8191
ctypedef struct npy_complex128:
82-
double real
83-
double imag
92+
pass
8493

8594
ctypedef struct npy_complex160:
86-
long double real
87-
long double imag
95+
pass
8896

8997
ctypedef struct npy_complex192:
90-
long double real
91-
long double imag
98+
pass
9299

93100
ctypedef struct npy_complex256:
94-
long double real
95-
long double imag
101+
pass
96102

97103
ctypedef struct PyArray_Dims:
98104
npy_intp *ptr
@@ -154,7 +160,7 @@ cdef extern from "numpy/arrayobject.h":
154160
NPY_COMPLEX512
155161

156162
NPY_INTP
157-
NPY_DEFAULT_INT
163+
NPY_DEFAULT_INT # Not a compile time constant (normally)!
158164

159165
ctypedef enum NPY_ORDER:
160166
NPY_ANYORDER
@@ -350,7 +356,10 @@ cdef extern from "numpy/arrayobject.h":
350356

351357
PyObject *PyArray_BASE(ndarray) nogil # returns borrowed reference!
352358
PyArray_Descr *PyArray_DESCR(ndarray) nogil # returns borrowed reference to dtype!
359+
PyArray_Descr *PyArray_DTYPE(ndarray) nogil # returns borrowed reference to dtype! NP 1.7+ alias for descr.
353360
int PyArray_FLAGS(ndarray) nogil
361+
void PyArray_CLEARFLAGS(ndarray, int flags) nogil # Added in NumPy 1.7
362+
void PyArray_ENABLEFLAGS(ndarray, int flags) nogil # Added in NumPy 1.7
354363
npy_intp PyArray_ITEMSIZE(ndarray) nogil
355364
int PyArray_TYPE(ndarray arr) nogil
356365

@@ -371,7 +380,6 @@ cdef extern from "numpy/arrayobject.h":
371380
bint PyTypeNum_ISOBJECT(int) nogil
372381

373382
npy_intp PyDataType_ELSIZE(dtype) nogil
374-
void PyDataType_SET_ELSIZE(dtype, npy_intp) nogil
375383
npy_intp PyDataType_ALIGNMENT(dtype) nogil
376384
PyObject* PyDataType_METADATA(dtype) nogil
377385
PyArray_ArrayDescr* PyDataType_SUBARRAY(dtype) nogil
@@ -501,6 +509,12 @@ cdef extern from "numpy/arrayobject.h":
501509
void* PyArray_MultiIter_DATA(broadcast multi, npy_intp i) nogil
502510
void PyArray_MultiIter_NEXTi(broadcast multi, npy_intp i) nogil
503511
bint PyArray_MultiIter_NOTDONE(broadcast multi) nogil
512+
npy_intp PyArray_MultiIter_SIZE(broadcast multi) nogil
513+
int PyArray_MultiIter_NDIM(broadcast multi) nogil
514+
npy_intp PyArray_MultiIter_INDEX(broadcast multi) nogil
515+
int PyArray_MultiIter_NUMITER(broadcast multi) nogil
516+
npy_intp* PyArray_MultiIter_DIMS(broadcast multi) nogil
517+
void** PyArray_MultiIter_ITERS(broadcast multi) nogil
504518

505519
# Functions from __multiarray_api.h
506520

@@ -700,11 +714,10 @@ ctypedef npy_double float_t
700714
ctypedef npy_double double_t
701715
ctypedef npy_longdouble longdouble_t
702716

703-
ctypedef npy_cfloat cfloat_t
704-
ctypedef npy_cdouble cdouble_t
705-
ctypedef npy_clongdouble clongdouble_t
706-
707-
ctypedef npy_cdouble complex_t
717+
ctypedef float complex cfloat_t
718+
ctypedef double complex cdouble_t
719+
ctypedef double complex complex_t
720+
ctypedef long double complex clongdouble_t
708721

709722
cdef inline object PyArray_MultiIterNew1(a):
710723
return PyArray_MultiIterNew(1, <void*>a)
@@ -939,13 +952,6 @@ cdef inline int import_ufunc() except -1:
939952
except Exception:
940953
raise ImportError("numpy._core.umath failed to import")
941954

942-
cdef extern from *:
943-
# Leave a marker that the NumPy declarations came from this file
944-
# See https://github.com/cython/cython/issues/3573
945-
"""
946-
/* NumPy API declarations from "numpy/__init__.pxd" */
947-
"""
948-
949955

950956
cdef inline bint is_timedelta64_object(object obj):
951957
"""
@@ -999,3 +1005,137 @@ cdef inline NPY_DATETIMEUNIT get_datetime64_unit(object obj) nogil:
9991005
returns the unit part of the dtype for a numpy datetime64 object.
10001006
"""
10011007
return <NPY_DATETIMEUNIT>(<PyDatetimeScalarObject*>obj).obmeta.base
1008+
1009+
1010+
# Iterator API added in v1.6
1011+
ctypedef int (*NpyIter_IterNextFunc)(NpyIter* it) noexcept nogil
1012+
ctypedef void (*NpyIter_GetMultiIndexFunc)(NpyIter* it, npy_intp* outcoords) noexcept nogil
1013+
1014+
cdef extern from "numpy/arrayobject.h":
1015+
1016+
ctypedef struct NpyIter:
1017+
pass
1018+
1019+
cdef enum:
1020+
NPY_FAIL
1021+
NPY_SUCCEED
1022+
1023+
cdef enum:
1024+
# Track an index representing C order
1025+
NPY_ITER_C_INDEX
1026+
# Track an index representing Fortran order
1027+
NPY_ITER_F_INDEX
1028+
# Track a multi-index
1029+
NPY_ITER_MULTI_INDEX
1030+
# User code external to the iterator does the 1-dimensional innermost loop
1031+
NPY_ITER_EXTERNAL_LOOP
1032+
# Convert all the operands to a common data type
1033+
NPY_ITER_COMMON_DTYPE
1034+
# Operands may hold references, requiring API access during iteration
1035+
NPY_ITER_REFS_OK
1036+
# Zero-sized operands should be permitted, iteration checks IterSize for 0
1037+
NPY_ITER_ZEROSIZE_OK
1038+
# Permits reductions (size-0 stride with dimension size > 1)
1039+
NPY_ITER_REDUCE_OK
1040+
# Enables sub-range iteration
1041+
NPY_ITER_RANGED
1042+
# Enables buffering
1043+
NPY_ITER_BUFFERED
1044+
# When buffering is enabled, grows the inner loop if possible
1045+
NPY_ITER_GROWINNER
1046+
# Delay allocation of buffers until first Reset* call
1047+
NPY_ITER_DELAY_BUFALLOC
1048+
# When NPY_KEEPORDER is specified, disable reversing negative-stride axes
1049+
NPY_ITER_DONT_NEGATE_STRIDES
1050+
NPY_ITER_COPY_IF_OVERLAP
1051+
# The operand will be read from and written to
1052+
NPY_ITER_READWRITE
1053+
# The operand will only be read from
1054+
NPY_ITER_READONLY
1055+
# The operand will only be written to
1056+
NPY_ITER_WRITEONLY
1057+
# The operand's data must be in native byte order
1058+
NPY_ITER_NBO
1059+
# The operand's data must be aligned
1060+
NPY_ITER_ALIGNED
1061+
# The operand's data must be contiguous (within the inner loop)
1062+
NPY_ITER_CONTIG
1063+
# The operand may be copied to satisfy requirements
1064+
NPY_ITER_COPY
1065+
# The operand may be copied with WRITEBACKIFCOPY to satisfy requirements
1066+
NPY_ITER_UPDATEIFCOPY
1067+
# Allocate the operand if it is NULL
1068+
NPY_ITER_ALLOCATE
1069+
# If an operand is allocated, don't use any subtype
1070+
NPY_ITER_NO_SUBTYPE
1071+
# This is a virtual array slot, operand is NULL but temporary data is there
1072+
NPY_ITER_VIRTUAL
1073+
# Require that the dimension match the iterator dimensions exactly
1074+
NPY_ITER_NO_BROADCAST
1075+
# A mask is being used on this array, affects buffer -> array copy
1076+
NPY_ITER_WRITEMASKED
1077+
# This array is the mask for all WRITEMASKED operands
1078+
NPY_ITER_ARRAYMASK
1079+
# Assume iterator order data access for COPY_IF_OVERLAP
1080+
NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE
1081+
1082+
# construction and destruction functions
1083+
NpyIter* NpyIter_New(ndarray arr, npy_uint32 flags, NPY_ORDER order,
1084+
NPY_CASTING casting, dtype datatype) except NULL
1085+
NpyIter* NpyIter_MultiNew(npy_intp nop, PyArrayObject** op, npy_uint32 flags,
1086+
NPY_ORDER order, NPY_CASTING casting, npy_uint32*
1087+
op_flags, PyArray_Descr** op_dtypes) except NULL
1088+
NpyIter* NpyIter_AdvancedNew(npy_intp nop, PyArrayObject** op,
1089+
npy_uint32 flags, NPY_ORDER order,
1090+
NPY_CASTING casting, npy_uint32* op_flags,
1091+
PyArray_Descr** op_dtypes, int oa_ndim,
1092+
int** op_axes, const npy_intp* itershape,
1093+
npy_intp buffersize) except NULL
1094+
NpyIter* NpyIter_Copy(NpyIter* it) except NULL
1095+
int NpyIter_RemoveAxis(NpyIter* it, int axis) except NPY_FAIL
1096+
int NpyIter_RemoveMultiIndex(NpyIter* it) except NPY_FAIL
1097+
int NpyIter_EnableExternalLoop(NpyIter* it) except NPY_FAIL
1098+
int NpyIter_Deallocate(NpyIter* it) except NPY_FAIL
1099+
int NpyIter_Reset(NpyIter* it, char** errmsg) except NPY_FAIL
1100+
int NpyIter_ResetToIterIndexRange(NpyIter* it, npy_intp istart,
1101+
npy_intp iend, char** errmsg) except NPY_FAIL
1102+
int NpyIter_ResetBasePointers(NpyIter* it, char** baseptrs, char** errmsg) except NPY_FAIL
1103+
int NpyIter_GotoMultiIndex(NpyIter* it, const npy_intp* multi_index) except NPY_FAIL
1104+
int NpyIter_GotoIndex(NpyIter* it, npy_intp index) except NPY_FAIL
1105+
npy_intp NpyIter_GetIterSize(NpyIter* it) nogil
1106+
npy_intp NpyIter_GetIterIndex(NpyIter* it) nogil
1107+
void NpyIter_GetIterIndexRange(NpyIter* it, npy_intp* istart,
1108+
npy_intp* iend) nogil
1109+
int NpyIter_GotoIterIndex(NpyIter* it, npy_intp iterindex) except NPY_FAIL
1110+
npy_bool NpyIter_HasDelayedBufAlloc(NpyIter* it) nogil
1111+
npy_bool NpyIter_HasExternalLoop(NpyIter* it) nogil
1112+
npy_bool NpyIter_HasMultiIndex(NpyIter* it) nogil
1113+
npy_bool NpyIter_HasIndex(NpyIter* it) nogil
1114+
npy_bool NpyIter_RequiresBuffering(NpyIter* it) nogil
1115+
npy_bool NpyIter_IsBuffered(NpyIter* it) nogil
1116+
npy_bool NpyIter_IsGrowInner(NpyIter* it) nogil
1117+
npy_intp NpyIter_GetBufferSize(NpyIter* it) nogil
1118+
int NpyIter_GetNDim(NpyIter* it) nogil
1119+
int NpyIter_GetNOp(NpyIter* it) nogil
1120+
npy_intp* NpyIter_GetAxisStrideArray(NpyIter* it, int axis) except NULL
1121+
int NpyIter_GetShape(NpyIter* it, npy_intp* outshape) nogil
1122+
PyArray_Descr** NpyIter_GetDescrArray(NpyIter* it)
1123+
PyArrayObject** NpyIter_GetOperandArray(NpyIter* it)
1124+
ndarray NpyIter_GetIterView(NpyIter* it, npy_intp i)
1125+
void NpyIter_GetReadFlags(NpyIter* it, char* outreadflags)
1126+
void NpyIter_GetWriteFlags(NpyIter* it, char* outwriteflags)
1127+
int NpyIter_CreateCompatibleStrides(NpyIter* it, npy_intp itemsize,
1128+
npy_intp* outstrides) except NPY_FAIL
1129+
npy_bool NpyIter_IsFirstVisit(NpyIter* it, int iop) nogil
1130+
# functions for iterating an NpyIter object
1131+
NpyIter_IterNextFunc* NpyIter_GetIterNext(NpyIter* it, char** errmsg) except NULL
1132+
NpyIter_GetMultiIndexFunc* NpyIter_GetGetMultiIndex(NpyIter* it,
1133+
char** errmsg) except NULL
1134+
char** NpyIter_GetDataPtrArray(NpyIter* it) nogil
1135+
char** NpyIter_GetInitialDataPtrArray(NpyIter* it) nogil
1136+
npy_intp* NpyIter_GetIndexPtr(NpyIter* it)
1137+
npy_intp* NpyIter_GetInnerStrideArray(NpyIter* it) nogil
1138+
npy_intp* NpyIter_GetInnerLoopSizePtr(NpyIter* it) nogil
1139+
void NpyIter_GetInnerFixedStrideArray(NpyIter* it, npy_intp* outstrides) nogil
1140+
npy_bool NpyIter_IterationNeedsAPI(NpyIter* it) nogil
1141+
void NpyIter_DebugPrint(NpyIter* it)

numpy/_core/tests/examples/cython/checks.pyx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,4 +253,12 @@ def compile_fillwithbyte():
253253
dims = (1, 2)
254254
pos = cnp.PyArray_ZEROS(2, dims, cnp.NPY_UINT8, 0)
255255
cnp.PyArray_FILLWBYTE(pos, 1)
256-
return pos
256+
return pos
257+
258+
def inc2_cfloat_struct(cnp.ndarray[cnp.cfloat_t] arr):
259+
# This works since we compile in C mode, it will fail in cpp mode
260+
arr[1].real += 1
261+
arr[1].imag += 1
262+
# This works in both modes
263+
arr[1].real = arr[1].real + 1
264+
arr[1].imag = arr[1].imag + 1

0 commit comments

Comments
 (0)