numexpr 2.14.2__cp313-cp313-win_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1585 @@
1
+ /*********************************************************************
2
+ Numexpr - Fast numerical array expression evaluator for NumPy.
3
+
4
+ License: MIT
5
+ Author: See AUTHORS.txt
6
+
7
+ See LICENSE.txt for details about copyright and rights to use.
8
+ **********************************************************************/
9
+
10
+ #include "module.hpp"
11
+ #include <numpy/npy_cpu.h>
12
+ #include <math.h>
13
+ #include <string.h>
14
+ #include <assert.h>
15
+ #include <vector>
16
+
17
+ #include "numexpr_config.hpp"
18
+ #include "complex_functions.hpp"
19
+ #include "interpreter.hpp"
20
+ #include "numexpr_object.hpp"
21
+ #include "bespoke_functions.hpp"
22
+
23
+ #ifdef _MSC_VER
24
+ /* Some missing symbols and functions for Win */
25
+ #define fmax max
26
+ #define fmin min
27
+ #define NE_INFINITY (DBL_MAX+DBL_MAX)
28
+ #define NE_NAN (INFINITY-INFINITY)
29
+ #else
30
+ #define NE_INFINITY INFINITY
31
+ #define NE_NAN NAN
32
+ #endif
33
+
34
+ #ifndef SIZE_MAX
35
+ #define SIZE_MAX ((size_t)-1)
36
+ #endif
37
+
38
+ #define RETURN_TYPE char*
39
+
40
+ // AVAILABLE(Haystack, Haystack_Len, J, Needle_Len)
41
+ // A macro that returns nonzero if there are at least Needle_Len
42
+ // bytes left starting at Haystack[J].
43
+ // Haystack is 'unsigned char *', Haystack_Len, J, and Needle_Len
44
+ // are 'size_t'; Haystack_Len is an lvalue. For NUL-terminated
45
+ // searches, Haystack_Len can be modified each iteration to avoid
46
+ // having to compute the end of Haystack up front.
47
+
48
+ #define AVAILABLE(Haystack, Haystack_Len, J, Needle_Len) \
49
+ ((Haystack_Len) >= (J) + (Needle_Len))
50
+
51
+ // To allow building with NumPy<2 locally define the new NumPy macros:
52
+ #if NPY_ABI_VERSION < 0x02000000
53
+ #define PyDataType_ELSIZE(descr) ((descr)->elsize)
54
+ #define PyDataType_SET_ELSIZE(descr, size) (descr)->elsize = size
55
+ #endif
56
+
57
+ #include "str-two-way.hpp"
58
+
59
+ #ifdef DEBUG
60
+ #define DEBUG_TEST 1
61
+ #else
62
+ #define DEBUG_TEST 0
63
+ #endif
64
+
65
+
66
+ using namespace std;
67
+
68
+ // Global state
69
+ thread_data th_params;
70
+
71
+ /* This file and interp_body should really be generated from a description of
72
+ the opcodes -- there's too much repetition here for manually editing */
73
+
74
+
75
+ /* bit of a misnomer; includes the return value. */
76
+ #define NUMEXPR_MAX_ARGS 4
77
+
78
+ static char op_signature_table[][NUMEXPR_MAX_ARGS] = {
79
+ #define Tb 'b'
80
+ #define Ti 'i'
81
+ #define Tl 'l'
82
+ #define Tf 'f'
83
+ #define Td 'd'
84
+ #define Tc 'c'
85
+ #define Ts 's'
86
+ #define Tn 'n'
87
+ #define T0 0
88
+ #define OPCODE(n, e, ex, rt, a1, a2, a3) {rt, a1, a2, a3},
89
+ #include "opcodes.hpp"
90
+ #undef OPCODE
91
+ #undef Tb
92
+ #undef Ti
93
+ #undef Tl
94
+ #undef Tf
95
+ #undef Td
96
+ #undef Tc
97
+ #undef Ts
98
+ #undef Tn
99
+ #undef T0
100
+ };
101
+
102
+ /* returns the sig of the nth op, '\0' if no more ops -1 on failure */
103
+ static int
104
+ op_signature(int op, unsigned int n) {
105
+ if (n >= NUMEXPR_MAX_ARGS) {
106
+ return 0;
107
+ }
108
+ if (op < 0 || op > OP_END) {
109
+ return -1;
110
+ }
111
+ return op_signature_table[op][n];
112
+ }
113
+
114
+
115
+
116
+ /*
117
+ To add a function to the lookup table, add to FUNC_CODES (first
118
+ group is 1-arg functions, second is 2-arg functions), also to
119
+ functions_f or functions_ff as appropriate. Finally, use add_func
120
+ down below to add to funccodes. Functions with more arguments
121
+ aren't implemented at present, but should be easy; just copy the 1-
122
+ or 2-arg case.
123
+
124
+ Some functions (for example, sqrt) are repeated in this table that
125
+ are opcodes, but there's no problem with that as the compiler
126
+ selects opcodes over functions, and this makes it easier to compare
127
+ opcode vs. function speeds.
128
+ */
129
+
130
+ typedef float (*FuncFFPtr)(float);
131
+
132
+ #ifdef _WIN32
133
+ inline float signf2(float x) { // needed to wait for bespoke_functions to be loaded
134
+ return signf(x);
135
+ }
136
+ FuncFFPtr functions_ff[] = {
137
+ #define FUNC_FF(fop, s, f, f_win32, ...) f_win32,
138
+ #include "functions.hpp"
139
+ #undef FUNC_FF
140
+ };
141
+ #else
142
+ FuncFFPtr functions_ff[] = {
143
+ #define FUNC_FF(fop, s, f, ...) f,
144
+ #include "functions.hpp"
145
+ #undef FUNC_FF
146
+ };
147
+ #endif
148
+
149
+ #ifdef USE_VML
150
+ typedef void (*FuncFFPtr_vml)(MKL_INT, const float*, float*);
151
+ FuncFFPtr_vml functions_ff_vml[] = {
152
+ #define FUNC_FF(fop, s, f, f_win32, f_vml) f_vml,
153
+ #include "functions.hpp"
154
+ #undef FUNC_FF
155
+ };
156
+ #endif
157
+
158
+ typedef float (*FuncFFFPtr)(float, float);
159
+
160
+ #ifdef _WIN32
161
+ FuncFFFPtr functions_fff[] = {
162
+ #define FUNC_FFF(fop, s, f, f_win32, ...) f_win32,
163
+ #include "functions.hpp"
164
+ #undef FUNC_FFF
165
+ };
166
+ #else
167
+ FuncFFFPtr functions_fff[] = {
168
+ #define FUNC_FFF(fop, s, f, ...) f,
169
+ #include "functions.hpp"
170
+ #undef FUNC_FFF
171
+ };
172
+ #endif
173
+
174
+ #ifdef USE_VML
175
+ typedef void (*FuncFFFPtr_vml)(MKL_INT, const float*, const float*, float*);
176
+ FuncFFFPtr_vml functions_fff_vml[] = {
177
+ #define FUNC_FFF(fop, s, f, f_win32, f_vml) f_vml,
178
+ #include "functions.hpp"
179
+ #undef FUNC_FFF
180
+ };
181
+ #endif
182
+
183
+ typedef double (*FuncDDPtr)(double);
184
+
185
+ FuncDDPtr functions_dd[] = {
186
+ #define FUNC_DD(fop, s, f, ...) f,
187
+ #include "functions.hpp"
188
+ #undef FUNC_DD
189
+ };
190
+
191
+ // Boolean output functions
192
+ typedef bool (*FuncBFPtr)(float);
193
+ #ifdef _WIN32
194
+ FuncBFPtr functions_bf[] = {
195
+ #define FUNC_BF(fop, s, f, f_win32, ...) f_win32,
196
+ #include "functions.hpp"
197
+ #undef FUNC_BF
198
+ };
199
+ #else
200
+ FuncBFPtr functions_bf[] = {
201
+ #define FUNC_BF(fop, s, f, ...) f,
202
+ #include "functions.hpp"
203
+ #undef FUNC_BF
204
+ };
205
+ #endif
206
+
207
+ #ifdef USE_VML
208
+ typedef void (*FuncBFPtr_vml)(MKL_INT, const float*, bool*);
209
+ FuncBFPtr_vml functions_bf_vml[] = {
210
+ #define FUNC_BF(fop, s, f, f_win32, f_vml) f_vml,
211
+ #include "functions.hpp"
212
+ #undef FUNC_BF
213
+ };
214
+ #endif
215
+
216
+ typedef bool (*FuncBDPtr)(double);
217
+ FuncBDPtr functions_bd[] = {
218
+ #define FUNC_BD(fop, s, f, ...) f,
219
+ #include "functions.hpp"
220
+ #undef FUNC_BD
221
+ };
222
+
223
+ #ifdef USE_VML
224
+ typedef void (*FuncBDPtr_vml)(MKL_INT, const double*, bool*);
225
+ FuncBDPtr_vml functions_bd_vml[] = {
226
+ #define FUNC_BD(fop, s, f, f_vml) f_vml,
227
+ #include "functions.hpp"
228
+ #undef FUNC_BD
229
+ };
230
+ #endif
231
+
232
+ typedef bool (*FuncBCPtr)(std::complex<double>*);
233
+ FuncBCPtr functions_bc[] = {
234
+ #define FUNC_BC(fop, s, f, ...) f,
235
+ #include "functions.hpp"
236
+ #undef FUNC_BC
237
+ };
238
+
239
+
240
+ #ifdef USE_VML
241
+ typedef void (*FuncBCPtr_vml)(MKL_INT, const MKL_Complex16[], bool*);
242
+ FuncBCPtr_vml functions_bc_vml[] = {
243
+ #define FUNC_BC(fop, s, f, f_vml) f_vml,
244
+ #include "functions.hpp"
245
+ #undef FUNC_BC
246
+ };
247
+ #endif
248
+
249
+ #ifdef USE_VML
250
+ typedef void (*FuncDDPtr_vml)(MKL_INT, const double*, double*);
251
+ FuncDDPtr_vml functions_dd_vml[] = {
252
+ #define FUNC_DD(fop, s, f, f_vml) f_vml,
253
+ #include "functions.hpp"
254
+ #undef FUNC_DD
255
+ };
256
+ #endif
257
+
258
+ typedef double (*FuncDDDPtr)(double, double);
259
+
260
+ FuncDDDPtr functions_ddd[] = {
261
+ #define FUNC_DDD(fop, s, f, ...) f,
262
+ #include "functions.hpp"
263
+ #undef FUNC_DDD
264
+ };
265
+
266
+ #ifdef USE_VML
267
+ typedef void (*FuncDDDPtr_vml)(MKL_INT, const double*, const double*, double*);
268
+ FuncDDDPtr_vml functions_ddd_vml[] = {
269
+ #define FUNC_DDD(fop, s, f, f_vml) f_vml,
270
+ #include "functions.hpp"
271
+ #undef FUNC_DDD
272
+ };
273
+ #endif
274
+
275
+
276
+
277
+ typedef void (*FuncCCPtr)(std::complex<double>*, std::complex<double>*);
278
+
279
+ FuncCCPtr functions_cc[] = {
280
+ #define FUNC_CC(fop, s, f, ...) f,
281
+ #include "functions.hpp"
282
+ #undef FUNC_CC
283
+ };
284
+
285
+ #ifdef USE_VML
286
+ typedef void (*FuncCCPtr_vml)(MKL_INT, const MKL_Complex16[], MKL_Complex16[]);
287
+ FuncCCPtr_vml functions_cc_vml[] = {
288
+ #define FUNC_CC(fop, s, f, f_vml) f_vml,
289
+ #include "functions.hpp"
290
+ #undef FUNC_CC
291
+ };
292
+ #endif
293
+
294
+
295
+ typedef void (*FuncCCCPtr)(std::complex<double>*, std::complex<double>*, std::complex<double>*);
296
+
297
+ FuncCCCPtr functions_ccc[] = {
298
+ #define FUNC_CCC(fop, s, f) f,
299
+ #include "functions.hpp"
300
+ #undef FUNC_CCC
301
+ };
302
+
303
+ /* integer return types*/
304
+ typedef int (*FuncIIPtr)(int);
305
+ FuncIIPtr functions_ii[] = {
306
+ #define FUNC_II(fop, s, f, ...) f,
307
+ #include "functions.hpp"
308
+ #undef FUNC_II
309
+ };
310
+
311
+ #ifdef USE_VML
312
+ typedef void (*FuncIIPtr_vml)(MKL_INT, const int*, int*);
313
+ FuncIIPtr_vml functions_ii_vml[] = {
314
+ #define FUNC_II(fop, s, f, f_vml) f_vml,
315
+ #include "functions.hpp"
316
+ #undef FUNC_II
317
+ };
318
+ #endif
319
+
320
+ typedef long (*FuncLLPtr)(long);
321
+ FuncLLPtr functions_ll[] = {
322
+ #define FUNC_LL(fop, s, f, ...) f,
323
+ #include "functions.hpp"
324
+ #undef FUNC_LL
325
+ };
326
+
327
+ #ifdef USE_VML
328
+ typedef void (*FuncLLPtr_vml)(MKL_INT, const long*, long*);
329
+ FuncLLPtr_vml functions_ll_vml[] = {
330
+ #define FUNC_LL(fop, s, f, f_vml) f_vml,
331
+ #include "functions.hpp"
332
+ #undef FUNC_LL
333
+ };
334
+ #endif
335
+
336
+
337
+ char
338
+ get_return_sig(PyObject* program)
339
+ { // use unsigned chars to match OPCODE table and allow OPCODE > 127
340
+ int sig;
341
+ unsigned char last_opcode;
342
+ Py_ssize_t end = PyBytes_Size(program);
343
+ unsigned char *program_str = (unsigned char *)PyBytes_AS_STRING(program);
344
+
345
+ do {
346
+ end -= 4;
347
+ if (end < 0) return 'X';
348
+ last_opcode = program_str[end];
349
+ }
350
+ while (last_opcode == OP_NOOP);
351
+
352
+ sig = op_signature(last_opcode, 0);
353
+ if (sig <= 0) {
354
+ return 'X';
355
+ } else {
356
+ return (char)sig;
357
+ }
358
+ }
359
+
360
+ static int
361
+ typecode_from_char(char c)
362
+ {
363
+ switch (c) {
364
+ case 'b': return NPY_BOOL;
365
+ case 'i': return NPY_INT;
366
+ case 'l': return NPY_LONGLONG;
367
+ case 'f': return NPY_FLOAT;
368
+ case 'd': return NPY_DOUBLE;
369
+ case 'c': return NPY_CDOUBLE;
370
+ case 's': return NPY_STRING;
371
+ default:
372
+ PyErr_SetString(PyExc_TypeError, "signature value not in 'bilfdcs'");
373
+ return -1;
374
+ }
375
+ }
376
+
377
+ static int
378
+ last_opcode(PyObject *program_object) {
379
+ Py_ssize_t n;
380
+ unsigned char *program;
381
+ PyBytes_AsStringAndSize(program_object, (char **)&program, &n);
382
+ return program[n-4];
383
+ }
384
+
385
+ static int
386
+ get_reduction_axis(PyObject* program) {
387
+ Py_ssize_t end = PyBytes_Size(program);
388
+ int axis = ((unsigned char *)PyBytes_AS_STRING(program))[end-1];
389
+ if (axis != 255 && axis >= NPY_MAXDIMS)
390
+ axis = NPY_MAXDIMS - axis;
391
+ return axis;
392
+ }
393
+
394
+
395
+
396
+ int
397
+ check_program(NumExprObject *self)
398
+ {
399
+ unsigned char *program;
400
+ Py_ssize_t prog_len, n_buffers, n_inputs;
401
+ int pc, arg, argloc, argno, sig;
402
+ char *fullsig, *signature;
403
+
404
+ if (PyBytes_AsStringAndSize(self->program, (char **)&program,
405
+ &prog_len) < 0) {
406
+ PyErr_Format(PyExc_RuntimeError, "invalid program: can't read program");
407
+ return -1;
408
+ }
409
+ if (prog_len % 4 != 0) {
410
+ PyErr_Format(PyExc_RuntimeError, "invalid program: prog_len mod 4 != 0");
411
+ return -1;
412
+ }
413
+ if (PyBytes_AsStringAndSize(self->fullsig, (char **)&fullsig,
414
+ &n_buffers) < 0) {
415
+ PyErr_Format(PyExc_RuntimeError, "invalid program: can't read fullsig");
416
+ return -1;
417
+ }
418
+ if (PyBytes_AsStringAndSize(self->signature, (char **)&signature,
419
+ &n_inputs) < 0) {
420
+ PyErr_Format(PyExc_RuntimeError, "invalid program: can't read signature");
421
+ return -1;
422
+ }
423
+ if (n_buffers > 255) {
424
+ PyErr_Format(PyExc_RuntimeError, "invalid program: too many buffers");
425
+ return -1;
426
+ }
427
+ for (pc = 0; pc < prog_len; pc += 4) {
428
+ unsigned int op = program[pc];
429
+ if (op == OP_NOOP) {
430
+ continue;
431
+ }
432
+ if ((op >= OP_REDUCTION) && pc != prog_len-4) {
433
+ PyErr_Format(PyExc_RuntimeError,
434
+ "invalid program: reduction operations must occur last");
435
+ return -1;
436
+ }
437
+ for (argno = 0; ; argno++) {
438
+ sig = op_signature(op, argno);
439
+ if (sig == -1) {
440
+ PyErr_Format(PyExc_RuntimeError, "invalid program: illegal opcode at %i (%d)", pc, op);
441
+ return -1;
442
+ }
443
+ if (sig == 0) break;
444
+ if (argno < 3) {
445
+ argloc = pc+argno+1;
446
+ }
447
+ if (argno >= 3) {
448
+ if (pc + 1 >= prog_len) {
449
+ PyErr_Format(PyExc_RuntimeError, "invalid program: double opcode (%c) at end (%i)", pc, sig);
450
+ return -1;
451
+ }
452
+ argloc = pc+argno+2;
453
+ }
454
+ arg = program[argloc];
455
+
456
+ if (sig != 'n' && ((arg >= n_buffers) || (arg < 0))) {
457
+ PyErr_Format(PyExc_RuntimeError, "invalid program: buffer out of range (%i) at %i", arg, argloc);
458
+ return -1;
459
+ }
460
+ if (sig == 'n') {
461
+ if (op == OP_FUNC_FFN) {
462
+ if (arg < 0 || arg >= FUNC_FF_LAST) {
463
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
464
+ return -1;
465
+ }
466
+ } else if (op == OP_FUNC_FFFN) {
467
+ if (arg < 0 || arg >= FUNC_FFF_LAST) {
468
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
469
+ return -1;
470
+ }
471
+ } else if (op == OP_FUNC_DDN) {
472
+ if (arg < 0 || arg >= FUNC_DD_LAST) {
473
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
474
+ return -1;
475
+ }
476
+ } else if (op == OP_FUNC_DDDN) {
477
+ if (arg < 0 || arg >= FUNC_DDD_LAST) {
478
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
479
+ return -1;
480
+ }
481
+ } else if (op == OP_FUNC_CCN) {
482
+ if (arg < 0 || arg >= FUNC_CC_LAST) {
483
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
484
+ return -1;
485
+ }
486
+ } else if (op == OP_FUNC_CCCN) {
487
+ if (arg < 0 || arg >= FUNC_CCC_LAST) {
488
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
489
+ return -1;
490
+ }
491
+ }
492
+ else if (op == OP_FUNC_BDN) {
493
+ if (arg < 0 || arg >= FUNC_BD_LAST) {
494
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
495
+ return -1;
496
+ }
497
+ }
498
+ else if (op == OP_FUNC_BFN) {
499
+ if (arg < 0 || arg >= FUNC_BF_LAST) {
500
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
501
+ return -1;
502
+ }
503
+ }
504
+ else if (op == OP_FUNC_BCN) {
505
+ if (arg < 0 || arg >= FUNC_BC_LAST) {
506
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
507
+ return -1;
508
+ }
509
+ }
510
+ else if (op == OP_FUNC_IIN) {
511
+ if (arg < 0 || arg >= FUNC_II_LAST) {
512
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
513
+ return -1;
514
+ }
515
+ }
516
+ else if (op == OP_FUNC_LLN) {
517
+ if (arg < 0 || arg >= FUNC_LL_LAST) {
518
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
519
+ return -1;
520
+ }
521
+ }
522
+ else if (op >= OP_REDUCTION) {
523
+ ;
524
+ } else {
525
+ PyErr_Format(PyExc_RuntimeError, "invalid program: internal checker error processing %i", argloc);
526
+ return -1;
527
+ }
528
+ /* The next is to avoid problems with the ('i','l') duality,
529
+ specially in 64-bit platforms */
530
+ } else if (((sig == 'l') && (fullsig[arg] == 'i')) ||
531
+ ((sig == 'i') && (fullsig[arg] == 'l'))) {
532
+ ;
533
+ } else if (sig != fullsig[arg]) {
534
+ PyErr_Format(PyExc_RuntimeError,
535
+ "invalid : opcode signature doesn't match buffer (%c vs %c) at %i", sig, fullsig[arg], argloc);
536
+ return -1;
537
+ }
538
+ }
539
+ }
540
+ return 0;
541
+ }
542
+
543
+
544
+
545
+
546
+ struct index_data {
547
+ int count;
548
+ int size;
549
+ int findex;
550
+ npy_intp *shape;
551
+ npy_intp *strides;
552
+ int *index;
553
+ char *buffer;
554
+ };
555
+
556
+ // BOUNDS_CHECK is used in interp_body.cpp
557
+ #define DO_BOUNDS_CHECK 1
558
+
559
+ #if DO_BOUNDS_CHECK
560
+ #define BOUNDS_CHECK(arg) if ((arg) >= params.r_end) { \
561
+ *pc_error = pc; \
562
+ return -2; \
563
+ }
564
+ #else
565
+ #define BOUNDS_CHECK(arg)
566
+ #endif
567
+
568
+ int
569
+ stringcmp(const char *s1, const char *s2, npy_intp maxlen1, npy_intp maxlen2)
570
+ {
571
+ npy_intp maxlen, nextpos;
572
+ /* Point to this when the end of a string is found,
573
+ to simulate infinte trailing NULL characters. */
574
+ const char null = 0;
575
+
576
+ // First check if some of the operands is the empty string and if so,
577
+ // just check that the first char of the other is the NULL one.
578
+ // Fixes #121
579
+ if (maxlen2 == 0) return *s1 != null;
580
+ if (maxlen1 == 0) return *s2 != null;
581
+
582
+ maxlen = (maxlen1 > maxlen2) ? maxlen1 : maxlen2;
583
+ for (nextpos = 1; nextpos <= maxlen; nextpos++) {
584
+ if (*s1 < *s2)
585
+ return -1;
586
+ if (*s1 > *s2)
587
+ return +1;
588
+ s1 = (nextpos >= maxlen1) ? &null : s1+1;
589
+ s2 = (nextpos >= maxlen2) ? &null : s2+1;
590
+ }
591
+ return 0;
592
+ }
593
+
594
+
595
+ /* contains(str1, str2) function for string columns.
596
+
597
+ Based on Newlib/strstr.c. */
598
+
599
+ int
600
+ stringcontains(const char *haystack_start, const char *needle_start, npy_intp max_haystack_len, npy_intp max_needle_len)
601
+ {
602
+ // needle_len - Length of needle.
603
+ // haystack_len - Known minimum length of haystack.
604
+ size_t needle_len = (size_t)max_needle_len;
605
+ size_t haystack_len = (size_t)max_haystack_len;
606
+
607
+ const char *haystack = haystack_start;
608
+ const char *needle = needle_start;
609
+ bool ok = true; /* needle is prefix of haystack. */
610
+ char *res;
611
+
612
+ size_t si = 0;
613
+ size_t min_len = min(needle_len, haystack_len);
614
+ while (si < min_len && *haystack && *needle)
615
+ {
616
+ ok &= *haystack++ == *needle++;
617
+ si++;
618
+ }
619
+
620
+ /* check needle is prefix of haystack and calc needle length */
621
+ if (si == needle_len || *needle == 0) {
622
+ if (ok)
623
+ return 1;
624
+ needle_len = si;
625
+ } else {
626
+ /* haystack less needle */
627
+ return 0;
628
+ }
629
+
630
+ /* calc haystack length */
631
+ while (si < haystack_len && *haystack) {
632
+ haystack++;
633
+ si++;
634
+ }
635
+ haystack_len = si;
636
+
637
+ if (needle_len < LONG_NEEDLE_THRESHOLD)
638
+ {
639
+ res = two_way_short_needle((const unsigned char *)haystack_start, haystack_len,
640
+ (const unsigned char *)needle_start, needle_len);
641
+ } else {
642
+ res = two_way_long_needle((const unsigned char *)haystack_start, haystack_len,
643
+ (const unsigned char *)needle_start, needle_len);
644
+ }
645
+ return res != NULL ? 1 : 0;
646
+ }
647
+
648
+
649
+ /* Get space for VM temporary registers */
650
+ int get_temps_space(const vm_params& params, char **mem, size_t block_size)
651
+ {
652
+ int r, k = 1 + params.n_inputs + params.n_constants;
653
+
654
+ for (r = k; r < k + params.n_temps; r++) {
655
+ mem[r] = (char *)malloc(block_size * params.memsizes[r]);
656
+ if (mem[r] == NULL) {
657
+ return -1;
658
+ }
659
+ }
660
+ return 0;
661
+ }
662
+
663
+ /* Free space for VM temporary registers */
664
+ void free_temps_space(const vm_params& params, char **mem)
665
+ {
666
+ int r, k = 1 + params.n_inputs + params.n_constants;
667
+
668
+ for (r = k; r < k + params.n_temps; r++) {
669
+ free(mem[r]);
670
+ }
671
+ }
672
+
673
+ /* Serial/parallel task iterator version of the VM engine */
674
+ int vm_engine_iter_task(NpyIter *iter, npy_intp *memsteps,
675
+ const vm_params& params,
676
+ int *pc_error, char **errmsg)
677
+ {
678
+ char **mem = params.mem;
679
+ NpyIter_IterNextFunc *iternext;
680
+ npy_intp block_size, *size_ptr;
681
+ char **iter_dataptr;
682
+ npy_intp *iter_strides;
683
+
684
+ iternext = NpyIter_GetIterNext(iter, errmsg);
685
+ if (iternext == NULL) {
686
+ return -1;
687
+ }
688
+
689
+ size_ptr = NpyIter_GetInnerLoopSizePtr(iter);
690
+ iter_dataptr = NpyIter_GetDataPtrArray(iter);
691
+ iter_strides = NpyIter_GetInnerStrideArray(iter);
692
+
693
+ /*
694
+ * First do all the blocks with a compile-time fixed size.
695
+ * This makes a big difference (30-50% on some tests).
696
+ */
697
+ block_size = *size_ptr;
698
+ while (block_size == BLOCK_SIZE1) {
699
+ #define REDUCTION_INNER_LOOP
700
+ #define BLOCK_SIZE BLOCK_SIZE1
701
+ #include "interp_body.cpp"
702
+ #undef BLOCK_SIZE
703
+ #undef REDUCTION_INNER_LOOP
704
+ iternext(iter);
705
+ block_size = *size_ptr;
706
+ }
707
+
708
+ /* Then finish off the rest */
709
+ if (block_size > 0) do {
710
+ block_size = *size_ptr;
711
+ #define REDUCTION_INNER_LOOP
712
+ #define BLOCK_SIZE block_size
713
+ #include "interp_body.cpp"
714
+ #undef BLOCK_SIZE
715
+ #undef REDUCTION_INNER_LOOP
716
+ } while (iternext(iter));
717
+
718
+ return 0;
719
+ }
720
+
721
+ static int
722
+ vm_engine_iter_outer_reduce_task(NpyIter *iter, npy_intp *memsteps,
723
+ const vm_params& params, int *pc_error, char **errmsg)
724
+ {
725
+ char **mem = params.mem;
726
+ NpyIter_IterNextFunc *iternext;
727
+ npy_intp block_size, *size_ptr;
728
+ char **iter_dataptr;
729
+ npy_intp *iter_strides;
730
+
731
+ iternext = NpyIter_GetIterNext(iter, errmsg);
732
+ if (iternext == NULL) {
733
+ return -1;
734
+ }
735
+
736
+ size_ptr = NpyIter_GetInnerLoopSizePtr(iter);
737
+ iter_dataptr = NpyIter_GetDataPtrArray(iter);
738
+ iter_strides = NpyIter_GetInnerStrideArray(iter);
739
+
740
+ /*
741
+ * First do all the blocks with a compile-time fixed size.
742
+ * This makes a big difference (30-50% on some tests).
743
+ */
744
+ block_size = *size_ptr;
745
+ while (block_size == BLOCK_SIZE1) {
746
+ #define BLOCK_SIZE BLOCK_SIZE1
747
+ #define NO_OUTPUT_BUFFERING // Because it's a reduction
748
+ #include "interp_body.cpp"
749
+ #undef NO_OUTPUT_BUFFERING
750
+ #undef BLOCK_SIZE
751
+ iternext(iter);
752
+ block_size = *size_ptr;
753
+ }
754
+
755
+ /* Then finish off the rest */
756
+ if (block_size > 0) do {
757
+ block_size = *size_ptr;
758
+ #define BLOCK_SIZE block_size
759
+ #define NO_OUTPUT_BUFFERING // Because it's a reduction
760
+ #include "interp_body.cpp"
761
+ #undef NO_OUTPUT_BUFFERING
762
+ #undef BLOCK_SIZE
763
+ } while (iternext(iter));
764
+
765
+ return 0;
766
+ }
767
+
768
+ /* Parallel iterator version of VM engine */
769
+ static int
770
+ vm_engine_iter_parallel(NpyIter *iter, const vm_params& params,
771
+ bool need_output_buffering, int *pc_error,
772
+ char **errmsg)
773
+ {
774
+ int i, ret = -1;
775
+ npy_intp numblocks, taskfactor;
776
+
777
+ if (errmsg == NULL) {
778
+ return -1;
779
+ }
780
+
781
+ /* Ensure only one parallel job is running at a time (otherwise
782
+ the global th_params get corrupted). */
783
+ Py_BEGIN_ALLOW_THREADS;
784
+ pthread_mutex_lock(&gs.parallel_mutex);
785
+ Py_END_ALLOW_THREADS;
786
+
787
+ /* Populate parameters for worker threads */
788
+ NpyIter_GetIterIndexRange(iter, &th_params.start, &th_params.vlen);
789
+ /*
790
+ * Try to make it so each thread gets 16 tasks. This is a compromise
791
+ * between 1 task per thread and one block per task.
792
+ */
793
+ taskfactor = 16*BLOCK_SIZE1*gs.nthreads;
794
+ numblocks = (th_params.vlen - th_params.start + taskfactor - 1) /
795
+ taskfactor;
796
+ th_params.block_size = numblocks * BLOCK_SIZE1;
797
+
798
+ th_params.params = params;
799
+ th_params.need_output_buffering = need_output_buffering;
800
+ th_params.ret_code = 0;
801
+ th_params.pc_error = pc_error;
802
+ th_params.errmsg = errmsg;
803
+ th_params.iter[0] = iter;
804
+ /* Make one copy for each additional thread */
805
+ for (i = 1; i < gs.nthreads; ++i) {
806
+ th_params.iter[i] = NpyIter_Copy(iter);
807
+ if (th_params.iter[i] == NULL) {
808
+ --i;
809
+ for (; i > 0; --i) {
810
+ NpyIter_Deallocate(th_params.iter[i]);
811
+ }
812
+ goto end;
813
+ }
814
+ }
815
+ th_params.memsteps[0] = params.memsteps;
816
+ /* Make one copy of memsteps for each additional thread */
817
+ for (i = 1; i < gs.nthreads; ++i) {
818
+ th_params.memsteps[i] = PyMem_New(npy_intp,
819
+ 1 + params.n_inputs + params.n_constants + params.n_temps);
820
+ if (th_params.memsteps[i] == NULL) {
821
+ --i;
822
+ for (; i > 0; --i) {
823
+ PyMem_Del(th_params.memsteps[i]);
824
+ }
825
+ for (i = 0; i < gs.nthreads; ++i) {
826
+ NpyIter_Deallocate(th_params.iter[i]);
827
+ }
828
+ goto end;
829
+ }
830
+ memcpy(th_params.memsteps[i], th_params.memsteps[0],
831
+ sizeof(npy_intp) *
832
+ (1 + params.n_inputs + params.n_constants + params.n_temps));
833
+ }
834
+
835
+ Py_BEGIN_ALLOW_THREADS;
836
+
837
+ /* Synchronization point for all threads (wait for initialization) */
838
+ pthread_mutex_lock(&gs.count_threads_mutex);
839
+ if (gs.count_threads < gs.nthreads) {
840
+ gs.count_threads++;
841
+ /* Beware of spurious wakeups. See issue pydata/numexpr#306. */
842
+ do {
843
+ pthread_cond_wait(&gs.count_threads_cv, &gs.count_threads_mutex);
844
+ } while (!gs.barrier_passed);
845
+ }
846
+ else {
847
+ gs.barrier_passed = 1;
848
+ pthread_cond_broadcast(&gs.count_threads_cv);
849
+ }
850
+ pthread_mutex_unlock(&gs.count_threads_mutex);
851
+
852
+ /* Synchronization point for all threads (wait for finalization) */
853
+ pthread_mutex_lock(&gs.count_threads_mutex);
854
+ if (gs.count_threads > 0) {
855
+ gs.count_threads--;
856
+ do {
857
+ pthread_cond_wait(&gs.count_threads_cv, &gs.count_threads_mutex);
858
+ } while (gs.barrier_passed);
859
+ }
860
+ else {
861
+ gs.barrier_passed = 0;
862
+ pthread_cond_broadcast(&gs.count_threads_cv);
863
+ }
864
+ pthread_mutex_unlock(&gs.count_threads_mutex);
865
+
866
+ Py_END_ALLOW_THREADS;
867
+
868
+ /* Deallocate all the iterator and memsteps copies */
869
+ for (i = 1; i < gs.nthreads; ++i) {
870
+ NpyIter_Deallocate(th_params.iter[i]);
871
+ PyMem_Del(th_params.memsteps[i]);
872
+ }
873
+
874
+ ret = th_params.ret_code;
875
+
876
+ end:
877
+ pthread_mutex_unlock(&gs.parallel_mutex);
878
+ return ret;
879
+ }
880
+
881
+ static int
882
+ run_interpreter(NumExprObject *self, NpyIter *iter, NpyIter *reduce_iter,
883
+ bool reduction_outer_loop, bool need_output_buffering,
884
+ int *pc_error)
885
+ {
886
+ int r;
887
+ Py_ssize_t plen;
888
+ vm_params params;
889
+ char *errmsg = NULL;
890
+
891
+ *pc_error = -1;
892
+ if (PyBytes_AsStringAndSize(self->program, (char **)&(params.program),
893
+ &plen) < 0) {
894
+ return -1;
895
+ }
896
+
897
+ params.prog_len = (int)plen;
898
+ params.output = NULL;
899
+ params.inputs = NULL;
900
+ params.index_data = NULL;
901
+ params.n_inputs = self->n_inputs;
902
+ params.n_constants = self->n_constants;
903
+ params.n_temps = self->n_temps;
904
+ params.mem = self->mem;
905
+ params.memsteps = self->memsteps;
906
+ params.memsizes = self->memsizes;
907
+ params.r_end = (int)PyBytes_Size(self->fullsig);
908
+ params.out_buffer = NULL;
909
+
910
+ if ((gs.nthreads == 1) || gs.force_serial) {
911
+ // Can do it as one "task"
912
+ if (reduce_iter == NULL) {
913
+ // Allocate memory for output buffering if needed
914
+ vector<char> out_buffer(need_output_buffering ?
915
+ (self->memsizes[0] * BLOCK_SIZE1) : 0);
916
+ params.out_buffer = need_output_buffering ? &out_buffer[0] : NULL;
917
+ // Reset the iterator to allocate its buffers
918
+ if(NpyIter_Reset(iter, NULL) != NPY_SUCCEED) {
919
+ return -1;
920
+ }
921
+ get_temps_space(params, params.mem, BLOCK_SIZE1);
922
+ Py_BEGIN_ALLOW_THREADS;
923
+ r = vm_engine_iter_task(iter, params.memsteps,
924
+ params, pc_error, &errmsg);
925
+ Py_END_ALLOW_THREADS;
926
+ free_temps_space(params, params.mem);
927
+ }
928
+ else {
929
+ if (reduction_outer_loop) {
930
+ char **dataptr;
931
+ NpyIter_IterNextFunc *iternext;
932
+
933
+ dataptr = NpyIter_GetDataPtrArray(reduce_iter);
934
+ iternext = NpyIter_GetIterNext(reduce_iter, NULL);
935
+ if (iternext == NULL) {
936
+ return -1;
937
+ }
938
+
939
+ get_temps_space(params, params.mem, BLOCK_SIZE1);
940
+ Py_BEGIN_ALLOW_THREADS;
941
+ do {
942
+ r = NpyIter_ResetBasePointers(iter, dataptr, &errmsg);
943
+ if (r >= 0) {
944
+ r = vm_engine_iter_outer_reduce_task(iter,
945
+ params.memsteps, params,
946
+ pc_error, &errmsg);
947
+ }
948
+ if (r < 0) {
949
+ break;
950
+ }
951
+ } while (iternext(reduce_iter));
952
+ Py_END_ALLOW_THREADS;
953
+ free_temps_space(params, params.mem);
954
+ }
955
+ else {
956
+ char **dataptr;
957
+ NpyIter_IterNextFunc *iternext;
958
+
959
+ dataptr = NpyIter_GetDataPtrArray(iter);
960
+ iternext = NpyIter_GetIterNext(iter, NULL);
961
+ if (iternext == NULL) {
962
+ return -1;
963
+ }
964
+
965
+ get_temps_space(params, params.mem, BLOCK_SIZE1);
966
+ Py_BEGIN_ALLOW_THREADS;
967
+ do {
968
+ r = NpyIter_ResetBasePointers(reduce_iter, dataptr,
969
+ &errmsg);
970
+ if (r >= 0) {
971
+ r = vm_engine_iter_task(reduce_iter, params.memsteps,
972
+ params, pc_error, &errmsg);
973
+ }
974
+ if (r < 0) {
975
+ break;
976
+ }
977
+ } while (iternext(iter));
978
+ Py_END_ALLOW_THREADS;
979
+ free_temps_space(params, params.mem);
980
+ }
981
+ }
982
+ }
983
+ else {
984
+ if (reduce_iter == NULL) {
985
+ r = vm_engine_iter_parallel(iter, params, need_output_buffering,
986
+ pc_error, &errmsg);
987
+ }
988
+ else {
989
+ errmsg = (char *) "Parallel engine doesn't support reduction yet";
990
+ r = -1;
991
+ }
992
+ }
993
+
994
+ if (r < 0 && errmsg != NULL) {
995
+ PyErr_SetString(PyExc_RuntimeError, errmsg);
996
+ }
997
+
998
+ return r;
999
+ }
1000
+
1001
+ static int
1002
+ run_interpreter_const(NumExprObject *self, char *output, int *pc_error)
1003
+ {
1004
+ vm_params params;
1005
+ Py_ssize_t plen;
1006
+ char **mem;
1007
+ npy_intp *memsteps;
1008
+
1009
+ *pc_error = -1;
1010
+ if (PyBytes_AsStringAndSize(self->program, (char **)&(params.program),
1011
+ &plen) < 0) {
1012
+ return -1;
1013
+ }
1014
+ if (self->n_inputs != 0) {
1015
+ return -1;
1016
+ }
1017
+ params.prog_len = (int)plen;
1018
+ params.output = output;
1019
+ params.inputs = NULL;
1020
+ params.index_data = NULL;
1021
+ params.n_inputs = self->n_inputs;
1022
+ params.n_constants = self->n_constants;
1023
+ params.n_temps = self->n_temps;
1024
+ params.mem = self->mem;
1025
+ memsteps = self->memsteps;
1026
+ params.memsizes = self->memsizes;
1027
+ params.r_end = (int)PyBytes_Size(self->fullsig);
1028
+
1029
+ mem = params.mem;
1030
+ get_temps_space(params, mem, 1);
1031
+ #define SINGLE_ITEM_CONST_LOOP
1032
+ #define BLOCK_SIZE 1
1033
+ #define NO_OUTPUT_BUFFERING // Because it's constant
1034
+ #include "interp_body.cpp"
1035
+ #undef NO_OUTPUT_BUFFERING
1036
+ #undef BLOCK_SIZE
1037
+ #undef SINGLE_ITEM_CONST_LOOP
1038
+ free_temps_space(params, mem);
1039
+
1040
+ return 0;
1041
+ }
1042
+
1043
+ PyObject *
1044
+ NumExpr_run(NumExprObject *self, PyObject *args, PyObject *kwds)
1045
+ {
1046
+ PyArrayObject *operands[NE_MAXARGS];
1047
+ PyArray_Descr *dtypes[NE_MAXARGS], **dtypes_tmp;
1048
+ PyObject *tmp, *ret;
1049
+ npy_uint32 op_flags[NE_MAXARGS];
1050
+ NPY_CASTING casting = NPY_SAFE_CASTING;
1051
+ NPY_ORDER order = NPY_KEEPORDER;
1052
+ unsigned int i, n_inputs;
1053
+ int r, pc_error = 0;
1054
+ int reduction_axis = -1;
1055
+ npy_intp reduction_size = -1; // For #277 change this 1 -> -1 to be in-line with NumPy 1.8,
1056
+ #ifdef USE_VML
1057
+ int ex_uses_vml = 0;
1058
+ #endif
1059
+ int is_reduction = 0;
1060
+ bool reduction_outer_loop = false, need_output_buffering = false, full_reduction = false;
1061
+
1062
+ // To specify axes when doing a reduction
1063
+ int op_axes_values[NE_MAXARGS][NPY_MAXDIMS],
1064
+ op_axes_reduction_values[NE_MAXARGS];
1065
+ int *op_axes_ptrs[NPY_MAXDIMS];
1066
+ int oa_ndim = 0;
1067
+ int **op_axes = NULL;
1068
+
1069
+ NpyIter *iter = NULL, *reduce_iter = NULL;
1070
+
1071
+ // Check whether we need to restart threads
1072
+ if (!gs.init_threads_done || gs.pid != getpid()) {
1073
+ numexpr_set_nthreads(gs.nthreads);
1074
+ }
1075
+
1076
+ // Don't force serial mode by default
1077
+ gs.force_serial = 0;
1078
+
1079
+ // Check whether there's a reduction as the final step
1080
+ is_reduction = last_opcode(self->program) > OP_REDUCTION;
1081
+
1082
+ n_inputs = (int)PyTuple_Size(args);
1083
+ if (PyBytes_Size(self->signature) != n_inputs) {
1084
+ return PyErr_Format(PyExc_ValueError,
1085
+ "number of inputs doesn't match program");
1086
+ }
1087
+ else if (n_inputs+1 > NPY_MAXARGS) {
1088
+ return PyErr_Format(PyExc_ValueError,
1089
+ "too many inputs");
1090
+ }
1091
+
1092
+ memset(operands, 0, sizeof(operands));
1093
+ memset(dtypes, 0, sizeof(dtypes));
1094
+
1095
+ if (kwds && PyDict_Size(kwds) > 0) {
1096
+ tmp = PyDict_GetItemString(kwds, "casting"); // borrowed ref
1097
+ if (tmp != NULL && !PyArray_CastingConverter(tmp, &casting)) {
1098
+ return NULL;
1099
+ }
1100
+ tmp = PyDict_GetItemString(kwds, "order"); // borrowed ref
1101
+ if (tmp != NULL && !PyArray_OrderConverter(tmp, &order)) {
1102
+ return NULL;
1103
+ }
1104
+ tmp = PyDict_GetItemString(kwds, "ex_uses_vml"); // borrowed ref
1105
+ if (tmp == NULL) {
1106
+ return PyErr_Format(PyExc_ValueError,
1107
+ "ex_uses_vml parameter is required");
1108
+ }
1109
+ #ifdef USE_VML
1110
+ if (tmp == Py_True) {
1111
+ ex_uses_vml = 1;
1112
+ }
1113
+ #endif
1114
+ // borrowed ref
1115
+ operands[0] = (PyArrayObject *)PyDict_GetItemString(kwds, "out");
1116
+ if (operands[0] != NULL) {
1117
+ if ((PyObject *)operands[0] == Py_None) {
1118
+ operands[0] = NULL;
1119
+ }
1120
+ else if (!PyArray_Check(operands[0])) {
1121
+ return PyErr_Format(PyExc_ValueError,
1122
+ "out keyword parameter is not an array");
1123
+ }
1124
+ else {
1125
+ Py_INCREF(operands[0]);
1126
+ }
1127
+ }
1128
+ }
1129
+
1130
+ for (i = 0; i < n_inputs; i++) {
1131
+ PyObject *o = PyTuple_GET_ITEM(args, i); // borrowed ref
1132
+ PyObject *a;
1133
+ char c = PyBytes_AS_STRING(self->signature)[i];
1134
+ int typecode = typecode_from_char(c);
1135
+ // Convert it if it's not an array
1136
+ if (!PyArray_Check(o)) {
1137
+ if (typecode == -1) goto fail;
1138
+ a = PyArray_FROM_OTF(o, typecode, NPY_ARRAY_NOTSWAPPED);
1139
+ }
1140
+ else {
1141
+ Py_INCREF(o);
1142
+ a = o;
1143
+ }
1144
+ operands[i+1] = (PyArrayObject *)a;
1145
+ dtypes[i+1] = PyArray_DescrFromType(typecode);
1146
+
1147
+ if (operands[0] != NULL) {
1148
+ // Check for the case where "out" is one of the inputs
1149
+ // TODO: Probably should deal with the general overlap case,
1150
+ // but NumPy ufuncs don't do that yet either.
1151
+ if (PyArray_DATA(operands[0]) == PyArray_DATA(operands[i+1])) {
1152
+ need_output_buffering = true;
1153
+ }
1154
+ }
1155
+
1156
+ if (operands[i+1] == NULL || dtypes[i+1] == NULL) {
1157
+ goto fail;
1158
+ }
1159
+ op_flags[i+1] = NPY_ITER_READONLY|
1160
+ #ifdef USE_VML
1161
+ (ex_uses_vml ? (NPY_ITER_CONTIG|NPY_ITER_ALIGNED) : 0)|
1162
+ #endif
1163
+ #ifndef USE_UNALIGNED_ACCESS
1164
+ NPY_ITER_ALIGNED|
1165
+ #endif
1166
+ NPY_ITER_NBO
1167
+ ;
1168
+ }
1169
+
1170
+ if (is_reduction) {
1171
+ // A reduction can not result in a string,
1172
+ // so we don't need to worry about item sizes here.
1173
+ char retsig = get_return_sig(self->program);
1174
+ reduction_axis = get_reduction_axis(self->program);
1175
+
1176
+ // Need to set up op_axes for the non-reduction part
1177
+ if (reduction_axis != 255) {
1178
+ // Get the number of broadcast dimensions
1179
+ for (i = 0; i < n_inputs; ++i) {
1180
+ int ndim = PyArray_NDIM(operands[i+1]);
1181
+ if (ndim > oa_ndim) {
1182
+ oa_ndim = ndim;
1183
+ }
1184
+ }
1185
+ if (reduction_axis < 0 || reduction_axis >= oa_ndim) {
1186
+ PyErr_Format(PyExc_ValueError,
1187
+ "reduction axis is out of bounds");
1188
+ goto fail;
1189
+ }
1190
+ // Fill in the op_axes
1191
+ op_axes_ptrs[0] = NULL;
1192
+ op_axes_reduction_values[0] = -1;
1193
+ for (i = 0; i < n_inputs; ++i) {
1194
+ int j = 0, idim, ndim = PyArray_NDIM(operands[i+1]);
1195
+ for (idim = 0; idim < oa_ndim-ndim; ++idim) {
1196
+ if (idim != reduction_axis) {
1197
+ op_axes_values[i+1][j++] = -1;
1198
+ }
1199
+ else {
1200
+ op_axes_reduction_values[i+1] = -1;
1201
+ }
1202
+ }
1203
+ for (idim = oa_ndim-ndim; idim < oa_ndim; ++idim) {
1204
+ if (idim != reduction_axis) {
1205
+ op_axes_values[i+1][j++] = idim-(oa_ndim-ndim);
1206
+ }
1207
+ else {
1208
+ npy_intp size = PyArray_DIM(operands[i+1],
1209
+ idim-(oa_ndim-ndim));
1210
+ if (size > reduction_size) {
1211
+ reduction_size = size;
1212
+ }
1213
+ op_axes_reduction_values[i+1] = idim-(oa_ndim-ndim);
1214
+ }
1215
+ }
1216
+ op_axes_ptrs[i+1] = op_axes_values[i+1];
1217
+ }
1218
+ // op_axes has one less than the broadcast dimensions
1219
+ --oa_ndim;
1220
+ if (oa_ndim > 0) {
1221
+ op_axes = op_axes_ptrs;
1222
+ }
1223
+ else {
1224
+ reduction_size = 1;
1225
+ }
1226
+ }
1227
+ // A full reduction can be done without nested iteration
1228
+ if (oa_ndim == 0) {
1229
+ full_reduction = true;
1230
+ if (operands[0] == NULL) {
1231
+ npy_intp dim = 1;
1232
+ operands[0] = (PyArrayObject *)PyArray_SimpleNew(0, &dim,
1233
+ typecode_from_char(retsig));
1234
+ if (!operands[0])
1235
+ goto fail;
1236
+ } else if (PyArray_SIZE(operands[0]) != 1) {
1237
+ PyErr_Format(PyExc_ValueError,
1238
+ "out argument must have size 1 for a full reduction");
1239
+ goto fail;
1240
+ }
1241
+ }
1242
+
1243
+ dtypes[0] = PyArray_DescrFromType(typecode_from_char(retsig));
1244
+
1245
+ op_flags[0] = NPY_ITER_READWRITE|
1246
+ NPY_ITER_ALLOCATE|
1247
+ // Copy, because it can't buffer the reduction
1248
+ NPY_ITER_UPDATEIFCOPY|
1249
+ NPY_ITER_NBO|
1250
+ #ifndef USE_UNALIGNED_ACCESS
1251
+ NPY_ITER_ALIGNED|
1252
+ #endif
1253
+ (oa_ndim == 0 ? 0 : NPY_ITER_NO_BROADCAST);
1254
+ }
1255
+ else {
1256
+ char retsig = get_return_sig(self->program);
1257
+ if (retsig != 's') {
1258
+ dtypes[0] = PyArray_DescrFromType(typecode_from_char(retsig));
1259
+ } else {
1260
+ /* Since the *only* supported operation returning a string
1261
+ * is a copy, the size of returned strings
1262
+ * can be directly gotten from the first (and only)
1263
+ * input/constant/temporary. */
1264
+ if (n_inputs > 0) { // input, like in 'a' where a -> 'foo'
1265
+ dtypes[0] = PyArray_DESCR(operands[1]);
1266
+ Py_INCREF(dtypes[0]);
1267
+ } else { // constant, like in '"foo"'
1268
+ dtypes[0] = PyArray_DescrNewFromType(NPY_STRING);
1269
+ PyDataType_SET_ELSIZE(dtypes[0], (npy_intp)self->memsizes[1]);
1270
+ } // no string temporaries, so no third case
1271
+ }
1272
+ if (dtypes[0] == NULL) {
1273
+ goto fail;
1274
+ }
1275
+ op_flags[0] = NPY_ITER_WRITEONLY|
1276
+ NPY_ITER_ALLOCATE|
1277
+ NPY_ITER_CONTIG|
1278
+ NPY_ITER_NBO|
1279
+ #ifndef USE_UNALIGNED_ACCESS
1280
+ NPY_ITER_ALIGNED|
1281
+ #endif
1282
+ NPY_ITER_NO_BROADCAST;
1283
+ }
1284
+
1285
+ // Check for empty arrays in expression
1286
+ if (n_inputs > 0) {
1287
+ char retsig = get_return_sig(self->program);
1288
+
1289
+ // Check length for all inputs
1290
+ int zeroi, zerolen = 0;
1291
+ for (i=0; i < n_inputs; i++) {
1292
+ if (PyArray_SIZE(operands[i+1]) == 0) {
1293
+ zerolen = 1;
1294
+ zeroi = i+1;
1295
+ break;
1296
+ }
1297
+ }
1298
+
1299
+ if (zerolen != 0) {
1300
+ // Allocate the output
1301
+ int ndim = PyArray_NDIM(operands[zeroi]);
1302
+ npy_intp *dims = PyArray_DIMS(operands[zeroi]);
1303
+ operands[0] = (PyArrayObject *)PyArray_SimpleNew(ndim, dims,
1304
+ typecode_from_char(retsig));
1305
+ if (operands[0] == NULL) {
1306
+ goto fail;
1307
+ }
1308
+
1309
+ ret = (PyObject *)operands[0];
1310
+ Py_INCREF(ret);
1311
+ goto cleanup_and_exit;
1312
+ }
1313
+ }
1314
+
1315
+
1316
+ /* A case with a single constant output */
1317
+ PyArrayObject *singleton;
1318
+ bool writeback;
1319
+ // NOTE: cannot assign on declaration due to `goto` statements
1320
+ singleton = NULL;
1321
+ writeback = false;
1322
+ if (n_inputs == 0) {
1323
+ char retsig = get_return_sig(self->program);
1324
+
1325
+ /* Allocate the output */
1326
+ if (operands[0] == NULL) {
1327
+ npy_intp dim = 1;
1328
+ operands[0] = (PyArrayObject *)PyArray_SimpleNew(0, &dim,
1329
+ typecode_from_char(retsig));
1330
+ if (operands[0] == NULL) {
1331
+ goto fail;
1332
+ }
1333
+ }
1334
+ else { // Use the provided output array
1335
+ if (PyArray_SIZE(operands[0]) != 1) {
1336
+ PyErr_SetString(PyExc_ValueError,
1337
+ "output for a constant expression must have size 1");
1338
+ goto fail;
1339
+ }
1340
+ else if (!PyArray_ISWRITEABLE(operands[0])) {
1341
+ PyErr_SetString(PyExc_ValueError,
1342
+ "output is not writeable");
1343
+ goto fail;
1344
+ }
1345
+ Py_INCREF(dtypes[0]);
1346
+
1347
+ // NumPy folks suggested using WRITEBACKIFCOPY to resolve issue #397
1348
+ singleton = (PyArrayObject *)PyArray_FromArray(operands[0], dtypes[0],
1349
+ NPY_ARRAY_ALIGNED|NPY_ARRAY_WRITEBACKIFCOPY);
1350
+ if (singleton == NULL) {
1351
+ goto fail;
1352
+ }
1353
+ writeback = true;
1354
+ Py_DECREF(operands[0]);
1355
+ operands[0] = singleton;
1356
+ }
1357
+
1358
+ r = run_interpreter_const(self, PyArray_BYTES(operands[0]), &pc_error);
1359
+
1360
+ if (writeback) {
1361
+ // Write-back our copy to the passed in output array if we had to make a copy
1362
+ // (which only happens if the input was not aligned)
1363
+ int retval = PyArray_ResolveWritebackIfCopy(singleton);
1364
+ if (retval < 0) {
1365
+ // 1 means it copied the value, 0 means no copy, only -1 is an error.
1366
+ PyErr_Format(PyExc_ValueError, "Writeback to singleton failed with error code: %d", retval);
1367
+ goto fail;
1368
+ }
1369
+ }
1370
+ ret = (PyObject *)operands[0];
1371
+ Py_INCREF(ret);
1372
+ goto cleanup_and_exit;
1373
+ }
1374
+
1375
+
1376
+ /* Allocate the iterator or nested iterators */
1377
+ if (reduction_size < 0 || full_reduction) {
1378
+ /* When there's no reduction, reduction_size is 1 as well */
1379
+ // RAM: in issue #277 this was also the case for reductions on arrays
1380
+ // with axis=0 having singleton dimension, i.e. such ops were interpreted
1381
+ // as full_reductions when they weren't in Numpy. As such, the default
1382
+ // reduction_size is now -1 and we add the flag for full_reduction,
1383
+ // e.g. ne.evaluate("sum(a)")"
1384
+ iter = NpyIter_AdvancedNew(n_inputs+1, operands,
1385
+ NPY_ITER_BUFFERED|
1386
+ NPY_ITER_REDUCE_OK|
1387
+ NPY_ITER_RANGED|
1388
+ NPY_ITER_DELAY_BUFALLOC|
1389
+ NPY_ITER_EXTERNAL_LOOP,
1390
+ order, casting,
1391
+ op_flags, dtypes,
1392
+ -1, NULL, NULL,
1393
+ BLOCK_SIZE1);
1394
+ if (iter == NULL) {
1395
+ goto fail;
1396
+ }
1397
+ } else {
1398
+ npy_uint32 op_flags_outer[NPY_MAXDIMS];
1399
+ /* The outer loop is unbuffered */
1400
+ op_flags_outer[0] = NPY_ITER_READWRITE|
1401
+ NPY_ITER_ALLOCATE|
1402
+ NPY_ITER_NO_BROADCAST;
1403
+ for (i = 0; i < n_inputs; ++i) {
1404
+ op_flags_outer[i+1] = NPY_ITER_READONLY;
1405
+ }
1406
+ /* Arbitrary threshold for which is the inner loop...benchmark? */
1407
+ if (reduction_size < 64) {
1408
+ reduction_outer_loop = true;
1409
+ iter = NpyIter_AdvancedNew(n_inputs+1, operands,
1410
+ NPY_ITER_BUFFERED|
1411
+ NPY_ITER_RANGED|
1412
+ NPY_ITER_DELAY_BUFALLOC|
1413
+ NPY_ITER_EXTERNAL_LOOP,
1414
+ order, casting,
1415
+ op_flags, dtypes,
1416
+ oa_ndim, op_axes, NULL,
1417
+ BLOCK_SIZE1);
1418
+ if (iter == NULL) {
1419
+ goto fail;
1420
+ }
1421
+
1422
+ /* If the output was allocated, get it for the second iterator */
1423
+ if (operands[0] == NULL) {
1424
+ operands[0] = NpyIter_GetOperandArray(iter)[0];
1425
+ Py_INCREF(operands[0]);
1426
+ }
1427
+
1428
+ op_axes[0] = &op_axes_reduction_values[0];
1429
+ for (i = 0; i < n_inputs; ++i) {
1430
+ op_axes[i+1] = &op_axes_reduction_values[i+1];
1431
+ }
1432
+ op_flags_outer[0] &= ~NPY_ITER_NO_BROADCAST;
1433
+ reduce_iter = NpyIter_AdvancedNew(n_inputs+1, operands,
1434
+ NPY_ITER_REDUCE_OK,
1435
+ order, casting,
1436
+ op_flags_outer, NULL,
1437
+ 1, op_axes, NULL,
1438
+ 0);
1439
+ if (reduce_iter == NULL) {
1440
+ goto fail;
1441
+ }
1442
+ }
1443
+ else {
1444
+ PyArray_Descr *dtypes_outer[NPY_MAXDIMS];
1445
+
1446
+ /* If the output is being allocated, need to specify its dtype */
1447
+ dtypes_outer[0] = dtypes[0];
1448
+ for (i = 0; i < n_inputs; ++i) {
1449
+ dtypes_outer[i+1] = NULL;
1450
+ }
1451
+ iter = NpyIter_AdvancedNew(n_inputs+1, operands,
1452
+ NPY_ITER_RANGED,
1453
+ order, casting,
1454
+ op_flags_outer, dtypes_outer,
1455
+ oa_ndim, op_axes, NULL,
1456
+ 0);
1457
+ if (iter == NULL) {
1458
+ goto fail;
1459
+ }
1460
+
1461
+ /* If the output was allocated, get it for the second iterator */
1462
+ if (operands[0] == NULL) {
1463
+ operands[0] = NpyIter_GetOperandArray(iter)[0];
1464
+ Py_INCREF(operands[0]);
1465
+ }
1466
+
1467
+ op_axes[0] = &op_axes_reduction_values[0];
1468
+ for (i = 0; i < n_inputs; ++i) {
1469
+ op_axes[i+1] = &op_axes_reduction_values[i+1];
1470
+ }
1471
+ op_flags[0] &= ~NPY_ITER_NO_BROADCAST;
1472
+ reduce_iter = NpyIter_AdvancedNew(n_inputs+1, operands,
1473
+ NPY_ITER_BUFFERED|
1474
+ NPY_ITER_REDUCE_OK|
1475
+ NPY_ITER_DELAY_BUFALLOC|
1476
+ NPY_ITER_EXTERNAL_LOOP,
1477
+ order, casting,
1478
+ op_flags, dtypes,
1479
+ 1, op_axes, NULL,
1480
+ BLOCK_SIZE1);
1481
+ if (reduce_iter == NULL) {
1482
+ goto fail;
1483
+ }
1484
+ }
1485
+ }
1486
+
1487
+ /* Initialize the output to the reduction unit */
1488
+ if (is_reduction) {
1489
+ PyArrayObject *a = NpyIter_GetOperandArray(iter)[0];
1490
+ PyObject *fill;
1491
+ int op = last_opcode(self->program);
1492
+ if (op < OP_PROD) {
1493
+ /* sum identity is 0 */
1494
+ fill = PyLong_FromLong(0);
1495
+ } else if (op >= OP_PROD && op < OP_MIN) {
1496
+ /* product identity is 1 */
1497
+ fill = PyLong_FromLong(1);
1498
+ } else if (PyArray_DESCR(a)->kind == 'f') {
1499
+ /* floating point min/max identity is NaN */
1500
+ fill = PyFloat_FromDouble(NE_NAN);
1501
+ } else if (op >= OP_MIN && op < OP_MAX) {
1502
+ /* integer min identity */
1503
+ fill = PyLong_FromLong(LONG_MAX);
1504
+ } else {
1505
+ /* integer max identity */
1506
+ fill = PyLong_FromLong(LONG_MIN);
1507
+ }
1508
+ PyArray_FillWithScalar(a, fill);
1509
+ Py_DECREF(fill);
1510
+ }
1511
+
1512
+ /* Get the sizes of all the operands */
1513
+ dtypes_tmp = NpyIter_GetDescrArray(iter);
1514
+ for (i = 0; i < n_inputs+1; ++i) {
1515
+ self->memsizes[i] = PyDataType_ELSIZE(dtypes_tmp[i]);
1516
+ }
1517
+
1518
+ /* For small calculations, just use 1 thread */
1519
+ if (NpyIter_GetIterSize(iter) < 2*BLOCK_SIZE1) {
1520
+ gs.force_serial = 1;
1521
+ }
1522
+
1523
+ /* Reductions do not support parallel execution yet */
1524
+ if (is_reduction) {
1525
+ gs.force_serial = 1;
1526
+ }
1527
+
1528
+ r = run_interpreter(self, iter, reduce_iter,
1529
+ reduction_outer_loop, need_output_buffering,
1530
+ &pc_error);
1531
+
1532
+ if (r < 0) {
1533
+ if (r == -1) {
1534
+ if (!PyErr_Occurred()) {
1535
+ PyErr_SetString(PyExc_RuntimeError,
1536
+ "an error occurred while running the program");
1537
+ }
1538
+ } else if (r == -2) {
1539
+ PyErr_Format(PyExc_RuntimeError,
1540
+ "bad argument at pc=%d", pc_error);
1541
+ } else if (r == -3) {
1542
+ PyErr_Format(PyExc_RuntimeError,
1543
+ "bad opcode at pc=%d", pc_error);
1544
+ } else {
1545
+ PyErr_SetString(PyExc_RuntimeError,
1546
+ "unknown error occurred while running the program");
1547
+ }
1548
+ goto fail;
1549
+ }
1550
+
1551
+ /* Get the output from the iterator */
1552
+ ret = (PyObject *)NpyIter_GetOperandArray(iter)[0];
1553
+ Py_INCREF(ret);
1554
+
1555
+ NpyIter_Deallocate(iter);
1556
+ if (reduce_iter != NULL) {
1557
+ NpyIter_Deallocate(reduce_iter);
1558
+ }
1559
+ cleanup_and_exit:
1560
+ for (i = 0; i < n_inputs+1; i++) {
1561
+ Py_XDECREF(operands[i]);
1562
+ Py_XDECREF(dtypes[i]);
1563
+ }
1564
+
1565
+ return ret;
1566
+ fail:
1567
+ for (i = 0; i < n_inputs+1; i++) {
1568
+ Py_XDECREF(operands[i]);
1569
+ Py_XDECREF(dtypes[i]);
1570
+ }
1571
+ if (iter != NULL) {
1572
+ NpyIter_Deallocate(iter);
1573
+ }
1574
+ if (reduce_iter != NULL) {
1575
+ NpyIter_Deallocate(reduce_iter);
1576
+ }
1577
+
1578
+ return NULL;
1579
+ }
1580
+
1581
+ /*
1582
+ Local Variables:
1583
+ c-basic-offset: 4
1584
+ End:
1585
+ */