numpy 2.0.0
|
00001 #ifndef __LOWLEVEL_STRIDED_LOOPS_H 00002 #define __LOWLEVEL_STRIDED_LOOPS_H 00003 00004 /* 00005 * NOTE: This API should remain private for the time being, to allow 00006 * for further refinement. I think the 'aligned' mechanism 00007 * needs changing, for example. 00008 */ 00009 00010 /* 00011 * This function pointer is for functions that transfer an arbitrarily strided 00012 * input to a an arbitrarily strided output. It may be a fully general 00013 * function, or a specialized function when the strides or item size 00014 * have special values. 00015 * 00016 * Examples of transfer functions are a straight copy, a byte-swap, 00017 * and a casting operation, 00018 * 00019 * The 'transferdata' parameter is slightly special, following a 00020 * generic auxiliary data pattern defined in ndarraytypes.h 00021 * Use NPY_AUXDATA_CLONE and NPY_AUXDATA_FREE to deal with this data. 00022 * 00023 */ 00024 typedef void (PyArray_StridedTransferFn)(char *dst, npy_intp dst_stride, 00025 char *src, npy_intp src_stride, 00026 npy_intp N, npy_intp src_itemsize, 00027 NpyAuxData *transferdata); 00028 00029 /* 00030 * This is for pointers to functions which behave exactly as 00031 * for PyArray_StridedTransferFn, but with an additional mask controlling 00032 * which values are transferred. 00033 * 00034 * In particular, the 'i'-th element is transfered if and only if 00035 * NpyMask_IsExposed(mask[i*mask_stride]). 00036 */ 00037 typedef void (PyArray_MaskedStridedTransferFn)(char *dst, npy_intp dst_stride, 00038 char *src, npy_intp src_stride, 00039 npy_mask *mask, npy_intp mask_stride, 00040 npy_intp N, npy_intp src_itemsize, 00041 NpyAuxData *transferdata); 00042 00043 /* 00044 * Gives back a function pointer to a specialized function for copying 00045 * strided memory. Returns NULL if there is a problem with the inputs. 00046 * 00047 * aligned: 00048 * Should be 1 if the src and dst pointers are always aligned, 00049 * 0 otherwise. 00050 * src_stride: 00051 * Should be the src stride if it will always be the same, 00052 * NPY_MAX_INTP otherwise. 00053 * dst_stride: 00054 * Should be the dst stride if it will always be the same, 00055 * NPY_MAX_INTP otherwise. 00056 * itemsize: 00057 * Should be the item size if it will always be the same, 0 otherwise. 00058 * 00059 */ 00060 NPY_NO_EXPORT PyArray_StridedTransferFn * 00061 PyArray_GetStridedCopyFn(int aligned, 00062 npy_intp src_stride, npy_intp dst_stride, 00063 npy_intp itemsize); 00064 00065 /* 00066 * Gives back a function pointer to a specialized function for copying 00067 * and swapping strided memory. This assumes each element is a single 00068 * value to be swapped. 00069 * 00070 * For information on the 'aligned', 'src_stride' and 'dst_stride' parameters 00071 * see above. 00072 * 00073 * Parameters are as for PyArray_GetStridedCopyFn. 00074 */ 00075 NPY_NO_EXPORT PyArray_StridedTransferFn * 00076 PyArray_GetStridedCopySwapFn(int aligned, 00077 npy_intp src_stride, npy_intp dst_stride, 00078 npy_intp itemsize); 00079 00080 /* 00081 * Gives back a function pointer to a specialized function for copying 00082 * and swapping strided memory. This assumes each element is a pair 00083 * of values, each of which needs to be swapped. 00084 * 00085 * For information on the 'aligned', 'src_stride' and 'dst_stride' parameters 00086 * see above. 00087 * 00088 * Parameters are as for PyArray_GetStridedCopyFn. 00089 */ 00090 NPY_NO_EXPORT PyArray_StridedTransferFn * 00091 PyArray_GetStridedCopySwapPairFn(int aligned, 00092 npy_intp src_stride, npy_intp dst_stride, 00093 npy_intp itemsize); 00094 00095 /* 00096 * Gives back a transfer function and transfer data pair which copies 00097 * the data from source to dest, truncating it if the data doesn't 00098 * fit, and padding with zero bytes if there's too much space. 00099 * 00100 * For information on the 'aligned', 'src_stride' and 'dst_stride' parameters 00101 * see above. 00102 * 00103 * Returns NPY_SUCCEED or NPY_FAIL 00104 */ 00105 NPY_NO_EXPORT int 00106 PyArray_GetStridedZeroPadCopyFn(int aligned, 00107 npy_intp src_stride, npy_intp dst_stride, 00108 npy_intp src_itemsize, npy_intp dst_itemsize, 00109 PyArray_StridedTransferFn **outstransfer, 00110 NpyAuxData **outtransferdata); 00111 00112 /* 00113 * For casts between built-in numeric types, 00114 * this produces a function pointer for casting from src_type_num 00115 * to dst_type_num. If a conversion is unsupported, returns NULL 00116 * without setting a Python exception. 00117 */ 00118 NPY_NO_EXPORT PyArray_StridedTransferFn * 00119 PyArray_GetStridedNumericCastFn(int aligned, 00120 npy_intp src_stride, npy_intp dst_stride, 00121 int src_type_num, int dst_type_num); 00122 00123 /* 00124 * Gets an operation which copies elements of the given dtype, 00125 * swapping if the dtype isn't in NBO. 00126 * 00127 * Returns NPY_SUCCEED or NPY_FAIL 00128 */ 00129 NPY_NO_EXPORT int 00130 PyArray_GetDTypeCopySwapFn(int aligned, 00131 npy_intp src_stride, npy_intp dst_stride, 00132 PyArray_Descr *dtype, 00133 PyArray_StridedTransferFn **outstransfer, 00134 NpyAuxData **outtransferdata); 00135 00136 /* 00137 * If it's possible, gives back a transfer function which casts and/or 00138 * byte swaps data with the dtype 'src_dtype' into data with the dtype 00139 * 'dst_dtype'. If the outtransferdata is populated with a non-NULL value, 00140 * it must be deallocated with the ``PyArray_FreeStridedTransferData`` 00141 * function when the transfer function is no longer required. 00142 * 00143 * aligned: 00144 * Should be 1 if the src and dst pointers are always aligned, 00145 * 0 otherwise. 00146 * src_stride: 00147 * Should be the src stride if it will always be the same, 00148 * NPY_MAX_INTP otherwise. 00149 * dst_stride: 00150 * Should be the dst stride if it will always be the same, 00151 * NPY_MAX_INTP otherwise. 00152 * src_dtype: 00153 * The data type of source data. If this is NULL, a transfer 00154 * function which sets the destination to zeros is produced. 00155 * dst_dtype: 00156 * The data type of destination data. If this is NULL and 00157 * move_references is 1, a transfer function which decrements 00158 * source data references is produced. 00159 * move_references: 00160 * If 0, the destination data gets new reference ownership. 00161 * If 1, the references from the source data are moved to 00162 * the destination data. 00163 * out_stransfer: 00164 * The resulting transfer function is placed here. 00165 * out_transferdata: 00166 * The auxiliary data for the transfer function is placed here. 00167 * When finished with the transfer function, the caller must call 00168 * ``PyArray_FreeStridedTransferData`` on this data. 00169 * out_needs_api: 00170 * If this is non-NULL, and the transfer function produced needs 00171 * to call into the (Python) API, this gets set to 1. This 00172 * remains untouched if no API access is required. 00173 * 00174 * WARNING: If you set move_references to 1, it is best that src_stride is 00175 * never zero when calling the transfer function. Otherwise, the 00176 * first destination reference will get the value and all the rest 00177 * will get NULL. 00178 * 00179 * Returns NPY_SUCCEED or NPY_FAIL. 00180 */ 00181 NPY_NO_EXPORT int 00182 PyArray_GetDTypeTransferFunction(int aligned, 00183 npy_intp src_stride, npy_intp dst_stride, 00184 PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, 00185 int move_references, 00186 PyArray_StridedTransferFn **out_stransfer, 00187 NpyAuxData **out_transferdata, 00188 int *out_needs_api); 00189 00190 /* 00191 * This is identical to PyArray_GetDTypeTransferFunction, but 00192 * returns a transfer function which also takes a mask as a parameter. 00193 * Bit zero of the mask is used to determine which values to copy, 00194 * and data is transfered exactly when NpyMask_IsExposed(mask[i*mask_stride]). 00195 * 00196 * If move_references is true, values which are not copied to the 00197 * destination will still have their source reference decremented. 00198 * 00199 * If mask_dtype is NPY_BOOL or NPY_UINT8, each full element is either 00200 * transferred or not according to the mask as described above. If 00201 * dst_dtype and mask_dtype are both struct dtypes, their names must 00202 * match exactly, and the dtype of each leaf field in mask_dtype must 00203 * be either NPY_BOOL or NPY_UINT8. 00204 */ 00205 NPY_NO_EXPORT int 00206 PyArray_GetMaskedDTypeTransferFunction(int aligned, 00207 npy_intp src_stride, 00208 npy_intp dst_stride, 00209 npy_intp mask_stride, 00210 PyArray_Descr *src_dtype, 00211 PyArray_Descr *dst_dtype, 00212 PyArray_Descr *mask_dtype, 00213 int move_references, 00214 PyArray_MaskedStridedTransferFn **out_stransfer, 00215 NpyAuxData **out_transferdata, 00216 int *out_needs_api); 00217 00218 /* 00219 * Casts the specified number of elements from 'src' with data type 00220 * 'src_dtype' to 'dst' with 'dst_dtype'. See 00221 * PyArray_GetDTypeTransferFunction for more details. 00222 * 00223 * returns NPY_SUCCEED or NPY_FAIL. 00224 */ 00225 NPY_NO_EXPORT int 00226 PyArray_CastRawArrays(npy_intp count, 00227 char *src, char *dst, 00228 npy_intp src_stride, npy_intp dst_stride, 00229 PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, 00230 int move_references); 00231 00232 /* 00233 * These two functions copy or convert the data of an n-dimensional array 00234 * to/from a 1-dimensional strided buffer. These functions will only call 00235 * 'stransfer' with the provided dst_stride/src_stride and 00236 * dst_strides[0]/src_strides[0], so the caller can use those values to 00237 * specialize the function. 00238 * 00239 * The return value is the number of elements it couldn't copy. A return value 00240 * of 0 means all elements were copied, a larger value means the end of 00241 * the n-dimensional array was reached before 'count' elements were copied. 00242 * 00243 * ndim: 00244 * The number of dimensions of the n-dimensional array. 00245 * dst/src/mask: 00246 * The destination, source or mask starting pointer. 00247 * dst_stride/src_stride/mask_stride: 00248 * The stride of the 1-dimensional strided buffer 00249 * dst_strides/src_strides: 00250 * The strides of the n-dimensional array. 00251 * dst_strides_inc/src_strides_inc: 00252 * How much to add to the ..._strides pointer to get to the next stride. 00253 * coords: 00254 * The starting coordinates in the n-dimensional array. 00255 * coords_inc: 00256 * How much to add to the coords pointer to get to the next coordinate. 00257 * shape: 00258 * The shape of the n-dimensional array. 00259 * shape_inc: 00260 * How much to add to the shape pointer to get to the next shape entry. 00261 * count: 00262 * How many elements to transfer 00263 * src_itemsize: 00264 * How big each element is. If transfering between elements of different 00265 * sizes, for example a casting operation, the 'stransfer' function 00266 * should be specialized for that, in which case 'stransfer' will use 00267 * this parameter as the source item size. 00268 * stransfer: 00269 * The strided transfer function. 00270 * transferdata: 00271 * An auxiliary data pointer passed to the strided transfer function. 00272 * This follows the conventions of NpyAuxData objects. 00273 */ 00274 NPY_NO_EXPORT npy_intp 00275 PyArray_TransferNDimToStrided(npy_intp ndim, 00276 char *dst, npy_intp dst_stride, 00277 char *src, npy_intp *src_strides, npy_intp src_strides_inc, 00278 npy_intp *coords, npy_intp coords_inc, 00279 npy_intp *shape, npy_intp shape_inc, 00280 npy_intp count, npy_intp src_itemsize, 00281 PyArray_StridedTransferFn *stransfer, 00282 NpyAuxData *transferdata); 00283 00284 NPY_NO_EXPORT npy_intp 00285 PyArray_TransferStridedToNDim(npy_intp ndim, 00286 char *dst, npy_intp *dst_strides, npy_intp dst_strides_inc, 00287 char *src, npy_intp src_stride, 00288 npy_intp *coords, npy_intp coords_inc, 00289 npy_intp *shape, npy_intp shape_inc, 00290 npy_intp count, npy_intp src_itemsize, 00291 PyArray_StridedTransferFn *stransfer, 00292 NpyAuxData *transferdata); 00293 00294 NPY_NO_EXPORT npy_intp 00295 PyArray_TransferMaskedStridedToNDim(npy_intp ndim, 00296 char *dst, npy_intp *dst_strides, npy_intp dst_strides_inc, 00297 char *src, npy_intp src_stride, 00298 npy_mask *mask, npy_intp mask_stride, 00299 npy_intp *coords, npy_intp coords_inc, 00300 npy_intp *shape, npy_intp shape_inc, 00301 npy_intp count, npy_intp src_itemsize, 00302 PyArray_MaskedStridedTransferFn *stransfer, 00303 NpyAuxData *data); 00304 00305 00306 /* 00307 * TRIVIAL ITERATION 00308 * 00309 * In some cases when the iteration order isn't important, iteration over 00310 * arrays is trivial. This is the case when: 00311 * * The array has 0 or 1 dimensions. 00312 * * The array is C or Fortran contiguous. 00313 * Use of an iterator can be skipped when this occurs. These macros assist 00314 * in detecting and taking advantage of the situation. Note that it may 00315 * be worthwhile to further check if the stride is a contiguous stride 00316 * and take advantage of that. 00317 * 00318 * Here is example code for a single array: 00319 * 00320 * if (PyArray_TRIVIALLY_ITERABLE(self) { 00321 * char *data; 00322 * npy_intp count, stride; 00323 * 00324 * PyArray_PREPARE_TRIVIAL_ITERATION(self, count, data, stride); 00325 * 00326 * while (count--) { 00327 * // Use the data pointer 00328 * 00329 * data += stride; 00330 * } 00331 * } 00332 * else { 00333 * // Create iterator, etc... 00334 * } 00335 * 00336 * Here is example code for a pair of arrays: 00337 * 00338 * if (PyArray_TRIVIALLY_ITERABLE_PAIR(a1, a2) { 00339 * char *data1, *data2; 00340 * npy_intp count, stride1, stride2; 00341 * 00342 * PyArray_PREPARE_TRIVIAL_PAIR_ITERATION(a1, a2, count, 00343 * data1, data2, stride1, stride2); 00344 * 00345 * while (count--) { 00346 * // Use the data1 and data2 pointers 00347 * 00348 * data1 += stride1; 00349 * data2 += stride2; 00350 * } 00351 * } 00352 * else { 00353 * // Create iterator, etc... 00354 * } 00355 */ 00356 00357 /* 00358 * Note: Equivalently iterable macro requires one of arr1 or arr2 be 00359 * trivially iterable to be valid. 00360 */ 00361 #define PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2) ( \ 00362 PyArray_NDIM(arr1) == PyArray_NDIM(arr2) && \ 00363 PyArray_CompareLists(PyArray_DIMS(arr1), \ 00364 PyArray_DIMS(arr2), \ 00365 PyArray_NDIM(arr1)) && \ 00366 (arr1->flags&(NPY_ARRAY_C_CONTIGUOUS| \ 00367 NPY_ARRAY_F_CONTIGUOUS)) == \ 00368 (arr2->flags&(NPY_ARRAY_C_CONTIGUOUS| \ 00369 NPY_ARRAY_F_CONTIGUOUS)) \ 00370 ) 00371 00372 #define PyArray_TRIVIALLY_ITERABLE(arr) ( \ 00373 PyArray_NDIM(arr) <= 1 || \ 00374 PyArray_CHKFLAGS(arr, NPY_ARRAY_C_CONTIGUOUS) || \ 00375 PyArray_CHKFLAGS(arr, NPY_ARRAY_F_CONTIGUOUS) \ 00376 ) 00377 #define PyArray_PREPARE_TRIVIAL_ITERATION(arr, count, data, stride) \ 00378 count = PyArray_SIZE(arr), \ 00379 data = PyArray_BYTES(arr), \ 00380 stride = ((PyArray_NDIM(arr) == 0) ? 0 : \ 00381 (PyArray_CHKFLAGS(arr, NPY_ARRAY_F_CONTIGUOUS) ? \ 00382 PyArray_STRIDE(arr, 0) : \ 00383 PyArray_STRIDE(arr, \ 00384 PyArray_NDIM(arr)-1))) 00385 00386 #define PyArray_TRIVIALLY_ITERABLE_PAIR(arr1, arr2) (\ 00387 PyArray_TRIVIALLY_ITERABLE(arr1) && \ 00388 (PyArray_NDIM(arr2) == 0 || \ 00389 PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2) || \ 00390 (PyArray_NDIM(arr1) == 0 && \ 00391 PyArray_TRIVIALLY_ITERABLE(arr2) \ 00392 ) \ 00393 ) \ 00394 ) 00395 #define PyArray_PREPARE_TRIVIAL_PAIR_ITERATION(arr1, arr2, \ 00396 count, \ 00397 data1, data2, \ 00398 stride1, stride2) { \ 00399 npy_intp size1 = PyArray_SIZE(arr1); \ 00400 npy_intp size2 = PyArray_SIZE(arr2); \ 00401 count = ((size1 > size2) || size1 == 0) ? size1 : size2; \ 00402 data1 = PyArray_BYTES(arr1); \ 00403 data2 = PyArray_BYTES(arr2); \ 00404 stride1 = (size1 == 1 ? 0 : \ 00405 (PyArray_CHKFLAGS(arr1, NPY_ARRAY_F_CONTIGUOUS) ? \ 00406 PyArray_STRIDE(arr1, 0) : \ 00407 PyArray_STRIDE(arr1, \ 00408 PyArray_NDIM(arr1)-1))); \ 00409 stride2 = (size2 == 1 ? 0 : \ 00410 (PyArray_CHKFLAGS(arr2, NPY_ARRAY_F_CONTIGUOUS) ? \ 00411 PyArray_STRIDE(arr2, 0) : \ 00412 PyArray_STRIDE(arr2, \ 00413 PyArray_NDIM(arr2)-1))); \ 00414 } 00415 00416 #define PyArray_TRIVIALLY_ITERABLE_TRIPLE(arr1, arr2, arr3) (\ 00417 PyArray_TRIVIALLY_ITERABLE(arr1) && \ 00418 ((PyArray_NDIM(arr2) == 0 && \ 00419 (PyArray_NDIM(arr3) == 0 || \ 00420 PyArray_EQUIVALENTLY_ITERABLE(arr1, arr3) \ 00421 ) \ 00422 ) || \ 00423 (PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2) && \ 00424 (PyArray_NDIM(arr3) == 0 || \ 00425 PyArray_EQUIVALENTLY_ITERABLE(arr1, arr3) \ 00426 ) \ 00427 ) || \ 00428 (PyArray_NDIM(arr1) == 0 && \ 00429 PyArray_TRIVIALLY_ITERABLE(arr2) && \ 00430 (PyArray_NDIM(arr3) == 0 || \ 00431 PyArray_EQUIVALENTLY_ITERABLE(arr2, arr3) \ 00432 ) \ 00433 ) \ 00434 ) \ 00435 ) 00436 00437 #define PyArray_PREPARE_TRIVIAL_TRIPLE_ITERATION(arr1, arr2, arr3, \ 00438 count, \ 00439 data1, data2, data3, \ 00440 stride1, stride2, stride3) { \ 00441 npy_intp size1 = PyArray_SIZE(arr1); \ 00442 npy_intp size2 = PyArray_SIZE(arr2); \ 00443 npy_intp size3 = PyArray_SIZE(arr3); \ 00444 count = ((size1 > size2) || size1 == 0) ? size1 : size2; \ 00445 count = ((size3 > count) || size3 == 0) ? size3 : count; \ 00446 data1 = PyArray_BYTES(arr1); \ 00447 data2 = PyArray_BYTES(arr2); \ 00448 data3 = PyArray_BYTES(arr3); \ 00449 stride1 = (size1 == 1 ? 0 : \ 00450 (PyArray_CHKFLAGS(arr1, NPY_ARRAY_F_CONTIGUOUS) ? \ 00451 PyArray_STRIDE(arr1, 0) : \ 00452 PyArray_STRIDE(arr1, \ 00453 PyArray_NDIM(arr1)-1))); \ 00454 stride2 = (size2 == 1 ? 0 : \ 00455 (PyArray_CHKFLAGS(arr2, NPY_ARRAY_F_CONTIGUOUS) ? \ 00456 PyArray_STRIDE(arr2, 0) : \ 00457 PyArray_STRIDE(arr2, \ 00458 PyArray_NDIM(arr2)-1))); \ 00459 stride3 = (size3 == 1 ? 0 : \ 00460 (PyArray_CHKFLAGS(arr3, NPY_ARRAY_F_CONTIGUOUS) ? \ 00461 PyArray_STRIDE(arr3, 0) : \ 00462 PyArray_STRIDE(arr3, \ 00463 PyArray_NDIM(arr3)-1))); \ 00464 } 00465 00466 #endif