File: | numpy/core/src/umath/ufunc_object.c |
Warning: | line 1532, column 22 PyObject ownership leak with reference count of 1 |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | ||||
2 | * Python Universal Functions Object -- Math for all types, plus fast | ||||
3 | * arrays math | ||||
4 | * | ||||
5 | * Full description | ||||
6 | * | ||||
7 | * This supports mathematical (and Boolean) functions on arrays and other python | ||||
8 | * objects. Math on large arrays of basic C types is rather efficient. | ||||
9 | * | ||||
10 | * Travis E. Oliphant 2005, 2006 oliphant@ee.byu.edu (oliphant.travis@ieee.org) | ||||
11 | * Brigham Young University | ||||
12 | * | ||||
13 | * based on the | ||||
14 | * | ||||
15 | * Original Implementation: | ||||
16 | * Copyright (c) 1995, 1996, 1997 Jim Hugunin, hugunin@mit.edu | ||||
17 | * | ||||
18 | * with inspiration and code from | ||||
19 | * Numarray | ||||
20 | * Space Science Telescope Institute | ||||
21 | * J. Todd Miller | ||||
22 | * Perry Greenfield | ||||
23 | * Rick White | ||||
24 | * | ||||
25 | */ | ||||
26 | #define _UMATHMODULE | ||||
27 | #define _MULTIARRAYMODULE | ||||
28 | #define NPY_NO_DEPRECATED_API0x0000000E NPY_API_VERSION0x0000000E | ||||
29 | |||||
30 | #include "Python.h" | ||||
31 | #include "stddef.h" | ||||
32 | |||||
33 | #include "npy_config.h" | ||||
34 | #include "npy_pycompat.h" | ||||
35 | #include "npy_argparse.h" | ||||
36 | |||||
37 | #include "numpy/arrayobject.h" | ||||
38 | #include "numpy/ufuncobject.h" | ||||
39 | #include "numpy/arrayscalars.h" | ||||
40 | #include "lowlevel_strided_loops.h" | ||||
41 | #include "ufunc_type_resolution.h" | ||||
42 | #include "reduction.h" | ||||
43 | #include "mem_overlap.h" | ||||
44 | |||||
45 | #include "ufunc_object.h" | ||||
46 | #include "override.h" | ||||
47 | #include "npy_import.h" | ||||
48 | #include "extobj.h" | ||||
49 | #include "common.h" | ||||
50 | #include "dtypemeta.h" | ||||
51 | #include "numpyos.h" | ||||
52 | |||||
53 | /********** PRINTF DEBUG TRACING **************/ | ||||
54 | #define NPY_UF_DBG_TRACING0 0 | ||||
55 | |||||
56 | #if NPY_UF_DBG_TRACING0 | ||||
57 | #define NPY_UF_DBG_PRINT(s) {printf("%s", s)__printf_chk (2 - 1, "%s", s);fflush(stdoutstdout);} | ||||
58 | #define NPY_UF_DBG_PRINT1(s, p1) {printf((s), (p1))__printf_chk (2 - 1, (s), (p1));fflush(stdoutstdout);} | ||||
59 | #define NPY_UF_DBG_PRINT2(s, p1, p2) {printf(s, p1, p2)__printf_chk (2 - 1, s, p1, p2);fflush(stdoutstdout);} | ||||
60 | #define NPY_UF_DBG_PRINT3(s, p1, p2, p3) {printf(s, p1, p2, p3)__printf_chk (2 - 1, s, p1, p2, p3);fflush(stdoutstdout);} | ||||
61 | #else | ||||
62 | #define NPY_UF_DBG_PRINT(s) | ||||
63 | #define NPY_UF_DBG_PRINT1(s, p1) | ||||
64 | #define NPY_UF_DBG_PRINT2(s, p1, p2) | ||||
65 | #define NPY_UF_DBG_PRINT3(s, p1, p2, p3) | ||||
66 | #endif | ||||
67 | /**********************************************/ | ||||
68 | |||||
69 | typedef struct { | ||||
70 | PyObject *in; /* The input arguments to the ufunc, a tuple */ | ||||
71 | PyObject *out; /* The output arguments, a tuple. If no non-None outputs are | ||||
72 | provided, then this is NULL. */ | ||||
73 | } ufunc_full_args; | ||||
74 | |||||
75 | /* C representation of the context argument to __array_wrap__ */ | ||||
76 | typedef struct { | ||||
77 | PyUFuncObject *ufunc; | ||||
78 | ufunc_full_args args; | ||||
79 | int out_i; | ||||
80 | } _ufunc_context; | ||||
81 | |||||
82 | /* Get the arg tuple to pass in the context argument to __array_wrap__ and | ||||
83 | * __array_prepare__. | ||||
84 | * | ||||
85 | * Output arguments are only passed if at least one is non-None. | ||||
86 | */ | ||||
87 | static PyObject * | ||||
88 | _get_wrap_prepare_args(ufunc_full_args full_args) { | ||||
89 | if (full_args.out == NULL((void*)0)) { | ||||
90 | Py_INCREF(full_args.in)_Py_INCREF(((PyObject*)(full_args.in))); | ||||
91 | return full_args.in; | ||||
92 | } | ||||
93 | else { | ||||
94 | return PySequence_Concat(full_args.in, full_args.out); | ||||
95 | } | ||||
96 | } | ||||
97 | |||||
98 | /* ---------------------------------------------------------------- */ | ||||
99 | |||||
100 | static PyObject * | ||||
101 | prepare_input_arguments_for_outer(PyObject *args, PyUFuncObject *ufunc); | ||||
102 | |||||
103 | |||||
104 | /*UFUNC_API*/ | ||||
105 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) int | ||||
106 | PyUFunc_getfperr(void) | ||||
107 | { | ||||
108 | /* | ||||
109 | * non-clearing get was only added in 1.9 so this function always cleared | ||||
110 | * keep it so just in case third party code relied on the clearing | ||||
111 | */ | ||||
112 | char param = 0; | ||||
113 | return npy_clear_floatstatus_barrier(¶m); | ||||
114 | } | ||||
115 | |||||
116 | #define HANDLEIT(NAME, str) {if (retstatus & NPY_FPE_##NAME) { \ | ||||
117 | handle = errmask & UFUNC_MASK_##NAME; \ | ||||
118 | if (handle && \ | ||||
119 | _error_handler(handle >> UFUNC_SHIFT_##NAME, \ | ||||
120 | errobj, str, retstatus, first) < 0) \ | ||||
121 | return -1; \ | ||||
122 | }} | ||||
123 | |||||
124 | /*UFUNC_API*/ | ||||
125 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) int | ||||
126 | PyUFunc_handlefperr(int errmask, PyObject *errobj, int retstatus, int *first) | ||||
127 | { | ||||
128 | int handle; | ||||
129 | if (errmask && retstatus) { | ||||
130 | HANDLEIT(DIVIDEBYZERO, "divide by zero"); | ||||
131 | HANDLEIT(OVERFLOW, "overflow"); | ||||
132 | HANDLEIT(UNDERFLOW, "underflow"); | ||||
133 | HANDLEIT(INVALID, "invalid value"); | ||||
134 | } | ||||
135 | return 0; | ||||
136 | } | ||||
137 | |||||
138 | #undef HANDLEIT | ||||
139 | |||||
140 | |||||
141 | /*UFUNC_API*/ | ||||
142 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) int | ||||
143 | PyUFunc_checkfperr(int errmask, PyObject *errobj, int *first) | ||||
144 | { | ||||
145 | /* clearing is done for backward compatibility */ | ||||
146 | int retstatus; | ||||
147 | retstatus = npy_clear_floatstatus_barrier((char*)&retstatus); | ||||
148 | |||||
149 | return PyUFunc_handlefperr(errmask, errobj, retstatus, first); | ||||
150 | } | ||||
151 | |||||
152 | |||||
153 | /* Checking the status flag clears it */ | ||||
154 | /*UFUNC_API*/ | ||||
155 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) void | ||||
156 | PyUFunc_clearfperr() | ||||
157 | { | ||||
158 | char param = 0; | ||||
159 | npy_clear_floatstatus_barrier(¶m); | ||||
160 | } | ||||
161 | |||||
162 | /* | ||||
163 | * This function analyzes the input arguments and determines an appropriate | ||||
164 | * method (__array_prepare__ or __array_wrap__) function to call, taking it | ||||
165 | * from the input with the highest priority. Return NULL if no argument | ||||
166 | * defines the method. | ||||
167 | */ | ||||
168 | static PyObject* | ||||
169 | _find_array_method(PyObject *args, PyObject *method_name) | ||||
170 | { | ||||
171 | int i, n_methods; | ||||
172 | PyObject *obj; | ||||
173 | PyObject *with_method[NPY_MAXARGS32], *methods[NPY_MAXARGS32]; | ||||
174 | PyObject *method = NULL((void*)0); | ||||
175 | |||||
176 | n_methods = 0; | ||||
177 | for (i = 0; i < PyTuple_GET_SIZE(args)(((PyVarObject*)((((void) (0)), (PyTupleObject *)(args))))-> ob_size); i++) { | ||||
178 | obj = PyTuple_GET_ITEM(args, i)((((void) (0)), (PyTupleObject *)(args))->ob_item[i]); | ||||
179 | if (PyArray_CheckExact(obj)(((PyObject*)(obj))->ob_type == &PyArray_Type) || PyArray_IsAnyScalar(obj)((((((PyObject*)(obj))->ob_type) == (&PyGenericArrType_Type ) || PyType_IsSubtype((((PyObject*)(obj))->ob_type), (& PyGenericArrType_Type)))) || ((((((PyObject*)(obj))->ob_type ) == (&PyFloat_Type) || PyType_IsSubtype((((PyObject*)(obj ))->ob_type), (&PyFloat_Type))) || ((((PyObject*)(obj) )->ob_type) == (&PyComplex_Type) || PyType_IsSubtype(( ((PyObject*)(obj))->ob_type), (&PyComplex_Type))) || ( (((((PyObject*)(obj))->ob_type))->tp_flags & ((1UL << 24))) != 0) || ((((PyObject*)(obj))->ob_type) == &PyBool_Type )) || ((((((PyObject*)(obj))->ob_type))->tp_flags & ((1UL << 27))) != 0) || ((((((PyObject*)(obj))->ob_type ))->tp_flags & ((1UL << 28))) != 0)))) { | ||||
180 | continue; | ||||
181 | } | ||||
182 | method = PyObject_GetAttr(obj, method_name); | ||||
183 | if (method) { | ||||
184 | if (PyCallable_Check(method)) { | ||||
185 | with_method[n_methods] = obj; | ||||
186 | methods[n_methods] = method; | ||||
187 | ++n_methods; | ||||
188 | } | ||||
189 | else { | ||||
190 | Py_DECREF(method)_Py_DECREF(((PyObject*)(method))); | ||||
191 | method = NULL((void*)0); | ||||
192 | } | ||||
193 | } | ||||
194 | else { | ||||
195 | PyErr_Clear(); | ||||
196 | } | ||||
197 | } | ||||
198 | if (n_methods > 0) { | ||||
199 | /* If we have some methods defined, find the one of highest priority */ | ||||
200 | method = methods[0]; | ||||
201 | if (n_methods > 1) { | ||||
202 | double maxpriority = PyArray_GetPriority(with_method[0], | ||||
203 | NPY_PRIORITY0.0); | ||||
204 | for (i = 1; i < n_methods; ++i) { | ||||
205 | double priority = PyArray_GetPriority(with_method[i], | ||||
206 | NPY_PRIORITY0.0); | ||||
207 | if (priority > maxpriority) { | ||||
208 | maxpriority = priority; | ||||
209 | Py_DECREF(method)_Py_DECREF(((PyObject*)(method))); | ||||
210 | method = methods[i]; | ||||
211 | } | ||||
212 | else { | ||||
213 | Py_DECREF(methods[i])_Py_DECREF(((PyObject*)(methods[i]))); | ||||
214 | } | ||||
215 | } | ||||
216 | } | ||||
217 | } | ||||
218 | return method; | ||||
219 | } | ||||
220 | |||||
221 | /* | ||||
222 | * Returns an incref'ed pointer to the proper __array_prepare__/__array_wrap__ | ||||
223 | * method for a ufunc output argument, given the output argument `obj`, and the | ||||
224 | * method chosen from the inputs `input_method`. | ||||
225 | */ | ||||
226 | static PyObject * | ||||
227 | _get_output_array_method(PyObject *obj, PyObject *method, | ||||
228 | PyObject *input_method) { | ||||
229 | if (obj != Py_None(&_Py_NoneStruct)) { | ||||
230 | PyObject *ometh; | ||||
231 | |||||
232 | if (PyArray_CheckExact(obj)(((PyObject*)(obj))->ob_type == &PyArray_Type)) { | ||||
233 | /* | ||||
234 | * No need to wrap regular arrays - None signals to not call | ||||
235 | * wrap/prepare at all | ||||
236 | */ | ||||
237 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
238 | } | ||||
239 | |||||
240 | ometh = PyObject_GetAttr(obj, method); | ||||
241 | if (ometh == NULL((void*)0)) { | ||||
242 | PyErr_Clear(); | ||||
243 | } | ||||
244 | else if (!PyCallable_Check(ometh)) { | ||||
245 | Py_DECREF(ometh)_Py_DECREF(((PyObject*)(ometh))); | ||||
246 | } | ||||
247 | else { | ||||
248 | /* Use the wrap/prepare method of the output if it's callable */ | ||||
249 | return ometh; | ||||
250 | } | ||||
251 | } | ||||
252 | |||||
253 | /* Fall back on the input's wrap/prepare */ | ||||
254 | Py_XINCREF(input_method)_Py_XINCREF(((PyObject*)(input_method))); | ||||
255 | return input_method; | ||||
256 | } | ||||
257 | |||||
258 | /* | ||||
259 | * This function analyzes the input arguments | ||||
260 | * and determines an appropriate __array_prepare__ function to call | ||||
261 | * for the outputs. | ||||
262 | * | ||||
263 | * If an output argument is provided, then it is prepped | ||||
264 | * with its own __array_prepare__ not with the one determined by | ||||
265 | * the input arguments. | ||||
266 | * | ||||
267 | * if the provided output argument is already an ndarray, | ||||
268 | * the prepping function is None (which means no prepping will | ||||
269 | * be done --- not even PyArray_Return). | ||||
270 | * | ||||
271 | * A NULL is placed in output_prep for outputs that | ||||
272 | * should just have PyArray_Return called. | ||||
273 | */ | ||||
274 | static void | ||||
275 | _find_array_prepare(ufunc_full_args args, | ||||
276 | PyObject **output_prep, int nout) | ||||
277 | { | ||||
278 | int i; | ||||
279 | PyObject *prep; | ||||
280 | |||||
281 | /* | ||||
282 | * Determine the prepping function given by the input arrays | ||||
283 | * (could be NULL). | ||||
284 | */ | ||||
285 | prep = _find_array_method(args.in, npy_um_str_array_prepare); | ||||
286 | /* | ||||
287 | * For all the output arrays decide what to do. | ||||
288 | * | ||||
289 | * 1) Use the prep function determined from the input arrays | ||||
290 | * This is the default if the output array is not | ||||
291 | * passed in. | ||||
292 | * | ||||
293 | * 2) Use the __array_prepare__ method of the output object. | ||||
294 | * This is special cased for | ||||
295 | * exact ndarray so that no PyArray_Return is | ||||
296 | * done in that case. | ||||
297 | */ | ||||
298 | if (args.out == NULL((void*)0)) { | ||||
299 | for (i = 0; i < nout; i++) { | ||||
300 | Py_XINCREF(prep)_Py_XINCREF(((PyObject*)(prep))); | ||||
301 | output_prep[i] = prep; | ||||
302 | } | ||||
303 | } | ||||
304 | else { | ||||
305 | for (i = 0; i < nout; i++) { | ||||
306 | output_prep[i] = _get_output_array_method( | ||||
307 | PyTuple_GET_ITEM(args.out, i)((((void) (0)), (PyTupleObject *)(args.out))->ob_item[i]), npy_um_str_array_prepare, prep); | ||||
308 | } | ||||
309 | } | ||||
310 | Py_XDECREF(prep)_Py_XDECREF(((PyObject*)(prep))); | ||||
311 | return; | ||||
312 | } | ||||
313 | |||||
314 | #define NPY_UFUNC_DEFAULT_INPUT_FLAGS0x00020000 | 0x00100000 | 0x40000000 \ | ||||
315 | NPY_ITER_READONLY0x00020000 | \ | ||||
316 | NPY_ITER_ALIGNED0x00100000 | \ | ||||
317 | NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE0x40000000 | ||||
318 | |||||
319 | #define NPY_UFUNC_DEFAULT_OUTPUT_FLAGS0x00100000 | 0x01000000 | 0x08000000 | 0x02000000 | 0x40000000 \ | ||||
320 | NPY_ITER_ALIGNED0x00100000 | \ | ||||
321 | NPY_ITER_ALLOCATE0x01000000 | \ | ||||
322 | NPY_ITER_NO_BROADCAST0x08000000 | \ | ||||
323 | NPY_ITER_NO_SUBTYPE0x02000000 | \ | ||||
324 | NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE0x40000000 | ||||
325 | |||||
326 | /* Called at module initialization to set the matmul ufunc output flags */ | ||||
327 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) int | ||||
328 | set_matmul_flags(PyObject *d) | ||||
329 | { | ||||
330 | PyObject *matmul = _PyDict_GetItemStringWithError(d, "matmul"); | ||||
331 | if (matmul == NULL((void*)0)) { | ||||
332 | return -1; | ||||
333 | } | ||||
334 | /* | ||||
335 | * The default output flag NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE allows | ||||
336 | * perfectly overlapping input and output (in-place operations). While | ||||
337 | * correct for the common mathematical operations, this assumption is | ||||
338 | * incorrect in the general case and specifically in the case of matmul. | ||||
339 | * | ||||
340 | * NPY_ITER_UPDATEIFCOPY is added by default in | ||||
341 | * PyUFunc_GeneralizedFunction, which is the variant called for gufuncs | ||||
342 | * with a signature | ||||
343 | * | ||||
344 | * Enabling NPY_ITER_WRITEONLY can prevent a copy in some cases. | ||||
345 | */ | ||||
346 | ((PyUFuncObject *)matmul)->op_flags[2] = (NPY_ITER_WRITEONLY0x00040000 | | ||||
347 | NPY_ITER_UPDATEIFCOPY0x00800000 | | ||||
348 | NPY_UFUNC_DEFAULT_OUTPUT_FLAGS0x00100000 | 0x01000000 | 0x08000000 | 0x02000000 | 0x40000000) & | ||||
349 | ~NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE0x40000000; | ||||
350 | return 0; | ||||
351 | } | ||||
352 | |||||
353 | |||||
354 | /* | ||||
355 | * Set per-operand flags according to desired input or output flags. | ||||
356 | * op_flags[i] for i in input (as determined by ufunc->nin) will be | ||||
357 | * merged with op_in_flags, perhaps overriding per-operand flags set | ||||
358 | * in previous stages. | ||||
359 | * op_flags[i] for i in output will be set to op_out_flags only if previously | ||||
360 | * unset. | ||||
361 | * The input flag behavior preserves backward compatibility, while the | ||||
362 | * output flag behaviour is the "correct" one for maximum flexibility. | ||||
363 | */ | ||||
364 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) void | ||||
365 | _ufunc_setup_flags(PyUFuncObject *ufunc, npy_uint32 op_in_flags, | ||||
366 | npy_uint32 op_out_flags, npy_uint32 *op_flags) | ||||
367 | { | ||||
368 | int nin = ufunc->nin; | ||||
369 | int nout = ufunc->nout; | ||||
370 | int nop = nin + nout, i; | ||||
371 | /* Set up the flags */ | ||||
372 | for (i = 0; i < nin; ++i) { | ||||
373 | op_flags[i] = ufunc->op_flags[i] | op_in_flags; | ||||
374 | /* | ||||
375 | * If READWRITE flag has been set for this operand, | ||||
376 | * then clear default READONLY flag | ||||
377 | */ | ||||
378 | if (op_flags[i] & (NPY_ITER_READWRITE0x00010000 | NPY_ITER_WRITEONLY0x00040000)) { | ||||
379 | op_flags[i] &= ~NPY_ITER_READONLY0x00020000; | ||||
380 | } | ||||
381 | } | ||||
382 | for (i = nin; i < nop; ++i) { | ||||
383 | op_flags[i] = ufunc->op_flags[i] ? ufunc->op_flags[i] : op_out_flags; | ||||
384 | } | ||||
385 | } | ||||
386 | |||||
387 | /* | ||||
388 | * This function analyzes the input arguments | ||||
389 | * and determines an appropriate __array_wrap__ function to call | ||||
390 | * for the outputs. | ||||
391 | * | ||||
392 | * If an output argument is provided, then it is wrapped | ||||
393 | * with its own __array_wrap__ not with the one determined by | ||||
394 | * the input arguments. | ||||
395 | * | ||||
396 | * if the provided output argument is already an array, | ||||
397 | * the wrapping function is None (which means no wrapping will | ||||
398 | * be done --- not even PyArray_Return). | ||||
399 | * | ||||
400 | * A NULL is placed in output_wrap for outputs that | ||||
401 | * should just have PyArray_Return called. | ||||
402 | */ | ||||
403 | static void | ||||
404 | _find_array_wrap(ufunc_full_args args, npy_bool subok, | ||||
405 | PyObject **output_wrap, int nin, int nout) | ||||
406 | { | ||||
407 | int i; | ||||
408 | PyObject *wrap = NULL((void*)0); | ||||
409 | |||||
410 | /* | ||||
411 | * If a 'subok' parameter is passed and isn't True, don't wrap but put None | ||||
412 | * into slots with out arguments which means return the out argument | ||||
413 | */ | ||||
414 | if (!subok) { | ||||
415 | goto handle_out; | ||||
416 | } | ||||
417 | |||||
418 | /* | ||||
419 | * Determine the wrapping function given by the input arrays | ||||
420 | * (could be NULL). | ||||
421 | */ | ||||
422 | wrap = _find_array_method(args.in, npy_um_str_array_wrap); | ||||
423 | |||||
424 | /* | ||||
425 | * For all the output arrays decide what to do. | ||||
426 | * | ||||
427 | * 1) Use the wrap function determined from the input arrays | ||||
428 | * This is the default if the output array is not | ||||
429 | * passed in. | ||||
430 | * | ||||
431 | * 2) Use the __array_wrap__ method of the output object | ||||
432 | * passed in. -- this is special cased for | ||||
433 | * exact ndarray so that no PyArray_Return is | ||||
434 | * done in that case. | ||||
435 | */ | ||||
436 | handle_out: | ||||
437 | if (args.out == NULL((void*)0)) { | ||||
438 | for (i = 0; i < nout; i++) { | ||||
439 | Py_XINCREF(wrap)_Py_XINCREF(((PyObject*)(wrap))); | ||||
440 | output_wrap[i] = wrap; | ||||
441 | } | ||||
442 | } | ||||
443 | else { | ||||
444 | for (i = 0; i < nout; i++) { | ||||
445 | output_wrap[i] = _get_output_array_method( | ||||
446 | PyTuple_GET_ITEM(args.out, i)((((void) (0)), (PyTupleObject *)(args.out))->ob_item[i]), npy_um_str_array_wrap, wrap); | ||||
447 | } | ||||
448 | } | ||||
449 | |||||
450 | Py_XDECREF(wrap)_Py_XDECREF(((PyObject*)(wrap))); | ||||
451 | } | ||||
452 | |||||
453 | |||||
454 | /* | ||||
455 | * Apply the __array_wrap__ function with the given array and content. | ||||
456 | * | ||||
457 | * Interprets wrap=None and wrap=NULL as intended by _find_array_wrap | ||||
458 | * | ||||
459 | * Steals a reference to obj and wrap. | ||||
460 | * Pass context=NULL to indicate there is no context. | ||||
461 | */ | ||||
462 | static PyObject * | ||||
463 | _apply_array_wrap( | ||||
464 | PyObject *wrap, PyArrayObject *obj, _ufunc_context const *context) { | ||||
465 | if (wrap == NULL((void*)0)) { | ||||
466 | /* default behavior */ | ||||
467 | return PyArray_Return(obj); | ||||
468 | } | ||||
469 | else if (wrap == Py_None(&_Py_NoneStruct)) { | ||||
470 | Py_DECREF(wrap)_Py_DECREF(((PyObject*)(wrap))); | ||||
471 | return (PyObject *)obj; | ||||
472 | } | ||||
473 | else { | ||||
474 | PyObject *res; | ||||
475 | PyObject *py_context = NULL((void*)0); | ||||
476 | |||||
477 | /* Convert the context object to a tuple, if present */ | ||||
478 | if (context == NULL((void*)0)) { | ||||
479 | py_context = Py_None(&_Py_NoneStruct); | ||||
480 | Py_INCREF(py_context)_Py_INCREF(((PyObject*)(py_context))); | ||||
481 | } | ||||
482 | else { | ||||
483 | PyObject *args_tup; | ||||
484 | /* Call the method with appropriate context */ | ||||
485 | args_tup = _get_wrap_prepare_args(context->args); | ||||
486 | if (args_tup == NULL((void*)0)) { | ||||
487 | goto fail; | ||||
488 | } | ||||
489 | py_context = Py_BuildValue("OOi", | ||||
490 | context->ufunc, args_tup, context->out_i); | ||||
491 | Py_DECREF(args_tup)_Py_DECREF(((PyObject*)(args_tup))); | ||||
492 | if (py_context == NULL((void*)0)) { | ||||
493 | goto fail; | ||||
494 | } | ||||
495 | } | ||||
496 | /* try __array_wrap__(obj, context) */ | ||||
497 | res = PyObject_CallFunctionObjArgs(wrap, obj, py_context, NULL((void*)0)); | ||||
498 | Py_DECREF(py_context)_Py_DECREF(((PyObject*)(py_context))); | ||||
499 | |||||
500 | /* try __array_wrap__(obj) if the context argument is not accepted */ | ||||
501 | if (res == NULL((void*)0) && PyErr_ExceptionMatches(PyExc_TypeError)) { | ||||
502 | PyErr_Clear(); | ||||
503 | res = PyObject_CallFunctionObjArgs(wrap, obj, NULL((void*)0)); | ||||
504 | } | ||||
505 | Py_DECREF(wrap)_Py_DECREF(((PyObject*)(wrap))); | ||||
506 | Py_DECREF(obj)_Py_DECREF(((PyObject*)(obj))); | ||||
507 | return res; | ||||
508 | fail: | ||||
509 | Py_DECREF(wrap)_Py_DECREF(((PyObject*)(wrap))); | ||||
510 | Py_DECREF(obj)_Py_DECREF(((PyObject*)(obj))); | ||||
511 | return NULL((void*)0); | ||||
512 | } | ||||
513 | } | ||||
514 | |||||
515 | |||||
516 | /*UFUNC_API | ||||
517 | * | ||||
518 | * On return, if errobj is populated with a non-NULL value, the caller | ||||
519 | * owns a new reference to errobj. | ||||
520 | */ | ||||
521 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) int | ||||
522 | PyUFunc_GetPyValues(char *name, int *bufsize, int *errmask, PyObject **errobj) | ||||
523 | { | ||||
524 | PyObject *ref = get_global_ext_obj(); | ||||
525 | |||||
526 | return _extract_pyvals(ref, name, bufsize, errmask, errobj); | ||||
527 | } | ||||
528 | |||||
529 | /* Return the position of next non-white-space char in the string */ | ||||
530 | static int | ||||
531 | _next_non_white_space(const char* str, int offset) | ||||
532 | { | ||||
533 | int ret = offset; | ||||
534 | while (str[ret] == ' ' || str[ret] == '\t') { | ||||
535 | ret++; | ||||
536 | } | ||||
537 | return ret; | ||||
538 | } | ||||
539 | |||||
540 | static int | ||||
541 | _is_alpha_underscore(char ch) | ||||
542 | { | ||||
543 | return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_'; | ||||
544 | } | ||||
545 | |||||
546 | static int | ||||
547 | _is_alnum_underscore(char ch) | ||||
548 | { | ||||
549 | return _is_alpha_underscore(ch) || (ch >= '0' && ch <= '9'); | ||||
550 | } | ||||
551 | |||||
552 | /* | ||||
553 | * Convert a string into a number | ||||
554 | */ | ||||
555 | static npy_intp | ||||
556 | _get_size(const char* str) | ||||
557 | { | ||||
558 | char *stop; | ||||
559 | npy_longlong size = NumPyOS_strtoll(str, &stop, 10); | ||||
560 | |||||
561 | if (stop == str || _is_alpha_underscore(*stop)) { | ||||
562 | /* not a well formed number */ | ||||
563 | return -1; | ||||
564 | } | ||||
565 | if (size >= NPY_MAX_INTP9223372036854775807L || size <= NPY_MIN_INTP(-9223372036854775807L -1L)) { | ||||
566 | /* len(str) too long */ | ||||
567 | return -1; | ||||
568 | } | ||||
569 | return size; | ||||
570 | } | ||||
571 | |||||
572 | /* | ||||
573 | * Return the ending position of a variable name including optional modifier | ||||
574 | */ | ||||
575 | static int | ||||
576 | _get_end_of_name(const char* str, int offset) | ||||
577 | { | ||||
578 | int ret = offset; | ||||
579 | while (_is_alnum_underscore(str[ret])) { | ||||
580 | ret++; | ||||
581 | } | ||||
582 | if (str[ret] == '?') { | ||||
583 | ret ++; | ||||
584 | } | ||||
585 | return ret; | ||||
586 | } | ||||
587 | |||||
588 | /* | ||||
589 | * Returns 1 if the dimension names pointed by s1 and s2 are the same, | ||||
590 | * otherwise returns 0. | ||||
591 | */ | ||||
592 | static int | ||||
593 | _is_same_name(const char* s1, const char* s2) | ||||
594 | { | ||||
595 | while (_is_alnum_underscore(*s1) && _is_alnum_underscore(*s2)) { | ||||
596 | if (*s1 != *s2) { | ||||
597 | return 0; | ||||
598 | } | ||||
599 | s1++; | ||||
600 | s2++; | ||||
601 | } | ||||
602 | return !_is_alnum_underscore(*s1) && !_is_alnum_underscore(*s2); | ||||
603 | } | ||||
604 | |||||
605 | /* | ||||
606 | * Sets core_num_dim_ix, core_num_dims, core_dim_ixs, core_offsets, | ||||
607 | * and core_signature in PyUFuncObject "ufunc". Returns 0 unless an | ||||
608 | * error occurred. | ||||
609 | */ | ||||
610 | static int | ||||
611 | _parse_signature(PyUFuncObject *ufunc, const char *signature) | ||||
612 | { | ||||
613 | size_t len; | ||||
614 | char const **var_names; | ||||
615 | int nd = 0; /* number of dimension of the current argument */ | ||||
616 | int cur_arg = 0; /* index into core_num_dims&core_offsets */ | ||||
617 | int cur_core_dim = 0; /* index into core_dim_ixs */ | ||||
618 | int i = 0; | ||||
619 | char *parse_error = NULL((void*)0); | ||||
620 | |||||
621 | if (signature == NULL((void*)0)) { | ||||
622 | PyErr_SetString(PyExc_RuntimeError, | ||||
623 | "_parse_signature with NULL signature"); | ||||
624 | return -1; | ||||
625 | } | ||||
626 | len = strlen(signature); | ||||
627 | ufunc->core_signature = PyArray_mallocPyMem_RawMalloc(sizeof(char) * (len+1)); | ||||
628 | if (ufunc->core_signature) { | ||||
629 | strcpy(ufunc->core_signature, signature); | ||||
630 | } | ||||
631 | /* Allocate sufficient memory to store pointers to all dimension names */ | ||||
632 | var_names = PyArray_mallocPyMem_RawMalloc(sizeof(char const*) * len); | ||||
633 | if (var_names == NULL((void*)0)) { | ||||
634 | PyErr_NoMemory(); | ||||
635 | return -1; | ||||
636 | } | ||||
637 | |||||
638 | ufunc->core_enabled = 1; | ||||
639 | ufunc->core_num_dim_ix = 0; | ||||
640 | ufunc->core_num_dims = PyArray_mallocPyMem_RawMalloc(sizeof(int) * ufunc->nargs); | ||||
641 | ufunc->core_offsets = PyArray_mallocPyMem_RawMalloc(sizeof(int) * ufunc->nargs); | ||||
642 | /* The next three items will be shrunk later */ | ||||
643 | ufunc->core_dim_ixs = PyArray_mallocPyMem_RawMalloc(sizeof(int) * len); | ||||
644 | ufunc->core_dim_sizes = PyArray_mallocPyMem_RawMalloc(sizeof(npy_intp) * len); | ||||
645 | ufunc->core_dim_flags = PyArray_mallocPyMem_RawMalloc(sizeof(npy_uint32) * len); | ||||
646 | |||||
647 | if (ufunc->core_num_dims == NULL((void*)0) || ufunc->core_dim_ixs == NULL((void*)0) || | ||||
648 | ufunc->core_offsets == NULL((void*)0) || | ||||
649 | ufunc->core_dim_sizes == NULL((void*)0) || | ||||
650 | ufunc->core_dim_flags == NULL((void*)0)) { | ||||
651 | PyErr_NoMemory(); | ||||
652 | goto fail; | ||||
653 | } | ||||
654 | for (size_t j = 0; j < len; j++) { | ||||
655 | ufunc->core_dim_flags[j] = 0; | ||||
656 | } | ||||
657 | |||||
658 | i = _next_non_white_space(signature, 0); | ||||
659 | while (signature[i] != '\0') { | ||||
660 | /* loop over input/output arguments */ | ||||
661 | if (cur_arg == ufunc->nin) { | ||||
662 | /* expect "->" */ | ||||
663 | if (signature[i] != '-' || signature[i+1] != '>') { | ||||
664 | parse_error = "expect '->'"; | ||||
665 | goto fail; | ||||
666 | } | ||||
667 | i = _next_non_white_space(signature, i + 2); | ||||
668 | } | ||||
669 | |||||
670 | /* | ||||
671 | * parse core dimensions of one argument, | ||||
672 | * e.g. "()", "(i)", or "(i,j)" | ||||
673 | */ | ||||
674 | if (signature[i] != '(') { | ||||
675 | parse_error = "expect '('"; | ||||
676 | goto fail; | ||||
677 | } | ||||
678 | i = _next_non_white_space(signature, i + 1); | ||||
679 | while (signature[i] != ')') { | ||||
680 | /* loop over core dimensions */ | ||||
681 | int ix, i_end; | ||||
682 | npy_intp frozen_size; | ||||
683 | npy_bool can_ignore; | ||||
684 | |||||
685 | if (signature[i] == '\0') { | ||||
686 | parse_error = "unexpected end of signature string"; | ||||
687 | goto fail; | ||||
688 | } | ||||
689 | /* | ||||
690 | * Is this a variable or a fixed size dimension? | ||||
691 | */ | ||||
692 | if (_is_alpha_underscore(signature[i])) { | ||||
693 | frozen_size = -1; | ||||
694 | } | ||||
695 | else { | ||||
696 | frozen_size = (npy_intp)_get_size(signature + i); | ||||
697 | if (frozen_size <= 0) { | ||||
698 | parse_error = "expect dimension name or non-zero frozen size"; | ||||
699 | goto fail; | ||||
700 | } | ||||
701 | } | ||||
702 | /* Is this dimension flexible? */ | ||||
703 | i_end = _get_end_of_name(signature, i); | ||||
704 | can_ignore = (i_end > 0 && signature[i_end - 1] == '?'); | ||||
705 | /* | ||||
706 | * Determine whether we already saw this dimension name, | ||||
707 | * get its index, and set its properties | ||||
708 | */ | ||||
709 | for(ix = 0; ix < ufunc->core_num_dim_ix; ix++) { | ||||
710 | if (frozen_size > 0 ? | ||||
711 | frozen_size == ufunc->core_dim_sizes[ix] : | ||||
712 | _is_same_name(signature + i, var_names[ix])) { | ||||
713 | break; | ||||
714 | } | ||||
715 | } | ||||
716 | /* | ||||
717 | * If a new dimension, store its properties; if old, check consistency. | ||||
718 | */ | ||||
719 | if (ix == ufunc->core_num_dim_ix) { | ||||
720 | ufunc->core_num_dim_ix++; | ||||
721 | var_names[ix] = signature + i; | ||||
722 | ufunc->core_dim_sizes[ix] = frozen_size; | ||||
723 | if (frozen_size < 0) { | ||||
724 | ufunc->core_dim_flags[ix] |= UFUNC_CORE_DIM_SIZE_INFERRED0x0002; | ||||
725 | } | ||||
726 | if (can_ignore) { | ||||
727 | ufunc->core_dim_flags[ix] |= UFUNC_CORE_DIM_CAN_IGNORE0x0004; | ||||
728 | } | ||||
729 | } else { | ||||
730 | if (can_ignore && !(ufunc->core_dim_flags[ix] & | ||||
731 | UFUNC_CORE_DIM_CAN_IGNORE0x0004)) { | ||||
732 | parse_error = "? cannot be used, name already seen without ?"; | ||||
733 | goto fail; | ||||
734 | } | ||||
735 | if (!can_ignore && (ufunc->core_dim_flags[ix] & | ||||
736 | UFUNC_CORE_DIM_CAN_IGNORE0x0004)) { | ||||
737 | parse_error = "? must be used, name already seen with ?"; | ||||
738 | goto fail; | ||||
739 | } | ||||
740 | } | ||||
741 | ufunc->core_dim_ixs[cur_core_dim] = ix; | ||||
742 | cur_core_dim++; | ||||
743 | nd++; | ||||
744 | i = _next_non_white_space(signature, i_end); | ||||
745 | if (signature[i] != ',' && signature[i] != ')') { | ||||
746 | parse_error = "expect ',' or ')'"; | ||||
747 | goto fail; | ||||
748 | } | ||||
749 | if (signature[i] == ',') | ||||
750 | { | ||||
751 | i = _next_non_white_space(signature, i + 1); | ||||
752 | if (signature[i] == ')') { | ||||
753 | parse_error = "',' must not be followed by ')'"; | ||||
754 | goto fail; | ||||
755 | } | ||||
756 | } | ||||
757 | } | ||||
758 | ufunc->core_num_dims[cur_arg] = nd; | ||||
759 | ufunc->core_offsets[cur_arg] = cur_core_dim-nd; | ||||
760 | cur_arg++; | ||||
761 | nd = 0; | ||||
762 | |||||
763 | i = _next_non_white_space(signature, i + 1); | ||||
764 | if (cur_arg != ufunc->nin && cur_arg != ufunc->nargs) { | ||||
765 | /* | ||||
766 | * The list of input arguments (or output arguments) was | ||||
767 | * only read partially | ||||
768 | */ | ||||
769 | if (signature[i] != ',') { | ||||
770 | parse_error = "expect ','"; | ||||
771 | goto fail; | ||||
772 | } | ||||
773 | i = _next_non_white_space(signature, i + 1); | ||||
774 | } | ||||
775 | } | ||||
776 | if (cur_arg != ufunc->nargs) { | ||||
777 | parse_error = "incomplete signature: not all arguments found"; | ||||
778 | goto fail; | ||||
779 | } | ||||
780 | ufunc->core_dim_ixs = PyArray_reallocPyMem_RawRealloc(ufunc->core_dim_ixs, | ||||
781 | sizeof(int) * cur_core_dim); | ||||
782 | ufunc->core_dim_sizes = PyArray_reallocPyMem_RawRealloc( | ||||
783 | ufunc->core_dim_sizes, | ||||
784 | sizeof(npy_intp) * ufunc->core_num_dim_ix); | ||||
785 | ufunc->core_dim_flags = PyArray_reallocPyMem_RawRealloc( | ||||
786 | ufunc->core_dim_flags, | ||||
787 | sizeof(npy_uint32) * ufunc->core_num_dim_ix); | ||||
788 | |||||
789 | /* check for trivial core-signature, e.g. "(),()->()" */ | ||||
790 | if (cur_core_dim == 0) { | ||||
791 | ufunc->core_enabled = 0; | ||||
792 | } | ||||
793 | PyArray_freePyMem_RawFree((void*)var_names); | ||||
794 | return 0; | ||||
795 | |||||
796 | fail: | ||||
797 | PyArray_freePyMem_RawFree((void*)var_names); | ||||
798 | if (parse_error) { | ||||
799 | PyErr_Format(PyExc_ValueError, | ||||
800 | "%s at position %d in \"%s\"", | ||||
801 | parse_error, i, signature); | ||||
802 | } | ||||
803 | return -1; | ||||
804 | } | ||||
805 | |||||
806 | /* | ||||
807 | * Checks if 'obj' is a valid output array for a ufunc, i.e. it is | ||||
808 | * either None or a writeable array, increments its reference count | ||||
809 | * and stores a pointer to it in 'store'. Returns 0 on success, sets | ||||
810 | * an exception and returns -1 on failure. | ||||
811 | */ | ||||
812 | static int | ||||
813 | _set_out_array(PyObject *obj, PyArrayObject **store) | ||||
814 | { | ||||
815 | if (obj == Py_None(&_Py_NoneStruct)) { | ||||
816 | /* Translate None to NULL */ | ||||
817 | return 0; | ||||
818 | } | ||||
819 | if (PyArray_Check(obj)((((PyObject*)(obj))->ob_type) == (&PyArray_Type) || PyType_IsSubtype ((((PyObject*)(obj))->ob_type), (&PyArray_Type)))) { | ||||
820 | /* If it's an array, store it */ | ||||
821 | if (PyArray_FailUnlessWriteable((PyArrayObject *)obj, | ||||
822 | "output array") < 0) { | ||||
823 | return -1; | ||||
824 | } | ||||
825 | Py_INCREF(obj)_Py_INCREF(((PyObject*)(obj))); | ||||
826 | *store = (PyArrayObject *)obj; | ||||
827 | |||||
828 | return 0; | ||||
829 | } | ||||
830 | PyErr_SetString(PyExc_TypeError, "return arrays must be of ArrayType"); | ||||
831 | |||||
832 | return -1; | ||||
833 | } | ||||
834 | |||||
835 | /********* GENERIC UFUNC USING ITERATOR *********/ | ||||
836 | |||||
837 | /* | ||||
838 | * Produce a name for the ufunc, if one is not already set | ||||
839 | * This is used in the PyUFunc_handlefperr machinery, and in error messages | ||||
840 | */ | ||||
841 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) const char* | ||||
842 | ufunc_get_name_cstr(PyUFuncObject *ufunc) { | ||||
843 | return ufunc->name ? ufunc->name : "<unnamed ufunc>"; | ||||
844 | } | ||||
845 | |||||
846 | |||||
847 | /* | ||||
848 | * Converters for use in parsing of keywords arguments. | ||||
849 | */ | ||||
850 | static int | ||||
851 | _subok_converter(PyObject *obj, npy_bool *subok) | ||||
852 | { | ||||
853 | if (PyBool_Check(obj)((((PyObject*)(obj))->ob_type) == &PyBool_Type)) { | ||||
854 | *subok = (obj == Py_True((PyObject *) &_Py_TrueStruct)); | ||||
855 | return NPY_SUCCEED1; | ||||
856 | } | ||||
857 | else { | ||||
858 | PyErr_SetString(PyExc_TypeError, | ||||
859 | "'subok' must be a boolean"); | ||||
860 | return NPY_FAIL0; | ||||
861 | } | ||||
862 | } | ||||
863 | |||||
864 | static int | ||||
865 | _keepdims_converter(PyObject *obj, int *keepdims) | ||||
866 | { | ||||
867 | if (PyBool_Check(obj)((((PyObject*)(obj))->ob_type) == &PyBool_Type)) { | ||||
868 | *keepdims = (obj == Py_True((PyObject *) &_Py_TrueStruct)); | ||||
869 | return NPY_SUCCEED1; | ||||
870 | } | ||||
871 | else { | ||||
872 | PyErr_SetString(PyExc_TypeError, | ||||
873 | "'keepdims' must be a boolean"); | ||||
874 | return NPY_FAIL0; | ||||
875 | } | ||||
876 | } | ||||
877 | |||||
878 | static int | ||||
879 | _wheremask_converter(PyObject *obj, PyArrayObject **wheremask) | ||||
880 | { | ||||
881 | /* | ||||
882 | * Optimization: where=True is the same as no where argument. | ||||
883 | * This lets us document True as the default. | ||||
884 | */ | ||||
885 | if (obj == Py_True((PyObject *) &_Py_TrueStruct)) { | ||||
886 | return NPY_SUCCEED1; | ||||
887 | } | ||||
888 | else { | ||||
889 | PyArray_Descr *dtype = PyArray_DescrFromType(NPY_BOOL); | ||||
890 | if (dtype == NULL((void*)0)) { | ||||
891 | return NPY_FAIL0; | ||||
892 | } | ||||
893 | /* PyArray_FromAny steals reference to dtype, even on failure */ | ||||
894 | *wheremask = (PyArrayObject *)PyArray_FromAny(obj, dtype, 0, 0, 0, NULL((void*)0)); | ||||
895 | if ((*wheremask) == NULL((void*)0)) { | ||||
896 | return NPY_FAIL0; | ||||
897 | } | ||||
898 | return NPY_SUCCEED1; | ||||
899 | } | ||||
900 | } | ||||
901 | |||||
902 | |||||
903 | /* | ||||
904 | * Due to the array override, do the actual parameter conversion | ||||
905 | * only in this step. This function takes the reference objects and | ||||
906 | * parses them into the desired values. | ||||
907 | * This function cleans up after itself and NULLs references on error, | ||||
908 | * however, the caller has to ensure that `out_op[0:nargs]` and `out_whermeask` | ||||
909 | * are NULL initialized. | ||||
910 | */ | ||||
911 | static int | ||||
912 | convert_ufunc_arguments(PyUFuncObject *ufunc, | ||||
913 | ufunc_full_args full_args, PyArrayObject **out_op, | ||||
914 | PyObject *order_obj, NPY_ORDER *out_order, | ||||
915 | PyObject *casting_obj, NPY_CASTING *out_casting, | ||||
916 | PyObject *subok_obj, npy_bool *out_subok, | ||||
917 | PyObject *where_obj, PyArrayObject **out_wheremask, /* PyArray of bool */ | ||||
918 | PyObject *keepdims_obj, int *out_keepdims) | ||||
919 | { | ||||
920 | int nin = ufunc->nin; | ||||
921 | int nout = ufunc->nout; | ||||
922 | int nop = ufunc->nargs; | ||||
923 | PyObject *obj; | ||||
924 | |||||
925 | /* Convert and fill in input arguments */ | ||||
926 | for (int i = 0; i < nin; i++) { | ||||
927 | obj = PyTuple_GET_ITEM(full_args.in, i)((((void) (0)), (PyTupleObject *)(full_args.in))->ob_item[ i]); | ||||
928 | |||||
929 | if (PyArray_Check(obj)((((PyObject*)(obj))->ob_type) == (&PyArray_Type) || PyType_IsSubtype ((((PyObject*)(obj))->ob_type), (&PyArray_Type)))) { | ||||
930 | PyArrayObject *obj_a = (PyArrayObject *)obj; | ||||
931 | out_op[i] = (PyArrayObject *)PyArray_FromArray(obj_a, NULL((void*)0), 0); | ||||
932 | } | ||||
933 | else { | ||||
934 | out_op[i] = (PyArrayObject *)PyArray_FromAny(obj, | ||||
935 | NULL((void*)0), 0, 0, 0, NULL((void*)0)); | ||||
936 | } | ||||
937 | |||||
938 | if (out_op[i] == NULL((void*)0)) { | ||||
939 | goto fail; | ||||
940 | } | ||||
941 | } | ||||
942 | |||||
943 | /* Convert and fill in output arguments */ | ||||
944 | if (full_args.out != NULL((void*)0)) { | ||||
945 | for (int i = 0; i < nout; i++) { | ||||
946 | obj = PyTuple_GET_ITEM(full_args.out, i)((((void) (0)), (PyTupleObject *)(full_args.out))->ob_item [i]); | ||||
947 | if (_set_out_array(obj, out_op + i + nin) < 0) { | ||||
948 | goto fail; | ||||
949 | } | ||||
950 | } | ||||
951 | } | ||||
952 | |||||
953 | /* | ||||
954 | * Convert most arguments manually here, since it is easier to handle | ||||
955 | * the ufunc override if we first parse only to objects. | ||||
956 | */ | ||||
957 | if (where_obj && !_wheremask_converter(where_obj, out_wheremask)) { | ||||
958 | goto fail; | ||||
959 | } | ||||
960 | if (keepdims_obj && !_keepdims_converter(keepdims_obj, out_keepdims)) { | ||||
961 | goto fail; | ||||
962 | } | ||||
963 | if (casting_obj && !PyArray_CastingConverter(casting_obj, out_casting)) { | ||||
964 | goto fail; | ||||
965 | } | ||||
966 | if (order_obj && !PyArray_OrderConverter(order_obj, out_order)) { | ||||
967 | goto fail; | ||||
968 | } | ||||
969 | if (subok_obj && !_subok_converter(subok_obj, out_subok)) { | ||||
970 | goto fail; | ||||
971 | } | ||||
972 | return 0; | ||||
973 | |||||
974 | fail: | ||||
975 | if (out_wheremask != NULL((void*)0)) { | ||||
976 | Py_XSETREF(*out_wheremask, NULL)do { PyObject *_py_tmp = ((PyObject*)(*out_wheremask)); (*out_wheremask ) = (((void*)0)); _Py_XDECREF(((PyObject*)(_py_tmp))); } while (0); | ||||
977 | } | ||||
978 | for (int i = 0; i < nop; i++) { | ||||
979 | Py_XSETREF(out_op[i], NULL)do { PyObject *_py_tmp = ((PyObject*)(out_op[i])); (out_op[i] ) = (((void*)0)); _Py_XDECREF(((PyObject*)(_py_tmp))); } while (0); | ||||
980 | } | ||||
981 | return -1; | ||||
982 | } | ||||
983 | |||||
984 | /* | ||||
985 | * This checks whether a trivial loop is ok, | ||||
986 | * making copies of scalar and one dimensional operands if that will | ||||
987 | * help. | ||||
988 | * | ||||
989 | * Returns 1 if a trivial loop is ok, 0 if it is not, and | ||||
990 | * -1 if there is an error. | ||||
991 | */ | ||||
992 | static int | ||||
993 | check_for_trivial_loop(PyUFuncObject *ufunc, | ||||
994 | PyArrayObject **op, | ||||
995 | PyArray_Descr **dtype, | ||||
996 | npy_intp buffersize) | ||||
997 | { | ||||
998 | npy_intp i, nin = ufunc->nin, nop = nin + ufunc->nout; | ||||
999 | |||||
1000 | for (i = 0; i < nop; ++i) { | ||||
1001 | /* | ||||
1002 | * If the dtype doesn't match, or the array isn't aligned, | ||||
1003 | * indicate that the trivial loop can't be done. | ||||
1004 | */ | ||||
1005 | if (op[i] != NULL((void*)0) && | ||||
1006 | (!PyArray_ISALIGNED(op[i])PyArray_CHKFLAGS((op[i]), 0x0100) || | ||||
1007 | !PyArray_EquivTypes(dtype[i], PyArray_DESCR(op[i])) | ||||
1008 | )) { | ||||
1009 | /* | ||||
1010 | * If op[j] is a scalar or small one dimensional | ||||
1011 | * array input, make a copy to keep the opportunity | ||||
1012 | * for a trivial loop. | ||||
1013 | */ | ||||
1014 | if (i < nin && (PyArray_NDIM(op[i]) == 0 || | ||||
1015 | (PyArray_NDIM(op[i]) == 1 && | ||||
1016 | PyArray_DIM(op[i],0) <= buffersize))) { | ||||
1017 | PyArrayObject *tmp; | ||||
1018 | Py_INCREF(dtype[i])_Py_INCREF(((PyObject*)(dtype[i]))); | ||||
1019 | tmp = (PyArrayObject *) | ||||
1020 | PyArray_CastToType(op[i], dtype[i], 0); | ||||
1021 | if (tmp == NULL((void*)0)) { | ||||
1022 | return -1; | ||||
1023 | } | ||||
1024 | Py_DECREF(op[i])_Py_DECREF(((PyObject*)(op[i]))); | ||||
1025 | op[i] = tmp; | ||||
1026 | } | ||||
1027 | else { | ||||
1028 | return 0; | ||||
1029 | } | ||||
1030 | } | ||||
1031 | } | ||||
1032 | |||||
1033 | return 1; | ||||
1034 | } | ||||
1035 | |||||
1036 | |||||
1037 | /* | ||||
1038 | * Calls the given __array_prepare__ function on the operand *op, | ||||
1039 | * substituting it in place if a new array is returned and matches | ||||
1040 | * the old one. | ||||
1041 | * | ||||
1042 | * This requires that the dimensions, strides and data type remain | ||||
1043 | * exactly the same, which may be more strict than before. | ||||
1044 | */ | ||||
1045 | static int | ||||
1046 | prepare_ufunc_output(PyUFuncObject *ufunc, | ||||
1047 | PyArrayObject **op, | ||||
1048 | PyObject *arr_prep, | ||||
1049 | ufunc_full_args full_args, | ||||
1050 | int i) | ||||
1051 | { | ||||
1052 | if (arr_prep != NULL((void*)0) && arr_prep != Py_None(&_Py_NoneStruct)) { | ||||
1053 | PyObject *res; | ||||
1054 | PyArrayObject *arr; | ||||
1055 | PyObject *args_tup; | ||||
1056 | |||||
1057 | /* Call with the context argument */ | ||||
1058 | args_tup = _get_wrap_prepare_args(full_args); | ||||
1059 | if (args_tup == NULL((void*)0)) { | ||||
1060 | return -1; | ||||
1061 | } | ||||
1062 | res = PyObject_CallFunction( | ||||
1063 | arr_prep, "O(OOi)", *op, ufunc, args_tup, i); | ||||
1064 | Py_DECREF(args_tup)_Py_DECREF(((PyObject*)(args_tup))); | ||||
1065 | |||||
1066 | if (res == NULL((void*)0)) { | ||||
1067 | return -1; | ||||
1068 | } | ||||
1069 | else if (!PyArray_Check(res)((((PyObject*)(res))->ob_type) == (&PyArray_Type) || PyType_IsSubtype ((((PyObject*)(res))->ob_type), (&PyArray_Type)))) { | ||||
1070 | PyErr_SetString(PyExc_TypeError, | ||||
1071 | "__array_prepare__ must return an " | ||||
1072 | "ndarray or subclass thereof"); | ||||
1073 | Py_DECREF(res)_Py_DECREF(((PyObject*)(res))); | ||||
1074 | return -1; | ||||
1075 | } | ||||
1076 | arr = (PyArrayObject *)res; | ||||
1077 | |||||
1078 | /* If the same object was returned, nothing to do */ | ||||
1079 | if (arr == *op) { | ||||
1080 | Py_DECREF(arr)_Py_DECREF(((PyObject*)(arr))); | ||||
1081 | } | ||||
1082 | /* If the result doesn't match, throw an error */ | ||||
1083 | else if (PyArray_NDIM(arr) != PyArray_NDIM(*op) || | ||||
1084 | !PyArray_CompareLists(PyArray_DIMS(arr), | ||||
1085 | PyArray_DIMS(*op), | ||||
1086 | PyArray_NDIM(arr)) || | ||||
1087 | !PyArray_CompareLists(PyArray_STRIDES(arr), | ||||
1088 | PyArray_STRIDES(*op), | ||||
1089 | PyArray_NDIM(arr)) || | ||||
1090 | !PyArray_EquivTypes(PyArray_DESCR(arr), | ||||
1091 | PyArray_DESCR(*op))) { | ||||
1092 | PyErr_SetString(PyExc_TypeError, | ||||
1093 | "__array_prepare__ must return an " | ||||
1094 | "ndarray or subclass thereof which is " | ||||
1095 | "otherwise identical to its input"); | ||||
1096 | Py_DECREF(arr)_Py_DECREF(((PyObject*)(arr))); | ||||
1097 | return -1; | ||||
1098 | } | ||||
1099 | /* Replace the op value */ | ||||
1100 | else { | ||||
1101 | Py_DECREF(*op)_Py_DECREF(((PyObject*)(*op))); | ||||
1102 | *op = arr; | ||||
1103 | } | ||||
1104 | } | ||||
1105 | |||||
1106 | return 0; | ||||
1107 | } | ||||
1108 | |||||
1109 | |||||
1110 | /* | ||||
1111 | * Check whether a trivial loop is possible and call the innerloop if it is. | ||||
1112 | * A trivial loop is defined as one where a single strided inner-loop call | ||||
1113 | * is possible. | ||||
1114 | * | ||||
1115 | * This function only supports a single output (due to the overlap check). | ||||
1116 | * It always accepts 0-D arrays and will broadcast them. The function | ||||
1117 | * cannot broadcast any other array (as it requires a single stride). | ||||
1118 | * The function accepts all 1-D arrays, and N-D arrays that are either all | ||||
1119 | * C- or all F-contiguous. | ||||
1120 | * | ||||
1121 | * Returns -2 if a trivial loop is not possible, 0 on success and -1 on error. | ||||
1122 | */ | ||||
1123 | static NPY_INLINEinline int | ||||
1124 | try_trivial_single_output_loop(PyUFuncObject *ufunc, | ||||
1125 | PyArrayObject *op[], PyArray_Descr *dtypes[], | ||||
1126 | NPY_ORDER order, PyObject *arr_prep[], ufunc_full_args full_args, | ||||
1127 | PyUFuncGenericFunction innerloop, void *innerloopdata) | ||||
1128 | { | ||||
1129 | int nin = ufunc->nin; | ||||
1130 | int nop = nin + 1; | ||||
1131 | assert(ufunc->nout == 1)((void) (0)); | ||||
1132 | |||||
1133 | /* The order of all N-D contiguous operands, can be fixed by `order` */ | ||||
1134 | int operation_order = 0; | ||||
1135 | if (order == NPY_CORDER) { | ||||
1136 | operation_order = NPY_ARRAY_C_CONTIGUOUS0x0001; | ||||
1137 | } | ||||
1138 | else if (order == NPY_FORTRANORDER) { | ||||
1139 | operation_order = NPY_ARRAY_F_CONTIGUOUS0x0002; | ||||
1140 | } | ||||
1141 | |||||
1142 | int operation_ndim = 0; | ||||
1143 | npy_intp *operation_shape = NULL((void*)0); | ||||
1144 | npy_intp fixed_strides[NPY_MAXARGS32]; | ||||
1145 | |||||
1146 | for (int iop = 0; iop < nop; iop++) { | ||||
1147 | if (op[iop] == NULL((void*)0)) { | ||||
1148 | /* The out argument may be NULL (and only that one); fill later */ | ||||
1149 | assert(iop == nin)((void) (0)); | ||||
1150 | continue; | ||||
1151 | } | ||||
1152 | |||||
1153 | int op_ndim = PyArray_NDIM(op[iop]); | ||||
1154 | |||||
1155 | /* Special case 0-D since we can handle broadcasting using a 0-stride */ | ||||
1156 | if (op_ndim == 0) { | ||||
1157 | fixed_strides[iop] = 0; | ||||
1158 | continue; | ||||
1159 | } | ||||
1160 | |||||
1161 | /* First non 0-D op: fix dimensions, shape (order is fixed later) */ | ||||
1162 | if (operation_ndim == 0) { | ||||
1163 | operation_ndim = op_ndim; | ||||
1164 | operation_shape = PyArray_SHAPE(op[iop]); | ||||
1165 | } | ||||
1166 | else if (op_ndim != operation_ndim) { | ||||
1167 | return -2; /* dimension mismatch (except 0-d ops) */ | ||||
1168 | } | ||||
1169 | else if (!PyArray_CompareLists( | ||||
1170 | operation_shape, PyArray_DIMS(op[iop]), op_ndim)) { | ||||
1171 | return -2; /* shape mismatch */ | ||||
1172 | } | ||||
1173 | |||||
1174 | if (op_ndim == 1) { | ||||
1175 | fixed_strides[iop] = PyArray_STRIDES(op[iop])[0]; | ||||
1176 | } | ||||
1177 | else { | ||||
1178 | fixed_strides[iop] = PyArray_ITEMSIZE(op[iop]); /* contiguous */ | ||||
1179 | |||||
1180 | /* This op must match the operation order (and be contiguous) */ | ||||
1181 | int op_order = (PyArray_FLAGS(op[iop]) & | ||||
1182 | (NPY_ARRAY_C_CONTIGUOUS0x0001|NPY_ARRAY_F_CONTIGUOUS0x0002)); | ||||
1183 | if (op_order == 0) { | ||||
1184 | return -2; /* N-dimensional op must be contiguous */ | ||||
1185 | } | ||||
1186 | else if (operation_order == 0) { | ||||
1187 | operation_order = op_order; /* op fixes order */ | ||||
1188 | } | ||||
1189 | else if (operation_order != op_order) { | ||||
1190 | return -2; | ||||
1191 | } | ||||
1192 | } | ||||
1193 | } | ||||
1194 | |||||
1195 | if (op[nin] == NULL((void*)0)) { | ||||
1196 | Py_INCREF(dtypes[nin])_Py_INCREF(((PyObject*)(dtypes[nin]))); | ||||
1197 | op[nin] = (PyArrayObject *) PyArray_NewFromDescr(&PyArray_Type, | ||||
1198 | dtypes[nin], operation_ndim, operation_shape, | ||||
1199 | NULL((void*)0), NULL((void*)0), operation_order==NPY_ARRAY_F_CONTIGUOUS0x0002, NULL((void*)0)); | ||||
1200 | if (op[nin] == NULL((void*)0)) { | ||||
1201 | return -1; | ||||
1202 | } | ||||
1203 | fixed_strides[nin] = dtypes[nin]->elsize; | ||||
1204 | } | ||||
1205 | else { | ||||
1206 | /* If any input overlaps with the output, we use the full path. */ | ||||
1207 | for (int iop = 0; iop < nin; iop++) { | ||||
1208 | if (!PyArray_EQUIVALENTLY_ITERABLE_OVERLAP_OK( | ||||
1209 | op[iop], op[nin], | ||||
1210 | PyArray_TRIVIALLY_ITERABLE_OP_READ1, | ||||
1211 | PyArray_TRIVIALLY_ITERABLE_OP_NOREAD0)) { | ||||
1212 | return -2; | ||||
1213 | } | ||||
1214 | } | ||||
1215 | /* Check self-overlap (non 1-D are contiguous, perfect overlap is OK) */ | ||||
1216 | if (operation_ndim == 1 && | ||||
1217 | PyArray_STRIDES(op[nin])[0] < PyArray_ITEMSIZE(op[nin]) && | ||||
1218 | PyArray_STRIDES(op[nin])[0] != 0) { | ||||
1219 | return -2; | ||||
1220 | } | ||||
1221 | } | ||||
1222 | |||||
1223 | /* Call the __prepare_array__ if necessary */ | ||||
1224 | if (prepare_ufunc_output(ufunc, &op[nin], | ||||
1225 | arr_prep[0], full_args, 0) < 0) { | ||||
1226 | return -1; | ||||
1227 | } | ||||
1228 | |||||
1229 | /* | ||||
1230 | * We can use the trivial (single inner-loop call) optimization | ||||
1231 | * and `fixed_strides` holds the strides for that call. | ||||
1232 | */ | ||||
1233 | char *data[NPY_MAXARGS32]; | ||||
1234 | npy_intp count = PyArray_MultiplyList(operation_shape, operation_ndim); | ||||
1235 | int needs_api = 0; | ||||
1236 | NPY_BEGIN_THREADS_DEFPyThreadState *_save=((void*)0);; | ||||
1237 | |||||
1238 | for (int iop = 0; iop < nop; iop++) { | ||||
1239 | data[iop] = PyArray_BYTES(op[iop]); | ||||
1240 | needs_api |= PyDataType_REFCHK(dtypes[iop])(((dtypes[iop])->flags & (0x01)) == (0x01)); | ||||
1241 | } | ||||
1242 | |||||
1243 | if (!needs_api) { | ||||
1244 | NPY_BEGIN_THREADS_THRESHOLDED(count)do { if ((count) > 500) { _save = PyEval_SaveThread();} } while (0);; | ||||
1245 | } | ||||
1246 | |||||
1247 | innerloop(data, &count, fixed_strides, innerloopdata); | ||||
1248 | |||||
1249 | NPY_END_THREADSdo { if (_save) { PyEval_RestoreThread(_save); _save = ((void *)0);} } while (0);; | ||||
1250 | return 0; | ||||
1251 | } | ||||
1252 | |||||
1253 | |||||
1254 | static int | ||||
1255 | iterator_loop(PyUFuncObject *ufunc, | ||||
1256 | PyArrayObject **op, | ||||
1257 | PyArray_Descr **dtype, | ||||
1258 | NPY_ORDER order, | ||||
1259 | npy_intp buffersize, | ||||
1260 | PyObject **arr_prep, | ||||
1261 | ufunc_full_args full_args, | ||||
1262 | PyUFuncGenericFunction innerloop, | ||||
1263 | void *innerloopdata, | ||||
1264 | npy_uint32 *op_flags) | ||||
1265 | { | ||||
1266 | npy_intp i, nin = ufunc->nin, nout = ufunc->nout; | ||||
1267 | npy_intp nop = nin + nout; | ||||
1268 | NpyIter *iter; | ||||
1269 | char *baseptrs[NPY_MAXARGS32]; | ||||
1270 | |||||
1271 | NpyIter_IterNextFunc *iternext; | ||||
1272 | char **dataptr; | ||||
1273 | npy_intp *stride; | ||||
1274 | npy_intp *count_ptr; | ||||
1275 | int needs_api; | ||||
1276 | |||||
1277 | PyArrayObject **op_it; | ||||
1278 | npy_uint32 iter_flags; | ||||
1279 | |||||
1280 | NPY_BEGIN_THREADS_DEFPyThreadState *_save=((void*)0);; | ||||
1281 | |||||
1282 | iter_flags = ufunc->iter_flags | | ||||
1283 | NPY_ITER_EXTERNAL_LOOP0x00000008 | | ||||
1284 | NPY_ITER_REFS_OK0x00000020 | | ||||
1285 | NPY_ITER_ZEROSIZE_OK0x00000040 | | ||||
1286 | NPY_ITER_BUFFERED0x00000200 | | ||||
1287 | NPY_ITER_GROWINNER0x00000400 | | ||||
1288 | NPY_ITER_DELAY_BUFALLOC0x00000800 | | ||||
1289 | NPY_ITER_COPY_IF_OVERLAP0x00002000; | ||||
1290 | |||||
1291 | /* Call the __array_prepare__ functions for already existing output arrays. | ||||
1292 | * Do this before creating the iterator, as the iterator may UPDATEIFCOPY | ||||
1293 | * some of them. | ||||
1294 | */ | ||||
1295 | for (i = 0; i < nout; ++i) { | ||||
1296 | if (op[nin+i] == NULL((void*)0)) { | ||||
1297 | continue; | ||||
1298 | } | ||||
1299 | if (prepare_ufunc_output(ufunc, &op[nin+i], | ||||
1300 | arr_prep[i], full_args, i) < 0) { | ||||
1301 | return -1; | ||||
1302 | } | ||||
1303 | } | ||||
1304 | |||||
1305 | /* | ||||
1306 | * Allocate the iterator. Because the types of the inputs | ||||
1307 | * were already checked, we use the casting rule 'unsafe' which | ||||
1308 | * is faster to calculate. | ||||
1309 | */ | ||||
1310 | iter = NpyIter_AdvancedNew(nop, op, | ||||
1311 | iter_flags, | ||||
1312 | order, NPY_UNSAFE_CASTING, | ||||
1313 | op_flags, dtype, | ||||
1314 | -1, NULL((void*)0), NULL((void*)0), buffersize); | ||||
1315 | if (iter == NULL((void*)0)) { | ||||
1316 | return -1; | ||||
1317 | } | ||||
1318 | |||||
1319 | /* Copy any allocated outputs */ | ||||
1320 | op_it = NpyIter_GetOperandArray(iter); | ||||
1321 | for (i = 0; i < nout; ++i) { | ||||
1322 | if (op[nin+i] == NULL((void*)0)) { | ||||
1323 | op[nin+i] = op_it[nin+i]; | ||||
1324 | Py_INCREF(op[nin+i])_Py_INCREF(((PyObject*)(op[nin+i]))); | ||||
1325 | |||||
1326 | /* Call the __array_prepare__ functions for the new array */ | ||||
1327 | if (prepare_ufunc_output(ufunc, &op[nin+i], | ||||
1328 | arr_prep[i], full_args, i) < 0) { | ||||
1329 | NpyIter_Deallocate(iter); | ||||
1330 | return -1; | ||||
1331 | } | ||||
1332 | |||||
1333 | /* | ||||
1334 | * In case __array_prepare__ returned a different array, put the | ||||
1335 | * results directly there, ignoring the array allocated by the | ||||
1336 | * iterator. | ||||
1337 | * | ||||
1338 | * Here, we assume the user-provided __array_prepare__ behaves | ||||
1339 | * sensibly and doesn't return an array overlapping in memory | ||||
1340 | * with other operands --- the op[nin+i] array passed to it is newly | ||||
1341 | * allocated and doesn't have any overlap. | ||||
1342 | */ | ||||
1343 | baseptrs[nin+i] = PyArray_BYTES(op[nin+i]); | ||||
1344 | } | ||||
1345 | else { | ||||
1346 | baseptrs[nin+i] = PyArray_BYTES(op_it[nin+i]); | ||||
1347 | } | ||||
1348 | } | ||||
1349 | |||||
1350 | /* Only do the loop if the iteration size is non-zero */ | ||||
1351 | if (NpyIter_GetIterSize(iter) != 0) { | ||||
1352 | /* Reset the iterator with the base pointers from possible __array_prepare__ */ | ||||
1353 | for (i = 0; i < nin; ++i) { | ||||
1354 | baseptrs[i] = PyArray_BYTES(op_it[i]); | ||||
1355 | } | ||||
1356 | if (NpyIter_ResetBasePointers(iter, baseptrs, NULL((void*)0)) != NPY_SUCCEED1) { | ||||
1357 | NpyIter_Deallocate(iter); | ||||
1358 | return -1; | ||||
1359 | } | ||||
1360 | |||||
1361 | /* Get the variables needed for the loop */ | ||||
1362 | iternext = NpyIter_GetIterNext(iter, NULL((void*)0)); | ||||
1363 | if (iternext == NULL((void*)0)) { | ||||
1364 | NpyIter_Deallocate(iter); | ||||
1365 | return -1; | ||||
1366 | } | ||||
1367 | dataptr = NpyIter_GetDataPtrArray(iter); | ||||
1368 | stride = NpyIter_GetInnerStrideArray(iter); | ||||
1369 | count_ptr = NpyIter_GetInnerLoopSizePtr(iter); | ||||
1370 | needs_api = NpyIter_IterationNeedsAPI(iter); | ||||
1371 | |||||
1372 | NPY_BEGIN_THREADS_NDITER(iter)do { if (!NpyIter_IterationNeedsAPI(iter)) { do { if ((NpyIter_GetIterSize (iter)) > 500) { _save = PyEval_SaveThread();} } while (0) ;; } } while(0); | ||||
1373 | |||||
1374 | /* Execute the loop */ | ||||
1375 | do { | ||||
1376 | NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)*count_ptr); | ||||
1377 | innerloop(dataptr, count_ptr, stride, innerloopdata); | ||||
1378 | } while (!(needs_api && PyErr_Occurred()) && iternext(iter)); | ||||
1379 | |||||
1380 | NPY_END_THREADSdo { if (_save) { PyEval_RestoreThread(_save); _save = ((void *)0);} } while (0);; | ||||
1381 | } | ||||
1382 | /* | ||||
1383 | * Currently `innerloop` may leave an error set, in this case | ||||
1384 | * NpyIter_Deallocate will always return an error as well. | ||||
1385 | */ | ||||
1386 | if (NpyIter_Deallocate(iter) == NPY_FAIL0) { | ||||
1387 | return -1; | ||||
1388 | } | ||||
1389 | return 0; | ||||
1390 | } | ||||
1391 | |||||
1392 | /* | ||||
1393 | * ufunc - the ufunc to call | ||||
1394 | * trivial_loop_ok - 1 if no alignment, data conversion, etc required | ||||
1395 | * op - the operands (ufunc->nin + ufunc->nout of them) | ||||
1396 | * dtypes - the dtype of each operand | ||||
1397 | * order - the loop execution order/output memory order | ||||
1398 | * buffersize - how big of a buffer to use | ||||
1399 | * arr_prep - the __array_prepare__ functions for the outputs | ||||
1400 | * full_args - the original input, output PyObject * | ||||
1401 | * op_flags - per-operand flags, a combination of NPY_ITER_* constants | ||||
1402 | */ | ||||
1403 | static int | ||||
1404 | execute_legacy_ufunc_loop(PyUFuncObject *ufunc, | ||||
1405 | int trivial_loop_ok, | ||||
1406 | PyArrayObject **op, | ||||
1407 | PyArray_Descr **dtypes, | ||||
1408 | NPY_ORDER order, | ||||
1409 | npy_intp buffersize, | ||||
1410 | PyObject **arr_prep, | ||||
1411 | ufunc_full_args full_args, | ||||
1412 | npy_uint32 *op_flags) | ||||
1413 | { | ||||
1414 | PyUFuncGenericFunction innerloop; | ||||
1415 | void *innerloopdata; | ||||
1416 | int needs_api = 0; | ||||
1417 | |||||
1418 | if (ufunc->legacy_inner_loop_selector(ufunc, dtypes, | ||||
1419 | &innerloop, &innerloopdata, &needs_api) < 0) { | ||||
1420 | return -1; | ||||
1421 | } | ||||
1422 | |||||
1423 | /* First check for the trivial cases that don't need an iterator */ | ||||
1424 | if (trivial_loop_ok && ufunc->nout == 1) { | ||||
1425 | int fast_path_result = try_trivial_single_output_loop(ufunc, | ||||
1426 | op, dtypes, order, arr_prep, full_args, | ||||
1427 | innerloop, innerloopdata); | ||||
1428 | if (fast_path_result != -2) { | ||||
1429 | return fast_path_result; | ||||
1430 | } | ||||
1431 | } | ||||
1432 | |||||
1433 | /* | ||||
1434 | * If no trivial loop matched, an iterator is required to | ||||
1435 | * resolve broadcasting, etc | ||||
1436 | */ | ||||
1437 | NPY_UF_DBG_PRINT("iterator loop\n"); | ||||
1438 | if (iterator_loop(ufunc, op, dtypes, order, | ||||
1439 | buffersize, arr_prep, full_args, | ||||
1440 | innerloop, innerloopdata, op_flags) < 0) { | ||||
1441 | return -1; | ||||
1442 | } | ||||
1443 | |||||
1444 | return 0; | ||||
1445 | } | ||||
1446 | |||||
1447 | /* | ||||
1448 | * nin - number of inputs | ||||
1449 | * nout - number of outputs | ||||
1450 | * wheremask - if not NULL, the 'where=' parameter to the ufunc. | ||||
1451 | * op - the operands (nin + nout of them) | ||||
1452 | * order - the loop execution order/output memory order | ||||
1453 | * buffersize - how big of a buffer to use | ||||
1454 | * arr_prep - the __array_prepare__ functions for the outputs | ||||
1455 | * innerloop - the inner loop function | ||||
1456 | * innerloopdata - data to pass to the inner loop | ||||
1457 | */ | ||||
1458 | static int | ||||
1459 | execute_fancy_ufunc_loop(PyUFuncObject *ufunc, | ||||
1460 | PyArrayObject *wheremask, | ||||
1461 | PyArrayObject **op, | ||||
1462 | PyArray_Descr **dtypes, | ||||
1463 | NPY_ORDER order, | ||||
1464 | npy_intp buffersize, | ||||
1465 | PyObject **arr_prep, | ||||
1466 | ufunc_full_args full_args, | ||||
1467 | npy_uint32 *op_flags) | ||||
1468 | { | ||||
1469 | int i, nin = ufunc->nin, nout = ufunc->nout; | ||||
1470 | int nop = nin + nout; | ||||
1471 | NpyIter *iter; | ||||
1472 | int needs_api; | ||||
1473 | |||||
1474 | NpyIter_IterNextFunc *iternext; | ||||
1475 | char **dataptr; | ||||
1476 | npy_intp *strides; | ||||
1477 | npy_intp *countptr; | ||||
1478 | |||||
1479 | PyArrayObject **op_it; | ||||
1480 | npy_uint32 iter_flags; | ||||
1481 | |||||
1482 | for (i = nin; i < nop; ++i) { | ||||
| |||||
1483 | op_flags[i] |= (op[i] != NULL((void*)0) ? NPY_ITER_READWRITE0x00010000 : NPY_ITER_WRITEONLY0x00040000); | ||||
1484 | } | ||||
1485 | |||||
1486 | if (wheremask != NULL((void*)0)) { | ||||
1487 | op_flags[nop] = NPY_ITER_READONLY0x00020000 | NPY_ITER_ARRAYMASK0x20000000; | ||||
1488 | } | ||||
1489 | |||||
1490 | NPY_UF_DBG_PRINT("Making iterator\n"); | ||||
1491 | |||||
1492 | iter_flags = ufunc->iter_flags | | ||||
1493 | NPY_ITER_EXTERNAL_LOOP0x00000008 | | ||||
1494 | NPY_ITER_REFS_OK0x00000020 | | ||||
1495 | NPY_ITER_ZEROSIZE_OK0x00000040 | | ||||
1496 | NPY_ITER_BUFFERED0x00000200 | | ||||
1497 | NPY_ITER_GROWINNER0x00000400 | | ||||
1498 | NPY_ITER_COPY_IF_OVERLAP0x00002000; | ||||
1499 | |||||
1500 | /* | ||||
1501 | * Allocate the iterator. Because the types of the inputs | ||||
1502 | * were already checked, we use the casting rule 'unsafe' which | ||||
1503 | * is faster to calculate. | ||||
1504 | */ | ||||
1505 | iter = NpyIter_AdvancedNew(nop + ((wheremask
| ||||
1506 | iter_flags, | ||||
1507 | order, NPY_UNSAFE_CASTING, | ||||
1508 | op_flags, dtypes, | ||||
1509 | -1, NULL((void*)0), NULL((void*)0), buffersize); | ||||
1510 | if (iter == NULL((void*)0)) { | ||||
1511 | return -1; | ||||
1512 | } | ||||
1513 | |||||
1514 | NPY_UF_DBG_PRINT("Made iterator\n"); | ||||
1515 | |||||
1516 | needs_api = NpyIter_IterationNeedsAPI(iter); | ||||
1517 | |||||
1518 | /* Call the __array_prepare__ functions where necessary */ | ||||
1519 | op_it = NpyIter_GetOperandArray(iter); | ||||
1520 | for (i = nin; i
| ||||
1521 | PyArrayObject *op_tmp, *orig_op_tmp; | ||||
1522 | |||||
1523 | /* | ||||
1524 | * The array can be allocated by the iterator -- it is placed in op[i] | ||||
1525 | * and returned to the caller, and this needs an extra incref. | ||||
1526 | */ | ||||
1527 | if (op[i] == NULL((void*)0)) { | ||||
1528 | op_tmp = op_it[i]; | ||||
1529 | Py_INCREF(op_tmp)_Py_INCREF(((PyObject*)(op_tmp))); | ||||
1530 | } | ||||
1531 | else { | ||||
1532 | op_tmp = op[i]; | ||||
| |||||
1533 | } | ||||
1534 | |||||
1535 | /* prepare_ufunc_output may decref & replace the pointer */ | ||||
1536 | orig_op_tmp = op_tmp; | ||||
1537 | Py_INCREF(op_tmp)_Py_INCREF(((PyObject*)(op_tmp))); | ||||
1538 | |||||
1539 | if (prepare_ufunc_output(ufunc, &op_tmp, | ||||
1540 | arr_prep[i], full_args, i) < 0) { | ||||
1541 | NpyIter_Deallocate(iter); | ||||
1542 | return -1; | ||||
1543 | } | ||||
1544 | |||||
1545 | /* Validate that the prepare_ufunc_output didn't mess with pointers */ | ||||
1546 | if (PyArray_BYTES(op_tmp) != PyArray_BYTES(orig_op_tmp)) { | ||||
1547 | PyErr_SetString(PyExc_ValueError, | ||||
1548 | "The __array_prepare__ functions modified the data " | ||||
1549 | "pointer addresses in an invalid fashion"); | ||||
1550 | Py_DECREF(op_tmp)_Py_DECREF(((PyObject*)(op_tmp))); | ||||
1551 | NpyIter_Deallocate(iter); | ||||
1552 | return -1; | ||||
1553 | } | ||||
1554 | |||||
1555 | /* | ||||
1556 | * Put the updated operand back and undo the DECREF above. If | ||||
1557 | * COPY_IF_OVERLAP made a temporary copy, the output will be copied | ||||
1558 | * by UPDATEIFCOPY even if op[i] was changed by prepare_ufunc_output. | ||||
1559 | */ | ||||
1560 | op[i] = op_tmp; | ||||
1561 | Py_DECREF(op_tmp)_Py_DECREF(((PyObject*)(op_tmp))); | ||||
1562 | } | ||||
1563 | |||||
1564 | /* Only do the loop if the iteration size is non-zero */ | ||||
1565 | if (NpyIter_GetIterSize(iter) != 0) { | ||||
1566 | PyUFunc_MaskedStridedInnerLoopFunc *innerloop; | ||||
1567 | NpyAuxData *innerloopdata; | ||||
1568 | npy_intp fixed_strides[2*NPY_MAXARGS32]; | ||||
1569 | PyArray_Descr **iter_dtypes; | ||||
1570 | NPY_BEGIN_THREADS_DEFPyThreadState *_save=((void*)0);; | ||||
1571 | |||||
1572 | /* | ||||
1573 | * Get the inner loop, with the possibility of specialization | ||||
1574 | * based on the fixed strides. | ||||
1575 | */ | ||||
1576 | NpyIter_GetInnerFixedStrideArray(iter, fixed_strides); | ||||
1577 | iter_dtypes = NpyIter_GetDescrArray(iter); | ||||
1578 | if (ufunc->masked_inner_loop_selector(ufunc, dtypes, | ||||
1579 | wheremask != NULL((void*)0) ? iter_dtypes[nop] | ||||
1580 | : iter_dtypes[nop + nin], | ||||
1581 | fixed_strides, | ||||
1582 | wheremask != NULL((void*)0) ? fixed_strides[nop] | ||||
1583 | : fixed_strides[nop + nin], | ||||
1584 | &innerloop, &innerloopdata, &needs_api) < 0) { | ||||
1585 | NpyIter_Deallocate(iter); | ||||
1586 | return -1; | ||||
1587 | } | ||||
1588 | |||||
1589 | /* Get the variables needed for the loop */ | ||||
1590 | iternext = NpyIter_GetIterNext(iter, NULL((void*)0)); | ||||
1591 | if (iternext == NULL((void*)0)) { | ||||
1592 | NpyIter_Deallocate(iter); | ||||
1593 | return -1; | ||||
1594 | } | ||||
1595 | dataptr = NpyIter_GetDataPtrArray(iter); | ||||
1596 | strides = NpyIter_GetInnerStrideArray(iter); | ||||
1597 | countptr = NpyIter_GetInnerLoopSizePtr(iter); | ||||
1598 | needs_api = NpyIter_IterationNeedsAPI(iter); | ||||
1599 | |||||
1600 | NPY_BEGIN_THREADS_NDITER(iter)do { if (!NpyIter_IterationNeedsAPI(iter)) { do { if ((NpyIter_GetIterSize (iter)) > 500) { _save = PyEval_SaveThread();} } while (0) ;; } } while(0); | ||||
1601 | |||||
1602 | NPY_UF_DBG_PRINT("Actual inner loop:\n"); | ||||
1603 | /* Execute the loop */ | ||||
1604 | do { | ||||
1605 | NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)*countptr); | ||||
1606 | innerloop(dataptr, strides, | ||||
1607 | dataptr[nop], strides[nop], | ||||
1608 | *countptr, innerloopdata); | ||||
1609 | } while (!(needs_api && PyErr_Occurred()) && iternext(iter)); | ||||
1610 | |||||
1611 | NPY_END_THREADSdo { if (_save) { PyEval_RestoreThread(_save); _save = ((void *)0);} } while (0);; | ||||
1612 | |||||
1613 | NPY_AUXDATA_FREE(innerloopdata)do { if ((innerloopdata) != ((void*)0)) { (innerloopdata)-> free(innerloopdata); } } while(0); | ||||
1614 | } | ||||
1615 | |||||
1616 | return NpyIter_Deallocate(iter); | ||||
1617 | } | ||||
1618 | |||||
1619 | |||||
1620 | /* | ||||
1621 | * Validate that operands have enough dimensions, accounting for | ||||
1622 | * possible flexible dimensions that may be absent. | ||||
1623 | */ | ||||
1624 | static int | ||||
1625 | _validate_num_dims(PyUFuncObject *ufunc, PyArrayObject **op, | ||||
1626 | npy_uint32 *core_dim_flags, | ||||
1627 | int *op_core_num_dims) { | ||||
1628 | int i, j; | ||||
1629 | int nin = ufunc->nin; | ||||
1630 | int nop = ufunc->nargs; | ||||
1631 | |||||
1632 | for (i = 0; i < nop; i++) { | ||||
1633 | if (op[i] != NULL((void*)0)) { | ||||
1634 | int op_ndim = PyArray_NDIM(op[i]); | ||||
1635 | |||||
1636 | if (op_ndim < op_core_num_dims[i]) { | ||||
1637 | int core_offset = ufunc->core_offsets[i]; | ||||
1638 | /* We've too few, but some dimensions might be flexible */ | ||||
1639 | for (j = core_offset; | ||||
1640 | j < core_offset + ufunc->core_num_dims[i]; j++) { | ||||
1641 | int core_dim_index = ufunc->core_dim_ixs[j]; | ||||
1642 | if ((core_dim_flags[core_dim_index] & | ||||
1643 | UFUNC_CORE_DIM_CAN_IGNORE0x0004)) { | ||||
1644 | int i1, j1, k; | ||||
1645 | /* | ||||
1646 | * Found a dimension that can be ignored. Flag that | ||||
1647 | * it is missing, and unflag that it can be ignored, | ||||
1648 | * since we are doing so already. | ||||
1649 | */ | ||||
1650 | core_dim_flags[core_dim_index] |= UFUNC_CORE_DIM_MISSING0x00040000; | ||||
1651 | core_dim_flags[core_dim_index] ^= UFUNC_CORE_DIM_CAN_IGNORE0x0004; | ||||
1652 | /* | ||||
1653 | * Reduce the number of core dimensions for all | ||||
1654 | * operands that use this one (including ours), | ||||
1655 | * and check whether we're now OK. | ||||
1656 | */ | ||||
1657 | for (i1 = 0, k=0; i1 < nop; i1++) { | ||||
1658 | for (j1 = 0; j1 < ufunc->core_num_dims[i1]; j1++) { | ||||
1659 | if (ufunc->core_dim_ixs[k++] == core_dim_index) { | ||||
1660 | op_core_num_dims[i1]--; | ||||
1661 | } | ||||
1662 | } | ||||
1663 | } | ||||
1664 | if (op_ndim == op_core_num_dims[i]) { | ||||
1665 | break; | ||||
1666 | } | ||||
1667 | } | ||||
1668 | } | ||||
1669 | if (op_ndim < op_core_num_dims[i]) { | ||||
1670 | PyErr_Format(PyExc_ValueError, | ||||
1671 | "%s: %s operand %d does not have enough " | ||||
1672 | "dimensions (has %d, gufunc core with " | ||||
1673 | "signature %s requires %d)", | ||||
1674 | ufunc_get_name_cstr(ufunc), | ||||
1675 | i < nin ? "Input" : "Output", | ||||
1676 | i < nin ? i : i - nin, PyArray_NDIM(op[i]), | ||||
1677 | ufunc->core_signature, op_core_num_dims[i]); | ||||
1678 | return -1; | ||||
1679 | } | ||||
1680 | } | ||||
1681 | } | ||||
1682 | } | ||||
1683 | return 0; | ||||
1684 | } | ||||
1685 | |||||
1686 | /* | ||||
1687 | * Check whether any of the outputs of a gufunc has core dimensions. | ||||
1688 | */ | ||||
1689 | static int | ||||
1690 | _has_output_coredims(PyUFuncObject *ufunc) { | ||||
1691 | int i; | ||||
1692 | for (i = ufunc->nin; i < ufunc->nin + ufunc->nout; ++i) { | ||||
1693 | if (ufunc->core_num_dims[i] > 0) { | ||||
1694 | return 1; | ||||
1695 | } | ||||
1696 | } | ||||
1697 | return 0; | ||||
1698 | } | ||||
1699 | |||||
1700 | /* | ||||
1701 | * Check whether the gufunc can be used with axis, i.e., that there is only | ||||
1702 | * a single, shared core dimension (which means that operands either have | ||||
1703 | * that dimension, or have no core dimensions). Returns 0 if all is fine, | ||||
1704 | * and sets an error and returns -1 if not. | ||||
1705 | */ | ||||
1706 | static int | ||||
1707 | _check_axis_support(PyUFuncObject *ufunc) { | ||||
1708 | if (ufunc->core_num_dim_ix != 1) { | ||||
1709 | PyErr_Format(PyExc_TypeError, | ||||
1710 | "%s: axis can only be used with a single shared core " | ||||
1711 | "dimension, not with the %d distinct ones implied by " | ||||
1712 | "signature %s.", | ||||
1713 | ufunc_get_name_cstr(ufunc), | ||||
1714 | ufunc->core_num_dim_ix, | ||||
1715 | ufunc->core_signature); | ||||
1716 | return -1; | ||||
1717 | } | ||||
1718 | return 0; | ||||
1719 | } | ||||
1720 | |||||
1721 | /* | ||||
1722 | * Check whether the gufunc can be used with keepdims, i.e., that all its | ||||
1723 | * input arguments have the same number of core dimension, and all output | ||||
1724 | * arguments have no core dimensions. Returns 0 if all is fine, and sets | ||||
1725 | * an error and returns -1 if not. | ||||
1726 | */ | ||||
1727 | static int | ||||
1728 | _check_keepdims_support(PyUFuncObject *ufunc) { | ||||
1729 | int i; | ||||
1730 | int nin = ufunc->nin, nout = ufunc->nout; | ||||
1731 | int input_core_dims = ufunc->core_num_dims[0]; | ||||
1732 | for (i = 1; i < nin + nout; i++) { | ||||
1733 | if (ufunc->core_num_dims[i] != (i < nin ? input_core_dims : 0)) { | ||||
1734 | PyErr_Format(PyExc_TypeError, | ||||
1735 | "%s does not support keepdims: its signature %s requires " | ||||
1736 | "%s %d to have %d core dimensions, but keepdims can only " | ||||
1737 | "be used when all inputs have the same number of core " | ||||
1738 | "dimensions and all outputs have no core dimensions.", | ||||
1739 | ufunc_get_name_cstr(ufunc), | ||||
1740 | ufunc->core_signature, | ||||
1741 | i < nin ? "input" : "output", | ||||
1742 | i < nin ? i : i - nin, | ||||
1743 | ufunc->core_num_dims[i]); | ||||
1744 | return -1; | ||||
1745 | } | ||||
1746 | } | ||||
1747 | return 0; | ||||
1748 | } | ||||
1749 | |||||
1750 | /* | ||||
1751 | * Interpret a possible axes keyword argument, using it to fill the remap_axis | ||||
1752 | * array which maps default to actual axes for each operand, indexed as | ||||
1753 | * as remap_axis[iop][iaxis]. The default axis order has first all broadcast | ||||
1754 | * axes and then the core axes the gufunc operates on. | ||||
1755 | * | ||||
1756 | * Returns 0 on success, and -1 on failure | ||||
1757 | */ | ||||
1758 | static int | ||||
1759 | _parse_axes_arg(PyUFuncObject *ufunc, int op_core_num_dims[], PyObject *axes, | ||||
1760 | PyArrayObject **op, int broadcast_ndim, int **remap_axis) { | ||||
1761 | int nin = ufunc->nin; | ||||
1762 | int nop = ufunc->nargs; | ||||
1763 | int iop, list_size; | ||||
1764 | |||||
1765 | if (!PyList_Check(axes)((((((PyObject*)(axes))->ob_type))->tp_flags & ((1UL << 25))) != 0)) { | ||||
1766 | PyErr_SetString(PyExc_TypeError, "axes should be a list."); | ||||
1767 | return -1; | ||||
1768 | } | ||||
1769 | list_size = PyList_Size(axes); | ||||
1770 | if (list_size != nop) { | ||||
1771 | if (list_size != nin || _has_output_coredims(ufunc)) { | ||||
1772 | PyErr_Format(PyExc_ValueError, | ||||
1773 | "axes should be a list with an entry for all " | ||||
1774 | "%d inputs and outputs; entries for outputs can only " | ||||
1775 | "be omitted if none of them has core axes.", | ||||
1776 | nop); | ||||
1777 | return -1; | ||||
1778 | } | ||||
1779 | for (iop = nin; iop < nop; iop++) { | ||||
1780 | remap_axis[iop] = NULL((void*)0); | ||||
1781 | } | ||||
1782 | } | ||||
1783 | for (iop = 0; iop < list_size; ++iop) { | ||||
1784 | int op_ndim, op_ncore, op_nbroadcast; | ||||
1785 | int have_seen_axis[NPY_MAXDIMS32] = {0}; | ||||
1786 | PyObject *op_axes_tuple, *axis_item; | ||||
1787 | int axis, op_axis; | ||||
1788 | |||||
1789 | op_ncore = op_core_num_dims[iop]; | ||||
1790 | if (op[iop] != NULL((void*)0)) { | ||||
1791 | op_ndim = PyArray_NDIM(op[iop]); | ||||
1792 | op_nbroadcast = op_ndim - op_ncore; | ||||
1793 | } | ||||
1794 | else { | ||||
1795 | op_nbroadcast = broadcast_ndim; | ||||
1796 | op_ndim = broadcast_ndim + op_ncore; | ||||
1797 | } | ||||
1798 | /* | ||||
1799 | * Get axes tuple for operand. If not a tuple already, make it one if | ||||
1800 | * there is only one axis (its content is checked later). | ||||
1801 | */ | ||||
1802 | op_axes_tuple = PyList_GET_ITEM(axes, iop)(((PyListObject *)(axes))->ob_item[iop]); | ||||
1803 | if (PyTuple_Check(op_axes_tuple)((((((PyObject*)(op_axes_tuple))->ob_type))->tp_flags & ((1UL << 26))) != 0)) { | ||||
1804 | if (PyTuple_Size(op_axes_tuple) != op_ncore) { | ||||
1805 | if (op_ncore == 1) { | ||||
1806 | PyErr_Format(PyExc_ValueError, | ||||
1807 | "axes item %d should be a tuple with a " | ||||
1808 | "single element, or an integer", iop); | ||||
1809 | } | ||||
1810 | else { | ||||
1811 | PyErr_Format(PyExc_ValueError, | ||||
1812 | "axes item %d should be a tuple with %d " | ||||
1813 | "elements", iop, op_ncore); | ||||
1814 | } | ||||
1815 | return -1; | ||||
1816 | } | ||||
1817 | Py_INCREF(op_axes_tuple)_Py_INCREF(((PyObject*)(op_axes_tuple))); | ||||
1818 | } | ||||
1819 | else if (op_ncore == 1) { | ||||
1820 | op_axes_tuple = PyTuple_Pack(1, op_axes_tuple); | ||||
1821 | if (op_axes_tuple == NULL((void*)0)) { | ||||
1822 | return -1; | ||||
1823 | } | ||||
1824 | } | ||||
1825 | else { | ||||
1826 | PyErr_Format(PyExc_TypeError, "axes item %d should be a tuple", | ||||
1827 | iop); | ||||
1828 | return -1; | ||||
1829 | } | ||||
1830 | /* | ||||
1831 | * Now create the remap, starting with the core dimensions, and then | ||||
1832 | * adding the remaining broadcast axes that are to be iterated over. | ||||
1833 | */ | ||||
1834 | for (axis = op_nbroadcast; axis < op_ndim; axis++) { | ||||
1835 | axis_item = PyTuple_GET_ITEM(op_axes_tuple, axis - op_nbroadcast)((((void) (0)), (PyTupleObject *)(op_axes_tuple))->ob_item [axis - op_nbroadcast]); | ||||
1836 | op_axis = PyArray_PyIntAsInt(axis_item); | ||||
1837 | if (error_converting(op_axis)(((op_axis) == -1) && PyErr_Occurred()) || | ||||
1838 | (check_and_adjust_axis(&op_axis, op_ndim) < 0)) { | ||||
1839 | Py_DECREF(op_axes_tuple)_Py_DECREF(((PyObject*)(op_axes_tuple))); | ||||
1840 | return -1; | ||||
1841 | } | ||||
1842 | if (have_seen_axis[op_axis]) { | ||||
1843 | PyErr_Format(PyExc_ValueError, | ||||
1844 | "axes item %d has value %d repeated", | ||||
1845 | iop, op_axis); | ||||
1846 | Py_DECREF(op_axes_tuple)_Py_DECREF(((PyObject*)(op_axes_tuple))); | ||||
1847 | return -1; | ||||
1848 | } | ||||
1849 | have_seen_axis[op_axis] = 1; | ||||
1850 | remap_axis[iop][axis] = op_axis; | ||||
1851 | } | ||||
1852 | Py_DECREF(op_axes_tuple)_Py_DECREF(((PyObject*)(op_axes_tuple))); | ||||
1853 | /* | ||||
1854 | * Fill the op_nbroadcast=op_ndim-op_ncore axes not yet set, | ||||
1855 | * using have_seen_axis to skip over entries set above. | ||||
1856 | */ | ||||
1857 | for (axis = 0, op_axis = 0; axis < op_nbroadcast; axis++) { | ||||
1858 | while (have_seen_axis[op_axis]) { | ||||
1859 | op_axis++; | ||||
1860 | } | ||||
1861 | remap_axis[iop][axis] = op_axis++; | ||||
1862 | } | ||||
1863 | /* | ||||
1864 | * Check whether we are actually remapping anything. Here, | ||||
1865 | * op_axis can only equal axis if all broadcast axes were the same | ||||
1866 | * (i.e., the while loop above was never entered). | ||||
1867 | */ | ||||
1868 | if (axis == op_axis) { | ||||
1869 | while (axis < op_ndim && remap_axis[iop][axis] == axis) { | ||||
1870 | axis++; | ||||
1871 | } | ||||
1872 | } | ||||
1873 | if (axis == op_ndim) { | ||||
1874 | remap_axis[iop] = NULL((void*)0); | ||||
1875 | } | ||||
1876 | } /* end of for(iop) loop over operands */ | ||||
1877 | return 0; | ||||
1878 | } | ||||
1879 | |||||
1880 | /* | ||||
1881 | * Simplified version of the above, using axis to fill the remap_axis | ||||
1882 | * array, which maps default to actual axes for each operand, indexed as | ||||
1883 | * as remap_axis[iop][iaxis]. The default axis order has first all broadcast | ||||
1884 | * axes and then the core axes the gufunc operates on. | ||||
1885 | * | ||||
1886 | * Returns 0 on success, and -1 on failure | ||||
1887 | */ | ||||
1888 | static int | ||||
1889 | _parse_axis_arg(PyUFuncObject *ufunc, const int core_num_dims[], PyObject *axis, | ||||
1890 | PyArrayObject **op, int broadcast_ndim, int **remap_axis) { | ||||
1891 | int nop = ufunc->nargs; | ||||
1892 | int iop, axis_int; | ||||
1893 | |||||
1894 | axis_int = PyArray_PyIntAsInt(axis); | ||||
1895 | if (error_converting(axis_int)(((axis_int) == -1) && PyErr_Occurred())) { | ||||
1896 | return -1; | ||||
1897 | } | ||||
1898 | |||||
1899 | for (iop = 0; iop < nop; ++iop) { | ||||
1900 | int axis, op_ndim, op_axis; | ||||
1901 | |||||
1902 | /* _check_axis_support ensures core_num_dims is 0 or 1 */ | ||||
1903 | if (core_num_dims[iop] == 0) { | ||||
1904 | remap_axis[iop] = NULL((void*)0); | ||||
1905 | continue; | ||||
1906 | } | ||||
1907 | if (op[iop]) { | ||||
1908 | op_ndim = PyArray_NDIM(op[iop]); | ||||
1909 | } | ||||
1910 | else { | ||||
1911 | op_ndim = broadcast_ndim + 1; | ||||
1912 | } | ||||
1913 | op_axis = axis_int; /* ensure we don't modify axis_int */ | ||||
1914 | if (check_and_adjust_axis(&op_axis, op_ndim) < 0) { | ||||
1915 | return -1; | ||||
1916 | } | ||||
1917 | /* Are we actually remapping away from last axis? */ | ||||
1918 | if (op_axis == op_ndim - 1) { | ||||
1919 | remap_axis[iop] = NULL((void*)0); | ||||
1920 | continue; | ||||
1921 | } | ||||
1922 | remap_axis[iop][op_ndim - 1] = op_axis; | ||||
1923 | for (axis = 0; axis < op_axis; axis++) { | ||||
1924 | remap_axis[iop][axis] = axis; | ||||
1925 | } | ||||
1926 | for (axis = op_axis; axis < op_ndim - 1; axis++) { | ||||
1927 | remap_axis[iop][axis] = axis + 1; | ||||
1928 | } | ||||
1929 | } /* end of for(iop) loop over operands */ | ||||
1930 | return 0; | ||||
1931 | } | ||||
1932 | |||||
1933 | #define REMAP_AXIS(iop, axis)((remap_axis != ((void*)0) && remap_axis[iop] != ((void *)0))? remap_axis[iop][axis] : axis) ((remap_axis != NULL((void*)0) && \ | ||||
1934 | remap_axis[iop] != NULL((void*)0))? \ | ||||
1935 | remap_axis[iop][axis] : axis) | ||||
1936 | |||||
1937 | /* | ||||
1938 | * Validate the core dimensions of all the operands, and collect all of | ||||
1939 | * the labelled core dimensions into 'core_dim_sizes'. | ||||
1940 | * | ||||
1941 | * Returns 0 on success, and -1 on failure | ||||
1942 | * | ||||
1943 | * The behavior has been changed in NumPy 1.16.0, and the following | ||||
1944 | * requirements must be fulfilled or an error will be raised: | ||||
1945 | * * Arguments, both input and output, must have at least as many | ||||
1946 | * dimensions as the corresponding number of core dimensions. In | ||||
1947 | * versions before 1.10, 1's were prepended to the shape as needed. | ||||
1948 | * * Core dimensions with same labels must have exactly matching sizes. | ||||
1949 | * In versions before 1.10, core dimensions of size 1 would broadcast | ||||
1950 | * against other core dimensions with the same label. | ||||
1951 | * * All core dimensions must have their size specified by a passed in | ||||
1952 | * input or output argument. In versions before 1.10, core dimensions in | ||||
1953 | * an output argument that were not specified in an input argument, | ||||
1954 | * and whose size could not be inferred from a passed in output | ||||
1955 | * argument, would have their size set to 1. | ||||
1956 | * * Core dimensions may be fixed, new in NumPy 1.16 | ||||
1957 | */ | ||||
1958 | static int | ||||
1959 | _get_coredim_sizes(PyUFuncObject *ufunc, PyArrayObject **op, | ||||
1960 | const int *op_core_num_dims, npy_uint32 *core_dim_flags, | ||||
1961 | npy_intp *core_dim_sizes, int **remap_axis) { | ||||
1962 | int i; | ||||
1963 | int nin = ufunc->nin; | ||||
1964 | int nout = ufunc->nout; | ||||
1965 | int nop = nin + nout; | ||||
1966 | |||||
1967 | for (i = 0; i < nop; ++i) { | ||||
1968 | if (op[i] != NULL((void*)0)) { | ||||
1969 | int idim; | ||||
1970 | int dim_offset = ufunc->core_offsets[i]; | ||||
1971 | int core_start_dim = PyArray_NDIM(op[i]) - op_core_num_dims[i]; | ||||
1972 | int dim_delta = 0; | ||||
1973 | |||||
1974 | /* checked before this routine gets called */ | ||||
1975 | assert(core_start_dim >= 0)((void) (0)); | ||||
1976 | |||||
1977 | /* | ||||
1978 | * Make sure every core dimension exactly matches all other core | ||||
1979 | * dimensions with the same label. Note that flexible dimensions | ||||
1980 | * may have been removed at this point, if so, they are marked | ||||
1981 | * with UFUNC_CORE_DIM_MISSING. | ||||
1982 | */ | ||||
1983 | for (idim = 0; idim < ufunc->core_num_dims[i]; ++idim) { | ||||
1984 | int core_index = dim_offset + idim; | ||||
1985 | int core_dim_index = ufunc->core_dim_ixs[core_index]; | ||||
1986 | npy_intp core_dim_size = core_dim_sizes[core_dim_index]; | ||||
1987 | npy_intp op_dim_size; | ||||
1988 | |||||
1989 | /* can only happen if flexible; dimension missing altogether */ | ||||
1990 | if (core_dim_flags[core_dim_index] & UFUNC_CORE_DIM_MISSING0x00040000) { | ||||
1991 | op_dim_size = 1; | ||||
1992 | dim_delta++; /* for indexing in dimensions */ | ||||
1993 | } | ||||
1994 | else { | ||||
1995 | op_dim_size = PyArray_DIM(op[i], | ||||
1996 | REMAP_AXIS(i, core_start_dim + idim - dim_delta)((remap_axis != ((void*)0) && remap_axis[i] != ((void *)0))? remap_axis[i][core_start_dim + idim - dim_delta] : core_start_dim + idim - dim_delta)); | ||||
1997 | } | ||||
1998 | if (core_dim_sizes[core_dim_index] < 0) { | ||||
1999 | core_dim_sizes[core_dim_index] = op_dim_size; | ||||
2000 | } | ||||
2001 | else if (op_dim_size != core_dim_size) { | ||||
2002 | PyErr_Format(PyExc_ValueError, | ||||
2003 | "%s: %s operand %d has a mismatch in its " | ||||
2004 | "core dimension %d, with gufunc " | ||||
2005 | "signature %s (size %zd is different " | ||||
2006 | "from %zd)", | ||||
2007 | ufunc_get_name_cstr(ufunc), i < nin ? "Input" : "Output", | ||||
2008 | i < nin ? i : i - nin, idim - dim_delta, | ||||
2009 | ufunc->core_signature, op_dim_size, | ||||
2010 | core_dim_sizes[core_dim_index]); | ||||
2011 | return -1; | ||||
2012 | } | ||||
2013 | } | ||||
2014 | } | ||||
2015 | } | ||||
2016 | |||||
2017 | /* | ||||
2018 | * Make sure no core dimension is unspecified. | ||||
2019 | */ | ||||
2020 | for (i = nin; i < nop; ++i) { | ||||
2021 | int idim; | ||||
2022 | int dim_offset = ufunc->core_offsets[i]; | ||||
2023 | |||||
2024 | for (idim = 0; idim < ufunc->core_num_dims[i]; ++idim) { | ||||
2025 | int core_dim_index = ufunc->core_dim_ixs[dim_offset + idim]; | ||||
2026 | |||||
2027 | /* check all cases where the size has not yet been set */ | ||||
2028 | if (core_dim_sizes[core_dim_index] < 0) { | ||||
2029 | /* | ||||
2030 | * Oops, this dimension was never specified | ||||
2031 | * (can only happen if output op not given) | ||||
2032 | */ | ||||
2033 | PyErr_Format(PyExc_ValueError, | ||||
2034 | "%s: Output operand %d has core dimension %d " | ||||
2035 | "unspecified, with gufunc signature %s", | ||||
2036 | ufunc_get_name_cstr(ufunc), i - nin, idim, | ||||
2037 | ufunc->core_signature); | ||||
2038 | return -1; | ||||
2039 | } | ||||
2040 | } | ||||
2041 | } | ||||
2042 | |||||
2043 | return 0; | ||||
2044 | } | ||||
2045 | |||||
2046 | /* | ||||
2047 | * Returns a new reference | ||||
2048 | * TODO: store a reference in the ufunc object itself, rather than | ||||
2049 | * constructing one each time | ||||
2050 | */ | ||||
2051 | static PyObject * | ||||
2052 | _get_identity(PyUFuncObject *ufunc, npy_bool *reorderable) { | ||||
2053 | switch(ufunc->identity) { | ||||
2054 | case PyUFunc_One1: | ||||
2055 | *reorderable = 1; | ||||
2056 | return PyLong_FromLong(1); | ||||
2057 | |||||
2058 | case PyUFunc_Zero0: | ||||
2059 | *reorderable = 1; | ||||
2060 | return PyLong_FromLong(0); | ||||
2061 | |||||
2062 | case PyUFunc_MinusOne2: | ||||
2063 | *reorderable = 1; | ||||
2064 | return PyLong_FromLong(-1); | ||||
2065 | |||||
2066 | case PyUFunc_ReorderableNone-2: | ||||
2067 | *reorderable = 1; | ||||
2068 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
2069 | |||||
2070 | case PyUFunc_None-1: | ||||
2071 | *reorderable = 0; | ||||
2072 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
2073 | |||||
2074 | case PyUFunc_IdentityValue-3: | ||||
2075 | *reorderable = 1; | ||||
2076 | Py_INCREF(ufunc->identity_value)_Py_INCREF(((PyObject*)(ufunc->identity_value))); | ||||
2077 | return ufunc->identity_value; | ||||
2078 | |||||
2079 | default: | ||||
2080 | PyErr_Format(PyExc_ValueError, | ||||
2081 | "ufunc %s has an invalid identity", ufunc_get_name_cstr(ufunc)); | ||||
2082 | return NULL((void*)0); | ||||
2083 | } | ||||
2084 | } | ||||
2085 | |||||
2086 | /* | ||||
2087 | * Copy over parts of the ufunc structure that may need to be | ||||
2088 | * changed during execution. Returns 0 on success; -1 otherwise. | ||||
2089 | */ | ||||
2090 | static int | ||||
2091 | _initialize_variable_parts(PyUFuncObject *ufunc, | ||||
2092 | int op_core_num_dims[], | ||||
2093 | npy_intp core_dim_sizes[], | ||||
2094 | npy_uint32 core_dim_flags[]) { | ||||
2095 | int i; | ||||
2096 | |||||
2097 | for (i = 0; i < ufunc->nargs; i++) { | ||||
2098 | op_core_num_dims[i] = ufunc->core_num_dims[i]; | ||||
2099 | } | ||||
2100 | for (i = 0; i < ufunc->core_num_dim_ix; i++) { | ||||
2101 | core_dim_sizes[i] = ufunc->core_dim_sizes[i]; | ||||
2102 | core_dim_flags[i] = ufunc->core_dim_flags[i]; | ||||
2103 | } | ||||
2104 | return 0; | ||||
2105 | } | ||||
2106 | |||||
2107 | static int | ||||
2108 | PyUFunc_GeneralizedFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, | ||||
2109 | ufunc_full_args full_args, PyObject *type_tup, PyObject *extobj, | ||||
2110 | NPY_CASTING casting, NPY_ORDER order, npy_bool subok, | ||||
2111 | PyObject *axis, PyObject *axes, int keepdims) | ||||
2112 | { | ||||
2113 | int nin, nout; | ||||
2114 | int i, j, idim, nop; | ||||
2115 | const char *ufunc_name; | ||||
2116 | int retval; | ||||
2117 | int needs_api = 0; | ||||
2118 | |||||
2119 | PyArray_Descr *dtypes[NPY_MAXARGS32]; | ||||
2120 | |||||
2121 | /* Use remapped axes for generalized ufunc */ | ||||
2122 | int broadcast_ndim, iter_ndim; | ||||
2123 | int op_core_num_dims[NPY_MAXARGS32]; | ||||
2124 | int op_axes_arrays[NPY_MAXARGS32][NPY_MAXDIMS32]; | ||||
2125 | int *op_axes[NPY_MAXARGS32]; | ||||
2126 | npy_uint32 core_dim_flags[NPY_MAXARGS32]; | ||||
2127 | |||||
2128 | npy_uint32 op_flags[NPY_MAXARGS32]; | ||||
2129 | npy_intp iter_shape[NPY_MAXARGS32]; | ||||
2130 | NpyIter *iter = NULL((void*)0); | ||||
2131 | npy_uint32 iter_flags; | ||||
2132 | npy_intp total_problem_size; | ||||
2133 | |||||
2134 | /* These parameters come from extobj= or from a TLS global */ | ||||
2135 | int buffersize = 0, errormask = 0; | ||||
2136 | |||||
2137 | /* The selected inner loop */ | ||||
2138 | PyUFuncGenericFunction innerloop = NULL((void*)0); | ||||
2139 | void *innerloopdata = NULL((void*)0); | ||||
2140 | /* The dimensions which get passed to the inner loop */ | ||||
2141 | npy_intp inner_dimensions[NPY_MAXDIMS32+1]; | ||||
2142 | /* The strides which get passed to the inner loop */ | ||||
2143 | npy_intp *inner_strides = NULL((void*)0); | ||||
2144 | |||||
2145 | /* The sizes of the core dimensions (# entries is ufunc->core_num_dim_ix) */ | ||||
2146 | npy_intp *core_dim_sizes = inner_dimensions + 1; | ||||
2147 | int core_dim_ixs_size; | ||||
2148 | /* swapping around of axes */ | ||||
2149 | int *remap_axis_memory = NULL((void*)0); | ||||
2150 | int **remap_axis = NULL((void*)0); | ||||
2151 | /* The __array_prepare__ function to call for each output */ | ||||
2152 | PyObject *arr_prep[NPY_MAXARGS32]; | ||||
2153 | |||||
2154 | nin = ufunc->nin; | ||||
2155 | nout = ufunc->nout; | ||||
2156 | nop = nin + nout; | ||||
2157 | |||||
2158 | ufunc_name = ufunc_get_name_cstr(ufunc); | ||||
2159 | |||||
2160 | NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s\n", ufunc_name); | ||||
2161 | |||||
2162 | /* Initialize all dtypes and __array_prepare__ call-backs to NULL */ | ||||
2163 | for (i = 0; i < nop; ++i) { | ||||
2164 | dtypes[i] = NULL((void*)0); | ||||
2165 | arr_prep[i] = NULL((void*)0); | ||||
2166 | } | ||||
2167 | /* Initialize possibly variable parts to the values from the ufunc */ | ||||
2168 | retval = _initialize_variable_parts(ufunc, op_core_num_dims, | ||||
2169 | core_dim_sizes, core_dim_flags); | ||||
2170 | if (retval < 0) { | ||||
2171 | goto fail; | ||||
2172 | } | ||||
2173 | |||||
2174 | /* | ||||
2175 | * If keepdims was passed in (and thus changed from the initial value | ||||
2176 | * on top), check the gufunc is suitable, i.e., that its inputs share | ||||
2177 | * the same number of core dimensions, and its outputs have none. | ||||
2178 | */ | ||||
2179 | if (keepdims != -1) { | ||||
2180 | retval = _check_keepdims_support(ufunc); | ||||
2181 | if (retval < 0) { | ||||
2182 | goto fail; | ||||
2183 | } | ||||
2184 | } | ||||
2185 | if (axis != NULL((void*)0)) { | ||||
2186 | retval = _check_axis_support(ufunc); | ||||
2187 | if (retval < 0) { | ||||
2188 | goto fail; | ||||
2189 | } | ||||
2190 | } | ||||
2191 | /* | ||||
2192 | * If keepdims is set and true, which means all input dimensions are | ||||
2193 | * the same, signal that all output dimensions will be the same too. | ||||
2194 | */ | ||||
2195 | if (keepdims == 1) { | ||||
2196 | int num_dims = op_core_num_dims[0]; | ||||
2197 | for (i = nin; i < nop; ++i) { | ||||
2198 | op_core_num_dims[i] = num_dims; | ||||
2199 | } | ||||
2200 | } | ||||
2201 | else { | ||||
2202 | /* keepdims was not set or was false; no adjustment necessary */ | ||||
2203 | keepdims = 0; | ||||
2204 | } | ||||
2205 | /* | ||||
2206 | * Check that operands have the minimum dimensions required. | ||||
2207 | * (Just checks core; broadcast dimensions are tested by the iterator.) | ||||
2208 | */ | ||||
2209 | retval = _validate_num_dims(ufunc, op, core_dim_flags, | ||||
2210 | op_core_num_dims); | ||||
2211 | if (retval < 0) { | ||||
2212 | goto fail; | ||||
2213 | } | ||||
2214 | /* | ||||
2215 | * Figure out the number of iteration dimensions, which | ||||
2216 | * is the broadcast result of all the non-core dimensions. | ||||
2217 | * (We do allow outputs to broadcast inputs currently, if they are given. | ||||
2218 | * This is in line with what normal ufuncs do.) | ||||
2219 | */ | ||||
2220 | broadcast_ndim = 0; | ||||
2221 | for (i = 0; i < nop; ++i) { | ||||
2222 | if (op[i] == NULL((void*)0)) { | ||||
2223 | continue; | ||||
2224 | } | ||||
2225 | int n = PyArray_NDIM(op[i]) - op_core_num_dims[i]; | ||||
2226 | if (n > broadcast_ndim) { | ||||
2227 | broadcast_ndim = n; | ||||
2228 | } | ||||
2229 | } | ||||
2230 | |||||
2231 | /* Possibly remap axes. */ | ||||
2232 | if (axes != NULL((void*)0) || axis != NULL((void*)0)) { | ||||
2233 | assert(!(axes != NULL && axis != NULL))((void) (0)); | ||||
2234 | |||||
2235 | remap_axis = PyArray_mallocPyMem_RawMalloc(sizeof(remap_axis[0]) * nop); | ||||
2236 | remap_axis_memory = PyArray_mallocPyMem_RawMalloc(sizeof(remap_axis_memory[0]) * | ||||
2237 | nop * NPY_MAXDIMS32); | ||||
2238 | if (remap_axis == NULL((void*)0) || remap_axis_memory == NULL((void*)0)) { | ||||
2239 | PyErr_NoMemory(); | ||||
2240 | goto fail; | ||||
2241 | } | ||||
2242 | for (i=0; i < nop; i++) { | ||||
2243 | remap_axis[i] = remap_axis_memory + i * NPY_MAXDIMS32; | ||||
2244 | } | ||||
2245 | if (axis) { | ||||
2246 | retval = _parse_axis_arg(ufunc, op_core_num_dims, axis, op, | ||||
2247 | broadcast_ndim, remap_axis); | ||||
2248 | } | ||||
2249 | else { | ||||
2250 | retval = _parse_axes_arg(ufunc, op_core_num_dims, axes, op, | ||||
2251 | broadcast_ndim, remap_axis); | ||||
2252 | } | ||||
2253 | if(retval < 0) { | ||||
2254 | goto fail; | ||||
2255 | } | ||||
2256 | } | ||||
2257 | |||||
2258 | /* Collect the lengths of the labelled core dimensions */ | ||||
2259 | retval = _get_coredim_sizes(ufunc, op, op_core_num_dims, core_dim_flags, | ||||
2260 | core_dim_sizes, remap_axis); | ||||
2261 | if(retval < 0) { | ||||
2262 | goto fail; | ||||
2263 | } | ||||
2264 | /* | ||||
2265 | * Figure out the number of iterator creation dimensions, | ||||
2266 | * which is the broadcast dimensions + all the core dimensions of | ||||
2267 | * the outputs, so that the iterator can allocate those output | ||||
2268 | * dimensions following the rules of order='F', for example. | ||||
2269 | */ | ||||
2270 | iter_ndim = broadcast_ndim; | ||||
2271 | for (i = nin; i < nop; ++i) { | ||||
2272 | iter_ndim += op_core_num_dims[i]; | ||||
2273 | } | ||||
2274 | if (iter_ndim > NPY_MAXDIMS32) { | ||||
2275 | PyErr_Format(PyExc_ValueError, | ||||
2276 | "too many dimensions for generalized ufunc %s", | ||||
2277 | ufunc_name); | ||||
2278 | retval = -1; | ||||
2279 | goto fail; | ||||
2280 | } | ||||
2281 | |||||
2282 | /* Fill in the initial part of 'iter_shape' */ | ||||
2283 | for (idim = 0; idim < broadcast_ndim; ++idim) { | ||||
2284 | iter_shape[idim] = -1; | ||||
2285 | } | ||||
2286 | |||||
2287 | /* Fill in op_axes for all the operands */ | ||||
2288 | j = broadcast_ndim; | ||||
2289 | for (i = 0; i < nop; ++i) { | ||||
2290 | int n; | ||||
2291 | |||||
2292 | if (op[i]) { | ||||
2293 | n = PyArray_NDIM(op[i]) - op_core_num_dims[i]; | ||||
2294 | } | ||||
2295 | else { | ||||
2296 | n = broadcast_ndim; | ||||
2297 | } | ||||
2298 | /* Broadcast all the unspecified dimensions normally */ | ||||
2299 | for (idim = 0; idim < broadcast_ndim; ++idim) { | ||||
2300 | if (idim >= broadcast_ndim - n) { | ||||
2301 | op_axes_arrays[i][idim] = | ||||
2302 | REMAP_AXIS(i, idim - (broadcast_ndim - n))((remap_axis != ((void*)0) && remap_axis[i] != ((void *)0))? remap_axis[i][idim - (broadcast_ndim - n)] : idim - (broadcast_ndim - n)); | ||||
2303 | } | ||||
2304 | else { | ||||
2305 | op_axes_arrays[i][idim] = -1; | ||||
2306 | } | ||||
2307 | } | ||||
2308 | |||||
2309 | /* | ||||
2310 | * Any output core dimensions shape should be ignored, so we add | ||||
2311 | * it as a Reduce dimension (which can be broadcast with the rest). | ||||
2312 | * These will be removed before the actual iteration for gufuncs. | ||||
2313 | */ | ||||
2314 | for (idim = broadcast_ndim; idim < iter_ndim; ++idim) { | ||||
2315 | op_axes_arrays[i][idim] = NPY_ITER_REDUCTION_AXIS(-1)(-1 + (1 << ((4 * 8) - 2))); | ||||
2316 | } | ||||
2317 | |||||
2318 | /* Except for when it belongs to this output */ | ||||
2319 | if (i >= nin) { | ||||
2320 | int dim_offset = ufunc->core_offsets[i]; | ||||
2321 | int num_removed = 0; | ||||
2322 | /* | ||||
2323 | * Fill in 'iter_shape' and 'op_axes' for the core dimensions | ||||
2324 | * of this output. Here, we have to be careful: if keepdims | ||||
2325 | * was used, then the axes are not real core dimensions, but | ||||
2326 | * are being added back for broadcasting, so their size is 1. | ||||
2327 | * If the axis was removed, we should skip altogether. | ||||
2328 | */ | ||||
2329 | if (keepdims) { | ||||
2330 | for (idim = 0; idim < op_core_num_dims[i]; ++idim) { | ||||
2331 | iter_shape[j] = 1; | ||||
2332 | op_axes_arrays[i][j] = REMAP_AXIS(i, n + idim)((remap_axis != ((void*)0) && remap_axis[i] != ((void *)0))? remap_axis[i][n + idim] : n + idim); | ||||
2333 | ++j; | ||||
2334 | } | ||||
2335 | } | ||||
2336 | else { | ||||
2337 | for (idim = 0; idim < ufunc->core_num_dims[i]; ++idim) { | ||||
2338 | int core_index = dim_offset + idim; | ||||
2339 | int core_dim_index = ufunc->core_dim_ixs[core_index]; | ||||
2340 | if ((core_dim_flags[core_dim_index] & | ||||
2341 | UFUNC_CORE_DIM_MISSING0x00040000)) { | ||||
2342 | /* skip it */ | ||||
2343 | num_removed++; | ||||
2344 | continue; | ||||
2345 | } | ||||
2346 | iter_shape[j] = core_dim_sizes[ufunc->core_dim_ixs[core_index]]; | ||||
2347 | op_axes_arrays[i][j] = REMAP_AXIS(i, n + idim - num_removed)((remap_axis != ((void*)0) && remap_axis[i] != ((void *)0))? remap_axis[i][n + idim - num_removed] : n + idim - num_removed ); | ||||
2348 | ++j; | ||||
2349 | } | ||||
2350 | } | ||||
2351 | } | ||||
2352 | |||||
2353 | op_axes[i] = op_axes_arrays[i]; | ||||
2354 | } | ||||
2355 | |||||
2356 | #if NPY_UF_DBG_TRACING0 | ||||
2357 | printf("iter shapes:")__printf_chk (2 - 1, "iter shapes:"); | ||||
2358 | for (j=0; j < iter_ndim; j++) { | ||||
2359 | printf(" %ld", iter_shape[j])__printf_chk (2 - 1, " %ld", iter_shape[j]); | ||||
2360 | } | ||||
2361 | printf("\n")__printf_chk (2 - 1, "\n"); | ||||
2362 | #endif | ||||
2363 | |||||
2364 | /* Get the buffersize and errormask */ | ||||
2365 | if (_get_bufsize_errmask(extobj, ufunc_name, &buffersize, &errormask) < 0) { | ||||
2366 | retval = -1; | ||||
2367 | goto fail; | ||||
2368 | } | ||||
2369 | |||||
2370 | NPY_UF_DBG_PRINT("Finding inner loop\n"); | ||||
2371 | |||||
2372 | |||||
2373 | retval = ufunc->type_resolver(ufunc, casting, | ||||
2374 | op, type_tup, dtypes); | ||||
2375 | if (retval < 0) { | ||||
2376 | goto fail; | ||||
2377 | } | ||||
2378 | /* | ||||
2379 | * We don't write to all elements, and the iterator may make | ||||
2380 | * UPDATEIFCOPY temporary copies. The output arrays (unless they are | ||||
2381 | * allocated by the iterator itself) must be considered READWRITE by the | ||||
2382 | * iterator, so that the elements we don't write to are copied to the | ||||
2383 | * possible temporary array. | ||||
2384 | */ | ||||
2385 | _ufunc_setup_flags(ufunc, NPY_ITER_COPY0x00400000 | NPY_UFUNC_DEFAULT_INPUT_FLAGS0x00020000 | 0x00100000 | 0x40000000, | ||||
2386 | NPY_ITER_UPDATEIFCOPY0x00800000 | | ||||
2387 | NPY_ITER_WRITEONLY0x00040000 | | ||||
2388 | NPY_UFUNC_DEFAULT_OUTPUT_FLAGS0x00100000 | 0x01000000 | 0x08000000 | 0x02000000 | 0x40000000, | ||||
2389 | op_flags); | ||||
2390 | /* For the generalized ufunc, we get the loop right away too */ | ||||
2391 | retval = ufunc->legacy_inner_loop_selector(ufunc, dtypes, | ||||
2392 | &innerloop, &innerloopdata, &needs_api); | ||||
2393 | if (retval < 0) { | ||||
2394 | goto fail; | ||||
2395 | } | ||||
2396 | |||||
2397 | #if NPY_UF_DBG_TRACING0 | ||||
2398 | printf("input types:\n")__printf_chk (2 - 1, "input types:\n"); | ||||
2399 | for (i = 0; i < nin; ++i) { | ||||
2400 | PyObject_Print((PyObject *)dtypes[i], stdoutstdout, 0); | ||||
2401 | printf(" ")__printf_chk (2 - 1, " "); | ||||
2402 | } | ||||
2403 | printf("\noutput types:\n")__printf_chk (2 - 1, "\noutput types:\n"); | ||||
2404 | for (i = nin; i < nop; ++i) { | ||||
2405 | PyObject_Print((PyObject *)dtypes[i], stdoutstdout, 0); | ||||
2406 | printf(" ")__printf_chk (2 - 1, " "); | ||||
2407 | } | ||||
2408 | printf("\n")__printf_chk (2 - 1, "\n"); | ||||
2409 | #endif | ||||
2410 | |||||
2411 | if (subok) { | ||||
2412 | /* | ||||
2413 | * Get the appropriate __array_prepare__ function to call | ||||
2414 | * for each output | ||||
2415 | */ | ||||
2416 | _find_array_prepare(full_args, arr_prep, nout); | ||||
2417 | } | ||||
2418 | |||||
2419 | /* | ||||
2420 | * Set up the iterator per-op flags. For generalized ufuncs, we | ||||
2421 | * can't do buffering, so must COPY or UPDATEIFCOPY. | ||||
2422 | */ | ||||
2423 | |||||
2424 | iter_flags = ufunc->iter_flags | | ||||
2425 | NPY_ITER_MULTI_INDEX0x00000004 | | ||||
2426 | NPY_ITER_REFS_OK0x00000020 | | ||||
2427 | NPY_ITER_ZEROSIZE_OK0x00000040 | | ||||
2428 | NPY_ITER_COPY_IF_OVERLAP0x00002000; | ||||
2429 | |||||
2430 | /* Create the iterator */ | ||||
2431 | iter = NpyIter_AdvancedNew(nop, op, iter_flags, | ||||
2432 | order, NPY_UNSAFE_CASTING, op_flags, | ||||
2433 | dtypes, iter_ndim, | ||||
2434 | op_axes, iter_shape, 0); | ||||
2435 | if (iter == NULL((void*)0)) { | ||||
2436 | retval = -1; | ||||
2437 | goto fail; | ||||
2438 | } | ||||
2439 | |||||
2440 | /* Fill in any allocated outputs */ | ||||
2441 | { | ||||
2442 | PyArrayObject **operands = NpyIter_GetOperandArray(iter); | ||||
2443 | for (i = nin; i < nop; ++i) { | ||||
2444 | if (op[i] == NULL((void*)0)) { | ||||
2445 | op[i] = operands[i]; | ||||
2446 | Py_INCREF(op[i])_Py_INCREF(((PyObject*)(op[i]))); | ||||
2447 | } | ||||
2448 | } | ||||
2449 | } | ||||
2450 | /* | ||||
2451 | * Set up the inner strides array. Because we're not doing | ||||
2452 | * buffering, the strides are fixed throughout the looping. | ||||
2453 | */ | ||||
2454 | core_dim_ixs_size = 0; | ||||
2455 | for (i = 0; i < nop; ++i) { | ||||
2456 | core_dim_ixs_size += ufunc->core_num_dims[i]; | ||||
2457 | } | ||||
2458 | inner_strides = (npy_intp *)PyArray_mallocPyMem_RawMalloc( | ||||
2459 | NPY_SIZEOF_INTP8 * (nop+core_dim_ixs_size)); | ||||
2460 | if (inner_strides == NULL((void*)0)) { | ||||
2461 | PyErr_NoMemory(); | ||||
2462 | retval = -1; | ||||
2463 | goto fail; | ||||
2464 | } | ||||
2465 | /* Copy the strides after the first nop */ | ||||
2466 | idim = nop; | ||||
2467 | for (i = 0; i < nop; ++i) { | ||||
2468 | /* | ||||
2469 | * Need to use the arrays in the iterator, not op, because | ||||
2470 | * a copy with a different-sized type may have been made. | ||||
2471 | */ | ||||
2472 | PyArrayObject *arr = NpyIter_GetOperandArray(iter)[i]; | ||||
2473 | npy_intp *shape = PyArray_SHAPE(arr); | ||||
2474 | npy_intp *strides = PyArray_STRIDES(arr); | ||||
2475 | /* | ||||
2476 | * Could be negative if flexible dims are used, but not for | ||||
2477 | * keepdims, since those dimensions are allocated in arr. | ||||
2478 | */ | ||||
2479 | int core_start_dim = PyArray_NDIM(arr) - op_core_num_dims[i]; | ||||
2480 | int num_removed = 0; | ||||
2481 | int dim_offset = ufunc->core_offsets[i]; | ||||
2482 | |||||
2483 | for (j = 0; j < ufunc->core_num_dims[i]; ++j) { | ||||
2484 | int core_dim_index = ufunc->core_dim_ixs[dim_offset + j]; | ||||
2485 | /* | ||||
2486 | * Force zero stride when the shape is 1 (always the case for | ||||
2487 | * for missing dimensions), so that broadcasting works right. | ||||
2488 | */ | ||||
2489 | if (core_dim_flags[core_dim_index] & UFUNC_CORE_DIM_MISSING0x00040000) { | ||||
2490 | num_removed++; | ||||
2491 | inner_strides[idim++] = 0; | ||||
2492 | } | ||||
2493 | else { | ||||
2494 | int remapped_axis = REMAP_AXIS(i, core_start_dim + j - num_removed)((remap_axis != ((void*)0) && remap_axis[i] != ((void *)0))? remap_axis[i][core_start_dim + j - num_removed] : core_start_dim + j - num_removed); | ||||
2495 | if (shape[remapped_axis] != 1) { | ||||
2496 | inner_strides[idim++] = strides[remapped_axis]; | ||||
2497 | } else { | ||||
2498 | inner_strides[idim++] = 0; | ||||
2499 | } | ||||
2500 | } | ||||
2501 | } | ||||
2502 | } | ||||
2503 | |||||
2504 | total_problem_size = NpyIter_GetIterSize(iter); | ||||
2505 | if (total_problem_size < 0) { | ||||
2506 | /* | ||||
2507 | * Only used for threading, if negative (this means that it is | ||||
2508 | * larger then ssize_t before axes removal) assume that the actual | ||||
2509 | * problem is large enough to be threaded usefully. | ||||
2510 | */ | ||||
2511 | total_problem_size = 1000; | ||||
2512 | } | ||||
2513 | |||||
2514 | /* Remove all the core output dimensions from the iterator */ | ||||
2515 | for (i = broadcast_ndim; i < iter_ndim; ++i) { | ||||
2516 | if (NpyIter_RemoveAxis(iter, broadcast_ndim) != NPY_SUCCEED1) { | ||||
2517 | retval = -1; | ||||
2518 | goto fail; | ||||
2519 | } | ||||
2520 | } | ||||
2521 | if (NpyIter_RemoveMultiIndex(iter) != NPY_SUCCEED1) { | ||||
2522 | retval = -1; | ||||
2523 | goto fail; | ||||
2524 | } | ||||
2525 | if (NpyIter_EnableExternalLoop(iter) != NPY_SUCCEED1) { | ||||
2526 | retval = -1; | ||||
2527 | goto fail; | ||||
2528 | } | ||||
2529 | |||||
2530 | /* | ||||
2531 | * The first nop strides are for the inner loop (but only can | ||||
2532 | * copy them after removing the core axes) | ||||
2533 | */ | ||||
2534 | memcpy(inner_strides, NpyIter_GetInnerStrideArray(iter), | ||||
2535 | NPY_SIZEOF_INTP8 * nop); | ||||
2536 | |||||
2537 | #if 0 | ||||
2538 | printf("strides: ")__printf_chk (2 - 1, "strides: "); | ||||
2539 | for (i = 0; i < nop+core_dim_ixs_size; ++i) { | ||||
2540 | printf("%d ", (int)inner_strides[i])__printf_chk (2 - 1, "%d ", (int)inner_strides[i]); | ||||
2541 | } | ||||
2542 | printf("\n")__printf_chk (2 - 1, "\n"); | ||||
2543 | #endif | ||||
2544 | |||||
2545 | /* Start with the floating-point exception flags cleared */ | ||||
2546 | npy_clear_floatstatus_barrier((char*)&iter); | ||||
2547 | |||||
2548 | NPY_UF_DBG_PRINT("Executing inner loop\n"); | ||||
2549 | |||||
2550 | if (NpyIter_GetIterSize(iter) != 0) { | ||||
2551 | /* Do the ufunc loop */ | ||||
2552 | NpyIter_IterNextFunc *iternext; | ||||
2553 | char **dataptr; | ||||
2554 | npy_intp *count_ptr; | ||||
2555 | NPY_BEGIN_THREADS_DEFPyThreadState *_save=((void*)0);; | ||||
2556 | |||||
2557 | /* Get the variables needed for the loop */ | ||||
2558 | iternext = NpyIter_GetIterNext(iter, NULL((void*)0)); | ||||
2559 | if (iternext == NULL((void*)0)) { | ||||
2560 | retval = -1; | ||||
2561 | goto fail; | ||||
2562 | } | ||||
2563 | dataptr = NpyIter_GetDataPtrArray(iter); | ||||
2564 | count_ptr = NpyIter_GetInnerLoopSizePtr(iter); | ||||
2565 | needs_api = NpyIter_IterationNeedsAPI(iter); | ||||
2566 | |||||
2567 | if (!needs_api && !NpyIter_IterationNeedsAPI(iter)) { | ||||
2568 | NPY_BEGIN_THREADS_THRESHOLDED(total_problem_size)do { if ((total_problem_size) > 500) { _save = PyEval_SaveThread ();} } while (0);; | ||||
2569 | } | ||||
2570 | do { | ||||
2571 | inner_dimensions[0] = *count_ptr; | ||||
2572 | innerloop(dataptr, inner_dimensions, inner_strides, innerloopdata); | ||||
2573 | } while (!(needs_api && PyErr_Occurred()) && iternext(iter)); | ||||
2574 | |||||
2575 | if (!needs_api && !NpyIter_IterationNeedsAPI(iter)) { | ||||
2576 | NPY_END_THREADSdo { if (_save) { PyEval_RestoreThread(_save); _save = ((void *)0);} } while (0);; | ||||
2577 | } | ||||
2578 | } | ||||
2579 | |||||
2580 | /* Check whether any errors occurred during the loop */ | ||||
2581 | if (PyErr_Occurred() || | ||||
2582 | _check_ufunc_fperr(errormask, extobj, ufunc_name) < 0) { | ||||
2583 | retval = -1; | ||||
2584 | goto fail; | ||||
2585 | } | ||||
2586 | |||||
2587 | PyArray_freePyMem_RawFree(inner_strides); | ||||
2588 | if (NpyIter_Deallocate(iter) < 0) { | ||||
2589 | retval = -1; | ||||
2590 | } | ||||
2591 | |||||
2592 | /* The caller takes ownership of all the references in op */ | ||||
2593 | for (i = 0; i < nop; ++i) { | ||||
2594 | Py_XDECREF(dtypes[i])_Py_XDECREF(((PyObject*)(dtypes[i]))); | ||||
2595 | Py_XDECREF(arr_prep[i])_Py_XDECREF(((PyObject*)(arr_prep[i]))); | ||||
2596 | } | ||||
2597 | PyArray_freePyMem_RawFree(remap_axis_memory); | ||||
2598 | PyArray_freePyMem_RawFree(remap_axis); | ||||
2599 | |||||
2600 | NPY_UF_DBG_PRINT1("Returning code %d\n", retval); | ||||
2601 | |||||
2602 | return retval; | ||||
2603 | |||||
2604 | fail: | ||||
2605 | NPY_UF_DBG_PRINT1("Returning failure code %d\n", retval); | ||||
2606 | PyArray_freePyMem_RawFree(inner_strides); | ||||
2607 | NpyIter_Deallocate(iter); | ||||
2608 | for (i = 0; i < nop; ++i) { | ||||
2609 | Py_XDECREF(dtypes[i])_Py_XDECREF(((PyObject*)(dtypes[i]))); | ||||
2610 | Py_XDECREF(arr_prep[i])_Py_XDECREF(((PyObject*)(arr_prep[i]))); | ||||
2611 | } | ||||
2612 | PyArray_freePyMem_RawFree(remap_axis_memory); | ||||
2613 | PyArray_freePyMem_RawFree(remap_axis); | ||||
2614 | return retval; | ||||
2615 | } | ||||
2616 | |||||
2617 | |||||
2618 | static int | ||||
2619 | PyUFunc_GenericFunctionInternal(PyUFuncObject *ufunc, PyArrayObject **op, | ||||
2620 | ufunc_full_args full_args, PyObject *type_tup, PyObject *extobj, | ||||
2621 | NPY_CASTING casting, NPY_ORDER order, npy_bool subok, | ||||
2622 | PyArrayObject *wheremask) | ||||
2623 | { | ||||
2624 | int nin, nout; | ||||
2625 | int i, nop; | ||||
2626 | const char *ufunc_name; | ||||
2627 | int retval = -1; | ||||
2628 | npy_uint32 op_flags[NPY_MAXARGS32]; | ||||
2629 | npy_intp default_op_out_flags; | ||||
2630 | |||||
2631 | PyArray_Descr *dtypes[NPY_MAXARGS32]; | ||||
2632 | |||||
2633 | /* These parameters come from extobj= or from a TLS global */ | ||||
2634 | int buffersize = 0, errormask = 0; | ||||
2635 | |||||
2636 | /* The __array_prepare__ function to call for each output */ | ||||
2637 | PyObject *arr_prep[NPY_MAXARGS32]; | ||||
2638 | |||||
2639 | int trivial_loop_ok = 0; | ||||
2640 | |||||
2641 | nin = ufunc->nin; | ||||
2642 | nout = ufunc->nout; | ||||
2643 | nop = nin + nout; | ||||
2644 | |||||
2645 | ufunc_name = ufunc_get_name_cstr(ufunc); | ||||
2646 | |||||
2647 | NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s\n", ufunc_name); | ||||
2648 | |||||
2649 | /* Initialize all the dtypes and __array_prepare__ callbacks to NULL */ | ||||
2650 | for (i = 0; i < nop; ++i) { | ||||
2651 | dtypes[i] = NULL((void*)0); | ||||
2652 | arr_prep[i] = NULL((void*)0); | ||||
2653 | } | ||||
2654 | |||||
2655 | /* Get the buffersize and errormask */ | ||||
2656 | if (_get_bufsize_errmask(extobj, ufunc_name, &buffersize, &errormask) < 0) { | ||||
2657 | retval = -1; | ||||
2658 | goto fail; | ||||
2659 | } | ||||
2660 | |||||
2661 | NPY_UF_DBG_PRINT("Finding inner loop\n"); | ||||
2662 | |||||
2663 | retval = ufunc->type_resolver(ufunc, casting, | ||||
2664 | op, type_tup, dtypes); | ||||
2665 | if (retval < 0) { | ||||
2666 | goto fail; | ||||
2667 | } | ||||
2668 | |||||
2669 | if (wheremask != NULL((void*)0)) { | ||||
2670 | /* Set up the flags. */ | ||||
2671 | default_op_out_flags = NPY_ITER_NO_SUBTYPE0x02000000 | | ||||
2672 | NPY_ITER_WRITEMASKED0x10000000 | | ||||
2673 | NPY_UFUNC_DEFAULT_OUTPUT_FLAGS0x00100000 | 0x01000000 | 0x08000000 | 0x02000000 | 0x40000000; | ||||
2674 | _ufunc_setup_flags(ufunc, NPY_UFUNC_DEFAULT_INPUT_FLAGS0x00020000 | 0x00100000 | 0x40000000, | ||||
2675 | default_op_out_flags, op_flags); | ||||
2676 | } | ||||
2677 | else { | ||||
2678 | /* Set up the flags. */ | ||||
2679 | default_op_out_flags = NPY_ITER_WRITEONLY0x00040000 | | ||||
2680 | NPY_UFUNC_DEFAULT_OUTPUT_FLAGS0x00100000 | 0x01000000 | 0x08000000 | 0x02000000 | 0x40000000; | ||||
2681 | _ufunc_setup_flags(ufunc, NPY_UFUNC_DEFAULT_INPUT_FLAGS0x00020000 | 0x00100000 | 0x40000000, | ||||
2682 | default_op_out_flags, op_flags); | ||||
2683 | } | ||||
2684 | |||||
2685 | #if NPY_UF_DBG_TRACING0 | ||||
2686 | printf("input types:\n")__printf_chk (2 - 1, "input types:\n"); | ||||
2687 | for (i = 0; i < nin; ++i) { | ||||
2688 | PyObject_Print((PyObject *)dtypes[i], stdoutstdout, 0); | ||||
2689 | printf(" ")__printf_chk (2 - 1, " "); | ||||
2690 | } | ||||
2691 | printf("\noutput types:\n")__printf_chk (2 - 1, "\noutput types:\n"); | ||||
2692 | for (i = nin; i < nop; ++i) { | ||||
2693 | PyObject_Print((PyObject *)dtypes[i], stdoutstdout, 0); | ||||
2694 | printf(" ")__printf_chk (2 - 1, " "); | ||||
2695 | } | ||||
2696 | printf("\n")__printf_chk (2 - 1, "\n"); | ||||
2697 | #endif | ||||
2698 | |||||
2699 | if (subok) { | ||||
2700 | /* | ||||
2701 | * Get the appropriate __array_prepare__ function to call | ||||
2702 | * for each output | ||||
2703 | */ | ||||
2704 | _find_array_prepare(full_args, arr_prep, nout); | ||||
2705 | } | ||||
2706 | |||||
2707 | /* Do the ufunc loop */ | ||||
2708 | if (wheremask != NULL((void*)0)) { | ||||
2709 | NPY_UF_DBG_PRINT("Executing fancy inner loop\n"); | ||||
2710 | |||||
2711 | if (nop + 1 > NPY_MAXARGS32) { | ||||
2712 | PyErr_SetString(PyExc_ValueError, | ||||
2713 | "Too many operands when including where= parameter"); | ||||
2714 | return -1; | ||||
2715 | } | ||||
2716 | op[nop] = wheremask; | ||||
2717 | dtypes[nop] = NULL((void*)0); | ||||
2718 | |||||
2719 | /* Set up the flags */ | ||||
2720 | |||||
2721 | npy_clear_floatstatus_barrier((char*)&ufunc); | ||||
2722 | retval = execute_fancy_ufunc_loop(ufunc, wheremask, | ||||
2723 | op, dtypes, order, | ||||
2724 | buffersize, arr_prep, full_args, op_flags); | ||||
2725 | } | ||||
2726 | else { | ||||
2727 | NPY_UF_DBG_PRINT("Executing legacy inner loop\n"); | ||||
2728 | |||||
2729 | /* | ||||
2730 | * This checks whether a trivial loop is ok, making copies of | ||||
2731 | * scalar and one dimensional operands if that will help. | ||||
2732 | * Since it requires dtypes, it can only be called after | ||||
2733 | * ufunc->type_resolver | ||||
2734 | */ | ||||
2735 | trivial_loop_ok = check_for_trivial_loop(ufunc, op, dtypes, buffersize); | ||||
2736 | if (trivial_loop_ok < 0) { | ||||
2737 | goto fail; | ||||
2738 | } | ||||
2739 | |||||
2740 | /* check_for_trivial_loop on half-floats can overflow */ | ||||
2741 | npy_clear_floatstatus_barrier((char*)&ufunc); | ||||
2742 | |||||
2743 | retval = execute_legacy_ufunc_loop(ufunc, trivial_loop_ok, | ||||
2744 | op, dtypes, order, | ||||
2745 | buffersize, arr_prep, full_args, op_flags); | ||||
2746 | } | ||||
2747 | if (retval < 0) { | ||||
2748 | goto fail; | ||||
2749 | } | ||||
2750 | |||||
2751 | /* | ||||
2752 | * Check whether any errors occurred during the loop. The loops should | ||||
2753 | * indicate this in retval, but since the inner-loop currently does not | ||||
2754 | * report errors, this does not happen in all branches (at this time). | ||||
2755 | */ | ||||
2756 | if (PyErr_Occurred() || | ||||
2757 | _check_ufunc_fperr(errormask, extobj, ufunc_name) < 0) { | ||||
2758 | retval = -1; | ||||
2759 | goto fail; | ||||
2760 | } | ||||
2761 | |||||
2762 | |||||
2763 | /* The caller takes ownership of all the references in op */ | ||||
2764 | for (i = 0; i < nop; ++i) { | ||||
2765 | Py_XDECREF(dtypes[i])_Py_XDECREF(((PyObject*)(dtypes[i]))); | ||||
2766 | Py_XDECREF(arr_prep[i])_Py_XDECREF(((PyObject*)(arr_prep[i]))); | ||||
2767 | } | ||||
2768 | |||||
2769 | NPY_UF_DBG_PRINT("Returning success code 0\n"); | ||||
2770 | |||||
2771 | return 0; | ||||
2772 | |||||
2773 | fail: | ||||
2774 | NPY_UF_DBG_PRINT1("Returning failure code %d\n", retval); | ||||
2775 | for (i = 0; i < nop; ++i) { | ||||
2776 | Py_XDECREF(dtypes[i])_Py_XDECREF(((PyObject*)(dtypes[i]))); | ||||
2777 | Py_XDECREF(arr_prep[i])_Py_XDECREF(((PyObject*)(arr_prep[i]))); | ||||
2778 | } | ||||
2779 | |||||
2780 | return retval; | ||||
2781 | } | ||||
2782 | |||||
2783 | |||||
2784 | /*UFUNC_API*/ | ||||
2785 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) int | ||||
2786 | PyUFunc_GenericFunction(PyUFuncObject *NPY_UNUSED(ufunc)(__NPY_UNUSED_TAGGEDufunc) __attribute__ ((__unused__)), | ||||
2787 | PyObject *NPY_UNUSED(args)(__NPY_UNUSED_TAGGEDargs) __attribute__ ((__unused__)), PyObject *NPY_UNUSED(kwds)(__NPY_UNUSED_TAGGEDkwds) __attribute__ ((__unused__)), | ||||
2788 | PyArrayObject **NPY_UNUSED(op)(__NPY_UNUSED_TAGGEDop) __attribute__ ((__unused__))) | ||||
2789 | { | ||||
2790 | /* NumPy 1.21, 2020-03-29 */ | ||||
2791 | PyErr_SetString(PyExc_RuntimeError, | ||||
2792 | "The `PyUFunc_GenericFunction()` C-API function has been disabled. " | ||||
2793 | "Please use `PyObject_Call(ufunc, args, kwargs)`, which has " | ||||
2794 | "identical behaviour but allows subclass and `__array_ufunc__` " | ||||
2795 | "override handling and only returns the normal ufunc result."); | ||||
2796 | return -1; | ||||
2797 | } | ||||
2798 | |||||
2799 | |||||
2800 | /* | ||||
2801 | * Given the output type, finds the specified binary op. The | ||||
2802 | * ufunc must have nin==2 and nout==1. The function may modify | ||||
2803 | * otype if the given type isn't found. | ||||
2804 | * | ||||
2805 | * Returns 0 on success, -1 on failure. | ||||
2806 | */ | ||||
2807 | static int | ||||
2808 | get_binary_op_function(PyUFuncObject *ufunc, int *otype, | ||||
2809 | PyUFuncGenericFunction *out_innerloop, | ||||
2810 | void **out_innerloopdata) | ||||
2811 | { | ||||
2812 | int i; | ||||
2813 | |||||
2814 | NPY_UF_DBG_PRINT1("Getting binary op function for type number %d\n", | ||||
2815 | *otype); | ||||
2816 | |||||
2817 | /* If the type is custom and there are userloops, search for it here */ | ||||
2818 | if (ufunc->userloops != NULL((void*)0) && PyTypeNum_ISUSERDEF(*otype)(((*otype) >= NPY_USERDEF) && ((*otype) < NPY_USERDEF + NPY_NUMUSERTYPES))) { | ||||
2819 | PyObject *key, *obj; | ||||
2820 | key = PyLong_FromLong(*otype); | ||||
2821 | if (key == NULL((void*)0)) { | ||||
2822 | return -1; | ||||
2823 | } | ||||
2824 | obj = PyDict_GetItemWithError(ufunc->userloops, key); | ||||
2825 | Py_DECREF(key)_Py_DECREF(((PyObject*)(key))); | ||||
2826 | if (obj == NULL((void*)0) && PyErr_Occurred()) { | ||||
2827 | return -1; | ||||
2828 | } | ||||
2829 | else if (obj != NULL((void*)0)) { | ||||
2830 | PyUFunc_Loop1d *funcdata = PyCapsule_GetPointer(obj, NULL((void*)0)); | ||||
2831 | if (funcdata == NULL((void*)0)) { | ||||
2832 | return -1; | ||||
2833 | } | ||||
2834 | while (funcdata != NULL((void*)0)) { | ||||
2835 | int *types = funcdata->arg_types; | ||||
2836 | |||||
2837 | if (types[0] == *otype && types[1] == *otype && | ||||
2838 | types[2] == *otype) { | ||||
2839 | *out_innerloop = funcdata->func; | ||||
2840 | *out_innerloopdata = funcdata->data; | ||||
2841 | return 0; | ||||
2842 | } | ||||
2843 | |||||
2844 | funcdata = funcdata->next; | ||||
2845 | } | ||||
2846 | } | ||||
2847 | } | ||||
2848 | |||||
2849 | /* Search for a function with compatible inputs */ | ||||
2850 | for (i = 0; i < ufunc->ntypes; ++i) { | ||||
2851 | char *types = ufunc->types + i*ufunc->nargs; | ||||
2852 | |||||
2853 | NPY_UF_DBG_PRINT3("Trying loop with signature %d %d -> %d\n", | ||||
2854 | types[0], types[1], types[2]); | ||||
2855 | |||||
2856 | if (PyArray_CanCastSafely(*otype, types[0]) && | ||||
2857 | types[0] == types[1] && | ||||
2858 | (*otype == NPY_OBJECT || types[0] != NPY_OBJECT)) { | ||||
2859 | /* If the signature is "xx->x", we found the loop */ | ||||
2860 | if (types[2] == types[0]) { | ||||
2861 | *out_innerloop = ufunc->functions[i]; | ||||
2862 | *out_innerloopdata = ufunc->data[i]; | ||||
2863 | *otype = types[0]; | ||||
2864 | return 0; | ||||
2865 | } | ||||
2866 | /* | ||||
2867 | * Otherwise, we found the natural type of the reduction, | ||||
2868 | * replace otype and search again | ||||
2869 | */ | ||||
2870 | else { | ||||
2871 | *otype = types[2]; | ||||
2872 | break; | ||||
2873 | } | ||||
2874 | } | ||||
2875 | } | ||||
2876 | |||||
2877 | /* Search for the exact function */ | ||||
2878 | for (i = 0; i < ufunc->ntypes; ++i) { | ||||
2879 | char *types = ufunc->types + i*ufunc->nargs; | ||||
2880 | |||||
2881 | if (PyArray_CanCastSafely(*otype, types[0]) && | ||||
2882 | types[0] == types[1] && | ||||
2883 | types[1] == types[2] && | ||||
2884 | (*otype == NPY_OBJECT || types[0] != NPY_OBJECT)) { | ||||
2885 | /* Since the signature is "xx->x", we found the loop */ | ||||
2886 | *out_innerloop = ufunc->functions[i]; | ||||
2887 | *out_innerloopdata = ufunc->data[i]; | ||||
2888 | *otype = types[0]; | ||||
2889 | return 0; | ||||
2890 | } | ||||
2891 | } | ||||
2892 | |||||
2893 | return -1; | ||||
2894 | } | ||||
2895 | |||||
2896 | static int | ||||
2897 | reduce_type_resolver(PyUFuncObject *ufunc, PyArrayObject *arr, | ||||
2898 | PyArray_Descr *odtype, PyArray_Descr **out_dtype) | ||||
2899 | { | ||||
2900 | int i, retcode; | ||||
2901 | PyArrayObject *op[3] = {arr, arr, NULL((void*)0)}; | ||||
2902 | PyArray_Descr *dtypes[3] = {NULL((void*)0), NULL((void*)0), NULL((void*)0)}; | ||||
2903 | const char *ufunc_name = ufunc_get_name_cstr(ufunc); | ||||
2904 | PyObject *type_tup = NULL((void*)0); | ||||
2905 | |||||
2906 | *out_dtype = NULL((void*)0); | ||||
2907 | |||||
2908 | /* | ||||
2909 | * If odtype is specified, make a type tuple for the type | ||||
2910 | * resolution. | ||||
2911 | */ | ||||
2912 | if (odtype != NULL((void*)0)) { | ||||
2913 | type_tup = PyTuple_Pack(3, odtype, odtype, Py_None(&_Py_NoneStruct)); | ||||
2914 | if (type_tup == NULL((void*)0)) { | ||||
2915 | return -1; | ||||
2916 | } | ||||
2917 | } | ||||
2918 | |||||
2919 | /* Use the type resolution function to find our loop */ | ||||
2920 | retcode = ufunc->type_resolver( | ||||
2921 | ufunc, NPY_UNSAFE_CASTING, | ||||
2922 | op, type_tup, dtypes); | ||||
2923 | Py_DECREF(type_tup)_Py_DECREF(((PyObject*)(type_tup))); | ||||
2924 | if (retcode == -1) { | ||||
2925 | return -1; | ||||
2926 | } | ||||
2927 | else if (retcode == -2) { | ||||
2928 | PyErr_Format(PyExc_RuntimeError, | ||||
2929 | "type resolution returned NotImplemented to " | ||||
2930 | "reduce ufunc %s", ufunc_name); | ||||
2931 | return -1; | ||||
2932 | } | ||||
2933 | |||||
2934 | /* | ||||
2935 | * The first two type should be equivalent. Because of how | ||||
2936 | * reduce has historically behaved in NumPy, the return type | ||||
2937 | * could be different, and it is the return type on which the | ||||
2938 | * reduction occurs. | ||||
2939 | */ | ||||
2940 | if (!PyArray_EquivTypes(dtypes[0], dtypes[1])) { | ||||
2941 | for (i = 0; i < 3; ++i) { | ||||
2942 | Py_DECREF(dtypes[i])_Py_DECREF(((PyObject*)(dtypes[i]))); | ||||
2943 | } | ||||
2944 | PyErr_Format(PyExc_RuntimeError, | ||||
2945 | "could not find a type resolution appropriate for " | ||||
2946 | "reduce ufunc %s", ufunc_name); | ||||
2947 | return -1; | ||||
2948 | } | ||||
2949 | |||||
2950 | Py_DECREF(dtypes[0])_Py_DECREF(((PyObject*)(dtypes[0]))); | ||||
2951 | Py_DECREF(dtypes[1])_Py_DECREF(((PyObject*)(dtypes[1]))); | ||||
2952 | *out_dtype = dtypes[2]; | ||||
2953 | |||||
2954 | return 0; | ||||
2955 | } | ||||
2956 | |||||
2957 | static int | ||||
2958 | reduce_loop(NpyIter *iter, char **dataptrs, npy_intp const *strides, | ||||
2959 | npy_intp const *countptr, NpyIter_IterNextFunc *iternext, | ||||
2960 | int needs_api, npy_intp skip_first_count, void *data) | ||||
2961 | { | ||||
2962 | PyArray_Descr *dtypes[3], **iter_dtypes; | ||||
2963 | PyUFuncObject *ufunc = (PyUFuncObject *)data; | ||||
2964 | char *dataptrs_copy[3]; | ||||
2965 | npy_intp strides_copy[3]; | ||||
2966 | npy_bool masked; | ||||
2967 | |||||
2968 | /* The normal selected inner loop */ | ||||
2969 | PyUFuncGenericFunction innerloop = NULL((void*)0); | ||||
2970 | void *innerloopdata = NULL((void*)0); | ||||
2971 | |||||
2972 | NPY_BEGIN_THREADS_DEFPyThreadState *_save=((void*)0);; | ||||
2973 | /* Get the number of operands, to determine whether "where" is used */ | ||||
2974 | masked = (NpyIter_GetNOp(iter) == 3); | ||||
2975 | |||||
2976 | /* Get the inner loop */ | ||||
2977 | iter_dtypes = NpyIter_GetDescrArray(iter); | ||||
2978 | dtypes[0] = iter_dtypes[0]; | ||||
2979 | dtypes[1] = iter_dtypes[1]; | ||||
2980 | dtypes[2] = iter_dtypes[0]; | ||||
2981 | if (ufunc->legacy_inner_loop_selector(ufunc, dtypes, | ||||
2982 | &innerloop, &innerloopdata, &needs_api) < 0) { | ||||
2983 | return -1; | ||||
2984 | } | ||||
2985 | |||||
2986 | NPY_BEGIN_THREADS_NDITER(iter)do { if (!NpyIter_IterationNeedsAPI(iter)) { do { if ((NpyIter_GetIterSize (iter)) > 500) { _save = PyEval_SaveThread();} } while (0) ;; } } while(0); | ||||
2987 | |||||
2988 | if (skip_first_count > 0) { | ||||
2989 | do { | ||||
2990 | npy_intp count = *countptr; | ||||
2991 | |||||
2992 | /* Skip any first-visit elements */ | ||||
2993 | if (NpyIter_IsFirstVisit(iter, 0)) { | ||||
2994 | if (strides[0] == 0) { | ||||
2995 | --count; | ||||
2996 | --skip_first_count; | ||||
2997 | dataptrs[1] += strides[1]; | ||||
2998 | } | ||||
2999 | else { | ||||
3000 | skip_first_count -= count; | ||||
3001 | count = 0; | ||||
3002 | } | ||||
3003 | } | ||||
3004 | |||||
3005 | /* Turn the two items into three for the inner loop */ | ||||
3006 | dataptrs_copy[0] = dataptrs[0]; | ||||
3007 | dataptrs_copy[1] = dataptrs[1]; | ||||
3008 | dataptrs_copy[2] = dataptrs[0]; | ||||
3009 | strides_copy[0] = strides[0]; | ||||
3010 | strides_copy[1] = strides[1]; | ||||
3011 | strides_copy[2] = strides[0]; | ||||
3012 | innerloop(dataptrs_copy, &count, | ||||
3013 | strides_copy, innerloopdata); | ||||
3014 | |||||
3015 | if (needs_api && PyErr_Occurred()) { | ||||
3016 | goto finish_loop; | ||||
3017 | } | ||||
3018 | |||||
3019 | /* Jump to the faster loop when skipping is done */ | ||||
3020 | if (skip_first_count == 0) { | ||||
3021 | if (iternext(iter)) { | ||||
3022 | break; | ||||
3023 | } | ||||
3024 | else { | ||||
3025 | goto finish_loop; | ||||
3026 | } | ||||
3027 | } | ||||
3028 | } while (iternext(iter)); | ||||
3029 | } | ||||
3030 | |||||
3031 | if (needs_api && PyErr_Occurred()) { | ||||
3032 | goto finish_loop; | ||||
3033 | } | ||||
3034 | |||||
3035 | do { | ||||
3036 | /* Turn the two items into three for the inner loop */ | ||||
3037 | dataptrs_copy[0] = dataptrs[0]; | ||||
3038 | dataptrs_copy[1] = dataptrs[1]; | ||||
3039 | dataptrs_copy[2] = dataptrs[0]; | ||||
3040 | strides_copy[0] = strides[0]; | ||||
3041 | strides_copy[1] = strides[1]; | ||||
3042 | strides_copy[2] = strides[0]; | ||||
3043 | |||||
3044 | if (!masked) { | ||||
3045 | innerloop(dataptrs_copy, countptr, | ||||
3046 | strides_copy, innerloopdata); | ||||
3047 | } | ||||
3048 | else { | ||||
3049 | npy_intp count = *countptr; | ||||
3050 | char *maskptr = dataptrs[2]; | ||||
3051 | npy_intp mask_stride = strides[2]; | ||||
3052 | /* Optimization for when the mask is broadcast */ | ||||
3053 | npy_intp n = mask_stride == 0 ? count : 1; | ||||
3054 | while (count) { | ||||
3055 | char mask = *maskptr; | ||||
3056 | maskptr += mask_stride; | ||||
3057 | while (n < count && mask == *maskptr) { | ||||
3058 | n++; | ||||
3059 | maskptr += mask_stride; | ||||
3060 | } | ||||
3061 | /* If mask set, apply inner loop on this contiguous region */ | ||||
3062 | if (mask) { | ||||
3063 | innerloop(dataptrs_copy, &n, | ||||
3064 | strides_copy, innerloopdata); | ||||
3065 | } | ||||
3066 | dataptrs_copy[0] += n * strides[0]; | ||||
3067 | dataptrs_copy[1] += n * strides[1]; | ||||
3068 | dataptrs_copy[2] = dataptrs_copy[0]; | ||||
3069 | count -= n; | ||||
3070 | n = 1; | ||||
3071 | } | ||||
3072 | } | ||||
3073 | } while (!(needs_api && PyErr_Occurred()) && iternext(iter)); | ||||
3074 | |||||
3075 | finish_loop: | ||||
3076 | NPY_END_THREADSdo { if (_save) { PyEval_RestoreThread(_save); _save = ((void *)0);} } while (0);; | ||||
3077 | |||||
3078 | return (needs_api && PyErr_Occurred()) ? -1 : 0; | ||||
3079 | } | ||||
3080 | |||||
3081 | /* | ||||
3082 | * The implementation of the reduction operators with the new iterator | ||||
3083 | * turned into a bit of a long function here, but I think the design | ||||
3084 | * of this part needs to be changed to be more like einsum, so it may | ||||
3085 | * not be worth refactoring it too much. Consider this timing: | ||||
3086 | * | ||||
3087 | * >>> a = arange(10000) | ||||
3088 | * | ||||
3089 | * >>> timeit sum(a) | ||||
3090 | * 10000 loops, best of 3: 17 us per loop | ||||
3091 | * | ||||
3092 | * >>> timeit einsum("i->",a) | ||||
3093 | * 100000 loops, best of 3: 13.5 us per loop | ||||
3094 | * | ||||
3095 | * The axes must already be bounds-checked by the calling function, | ||||
3096 | * this function does not validate them. | ||||
3097 | */ | ||||
3098 | static PyArrayObject * | ||||
3099 | PyUFunc_Reduce(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, | ||||
3100 | int naxes, int *axes, PyArray_Descr *odtype, int keepdims, | ||||
3101 | PyObject *initial, PyArrayObject *wheremask) | ||||
3102 | { | ||||
3103 | int iaxes, ndim; | ||||
3104 | npy_bool reorderable; | ||||
3105 | npy_bool axis_flags[NPY_MAXDIMS32]; | ||||
3106 | PyArray_Descr *dtype; | ||||
3107 | PyArrayObject *result; | ||||
3108 | PyObject *identity; | ||||
3109 | const char *ufunc_name = ufunc_get_name_cstr(ufunc); | ||||
3110 | /* These parameters come from a TLS global */ | ||||
3111 | int buffersize = 0, errormask = 0; | ||||
3112 | |||||
3113 | NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s.reduce\n", ufunc_name); | ||||
3114 | |||||
3115 | ndim = PyArray_NDIM(arr); | ||||
3116 | |||||
3117 | /* Create an array of flags for reduction */ | ||||
3118 | memset(axis_flags, 0, ndim); | ||||
3119 | for (iaxes = 0; iaxes < naxes; ++iaxes) { | ||||
3120 | int axis = axes[iaxes]; | ||||
3121 | if (axis_flags[axis]) { | ||||
3122 | PyErr_SetString(PyExc_ValueError, | ||||
3123 | "duplicate value in 'axis'"); | ||||
3124 | return NULL((void*)0); | ||||
3125 | } | ||||
3126 | axis_flags[axis] = 1; | ||||
3127 | } | ||||
3128 | |||||
3129 | if (_get_bufsize_errmask(NULL((void*)0), "reduce", &buffersize, &errormask) < 0) { | ||||
3130 | return NULL((void*)0); | ||||
3131 | } | ||||
3132 | |||||
3133 | /* Get the identity */ | ||||
3134 | identity = _get_identity(ufunc, &reorderable); | ||||
3135 | if (identity == NULL((void*)0)) { | ||||
3136 | return NULL((void*)0); | ||||
3137 | } | ||||
3138 | |||||
3139 | /* Get the initial value */ | ||||
3140 | if (initial == NULL((void*)0)) { | ||||
3141 | initial = identity; | ||||
3142 | |||||
3143 | /* | ||||
3144 | * The identity for a dynamic dtype like | ||||
3145 | * object arrays can't be used in general | ||||
3146 | */ | ||||
3147 | if (initial != Py_None(&_Py_NoneStruct) && PyArray_ISOBJECT(arr)((PyArray_TYPE(arr)) == NPY_OBJECT) && PyArray_SIZE(arr)PyArray_MultiplyList(PyArray_DIMS(arr), PyArray_NDIM(arr)) != 0) { | ||||
3148 | Py_DECREF(initial)_Py_DECREF(((PyObject*)(initial))); | ||||
3149 | initial = Py_None(&_Py_NoneStruct); | ||||
3150 | Py_INCREF(initial)_Py_INCREF(((PyObject*)(initial))); | ||||
3151 | } | ||||
3152 | } else { | ||||
3153 | Py_DECREF(identity)_Py_DECREF(((PyObject*)(identity))); | ||||
3154 | Py_INCREF(initial)_Py_INCREF(((PyObject*)(initial))); /* match the reference count in the if above */ | ||||
3155 | } | ||||
3156 | |||||
3157 | /* Get the reduction dtype */ | ||||
3158 | if (reduce_type_resolver(ufunc, arr, odtype, &dtype) < 0) { | ||||
3159 | Py_DECREF(initial)_Py_DECREF(((PyObject*)(initial))); | ||||
3160 | return NULL((void*)0); | ||||
3161 | } | ||||
3162 | |||||
3163 | result = PyUFunc_ReduceWrapper(arr, out, wheremask, dtype, dtype, | ||||
3164 | NPY_UNSAFE_CASTING, | ||||
3165 | axis_flags, reorderable, | ||||
3166 | keepdims, | ||||
3167 | initial, | ||||
3168 | reduce_loop, | ||||
3169 | ufunc, buffersize, ufunc_name, errormask); | ||||
3170 | |||||
3171 | Py_DECREF(dtype)_Py_DECREF(((PyObject*)(dtype))); | ||||
3172 | Py_DECREF(initial)_Py_DECREF(((PyObject*)(initial))); | ||||
3173 | return result; | ||||
3174 | } | ||||
3175 | |||||
3176 | |||||
3177 | static PyObject * | ||||
3178 | PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, | ||||
3179 | int axis, int otype) | ||||
3180 | { | ||||
3181 | PyArrayObject *op[2]; | ||||
3182 | PyArray_Descr *op_dtypes[2] = {NULL((void*)0), NULL((void*)0)}; | ||||
3183 | int op_axes_arrays[2][NPY_MAXDIMS32]; | ||||
3184 | int *op_axes[2] = {op_axes_arrays[0], op_axes_arrays[1]}; | ||||
3185 | npy_uint32 op_flags[2]; | ||||
3186 | int idim, ndim, otype_final; | ||||
3187 | int needs_api, need_outer_iterator; | ||||
3188 | |||||
3189 | NpyIter *iter = NULL((void*)0), *iter_inner = NULL((void*)0); | ||||
3190 | |||||
3191 | /* The selected inner loop */ | ||||
3192 | PyUFuncGenericFunction innerloop = NULL((void*)0); | ||||
3193 | void *innerloopdata = NULL((void*)0); | ||||
3194 | |||||
3195 | const char *ufunc_name = ufunc_get_name_cstr(ufunc); | ||||
3196 | |||||
3197 | /* These parameters come from extobj= or from a TLS global */ | ||||
3198 | int buffersize = 0, errormask = 0; | ||||
3199 | |||||
3200 | NPY_BEGIN_THREADS_DEFPyThreadState *_save=((void*)0);; | ||||
3201 | |||||
3202 | NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s.accumulate\n", ufunc_name); | ||||
3203 | |||||
3204 | #if 0 | ||||
3205 | printf("Doing %s.accumulate on array with dtype : ", ufunc_name)__printf_chk (2 - 1, "Doing %s.accumulate on array with dtype : " , ufunc_name); | ||||
3206 | PyObject_Print((PyObject *)PyArray_DESCR(arr), stdoutstdout, 0); | ||||
3207 | printf("\n")__printf_chk (2 - 1, "\n"); | ||||
3208 | #endif | ||||
3209 | |||||
3210 | if (_get_bufsize_errmask(NULL((void*)0), "accumulate", &buffersize, &errormask) < 0) { | ||||
3211 | return NULL((void*)0); | ||||
3212 | } | ||||
3213 | |||||
3214 | /* Take a reference to out for later returning */ | ||||
3215 | Py_XINCREF(out)_Py_XINCREF(((PyObject*)(out))); | ||||
3216 | |||||
3217 | otype_final = otype; | ||||
3218 | if (get_binary_op_function(ufunc, &otype_final, | ||||
3219 | &innerloop, &innerloopdata) < 0) { | ||||
3220 | PyArray_Descr *dtype = PyArray_DescrFromType(otype); | ||||
3221 | PyErr_Format(PyExc_ValueError, | ||||
3222 | "could not find a matching type for %s.accumulate, " | ||||
3223 | "requested type has type code '%c'", | ||||
3224 | ufunc_name, dtype ? dtype->type : '-'); | ||||
3225 | Py_XDECREF(dtype)_Py_XDECREF(((PyObject*)(dtype))); | ||||
3226 | goto fail; | ||||
3227 | } | ||||
3228 | |||||
3229 | ndim = PyArray_NDIM(arr); | ||||
3230 | |||||
3231 | /* | ||||
3232 | * Set up the output data type, using the input's exact | ||||
3233 | * data type if the type number didn't change to preserve | ||||
3234 | * metadata | ||||
3235 | */ | ||||
3236 | if (PyArray_DESCR(arr)->type_num == otype_final) { | ||||
3237 | if (PyArray_ISNBO(PyArray_DESCR(arr)->byteorder)((PyArray_DESCR(arr)->byteorder) != '>')) { | ||||
3238 | op_dtypes[0] = PyArray_DESCR(arr); | ||||
3239 | Py_INCREF(op_dtypes[0])_Py_INCREF(((PyObject*)(op_dtypes[0]))); | ||||
3240 | } | ||||
3241 | else { | ||||
3242 | op_dtypes[0] = PyArray_DescrNewByteorder(PyArray_DESCR(arr), | ||||
3243 | NPY_NATIVE'='); | ||||
3244 | } | ||||
3245 | } | ||||
3246 | else { | ||||
3247 | op_dtypes[0] = PyArray_DescrFromType(otype_final); | ||||
3248 | } | ||||
3249 | if (op_dtypes[0] == NULL((void*)0)) { | ||||
3250 | goto fail; | ||||
3251 | } | ||||
3252 | |||||
3253 | #if NPY_UF_DBG_TRACING0 | ||||
3254 | printf("Found %s.accumulate inner loop with dtype : ", ufunc_name)__printf_chk (2 - 1, "Found %s.accumulate inner loop with dtype : " , ufunc_name); | ||||
3255 | PyObject_Print((PyObject *)op_dtypes[0], stdoutstdout, 0); | ||||
3256 | printf("\n")__printf_chk (2 - 1, "\n"); | ||||
3257 | #endif | ||||
3258 | |||||
3259 | /* Set up the op_axes for the outer loop */ | ||||
3260 | for (idim = 0; idim < ndim; ++idim) { | ||||
3261 | op_axes_arrays[0][idim] = idim; | ||||
3262 | op_axes_arrays[1][idim] = idim; | ||||
3263 | } | ||||
3264 | |||||
3265 | /* The per-operand flags for the outer loop */ | ||||
3266 | op_flags[0] = NPY_ITER_READWRITE0x00010000 | | ||||
3267 | NPY_ITER_NO_BROADCAST0x08000000 | | ||||
3268 | NPY_ITER_ALLOCATE0x01000000 | | ||||
3269 | NPY_ITER_NO_SUBTYPE0x02000000; | ||||
3270 | op_flags[1] = NPY_ITER_READONLY0x00020000; | ||||
3271 | |||||
3272 | op[0] = out; | ||||
3273 | op[1] = arr; | ||||
3274 | |||||
3275 | need_outer_iterator = (ndim > 1); | ||||
3276 | /* We can't buffer, so must do UPDATEIFCOPY */ | ||||
3277 | if (!PyArray_ISALIGNED(arr)PyArray_CHKFLAGS((arr), 0x0100) || (out && !PyArray_ISALIGNED(out)PyArray_CHKFLAGS((out), 0x0100)) || | ||||
3278 | !PyArray_EquivTypes(op_dtypes[0], PyArray_DESCR(arr)) || | ||||
3279 | (out && | ||||
3280 | !PyArray_EquivTypes(op_dtypes[0], PyArray_DESCR(out)))) { | ||||
3281 | need_outer_iterator = 1; | ||||
3282 | } | ||||
3283 | /* If input and output overlap in memory, use iterator to figure it out */ | ||||
3284 | else if (out != NULL((void*)0) && solve_may_share_memory(out, arr, NPY_MAY_SHARE_BOUNDS0) != 0) { | ||||
3285 | need_outer_iterator = 1; | ||||
3286 | } | ||||
3287 | |||||
3288 | if (need_outer_iterator) { | ||||
3289 | int ndim_iter = 0; | ||||
3290 | npy_uint32 flags = NPY_ITER_ZEROSIZE_OK0x00000040| | ||||
3291 | NPY_ITER_REFS_OK0x00000020| | ||||
3292 | NPY_ITER_COPY_IF_OVERLAP0x00002000; | ||||
3293 | PyArray_Descr **op_dtypes_param = NULL((void*)0); | ||||
3294 | |||||
3295 | /* | ||||
3296 | * The way accumulate is set up, we can't do buffering, | ||||
3297 | * so make a copy instead when necessary. | ||||
3298 | */ | ||||
3299 | ndim_iter = ndim; | ||||
3300 | flags |= NPY_ITER_MULTI_INDEX0x00000004; | ||||
3301 | /* | ||||
3302 | * Add some more flags. | ||||
3303 | * | ||||
3304 | * The accumulation outer loop is 'elementwise' over the array, so turn | ||||
3305 | * on NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE. That is, in-place | ||||
3306 | * accumulate(x, out=x) is safe to do without temporary copies. | ||||
3307 | */ | ||||
3308 | op_flags[0] |= NPY_ITER_UPDATEIFCOPY0x00800000|NPY_ITER_ALIGNED0x00100000|NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE0x40000000; | ||||
3309 | op_flags[1] |= NPY_ITER_COPY0x00400000|NPY_ITER_ALIGNED0x00100000|NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE0x40000000; | ||||
3310 | op_dtypes_param = op_dtypes; | ||||
3311 | op_dtypes[1] = op_dtypes[0]; | ||||
3312 | NPY_UF_DBG_PRINT("Allocating outer iterator\n"); | ||||
3313 | iter = NpyIter_AdvancedNew(2, op, flags, | ||||
3314 | NPY_KEEPORDER, NPY_UNSAFE_CASTING, | ||||
3315 | op_flags, | ||||
3316 | op_dtypes_param, | ||||
3317 | ndim_iter, op_axes, NULL((void*)0), 0); | ||||
3318 | if (iter == NULL((void*)0)) { | ||||
3319 | goto fail; | ||||
3320 | } | ||||
3321 | |||||
3322 | /* In case COPY or UPDATEIFCOPY occurred */ | ||||
3323 | op[0] = NpyIter_GetOperandArray(iter)[0]; | ||||
3324 | op[1] = NpyIter_GetOperandArray(iter)[1]; | ||||
3325 | |||||
3326 | if (NpyIter_RemoveAxis(iter, axis) != NPY_SUCCEED1) { | ||||
3327 | goto fail; | ||||
3328 | } | ||||
3329 | if (NpyIter_RemoveMultiIndex(iter) != NPY_SUCCEED1) { | ||||
3330 | goto fail; | ||||
3331 | } | ||||
3332 | } | ||||
3333 | |||||
3334 | /* Get the output */ | ||||
3335 | if (out == NULL((void*)0)) { | ||||
3336 | if (iter) { | ||||
3337 | op[0] = out = NpyIter_GetOperandArray(iter)[0]; | ||||
3338 | Py_INCREF(out)_Py_INCREF(((PyObject*)(out))); | ||||
3339 | } | ||||
3340 | else { | ||||
3341 | PyArray_Descr *dtype = op_dtypes[0]; | ||||
3342 | Py_INCREF(dtype)_Py_INCREF(((PyObject*)(dtype))); | ||||
3343 | op[0] = out = (PyArrayObject *)PyArray_NewFromDescr( | ||||
3344 | &PyArray_Type, dtype, | ||||
3345 | ndim, PyArray_DIMS(op[1]), NULL((void*)0), NULL((void*)0), | ||||
3346 | 0, NULL((void*)0)); | ||||
3347 | if (out == NULL((void*)0)) { | ||||
3348 | goto fail; | ||||
3349 | } | ||||
3350 | |||||
3351 | } | ||||
3352 | } | ||||
3353 | |||||
3354 | /* | ||||
3355 | * If the reduction axis has size zero, either return the reduction | ||||
3356 | * unit for UFUNC_REDUCE, or return the zero-sized output array | ||||
3357 | * for UFUNC_ACCUMULATE. | ||||
3358 | */ | ||||
3359 | if (PyArray_DIM(op[1], axis) == 0) { | ||||
3360 | goto finish; | ||||
3361 | } | ||||
3362 | else if (PyArray_SIZE(op[0])PyArray_MultiplyList(PyArray_DIMS(op[0]), PyArray_NDIM(op[0]) ) == 0) { | ||||
3363 | goto finish; | ||||
3364 | } | ||||
3365 | |||||
3366 | if (iter && NpyIter_GetIterSize(iter) != 0) { | ||||
3367 | char *dataptr_copy[3]; | ||||
3368 | npy_intp stride_copy[3]; | ||||
3369 | npy_intp count_m1, stride0, stride1; | ||||
3370 | |||||
3371 | NpyIter_IterNextFunc *iternext; | ||||
3372 | char **dataptr; | ||||
3373 | |||||
3374 | int itemsize = op_dtypes[0]->elsize; | ||||
3375 | |||||
3376 | /* Get the variables needed for the loop */ | ||||
3377 | iternext = NpyIter_GetIterNext(iter, NULL((void*)0)); | ||||
3378 | if (iternext == NULL((void*)0)) { | ||||
3379 | goto fail; | ||||
3380 | } | ||||
3381 | dataptr = NpyIter_GetDataPtrArray(iter); | ||||
3382 | needs_api = NpyIter_IterationNeedsAPI(iter); | ||||
3383 | |||||
3384 | |||||
3385 | /* Execute the loop with just the outer iterator */ | ||||
3386 | count_m1 = PyArray_DIM(op[1], axis)-1; | ||||
3387 | stride0 = 0, stride1 = PyArray_STRIDE(op[1], axis); | ||||
3388 | |||||
3389 | NPY_UF_DBG_PRINT("UFunc: Reduce loop with just outer iterator\n"); | ||||
3390 | |||||
3391 | stride0 = PyArray_STRIDE(op[0], axis); | ||||
3392 | |||||
3393 | stride_copy[0] = stride0; | ||||
3394 | stride_copy[1] = stride1; | ||||
3395 | stride_copy[2] = stride0; | ||||
3396 | |||||
3397 | NPY_BEGIN_THREADS_NDITER(iter)do { if (!NpyIter_IterationNeedsAPI(iter)) { do { if ((NpyIter_GetIterSize (iter)) > 500) { _save = PyEval_SaveThread();} } while (0) ;; } } while(0); | ||||
3398 | |||||
3399 | do { | ||||
3400 | dataptr_copy[0] = dataptr[0]; | ||||
3401 | dataptr_copy[1] = dataptr[1]; | ||||
3402 | dataptr_copy[2] = dataptr[0]; | ||||
3403 | |||||
3404 | /* | ||||
3405 | * Copy the first element to start the reduction. | ||||
3406 | * | ||||
3407 | * Output (dataptr[0]) and input (dataptr[1]) may point to | ||||
3408 | * the same memory, e.g. np.add.accumulate(a, out=a). | ||||
3409 | */ | ||||
3410 | if (otype == NPY_OBJECT) { | ||||
3411 | /* | ||||
3412 | * Incref before decref to avoid the possibility of the | ||||
3413 | * reference count being zero temporarily. | ||||
3414 | */ | ||||
3415 | Py_XINCREF(*(PyObject **)dataptr_copy[1])_Py_XINCREF(((PyObject*)(*(PyObject **)dataptr_copy[1]))); | ||||
3416 | Py_XDECREF(*(PyObject **)dataptr_copy[0])_Py_XDECREF(((PyObject*)(*(PyObject **)dataptr_copy[0]))); | ||||
3417 | *(PyObject **)dataptr_copy[0] = | ||||
3418 | *(PyObject **)dataptr_copy[1]; | ||||
3419 | } | ||||
3420 | else { | ||||
3421 | memmove(dataptr_copy[0], dataptr_copy[1], itemsize); | ||||
3422 | } | ||||
3423 | |||||
3424 | if (count_m1 > 0) { | ||||
3425 | /* Turn the two items into three for the inner loop */ | ||||
3426 | dataptr_copy[1] += stride1; | ||||
3427 | dataptr_copy[2] += stride0; | ||||
3428 | NPY_UF_DBG_PRINT1("iterator loop count %d\n", | ||||
3429 | (int)count_m1); | ||||
3430 | innerloop(dataptr_copy, &count_m1, | ||||
3431 | stride_copy, innerloopdata); | ||||
3432 | } | ||||
3433 | } while (!(needs_api && PyErr_Occurred()) && iternext(iter)); | ||||
3434 | |||||
3435 | NPY_END_THREADSdo { if (_save) { PyEval_RestoreThread(_save); _save = ((void *)0);} } while (0);; | ||||
3436 | } | ||||
3437 | else if (iter == NULL((void*)0)) { | ||||
3438 | char *dataptr_copy[3]; | ||||
3439 | npy_intp stride_copy[3]; | ||||
3440 | |||||
3441 | int itemsize = op_dtypes[0]->elsize; | ||||
3442 | |||||
3443 | /* Execute the loop with no iterators */ | ||||
3444 | npy_intp count = PyArray_DIM(op[1], axis); | ||||
3445 | npy_intp stride0 = 0, stride1 = PyArray_STRIDE(op[1], axis); | ||||
3446 | |||||
3447 | NPY_UF_DBG_PRINT("UFunc: Reduce loop with no iterators\n"); | ||||
3448 | |||||
3449 | if (PyArray_NDIM(op[0]) != PyArray_NDIM(op[1]) || | ||||
3450 | !PyArray_CompareLists(PyArray_DIMS(op[0]), | ||||
3451 | PyArray_DIMS(op[1]), | ||||
3452 | PyArray_NDIM(op[0]))) { | ||||
3453 | PyErr_SetString(PyExc_ValueError, | ||||
3454 | "provided out is the wrong size " | ||||
3455 | "for the reduction"); | ||||
3456 | goto fail; | ||||
3457 | } | ||||
3458 | stride0 = PyArray_STRIDE(op[0], axis); | ||||
3459 | |||||
3460 | stride_copy[0] = stride0; | ||||
3461 | stride_copy[1] = stride1; | ||||
3462 | stride_copy[2] = stride0; | ||||
3463 | |||||
3464 | /* Turn the two items into three for the inner loop */ | ||||
3465 | dataptr_copy[0] = PyArray_BYTES(op[0]); | ||||
3466 | dataptr_copy[1] = PyArray_BYTES(op[1]); | ||||
3467 | dataptr_copy[2] = PyArray_BYTES(op[0]); | ||||
3468 | |||||
3469 | /* | ||||
3470 | * Copy the first element to start the reduction. | ||||
3471 | * | ||||
3472 | * Output (dataptr[0]) and input (dataptr[1]) may point to the | ||||
3473 | * same memory, e.g. np.add.accumulate(a, out=a). | ||||
3474 | */ | ||||
3475 | if (otype == NPY_OBJECT) { | ||||
3476 | /* | ||||
3477 | * Incref before decref to avoid the possibility of the | ||||
3478 | * reference count being zero temporarily. | ||||
3479 | */ | ||||
3480 | Py_XINCREF(*(PyObject **)dataptr_copy[1])_Py_XINCREF(((PyObject*)(*(PyObject **)dataptr_copy[1]))); | ||||
3481 | Py_XDECREF(*(PyObject **)dataptr_copy[0])_Py_XDECREF(((PyObject*)(*(PyObject **)dataptr_copy[0]))); | ||||
3482 | *(PyObject **)dataptr_copy[0] = | ||||
3483 | *(PyObject **)dataptr_copy[1]; | ||||
3484 | } | ||||
3485 | else { | ||||
3486 | memmove(dataptr_copy[0], dataptr_copy[1], itemsize); | ||||
3487 | } | ||||
3488 | |||||
3489 | if (count > 1) { | ||||
3490 | --count; | ||||
3491 | dataptr_copy[1] += stride1; | ||||
3492 | dataptr_copy[2] += stride0; | ||||
3493 | |||||
3494 | NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)count); | ||||
3495 | |||||
3496 | needs_api = PyDataType_REFCHK(op_dtypes[0])(((op_dtypes[0])->flags & (0x01)) == (0x01)); | ||||
3497 | |||||
3498 | if (!needs_api) { | ||||
3499 | NPY_BEGIN_THREADS_THRESHOLDED(count)do { if ((count) > 500) { _save = PyEval_SaveThread();} } while (0);; | ||||
3500 | } | ||||
3501 | |||||
3502 | innerloop(dataptr_copy, &count, | ||||
3503 | stride_copy, innerloopdata); | ||||
3504 | |||||
3505 | NPY_END_THREADSdo { if (_save) { PyEval_RestoreThread(_save); _save = ((void *)0);} } while (0);; | ||||
3506 | } | ||||
3507 | } | ||||
3508 | |||||
3509 | finish: | ||||
3510 | Py_XDECREF(op_dtypes[0])_Py_XDECREF(((PyObject*)(op_dtypes[0]))); | ||||
3511 | int res = 0; | ||||
3512 | if (!NpyIter_Deallocate(iter)) { | ||||
3513 | res = -1; | ||||
3514 | } | ||||
3515 | if (!NpyIter_Deallocate(iter_inner)) { | ||||
3516 | res = -1; | ||||
3517 | } | ||||
3518 | if (res < 0) { | ||||
3519 | Py_DECREF(out)_Py_DECREF(((PyObject*)(out))); | ||||
3520 | return NULL((void*)0); | ||||
3521 | } | ||||
3522 | |||||
3523 | return (PyObject *)out; | ||||
3524 | |||||
3525 | fail: | ||||
3526 | Py_XDECREF(out)_Py_XDECREF(((PyObject*)(out))); | ||||
3527 | Py_XDECREF(op_dtypes[0])_Py_XDECREF(((PyObject*)(op_dtypes[0]))); | ||||
3528 | |||||
3529 | NpyIter_Deallocate(iter); | ||||
3530 | NpyIter_Deallocate(iter_inner); | ||||
3531 | |||||
3532 | return NULL((void*)0); | ||||
3533 | } | ||||
3534 | |||||
3535 | /* | ||||
3536 | * Reduceat performs a reduce over an axis using the indices as a guide | ||||
3537 | * | ||||
3538 | * op.reduceat(array,indices) computes | ||||
3539 | * op.reduce(array[indices[i]:indices[i+1]] | ||||
3540 | * for i=0..end with an implicit indices[i+1]=len(array) | ||||
3541 | * assumed when i=end-1 | ||||
3542 | * | ||||
3543 | * if indices[i+1] <= indices[i]+1 | ||||
3544 | * then the result is array[indices[i]] for that value | ||||
3545 | * | ||||
3546 | * op.accumulate(array) is the same as | ||||
3547 | * op.reduceat(array,indices)[::2] | ||||
3548 | * where indices is range(len(array)-1) with a zero placed in every other sample | ||||
3549 | * indices = zeros(len(array)*2-1) | ||||
3550 | * indices[1::2] = range(1,len(array)) | ||||
3551 | * | ||||
3552 | * output shape is based on the size of indices | ||||
3553 | */ | ||||
3554 | static PyObject * | ||||
3555 | PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, | ||||
3556 | PyArrayObject *out, int axis, int otype) | ||||
3557 | { | ||||
3558 | PyArrayObject *op[3]; | ||||
3559 | PyArray_Descr *op_dtypes[3] = {NULL((void*)0), NULL((void*)0), NULL((void*)0)}; | ||||
3560 | int op_axes_arrays[3][NPY_MAXDIMS32]; | ||||
3561 | int *op_axes[3] = {op_axes_arrays[0], op_axes_arrays[1], | ||||
3562 | op_axes_arrays[2]}; | ||||
3563 | npy_uint32 op_flags[3]; | ||||
3564 | int idim, ndim, otype_final; | ||||
3565 | int need_outer_iterator = 0; | ||||
3566 | |||||
3567 | NpyIter *iter = NULL((void*)0); | ||||
3568 | |||||
3569 | /* The reduceat indices - ind must be validated outside this call */ | ||||
3570 | npy_intp *reduceat_ind; | ||||
3571 | npy_intp i, ind_size, red_axis_size; | ||||
3572 | /* The selected inner loop */ | ||||
3573 | PyUFuncGenericFunction innerloop = NULL((void*)0); | ||||
3574 | void *innerloopdata = NULL((void*)0); | ||||
3575 | |||||
3576 | const char *ufunc_name = ufunc_get_name_cstr(ufunc); | ||||
3577 | char *opname = "reduceat"; | ||||
3578 | |||||
3579 | /* These parameters come from extobj= or from a TLS global */ | ||||
3580 | int buffersize = 0, errormask = 0; | ||||
3581 | |||||
3582 | NPY_BEGIN_THREADS_DEFPyThreadState *_save=((void*)0);; | ||||
3583 | |||||
3584 | reduceat_ind = (npy_intp *)PyArray_DATA(ind); | ||||
3585 | ind_size = PyArray_DIM(ind, 0); | ||||
3586 | red_axis_size = PyArray_DIM(arr, axis); | ||||
3587 | |||||
3588 | /* Check for out-of-bounds values in indices array */ | ||||
3589 | for (i = 0; i < ind_size; ++i) { | ||||
3590 | if (reduceat_ind[i] < 0 || reduceat_ind[i] >= red_axis_size) { | ||||
3591 | PyErr_Format(PyExc_IndexError, | ||||
3592 | "index %" NPY_INTP_FMT"ld" " out-of-bounds in %s.%s [0, %" NPY_INTP_FMT"ld" ")", | ||||
3593 | reduceat_ind[i], ufunc_name, opname, red_axis_size); | ||||
3594 | return NULL((void*)0); | ||||
3595 | } | ||||
3596 | } | ||||
3597 | |||||
3598 | NPY_UF_DBG_PRINT2("\nEvaluating ufunc %s.%s\n", ufunc_name, opname); | ||||
3599 | |||||
3600 | #if 0 | ||||
3601 | printf("Doing %s.%s on array with dtype : ", ufunc_name, opname)__printf_chk (2 - 1, "Doing %s.%s on array with dtype : ", ufunc_name , opname); | ||||
3602 | PyObject_Print((PyObject *)PyArray_DESCR(arr), stdoutstdout, 0); | ||||
3603 | printf("\n")__printf_chk (2 - 1, "\n"); | ||||
3604 | printf("Index size is %d\n", (int)ind_size)__printf_chk (2 - 1, "Index size is %d\n", (int)ind_size); | ||||
3605 | #endif | ||||
3606 | |||||
3607 | if (_get_bufsize_errmask(NULL((void*)0), opname, &buffersize, &errormask) < 0) { | ||||
3608 | return NULL((void*)0); | ||||
3609 | } | ||||
3610 | |||||
3611 | /* Take a reference to out for later returning */ | ||||
3612 | Py_XINCREF(out)_Py_XINCREF(((PyObject*)(out))); | ||||
3613 | |||||
3614 | otype_final = otype; | ||||
3615 | if (get_binary_op_function(ufunc, &otype_final, | ||||
3616 | &innerloop, &innerloopdata) < 0) { | ||||
3617 | PyArray_Descr *dtype = PyArray_DescrFromType(otype); | ||||
3618 | PyErr_Format(PyExc_ValueError, | ||||
3619 | "could not find a matching type for %s.%s, " | ||||
3620 | "requested type has type code '%c'", | ||||
3621 | ufunc_name, opname, dtype ? dtype->type : '-'); | ||||
3622 | Py_XDECREF(dtype)_Py_XDECREF(((PyObject*)(dtype))); | ||||
3623 | goto fail; | ||||
3624 | } | ||||
3625 | |||||
3626 | ndim = PyArray_NDIM(arr); | ||||
3627 | |||||
3628 | /* | ||||
3629 | * Set up the output data type, using the input's exact | ||||
3630 | * data type if the type number didn't change to preserve | ||||
3631 | * metadata | ||||
3632 | */ | ||||
3633 | if (PyArray_DESCR(arr)->type_num == otype_final) { | ||||
3634 | if (PyArray_ISNBO(PyArray_DESCR(arr)->byteorder)((PyArray_DESCR(arr)->byteorder) != '>')) { | ||||
3635 | op_dtypes[0] = PyArray_DESCR(arr); | ||||
3636 | Py_INCREF(op_dtypes[0])_Py_INCREF(((PyObject*)(op_dtypes[0]))); | ||||
3637 | } | ||||
3638 | else { | ||||
3639 | op_dtypes[0] = PyArray_DescrNewByteorder(PyArray_DESCR(arr), | ||||
3640 | NPY_NATIVE'='); | ||||
3641 | } | ||||
3642 | } | ||||
3643 | else { | ||||
3644 | op_dtypes[0] = PyArray_DescrFromType(otype_final); | ||||
3645 | } | ||||
3646 | if (op_dtypes[0] == NULL((void*)0)) { | ||||
3647 | goto fail; | ||||
3648 | } | ||||
3649 | |||||
3650 | #if NPY_UF_DBG_TRACING0 | ||||
3651 | printf("Found %s.%s inner loop with dtype : ", ufunc_name, opname)__printf_chk (2 - 1, "Found %s.%s inner loop with dtype : ", ufunc_name, opname); | ||||
3652 | PyObject_Print((PyObject *)op_dtypes[0], stdoutstdout, 0); | ||||
3653 | printf("\n")__printf_chk (2 - 1, "\n"); | ||||
3654 | #endif | ||||
3655 | |||||
3656 | /* Set up the op_axes for the outer loop */ | ||||
3657 | for (idim = 0; idim < ndim; ++idim) { | ||||
3658 | /* Use the i-th iteration dimension to match up ind */ | ||||
3659 | if (idim == axis) { | ||||
3660 | op_axes_arrays[0][idim] = axis; | ||||
3661 | op_axes_arrays[1][idim] = -1; | ||||
3662 | op_axes_arrays[2][idim] = 0; | ||||
3663 | } | ||||
3664 | else { | ||||
3665 | op_axes_arrays[0][idim] = idim; | ||||
3666 | op_axes_arrays[1][idim] = idim; | ||||
3667 | op_axes_arrays[2][idim] = -1; | ||||
3668 | } | ||||
3669 | } | ||||
3670 | |||||
3671 | op[0] = out; | ||||
3672 | op[1] = arr; | ||||
3673 | op[2] = ind; | ||||
3674 | |||||
3675 | if (out != NULL((void*)0) || ndim > 1 || !PyArray_ISALIGNED(arr)PyArray_CHKFLAGS((arr), 0x0100) || | ||||
3676 | !PyArray_EquivTypes(op_dtypes[0], PyArray_DESCR(arr))) { | ||||
3677 | need_outer_iterator = 1; | ||||
3678 | } | ||||
3679 | |||||
3680 | if (need_outer_iterator) { | ||||
3681 | npy_uint32 flags = NPY_ITER_ZEROSIZE_OK0x00000040| | ||||
3682 | NPY_ITER_REFS_OK0x00000020| | ||||
3683 | NPY_ITER_MULTI_INDEX0x00000004| | ||||
3684 | NPY_ITER_COPY_IF_OVERLAP0x00002000; | ||||
3685 | |||||
3686 | /* | ||||
3687 | * The way reduceat is set up, we can't do buffering, | ||||
3688 | * so make a copy instead when necessary using | ||||
3689 | * the UPDATEIFCOPY flag | ||||
3690 | */ | ||||
3691 | |||||
3692 | /* The per-operand flags for the outer loop */ | ||||
3693 | op_flags[0] = NPY_ITER_READWRITE0x00010000| | ||||
3694 | NPY_ITER_NO_BROADCAST0x08000000| | ||||
3695 | NPY_ITER_ALLOCATE0x01000000| | ||||
3696 | NPY_ITER_NO_SUBTYPE0x02000000| | ||||
3697 | NPY_ITER_UPDATEIFCOPY0x00800000| | ||||
3698 | NPY_ITER_ALIGNED0x00100000; | ||||
3699 | op_flags[1] = NPY_ITER_READONLY0x00020000| | ||||
3700 | NPY_ITER_COPY0x00400000| | ||||
3701 | NPY_ITER_ALIGNED0x00100000; | ||||
3702 | op_flags[2] = NPY_ITER_READONLY0x00020000; | ||||
3703 | |||||
3704 | op_dtypes[1] = op_dtypes[0]; | ||||
3705 | |||||
3706 | NPY_UF_DBG_PRINT("Allocating outer iterator\n"); | ||||
3707 | iter = NpyIter_AdvancedNew(3, op, flags, | ||||
3708 | NPY_KEEPORDER, NPY_UNSAFE_CASTING, | ||||
3709 | op_flags, | ||||
3710 | op_dtypes, | ||||
3711 | ndim, op_axes, NULL((void*)0), 0); | ||||
3712 | if (iter == NULL((void*)0)) { | ||||
3713 | goto fail; | ||||
3714 | } | ||||
3715 | |||||
3716 | /* Remove the inner loop axis from the outer iterator */ | ||||
3717 | if (NpyIter_RemoveAxis(iter, axis) != NPY_SUCCEED1) { | ||||
3718 | goto fail; | ||||
3719 | } | ||||
3720 | if (NpyIter_RemoveMultiIndex(iter) != NPY_SUCCEED1) { | ||||
3721 | goto fail; | ||||
3722 | } | ||||
3723 | |||||
3724 | /* In case COPY or UPDATEIFCOPY occurred */ | ||||
3725 | op[0] = NpyIter_GetOperandArray(iter)[0]; | ||||
3726 | op[1] = NpyIter_GetOperandArray(iter)[1]; | ||||
3727 | op[2] = NpyIter_GetOperandArray(iter)[2]; | ||||
3728 | |||||
3729 | if (out == NULL((void*)0)) { | ||||
3730 | out = op[0]; | ||||
3731 | Py_INCREF(out)_Py_INCREF(((PyObject*)(out))); | ||||
3732 | } | ||||
3733 | } | ||||
3734 | /* Allocate the output for when there's no outer iterator */ | ||||
3735 | else if (out == NULL((void*)0)) { | ||||
3736 | Py_INCREF(op_dtypes[0])_Py_INCREF(((PyObject*)(op_dtypes[0]))); | ||||
3737 | op[0] = out = (PyArrayObject *)PyArray_NewFromDescr( | ||||
3738 | &PyArray_Type, op_dtypes[0], | ||||
3739 | 1, &ind_size, NULL((void*)0), NULL((void*)0), | ||||
3740 | 0, NULL((void*)0)); | ||||
3741 | if (out == NULL((void*)0)) { | ||||
3742 | goto fail; | ||||
3743 | } | ||||
3744 | } | ||||
3745 | |||||
3746 | /* | ||||
3747 | * If the output has zero elements, return now. | ||||
3748 | */ | ||||
3749 | if (PyArray_SIZE(op[0])PyArray_MultiplyList(PyArray_DIMS(op[0]), PyArray_NDIM(op[0]) ) == 0) { | ||||
3750 | goto finish; | ||||
3751 | } | ||||
3752 | |||||
3753 | if (iter && NpyIter_GetIterSize(iter) != 0) { | ||||
3754 | char *dataptr_copy[3]; | ||||
3755 | npy_intp stride_copy[3]; | ||||
3756 | |||||
3757 | NpyIter_IterNextFunc *iternext; | ||||
3758 | char **dataptr; | ||||
3759 | npy_intp count_m1; | ||||
3760 | npy_intp stride0, stride1; | ||||
3761 | npy_intp stride0_ind = PyArray_STRIDE(op[0], axis); | ||||
3762 | |||||
3763 | int itemsize = op_dtypes[0]->elsize; | ||||
3764 | int needs_api = NpyIter_IterationNeedsAPI(iter); | ||||
3765 | |||||
3766 | /* Get the variables needed for the loop */ | ||||
3767 | iternext = NpyIter_GetIterNext(iter, NULL((void*)0)); | ||||
3768 | if (iternext == NULL((void*)0)) { | ||||
3769 | goto fail; | ||||
3770 | } | ||||
3771 | dataptr = NpyIter_GetDataPtrArray(iter); | ||||
3772 | |||||
3773 | /* Execute the loop with just the outer iterator */ | ||||
3774 | count_m1 = PyArray_DIM(op[1], axis)-1; | ||||
3775 | stride0 = 0; | ||||
3776 | stride1 = PyArray_STRIDE(op[1], axis); | ||||
3777 | |||||
3778 | NPY_UF_DBG_PRINT("UFunc: Reduce loop with just outer iterator\n"); | ||||
3779 | |||||
3780 | stride_copy[0] = stride0; | ||||
3781 | stride_copy[1] = stride1; | ||||
3782 | stride_copy[2] = stride0; | ||||
3783 | |||||
3784 | NPY_BEGIN_THREADS_NDITER(iter)do { if (!NpyIter_IterationNeedsAPI(iter)) { do { if ((NpyIter_GetIterSize (iter)) > 500) { _save = PyEval_SaveThread();} } while (0) ;; } } while(0); | ||||
3785 | |||||
3786 | do { | ||||
3787 | |||||
3788 | for (i = 0; i < ind_size; ++i) { | ||||
3789 | npy_intp start = reduceat_ind[i], | ||||
3790 | end = (i == ind_size-1) ? count_m1+1 : | ||||
3791 | reduceat_ind[i+1]; | ||||
3792 | npy_intp count = end - start; | ||||
3793 | |||||
3794 | dataptr_copy[0] = dataptr[0] + stride0_ind*i; | ||||
3795 | dataptr_copy[1] = dataptr[1] + stride1*start; | ||||
3796 | dataptr_copy[2] = dataptr[0] + stride0_ind*i; | ||||
3797 | |||||
3798 | /* | ||||
3799 | * Copy the first element to start the reduction. | ||||
3800 | * | ||||
3801 | * Output (dataptr[0]) and input (dataptr[1]) may point | ||||
3802 | * to the same memory, e.g. | ||||
3803 | * np.add.reduceat(a, np.arange(len(a)), out=a). | ||||
3804 | */ | ||||
3805 | if (otype == NPY_OBJECT) { | ||||
3806 | /* | ||||
3807 | * Incref before decref to avoid the possibility of | ||||
3808 | * the reference count being zero temporarily. | ||||
3809 | */ | ||||
3810 | Py_XINCREF(*(PyObject **)dataptr_copy[1])_Py_XINCREF(((PyObject*)(*(PyObject **)dataptr_copy[1]))); | ||||
3811 | Py_XDECREF(*(PyObject **)dataptr_copy[0])_Py_XDECREF(((PyObject*)(*(PyObject **)dataptr_copy[0]))); | ||||
3812 | *(PyObject **)dataptr_copy[0] = | ||||
3813 | *(PyObject **)dataptr_copy[1]; | ||||
3814 | } | ||||
3815 | else { | ||||
3816 | memmove(dataptr_copy[0], dataptr_copy[1], itemsize); | ||||
3817 | } | ||||
3818 | |||||
3819 | if (count > 1) { | ||||
3820 | /* Inner loop like REDUCE */ | ||||
3821 | --count; | ||||
3822 | dataptr_copy[1] += stride1; | ||||
3823 | NPY_UF_DBG_PRINT1("iterator loop count %d\n", | ||||
3824 | (int)count); | ||||
3825 | innerloop(dataptr_copy, &count, | ||||
3826 | stride_copy, innerloopdata); | ||||
3827 | } | ||||
3828 | } | ||||
3829 | } while (!(needs_api && PyErr_Occurred()) && iternext(iter)); | ||||
3830 | |||||
3831 | NPY_END_THREADSdo { if (_save) { PyEval_RestoreThread(_save); _save = ((void *)0);} } while (0);; | ||||
3832 | } | ||||
3833 | else if (iter == NULL((void*)0)) { | ||||
3834 | char *dataptr_copy[3]; | ||||
3835 | npy_intp stride_copy[3]; | ||||
3836 | |||||
3837 | int itemsize = op_dtypes[0]->elsize; | ||||
3838 | |||||
3839 | npy_intp stride0_ind = PyArray_STRIDE(op[0], axis); | ||||
3840 | |||||
3841 | /* Execute the loop with no iterators */ | ||||
3842 | npy_intp stride0 = 0, stride1 = PyArray_STRIDE(op[1], axis); | ||||
3843 | |||||
3844 | int needs_api = PyDataType_REFCHK(op_dtypes[0])(((op_dtypes[0])->flags & (0x01)) == (0x01)); | ||||
3845 | |||||
3846 | NPY_UF_DBG_PRINT("UFunc: Reduce loop with no iterators\n"); | ||||
3847 | |||||
3848 | stride_copy[0] = stride0; | ||||
3849 | stride_copy[1] = stride1; | ||||
3850 | stride_copy[2] = stride0; | ||||
3851 | |||||
3852 | if (!needs_api) { | ||||
3853 | NPY_BEGIN_THREADSdo {_save = PyEval_SaveThread();} while (0);; | ||||
3854 | } | ||||
3855 | |||||
3856 | for (i = 0; i < ind_size; ++i) { | ||||
3857 | npy_intp start = reduceat_ind[i], | ||||
3858 | end = (i == ind_size-1) ? PyArray_DIM(arr,axis) : | ||||
3859 | reduceat_ind[i+1]; | ||||
3860 | npy_intp count = end - start; | ||||
3861 | |||||
3862 | dataptr_copy[0] = PyArray_BYTES(op[0]) + stride0_ind*i; | ||||
3863 | dataptr_copy[1] = PyArray_BYTES(op[1]) + stride1*start; | ||||
3864 | dataptr_copy[2] = PyArray_BYTES(op[0]) + stride0_ind*i; | ||||
3865 | |||||
3866 | /* | ||||
3867 | * Copy the first element to start the reduction. | ||||
3868 | * | ||||
3869 | * Output (dataptr[0]) and input (dataptr[1]) may point to | ||||
3870 | * the same memory, e.g. | ||||
3871 | * np.add.reduceat(a, np.arange(len(a)), out=a). | ||||
3872 | */ | ||||
3873 | if (otype == NPY_OBJECT) { | ||||
3874 | /* | ||||
3875 | * Incref before decref to avoid the possibility of the | ||||
3876 | * reference count being zero temporarily. | ||||
3877 | */ | ||||
3878 | Py_XINCREF(*(PyObject **)dataptr_copy[1])_Py_XINCREF(((PyObject*)(*(PyObject **)dataptr_copy[1]))); | ||||
3879 | Py_XDECREF(*(PyObject **)dataptr_copy[0])_Py_XDECREF(((PyObject*)(*(PyObject **)dataptr_copy[0]))); | ||||
3880 | *(PyObject **)dataptr_copy[0] = | ||||
3881 | *(PyObject **)dataptr_copy[1]; | ||||
3882 | } | ||||
3883 | else { | ||||
3884 | memmove(dataptr_copy[0], dataptr_copy[1], itemsize); | ||||
3885 | } | ||||
3886 | |||||
3887 | if (count > 1) { | ||||
3888 | /* Inner loop like REDUCE */ | ||||
3889 | --count; | ||||
3890 | dataptr_copy[1] += stride1; | ||||
3891 | NPY_UF_DBG_PRINT1("iterator loop count %d\n", | ||||
3892 | (int)count); | ||||
3893 | innerloop(dataptr_copy, &count, | ||||
3894 | stride_copy, innerloopdata); | ||||
3895 | } | ||||
3896 | } | ||||
3897 | |||||
3898 | NPY_END_THREADSdo { if (_save) { PyEval_RestoreThread(_save); _save = ((void *)0);} } while (0);; | ||||
3899 | } | ||||
3900 | |||||
3901 | finish: | ||||
3902 | Py_XDECREF(op_dtypes[0])_Py_XDECREF(((PyObject*)(op_dtypes[0]))); | ||||
3903 | if (!NpyIter_Deallocate(iter)) { | ||||
3904 | Py_DECREF(out)_Py_DECREF(((PyObject*)(out))); | ||||
3905 | return NULL((void*)0); | ||||
3906 | } | ||||
3907 | |||||
3908 | return (PyObject *)out; | ||||
3909 | |||||
3910 | fail: | ||||
3911 | Py_XDECREF(out)_Py_XDECREF(((PyObject*)(out))); | ||||
3912 | Py_XDECREF(op_dtypes[0])_Py_XDECREF(((PyObject*)(op_dtypes[0]))); | ||||
3913 | |||||
3914 | NpyIter_Deallocate(iter); | ||||
3915 | return NULL((void*)0); | ||||
3916 | } | ||||
3917 | |||||
3918 | |||||
3919 | static npy_bool | ||||
3920 | tuple_all_none(PyObject *tup) { | ||||
3921 | npy_intp i; | ||||
3922 | for (i = 0; i < PyTuple_GET_SIZE(tup)(((PyVarObject*)((((void) (0)), (PyTupleObject *)(tup))))-> ob_size); ++i) { | ||||
3923 | if (PyTuple_GET_ITEM(tup, i)((((void) (0)), (PyTupleObject *)(tup))->ob_item[i]) != Py_None(&_Py_NoneStruct)) { | ||||
3924 | return NPY_FALSE0; | ||||
3925 | } | ||||
3926 | } | ||||
3927 | return NPY_TRUE1; | ||||
3928 | } | ||||
3929 | |||||
3930 | |||||
3931 | static int | ||||
3932 | _set_full_args_out(int nout, PyObject *out_obj, ufunc_full_args *full_args) | ||||
3933 | { | ||||
3934 | if (PyTuple_CheckExact(out_obj)((((PyObject*)(out_obj))->ob_type) == &PyTuple_Type)) { | ||||
3935 | if (PyTuple_GET_SIZE(out_obj)(((PyVarObject*)((((void) (0)), (PyTupleObject *)(out_obj)))) ->ob_size) != nout) { | ||||
3936 | PyErr_SetString(PyExc_ValueError, | ||||
3937 | "The 'out' tuple must have exactly " | ||||
3938 | "one entry per ufunc output"); | ||||
3939 | return -1; | ||||
3940 | } | ||||
3941 | if (tuple_all_none(out_obj)) { | ||||
3942 | return 0; | ||||
3943 | } | ||||
3944 | else { | ||||
3945 | Py_INCREF(out_obj)_Py_INCREF(((PyObject*)(out_obj))); | ||||
3946 | full_args->out = out_obj; | ||||
3947 | } | ||||
3948 | } | ||||
3949 | else if (nout == 1) { | ||||
3950 | if (out_obj == Py_None(&_Py_NoneStruct)) { | ||||
3951 | return 0; | ||||
3952 | } | ||||
3953 | /* Can be an array if it only has one output */ | ||||
3954 | full_args->out = PyTuple_Pack(1, out_obj); | ||||
3955 | if (full_args->out == NULL((void*)0)) { | ||||
3956 | return -1; | ||||
3957 | } | ||||
3958 | } | ||||
3959 | else { | ||||
3960 | PyErr_SetString(PyExc_TypeError, | ||||
3961 | nout > 1 ? "'out' must be a tuple of arrays" : | ||||
3962 | "'out' must be an array or a tuple with " | ||||
3963 | "a single array"); | ||||
3964 | return -1; | ||||
3965 | } | ||||
3966 | return 0; | ||||
3967 | } | ||||
3968 | |||||
3969 | |||||
3970 | /* | ||||
3971 | * Convert function which replaces np._NoValue with NULL. | ||||
3972 | * As a converter returns 0 on error and 1 on success. | ||||
3973 | */ | ||||
3974 | static int | ||||
3975 | _not_NoValue(PyObject *obj, PyObject **out) | ||||
3976 | { | ||||
3977 | static PyObject *NoValue = NULL((void*)0); | ||||
3978 | npy_cache_import("numpy", "_NoValue", &NoValue); | ||||
3979 | if (NoValue == NULL((void*)0)) { | ||||
3980 | return 0; | ||||
3981 | } | ||||
3982 | if (obj == NoValue) { | ||||
3983 | *out = NULL((void*)0); | ||||
3984 | } | ||||
3985 | else { | ||||
3986 | *out = obj; | ||||
3987 | } | ||||
3988 | return 1; | ||||
3989 | } | ||||
3990 | |||||
3991 | |||||
3992 | /* forward declaration */ | ||||
3993 | static PyArray_DTypeMeta * _get_dtype(PyObject *dtype_obj); | ||||
3994 | |||||
3995 | /* | ||||
3996 | * This code handles reduce, reduceat, and accumulate | ||||
3997 | * (accumulate and reduce are special cases of the more general reduceat | ||||
3998 | * but they are handled separately for speed) | ||||
3999 | */ | ||||
4000 | static PyObject * | ||||
4001 | PyUFunc_GenericReduction(PyUFuncObject *ufunc, | ||||
4002 | PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames, int operation) | ||||
4003 | { | ||||
4004 | int i, naxes=0, ndim; | ||||
4005 | int axes[NPY_MAXDIMS32]; | ||||
4006 | |||||
4007 | ufunc_full_args full_args = {NULL((void*)0), NULL((void*)0)}; | ||||
4008 | PyObject *axes_obj = NULL((void*)0); | ||||
4009 | PyArrayObject *mp = NULL((void*)0), *wheremask = NULL((void*)0), *ret = NULL((void*)0); | ||||
4010 | PyObject *op = NULL((void*)0); | ||||
4011 | PyArrayObject *indices = NULL((void*)0); | ||||
4012 | PyArray_Descr *otype = NULL((void*)0); | ||||
4013 | PyArrayObject *out = NULL((void*)0); | ||||
4014 | int keepdims = 0; | ||||
4015 | PyObject *initial = NULL((void*)0); | ||||
4016 | npy_bool out_is_passed_by_position; | ||||
4017 | |||||
4018 | |||||
4019 | static char *_reduce_type[] = {"reduce", "accumulate", "reduceat", NULL((void*)0)}; | ||||
4020 | |||||
4021 | if (ufunc == NULL((void*)0)) { | ||||
4022 | PyErr_SetString(PyExc_ValueError, "function not supported"); | ||||
4023 | return NULL((void*)0); | ||||
4024 | } | ||||
4025 | if (ufunc->core_enabled) { | ||||
4026 | PyErr_Format(PyExc_RuntimeError, | ||||
4027 | "Reduction not defined on ufunc with signature"); | ||||
4028 | return NULL((void*)0); | ||||
4029 | } | ||||
4030 | if (ufunc->nin != 2) { | ||||
4031 | PyErr_Format(PyExc_ValueError, | ||||
4032 | "%s only supported for binary functions", | ||||
4033 | _reduce_type[operation]); | ||||
4034 | return NULL((void*)0); | ||||
4035 | } | ||||
4036 | if (ufunc->nout != 1) { | ||||
4037 | PyErr_Format(PyExc_ValueError, | ||||
4038 | "%s only supported for functions " | ||||
4039 | "returning a single value", | ||||
4040 | _reduce_type[operation]); | ||||
4041 | return NULL((void*)0); | ||||
4042 | } | ||||
4043 | |||||
4044 | /* | ||||
4045 | * Perform argument parsing, but start by only extracting. This is | ||||
4046 | * just to preserve the behaviour that __array_ufunc__ did not perform | ||||
4047 | * any checks on arguments, and we could change this or change it for | ||||
4048 | * certain parameters. | ||||
4049 | */ | ||||
4050 | PyObject *otype_obj = NULL((void*)0), *out_obj = NULL((void*)0), *indices_obj = NULL((void*)0); | ||||
4051 | PyObject *keepdims_obj = NULL((void*)0), *wheremask_obj = NULL((void*)0); | ||||
4052 | if (operation == UFUNC_REDUCEAT2) { | ||||
4053 | NPY_PREPARE_ARGPARSERstatic _NpyArgParserCache __argparse_cache = {-1}; | ||||
4054 | |||||
4055 | if (npy_parse_arguments("reduceat", args, len_args, kwnames,_npy_parse_arguments("reduceat", &__argparse_cache, args, len_args, kwnames, "array", ((void*)0), &op, "indices", ( (void*)0), &indices_obj, "|axis", ((void*)0), &axes_obj , "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), & out_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4056 | "array", NULL, &op,_npy_parse_arguments("reduceat", &__argparse_cache, args, len_args, kwnames, "array", ((void*)0), &op, "indices", ( (void*)0), &indices_obj, "|axis", ((void*)0), &axes_obj , "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), & out_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4057 | "indices", NULL, &indices_obj,_npy_parse_arguments("reduceat", &__argparse_cache, args, len_args, kwnames, "array", ((void*)0), &op, "indices", ( (void*)0), &indices_obj, "|axis", ((void*)0), &axes_obj , "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), & out_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4058 | "|axis", NULL, &axes_obj,_npy_parse_arguments("reduceat", &__argparse_cache, args, len_args, kwnames, "array", ((void*)0), &op, "indices", ( (void*)0), &indices_obj, "|axis", ((void*)0), &axes_obj , "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), & out_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4059 | "|dtype", NULL, &otype_obj,_npy_parse_arguments("reduceat", &__argparse_cache, args, len_args, kwnames, "array", ((void*)0), &op, "indices", ( (void*)0), &indices_obj, "|axis", ((void*)0), &axes_obj , "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), & out_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4060 | "|out", NULL, &out_obj,_npy_parse_arguments("reduceat", &__argparse_cache, args, len_args, kwnames, "array", ((void*)0), &op, "indices", ( (void*)0), &indices_obj, "|axis", ((void*)0), &axes_obj , "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), & out_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4061 | NULL, NULL, NULL)_npy_parse_arguments("reduceat", &__argparse_cache, args, len_args, kwnames, "array", ((void*)0), &op, "indices", ( (void*)0), &indices_obj, "|axis", ((void*)0), &axes_obj , "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), & out_obj, ((void*)0), ((void*)0), ((void*)0)) < 0) { | ||||
4062 | goto fail; | ||||
4063 | } | ||||
4064 | /* Prepare inputs for PyUfunc_CheckOverride */ | ||||
4065 | full_args.in = PyTuple_Pack(2, op, indices_obj); | ||||
4066 | if (full_args.in == NULL((void*)0)) { | ||||
4067 | goto fail; | ||||
4068 | } | ||||
4069 | out_is_passed_by_position = len_args >= 5; | ||||
4070 | } | ||||
4071 | else if (operation == UFUNC_ACCUMULATE1) { | ||||
4072 | NPY_PREPARE_ARGPARSERstatic _NpyArgParserCache __argparse_cache = {-1}; | ||||
4073 | |||||
4074 | if (npy_parse_arguments("accumulate", args, len_args, kwnames,_npy_parse_arguments("accumulate", &__argparse_cache, args , len_args, kwnames, "array", ((void*)0), &op, "|axis", ( (void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj , "|out", ((void*)0), &out_obj, ((void*)0), ((void*)0), ( (void*)0)) | ||||
4075 | "array", NULL, &op,_npy_parse_arguments("accumulate", &__argparse_cache, args , len_args, kwnames, "array", ((void*)0), &op, "|axis", ( (void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj , "|out", ((void*)0), &out_obj, ((void*)0), ((void*)0), ( (void*)0)) | ||||
4076 | "|axis", NULL, &axes_obj,_npy_parse_arguments("accumulate", &__argparse_cache, args , len_args, kwnames, "array", ((void*)0), &op, "|axis", ( (void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj , "|out", ((void*)0), &out_obj, ((void*)0), ((void*)0), ( (void*)0)) | ||||
4077 | "|dtype", NULL, &otype_obj,_npy_parse_arguments("accumulate", &__argparse_cache, args , len_args, kwnames, "array", ((void*)0), &op, "|axis", ( (void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj , "|out", ((void*)0), &out_obj, ((void*)0), ((void*)0), ( (void*)0)) | ||||
4078 | "|out", NULL, &out_obj,_npy_parse_arguments("accumulate", &__argparse_cache, args , len_args, kwnames, "array", ((void*)0), &op, "|axis", ( (void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj , "|out", ((void*)0), &out_obj, ((void*)0), ((void*)0), ( (void*)0)) | ||||
4079 | NULL, NULL, NULL)_npy_parse_arguments("accumulate", &__argparse_cache, args , len_args, kwnames, "array", ((void*)0), &op, "|axis", ( (void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj , "|out", ((void*)0), &out_obj, ((void*)0), ((void*)0), ( (void*)0)) < 0) { | ||||
4080 | goto fail; | ||||
4081 | } | ||||
4082 | /* Prepare input for PyUfunc_CheckOverride */ | ||||
4083 | full_args.in = PyTuple_Pack(1, op); | ||||
4084 | if (full_args.in == NULL((void*)0)) { | ||||
4085 | goto fail; | ||||
4086 | } | ||||
4087 | out_is_passed_by_position = len_args >= 4; | ||||
4088 | } | ||||
4089 | else { | ||||
4090 | NPY_PREPARE_ARGPARSERstatic _NpyArgParserCache __argparse_cache = {-1}; | ||||
4091 | |||||
4092 | if (npy_parse_arguments("reduce", args, len_args, kwnames,_npy_parse_arguments("reduce", &__argparse_cache, args, len_args , kwnames, "array", ((void*)0), &op, "|axis", ((void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), &out_obj, "|keepdims", ((void*)0), &keepdims_obj , "|initial", &_not_NoValue, &initial, "|where", ((void *)0), &wheremask_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4093 | "array", NULL, &op,_npy_parse_arguments("reduce", &__argparse_cache, args, len_args , kwnames, "array", ((void*)0), &op, "|axis", ((void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), &out_obj, "|keepdims", ((void*)0), &keepdims_obj , "|initial", &_not_NoValue, &initial, "|where", ((void *)0), &wheremask_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4094 | "|axis", NULL, &axes_obj,_npy_parse_arguments("reduce", &__argparse_cache, args, len_args , kwnames, "array", ((void*)0), &op, "|axis", ((void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), &out_obj, "|keepdims", ((void*)0), &keepdims_obj , "|initial", &_not_NoValue, &initial, "|where", ((void *)0), &wheremask_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4095 | "|dtype", NULL, &otype_obj,_npy_parse_arguments("reduce", &__argparse_cache, args, len_args , kwnames, "array", ((void*)0), &op, "|axis", ((void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), &out_obj, "|keepdims", ((void*)0), &keepdims_obj , "|initial", &_not_NoValue, &initial, "|where", ((void *)0), &wheremask_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4096 | "|out", NULL, &out_obj,_npy_parse_arguments("reduce", &__argparse_cache, args, len_args , kwnames, "array", ((void*)0), &op, "|axis", ((void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), &out_obj, "|keepdims", ((void*)0), &keepdims_obj , "|initial", &_not_NoValue, &initial, "|where", ((void *)0), &wheremask_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4097 | "|keepdims", NULL, &keepdims_obj,_npy_parse_arguments("reduce", &__argparse_cache, args, len_args , kwnames, "array", ((void*)0), &op, "|axis", ((void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), &out_obj, "|keepdims", ((void*)0), &keepdims_obj , "|initial", &_not_NoValue, &initial, "|where", ((void *)0), &wheremask_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4098 | "|initial", &_not_NoValue, &initial,_npy_parse_arguments("reduce", &__argparse_cache, args, len_args , kwnames, "array", ((void*)0), &op, "|axis", ((void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), &out_obj, "|keepdims", ((void*)0), &keepdims_obj , "|initial", &_not_NoValue, &initial, "|where", ((void *)0), &wheremask_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4099 | "|where", NULL, &wheremask_obj,_npy_parse_arguments("reduce", &__argparse_cache, args, len_args , kwnames, "array", ((void*)0), &op, "|axis", ((void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), &out_obj, "|keepdims", ((void*)0), &keepdims_obj , "|initial", &_not_NoValue, &initial, "|where", ((void *)0), &wheremask_obj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4100 | NULL, NULL, NULL)_npy_parse_arguments("reduce", &__argparse_cache, args, len_args , kwnames, "array", ((void*)0), &op, "|axis", ((void*)0), &axes_obj, "|dtype", ((void*)0), &otype_obj, "|out", ((void*)0), &out_obj, "|keepdims", ((void*)0), &keepdims_obj , "|initial", &_not_NoValue, &initial, "|where", ((void *)0), &wheremask_obj, ((void*)0), ((void*)0), ((void*)0)) < 0) { | ||||
4101 | goto fail; | ||||
4102 | } | ||||
4103 | /* Prepare input for PyUfunc_CheckOverride */ | ||||
4104 | full_args.in = PyTuple_Pack(1, op); | ||||
4105 | if (full_args.in == NULL((void*)0)) { | ||||
4106 | goto fail; | ||||
4107 | } | ||||
4108 | out_is_passed_by_position = len_args >= 4; | ||||
4109 | } | ||||
4110 | |||||
4111 | /* Normalize output for PyUFunc_CheckOverride and conversion. */ | ||||
4112 | if (out_is_passed_by_position) { | ||||
4113 | /* in this branch, out is always wrapped in a tuple. */ | ||||
4114 | if (out_obj != Py_None(&_Py_NoneStruct)) { | ||||
4115 | full_args.out = PyTuple_Pack(1, out_obj); | ||||
4116 | if (full_args.out == NULL((void*)0)) { | ||||
4117 | goto fail; | ||||
4118 | } | ||||
4119 | } | ||||
4120 | } | ||||
4121 | else if (out_obj) { | ||||
4122 | if (_set_full_args_out(1, out_obj, &full_args) < 0) { | ||||
4123 | goto fail; | ||||
4124 | } | ||||
4125 | /* Ensure that out_obj is the array, not the tuple: */ | ||||
4126 | if (full_args.out != NULL((void*)0)) { | ||||
4127 | out_obj = PyTuple_GET_ITEM(full_args.out, 0)((((void) (0)), (PyTupleObject *)(full_args.out))->ob_item [0]); | ||||
4128 | } | ||||
4129 | } | ||||
4130 | |||||
4131 | /* We now have all the information required to check for Overrides */ | ||||
4132 | PyObject *override = NULL((void*)0); | ||||
4133 | int errval = PyUFunc_CheckOverride(ufunc, _reduce_type[operation], | ||||
4134 | full_args.in, full_args.out, args, len_args, kwnames, &override); | ||||
4135 | if (errval) { | ||||
4136 | return NULL((void*)0); | ||||
4137 | } | ||||
4138 | else if (override) { | ||||
4139 | Py_XDECREF(full_args.in)_Py_XDECREF(((PyObject*)(full_args.in))); | ||||
4140 | Py_XDECREF(full_args.out)_Py_XDECREF(((PyObject*)(full_args.out))); | ||||
4141 | return override; | ||||
4142 | } | ||||
4143 | |||||
4144 | /* Finish parsing of all parameters (no matter which reduce-like) */ | ||||
4145 | if (indices_obj) { | ||||
4146 | PyArray_Descr *indtype = PyArray_DescrFromType(NPY_INTPNPY_LONG); | ||||
4147 | |||||
4148 | indices = (PyArrayObject *)PyArray_FromAny(indices_obj, | ||||
4149 | indtype, 1, 1, NPY_ARRAY_CARRAY(0x0001 | (0x0100 | 0x0400)), NULL((void*)0)); | ||||
4150 | if (indices == NULL((void*)0)) { | ||||
4151 | goto fail; | ||||
4152 | } | ||||
4153 | } | ||||
4154 | if (otype_obj && otype_obj != Py_None(&_Py_NoneStruct)) { | ||||
4155 | /* Use `_get_dtype` because `dtype` is a DType and not the instance */ | ||||
4156 | PyArray_DTypeMeta *dtype = _get_dtype(otype_obj); | ||||
4157 | if (dtype == NULL((void*)0)) { | ||||
4158 | goto fail; | ||||
4159 | } | ||||
4160 | Py_INCREF(dtype->singleton)_Py_INCREF(((PyObject*)(dtype->singleton))); | ||||
4161 | otype = dtype->singleton; | ||||
4162 | } | ||||
4163 | if (out_obj && !PyArray_OutputConverter(out_obj, &out)) { | ||||
4164 | goto fail; | ||||
4165 | } | ||||
4166 | if (keepdims_obj && !PyArray_PythonPyIntFromInt(keepdims_obj, &keepdims)) { | ||||
4167 | goto fail; | ||||
4168 | } | ||||
4169 | if (wheremask_obj && !_wheremask_converter(wheremask_obj, &wheremask)) { | ||||
4170 | goto fail; | ||||
4171 | } | ||||
4172 | |||||
4173 | /* Ensure input is an array */ | ||||
4174 | mp = (PyArrayObject *)PyArray_FromAny(op, NULL((void*)0), 0, 0, 0, NULL((void*)0)); | ||||
4175 | if (mp == NULL((void*)0)) { | ||||
4176 | goto fail; | ||||
4177 | } | ||||
4178 | |||||
4179 | ndim = PyArray_NDIM(mp); | ||||
4180 | |||||
4181 | /* Check to see that type (and otype) is not FLEXIBLE */ | ||||
4182 | if (PyArray_ISFLEXIBLE(mp)(((PyArray_TYPE(mp)) >=NPY_STRING) && ((PyArray_TYPE (mp)) <=NPY_VOID)) || | ||||
4183 | (otype && PyTypeNum_ISFLEXIBLE(otype->type_num)(((otype->type_num) >=NPY_STRING) && ((otype-> type_num) <=NPY_VOID)))) { | ||||
4184 | PyErr_Format(PyExc_TypeError, | ||||
4185 | "cannot perform %s with flexible type", | ||||
4186 | _reduce_type[operation]); | ||||
4187 | goto fail; | ||||
4188 | } | ||||
4189 | |||||
4190 | /* Convert the 'axis' parameter into a list of axes */ | ||||
4191 | if (axes_obj == NULL((void*)0)) { | ||||
4192 | /* apply defaults */ | ||||
4193 | if (ndim == 0) { | ||||
4194 | naxes = 0; | ||||
4195 | } | ||||
4196 | else { | ||||
4197 | naxes = 1; | ||||
4198 | axes[0] = 0; | ||||
4199 | } | ||||
4200 | } | ||||
4201 | else if (axes_obj == Py_None(&_Py_NoneStruct)) { | ||||
4202 | /* Convert 'None' into all the axes */ | ||||
4203 | naxes = ndim; | ||||
4204 | for (i = 0; i < naxes; ++i) { | ||||
4205 | axes[i] = i; | ||||
4206 | } | ||||
4207 | } | ||||
4208 | else if (PyTuple_Check(axes_obj)((((((PyObject*)(axes_obj))->ob_type))->tp_flags & ( (1UL << 26))) != 0)) { | ||||
4209 | naxes = PyTuple_Size(axes_obj); | ||||
4210 | if (naxes < 0 || naxes > NPY_MAXDIMS32) { | ||||
4211 | PyErr_SetString(PyExc_ValueError, | ||||
4212 | "too many values for 'axis'"); | ||||
4213 | goto fail; | ||||
4214 | } | ||||
4215 | for (i = 0; i < naxes; ++i) { | ||||
4216 | PyObject *tmp = PyTuple_GET_ITEM(axes_obj, i)((((void) (0)), (PyTupleObject *)(axes_obj))->ob_item[i]); | ||||
4217 | int axis = PyArray_PyIntAsInt(tmp); | ||||
4218 | if (error_converting(axis)(((axis) == -1) && PyErr_Occurred())) { | ||||
4219 | goto fail; | ||||
4220 | } | ||||
4221 | if (check_and_adjust_axis(&axis, ndim) < 0) { | ||||
4222 | goto fail; | ||||
4223 | } | ||||
4224 | axes[i] = (int)axis; | ||||
4225 | } | ||||
4226 | } | ||||
4227 | else { | ||||
4228 | /* Try to interpret axis as an integer */ | ||||
4229 | int axis = PyArray_PyIntAsInt(axes_obj); | ||||
4230 | /* TODO: PyNumber_Index would be good to use here */ | ||||
4231 | if (error_converting(axis)(((axis) == -1) && PyErr_Occurred())) { | ||||
4232 | goto fail; | ||||
4233 | } | ||||
4234 | /* | ||||
4235 | * As a special case for backwards compatibility in 'sum', | ||||
4236 | * 'prod', et al, also allow a reduction for scalars even | ||||
4237 | * though this is technically incorrect. | ||||
4238 | */ | ||||
4239 | if (ndim == 0 && (axis == 0 || axis == -1)) { | ||||
4240 | naxes = 0; | ||||
4241 | } | ||||
4242 | else if (check_and_adjust_axis(&axis, ndim) < 0) { | ||||
4243 | goto fail; | ||||
4244 | } | ||||
4245 | else { | ||||
4246 | axes[0] = (int)axis; | ||||
4247 | naxes = 1; | ||||
4248 | } | ||||
4249 | } | ||||
4250 | |||||
4251 | /* | ||||
4252 | * If out is specified it determines otype | ||||
4253 | * unless otype already specified. | ||||
4254 | */ | ||||
4255 | if (otype == NULL((void*)0) && out != NULL((void*)0)) { | ||||
4256 | otype = PyArray_DESCR(out); | ||||
4257 | Py_INCREF(otype)_Py_INCREF(((PyObject*)(otype))); | ||||
4258 | } | ||||
4259 | if (otype == NULL((void*)0)) { | ||||
4260 | /* | ||||
4261 | * For integer types --- make sure at least a long | ||||
4262 | * is used for add and multiply reduction to avoid overflow | ||||
4263 | */ | ||||
4264 | int typenum = PyArray_TYPE(mp); | ||||
4265 | if ((PyTypeNum_ISBOOL(typenum)((typenum) == NPY_BOOL) || PyTypeNum_ISINTEGER(typenum)(((typenum) >= NPY_BYTE) && ((typenum) <= NPY_ULONGLONG ))) | ||||
4266 | && ((strcmp(ufunc->name,"add") == 0) | ||||
4267 | || (strcmp(ufunc->name,"multiply") == 0))) { | ||||
4268 | if (PyTypeNum_ISBOOL(typenum)((typenum) == NPY_BOOL)) { | ||||
4269 | typenum = NPY_LONG; | ||||
4270 | } | ||||
4271 | else if ((size_t)PyArray_DESCR(mp)->elsize < sizeof(long)) { | ||||
4272 | if (PyTypeNum_ISUNSIGNED(typenum)(((typenum) == NPY_UBYTE) || ((typenum) == NPY_USHORT) || ((typenum ) == NPY_UINT) || ((typenum) == NPY_ULONG) || ((typenum) == NPY_ULONGLONG ))) { | ||||
4273 | typenum = NPY_ULONG; | ||||
4274 | } | ||||
4275 | else { | ||||
4276 | typenum = NPY_LONG; | ||||
4277 | } | ||||
4278 | } | ||||
4279 | } | ||||
4280 | otype = PyArray_DescrFromType(typenum); | ||||
4281 | } | ||||
4282 | |||||
4283 | |||||
4284 | switch(operation) { | ||||
4285 | case UFUNC_REDUCE0: | ||||
4286 | ret = PyUFunc_Reduce(ufunc, mp, out, naxes, axes, | ||||
4287 | otype, keepdims, initial, wheremask); | ||||
4288 | Py_XDECREF(wheremask)_Py_XDECREF(((PyObject*)(wheremask))); | ||||
4289 | break; | ||||
4290 | case UFUNC_ACCUMULATE1: | ||||
4291 | if (ndim == 0) { | ||||
4292 | PyErr_SetString(PyExc_TypeError, "cannot accumulate on a scalar"); | ||||
4293 | goto fail; | ||||
4294 | } | ||||
4295 | if (naxes != 1) { | ||||
4296 | PyErr_SetString(PyExc_ValueError, | ||||
4297 | "accumulate does not allow multiple axes"); | ||||
4298 | goto fail; | ||||
4299 | } | ||||
4300 | ret = (PyArrayObject *)PyUFunc_Accumulate(ufunc, mp, out, axes[0], | ||||
4301 | otype->type_num); | ||||
4302 | break; | ||||
4303 | case UFUNC_REDUCEAT2: | ||||
4304 | if (ndim == 0) { | ||||
4305 | PyErr_SetString(PyExc_TypeError, "cannot reduceat on a scalar"); | ||||
4306 | goto fail; | ||||
4307 | } | ||||
4308 | if (naxes != 1) { | ||||
4309 | PyErr_SetString(PyExc_ValueError, | ||||
4310 | "reduceat does not allow multiple axes"); | ||||
4311 | goto fail; | ||||
4312 | } | ||||
4313 | ret = (PyArrayObject *)PyUFunc_Reduceat(ufunc, mp, indices, out, | ||||
4314 | axes[0], otype->type_num); | ||||
4315 | Py_DECREF(indices)_Py_DECREF(((PyObject*)(indices))); | ||||
4316 | break; | ||||
4317 | } | ||||
4318 | Py_DECREF(mp)_Py_DECREF(((PyObject*)(mp))); | ||||
4319 | Py_DECREF(otype)_Py_DECREF(((PyObject*)(otype))); | ||||
4320 | Py_XDECREF(full_args.in)_Py_XDECREF(((PyObject*)(full_args.in))); | ||||
4321 | Py_XDECREF(full_args.out)_Py_XDECREF(((PyObject*)(full_args.out))); | ||||
4322 | |||||
4323 | if (ret == NULL((void*)0)) { | ||||
4324 | return NULL((void*)0); | ||||
4325 | } | ||||
4326 | |||||
4327 | /* Wrap and return the output */ | ||||
4328 | { | ||||
4329 | /* Find __array_wrap__ - note that these rules are different to the | ||||
4330 | * normal ufunc path | ||||
4331 | */ | ||||
4332 | PyObject *wrap; | ||||
4333 | if (out != NULL((void*)0)) { | ||||
4334 | wrap = Py_None(&_Py_NoneStruct); | ||||
4335 | Py_INCREF(wrap)_Py_INCREF(((PyObject*)(wrap))); | ||||
4336 | } | ||||
4337 | else if (Py_TYPE(op)(((PyObject*)(op))->ob_type) != Py_TYPE(ret)(((PyObject*)(ret))->ob_type)) { | ||||
4338 | wrap = PyObject_GetAttr(op, npy_um_str_array_wrap); | ||||
4339 | if (wrap == NULL((void*)0)) { | ||||
4340 | PyErr_Clear(); | ||||
4341 | } | ||||
4342 | else if (!PyCallable_Check(wrap)) { | ||||
4343 | Py_DECREF(wrap)_Py_DECREF(((PyObject*)(wrap))); | ||||
4344 | wrap = NULL((void*)0); | ||||
4345 | } | ||||
4346 | } | ||||
4347 | else { | ||||
4348 | wrap = NULL((void*)0); | ||||
4349 | } | ||||
4350 | return _apply_array_wrap(wrap, ret, NULL((void*)0)); | ||||
4351 | } | ||||
4352 | |||||
4353 | fail: | ||||
4354 | Py_XDECREF(otype)_Py_XDECREF(((PyObject*)(otype))); | ||||
4355 | Py_XDECREF(mp)_Py_XDECREF(((PyObject*)(mp))); | ||||
4356 | Py_XDECREF(wheremask)_Py_XDECREF(((PyObject*)(wheremask))); | ||||
4357 | Py_XDECREF(full_args.in)_Py_XDECREF(((PyObject*)(full_args.in))); | ||||
4358 | Py_XDECREF(full_args.out)_Py_XDECREF(((PyObject*)(full_args.out))); | ||||
4359 | return NULL((void*)0); | ||||
4360 | } | ||||
4361 | |||||
4362 | |||||
4363 | /* | ||||
4364 | * Perform a basic check on `dtype`, `sig`, and `signature` since only one | ||||
4365 | * may be set. If `sig` is used, writes it into `out_signature` (which should | ||||
4366 | * be set to `signature_obj` so that following code only requires to handle | ||||
4367 | * `signature_obj`). | ||||
4368 | * | ||||
4369 | * Does NOT incref the output! This only copies the borrowed references | ||||
4370 | * gotten during the argument parsing. | ||||
4371 | * | ||||
4372 | * This function does not do any normalization of the input dtype tuples, | ||||
4373 | * this happens after the array-ufunc override check currently. | ||||
4374 | */ | ||||
4375 | static int | ||||
4376 | _check_and_copy_sig_to_signature( | ||||
4377 | PyObject *sig_obj, PyObject *signature_obj, PyObject *dtype, | ||||
4378 | PyObject **out_signature) | ||||
4379 | { | ||||
4380 | *out_signature = NULL((void*)0); | ||||
4381 | if (signature_obj != NULL((void*)0)) { | ||||
4382 | *out_signature = signature_obj; | ||||
4383 | } | ||||
4384 | |||||
4385 | if (sig_obj != NULL((void*)0)) { | ||||
4386 | if (*out_signature != NULL((void*)0)) { | ||||
4387 | PyErr_SetString(PyExc_TypeError, | ||||
4388 | "cannot specify both 'sig' and 'signature'"); | ||||
4389 | *out_signature = NULL((void*)0); | ||||
4390 | return -1; | ||||
4391 | } | ||||
4392 | *out_signature = sig_obj; | ||||
4393 | } | ||||
4394 | |||||
4395 | if (dtype != NULL((void*)0)) { | ||||
4396 | if (*out_signature != NULL((void*)0)) { | ||||
4397 | PyErr_SetString(PyExc_TypeError, | ||||
4398 | "cannot specify both 'signature' and 'dtype'"); | ||||
4399 | return -1; | ||||
4400 | } | ||||
4401 | /* dtype needs to be converted, delay after the override check */ | ||||
4402 | } | ||||
4403 | return 0; | ||||
4404 | } | ||||
4405 | |||||
4406 | |||||
4407 | /* | ||||
4408 | * Note: This function currently lets DType classes pass, but in general | ||||
4409 | * the class (not the descriptor instance) is the preferred input, so the | ||||
4410 | * parsing should eventually be adapted to prefer classes and possible | ||||
4411 | * deprecated instances. (Users should not notice that much, since `np.float64` | ||||
4412 | * or "float64" usually denotes the DType class rather than the instance.) | ||||
4413 | */ | ||||
4414 | static PyArray_DTypeMeta * | ||||
4415 | _get_dtype(PyObject *dtype_obj) { | ||||
4416 | if (PyObject_TypeCheck(dtype_obj, &PyArrayDTypeMeta_Type)((((PyObject*)(dtype_obj))->ob_type) == (&PyArrayDTypeMeta_Type ) || PyType_IsSubtype((((PyObject*)(dtype_obj))->ob_type), (&PyArrayDTypeMeta_Type)))) { | ||||
4417 | Py_INCREF(dtype_obj)_Py_INCREF(((PyObject*)(dtype_obj))); | ||||
4418 | return (PyArray_DTypeMeta *)dtype_obj; | ||||
4419 | } | ||||
4420 | else { | ||||
4421 | PyArray_Descr *descr = NULL((void*)0); | ||||
4422 | if (!PyArray_DescrConverter(dtype_obj, &descr)) { | ||||
4423 | return NULL((void*)0); | ||||
4424 | } | ||||
4425 | PyArray_DTypeMeta *out = NPY_DTYPE(descr)((PyArray_DTypeMeta *)(((PyObject*)(descr))->ob_type)); | ||||
4426 | if (NPY_UNLIKELY(!out->legacy)__builtin_expect(!!(!out->legacy), 0)) { | ||||
4427 | /* TODO: this path was unreachable when added. */ | ||||
4428 | PyErr_SetString(PyExc_TypeError, | ||||
4429 | "Cannot pass a new user DType instance to the `dtype` or " | ||||
4430 | "`signature` arguments of ufuncs. Pass the DType class " | ||||
4431 | "instead."); | ||||
4432 | Py_DECREF(descr)_Py_DECREF(((PyObject*)(descr))); | ||||
4433 | return NULL((void*)0); | ||||
4434 | } | ||||
4435 | else if (NPY_UNLIKELY(out->singleton != descr)__builtin_expect(!!(out->singleton != descr), 0)) { | ||||
4436 | /* This does not warn about `metadata`, but units is important. */ | ||||
4437 | if (!PyArray_EquivTypes(out->singleton, descr)) { | ||||
4438 | PyErr_Format(PyExc_TypeError, | ||||
4439 | "The `dtype` and `signature` arguments to " | ||||
4440 | "ufuncs only select the general DType and not details " | ||||
4441 | "such as the byte order or time unit (with rare " | ||||
4442 | "exceptions see release notes). To avoid this warning " | ||||
4443 | "please use the scalar types `np.float64`, or string " | ||||
4444 | "notation.\n" | ||||
4445 | "In rare cases where the time unit was preserved, " | ||||
4446 | "either cast the inputs or provide an output array. " | ||||
4447 | "In the future NumPy may transition to allow providing " | ||||
4448 | "`dtype=` to denote the outputs `dtype` as well"); | ||||
4449 | Py_DECREF(descr)_Py_DECREF(((PyObject*)(descr))); | ||||
4450 | return NULL((void*)0); | ||||
4451 | } | ||||
4452 | } | ||||
4453 | Py_INCREF(out)_Py_INCREF(((PyObject*)(out))); | ||||
4454 | Py_DECREF(descr)_Py_DECREF(((PyObject*)(descr))); | ||||
4455 | return out; | ||||
4456 | } | ||||
4457 | } | ||||
4458 | |||||
4459 | |||||
4460 | static int | ||||
4461 | _make_new_typetup( | ||||
4462 | int nop, PyArray_DTypeMeta *signature[], PyObject **out_typetup) { | ||||
4463 | *out_typetup = PyTuple_New(nop); | ||||
4464 | if (*out_typetup == NULL((void*)0)) { | ||||
4465 | return -1; | ||||
4466 | } | ||||
4467 | |||||
4468 | int noncount = 0; | ||||
4469 | for (int i = 0; i < nop; i++) { | ||||
4470 | PyObject *item; | ||||
4471 | if (signature[i] == NULL((void*)0)) { | ||||
4472 | item = Py_None(&_Py_NoneStruct); | ||||
4473 | noncount++; | ||||
4474 | } | ||||
4475 | else { | ||||
4476 | if (!signature[i]->legacy || signature[i]->abstract) { | ||||
4477 | /* | ||||
4478 | * The legacy type resolution can't deal with these. | ||||
4479 | * This path will return `None` or so in the future to | ||||
4480 | * set an error later if the legacy type resolution is used. | ||||
4481 | */ | ||||
4482 | PyErr_SetString(PyExc_RuntimeError, | ||||
4483 | "Internal NumPy error: new DType in signature not yet " | ||||
4484 | "supported. (This should be unreachable code!)"); | ||||
4485 | Py_SETREF(*out_typetup, NULL)do { PyObject *_py_tmp = ((PyObject*)(*out_typetup)); (*out_typetup ) = (((void*)0)); _Py_DECREF(((PyObject*)(_py_tmp))); } while (0); | ||||
4486 | return -1; | ||||
4487 | } | ||||
4488 | item = (PyObject *)signature[i]->singleton; | ||||
4489 | } | ||||
4490 | Py_INCREF(item)_Py_INCREF(((PyObject*)(item))); | ||||
4491 | PyTuple_SET_ITEM(*out_typetup, i, item)PyTuple_SetItem(*out_typetup, i, item); | ||||
4492 | } | ||||
4493 | if (noncount == nop) { | ||||
4494 | /* The whole signature was None, simply ignore type tuple */ | ||||
4495 | Py_DECREF(*out_typetup)_Py_DECREF(((PyObject*)(*out_typetup))); | ||||
4496 | *out_typetup = NULL((void*)0); | ||||
4497 | } | ||||
4498 | return 0; | ||||
4499 | } | ||||
4500 | |||||
4501 | |||||
4502 | /* | ||||
4503 | * Finish conversion parsing of the type tuple. NumPy always only honored | ||||
4504 | * the type number for passed in descriptors/dtypes. | ||||
4505 | * The `dtype` argument is interpreted as the first output DType (not | ||||
4506 | * descriptor). | ||||
4507 | * Unlike the dtype of an `out` array, it influences loop selection! | ||||
4508 | * | ||||
4509 | * NOTE: This function replaces the type tuple if passed in (it steals | ||||
4510 | * the original reference and returns a new object and reference)! | ||||
4511 | * The caller must XDECREF the type tuple both on error or success. | ||||
4512 | * | ||||
4513 | * The function returns a new, normalized type-tuple. | ||||
4514 | */ | ||||
4515 | static int | ||||
4516 | _get_normalized_typetup(PyUFuncObject *ufunc, | ||||
4517 | PyObject *dtype_obj, PyObject *signature_obj, PyObject **out_typetup) | ||||
4518 | { | ||||
4519 | if (dtype_obj == NULL((void*)0) && signature_obj == NULL((void*)0)) { | ||||
4520 | return 0; | ||||
4521 | } | ||||
4522 | |||||
4523 | int res = -1; | ||||
4524 | int nin = ufunc->nin, nout = ufunc->nout, nop = nin + nout; | ||||
4525 | /* | ||||
4526 | * TODO: `signature` will be the main result in the future and | ||||
4527 | * not the typetup. (Type tuple construction can be deffered to when | ||||
4528 | * the legacy fallback is used). | ||||
4529 | */ | ||||
4530 | PyArray_DTypeMeta *signature[NPY_MAXARGS32]; | ||||
4531 | memset(signature, '\0', sizeof(*signature) * nop); | ||||
4532 | |||||
4533 | if (dtype_obj != NULL((void*)0)) { | ||||
4534 | if (dtype_obj == Py_None(&_Py_NoneStruct)) { | ||||
4535 | /* If `dtype=None` is passed, no need to do anything */ | ||||
4536 | assert(*out_typetup == NULL)((void) (0)); | ||||
4537 | return 0; | ||||
4538 | } | ||||
4539 | if (nout == 0) { | ||||
4540 | /* This may be allowed (NumPy does not do this)? */ | ||||
4541 | PyErr_SetString(PyExc_TypeError, | ||||
4542 | "Cannot provide `dtype` when a ufunc has no outputs"); | ||||
4543 | return -1; | ||||
4544 | } | ||||
4545 | PyArray_DTypeMeta *dtype = _get_dtype(dtype_obj); | ||||
4546 | if (dtype == NULL((void*)0)) { | ||||
4547 | return -1; | ||||
4548 | } | ||||
4549 | for (int i = nin; i < nop; i++) { | ||||
4550 | Py_INCREF(dtype)_Py_INCREF(((PyObject*)(dtype))); | ||||
4551 | signature[i] = dtype; | ||||
4552 | } | ||||
4553 | Py_DECREF(dtype)_Py_DECREF(((PyObject*)(dtype))); | ||||
4554 | res = _make_new_typetup(nop, signature, out_typetup); | ||||
4555 | goto finish; | ||||
4556 | } | ||||
4557 | |||||
4558 | assert(signature_obj != NULL)((void) (0)); | ||||
4559 | /* Fill in specified_types from the tuple or string (signature_obj) */ | ||||
4560 | if (PyTuple_Check(signature_obj)((((((PyObject*)(signature_obj))->ob_type))->tp_flags & ((1UL << 26))) != 0)) { | ||||
4561 | Py_ssize_t n = PyTuple_GET_SIZE(signature_obj)(((PyVarObject*)((((void) (0)), (PyTupleObject *)(signature_obj ))))->ob_size); | ||||
4562 | if (n == 1 && nop != 1) { | ||||
4563 | /* | ||||
4564 | * Special handling, because we deprecate this path. The path | ||||
4565 | * probably mainly existed since the `dtype=obj` was passed through | ||||
4566 | * as `(obj,)` and parsed later. | ||||
4567 | */ | ||||
4568 | if (PyTuple_GET_ITEM(signature_obj, 0)((((void) (0)), (PyTupleObject *)(signature_obj))->ob_item [0]) == Py_None(&_Py_NoneStruct)) { | ||||
4569 | PyErr_SetString(PyExc_TypeError, | ||||
4570 | "a single item type tuple cannot contain None."); | ||||
4571 | goto finish; | ||||
4572 | } | ||||
4573 | if (DEPRECATE("The use of a length 1 tuple for the ufunc "PyErr_WarnEx(PyExc_DeprecationWarning,"The use of a length 1 tuple for the ufunc " "`signature` is deprecated. Use `dtype` or fill the" "tuple with `None`s." ,1) | ||||
4574 | "`signature` is deprecated. Use `dtype` or fill the"PyErr_WarnEx(PyExc_DeprecationWarning,"The use of a length 1 tuple for the ufunc " "`signature` is deprecated. Use `dtype` or fill the" "tuple with `None`s." ,1) | ||||
4575 | "tuple with `None`s.")PyErr_WarnEx(PyExc_DeprecationWarning,"The use of a length 1 tuple for the ufunc " "`signature` is deprecated. Use `dtype` or fill the" "tuple with `None`s." ,1) < 0) { | ||||
4576 | goto finish; | ||||
4577 | } | ||||
4578 | /* Use the same logic as for `dtype=` */ | ||||
4579 | res = _get_normalized_typetup(ufunc, | ||||
4580 | PyTuple_GET_ITEM(signature_obj, 0)((((void) (0)), (PyTupleObject *)(signature_obj))->ob_item [0]), NULL((void*)0), out_typetup); | ||||
4581 | goto finish; | ||||
4582 | } | ||||
4583 | if (n != nop) { | ||||
4584 | PyErr_Format(PyExc_ValueError, | ||||
4585 | "a type-tuple must be specified of length %d for ufunc '%s'", | ||||
4586 | nop, ufunc_get_name_cstr(ufunc)); | ||||
4587 | goto finish; | ||||
4588 | } | ||||
4589 | for (int i = 0; i < nop; ++i) { | ||||
4590 | PyObject *item = PyTuple_GET_ITEM(signature_obj, i)((((void) (0)), (PyTupleObject *)(signature_obj))->ob_item [i]); | ||||
4591 | if (item == Py_None(&_Py_NoneStruct)) { | ||||
4592 | continue; | ||||
4593 | } | ||||
4594 | signature[i] = _get_dtype(item); | ||||
4595 | if (signature[i] == NULL((void*)0)) { | ||||
4596 | goto finish; | ||||
4597 | } | ||||
4598 | } | ||||
4599 | } | ||||
4600 | else if (PyBytes_Check(signature_obj)((((((PyObject*)(signature_obj))->ob_type))->tp_flags & ((1UL << 27))) != 0) || PyUnicode_Check(signature_obj)((((((PyObject*)(signature_obj))->ob_type))->tp_flags & ((1UL << 28))) != 0)) { | ||||
4601 | PyObject *str_object = NULL((void*)0); | ||||
4602 | |||||
4603 | if (PyBytes_Check(signature_obj)((((((PyObject*)(signature_obj))->ob_type))->tp_flags & ((1UL << 27))) != 0)) { | ||||
4604 | str_object = PyUnicode_FromEncodedObject(signature_obj, NULL((void*)0), NULL((void*)0)); | ||||
4605 | if (str_object == NULL((void*)0)) { | ||||
4606 | goto finish; | ||||
4607 | } | ||||
4608 | } | ||||
4609 | else { | ||||
4610 | Py_INCREF(signature_obj)_Py_INCREF(((PyObject*)(signature_obj))); | ||||
4611 | str_object = signature_obj; | ||||
4612 | } | ||||
4613 | |||||
4614 | Py_ssize_t length; | ||||
4615 | const char *str = PyUnicode_AsUTF8AndSize(str_object, &length); | ||||
4616 | if (str == NULL((void*)0)) { | ||||
4617 | Py_DECREF(str_object)_Py_DECREF(((PyObject*)(str_object))); | ||||
4618 | goto finish; | ||||
4619 | } | ||||
4620 | |||||
4621 | if (length != 1 && (length != nin+nout + 2 || | ||||
4622 | str[nin] != '-' || str[nin+1] != '>')) { | ||||
4623 | PyErr_Format(PyExc_ValueError, | ||||
4624 | "a type-string for %s, %d typecode(s) before and %d after " | ||||
4625 | "the -> sign", ufunc_get_name_cstr(ufunc), nin, nout); | ||||
4626 | Py_DECREF(str_object)_Py_DECREF(((PyObject*)(str_object))); | ||||
4627 | goto finish; | ||||
4628 | } | ||||
4629 | if (length == 1 && nin+nout != 1) { | ||||
4630 | Py_DECREF(str_object)_Py_DECREF(((PyObject*)(str_object))); | ||||
4631 | if (DEPRECATE("The use of a length 1 string for the ufunc "PyErr_WarnEx(PyExc_DeprecationWarning,"The use of a length 1 string for the ufunc " "`signature` is deprecated. Use `dtype` attribute or " "pass a tuple with `None`s." ,1) | ||||
4632 | "`signature` is deprecated. Use `dtype` attribute or "PyErr_WarnEx(PyExc_DeprecationWarning,"The use of a length 1 string for the ufunc " "`signature` is deprecated. Use `dtype` attribute or " "pass a tuple with `None`s." ,1) | ||||
4633 | "pass a tuple with `None`s.")PyErr_WarnEx(PyExc_DeprecationWarning,"The use of a length 1 string for the ufunc " "`signature` is deprecated. Use `dtype` attribute or " "pass a tuple with `None`s." ,1) < 0) { | ||||
4634 | goto finish; | ||||
4635 | } | ||||
4636 | /* `signature="l"` is the same as `dtype="l"` */ | ||||
4637 | res = _get_normalized_typetup(ufunc, str_object, NULL((void*)0), out_typetup); | ||||
4638 | goto finish; | ||||
4639 | } | ||||
4640 | else { | ||||
4641 | for (int i = 0; i < nin+nout; ++i) { | ||||
4642 | npy_intp istr = i < nin ? i : i+2; | ||||
4643 | PyArray_Descr *descr = PyArray_DescrFromType(str[istr]); | ||||
4644 | if (descr == NULL((void*)0)) { | ||||
4645 | Py_DECREF(str_object)_Py_DECREF(((PyObject*)(str_object))); | ||||
4646 | goto finish; | ||||
4647 | } | ||||
4648 | signature[i] = NPY_DTYPE(descr)((PyArray_DTypeMeta *)(((PyObject*)(descr))->ob_type)); | ||||
4649 | Py_INCREF(signature[i])_Py_INCREF(((PyObject*)(signature[i]))); | ||||
4650 | Py_DECREF(descr)_Py_DECREF(((PyObject*)(descr))); | ||||
4651 | } | ||||
4652 | Py_DECREF(str_object)_Py_DECREF(((PyObject*)(str_object))); | ||||
4653 | } | ||||
4654 | } | ||||
4655 | else { | ||||
4656 | PyErr_SetString(PyExc_TypeError, | ||||
4657 | "the signature object to ufunc must be a string or a tuple."); | ||||
4658 | goto finish; | ||||
4659 | } | ||||
4660 | res = _make_new_typetup(nop, signature, out_typetup); | ||||
4661 | |||||
4662 | finish: | ||||
4663 | for (int i =0; i < nop; i++) { | ||||
4664 | Py_XDECREF(signature[i])_Py_XDECREF(((PyObject*)(signature[i]))); | ||||
4665 | } | ||||
4666 | return res; | ||||
4667 | } | ||||
4668 | |||||
4669 | |||||
4670 | /* | ||||
4671 | * Main ufunc call implementation. | ||||
4672 | * | ||||
4673 | * This implementation makes use of the "fastcall" way of passing keyword | ||||
4674 | * arguments and is called directly from `ufunc_generic_vectorcall` when | ||||
4675 | * Python has `tp_vectorcall` (Python 3.8+). | ||||
4676 | * If `tp_vectorcall` is not available, the dictionary `kwargs` are unpacked in | ||||
4677 | * `ufunc_generic_call` with fairly little overhead. | ||||
4678 | */ | ||||
4679 | static PyObject * | ||||
4680 | ufunc_generic_fastcall(PyUFuncObject *ufunc, | ||||
4681 | PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames, | ||||
4682 | npy_bool outer) | ||||
4683 | { | ||||
4684 | PyArrayObject *operands[NPY_MAXARGS32] = {NULL((void*)0)}; | ||||
4685 | PyObject *retobj[NPY_MAXARGS32]; | ||||
4686 | PyObject *wraparr[NPY_MAXARGS32]; | ||||
4687 | PyObject *override = NULL((void*)0); | ||||
4688 | ufunc_full_args full_args = {NULL((void*)0), NULL((void*)0)}; | ||||
4689 | PyObject *typetup = NULL((void*)0); | ||||
4690 | |||||
4691 | int errval; | ||||
4692 | int nin = ufunc->nin, nout = ufunc->nout, nop = ufunc->nargs; | ||||
4693 | |||||
4694 | /* | ||||
4695 | * Note that the input (and possibly output) arguments are passed in as | ||||
4696 | * positional arguments. We extract these first and check for `out` | ||||
4697 | * passed by keyword later. | ||||
4698 | * Outputs and inputs are stored in `full_args.in` and `full_args.out` | ||||
4699 | * as tuples (or NULL when no outputs are passed). | ||||
4700 | */ | ||||
4701 | |||||
4702 | /* Check number of arguments */ | ||||
4703 | if ((len_args < nin) || (len_args > nop)) { | ||||
4704 | PyErr_Format(PyExc_TypeError, | ||||
4705 | "%s() takes from %d to %d positional arguments but " | ||||
4706 | "%zd were given", | ||||
4707 | ufunc_get_name_cstr(ufunc) , nin, nop, len_args); | ||||
4708 | return NULL((void*)0); | ||||
4709 | } | ||||
4710 | |||||
4711 | /* Fetch input arguments. */ | ||||
4712 | full_args.in = PyTuple_New(ufunc->nin); | ||||
4713 | if (full_args.in == NULL((void*)0)) { | ||||
4714 | return NULL((void*)0); | ||||
4715 | } | ||||
4716 | for (int i = 0; i < ufunc->nin; i++) { | ||||
4717 | PyObject *tmp = args[i]; | ||||
4718 | Py_INCREF(tmp)_Py_INCREF(((PyObject*)(tmp))); | ||||
4719 | PyTuple_SET_ITEM(full_args.in, i, tmp)PyTuple_SetItem(full_args.in, i, tmp); | ||||
4720 | } | ||||
4721 | |||||
4722 | /* | ||||
4723 | * If there are more arguments, they define the out args. Otherwise | ||||
4724 | * full_args.out is NULL for now, and the `out` kwarg may still be passed. | ||||
4725 | */ | ||||
4726 | npy_bool out_is_passed_by_position = len_args > nin; | ||||
4727 | if (out_is_passed_by_position) { | ||||
4728 | npy_bool all_none = NPY_TRUE1; | ||||
4729 | |||||
4730 | full_args.out = PyTuple_New(nout); | ||||
4731 | if (full_args.out == NULL((void*)0)) { | ||||
4732 | goto fail; | ||||
4733 | } | ||||
4734 | for (int i = nin; i < nop; i++) { | ||||
4735 | PyObject *tmp; | ||||
4736 | if (i < (int)len_args) { | ||||
4737 | tmp = args[i]; | ||||
4738 | if (tmp != Py_None(&_Py_NoneStruct)) { | ||||
4739 | all_none = NPY_FALSE0; | ||||
4740 | } | ||||
4741 | } | ||||
4742 | else { | ||||
4743 | tmp = Py_None(&_Py_NoneStruct); | ||||
4744 | } | ||||
4745 | Py_INCREF(tmp)_Py_INCREF(((PyObject*)(tmp))); | ||||
4746 | PyTuple_SET_ITEM(full_args.out, i-nin, tmp)PyTuple_SetItem(full_args.out, i-nin, tmp); | ||||
4747 | } | ||||
4748 | if (all_none) { | ||||
4749 | Py_SETREF(full_args.out, NULL)do { PyObject *_py_tmp = ((PyObject*)(full_args.out)); (full_args .out) = (((void*)0)); _Py_DECREF(((PyObject*)(_py_tmp))); } while (0); | ||||
4750 | } | ||||
4751 | } | ||||
4752 | else { | ||||
4753 | full_args.out = NULL((void*)0); | ||||
4754 | } | ||||
4755 | |||||
4756 | /* | ||||
4757 | * We have now extracted (but not converted) the input arguments. | ||||
4758 | * To simplify overrides, extract all other arguments (as objects only) | ||||
4759 | */ | ||||
4760 | PyObject *out_obj = NULL((void*)0), *where_obj = NULL((void*)0); | ||||
4761 | PyObject *axes_obj = NULL((void*)0), *axis_obj = NULL((void*)0); | ||||
4762 | PyObject *keepdims_obj = NULL((void*)0), *casting_obj = NULL((void*)0), *order_obj = NULL((void*)0); | ||||
4763 | PyObject *subok_obj = NULL((void*)0), *signature_obj = NULL((void*)0), *sig_obj = NULL((void*)0); | ||||
4764 | PyObject *dtype_obj = NULL((void*)0), *extobj = NULL((void*)0); | ||||
4765 | |||||
4766 | /* Skip parsing if there are no keyword arguments, nothing left to do */ | ||||
4767 | if (kwnames != NULL((void*)0)) { | ||||
4768 | if (!ufunc->core_enabled) { | ||||
4769 | NPY_PREPARE_ARGPARSERstatic _NpyArgParserCache __argparse_cache = {-1}; | ||||
4770 | |||||
4771 | if (npy_parse_arguments(ufunc->name, args + len_args, 0, kwnames,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$where" , ((void*)0), &where_obj, "$casting", ((void*)0), &casting_obj , "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), &dtype_obj, "$signature" , ((void*)0), &signature_obj, "$sig", ((void*)0), &sig_obj , "$extobj", ((void*)0), &extobj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4772 | "$out", NULL, &out_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$where" , ((void*)0), &where_obj, "$casting", ((void*)0), &casting_obj , "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), &dtype_obj, "$signature" , ((void*)0), &signature_obj, "$sig", ((void*)0), &sig_obj , "$extobj", ((void*)0), &extobj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4773 | "$where", NULL, &where_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$where" , ((void*)0), &where_obj, "$casting", ((void*)0), &casting_obj , "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), &dtype_obj, "$signature" , ((void*)0), &signature_obj, "$sig", ((void*)0), &sig_obj , "$extobj", ((void*)0), &extobj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4774 | "$casting", NULL, &casting_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$where" , ((void*)0), &where_obj, "$casting", ((void*)0), &casting_obj , "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), &dtype_obj, "$signature" , ((void*)0), &signature_obj, "$sig", ((void*)0), &sig_obj , "$extobj", ((void*)0), &extobj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4775 | "$order", NULL, &order_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$where" , ((void*)0), &where_obj, "$casting", ((void*)0), &casting_obj , "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), &dtype_obj, "$signature" , ((void*)0), &signature_obj, "$sig", ((void*)0), &sig_obj , "$extobj", ((void*)0), &extobj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4776 | "$subok", NULL, &subok_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$where" , ((void*)0), &where_obj, "$casting", ((void*)0), &casting_obj , "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), &dtype_obj, "$signature" , ((void*)0), &signature_obj, "$sig", ((void*)0), &sig_obj , "$extobj", ((void*)0), &extobj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4777 | "$dtype", NULL, &dtype_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$where" , ((void*)0), &where_obj, "$casting", ((void*)0), &casting_obj , "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), &dtype_obj, "$signature" , ((void*)0), &signature_obj, "$sig", ((void*)0), &sig_obj , "$extobj", ((void*)0), &extobj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4778 | "$signature", NULL, &signature_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$where" , ((void*)0), &where_obj, "$casting", ((void*)0), &casting_obj , "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), &dtype_obj, "$signature" , ((void*)0), &signature_obj, "$sig", ((void*)0), &sig_obj , "$extobj", ((void*)0), &extobj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4779 | "$sig", NULL, &sig_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$where" , ((void*)0), &where_obj, "$casting", ((void*)0), &casting_obj , "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), &dtype_obj, "$signature" , ((void*)0), &signature_obj, "$sig", ((void*)0), &sig_obj , "$extobj", ((void*)0), &extobj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4780 | "$extobj", NULL, &extobj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$where" , ((void*)0), &where_obj, "$casting", ((void*)0), &casting_obj , "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), &dtype_obj, "$signature" , ((void*)0), &signature_obj, "$sig", ((void*)0), &sig_obj , "$extobj", ((void*)0), &extobj, ((void*)0), ((void*)0), ((void*)0)) | ||||
4781 | NULL, NULL, NULL)_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$where" , ((void*)0), &where_obj, "$casting", ((void*)0), &casting_obj , "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), &dtype_obj, "$signature" , ((void*)0), &signature_obj, "$sig", ((void*)0), &sig_obj , "$extobj", ((void*)0), &extobj, ((void*)0), ((void*)0), ((void*)0)) < 0) { | ||||
4782 | goto fail; | ||||
4783 | } | ||||
4784 | } | ||||
4785 | else { | ||||
4786 | NPY_PREPARE_ARGPARSERstatic _NpyArgParserCache __argparse_cache = {-1}; | ||||
4787 | |||||
4788 | if (npy_parse_arguments(ufunc->name, args + len_args, 0, kwnames,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) | ||||
4789 | "$out", NULL, &out_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) | ||||
4790 | "$axes", NULL, &axes_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) | ||||
4791 | "$axis", NULL, &axis_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) | ||||
4792 | "$keepdims", NULL, &keepdims_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) | ||||
4793 | "$casting", NULL, &casting_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) | ||||
4794 | "$order", NULL, &order_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) | ||||
4795 | "$subok", NULL, &subok_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) | ||||
4796 | "$dtype", NULL, &dtype_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) | ||||
4797 | "$signature", NULL, &signature_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) | ||||
4798 | "$sig", NULL, &sig_obj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) | ||||
4799 | "$extobj", NULL, &extobj,_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) | ||||
4800 | NULL, NULL, NULL)_npy_parse_arguments(ufunc->name, &__argparse_cache, args + len_args, 0, kwnames, "$out", ((void*)0), &out_obj, "$axes" , ((void*)0), &axes_obj, "$axis", ((void*)0), &axis_obj , "$keepdims", ((void*)0), &keepdims_obj, "$casting", ((void *)0), &casting_obj, "$order", ((void*)0), &order_obj, "$subok", ((void*)0), &subok_obj, "$dtype", ((void*)0), & dtype_obj, "$signature", ((void*)0), &signature_obj, "$sig" , ((void*)0), &sig_obj, "$extobj", ((void*)0), &extobj , ((void*)0), ((void*)0), ((void*)0)) < 0) { | ||||
4801 | goto fail; | ||||
4802 | } | ||||
4803 | if (NPY_UNLIKELY((axes_obj != NULL) && (axis_obj != NULL))__builtin_expect(!!((axes_obj != ((void*)0)) && (axis_obj != ((void*)0))), 0)) { | ||||
4804 | PyErr_SetString(PyExc_TypeError, | ||||
4805 | "cannot specify both 'axis' and 'axes'"); | ||||
4806 | goto fail; | ||||
4807 | } | ||||
4808 | } | ||||
4809 | |||||
4810 | /* Handle `out` arguments passed by keyword */ | ||||
4811 | if (out_obj != NULL((void*)0)) { | ||||
4812 | if (out_is_passed_by_position) { | ||||
4813 | PyErr_SetString(PyExc_TypeError, | ||||
4814 | "cannot specify 'out' as both a " | ||||
4815 | "positional and keyword argument"); | ||||
4816 | goto fail; | ||||
4817 | } | ||||
4818 | if (_set_full_args_out(nout, out_obj, &full_args) < 0) { | ||||
4819 | goto fail; | ||||
4820 | } | ||||
4821 | } | ||||
4822 | /* | ||||
4823 | * Only one of signature, sig, and dtype should be passed. If `sig` | ||||
4824 | * was passed, this puts it into `signature_obj` instead (these | ||||
4825 | * are borrowed references). | ||||
4826 | */ | ||||
4827 | if (_check_and_copy_sig_to_signature( | ||||
4828 | sig_obj, signature_obj, dtype_obj, &signature_obj) < 0) { | ||||
4829 | goto fail; | ||||
4830 | } | ||||
4831 | } | ||||
4832 | |||||
4833 | char *method; | ||||
4834 | if (!outer) { | ||||
4835 | method = "__call__"; | ||||
4836 | } | ||||
4837 | else { | ||||
4838 | method = "outer"; | ||||
4839 | } | ||||
4840 | /* We now have all the information required to check for Overrides */ | ||||
4841 | errval = PyUFunc_CheckOverride(ufunc, method, | ||||
4842 | full_args.in, full_args.out, | ||||
4843 | args, len_args, kwnames, &override); | ||||
4844 | if (errval) { | ||||
4845 | goto fail; | ||||
4846 | } | ||||
4847 | else if (override) { | ||||
4848 | Py_DECREF(full_args.in)_Py_DECREF(((PyObject*)(full_args.in))); | ||||
4849 | Py_XDECREF(full_args.out)_Py_XDECREF(((PyObject*)(full_args.out))); | ||||
4850 | return override; | ||||
4851 | } | ||||
4852 | |||||
4853 | if (outer) { | ||||
4854 | /* Outer uses special preparation of inputs (expand dims) */ | ||||
4855 | PyObject *new_in = prepare_input_arguments_for_outer(full_args.in, ufunc); | ||||
4856 | if (new_in == NULL((void*)0)) { | ||||
4857 | goto fail; | ||||
4858 | } | ||||
4859 | Py_SETREF(full_args.in, new_in)do { PyObject *_py_tmp = ((PyObject*)(full_args.in)); (full_args .in) = (new_in); _Py_DECREF(((PyObject*)(_py_tmp))); } while ( 0); | ||||
4860 | } | ||||
4861 | |||||
4862 | /* | ||||
4863 | * Parse the passed `dtype` or `signature` into an array containing | ||||
4864 | * PyArray_DTypeMeta and/or None. | ||||
4865 | */ | ||||
4866 | if (_get_normalized_typetup(ufunc, dtype_obj, signature_obj, &typetup) < 0) { | ||||
4867 | goto fail; | ||||
4868 | } | ||||
4869 | |||||
4870 | NPY_ORDER order = NPY_KEEPORDER; | ||||
4871 | NPY_CASTING casting = NPY_DEFAULT_ASSIGN_CASTING; | ||||
4872 | npy_bool subok = NPY_TRUE1; | ||||
4873 | int keepdims = -1; /* We need to know if it was passed */ | ||||
4874 | PyArrayObject *wheremask = NULL((void*)0); | ||||
4875 | if (convert_ufunc_arguments(ufunc, full_args, operands, | ||||
4876 | order_obj, &order, | ||||
4877 | casting_obj, &casting, | ||||
4878 | subok_obj, &subok, | ||||
4879 | where_obj, &wheremask, | ||||
4880 | keepdims_obj, &keepdims) < 0) { | ||||
4881 | goto fail; | ||||
4882 | } | ||||
4883 | |||||
4884 | if (!ufunc->core_enabled) { | ||||
4885 | errval = PyUFunc_GenericFunctionInternal(ufunc, operands, | ||||
4886 | full_args, typetup, extobj, casting, order, subok, | ||||
4887 | wheremask); | ||||
4888 | Py_XDECREF(wheremask)_Py_XDECREF(((PyObject*)(wheremask))); | ||||
4889 | } | ||||
4890 | else { | ||||
4891 | errval = PyUFunc_GeneralizedFunctionInternal(ufunc, operands, | ||||
4892 | full_args, typetup, extobj, casting, order, subok, | ||||
4893 | axis_obj, axes_obj, keepdims); | ||||
4894 | } | ||||
4895 | |||||
4896 | if (errval < 0) { | ||||
4897 | goto fail; | ||||
4898 | } | ||||
4899 | |||||
4900 | /* Free the input references */ | ||||
4901 | for (int i = 0; i < ufunc->nin; i++) { | ||||
4902 | Py_XSETREF(operands[i], NULL)do { PyObject *_py_tmp = ((PyObject*)(operands[i])); (operands [i]) = (((void*)0)); _Py_XDECREF(((PyObject*)(_py_tmp))); } while (0); | ||||
4903 | } | ||||
4904 | |||||
4905 | /* | ||||
4906 | * Use __array_wrap__ on all outputs | ||||
4907 | * if present on one of the input arguments. | ||||
4908 | * If present for multiple inputs: | ||||
4909 | * use __array_wrap__ of input object with largest | ||||
4910 | * __array_priority__ (default = 0.0) | ||||
4911 | * | ||||
4912 | * Exception: we should not wrap outputs for items already | ||||
4913 | * passed in as output-arguments. These items should either | ||||
4914 | * be left unwrapped or wrapped by calling their own __array_wrap__ | ||||
4915 | * routine. | ||||
4916 | * | ||||
4917 | * For each output argument, wrap will be either | ||||
4918 | * NULL --- call PyArray_Return() -- default if no output arguments given | ||||
4919 | * None --- array-object passed in don't call PyArray_Return | ||||
4920 | * method --- the __array_wrap__ method to call. | ||||
4921 | */ | ||||
4922 | _find_array_wrap(full_args, subok, wraparr, ufunc->nin, ufunc->nout); | ||||
4923 | |||||
4924 | /* wrap outputs */ | ||||
4925 | for (int i = 0; i < ufunc->nout; i++) { | ||||
4926 | int j = ufunc->nin+i; | ||||
4927 | _ufunc_context context; | ||||
4928 | PyObject *wrapped; | ||||
4929 | |||||
4930 | context.ufunc = ufunc; | ||||
4931 | context.args = full_args; | ||||
4932 | context.out_i = i; | ||||
4933 | |||||
4934 | wrapped = _apply_array_wrap(wraparr[i], operands[j], &context); | ||||
4935 | operands[j] = NULL((void*)0); /* Prevent fail double-freeing this */ | ||||
4936 | if (wrapped == NULL((void*)0)) { | ||||
4937 | for (int j = 0; j < i; j++) { | ||||
4938 | Py_DECREF(retobj[j])_Py_DECREF(((PyObject*)(retobj[j]))); | ||||
4939 | } | ||||
4940 | goto fail; | ||||
4941 | } | ||||
4942 | |||||
4943 | retobj[i] = wrapped; | ||||
4944 | } | ||||
4945 | |||||
4946 | Py_XDECREF(typetup)_Py_XDECREF(((PyObject*)(typetup))); | ||||
4947 | Py_XDECREF(full_args.in)_Py_XDECREF(((PyObject*)(full_args.in))); | ||||
4948 | Py_XDECREF(full_args.out)_Py_XDECREF(((PyObject*)(full_args.out))); | ||||
4949 | if (ufunc->nout == 1) { | ||||
4950 | return retobj[0]; | ||||
4951 | } | ||||
4952 | else { | ||||
4953 | PyTupleObject *ret; | ||||
4954 | |||||
4955 | ret = (PyTupleObject *)PyTuple_New(ufunc->nout); | ||||
4956 | for (int i = 0; i < ufunc->nout; i++) { | ||||
4957 | PyTuple_SET_ITEM(ret, i, retobj[i])PyTuple_SetItem(ret, i, retobj[i]); | ||||
4958 | } | ||||
4959 | return (PyObject *)ret; | ||||
4960 | } | ||||
4961 | |||||
4962 | fail: | ||||
4963 | Py_XDECREF(typetup)_Py_XDECREF(((PyObject*)(typetup))); | ||||
4964 | Py_XDECREF(full_args.in)_Py_XDECREF(((PyObject*)(full_args.in))); | ||||
4965 | Py_XDECREF(full_args.out)_Py_XDECREF(((PyObject*)(full_args.out))); | ||||
4966 | for (int i = 0; i < ufunc->nargs; i++) { | ||||
4967 | Py_XDECREF(operands[i])_Py_XDECREF(((PyObject*)(operands[i]))); | ||||
4968 | } | ||||
4969 | return NULL((void*)0); | ||||
4970 | } | ||||
4971 | |||||
4972 | |||||
4973 | /* | ||||
4974 | * TODO: The implementation below can be replaced with PyVectorcall_Call | ||||
4975 | * when available (should be Python 3.8+). | ||||
4976 | */ | ||||
4977 | static PyObject * | ||||
4978 | ufunc_generic_call( | ||||
4979 | PyUFuncObject *ufunc, PyObject *args, PyObject *kwds) | ||||
4980 | { | ||||
4981 | Py_ssize_t len_args = PyTuple_GET_SIZE(args)(((PyVarObject*)((((void) (0)), (PyTupleObject *)(args))))-> ob_size); | ||||
4982 | /* | ||||
4983 | * Wrapper for tp_call to tp_fastcall, to support both on older versions | ||||
4984 | * of Python. (and generally simplifying support of both versions in the | ||||
4985 | * same codebase. | ||||
4986 | */ | ||||
4987 | if (kwds == NULL((void*)0)) { | ||||
4988 | return ufunc_generic_fastcall(ufunc, | ||||
4989 | PySequence_Fast_ITEMS(args)(((((((PyObject*)(args))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(args))->ob_item : ((PyTupleObject *)(args))->ob_item), len_args, NULL((void*)0), NPY_FALSE0); | ||||
4990 | } | ||||
4991 | |||||
4992 | PyObject *new_args[NPY_MAXARGS32]; | ||||
4993 | Py_ssize_t len_kwds = PyDict_Size(kwds); | ||||
4994 | |||||
4995 | if (NPY_UNLIKELY(len_args + len_kwds > NPY_MAXARGS)__builtin_expect(!!(len_args + len_kwds > 32), 0)) { | ||||
4996 | /* | ||||
4997 | * We do not have enough scratch-space, so we have to abort; | ||||
4998 | * In practice this error should not be seen by users. | ||||
4999 | */ | ||||
5000 | PyErr_Format(PyExc_ValueError, | ||||
5001 | "%s() takes from %d to %d positional arguments but " | ||||
5002 | "%zd were given", | ||||
5003 | ufunc_get_name_cstr(ufunc) , ufunc->nin, ufunc->nargs, len_args); | ||||
5004 | return NULL((void*)0); | ||||
5005 | } | ||||
5006 | |||||
5007 | /* Copy args into the scratch space */ | ||||
5008 | for (Py_ssize_t i = 0; i < len_args; i++) { | ||||
5009 | new_args[i] = PyTuple_GET_ITEM(args, i)((((void) (0)), (PyTupleObject *)(args))->ob_item[i]); | ||||
5010 | } | ||||
5011 | |||||
5012 | PyObject *kwnames = PyTuple_New(len_kwds); | ||||
5013 | |||||
5014 | PyObject *key, *value; | ||||
5015 | Py_ssize_t pos = 0; | ||||
5016 | Py_ssize_t i = 0; | ||||
5017 | while (PyDict_Next(kwds, &pos, &key, &value)) { | ||||
5018 | Py_INCREF(key)_Py_INCREF(((PyObject*)(key))); | ||||
5019 | PyTuple_SET_ITEM(kwnames, i, key)PyTuple_SetItem(kwnames, i, key); | ||||
5020 | new_args[i + len_args] = value; | ||||
5021 | i++; | ||||
5022 | } | ||||
5023 | |||||
5024 | PyObject *res = ufunc_generic_fastcall(ufunc, | ||||
5025 | new_args, len_args, kwnames, NPY_FALSE0); | ||||
5026 | Py_DECREF(kwnames)_Py_DECREF(((PyObject*)(kwnames))); | ||||
5027 | return res; | ||||
5028 | } | ||||
5029 | |||||
5030 | |||||
5031 | #if PY_VERSION_HEX((3 << 24) | (8 << 16) | (5 << 8) | (0xF << 4) | (0 << 0)) >= 0x03080000 | ||||
5032 | /* | ||||
5033 | * Implement vectorcallfunc which should be defined with Python 3.8+. | ||||
5034 | * In principle this could be backported, but the speed gain seems moderate | ||||
5035 | * since ufunc calls often do not have keyword arguments and always have | ||||
5036 | * a large overhead. The only user would potentially be cython probably. | ||||
5037 | */ | ||||
5038 | static PyObject * | ||||
5039 | ufunc_generic_vectorcall(PyObject *ufunc, | ||||
5040 | PyObject *const *args, size_t len_args, PyObject *kwnames) | ||||
5041 | { | ||||
5042 | /* | ||||
5043 | * Unlike METH_FASTCALL, `len_args` may have a flag to signal that | ||||
5044 | * args[-1] may be (temporarily) used. So normalize it here. | ||||
5045 | */ | ||||
5046 | return ufunc_generic_fastcall((PyUFuncObject *)ufunc, | ||||
5047 | args, PyVectorcall_NARGS(len_args), kwnames, NPY_FALSE0); | ||||
5048 | } | ||||
5049 | #endif /* PY_VERSION_HEX >= 0x03080000 */ | ||||
5050 | |||||
5051 | |||||
5052 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) PyObject * | ||||
5053 | ufunc_geterr(PyObject *NPY_UNUSED(dummy)(__NPY_UNUSED_TAGGEDdummy) __attribute__ ((__unused__)), PyObject *args) | ||||
5054 | { | ||||
5055 | PyObject *thedict; | ||||
5056 | PyObject *res; | ||||
5057 | |||||
5058 | if (!PyArg_ParseTuple(args, "")) { | ||||
5059 | return NULL((void*)0); | ||||
5060 | } | ||||
5061 | thedict = PyThreadState_GetDict(); | ||||
5062 | if (thedict == NULL((void*)0)) { | ||||
5063 | thedict = PyEval_GetBuiltins(); | ||||
5064 | } | ||||
5065 | res = PyDict_GetItemWithError(thedict, npy_um_str_pyvals_name); | ||||
5066 | if (res == NULL((void*)0) && PyErr_Occurred()) { | ||||
5067 | return NULL((void*)0); | ||||
5068 | } | ||||
5069 | else if (res != NULL((void*)0)) { | ||||
5070 | Py_INCREF(res)_Py_INCREF(((PyObject*)(res))); | ||||
5071 | return res; | ||||
5072 | } | ||||
5073 | /* Construct list of defaults */ | ||||
5074 | res = PyList_New(3); | ||||
5075 | if (res == NULL((void*)0)) { | ||||
5076 | return NULL((void*)0); | ||||
5077 | } | ||||
5078 | PyList_SET_ITEM(res, 0, PyLong_FromLong(NPY_BUFSIZE))PyList_SetItem(res, 0, PyLong_FromLong(8192)); | ||||
5079 | PyList_SET_ITEM(res, 1, PyLong_FromLong(UFUNC_ERR_DEFAULT))PyList_SetItem(res, 1, PyLong_FromLong((1 << 0) + (1 << 3) + (1 << 9))); | ||||
5080 | PyList_SET_ITEM(res, 2, Py_None)PyList_SetItem(res, 2, (&_Py_NoneStruct)); Py_INCREF(Py_None)_Py_INCREF(((PyObject*)((&_Py_NoneStruct)))); | ||||
5081 | return res; | ||||
5082 | } | ||||
5083 | |||||
5084 | |||||
5085 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) PyObject * | ||||
5086 | ufunc_seterr(PyObject *NPY_UNUSED(dummy)(__NPY_UNUSED_TAGGEDdummy) __attribute__ ((__unused__)), PyObject *args) | ||||
5087 | { | ||||
5088 | PyObject *thedict; | ||||
5089 | int res; | ||||
5090 | PyObject *val; | ||||
5091 | static char *msg = "Error object must be a list of length 3"; | ||||
5092 | |||||
5093 | if (!PyArg_ParseTuple(args, "O:seterrobj", &val)) { | ||||
5094 | return NULL((void*)0); | ||||
5095 | } | ||||
5096 | if (!PyList_CheckExact(val)((((PyObject*)(val))->ob_type) == &PyList_Type) || PyList_GET_SIZE(val)(((void) (0)), (((PyVarObject*)(val))->ob_size)) != 3) { | ||||
5097 | PyErr_SetString(PyExc_ValueError, msg); | ||||
5098 | return NULL((void*)0); | ||||
5099 | } | ||||
5100 | thedict = PyThreadState_GetDict(); | ||||
5101 | if (thedict == NULL((void*)0)) { | ||||
5102 | thedict = PyEval_GetBuiltins(); | ||||
5103 | } | ||||
5104 | res = PyDict_SetItem(thedict, npy_um_str_pyvals_name, val); | ||||
5105 | if (res < 0) { | ||||
5106 | return NULL((void*)0); | ||||
5107 | } | ||||
5108 | #if USE_USE_DEFAULTS1==1 | ||||
5109 | if (ufunc_update_use_defaults() < 0) { | ||||
5110 | return NULL((void*)0); | ||||
5111 | } | ||||
5112 | #endif | ||||
5113 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
5114 | } | ||||
5115 | |||||
5116 | |||||
5117 | |||||
5118 | /*UFUNC_API*/ | ||||
5119 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) int | ||||
5120 | PyUFunc_ReplaceLoopBySignature(PyUFuncObject *func, | ||||
5121 | PyUFuncGenericFunction newfunc, | ||||
5122 | const int *signature, | ||||
5123 | PyUFuncGenericFunction *oldfunc) | ||||
5124 | { | ||||
5125 | int i, j; | ||||
5126 | int res = -1; | ||||
5127 | /* Find the location of the matching signature */ | ||||
5128 | for (i = 0; i < func->ntypes; i++) { | ||||
5129 | for (j = 0; j < func->nargs; j++) { | ||||
5130 | if (signature[j] != func->types[i*func->nargs+j]) { | ||||
5131 | break; | ||||
5132 | } | ||||
5133 | } | ||||
5134 | if (j < func->nargs) { | ||||
5135 | continue; | ||||
5136 | } | ||||
5137 | if (oldfunc != NULL((void*)0)) { | ||||
5138 | *oldfunc = func->functions[i]; | ||||
5139 | } | ||||
5140 | func->functions[i] = newfunc; | ||||
5141 | res = 0; | ||||
5142 | break; | ||||
5143 | } | ||||
5144 | return res; | ||||
5145 | } | ||||
5146 | |||||
5147 | /*UFUNC_API*/ | ||||
5148 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) PyObject * | ||||
5149 | PyUFunc_FromFuncAndData(PyUFuncGenericFunction *func, void **data, | ||||
5150 | char *types, int ntypes, | ||||
5151 | int nin, int nout, int identity, | ||||
5152 | const char *name, const char *doc, int unused) | ||||
5153 | { | ||||
5154 | return PyUFunc_FromFuncAndDataAndSignature(func, data, types, ntypes, | ||||
5155 | nin, nout, identity, name, doc, unused, NULL((void*)0)); | ||||
5156 | } | ||||
5157 | |||||
5158 | /*UFUNC_API*/ | ||||
5159 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) PyObject * | ||||
5160 | PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data, | ||||
5161 | char *types, int ntypes, | ||||
5162 | int nin, int nout, int identity, | ||||
5163 | const char *name, const char *doc, | ||||
5164 | int unused, const char *signature) | ||||
5165 | { | ||||
5166 | return PyUFunc_FromFuncAndDataAndSignatureAndIdentity( | ||||
5167 | func, data, types, ntypes, nin, nout, identity, name, doc, | ||||
5168 | unused, signature, NULL((void*)0)); | ||||
5169 | } | ||||
5170 | |||||
5171 | /*UFUNC_API*/ | ||||
5172 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) PyObject * | ||||
5173 | PyUFunc_FromFuncAndDataAndSignatureAndIdentity(PyUFuncGenericFunction *func, void **data, | ||||
5174 | char *types, int ntypes, | ||||
5175 | int nin, int nout, int identity, | ||||
5176 | const char *name, const char *doc, | ||||
5177 | const int unused, const char *signature, | ||||
5178 | PyObject *identity_value) | ||||
5179 | { | ||||
5180 | PyUFuncObject *ufunc; | ||||
5181 | if (nin + nout > NPY_MAXARGS32) { | ||||
5182 | PyErr_Format(PyExc_ValueError, | ||||
5183 | "Cannot construct a ufunc with more than %d operands " | ||||
5184 | "(requested number were: inputs = %d and outputs = %d)", | ||||
5185 | NPY_MAXARGS32, nin, nout); | ||||
5186 | return NULL((void*)0); | ||||
5187 | } | ||||
5188 | |||||
5189 | ufunc = PyObject_GC_New(PyUFuncObject, &PyUFunc_Type)( (PyUFuncObject *) _PyObject_GC_New(&PyUFunc_Type) ); | ||||
5190 | /* | ||||
5191 | * We use GC_New here for ufunc->obj, but do not use GC_Track since | ||||
5192 | * ufunc->obj is still NULL at the end of this function. | ||||
5193 | * See ufunc_frompyfunc where ufunc->obj is set and GC_Track is called. | ||||
5194 | */ | ||||
5195 | if (ufunc == NULL((void*)0)) { | ||||
5196 | return NULL((void*)0); | ||||
5197 | } | ||||
5198 | |||||
5199 | ufunc->nin = nin; | ||||
5200 | ufunc->nout = nout; | ||||
5201 | ufunc->nargs = nin+nout; | ||||
5202 | ufunc->identity = identity; | ||||
5203 | if (ufunc->identity == PyUFunc_IdentityValue-3) { | ||||
5204 | Py_INCREF(identity_value)_Py_INCREF(((PyObject*)(identity_value))); | ||||
5205 | ufunc->identity_value = identity_value; | ||||
5206 | } | ||||
5207 | else { | ||||
5208 | ufunc->identity_value = NULL((void*)0); | ||||
5209 | } | ||||
5210 | |||||
5211 | ufunc->functions = func; | ||||
5212 | ufunc->data = data; | ||||
5213 | ufunc->types = types; | ||||
5214 | ufunc->ntypes = ntypes; | ||||
5215 | ufunc->core_signature = NULL((void*)0); | ||||
5216 | ufunc->core_enabled = 0; | ||||
5217 | ufunc->obj = NULL((void*)0); | ||||
5218 | ufunc->core_num_dims = NULL((void*)0); | ||||
5219 | ufunc->core_num_dim_ix = 0; | ||||
5220 | ufunc->core_offsets = NULL((void*)0); | ||||
5221 | ufunc->core_dim_ixs = NULL((void*)0); | ||||
5222 | ufunc->core_dim_sizes = NULL((void*)0); | ||||
5223 | ufunc->core_dim_flags = NULL((void*)0); | ||||
5224 | ufunc->userloops = NULL((void*)0); | ||||
5225 | ufunc->ptr = NULL((void*)0); | ||||
5226 | #if PY_VERSION_HEX((3 << 24) | (8 << 16) | (5 << 8) | (0xF << 4) | (0 << 0)) >= 0x03080000 | ||||
5227 | ufunc->vectorcall = &ufunc_generic_vectorcall; | ||||
5228 | #else | ||||
5229 | ufunc->reserved2 = NULL((void*)0); | ||||
5230 | #endif | ||||
5231 | ufunc->reserved1 = 0; | ||||
5232 | ufunc->iter_flags = 0; | ||||
5233 | |||||
5234 | /* Type resolution and inner loop selection functions */ | ||||
5235 | ufunc->type_resolver = &PyUFunc_DefaultTypeResolver; | ||||
5236 | ufunc->legacy_inner_loop_selector = &PyUFunc_DefaultLegacyInnerLoopSelector; | ||||
5237 | ufunc->masked_inner_loop_selector = &PyUFunc_DefaultMaskedInnerLoopSelector; | ||||
5238 | |||||
5239 | if (name == NULL((void*)0)) { | ||||
5240 | ufunc->name = "?"; | ||||
5241 | } | ||||
5242 | else { | ||||
5243 | ufunc->name = name; | ||||
5244 | } | ||||
5245 | ufunc->doc = doc; | ||||
5246 | |||||
5247 | ufunc->op_flags = PyArray_mallocPyMem_RawMalloc(sizeof(npy_uint32)*ufunc->nargs); | ||||
5248 | if (ufunc->op_flags == NULL((void*)0)) { | ||||
5249 | Py_DECREF(ufunc)_Py_DECREF(((PyObject*)(ufunc))); | ||||
5250 | return PyErr_NoMemory(); | ||||
5251 | } | ||||
5252 | memset(ufunc->op_flags, 0, sizeof(npy_uint32)*ufunc->nargs); | ||||
5253 | |||||
5254 | if (signature != NULL((void*)0)) { | ||||
5255 | if (_parse_signature(ufunc, signature) != 0) { | ||||
5256 | Py_DECREF(ufunc)_Py_DECREF(((PyObject*)(ufunc))); | ||||
5257 | return NULL((void*)0); | ||||
5258 | } | ||||
5259 | } | ||||
5260 | return (PyObject *)ufunc; | ||||
5261 | } | ||||
5262 | |||||
5263 | |||||
5264 | /*UFUNC_API*/ | ||||
5265 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) int | ||||
5266 | PyUFunc_SetUsesArraysAsData(void **NPY_UNUSED(data)(__NPY_UNUSED_TAGGEDdata) __attribute__ ((__unused__)), size_t NPY_UNUSED(i)(__NPY_UNUSED_TAGGEDi) __attribute__ ((__unused__))) | ||||
5267 | { | ||||
5268 | /* NumPy 1.21, 201-03-29 */ | ||||
5269 | PyErr_SetString(PyExc_RuntimeError, | ||||
5270 | "PyUFunc_SetUsesArraysAsData() C-API function has been " | ||||
5271 | "disabled. It was initially deprecated in NumPy 1.19."); | ||||
5272 | return -1; | ||||
5273 | } | ||||
5274 | |||||
5275 | |||||
5276 | /* | ||||
5277 | * This is the first-part of the CObject structure. | ||||
5278 | * | ||||
5279 | * I don't think this will change, but if it should, then | ||||
5280 | * this needs to be fixed. The exposed C-API was insufficient | ||||
5281 | * because I needed to replace the pointer and it wouldn't | ||||
5282 | * let me with a destructor set (even though it works fine | ||||
5283 | * with the destructor). | ||||
5284 | */ | ||||
5285 | typedef struct { | ||||
5286 | PyObject_HEADPyObject ob_base; | ||||
5287 | void *c_obj; | ||||
5288 | } _simple_cobj; | ||||
5289 | |||||
5290 | #define _SETCPTR(cobj, val) ((_simple_cobj *)(cobj))->c_obj = (val) | ||||
5291 | |||||
5292 | /* return 1 if arg1 > arg2, 0 if arg1 == arg2, and -1 if arg1 < arg2 */ | ||||
5293 | static int | ||||
5294 | cmp_arg_types(int *arg1, int *arg2, int n) | ||||
5295 | { | ||||
5296 | for (; n > 0; n--, arg1++, arg2++) { | ||||
5297 | if (PyArray_EquivTypenums(*arg1, *arg2)) { | ||||
5298 | continue; | ||||
5299 | } | ||||
5300 | if (PyArray_CanCastSafely(*arg1, *arg2)) { | ||||
5301 | return -1; | ||||
5302 | } | ||||
5303 | return 1; | ||||
5304 | } | ||||
5305 | return 0; | ||||
5306 | } | ||||
5307 | |||||
5308 | /* | ||||
5309 | * This frees the linked-list structure when the CObject | ||||
5310 | * is destroyed (removed from the internal dictionary) | ||||
5311 | */ | ||||
5312 | static NPY_INLINEinline void | ||||
5313 | _free_loop1d_list(PyUFunc_Loop1d *data) | ||||
5314 | { | ||||
5315 | int i; | ||||
5316 | |||||
5317 | while (data != NULL((void*)0)) { | ||||
5318 | PyUFunc_Loop1d *next = data->next; | ||||
5319 | PyArray_freePyMem_RawFree(data->arg_types); | ||||
5320 | |||||
5321 | if (data->arg_dtypes != NULL((void*)0)) { | ||||
5322 | for (i = 0; i < data->nargs; i++) { | ||||
5323 | Py_DECREF(data->arg_dtypes[i])_Py_DECREF(((PyObject*)(data->arg_dtypes[i]))); | ||||
5324 | } | ||||
5325 | PyArray_freePyMem_RawFree(data->arg_dtypes); | ||||
5326 | } | ||||
5327 | |||||
5328 | PyArray_freePyMem_RawFree(data); | ||||
5329 | data = next; | ||||
5330 | } | ||||
5331 | } | ||||
5332 | |||||
5333 | static void | ||||
5334 | _loop1d_list_free(PyObject *ptr) | ||||
5335 | { | ||||
5336 | PyUFunc_Loop1d *data = (PyUFunc_Loop1d *)PyCapsule_GetPointer(ptr, NULL((void*)0)); | ||||
5337 | _free_loop1d_list(data); | ||||
5338 | } | ||||
5339 | |||||
5340 | |||||
5341 | /* | ||||
5342 | * This function allows the user to register a 1-d loop with an already | ||||
5343 | * created ufunc. This function is similar to RegisterLoopForType except | ||||
5344 | * that it allows a 1-d loop to be registered with PyArray_Descr objects | ||||
5345 | * instead of dtype type num values. This allows a 1-d loop to be registered | ||||
5346 | * for a structured array dtype or a custom dtype. The ufunc is called | ||||
5347 | * whenever any of it's input arguments match the user_dtype argument. | ||||
5348 | * | ||||
5349 | * ufunc - ufunc object created from call to PyUFunc_FromFuncAndData | ||||
5350 | * user_dtype - dtype that ufunc will be registered with | ||||
5351 | * function - 1-d loop function pointer | ||||
5352 | * arg_dtypes - array of dtype objects describing the ufunc operands | ||||
5353 | * data - arbitrary data pointer passed in to loop function | ||||
5354 | * | ||||
5355 | * returns 0 on success, -1 for failure | ||||
5356 | */ | ||||
5357 | /*UFUNC_API*/ | ||||
5358 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) int | ||||
5359 | PyUFunc_RegisterLoopForDescr(PyUFuncObject *ufunc, | ||||
5360 | PyArray_Descr *user_dtype, | ||||
5361 | PyUFuncGenericFunction function, | ||||
5362 | PyArray_Descr **arg_dtypes, | ||||
5363 | void *data) | ||||
5364 | { | ||||
5365 | int i; | ||||
5366 | int result = 0; | ||||
5367 | int *arg_typenums; | ||||
5368 | PyObject *key, *cobj; | ||||
5369 | |||||
5370 | if (user_dtype == NULL((void*)0)) { | ||||
5371 | PyErr_SetString(PyExc_TypeError, | ||||
5372 | "unknown user defined struct dtype"); | ||||
5373 | return -1; | ||||
5374 | } | ||||
5375 | |||||
5376 | key = PyLong_FromLong((long) user_dtype->type_num); | ||||
5377 | if (key == NULL((void*)0)) { | ||||
5378 | return -1; | ||||
5379 | } | ||||
5380 | |||||
5381 | arg_typenums = PyArray_mallocPyMem_RawMalloc(ufunc->nargs * sizeof(int)); | ||||
5382 | if (arg_typenums == NULL((void*)0)) { | ||||
5383 | PyErr_NoMemory(); | ||||
5384 | return -1; | ||||
5385 | } | ||||
5386 | if (arg_dtypes != NULL((void*)0)) { | ||||
5387 | for (i = 0; i < ufunc->nargs; i++) { | ||||
5388 | arg_typenums[i] = arg_dtypes[i]->type_num; | ||||
5389 | } | ||||
5390 | } | ||||
5391 | else { | ||||
5392 | for (i = 0; i < ufunc->nargs; i++) { | ||||
5393 | arg_typenums[i] = user_dtype->type_num; | ||||
5394 | } | ||||
5395 | } | ||||
5396 | |||||
5397 | result = PyUFunc_RegisterLoopForType(ufunc, user_dtype->type_num, | ||||
5398 | function, arg_typenums, data); | ||||
5399 | |||||
5400 | if (result == 0) { | ||||
5401 | cobj = PyDict_GetItemWithError(ufunc->userloops, key); | ||||
5402 | if (cobj == NULL((void*)0) && PyErr_Occurred()) { | ||||
5403 | result = -1; | ||||
5404 | } | ||||
5405 | else if (cobj == NULL((void*)0)) { | ||||
5406 | PyErr_SetString(PyExc_KeyError, | ||||
5407 | "userloop for user dtype not found"); | ||||
5408 | result = -1; | ||||
5409 | } | ||||
5410 | else { | ||||
5411 | int cmp = 1; | ||||
5412 | PyUFunc_Loop1d *current = PyCapsule_GetPointer(cobj, NULL((void*)0)); | ||||
5413 | if (current == NULL((void*)0)) { | ||||
5414 | result = -1; | ||||
5415 | goto done; | ||||
5416 | } | ||||
5417 | while (current != NULL((void*)0)) { | ||||
5418 | cmp = cmp_arg_types(current->arg_types, | ||||
5419 | arg_typenums, ufunc->nargs); | ||||
5420 | if (cmp >= 0 && current->arg_dtypes == NULL((void*)0)) { | ||||
5421 | break; | ||||
5422 | } | ||||
5423 | current = current->next; | ||||
5424 | } | ||||
5425 | if (cmp == 0 && current != NULL((void*)0) && current->arg_dtypes == NULL((void*)0)) { | ||||
5426 | current->arg_dtypes = PyArray_mallocPyMem_RawMalloc(ufunc->nargs * | ||||
5427 | sizeof(PyArray_Descr*)); | ||||
5428 | if (current->arg_dtypes == NULL((void*)0)) { | ||||
5429 | PyErr_NoMemory(); | ||||
5430 | result = -1; | ||||
5431 | goto done; | ||||
5432 | } | ||||
5433 | else if (arg_dtypes != NULL((void*)0)) { | ||||
5434 | for (i = 0; i < ufunc->nargs; i++) { | ||||
5435 | current->arg_dtypes[i] = arg_dtypes[i]; | ||||
5436 | Py_INCREF(current->arg_dtypes[i])_Py_INCREF(((PyObject*)(current->arg_dtypes[i]))); | ||||
5437 | } | ||||
5438 | } | ||||
5439 | else { | ||||
5440 | for (i = 0; i < ufunc->nargs; i++) { | ||||
5441 | current->arg_dtypes[i] = user_dtype; | ||||
5442 | Py_INCREF(current->arg_dtypes[i])_Py_INCREF(((PyObject*)(current->arg_dtypes[i]))); | ||||
5443 | } | ||||
5444 | } | ||||
5445 | current->nargs = ufunc->nargs; | ||||
5446 | } | ||||
5447 | else { | ||||
5448 | PyErr_SetString(PyExc_RuntimeError, | ||||
5449 | "loop already registered"); | ||||
5450 | result = -1; | ||||
5451 | } | ||||
5452 | } | ||||
5453 | } | ||||
5454 | |||||
5455 | done: | ||||
5456 | PyArray_freePyMem_RawFree(arg_typenums); | ||||
5457 | |||||
5458 | Py_DECREF(key)_Py_DECREF(((PyObject*)(key))); | ||||
5459 | |||||
5460 | return result; | ||||
5461 | } | ||||
5462 | |||||
5463 | /*UFUNC_API*/ | ||||
5464 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) int | ||||
5465 | PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc, | ||||
5466 | int usertype, | ||||
5467 | PyUFuncGenericFunction function, | ||||
5468 | const int *arg_types, | ||||
5469 | void *data) | ||||
5470 | { | ||||
5471 | PyArray_Descr *descr; | ||||
5472 | PyUFunc_Loop1d *funcdata; | ||||
5473 | PyObject *key, *cobj; | ||||
5474 | int i; | ||||
5475 | int *newtypes=NULL((void*)0); | ||||
5476 | |||||
5477 | descr=PyArray_DescrFromType(usertype); | ||||
5478 | if ((usertype < NPY_USERDEF && usertype != NPY_VOID) || (descr==NULL((void*)0))) { | ||||
5479 | PyErr_SetString(PyExc_TypeError, "unknown user-defined type"); | ||||
5480 | return -1; | ||||
5481 | } | ||||
5482 | Py_DECREF(descr)_Py_DECREF(((PyObject*)(descr))); | ||||
5483 | |||||
5484 | if (ufunc->userloops == NULL((void*)0)) { | ||||
5485 | ufunc->userloops = PyDict_New(); | ||||
5486 | } | ||||
5487 | key = PyLong_FromLong((long) usertype); | ||||
5488 | if (key == NULL((void*)0)) { | ||||
5489 | return -1; | ||||
5490 | } | ||||
5491 | funcdata = PyArray_mallocPyMem_RawMalloc(sizeof(PyUFunc_Loop1d)); | ||||
5492 | if (funcdata == NULL((void*)0)) { | ||||
5493 | goto fail; | ||||
5494 | } | ||||
5495 | newtypes = PyArray_mallocPyMem_RawMalloc(sizeof(int)*ufunc->nargs); | ||||
5496 | if (newtypes == NULL((void*)0)) { | ||||
5497 | goto fail; | ||||
5498 | } | ||||
5499 | if (arg_types != NULL((void*)0)) { | ||||
5500 | for (i = 0; i < ufunc->nargs; i++) { | ||||
5501 | newtypes[i] = arg_types[i]; | ||||
5502 | } | ||||
5503 | } | ||||
5504 | else { | ||||
5505 | for (i = 0; i < ufunc->nargs; i++) { | ||||
5506 | newtypes[i] = usertype; | ||||
5507 | } | ||||
5508 | } | ||||
5509 | |||||
5510 | funcdata->func = function; | ||||
5511 | funcdata->arg_types = newtypes; | ||||
5512 | funcdata->data = data; | ||||
5513 | funcdata->next = NULL((void*)0); | ||||
5514 | funcdata->arg_dtypes = NULL((void*)0); | ||||
5515 | funcdata->nargs = 0; | ||||
5516 | |||||
5517 | /* Get entry for this user-defined type*/ | ||||
5518 | cobj = PyDict_GetItemWithError(ufunc->userloops, key); | ||||
5519 | if (cobj == NULL((void*)0) && PyErr_Occurred()) { | ||||
5520 | return 0; | ||||
5521 | } | ||||
5522 | /* If it's not there, then make one and return. */ | ||||
5523 | else if (cobj == NULL((void*)0)) { | ||||
5524 | cobj = PyCapsule_New((void *)funcdata, NULL((void*)0), _loop1d_list_free); | ||||
5525 | if (cobj == NULL((void*)0)) { | ||||
5526 | goto fail; | ||||
5527 | } | ||||
5528 | PyDict_SetItem(ufunc->userloops, key, cobj); | ||||
5529 | Py_DECREF(cobj)_Py_DECREF(((PyObject*)(cobj))); | ||||
5530 | Py_DECREF(key)_Py_DECREF(((PyObject*)(key))); | ||||
5531 | return 0; | ||||
5532 | } | ||||
5533 | else { | ||||
5534 | PyUFunc_Loop1d *current, *prev = NULL((void*)0); | ||||
5535 | int cmp = 1; | ||||
5536 | /* | ||||
5537 | * There is already at least 1 loop. Place this one in | ||||
5538 | * lexicographic order. If the next one signature | ||||
5539 | * is exactly like this one, then just replace. | ||||
5540 | * Otherwise insert. | ||||
5541 | */ | ||||
5542 | current = PyCapsule_GetPointer(cobj, NULL((void*)0)); | ||||
5543 | if (current == NULL((void*)0)) { | ||||
5544 | goto fail; | ||||
5545 | } | ||||
5546 | while (current != NULL((void*)0)) { | ||||
5547 | cmp = cmp_arg_types(current->arg_types, newtypes, ufunc->nargs); | ||||
5548 | if (cmp >= 0) { | ||||
5549 | break; | ||||
5550 | } | ||||
5551 | prev = current; | ||||
5552 | current = current->next; | ||||
5553 | } | ||||
5554 | if (cmp == 0) { | ||||
5555 | /* just replace it with new function */ | ||||
5556 | current->func = function; | ||||
5557 | current->data = data; | ||||
5558 | PyArray_freePyMem_RawFree(newtypes); | ||||
5559 | PyArray_freePyMem_RawFree(funcdata); | ||||
5560 | } | ||||
5561 | else { | ||||
5562 | /* | ||||
5563 | * insert it before the current one by hacking the internals | ||||
5564 | * of cobject to replace the function pointer --- can't use | ||||
5565 | * CObject API because destructor is set. | ||||
5566 | */ | ||||
5567 | funcdata->next = current; | ||||
5568 | if (prev == NULL((void*)0)) { | ||||
5569 | /* place this at front */ | ||||
5570 | _SETCPTR(cobj, funcdata); | ||||
5571 | } | ||||
5572 | else { | ||||
5573 | prev->next = funcdata; | ||||
5574 | } | ||||
5575 | } | ||||
5576 | } | ||||
5577 | Py_DECREF(key)_Py_DECREF(((PyObject*)(key))); | ||||
5578 | return 0; | ||||
5579 | |||||
5580 | fail: | ||||
5581 | Py_DECREF(key)_Py_DECREF(((PyObject*)(key))); | ||||
5582 | PyArray_freePyMem_RawFree(funcdata); | ||||
5583 | PyArray_freePyMem_RawFree(newtypes); | ||||
5584 | if (!PyErr_Occurred()) PyErr_NoMemory(); | ||||
5585 | return -1; | ||||
5586 | } | ||||
5587 | |||||
5588 | #undef _SETCPTR | ||||
5589 | |||||
5590 | |||||
5591 | static void | ||||
5592 | ufunc_dealloc(PyUFuncObject *ufunc) | ||||
5593 | { | ||||
5594 | PyObject_GC_UnTrack((PyObject *)ufunc); | ||||
5595 | PyArray_freePyMem_RawFree(ufunc->core_num_dims); | ||||
5596 | PyArray_freePyMem_RawFree(ufunc->core_dim_ixs); | ||||
5597 | PyArray_freePyMem_RawFree(ufunc->core_dim_sizes); | ||||
5598 | PyArray_freePyMem_RawFree(ufunc->core_dim_flags); | ||||
5599 | PyArray_freePyMem_RawFree(ufunc->core_offsets); | ||||
5600 | PyArray_freePyMem_RawFree(ufunc->core_signature); | ||||
5601 | PyArray_freePyMem_RawFree(ufunc->ptr); | ||||
5602 | PyArray_freePyMem_RawFree(ufunc->op_flags); | ||||
5603 | Py_XDECREF(ufunc->userloops)_Py_XDECREF(((PyObject*)(ufunc->userloops))); | ||||
5604 | if (ufunc->identity == PyUFunc_IdentityValue-3) { | ||||
5605 | Py_DECREF(ufunc->identity_value)_Py_DECREF(((PyObject*)(ufunc->identity_value))); | ||||
5606 | } | ||||
5607 | if (ufunc->obj != NULL((void*)0)) { | ||||
5608 | Py_DECREF(ufunc->obj)_Py_DECREF(((PyObject*)(ufunc->obj))); | ||||
5609 | } | ||||
5610 | PyObject_GC_Del(ufunc); | ||||
5611 | } | ||||
5612 | |||||
5613 | static PyObject * | ||||
5614 | ufunc_repr(PyUFuncObject *ufunc) | ||||
5615 | { | ||||
5616 | return PyUnicode_FromFormat("<ufunc '%s'>", ufunc->name); | ||||
5617 | } | ||||
5618 | |||||
5619 | static int | ||||
5620 | ufunc_traverse(PyUFuncObject *self, visitproc visit, void *arg) | ||||
5621 | { | ||||
5622 | Py_VISIT(self->obj)do { if (self->obj) { int vret = visit(((PyObject*)(self-> obj)), arg); if (vret) return vret; } } while (0); | ||||
5623 | if (self->identity == PyUFunc_IdentityValue-3) { | ||||
5624 | Py_VISIT(self->identity_value)do { if (self->identity_value) { int vret = visit(((PyObject *)(self->identity_value)), arg); if (vret) return vret; } } while (0); | ||||
5625 | } | ||||
5626 | return 0; | ||||
5627 | } | ||||
5628 | |||||
5629 | /****************************************************************************** | ||||
5630 | *** UFUNC METHODS *** | ||||
5631 | *****************************************************************************/ | ||||
5632 | |||||
5633 | |||||
5634 | /* | ||||
5635 | * op.outer(a,b) is equivalent to op(a[:,NewAxis,NewAxis,etc.],b) | ||||
5636 | * where a has b.ndim NewAxis terms appended. | ||||
5637 | * | ||||
5638 | * The result has dimensions a.ndim + b.ndim | ||||
5639 | */ | ||||
5640 | static PyObject * | ||||
5641 | ufunc_outer(PyUFuncObject *ufunc, | ||||
5642 | PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) | ||||
5643 | { | ||||
5644 | if (ufunc->core_enabled) { | ||||
5645 | PyErr_Format(PyExc_TypeError, | ||||
5646 | "method outer is not allowed in ufunc with non-trivial"\ | ||||
5647 | " signature"); | ||||
5648 | return NULL((void*)0); | ||||
5649 | } | ||||
5650 | |||||
5651 | if (ufunc->nin != 2) { | ||||
5652 | PyErr_SetString(PyExc_ValueError, | ||||
5653 | "outer product only supported "\ | ||||
5654 | "for binary functions"); | ||||
5655 | return NULL((void*)0); | ||||
5656 | } | ||||
5657 | |||||
5658 | if (len_args != 2) { | ||||
5659 | PyErr_SetString(PyExc_TypeError, "exactly two arguments expected"); | ||||
5660 | return NULL((void*)0); | ||||
5661 | } | ||||
5662 | |||||
5663 | return ufunc_generic_fastcall(ufunc, args, len_args, kwnames, NPY_TRUE1); | ||||
5664 | } | ||||
5665 | |||||
5666 | |||||
5667 | static PyObject * | ||||
5668 | prepare_input_arguments_for_outer(PyObject *args, PyUFuncObject *ufunc) | ||||
5669 | { | ||||
5670 | PyArrayObject *ap1 = NULL((void*)0); | ||||
5671 | PyObject *tmp; | ||||
5672 | static PyObject *_numpy_matrix; | ||||
5673 | npy_cache_import("numpy", "matrix", &_numpy_matrix); | ||||
5674 | |||||
5675 | const char *matrix_deprecation_msg = ( | ||||
5676 | "%s.outer() was passed a numpy matrix as %s argument. " | ||||
5677 | "Special handling of matrix is deprecated and will result in an " | ||||
5678 | "error in most cases. Please convert the matrix to a NumPy " | ||||
5679 | "array to retain the old behaviour. You can use `matrix.A` " | ||||
5680 | "to achieve this."); | ||||
5681 | |||||
5682 | tmp = PyTuple_GET_ITEM(args, 0)((((void) (0)), (PyTupleObject *)(args))->ob_item[0]); | ||||
5683 | |||||
5684 | if (PyObject_IsInstance(tmp, _numpy_matrix)) { | ||||
5685 | /* DEPRECATED 2020-05-13, NumPy 1.20 */ | ||||
5686 | if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, | ||||
5687 | matrix_deprecation_msg, ufunc->name, "first") < 0) { | ||||
5688 | return NULL((void*)0); | ||||
5689 | } | ||||
5690 | ap1 = (PyArrayObject *) PyArray_FromObject(tmp, NPY_NOTYPE, 0, 0)PyArray_FromAny(tmp, PyArray_DescrFromType(NPY_NOTYPE), 0, 0, (0x0100 | 0x0400) | 0x0040, ((void*)0)); | ||||
5691 | } | ||||
5692 | else { | ||||
5693 | ap1 = (PyArrayObject *) PyArray_FROM_O(tmp)PyArray_FromAny(tmp, ((void*)0), 0, 0, 0, ((void*)0)); | ||||
5694 | } | ||||
5695 | if (ap1 == NULL((void*)0)) { | ||||
5696 | return NULL((void*)0); | ||||
5697 | } | ||||
5698 | |||||
5699 | PyArrayObject *ap2 = NULL((void*)0); | ||||
5700 | tmp = PyTuple_GET_ITEM(args, 1)((((void) (0)), (PyTupleObject *)(args))->ob_item[1]); | ||||
5701 | if (PyObject_IsInstance(tmp, _numpy_matrix)) { | ||||
5702 | /* DEPRECATED 2020-05-13, NumPy 1.20 */ | ||||
5703 | if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, | ||||
5704 | matrix_deprecation_msg, ufunc->name, "second") < 0) { | ||||
5705 | Py_DECREF(ap1)_Py_DECREF(((PyObject*)(ap1))); | ||||
5706 | return NULL((void*)0); | ||||
5707 | } | ||||
5708 | ap2 = (PyArrayObject *) PyArray_FromObject(tmp, NPY_NOTYPE, 0, 0)PyArray_FromAny(tmp, PyArray_DescrFromType(NPY_NOTYPE), 0, 0, (0x0100 | 0x0400) | 0x0040, ((void*)0)); | ||||
5709 | } | ||||
5710 | else { | ||||
5711 | ap2 = (PyArrayObject *) PyArray_FROM_O(tmp)PyArray_FromAny(tmp, ((void*)0), 0, 0, 0, ((void*)0)); | ||||
5712 | } | ||||
5713 | if (ap2 == NULL((void*)0)) { | ||||
5714 | Py_DECREF(ap1)_Py_DECREF(((PyObject*)(ap1))); | ||||
5715 | return NULL((void*)0); | ||||
5716 | } | ||||
5717 | /* Construct new shape from ap1 and ap2 and then reshape */ | ||||
5718 | PyArray_Dims newdims; | ||||
5719 | npy_intp newshape[NPY_MAXDIMS32]; | ||||
5720 | newdims.len = PyArray_NDIM(ap1) + PyArray_NDIM(ap2); | ||||
5721 | newdims.ptr = newshape; | ||||
5722 | |||||
5723 | if (newdims.len > NPY_MAXDIMS32) { | ||||
5724 | PyErr_Format(PyExc_ValueError, | ||||
5725 | "maximum supported dimension for an ndarray is %d, but " | ||||
5726 | "`%s.outer()` result would have %d.", | ||||
5727 | NPY_MAXDIMS32, ufunc->name, newdims.len); | ||||
5728 | goto fail; | ||||
5729 | } | ||||
5730 | if (newdims.ptr == NULL((void*)0)) { | ||||
5731 | goto fail; | ||||
5732 | } | ||||
5733 | memcpy(newshape, PyArray_DIMS(ap1), PyArray_NDIM(ap1) * sizeof(npy_intp)); | ||||
5734 | for (int i = PyArray_NDIM(ap1); i < newdims.len; i++) { | ||||
5735 | newshape[i] = 1; | ||||
5736 | } | ||||
5737 | |||||
5738 | PyArrayObject *ap_new; | ||||
5739 | ap_new = (PyArrayObject *)PyArray_Newshape(ap1, &newdims, NPY_CORDER); | ||||
5740 | if (ap_new == NULL((void*)0)) { | ||||
5741 | goto fail; | ||||
5742 | } | ||||
5743 | if (PyArray_NDIM(ap_new) != newdims.len || | ||||
5744 | !PyArray_CompareLists(PyArray_DIMS(ap_new), newshape, newdims.len)) { | ||||
5745 | PyErr_Format(PyExc_TypeError, | ||||
5746 | "%s.outer() called with ndarray-subclass of type '%s' " | ||||
5747 | "which modified its shape after a reshape. `outer()` relies " | ||||
5748 | "on reshaping the inputs and is for example not supported for " | ||||
5749 | "the 'np.matrix' class (the usage of matrix is generally " | ||||
5750 | "discouraged). " | ||||
5751 | "To work around this issue, please convert the inputs to " | ||||
5752 | "numpy arrays.", | ||||
5753 | ufunc->name, Py_TYPE(ap_new)(((PyObject*)(ap_new))->ob_type)->tp_name); | ||||
5754 | Py_DECREF(ap_new)_Py_DECREF(((PyObject*)(ap_new))); | ||||
5755 | goto fail; | ||||
5756 | } | ||||
5757 | |||||
5758 | Py_DECREF(ap1)_Py_DECREF(((PyObject*)(ap1))); | ||||
5759 | return Py_BuildValue("(NN)", ap_new, ap2); | ||||
5760 | |||||
5761 | fail: | ||||
5762 | Py_XDECREF(ap1)_Py_XDECREF(((PyObject*)(ap1))); | ||||
5763 | Py_XDECREF(ap2)_Py_XDECREF(((PyObject*)(ap2))); | ||||
5764 | return NULL((void*)0); | ||||
5765 | } | ||||
5766 | |||||
5767 | |||||
5768 | static PyObject * | ||||
5769 | ufunc_reduce(PyUFuncObject *ufunc, | ||||
5770 | PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) | ||||
5771 | { | ||||
5772 | return PyUFunc_GenericReduction( | ||||
5773 | ufunc, args, len_args, kwnames, UFUNC_REDUCE0); | ||||
5774 | } | ||||
5775 | |||||
5776 | static PyObject * | ||||
5777 | ufunc_accumulate(PyUFuncObject *ufunc, | ||||
5778 | PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) | ||||
5779 | { | ||||
5780 | return PyUFunc_GenericReduction( | ||||
5781 | ufunc, args, len_args, kwnames, UFUNC_ACCUMULATE1); | ||||
5782 | } | ||||
5783 | |||||
5784 | static PyObject * | ||||
5785 | ufunc_reduceat(PyUFuncObject *ufunc, | ||||
5786 | PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) | ||||
5787 | { | ||||
5788 | return PyUFunc_GenericReduction( | ||||
5789 | ufunc, args, len_args, kwnames, UFUNC_REDUCEAT2); | ||||
5790 | } | ||||
5791 | |||||
5792 | /* Helper for ufunc_at, below */ | ||||
5793 | static NPY_INLINEinline PyArrayObject * | ||||
5794 | new_array_op(PyArrayObject *op_array, char *data) | ||||
5795 | { | ||||
5796 | npy_intp dims[1] = {1}; | ||||
5797 | PyObject *r = PyArray_NewFromDescr(&PyArray_Type, PyArray_DESCR(op_array), | ||||
5798 | 1, dims, NULL((void*)0), data, | ||||
5799 | NPY_ARRAY_WRITEABLE0x0400, NULL((void*)0)); | ||||
5800 | return (PyArrayObject *)r; | ||||
5801 | } | ||||
5802 | |||||
5803 | /* | ||||
5804 | * Call ufunc only on selected array items and store result in first operand. | ||||
5805 | * For add ufunc, method call is equivalent to op1[idx] += op2 with no | ||||
5806 | * buffering of the first operand. | ||||
5807 | * Arguments: | ||||
5808 | * op1 - First operand to ufunc | ||||
5809 | * idx - Indices that are applied to first operand. Equivalent to op1[idx]. | ||||
5810 | * op2 - Second operand to ufunc (if needed). Must be able to broadcast | ||||
5811 | * over first operand. | ||||
5812 | */ | ||||
5813 | static PyObject * | ||||
5814 | ufunc_at(PyUFuncObject *ufunc, PyObject *args) | ||||
5815 | { | ||||
5816 | PyObject *op1 = NULL((void*)0); | ||||
5817 | PyObject *idx = NULL((void*)0); | ||||
5818 | PyObject *op2 = NULL((void*)0); | ||||
5819 | PyArrayObject *op1_array = NULL((void*)0); | ||||
5820 | PyArrayObject *op2_array = NULL((void*)0); | ||||
5821 | PyArrayMapIterObject *iter = NULL((void*)0); | ||||
5822 | PyArrayIterObject *iter2 = NULL((void*)0); | ||||
5823 | PyArray_Descr *dtypes[3] = {NULL((void*)0), NULL((void*)0), NULL((void*)0)}; | ||||
5824 | PyArrayObject *operands[3] = {NULL((void*)0), NULL((void*)0), NULL((void*)0)}; | ||||
5825 | PyArrayObject *array_operands[3] = {NULL((void*)0), NULL((void*)0), NULL((void*)0)}; | ||||
5826 | |||||
5827 | int needs_api = 0; | ||||
5828 | |||||
5829 | PyUFuncGenericFunction innerloop; | ||||
5830 | void *innerloopdata; | ||||
5831 | npy_intp i; | ||||
5832 | int nop; | ||||
5833 | |||||
5834 | /* override vars */ | ||||
5835 | int errval; | ||||
5836 | PyObject *override = NULL((void*)0); | ||||
5837 | |||||
5838 | NpyIter *iter_buffer; | ||||
5839 | NpyIter_IterNextFunc *iternext; | ||||
5840 | npy_uint32 op_flags[NPY_MAXARGS32]; | ||||
5841 | int buffersize; | ||||
5842 | int errormask = 0; | ||||
5843 | char * err_msg = NULL((void*)0); | ||||
5844 | NPY_BEGIN_THREADS_DEFPyThreadState *_save=((void*)0);; | ||||
5845 | |||||
5846 | if (ufunc->nin > 2) { | ||||
5847 | PyErr_SetString(PyExc_ValueError, | ||||
5848 | "Only unary and binary ufuncs supported at this time"); | ||||
5849 | return NULL((void*)0); | ||||
5850 | } | ||||
5851 | |||||
5852 | if (ufunc->nout != 1) { | ||||
5853 | PyErr_SetString(PyExc_ValueError, | ||||
5854 | "Only single output ufuncs supported at this time"); | ||||
5855 | return NULL((void*)0); | ||||
5856 | } | ||||
5857 | |||||
5858 | if (!PyArg_ParseTuple(args, "OO|O:at", &op1, &idx, &op2)) { | ||||
5859 | return NULL((void*)0); | ||||
5860 | } | ||||
5861 | |||||
5862 | if (ufunc->nin == 2 && op2 == NULL((void*)0)) { | ||||
5863 | PyErr_SetString(PyExc_ValueError, | ||||
5864 | "second operand needed for ufunc"); | ||||
5865 | return NULL((void*)0); | ||||
5866 | } | ||||
5867 | errval = PyUFunc_CheckOverride(ufunc, "at", | ||||
5868 | args, NULL((void*)0), NULL((void*)0), 0, NULL((void*)0), &override); | ||||
5869 | |||||
5870 | if (errval) { | ||||
5871 | return NULL((void*)0); | ||||
5872 | } | ||||
5873 | else if (override) { | ||||
5874 | return override; | ||||
5875 | } | ||||
5876 | |||||
5877 | if (!PyArray_Check(op1)((((PyObject*)(op1))->ob_type) == (&PyArray_Type) || PyType_IsSubtype ((((PyObject*)(op1))->ob_type), (&PyArray_Type)))) { | ||||
5878 | PyErr_SetString(PyExc_TypeError, | ||||
5879 | "first operand must be array"); | ||||
5880 | return NULL((void*)0); | ||||
5881 | } | ||||
5882 | |||||
5883 | op1_array = (PyArrayObject *)op1; | ||||
5884 | |||||
5885 | /* Create second operand from number array if needed. */ | ||||
5886 | if (op2 != NULL((void*)0)) { | ||||
5887 | op2_array = (PyArrayObject *)PyArray_FromAny(op2, NULL((void*)0), | ||||
5888 | 0, 0, 0, NULL((void*)0)); | ||||
5889 | if (op2_array == NULL((void*)0)) { | ||||
5890 | goto fail; | ||||
5891 | } | ||||
5892 | } | ||||
5893 | |||||
5894 | /* Create map iterator */ | ||||
5895 | iter = (PyArrayMapIterObject *)PyArray_MapIterArrayCopyIfOverlap( | ||||
5896 | op1_array, idx, 1, op2_array); | ||||
5897 | if (iter == NULL((void*)0)) { | ||||
5898 | goto fail; | ||||
5899 | } | ||||
5900 | op1_array = iter->array; /* May be updateifcopied on overlap */ | ||||
5901 | |||||
5902 | if (op2 != NULL((void*)0)) { | ||||
5903 | /* | ||||
5904 | * May need to swap axes so that second operand is | ||||
5905 | * iterated over correctly | ||||
5906 | */ | ||||
5907 | if ((iter->subspace != NULL((void*)0)) && (iter->consec)) { | ||||
5908 | PyArray_MapIterSwapAxes(iter, &op2_array, 0); | ||||
5909 | if (op2_array == NULL((void*)0)) { | ||||
5910 | goto fail; | ||||
5911 | } | ||||
5912 | } | ||||
5913 | |||||
5914 | /* | ||||
5915 | * Create array iter object for second operand that | ||||
5916 | * "matches" the map iter object for the first operand. | ||||
5917 | * Then we can just iterate over the first and second | ||||
5918 | * operands at the same time and not have to worry about | ||||
5919 | * picking the correct elements from each operand to apply | ||||
5920 | * the ufunc to. | ||||
5921 | */ | ||||
5922 | if ((iter2 = (PyArrayIterObject *)\ | ||||
5923 | PyArray_BroadcastToShape((PyObject *)op2_array, | ||||
5924 | iter->dimensions, iter->nd))==NULL((void*)0)) { | ||||
5925 | goto fail; | ||||
5926 | } | ||||
5927 | } | ||||
5928 | |||||
5929 | /* | ||||
5930 | * Create dtypes array for either one or two input operands. | ||||
5931 | * The output operand is set to the first input operand | ||||
5932 | */ | ||||
5933 | operands[0] = op1_array; | ||||
5934 | if (op2_array != NULL((void*)0)) { | ||||
5935 | operands[1] = op2_array; | ||||
5936 | operands[2] = op1_array; | ||||
5937 | nop = 3; | ||||
5938 | } | ||||
5939 | else { | ||||
5940 | operands[1] = op1_array; | ||||
5941 | operands[2] = NULL((void*)0); | ||||
5942 | nop = 2; | ||||
5943 | } | ||||
5944 | |||||
5945 | if (ufunc->type_resolver(ufunc, NPY_UNSAFE_CASTING, | ||||
5946 | operands, NULL((void*)0), dtypes) < 0) { | ||||
5947 | goto fail; | ||||
5948 | } | ||||
5949 | if (ufunc->legacy_inner_loop_selector(ufunc, dtypes, | ||||
5950 | &innerloop, &innerloopdata, &needs_api) < 0) { | ||||
5951 | goto fail; | ||||
5952 | } | ||||
5953 | |||||
5954 | Py_INCREF(PyArray_DESCR(op1_array))_Py_INCREF(((PyObject*)(PyArray_DESCR(op1_array)))); | ||||
5955 | array_operands[0] = new_array_op(op1_array, iter->dataptr); | ||||
5956 | if (iter2 != NULL((void*)0)) { | ||||
5957 | Py_INCREF(PyArray_DESCR(op2_array))_Py_INCREF(((PyObject*)(PyArray_DESCR(op2_array)))); | ||||
5958 | array_operands[1] = new_array_op(op2_array, PyArray_ITER_DATA(iter2)((void *)(((PyArrayIterObject *)(iter2))->dataptr))); | ||||
5959 | Py_INCREF(PyArray_DESCR(op1_array))_Py_INCREF(((PyObject*)(PyArray_DESCR(op1_array)))); | ||||
5960 | array_operands[2] = new_array_op(op1_array, iter->dataptr); | ||||
5961 | } | ||||
5962 | else { | ||||
5963 | Py_INCREF(PyArray_DESCR(op1_array))_Py_INCREF(((PyObject*)(PyArray_DESCR(op1_array)))); | ||||
5964 | array_operands[1] = new_array_op(op1_array, iter->dataptr); | ||||
5965 | array_operands[2] = NULL((void*)0); | ||||
5966 | } | ||||
5967 | |||||
5968 | /* Set up the flags */ | ||||
5969 | op_flags[0] = NPY_ITER_READONLY0x00020000| | ||||
5970 | NPY_ITER_ALIGNED0x00100000; | ||||
5971 | |||||
5972 | if (iter2 != NULL((void*)0)) { | ||||
5973 | op_flags[1] = NPY_ITER_READONLY0x00020000| | ||||
5974 | NPY_ITER_ALIGNED0x00100000; | ||||
5975 | op_flags[2] = NPY_ITER_WRITEONLY0x00040000| | ||||
5976 | NPY_ITER_ALIGNED0x00100000| | ||||
5977 | NPY_ITER_ALLOCATE0x01000000| | ||||
5978 | NPY_ITER_NO_BROADCAST0x08000000| | ||||
5979 | NPY_ITER_NO_SUBTYPE0x02000000; | ||||
5980 | } | ||||
5981 | else { | ||||
5982 | op_flags[1] = NPY_ITER_WRITEONLY0x00040000| | ||||
5983 | NPY_ITER_ALIGNED0x00100000| | ||||
5984 | NPY_ITER_ALLOCATE0x01000000| | ||||
5985 | NPY_ITER_NO_BROADCAST0x08000000| | ||||
5986 | NPY_ITER_NO_SUBTYPE0x02000000; | ||||
5987 | } | ||||
5988 | |||||
5989 | if (_get_bufsize_errmask(NULL((void*)0), ufunc->name, &buffersize, &errormask) < 0) { | ||||
5990 | goto fail; | ||||
5991 | } | ||||
5992 | |||||
5993 | /* | ||||
5994 | * Create NpyIter object to "iterate" over single element of each input | ||||
5995 | * operand. This is an easy way to reuse the NpyIter logic for dealing | ||||
5996 | * with certain cases like casting operands to correct dtype. On each | ||||
5997 | * iteration over the MapIterArray object created above, we'll take the | ||||
5998 | * current data pointers from that and reset this NpyIter object using | ||||
5999 | * those data pointers, and then trigger a buffer copy. The buffer data | ||||
6000 | * pointers from the NpyIter object will then be passed to the inner loop | ||||
6001 | * function. | ||||
6002 | */ | ||||
6003 | iter_buffer = NpyIter_AdvancedNew(nop, array_operands, | ||||
6004 | NPY_ITER_EXTERNAL_LOOP0x00000008| | ||||
6005 | NPY_ITER_REFS_OK0x00000020| | ||||
6006 | NPY_ITER_ZEROSIZE_OK0x00000040| | ||||
6007 | NPY_ITER_BUFFERED0x00000200| | ||||
6008 | NPY_ITER_GROWINNER0x00000400| | ||||
6009 | NPY_ITER_DELAY_BUFALLOC0x00000800, | ||||
6010 | NPY_KEEPORDER, NPY_UNSAFE_CASTING, | ||||
6011 | op_flags, dtypes, | ||||
6012 | -1, NULL((void*)0), NULL((void*)0), buffersize); | ||||
6013 | |||||
6014 | if (iter_buffer == NULL((void*)0)) { | ||||
6015 | goto fail; | ||||
6016 | } | ||||
6017 | |||||
6018 | needs_api = needs_api | NpyIter_IterationNeedsAPI(iter_buffer); | ||||
6019 | |||||
6020 | iternext = NpyIter_GetIterNext(iter_buffer, NULL((void*)0)); | ||||
6021 | if (iternext == NULL((void*)0)) { | ||||
6022 | NpyIter_Deallocate(iter_buffer); | ||||
6023 | goto fail; | ||||
6024 | } | ||||
6025 | |||||
6026 | if (!needs_api) { | ||||
6027 | NPY_BEGIN_THREADSdo {_save = PyEval_SaveThread();} while (0);; | ||||
6028 | } | ||||
6029 | |||||
6030 | /* | ||||
6031 | * Iterate over first and second operands and call ufunc | ||||
6032 | * for each pair of inputs | ||||
6033 | */ | ||||
6034 | i = iter->size; | ||||
6035 | while (i > 0) | ||||
6036 | { | ||||
6037 | char *dataptr[3]; | ||||
6038 | char **buffer_dataptr; | ||||
6039 | /* one element at a time, no stride required but read by innerloop */ | ||||
6040 | npy_intp count[3] = {1, 0xDEADBEEF, 0xDEADBEEF}; | ||||
6041 | npy_intp stride[3] = {0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF}; | ||||
6042 | |||||
6043 | /* | ||||
6044 | * Set up data pointers for either one or two input operands. | ||||
6045 | * The output data pointer points to the first operand data. | ||||
6046 | */ | ||||
6047 | dataptr[0] = iter->dataptr; | ||||
6048 | if (iter2 != NULL((void*)0)) { | ||||
6049 | dataptr[1] = PyArray_ITER_DATA(iter2)((void *)(((PyArrayIterObject *)(iter2))->dataptr)); | ||||
6050 | dataptr[2] = iter->dataptr; | ||||
6051 | } | ||||
6052 | else { | ||||
6053 | dataptr[1] = iter->dataptr; | ||||
6054 | dataptr[2] = NULL((void*)0); | ||||
6055 | } | ||||
6056 | |||||
6057 | /* Reset NpyIter data pointers which will trigger a buffer copy */ | ||||
6058 | NpyIter_ResetBasePointers(iter_buffer, dataptr, &err_msg); | ||||
6059 | if (err_msg) { | ||||
6060 | break; | ||||
6061 | } | ||||
6062 | |||||
6063 | buffer_dataptr = NpyIter_GetDataPtrArray(iter_buffer); | ||||
6064 | |||||
6065 | innerloop(buffer_dataptr, count, stride, innerloopdata); | ||||
6066 | |||||
6067 | if (needs_api && PyErr_Occurred()) { | ||||
6068 | break; | ||||
6069 | } | ||||
6070 | |||||
6071 | /* | ||||
6072 | * Call to iternext triggers copy from buffer back to output array | ||||
6073 | * after innerloop puts result in buffer. | ||||
6074 | */ | ||||
6075 | iternext(iter_buffer); | ||||
6076 | |||||
6077 | PyArray_MapIterNext(iter); | ||||
6078 | if (iter2 != NULL((void*)0)) { | ||||
6079 | PyArray_ITER_NEXT(iter2)do { ((PyArrayIterObject *)(iter2))->index++; if (((PyArrayIterObject *)(iter2))->nd_m1 == 0) { do { (((PyArrayIterObject *)(iter2 )))->dataptr += ((PyArrayIterObject *)(((PyArrayIterObject *)(iter2))))->strides[0]; (((PyArrayIterObject *)(iter2)) )->coordinates[0]++; } while (0); } else if (((PyArrayIterObject *)(iter2))->contiguous) ((PyArrayIterObject *)(iter2))-> dataptr += PyArray_DESCR(((PyArrayIterObject *)(iter2))->ao )->elsize; else if (((PyArrayIterObject *)(iter2))->nd_m1 == 1) { do { if ((((PyArrayIterObject *)(iter2)))->coordinates [1] < (((PyArrayIterObject *)(iter2)))->dims_m1[1]) { ( ((PyArrayIterObject *)(iter2)))->coordinates[1]++; (((PyArrayIterObject *)(iter2)))->dataptr += (((PyArrayIterObject *)(iter2)))-> strides[1]; } else { (((PyArrayIterObject *)(iter2)))->coordinates [1] = 0; (((PyArrayIterObject *)(iter2)))->coordinates[0]++ ; (((PyArrayIterObject *)(iter2)))->dataptr += (((PyArrayIterObject *)(iter2)))->strides[0] - (((PyArrayIterObject *)(iter2)) )->backstrides[1]; } } while (0); } else { int __npy_i; for (__npy_i=((PyArrayIterObject *)(iter2))->nd_m1; __npy_i >= 0; __npy_i--) { if (((PyArrayIterObject *)(iter2))->coordinates [__npy_i] < ((PyArrayIterObject *)(iter2))->dims_m1[__npy_i ]) { ((PyArrayIterObject *)(iter2))->coordinates[__npy_i]++ ; ((PyArrayIterObject *)(iter2))->dataptr += ((PyArrayIterObject *)(iter2))->strides[__npy_i]; break; } else { ((PyArrayIterObject *)(iter2))->coordinates[__npy_i] = 0; ((PyArrayIterObject *)(iter2))->dataptr -= ((PyArrayIterObject *)(iter2))-> backstrides[__npy_i]; } } } } while (0); | ||||
6080 | } | ||||
6081 | |||||
6082 | i--; | ||||
6083 | } | ||||
6084 | |||||
6085 | NPY_END_THREADSdo { if (_save) { PyEval_RestoreThread(_save); _save = ((void *)0);} } while (0);; | ||||
6086 | |||||
6087 | if (err_msg) { | ||||
6088 | PyErr_SetString(PyExc_ValueError, err_msg); | ||||
6089 | } | ||||
6090 | |||||
6091 | NpyIter_Deallocate(iter_buffer); | ||||
6092 | |||||
6093 | Py_XDECREF(op2_array)_Py_XDECREF(((PyObject*)(op2_array))); | ||||
6094 | Py_XDECREF(iter)_Py_XDECREF(((PyObject*)(iter))); | ||||
6095 | Py_XDECREF(iter2)_Py_XDECREF(((PyObject*)(iter2))); | ||||
6096 | for (i = 0; i < 3; i++) { | ||||
6097 | Py_XDECREF(dtypes[i])_Py_XDECREF(((PyObject*)(dtypes[i]))); | ||||
6098 | Py_XDECREF(array_operands[i])_Py_XDECREF(((PyObject*)(array_operands[i]))); | ||||
6099 | } | ||||
6100 | |||||
6101 | if (needs_api && PyErr_Occurred()) { | ||||
6102 | return NULL((void*)0); | ||||
6103 | } | ||||
6104 | else { | ||||
6105 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
6106 | } | ||||
6107 | |||||
6108 | fail: | ||||
6109 | /* iter_buffer has already been deallocated, don't use NpyIter_Dealloc */ | ||||
6110 | if (op1_array != (PyArrayObject*)op1) { | ||||
6111 | PyArray_DiscardWritebackIfCopy(op1_array); | ||||
6112 | } | ||||
6113 | Py_XDECREF(op2_array)_Py_XDECREF(((PyObject*)(op2_array))); | ||||
6114 | Py_XDECREF(iter)_Py_XDECREF(((PyObject*)(iter))); | ||||
6115 | Py_XDECREF(iter2)_Py_XDECREF(((PyObject*)(iter2))); | ||||
6116 | for (i = 0; i < 3; i++) { | ||||
6117 | Py_XDECREF(dtypes[i])_Py_XDECREF(((PyObject*)(dtypes[i]))); | ||||
6118 | Py_XDECREF(array_operands[i])_Py_XDECREF(((PyObject*)(array_operands[i]))); | ||||
6119 | } | ||||
6120 | |||||
6121 | return NULL((void*)0); | ||||
6122 | } | ||||
6123 | |||||
6124 | |||||
6125 | static struct PyMethodDef ufunc_methods[] = { | ||||
6126 | {"reduce", | ||||
6127 | (PyCFunction)ufunc_reduce, | ||||
6128 | METH_FASTCALL0x0080 | METH_KEYWORDS0x0002, NULL((void*)0) }, | ||||
6129 | {"accumulate", | ||||
6130 | (PyCFunction)ufunc_accumulate, | ||||
6131 | METH_FASTCALL0x0080 | METH_KEYWORDS0x0002, NULL((void*)0) }, | ||||
6132 | {"reduceat", | ||||
6133 | (PyCFunction)ufunc_reduceat, | ||||
6134 | METH_FASTCALL0x0080 | METH_KEYWORDS0x0002, NULL((void*)0) }, | ||||
6135 | {"outer", | ||||
6136 | (PyCFunction)ufunc_outer, | ||||
6137 | METH_FASTCALL0x0080 | METH_KEYWORDS0x0002, NULL((void*)0)}, | ||||
6138 | {"at", | ||||
6139 | (PyCFunction)ufunc_at, | ||||
6140 | METH_VARARGS0x0001, NULL((void*)0)}, | ||||
6141 | {NULL((void*)0), NULL((void*)0), 0, NULL((void*)0)} /* sentinel */ | ||||
6142 | }; | ||||
6143 | |||||
6144 | |||||
6145 | /****************************************************************************** | ||||
6146 | *** UFUNC GETSET *** | ||||
6147 | *****************************************************************************/ | ||||
6148 | |||||
6149 | |||||
6150 | static char | ||||
6151 | _typecharfromnum(int num) { | ||||
6152 | PyArray_Descr *descr; | ||||
6153 | char ret; | ||||
6154 | |||||
6155 | descr = PyArray_DescrFromType(num); | ||||
6156 | ret = descr->type; | ||||
6157 | Py_DECREF(descr)_Py_DECREF(((PyObject*)(descr))); | ||||
6158 | return ret; | ||||
6159 | } | ||||
6160 | |||||
6161 | |||||
6162 | static PyObject * | ||||
6163 | ufunc_get_doc(PyUFuncObject *ufunc) | ||||
6164 | { | ||||
6165 | static PyObject *_sig_formatter; | ||||
6166 | PyObject *doc; | ||||
6167 | |||||
6168 | npy_cache_import( | ||||
6169 | "numpy.core._internal", | ||||
6170 | "_ufunc_doc_signature_formatter", | ||||
6171 | &_sig_formatter); | ||||
6172 | |||||
6173 | if (_sig_formatter == NULL((void*)0)) { | ||||
6174 | return NULL((void*)0); | ||||
6175 | } | ||||
6176 | |||||
6177 | /* | ||||
6178 | * Put docstring first or FindMethod finds it... could so some | ||||
6179 | * introspection on name and nin + nout to automate the first part | ||||
6180 | * of it the doc string shouldn't need the calling convention | ||||
6181 | */ | ||||
6182 | doc = PyObject_CallFunctionObjArgs(_sig_formatter, | ||||
6183 | (PyObject *)ufunc, NULL((void*)0)); | ||||
6184 | if (doc == NULL((void*)0)) { | ||||
6185 | return NULL((void*)0); | ||||
6186 | } | ||||
6187 | if (ufunc->doc != NULL((void*)0)) { | ||||
6188 | Py_SETREF(doc, PyUnicode_FromFormat("%S\n\n%s", doc, ufunc->doc))do { PyObject *_py_tmp = ((PyObject*)(doc)); (doc) = (PyUnicode_FromFormat ("%S\n\n%s", doc, ufunc->doc)); _Py_DECREF(((PyObject*)(_py_tmp ))); } while (0); | ||||
6189 | } | ||||
6190 | return doc; | ||||
6191 | } | ||||
6192 | |||||
6193 | |||||
6194 | static PyObject * | ||||
6195 | ufunc_get_nin(PyUFuncObject *ufunc) | ||||
6196 | { | ||||
6197 | return PyLong_FromLong(ufunc->nin); | ||||
6198 | } | ||||
6199 | |||||
6200 | static PyObject * | ||||
6201 | ufunc_get_nout(PyUFuncObject *ufunc) | ||||
6202 | { | ||||
6203 | return PyLong_FromLong(ufunc->nout); | ||||
6204 | } | ||||
6205 | |||||
6206 | static PyObject * | ||||
6207 | ufunc_get_nargs(PyUFuncObject *ufunc) | ||||
6208 | { | ||||
6209 | return PyLong_FromLong(ufunc->nargs); | ||||
6210 | } | ||||
6211 | |||||
6212 | static PyObject * | ||||
6213 | ufunc_get_ntypes(PyUFuncObject *ufunc) | ||||
6214 | { | ||||
6215 | return PyLong_FromLong(ufunc->ntypes); | ||||
6216 | } | ||||
6217 | |||||
6218 | static PyObject * | ||||
6219 | ufunc_get_types(PyUFuncObject *ufunc) | ||||
6220 | { | ||||
6221 | /* return a list with types grouped input->output */ | ||||
6222 | PyObject *list; | ||||
6223 | PyObject *str; | ||||
6224 | int k, j, n, nt = ufunc->ntypes; | ||||
6225 | int ni = ufunc->nin; | ||||
6226 | int no = ufunc->nout; | ||||
6227 | char *t; | ||||
6228 | list = PyList_New(nt); | ||||
6229 | if (list == NULL((void*)0)) { | ||||
6230 | return NULL((void*)0); | ||||
6231 | } | ||||
6232 | t = PyArray_mallocPyMem_RawMalloc(no+ni+2); | ||||
6233 | n = 0; | ||||
6234 | for (k = 0; k < nt; k++) { | ||||
6235 | for (j = 0; j<ni; j++) { | ||||
6236 | t[j] = _typecharfromnum(ufunc->types[n]); | ||||
6237 | n++; | ||||
6238 | } | ||||
6239 | t[ni] = '-'; | ||||
6240 | t[ni+1] = '>'; | ||||
6241 | for (j = 0; j < no; j++) { | ||||
6242 | t[ni + 2 + j] = _typecharfromnum(ufunc->types[n]); | ||||
6243 | n++; | ||||
6244 | } | ||||
6245 | str = PyUnicode_FromStringAndSize(t, no + ni + 2); | ||||
6246 | PyList_SET_ITEM(list, k, str)PyList_SetItem(list, k, str); | ||||
6247 | } | ||||
6248 | PyArray_freePyMem_RawFree(t); | ||||
6249 | return list; | ||||
6250 | } | ||||
6251 | |||||
6252 | static PyObject * | ||||
6253 | ufunc_get_name(PyUFuncObject *ufunc) | ||||
6254 | { | ||||
6255 | return PyUnicode_FromString(ufunc->name); | ||||
6256 | } | ||||
6257 | |||||
6258 | static PyObject * | ||||
6259 | ufunc_get_identity(PyUFuncObject *ufunc) | ||||
6260 | { | ||||
6261 | npy_bool reorderable; | ||||
6262 | return _get_identity(ufunc, &reorderable); | ||||
6263 | } | ||||
6264 | |||||
6265 | static PyObject * | ||||
6266 | ufunc_get_signature(PyUFuncObject *ufunc) | ||||
6267 | { | ||||
6268 | if (!ufunc->core_enabled) { | ||||
6269 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
6270 | } | ||||
6271 | return PyUnicode_FromString(ufunc->core_signature); | ||||
6272 | } | ||||
6273 | |||||
6274 | #undef _typecharfromnum | ||||
6275 | |||||
6276 | /* | ||||
6277 | * Docstring is now set from python | ||||
6278 | * static char *Ufunctype__doc__ = NULL; | ||||
6279 | */ | ||||
6280 | static PyGetSetDef ufunc_getset[] = { | ||||
6281 | {"__doc__", | ||||
6282 | (getter)ufunc_get_doc, | ||||
6283 | NULL((void*)0), NULL((void*)0), NULL((void*)0)}, | ||||
6284 | {"nin", | ||||
6285 | (getter)ufunc_get_nin, | ||||
6286 | NULL((void*)0), NULL((void*)0), NULL((void*)0)}, | ||||
6287 | {"nout", | ||||
6288 | (getter)ufunc_get_nout, | ||||
6289 | NULL((void*)0), NULL((void*)0), NULL((void*)0)}, | ||||
6290 | {"nargs", | ||||
6291 | (getter)ufunc_get_nargs, | ||||
6292 | NULL((void*)0), NULL((void*)0), NULL((void*)0)}, | ||||
6293 | {"ntypes", | ||||
6294 | (getter)ufunc_get_ntypes, | ||||
6295 | NULL((void*)0), NULL((void*)0), NULL((void*)0)}, | ||||
6296 | {"types", | ||||
6297 | (getter)ufunc_get_types, | ||||
6298 | NULL((void*)0), NULL((void*)0), NULL((void*)0)}, | ||||
6299 | {"__name__", | ||||
6300 | (getter)ufunc_get_name, | ||||
6301 | NULL((void*)0), NULL((void*)0), NULL((void*)0)}, | ||||
6302 | {"identity", | ||||
6303 | (getter)ufunc_get_identity, | ||||
6304 | NULL((void*)0), NULL((void*)0), NULL((void*)0)}, | ||||
6305 | {"signature", | ||||
6306 | (getter)ufunc_get_signature, | ||||
6307 | NULL((void*)0), NULL((void*)0), NULL((void*)0)}, | ||||
6308 | {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}, /* Sentinel */ | ||||
6309 | }; | ||||
6310 | |||||
6311 | |||||
6312 | /****************************************************************************** | ||||
6313 | *** UFUNC TYPE OBJECT *** | ||||
6314 | *****************************************************************************/ | ||||
6315 | |||||
6316 | NPY_NO_EXPORT__attribute__((visibility("hidden"))) PyTypeObject PyUFunc_Type = { | ||||
6317 | PyVarObject_HEAD_INIT(NULL, 0){ { 1, ((void*)0) }, 0 }, | ||||
6318 | .tp_name = "numpy.ufunc", | ||||
6319 | .tp_basicsize = sizeof(PyUFuncObject), | ||||
6320 | .tp_dealloc = (destructor)ufunc_dealloc, | ||||
6321 | .tp_repr = (reprfunc)ufunc_repr, | ||||
6322 | .tp_call = (ternaryfunc)ufunc_generic_call, | ||||
6323 | .tp_str = (reprfunc)ufunc_repr, | ||||
6324 | .tp_flags = Py_TPFLAGS_DEFAULT( 0 | (1UL << 18) | 0) | | ||||
6325 | #if PY_VERSION_HEX((3 << 24) | (8 << 16) | (5 << 8) | (0xF << 4) | (0 << 0)) >= 0x03080000 | ||||
6326 | _Py_TPFLAGS_HAVE_VECTORCALL(1UL << 11) | | ||||
6327 | #endif | ||||
6328 | Py_TPFLAGS_HAVE_GC(1UL << 14), | ||||
6329 | .tp_traverse = (traverseproc)ufunc_traverse, | ||||
6330 | .tp_methods = ufunc_methods, | ||||
6331 | .tp_getset = ufunc_getset, | ||||
6332 | #if PY_VERSION_HEX((3 << 24) | (8 << 16) | (5 << 8) | (0xF << 4) | (0 << 0)) >= 0x03080000 | ||||
6333 | .tp_vectorcall_offset = offsetof(PyUFuncObject, vectorcall)__builtin_offsetof(PyUFuncObject, vectorcall), | ||||
6334 | #endif | ||||
6335 | }; | ||||
6336 | |||||
6337 | /* End of code for ufunc objects */ |
1 | void _Py_INCREF(PyObject *op) { ++op->ob_refcnt; } |