File: | numpy/core/src/umath/ufunc_object.c |
Warning: | line 5376, column 11 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 != NULL((void*)0)) ? 1 : 0), op, | |||
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 < nop; ++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 | #ifndef PyLong_FromLong |
2 | struct _object; |
3 | typedef struct _object PyObject; |
4 | PyObject* clang_analyzer_PyObject_New_Reference(); |
5 | PyObject* PyLong_FromLong(long v) { |
6 | return clang_analyzer_PyObject_New_Reference(); |
7 | } |
8 | #else |
9 | #warning "API PyLong_FromLong is defined as a macro." |
10 | #endif |