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