1import functools
2import itertools
3import re
4import sys
5import warnings
6import threading
7import operator
8
9import numpy as np
10
11import unittest
12from numba import typeof, njit
13from numba.core import types, typing, utils
14from numba.core.compiler import compile_isolated, Flags, DEFAULT_FLAGS
15from numba.np.numpy_support import from_dtype
16from numba import jit, vectorize
17from numba.core.errors import LoweringError, TypingError
18from numba.tests.support import TestCase, CompilationCache, MemoryLeakMixin, tag
19from numba.core.typing.npydecl import supported_ufuncs, all_ufuncs
20from numba.np import numpy_support
21
22is32bits = tuple.__itemsize__ == 4
23iswindows = sys.platform.startswith('win32')
24
25# NOTE: to test the implementation of Numpy ufuncs, we disable rewriting
26# of array expressions.
27
28enable_pyobj_flags = Flags()
29enable_pyobj_flags.set("enable_pyobject")
30enable_pyobj_flags.set("no_rewrites")
31
32no_pyobj_flags = Flags()
33no_pyobj_flags.set("no_rewrites")
34
35enable_nrt_flags = Flags()
36enable_nrt_flags.set("nrt")
37enable_nrt_flags.set("no_rewrites")
38
39
40def _unimplemented(func):
41    """An 'expectedFailure' like decorator that only expects compilation errors
42    caused by unimplemented functions that fail in no-python mode"""
43    @functools.wraps(func)
44    def wrapper(*args, **kwargs):
45        try:
46            func(*args, **kwargs)
47        except TypingError:
48            raise unittest._ExpectedFailure(sys.exc_info())
49        raise unittest._UnexpectedSuccess
50
51def _make_ufunc_usecase(ufunc):
52    ldict = {}
53    arg_str = ','.join(['a{0}'.format(i) for i in range(ufunc.nargs)])
54    func_str = 'def fn({0}):\n    np.{1}({0})'.format(arg_str, ufunc.__name__)
55    exec(func_str, globals(), ldict)
56    fn = ldict['fn']
57    fn.__name__ = '{0}_usecase'.format(ufunc.__name__)
58    return fn
59
60def _make_unary_ufunc_op_usecase(ufunc_op):
61    ldict = {}
62    exec("def fn(x):\n    return {0}(x)".format(ufunc_op), globals(), ldict)
63    fn = ldict["fn"]
64    fn.__name__ = "usecase_{0}".format(hash(ufunc_op))
65    return fn
66
67def _make_binary_ufunc_op_usecase(ufunc_op):
68    ldict = {}
69    exec("def fn(x,y):\n    return x{0}y".format(ufunc_op), globals(), ldict)
70    fn = ldict["fn"]
71    fn.__name__ = "usecase_{0}".format(hash(ufunc_op))
72    return fn
73
74
75def _make_inplace_ufunc_op_usecase(ufunc_op):
76    """Generates a function to be compiled that performs an inplace operation
77
78    ufunc_op can be a string like '+=' or a function like operator.iadd
79    """
80    if isinstance(ufunc_op, str):
81        ldict = {}
82        exec("def fn(x,y):\n    x{0}y".format(ufunc_op), globals(), ldict)
83        fn = ldict["fn"]
84        fn.__name__ = "usecase_{0}".format(hash(ufunc_op))
85    else:
86        def inplace_op(x, y):
87            ufunc_op(x, y)
88        fn = inplace_op
89    return fn
90
91
92def _as_dtype_value(tyargs, args):
93    """Convert python values into numpy scalar objects.
94    """
95    return [np.dtype(str(ty)).type(val) for ty, val in zip(tyargs, args)]
96
97
98
99class BaseUFuncTest(MemoryLeakMixin):
100
101    def setUp(self):
102        super(BaseUFuncTest, self).setUp()
103        self.inputs = [
104            (np.uint32(0), types.uint32),
105            (np.uint32(1), types.uint32),
106            (np.int32(-1), types.int32),
107            (np.int32(0), types.int32),
108            (np.int32(1), types.int32),
109            (np.uint64(0), types.uint64),
110            (np.uint64(1), types.uint64),
111            (np.int64(-1), types.int64),
112            (np.int64(0), types.int64),
113            (np.int64(1), types.int64),
114
115            (np.float32(-0.5), types.float32),
116            (np.float32(0.0), types.float32),
117            (np.float32(0.5), types.float32),
118
119            (np.float64(-0.5), types.float64),
120            (np.float64(0.0), types.float64),
121            (np.float64(0.5), types.float64),
122
123            (np.array([0,1], dtype='u4'), types.Array(types.uint32, 1, 'C')),
124            (np.array([0,1], dtype='u8'), types.Array(types.uint64, 1, 'C')),
125            (np.array([-1,0,1], dtype='i4'), types.Array(types.int32, 1, 'C')),
126            (np.array([-1,0,1], dtype='i8'), types.Array(types.int64, 1, 'C')),
127            (np.array([-0.5, 0.0, 0.5], dtype='f4'), types.Array(types.float32, 1, 'C')),
128            (np.array([-0.5, 0.0, 0.5], dtype='f8'), types.Array(types.float64, 1, 'C')),
129
130            (np.array([0,1], dtype=np.int8), types.Array(types.int8, 1, 'C')),
131            (np.array([0,1], dtype=np.int16), types.Array(types.int16, 1, 'C')),
132            (np.array([0,1], dtype=np.uint8), types.Array(types.uint8, 1, 'C')),
133            (np.array([0,1], dtype=np.uint16), types.Array(types.uint16, 1, 'C')),
134            ]
135        self.cache = CompilationCache()
136
137    def _determine_output_type(self, input_type, int_output_type=None,
138                               float_output_type=None):
139        ty = input_type
140        if isinstance(ty, types.Array):
141            ty = ty.dtype
142
143        if ty in types.signed_domain:
144            if int_output_type:
145                output_type = types.Array(int_output_type, 1, 'C')
146            else:
147                output_type = types.Array(ty, 1, 'C')
148        elif ty in types.unsigned_domain:
149            if int_output_type:
150                output_type = types.Array(int_output_type, 1, 'C')
151            else:
152                output_type = types.Array(ty, 1, 'C')
153        else:
154            if float_output_type:
155                output_type = types.Array(float_output_type, 1, 'C')
156            else:
157                output_type = types.Array(ty, 1, 'C')
158        return output_type
159
160
161class TestUFuncs(BaseUFuncTest, TestCase):
162
163    def basic_ufunc_test(self, ufunc, flags=no_pyobj_flags,
164                         skip_inputs=[], additional_inputs=[],
165                         int_output_type=None, float_output_type=None,
166                         kinds='ifc', positive_only=False):
167
168        # Necessary to avoid some Numpy warnings being silenced, despite
169        # the simplefilter() call below.
170        self.reset_module_warnings(__name__)
171
172        pyfunc = _make_ufunc_usecase(ufunc)
173
174        inputs = list(self.inputs) + additional_inputs
175
176        for input_tuple in inputs:
177            input_operand = input_tuple[0]
178            input_type = input_tuple[1]
179
180            is_tuple = isinstance(input_operand, tuple)
181            if is_tuple:
182                args = input_operand
183            else:
184                args = (input_operand,) * ufunc.nin
185
186            if input_type in skip_inputs:
187                continue
188            if positive_only and np.any(args[0] < 0):
189                continue
190
191            # Some ufuncs don't allow all kinds of arguments
192            if (args[0].dtype.kind not in kinds):
193                continue
194
195            output_type = self._determine_output_type(
196                input_type, int_output_type, float_output_type)
197
198            input_types = (input_type,) * ufunc.nin
199            output_types = (output_type,) * ufunc.nout
200            cr = self.cache.compile(pyfunc, input_types + output_types,
201                                    flags=flags)
202            cfunc = cr.entry_point
203
204            if isinstance(args[0], np.ndarray):
205                results = [
206                    np.zeros(args[0].size,
207                             dtype=out_ty.dtype.name)
208                    for out_ty in output_types
209                ]
210                expected = [
211                    np.zeros(args[0].size,
212                                    dtype=out_ty.dtype.name)
213                    for out_ty in output_types
214                ]
215            else:
216                results = [
217                    np.zeros(1, dtype=out_ty.dtype.name)
218                    for out_ty in output_types
219                ]
220                expected = [
221                    np.zeros(1, dtype=out_ty.dtype.name)
222                    for out_ty in output_types
223                ]
224
225            invalid_flag = False
226            with warnings.catch_warnings(record=True) as warnlist:
227                warnings.simplefilter('always')
228                pyfunc(*args, *expected)
229
230                warnmsg = "invalid value encountered"
231                for thiswarn in warnlist:
232
233                    if (issubclass(thiswarn.category, RuntimeWarning)
234                        and str(thiswarn.message).startswith(warnmsg)):
235                        invalid_flag = True
236
237            cfunc(*args, *results)
238
239            for expected_i, result_i in zip(expected, results):
240                msg = '\n'.join(["ufunc '{0}' failed",
241                                 "inputs ({1}):", "{2}",
242                                 "got({3})", "{4}",
243                                 "expected ({5}):", "{6}"
244                                 ]).format(ufunc.__name__,
245                                           input_type, input_operand,
246                                           output_type, result_i,
247                                           expected_i.dtype, expected_i)
248                try:
249                    np.testing.assert_array_almost_equal(
250                        expected_i, result_i,
251                        decimal=5,
252                        err_msg=msg)
253                except AssertionError:
254                    if invalid_flag:
255                        # Allow output to mismatch for invalid input
256                        print("Output mismatch for invalid input",
257                              input_tuple, result_i, expected_i)
258                    else:
259                        raise
260
261    def basic_int_ufunc_test(self, name=None, flags=no_pyobj_flags):
262        self.basic_ufunc_test(name, flags=flags,
263            skip_inputs=[types.float32, types.float64,
264                types.Array(types.float32, 1, 'C'),
265                types.Array(types.float64, 1, 'C')])
266
267    ############################################################################
268    # Math operations
269
270    def test_add_ufunc(self, flags=no_pyobj_flags):
271        self.basic_ufunc_test(np.add, flags=flags)
272
273    def test_subtract_ufunc(self, flags=no_pyobj_flags):
274        self.basic_ufunc_test(np.subtract, flags=flags)
275
276    def test_multiply_ufunc(self, flags=no_pyobj_flags):
277        self.basic_ufunc_test(np.multiply, flags=flags)
278
279    def test_divide_ufunc(self, flags=no_pyobj_flags):
280        # Bear in mind that in python3 divide IS true_divide
281        # so the out type for int types will be a double
282        int_out_type = None
283        int_out_type = types.float64
284
285        self.basic_ufunc_test(np.divide, flags=flags, int_output_type=int_out_type)
286
287    def test_logaddexp_ufunc(self):
288        self.basic_ufunc_test(np.logaddexp, kinds='f')
289
290    def test_logaddexp2_ufunc(self):
291        self.basic_ufunc_test(np.logaddexp2, kinds='f')
292
293    def test_true_divide_ufunc(self, flags=no_pyobj_flags):
294        self.basic_ufunc_test(np.true_divide, flags=flags, int_output_type=types.float64)
295
296    def test_floor_divide_ufunc(self):
297        self.basic_ufunc_test(np.floor_divide)
298
299    def test_negative_ufunc(self, flags=no_pyobj_flags):
300        # NumPy ufunc has bug with uint32 as input and int64 as output,
301        # so skip uint32 input.
302        self.basic_ufunc_test(np.negative, int_output_type=types.int64,
303                              skip_inputs=[types.Array(types.uint32, 1, 'C'), types.uint32],
304                              flags=flags)
305
306    def test_positive_ufunc(self, flags=no_pyobj_flags):
307        self.basic_ufunc_test(np.positive, flags=flags)
308
309    def test_power_ufunc(self, flags=no_pyobj_flags):
310        self.basic_ufunc_test(np.power, flags=flags,
311                               positive_only=True)
312
313    def test_gcd_ufunc(self, flags=no_pyobj_flags):
314        self.basic_ufunc_test(np.gcd, flags=flags, kinds="iu")
315
316    def test_lcm_ufunc(self, flags=no_pyobj_flags):
317        self.basic_ufunc_test(np.lcm, flags=flags, kinds="iu")
318
319    def test_remainder_ufunc(self, flags=no_pyobj_flags):
320        self.basic_ufunc_test(np.remainder, flags=flags)
321
322    def test_mod_ufunc(self, flags=no_pyobj_flags):
323        self.basic_ufunc_test(np.mod, flags=flags, kinds='ifcu',
324            additional_inputs = [
325                ((np.uint64(np.iinfo(np.uint64).max), np.uint64(16)), types.uint64)
326            ])
327
328    def test_fmod_ufunc(self, flags=no_pyobj_flags):
329        self.basic_ufunc_test(np.fmod, flags=flags)
330
331    def test_abs_ufunc(self, flags=no_pyobj_flags, ufunc=np.abs):
332        self.basic_ufunc_test(ufunc, flags=flags,
333            additional_inputs = [
334                (np.uint32(np.iinfo(np.uint32).max), types.uint32),
335                (np.uint64(np.iinfo(np.uint64).max), types.uint64),
336                (np.float32(np.finfo(np.float32).min), types.float32),
337                (np.float64(np.finfo(np.float64).min), types.float64)
338                ])
339
340    def test_absolute_ufunc(self, flags=no_pyobj_flags):
341        self.test_abs_ufunc(flags=flags, ufunc=np.absolute)
342
343    def test_fabs_ufunc(self, flags=no_pyobj_flags):
344        self.basic_ufunc_test(np.fabs, flags=flags, kinds='f')
345
346    def test_rint_ufunc(self, flags=no_pyobj_flags):
347        self.basic_ufunc_test(np.rint, flags=flags, kinds='cf')
348
349    def test_sign_ufunc(self, flags=no_pyobj_flags):
350        self.basic_ufunc_test(np.sign, flags=flags)
351
352    def test_conj_ufunc(self, flags=no_pyobj_flags):
353        self.basic_ufunc_test(np.conj, flags=flags)
354
355    def test_exp_ufunc(self, flags=no_pyobj_flags):
356        self.basic_ufunc_test(np.exp, flags=flags, kinds='cf')
357
358    def test_exp2_ufunc(self, flags=no_pyobj_flags):
359        self.basic_ufunc_test(np.exp2, flags=flags, kinds='cf')
360
361    def test_log_ufunc(self, flags=no_pyobj_flags):
362        self.basic_ufunc_test(np.log, flags=flags, kinds='cf')
363
364    def test_log2_ufunc(self, flags=no_pyobj_flags):
365        self.basic_ufunc_test(np.log2, flags=flags, kinds='cf')
366
367    def test_log10_ufunc(self, flags=no_pyobj_flags):
368        self.basic_ufunc_test(np.log10, flags=flags, kinds='cf')
369
370    def test_expm1_ufunc(self, flags=no_pyobj_flags):
371        self.basic_ufunc_test(np.expm1, flags=flags, kinds='cf')
372
373    def test_log1p_ufunc(self, flags=no_pyobj_flags):
374        self.basic_ufunc_test(np.log1p, flags=flags, kinds='cf')
375
376    def test_sqrt_ufunc(self, flags=no_pyobj_flags):
377        self.basic_ufunc_test(np.sqrt, flags=flags, kinds='cf')
378
379    def test_square_ufunc(self, flags=no_pyobj_flags):
380        self.basic_ufunc_test(np.square, flags=flags)
381
382    def test_reciprocal_ufunc(self, flags=no_pyobj_flags):
383        # reciprocal for integers doesn't make much sense and is problematic
384        # in the case of division by zero, as an inf will overflow float to
385        # int conversions, which is undefined behavior.
386        to_skip = [types.Array(types.uint32, 1, 'C'), types.uint32,
387                   types.Array(types.int32, 1, 'C'), types.int32,
388                   types.Array(types.uint64, 1, 'C'), types.uint64,
389                   types.Array(types.int64, 1, 'C'), types.int64]
390        self.basic_ufunc_test(np.reciprocal, skip_inputs=to_skip, flags=flags)
391
392    def test_conjugate_ufunc(self, flags=no_pyobj_flags):
393        self.basic_ufunc_test(np.conjugate, flags=flags)
394
395
396    ############################################################################
397    # Trigonometric Functions
398
399    def test_sin_ufunc(self, flags=no_pyobj_flags):
400        self.basic_ufunc_test(np.sin, flags=flags, kinds='cf')
401
402    def test_cos_ufunc(self, flags=no_pyobj_flags):
403        self.basic_ufunc_test(np.cos, flags=flags, kinds='cf')
404
405    def test_tan_ufunc(self, flags=no_pyobj_flags):
406        self.basic_ufunc_test(np.tan, flags=flags, kinds='cf')
407
408    def test_arcsin_ufunc(self, flags=no_pyobj_flags):
409        self.basic_ufunc_test(np.arcsin, flags=flags, kinds='cf')
410
411    def test_arccos_ufunc(self, flags=no_pyobj_flags):
412        self.basic_ufunc_test(np.arccos, flags=flags, kinds='cf')
413
414    def test_arctan_ufunc(self, flags=no_pyobj_flags):
415        self.basic_ufunc_test(np.arctan, flags=flags, kinds='cf')
416
417    def test_arctan2_ufunc(self, flags=no_pyobj_flags):
418        self.basic_ufunc_test(np.arctan2, flags=flags, kinds='cf')
419
420    def test_hypot_ufunc(self, flags=no_pyobj_flags):
421        self.basic_ufunc_test(np.hypot, kinds='f')
422
423    def test_sinh_ufunc(self, flags=no_pyobj_flags):
424        self.basic_ufunc_test(np.sinh, flags=flags, kinds='cf')
425
426    def test_cosh_ufunc(self, flags=no_pyobj_flags):
427        self.basic_ufunc_test(np.cosh, flags=flags, kinds='cf')
428
429    def test_tanh_ufunc(self, flags=no_pyobj_flags):
430        self.basic_ufunc_test(np.tanh, flags=flags, kinds='cf')
431
432    def test_arcsinh_ufunc(self, flags=no_pyobj_flags):
433        self.basic_ufunc_test(np.arcsinh, flags=flags, kinds='cf')
434
435    def test_arccosh_ufunc(self, flags=no_pyobj_flags):
436        self.basic_ufunc_test(np.arccosh, flags=flags, kinds='cf')
437
438    def test_arctanh_ufunc(self, flags=no_pyobj_flags):
439        # arctanh is only valid is only finite in the range ]-1, 1[
440        # This means that for any of the integer types it will produce
441        # conversion from infinity/-infinity to integer. That's undefined
442        # behavior in C, so the results may vary from implementation to
443        # implementation. This means that the result from the compiler
444        # used to compile NumPy may differ from the result generated by
445        # llvm. Skipping the integer types in this test avoids failed
446        # tests because of this.
447        to_skip = [types.Array(types.uint32, 1, 'C'), types.uint32,
448                   types.Array(types.int32, 1, 'C'), types.int32,
449                   types.Array(types.uint64, 1, 'C'), types.uint64,
450                   types.Array(types.int64, 1, 'C'), types.int64]
451
452        self.basic_ufunc_test(np.arctanh, skip_inputs=to_skip, flags=flags,
453                              kinds='cf')
454
455    def test_deg2rad_ufunc(self, flags=no_pyobj_flags):
456        self.basic_ufunc_test(np.deg2rad, flags=flags, kinds='f')
457
458    def test_rad2deg_ufunc(self, flags=no_pyobj_flags):
459        self.basic_ufunc_test(np.rad2deg, flags=flags, kinds='f')
460
461    def test_degrees_ufunc(self, flags=no_pyobj_flags):
462        self.basic_ufunc_test(np.degrees, flags=flags, kinds='f')
463
464    def test_radians_ufunc(self, flags=no_pyobj_flags):
465        self.basic_ufunc_test(np.radians, flags=flags, kinds='f')
466
467    ############################################################################
468    # Bit-twiddling Functions
469
470    def test_bitwise_and_ufunc(self, flags=no_pyobj_flags):
471        self.basic_int_ufunc_test(np.bitwise_and, flags=flags)
472
473    def test_bitwise_or_ufunc(self, flags=no_pyobj_flags):
474        self.basic_int_ufunc_test(np.bitwise_or, flags=flags)
475
476    def test_bitwise_xor_ufunc(self, flags=no_pyobj_flags):
477        self.basic_int_ufunc_test(np.bitwise_xor, flags=flags)
478
479    def test_invert_ufunc(self, flags=no_pyobj_flags):
480        self.basic_int_ufunc_test(np.invert, flags=flags)
481
482    def test_bitwise_not_ufunc(self, flags=no_pyobj_flags):
483        self.basic_int_ufunc_test(np.bitwise_not, flags=flags)
484
485    # Note: there is no entry for left_shift and right_shift as this harness
486    #       is not valid for them. This is so because left_shift and right
487    #       shift implementation in NumPy has undefined behavior (in C-parlance)
488    #       when the second argument is a negative (or bigger than the number
489    #       of bits) value.
490    #       Also, right_shift for negative first arguments also relies on
491    #       implementation defined behavior, although numba warantees "sane"
492    #       behavior (arithmetic shifts on signed integers, logic shifts on
493    #       unsigned integers).
494
495    ############################################################################
496    # Comparison functions
497    def test_greater_ufunc(self, flags=no_pyobj_flags):
498        self.basic_ufunc_test(np.greater, flags=flags)
499
500    def test_greater_equal_ufunc(self, flags=no_pyobj_flags):
501        self.basic_ufunc_test(np.greater_equal, flags=flags)
502
503    def test_less_ufunc(self, flags=no_pyobj_flags):
504        self.basic_ufunc_test(np.less, flags=flags)
505
506    def test_less_equal_ufunc(self, flags=no_pyobj_flags):
507        self.basic_ufunc_test(np.less_equal, flags=flags)
508
509    def test_not_equal_ufunc(self, flags=no_pyobj_flags):
510        self.basic_ufunc_test(np.not_equal, flags=flags)
511
512    def test_equal_ufunc(self, flags=no_pyobj_flags):
513        self.basic_ufunc_test(np.equal, flags=flags)
514
515    def test_logical_and_ufunc(self, flags=no_pyobj_flags):
516        self.basic_ufunc_test(np.logical_and, flags=flags)
517
518    def test_logical_or_ufunc(self, flags=no_pyobj_flags):
519        self.basic_ufunc_test(np.logical_or, flags=flags)
520
521    def test_logical_xor_ufunc(self, flags=no_pyobj_flags):
522        self.basic_ufunc_test(np.logical_xor, flags=flags)
523
524    def test_logical_not_ufunc(self, flags=no_pyobj_flags):
525        self.basic_ufunc_test(np.logical_not, flags=flags)
526
527    def test_maximum_ufunc(self, flags=no_pyobj_flags):
528        self.basic_ufunc_test(np.maximum, flags=flags)
529
530    def test_minimum_ufunc(self, flags=no_pyobj_flags):
531        self.basic_ufunc_test(np.minimum, flags=flags)
532
533    def test_fmax_ufunc(self, flags=no_pyobj_flags):
534        self.basic_ufunc_test(np.fmax, flags=flags)
535
536    def test_fmin_ufunc(self, flags=no_pyobj_flags):
537        self.basic_ufunc_test(np.fmin, flags=flags)
538
539
540    ############################################################################
541    # Floating functions
542
543    def bool_additional_inputs(self):
544        return [
545            (np.array([True, False], dtype=np.bool_),
546             types.Array(types.bool_, 1, 'C')),
547            ]
548
549    def test_isfinite_ufunc(self, flags=no_pyobj_flags):
550        self.basic_ufunc_test(
551            np.isfinite, flags=flags, kinds='ifcb',
552            additional_inputs=self.bool_additional_inputs(),
553        )
554
555    def test_isinf_ufunc(self, flags=no_pyobj_flags):
556        self.basic_ufunc_test(
557            np.isinf, flags=flags, kinds='ifcb',
558            additional_inputs=self.bool_additional_inputs(),
559        )
560
561    def test_isnan_ufunc(self, flags=no_pyobj_flags):
562        self.basic_ufunc_test(
563            np.isnan, flags=flags, kinds='ifcb',
564            additional_inputs=self.bool_additional_inputs(),
565        )
566
567    def test_signbit_ufunc(self, flags=no_pyobj_flags):
568        self.basic_ufunc_test(np.signbit, flags=flags)
569
570    def test_copysign_ufunc(self, flags=no_pyobj_flags):
571        self.basic_ufunc_test(np.copysign, flags=flags, kinds='f')
572
573    def test_nextafter_ufunc(self, flags=no_pyobj_flags):
574        self.basic_ufunc_test(np.nextafter, flags=flags, kinds='f')
575
576    @_unimplemented
577    def test_modf_ufunc(self, flags=no_pyobj_flags):
578        self.basic_ufunc_test(np.modf, flags=flags, kinds='f')
579
580    # Note: there is no entry for ldexp as this harness isn't valid for this
581    #       ufunc. this is so because ldexp requires heterogeneous inputs.
582    #       However, this ufunc is tested by the TestLoopTypes test classes.
583
584    @_unimplemented
585    def test_frexp_ufunc(self, flags=no_pyobj_flags):
586        self.basic_ufunc_test(np.frexp, flags=flags, kinds='f')
587
588    def test_floor_ufunc(self, flags=no_pyobj_flags):
589        self.basic_ufunc_test(np.floor, flags=flags, kinds='f')
590
591    def test_ceil_ufunc(self, flags=no_pyobj_flags):
592        self.basic_ufunc_test(np.ceil, flags=flags, kinds='f')
593
594    def test_trunc_ufunc(self, flags=no_pyobj_flags):
595        self.basic_ufunc_test(np.trunc, flags=flags, kinds='f')
596
597    def test_spacing_ufunc(self, flags=no_pyobj_flags):
598        self.basic_ufunc_test(np.spacing, flags=flags, kinds='f')
599
600    ############################################################################
601    # Other tests
602
603    def binary_ufunc_mixed_types_test(self, ufunc, flags=no_pyobj_flags):
604        ufunc_name = ufunc.__name__
605        ufunc = _make_ufunc_usecase(ufunc)
606        inputs1 = [
607            (1, types.uint64),
608            (-1, types.int64),
609            (0.5, types.float64),
610
611            (np.array([0, 1], dtype='u8'), types.Array(types.uint64, 1, 'C')),
612            (np.array([-1, 1], dtype='i8'), types.Array(types.int64, 1, 'C')),
613            (np.array([-0.5, 0.5], dtype='f8'), types.Array(types.float64, 1, 'C'))]
614
615        inputs2 = inputs1
616
617        output_types = [types.Array(types.int64, 1, 'C'),
618                        types.Array(types.float64, 1, 'C')]
619
620        pyfunc = ufunc
621
622        for input1, input2, output_type in itertools.product(inputs1, inputs2, output_types):
623
624            input1_operand = input1[0]
625            input1_type = input1[1]
626
627            input2_operand = input2[0]
628            input2_type = input2[1]
629
630            # Skip division by unsigned int because of NumPy bugs
631            if ufunc_name == 'divide' and (input2_type == types.Array(types.uint32, 1, 'C') or
632                    input2_type == types.Array(types.uint64, 1, 'C')):
633                continue
634
635            # Skip some subtraction tests because of NumPy bugs
636            if ufunc_name == 'subtract' and input1_type == types.Array(types.uint32, 1, 'C') and \
637                    input2_type == types.uint32 and types.Array(types.int64, 1, 'C'):
638                continue
639            if ufunc_name == 'subtract' and input1_type == types.Array(types.uint32, 1, 'C') and \
640                    input2_type == types.uint64 and types.Array(types.int64, 1, 'C'):
641                continue
642
643            if ((isinstance(input1_type, types.Array) or
644                    isinstance(input2_type, types.Array)) and
645                    not isinstance(output_type, types.Array)):
646                continue
647
648            cr = self.cache.compile(pyfunc,
649                                    (input1_type, input2_type, output_type),
650                                    flags=flags)
651            cfunc = cr.entry_point
652
653            if isinstance(input1_operand, np.ndarray):
654                result = np.zeros(input1_operand.size,
655                                  dtype=output_type.dtype.name)
656                expected = np.zeros(input1_operand.size,
657                                    dtype=output_type.dtype.name)
658            elif isinstance(input2_operand, np.ndarray):
659                result = np.zeros(input2_operand.size,
660                                  dtype=output_type.dtype.name)
661                expected = np.zeros(input2_operand.size,
662                                    dtype=output_type.dtype.name)
663            else:
664                result = np.zeros(1, dtype=output_type.dtype.name)
665                expected = np.zeros(1, dtype=output_type.dtype.name)
666
667            cfunc(input1_operand, input2_operand, result)
668            pyfunc(input1_operand, input2_operand, expected)
669
670            scalar_type = getattr(output_type, 'dtype', output_type)
671            prec = ('single'
672                    if scalar_type in (types.float32, types.complex64)
673                    else 'double')
674            self.assertPreciseEqual(expected, result, prec=prec)
675
676    def test_broadcasting(self):
677
678        # Test unary ufunc
679        pyfunc = _make_ufunc_usecase(np.negative)
680
681        input_operands = [
682            np.arange(3, dtype='i8'),
683            np.arange(3, dtype='i8').reshape(3,1),
684            np.arange(3, dtype='i8').reshape(1,3),
685            np.arange(3, dtype='i8').reshape(3,1),
686            np.arange(3, dtype='i8').reshape(1,3),
687            np.arange(3*3, dtype='i8').reshape(3,3)]
688
689        output_operands = [
690            np.zeros(3*3, dtype='i8').reshape(3,3),
691            np.zeros(3*3, dtype='i8').reshape(3,3),
692            np.zeros(3*3, dtype='i8').reshape(3,3),
693            np.zeros(3*3*3, dtype='i8').reshape(3,3,3),
694            np.zeros(3*3*3, dtype='i8').reshape(3,3,3),
695            np.zeros(3*3*3, dtype='i8').reshape(3,3,3)]
696
697        for x, result in zip(input_operands, output_operands):
698
699            input_type = types.Array(types.uint64, x.ndim, 'C')
700            output_type = types.Array(types.int64, result.ndim, 'C')
701
702            cr = self.cache.compile(pyfunc, (input_type, output_type),
703                                    flags=no_pyobj_flags)
704            cfunc = cr.entry_point
705
706            expected = np.zeros(result.shape, dtype=result.dtype)
707            np.negative(x, expected)
708
709            cfunc(x, result)
710            self.assertPreciseEqual(result, expected)
711
712        # Test binary ufunc
713        pyfunc = _make_ufunc_usecase(np.add)
714
715        input1_operands = [
716            np.arange(3, dtype='u8'),
717            np.arange(3*3, dtype='u8').reshape(3,3),
718            np.arange(3*3*3, dtype='u8').reshape(3,3,3),
719            np.arange(3, dtype='u8').reshape(3,1),
720            np.arange(3, dtype='u8').reshape(1,3),
721            np.arange(3, dtype='u8').reshape(3,1,1),
722            np.arange(3*3, dtype='u8').reshape(3,3,1),
723            np.arange(3*3, dtype='u8').reshape(3,1,3),
724            np.arange(3*3, dtype='u8').reshape(1,3,3)]
725
726        input2_operands = input1_operands
727
728        for x, y in itertools.product(input1_operands, input2_operands):
729
730            input1_type = types.Array(types.uint64, x.ndim, 'C')
731            input2_type = types.Array(types.uint64, y.ndim, 'C')
732            output_type = types.Array(types.uint64, max(x.ndim, y.ndim), 'C')
733
734            cr = self.cache.compile(pyfunc, (input1_type, input2_type, output_type),
735                                    flags=no_pyobj_flags)
736            cfunc = cr.entry_point
737
738            expected = np.add(x, y)
739            result = np.zeros(expected.shape, dtype='u8')
740
741            cfunc(x, y, result)
742            self.assertPreciseEqual(result, expected)
743
744    def test_implicit_output_npm(self):
745        with self.assertRaises(TypeError):
746            def myadd(a0, a1):
747                return np.add(a0, a1)
748            arr_ty = types.Array(types.uint64, 1, 'C')
749            cr = compile_isolated(myadd, (arr_ty, arr_ty),
750                                  flags=no_pyobj_flags)
751
752    def test_broadcast_implicit_output_npm_nrt(self):
753        def pyfunc(a0, a1):
754            return np.add(a0, a1)
755
756        input1_operands = [
757            np.arange(3, dtype='u8'),
758            np.arange(3*3, dtype='u8').reshape(3,3),
759            np.arange(3*3*3, dtype='u8').reshape(3,3,3),
760            np.arange(3, dtype='u8').reshape(3,1),
761            np.arange(3, dtype='u8').reshape(1,3),
762            np.arange(3, dtype='u8').reshape(3,1,1),
763            np.arange(3*3, dtype='u8').reshape(3,3,1),
764            np.arange(3*3, dtype='u8').reshape(3,1,3),
765            np.arange(3*3, dtype='u8').reshape(1,3,3)]
766
767        input2_operands = input1_operands
768
769        for x, y in itertools.product(input1_operands, input2_operands):
770
771            input1_type = types.Array(types.uint64, x.ndim, 'C')
772            input2_type = types.Array(types.uint64, y.ndim, 'C')
773
774            cr = self.cache.compile(pyfunc, (input1_type, input2_type),
775                                    flags=enable_nrt_flags)
776            cfunc = cr.entry_point
777
778            expected = np.add(x, y)
779            result = cfunc(x, y)
780            np.testing.assert_array_equal(expected, result)
781
782    def test_implicit_output_layout_binary(self):
783        def pyfunc(a0, a1):
784            return np.add(a0, a1)
785
786        # C layout
787        X = np.linspace(0, 1, 20).reshape(4, 5)
788        # F layout
789        Y = np.array(X, order='F')
790        # A layout
791        Z = X.reshape(5, 4).T[0]
792
793        Xty = typeof(X)
794        assert X.flags.c_contiguous and Xty.layout == 'C'
795        Yty = typeof(Y)
796        assert Y.flags.f_contiguous and Yty.layout == 'F'
797        Zty = typeof(Z)
798        assert Zty.layout == 'A'
799        assert not Z.flags.c_contiguous
800        assert not Z.flags.f_contiguous
801
802        testcases = list(itertools.permutations([X, Y, Z], 2))
803        testcases += [(X, X)]
804        testcases += [(Y, Y)]
805        testcases += [(Z, Z)]
806
807        for arg0, arg1 in testcases:
808            cr = self.cache.compile(pyfunc, (typeof(arg0), typeof(arg1)),
809                                    flags=enable_nrt_flags)
810            expected = pyfunc(arg0, arg1)
811            result = cr.entry_point(arg0, arg1)
812
813            self.assertEqual(expected.flags.c_contiguous,
814                             result.flags.c_contiguous)
815            self.assertEqual(expected.flags.f_contiguous,
816                             result.flags.f_contiguous)
817            np.testing.assert_array_equal(expected, result)
818
819    def test_implicit_output_layout_unary(self):
820        def pyfunc(a0):
821            return np.sqrt(a0)
822
823        # C layout
824        X = np.linspace(0, 1, 20).reshape(4, 5)
825        # F layout
826        Y = np.array(X, order='F')
827        # A layout
828        Z = X.reshape(5, 4).T[0]
829
830        Xty = typeof(X)
831        assert X.flags.c_contiguous and Xty.layout == 'C'
832        Yty = typeof(Y)
833        assert Y.flags.f_contiguous and Yty.layout == 'F'
834        Zty = typeof(Z)
835        assert Zty.layout == 'A'
836        assert not Z.flags.c_contiguous
837        assert not Z.flags.f_contiguous
838
839        for arg0 in [X, Y, Z]:
840            cr = self.cache.compile(pyfunc, (typeof(arg0),),
841                                    flags=enable_nrt_flags)
842            expected = pyfunc(arg0)
843            result = cr.entry_point(arg0)
844
845            self.assertEqual(expected.flags.c_contiguous,
846                             result.flags.c_contiguous)
847            self.assertEqual(expected.flags.f_contiguous,
848                             result.flags.f_contiguous)
849            np.testing.assert_array_equal(expected, result)
850
851
852
853class TestArrayOperators(BaseUFuncTest, TestCase):
854
855    def _check_results(self, expected, got):
856        self.assertEqual(expected.dtype.kind, got.dtype.kind)
857        np.testing.assert_array_almost_equal(expected, got)
858
859    def unary_op_test(self, operator, flags=enable_nrt_flags,
860                      skip_inputs=[], additional_inputs=[],
861                      int_output_type=None, float_output_type=None):
862        operator_func = _make_unary_ufunc_op_usecase(operator)
863        inputs = list(self.inputs)
864        inputs.extend(additional_inputs)
865        pyfunc = operator_func
866        for input_tuple in inputs:
867            input_operand, input_type = input_tuple
868
869            if ((input_type in skip_inputs) or
870                (not isinstance(input_type, types.Array))):
871                continue
872
873            cr = self.cache.compile(pyfunc, (input_type,),
874                                    flags=flags)
875            cfunc = cr.entry_point
876            expected = pyfunc(input_operand)
877            got = cfunc(input_operand)
878            self._check_results(expected, got)
879
880    def binary_op_test(self, operator, flags=enable_nrt_flags,
881                       skip_inputs=[], additional_inputs=[],
882                       int_output_type=None, float_output_type=None,
883                       positive_rhs=False):
884        operator_func = _make_binary_ufunc_op_usecase(operator)
885        inputs = list(self.inputs)
886        inputs.extend(additional_inputs)
887        pyfunc = operator_func
888        # when generating arbitrary sequences, we use a fixed seed
889        # for deterministic testing
890        random_state = np.random.RandomState(1)
891        for input_tuple in inputs:
892            input_operand1, input_type = input_tuple
893            input_dtype = numpy_support.as_dtype(
894                getattr(input_type, "dtype", input_type))
895            input_type1 = input_type
896
897            if input_type in skip_inputs:
898                continue
899
900            if positive_rhs:
901                zero = np.zeros(1, dtype=input_dtype)[0]
902            # If we only use two scalars, the code generator will not
903            # select the ufunctionalized operator, so we mix it up.
904            if isinstance(input_type, types.Array):
905                input_operand0 = input_operand1
906                input_type0 = input_type
907                if positive_rhs and np.any(input_operand1 < zero):
908                    continue
909            else:
910                input_operand0 = (random_state.uniform(0, 100, 10)).astype(
911                    input_dtype)
912                input_type0 = typeof(input_operand0)
913                if positive_rhs and input_operand1 < zero:
914                    continue
915
916            cr = self.cache.compile(pyfunc, (input_type0, input_type1),
917                                    flags=flags)
918            cfunc = cr.entry_point
919            expected = pyfunc(input_operand0, input_operand1)
920            got = cfunc(input_operand0, input_operand1)
921            self._check_results(expected, got)
922
923    def bitwise_additional_inputs(self):
924        # For bitwise operators, we want to check the results for boolean
925        # arrays (see #1813).
926        return [
927            (True, types.boolean),
928            (False, types.boolean),
929            (np.array([True, False]), types.Array(types.boolean, 1, 'C')),
930            ]
931
932    def binary_int_op_test(self, *args, **kws):
933        skip_inputs = kws.setdefault('skip_inputs', [])
934        skip_inputs += [
935            types.float32, types.float64,
936            types.Array(types.float32, 1, 'C'),
937            types.Array(types.float64, 1, 'C'),
938            ]
939        return self.binary_op_test(*args, **kws)
940
941    def binary_bitwise_op_test(self, *args, **kws):
942        additional_inputs = kws.setdefault('additional_inputs', [])
943        additional_inputs += self.bitwise_additional_inputs()
944        return self.binary_int_op_test(*args, **kws)
945
946    def inplace_op_test(self, operator, lhs_values, rhs_values,
947                        lhs_dtypes, rhs_dtypes):
948        operator_func = _make_inplace_ufunc_op_usecase(operator)
949        pyfunc = operator_func
950
951        # The left operand can only be an array, while the right operand
952        # can be either an array or a scalar
953        lhs_inputs = [np.array(lhs_values, dtype=dtype)
954                      for dtype in lhs_dtypes]
955
956        rhs_arrays = [np.array(rhs_values, dtype=dtype)
957                      for dtype in rhs_dtypes]
958        rhs_scalars = [dtype(v) for v in rhs_values for dtype in rhs_dtypes]
959        rhs_inputs = rhs_arrays + rhs_scalars
960
961        for lhs, rhs in itertools.product(lhs_inputs, rhs_inputs):
962            lhs_type = typeof(lhs)
963            rhs_type = typeof(rhs)
964            cr = self.cache.compile(pyfunc, (lhs_type, rhs_type),
965                                    flags=no_pyobj_flags)
966            cfunc = cr.entry_point
967            expected = lhs.copy()
968            pyfunc(expected, rhs)
969            got = lhs.copy()
970            cfunc(got, rhs)
971            self.assertPreciseEqual(got, expected)
972
973    def inplace_float_op_test(self, operator, lhs_values, rhs_values):
974        # Also accept integer inputs for the right operand (they should
975        # be converted to float).
976        return self.inplace_op_test(operator, lhs_values, rhs_values,
977                                    (np.float32, np.float64),
978                                    (np.float32, np.float64, np.int64))
979
980    def inplace_int_op_test(self, operator, lhs_values, rhs_values):
981        self.inplace_op_test(operator, lhs_values, rhs_values,
982                             (np.int16, np.int32, np.int64),
983                             (np.int16, np.uint32))
984
985    def inplace_bitwise_op_test(self, operator, lhs_values, rhs_values):
986        self.inplace_int_op_test(operator, lhs_values, rhs_values)
987        self.inplace_op_test(operator, lhs_values, rhs_values,
988                             (np.bool_,), (np.bool_, np.bool_))
989
990    # ____________________________________________________________
991    # Unary operators
992
993    def test_unary_positive_array_op(self):
994        self.unary_op_test('+')
995
996    def test_unary_negative_array_op(self):
997        self.unary_op_test('-')
998
999    def test_unary_invert_array_op(self):
1000        self.unary_op_test('~',
1001                           skip_inputs=[types.float32, types.float64,
1002                                        types.Array(types.float32, 1, 'C'),
1003                                        types.Array(types.float64, 1, 'C')],
1004                           additional_inputs=self.bitwise_additional_inputs())
1005
1006    # ____________________________________________________________
1007    # Inplace operators
1008
1009    def test_inplace_add(self):
1010        self.inplace_float_op_test('+=', [-1, 1.5, 3], [-5, 0, 2.5])
1011        self.inplace_float_op_test(operator.iadd, [-1, 1.5, 3], [-5, 0, 2.5])
1012
1013    def test_inplace_sub(self):
1014        self.inplace_float_op_test('-=', [-1, 1.5, 3], [-5, 0, 2.5])
1015        self.inplace_float_op_test(operator.isub, [-1, 1.5, 3], [-5, 0, 2.5])
1016
1017    def test_inplace_mul(self):
1018        self.inplace_float_op_test('*=', [-1, 1.5, 3], [-5, 0, 2.5])
1019        self.inplace_float_op_test(operator.imul, [-1, 1.5, 3], [-5, 0, 2.5])
1020
1021    def test_inplace_floordiv(self):
1022        self.inplace_float_op_test('//=', [-1, 1.5, 3], [-5, 1.25, 2.5])
1023        self.inplace_float_op_test(operator.ifloordiv, [-1, 1.5, 3], [-5, 1.25, 2.5])
1024
1025    def test_inplace_div(self):
1026        self.inplace_float_op_test('/=', [-1, 1.5, 3], [-5, 0, 2.5])
1027        self.inplace_float_op_test(operator.itruediv, [-1, 1.5, 3], [-5, 1.25, 2.5])
1028
1029    def test_inplace_remainder(self):
1030        self.inplace_float_op_test('%=', [-1, 1.5, 3], [-5, 2, 2.5])
1031        self.inplace_float_op_test(operator.imod, [-1, 1.5, 3], [-5, 2, 2.5])
1032
1033    def test_inplace_pow(self):
1034        self.inplace_float_op_test('**=', [-1, 1.5, 3], [-5, 2, 2.5])
1035        self.inplace_float_op_test(operator.ipow, [-1, 1.5, 3], [-5, 2, 2.5])
1036
1037    def test_inplace_and(self):
1038        self.inplace_bitwise_op_test('&=', [0, 1, 2, 3, 51], [0, 13, 16, 42, 255])
1039        self.inplace_bitwise_op_test(operator.iand, [0, 1, 2, 3, 51], [0, 13, 16, 42, 255])
1040
1041    def test_inplace_or(self):
1042        self.inplace_bitwise_op_test('|=', [0, 1, 2, 3, 51], [0, 13, 16, 42, 255])
1043        self.inplace_bitwise_op_test(operator.ior, [0, 1, 2, 3, 51], [0, 13, 16, 42, 255])
1044
1045    def test_inplace_xor(self):
1046        self.inplace_bitwise_op_test('^=', [0, 1, 2, 3, 51], [0, 13, 16, 42, 255])
1047        self.inplace_bitwise_op_test(operator.ixor, [0, 1, 2, 3, 51], [0, 13, 16, 42, 255])
1048
1049    def test_inplace_lshift(self):
1050        self.inplace_int_op_test('<<=', [0, 5, -10, -51], [0, 1, 4, 14])
1051        self.inplace_int_op_test(operator.ilshift, [0, 5, -10, -51], [0, 1, 4, 14])
1052
1053    def test_inplace_rshift(self):
1054        self.inplace_int_op_test('>>=', [0, 5, -10, -51], [0, 1, 4, 14])
1055        self.inplace_int_op_test(operator.irshift, [0, 5, -10, -51], [0, 1, 4, 14])
1056
1057    def test_unary_positive_array_op(self):
1058        '''
1059        Verify that the unary positive operator copies values, and doesn't
1060        just alias to the input array (mirrors normal Numpy/Python
1061        interaction behavior).
1062        '''
1063        # Test originally from @gmarkall
1064        def f(a1):
1065            a2 = +a1
1066            a1[0] = 3
1067            a2[1] = 4
1068            return a2
1069
1070        a1 = np.zeros(10)
1071        a2 = f(a1)
1072        self.assertTrue(a1[0] != a2[0] and a1[1] != a2[1])
1073        a3 = np.zeros(10)
1074        a4 = njit(f)(a3)
1075        self.assertTrue(a3[0] != a4[0] and a3[1] != a4[1])
1076        np.testing.assert_array_equal(a1, a3)
1077        np.testing.assert_array_equal(a2, a4)
1078
1079    # ____________________________________________________________
1080    # Binary operators
1081
1082    def test_add_array_op(self):
1083        self.binary_op_test('+')
1084
1085    def test_subtract_array_op(self):
1086        self.binary_op_test('-')
1087
1088    def test_multiply_array_op(self):
1089        self.binary_op_test('*')
1090
1091    def test_divide_array_op(self):
1092        int_out_type = None
1093        int_out_type = types.float64
1094        self.binary_op_test('/', int_output_type=int_out_type)
1095
1096    def test_floor_divide_array_op(self):
1097        # Avoid floating-point zeros as x // 0.0 can have varying results
1098        # depending on the algorithm (which changed across Numpy versions)
1099        self.inputs = [
1100            (np.uint32(1), types.uint32),
1101            (np.int32(-2), types.int32),
1102            (np.int32(0), types.int32),
1103            (np.uint64(4), types.uint64),
1104            (np.int64(-5), types.int64),
1105            (np.int64(0), types.int64),
1106
1107            (np.float32(-0.5), types.float32),
1108            (np.float32(1.5), types.float32),
1109
1110            (np.float64(-2.5), types.float64),
1111            (np.float64(3.5), types.float64),
1112
1113            (np.array([1,2], dtype='u4'), types.Array(types.uint32, 1, 'C')),
1114            (np.array([3,4], dtype='u8'), types.Array(types.uint64, 1, 'C')),
1115            (np.array([-1,1,5], dtype='i4'), types.Array(types.int32, 1, 'C')),
1116            (np.array([-1,1,6], dtype='i8'), types.Array(types.int64, 1, 'C')),
1117            (np.array([-0.5, 1.5], dtype='f4'), types.Array(types.float32, 1, 'C')),
1118            (np.array([-2.5, 3.5], dtype='f8'), types.Array(types.float64, 1, 'C')),
1119            ]
1120        self.binary_op_test('//')
1121
1122    def test_remainder_array_op(self):
1123        self.binary_op_test('%')
1124
1125    def test_power_array_op(self):
1126        self.binary_op_test('**', positive_rhs=True)
1127
1128    def test_left_shift_array_op(self):
1129        self.binary_int_op_test('<<', positive_rhs=True)
1130
1131    def test_right_shift_array_op(self):
1132        self.binary_int_op_test('>>', positive_rhs=True)
1133
1134    def test_bitwise_and_array_op(self):
1135        self.binary_bitwise_op_test('&')
1136
1137    def test_bitwise_or_array_op(self):
1138        self.binary_bitwise_op_test('|')
1139
1140    def test_bitwise_xor_array_op(self):
1141        self.binary_bitwise_op_test('^')
1142
1143    def test_equal_array_op(self):
1144        self.binary_op_test('==')
1145
1146    def test_greater_array_op(self):
1147        self.binary_op_test('>')
1148
1149    def test_greater_equal_array_op(self):
1150        self.binary_op_test('>=')
1151
1152    def test_less_array_op(self):
1153        self.binary_op_test('<')
1154
1155    def test_less_equal_array_op(self):
1156        self.binary_op_test('<=')
1157
1158    def test_not_equal_array_op(self):
1159        self.binary_op_test('!=')
1160
1161
1162class TestScalarUFuncs(TestCase):
1163    """check the machinery of ufuncs works when the result is an scalar.
1164    These are not exhaustive because:
1165    - the machinery to support this case is the same for all the functions of a
1166      given arity.
1167    - the result of the inner function itself is already tested in TestUFuncs
1168
1169    This class tests regular uses. A subclass tests the no python backend.
1170    """
1171
1172    _compile_flags = enable_pyobj_flags
1173
1174    def run_ufunc(self, pyfunc, arg_types, arg_values):
1175        for tyargs, args in zip(arg_types, arg_values):
1176            cr = compile_isolated(pyfunc, tyargs, flags=self._compile_flags)
1177            cfunc = cr.entry_point
1178            got = cfunc(*args)
1179            expected = pyfunc(*_as_dtype_value(tyargs, args))
1180
1181            msg = 'for args {0} typed {1}'.format(args, tyargs)
1182
1183            # note: due to semantics of ufuncs, thing like adding a int32 to a
1184            # uint64 results in doubles (as neither int32 can be cast safely
1185            # to uint64 nor vice-versa, falling back to using the float version.
1186            # Modify in those cases the expected value (the numpy version does
1187            # not use typed integers as inputs so its result is an integer)
1188            special = set([(types.int32, types.uint64), (types.uint64, types.int32),
1189                           (types.int64, types.uint64), (types.uint64, types.int64)])
1190            if tyargs in special:
1191                expected = float(expected)
1192            else:
1193                # The numba version of scalar ufuncs return an actual value that
1194                # gets converted to a Python type, instead of using NumPy scalars.
1195                # although in python 2 NumPy scalars are considered and instance of
1196                # the appropriate python type, in python 3 that is no longer the case.
1197                # This is why the expected result is casted to the appropriate Python
1198                # type (which is actually the expected behavior of the ufunc translation)
1199                if np.issubdtype(expected.dtype, np.inexact):
1200                    expected = float(expected)
1201                elif np.issubdtype(expected.dtype, np.integer):
1202                    expected = int(expected)
1203                elif np.issubdtype(expected.dtype, np.bool):
1204                    expected = bool(expected)
1205
1206            alltypes = cr.signature.args + (cr.signature.return_type,)
1207
1208            # select the appropriate precision for comparison: note that an argument
1209            # typed at a lower precision can introduce precision problems. For this
1210            # reason the argument types must be taken into account.
1211            if any([t==types.float32 for t in alltypes]):
1212                prec='single'
1213            elif any([t==types.float64 for t in alltypes]):
1214                prec='double'
1215            else:
1216                prec='exact'
1217
1218            self.assertPreciseEqual(got, expected, msg=msg, prec=prec)
1219
1220
1221    def test_scalar_unary_ufunc(self):
1222        def _func(x):
1223            return np.sqrt(x)
1224
1225        vals = [(2,), (2,), (1,), (2,), (.1,), (.2,)]
1226        tys = [(types.int32,), (types.uint32,),
1227               (types.int64,), (types.uint64,),
1228               (types.float32,), (types.float64,)]
1229        self.run_ufunc(_func, tys, vals)
1230
1231
1232    def test_scalar_binary_uniform_ufunc(self):
1233        def _func(x,y):
1234            return np.add(x,y)
1235
1236        vals = [2, 2, 1, 2, .1, .2]
1237        tys = [types.int32, types.uint32,
1238               types.int64, types.uint64, types.float32, types.float64]
1239        self.run_ufunc(_func, zip(tys, tys), zip(vals, vals))
1240
1241
1242    def test_scalar_binary_mixed_ufunc(self, flags=enable_pyobj_flags):
1243        def _func(x,y):
1244            return np.add(x,y)
1245
1246        vals = [2, 2, 1, 2, .1, .2]
1247        tys = [types.int32, types.uint32,
1248               types.int64, types.uint64,
1249               types.float32, types.float64]
1250        self.run_ufunc(_func, itertools.product(tys, tys),
1251                       itertools.product(vals, vals))
1252
1253
1254class TestScalarUFuncsNoPython(TestScalarUFuncs):
1255    """Same tests as TestScalarUFuncs, but forcing no python mode"""
1256    _compile_flags = no_pyobj_flags
1257
1258
1259class TestUfuncIssues(TestCase):
1260
1261    def test_issue_651(self):
1262        # Exercise the code path to make sure this does not fail
1263        @vectorize(["(float64,float64)"])
1264        def foo(x1, x2):
1265            return np.add(x1, x2) + np.add(x1, x2)
1266
1267        a = np.arange(10, dtype='f8')
1268        b = np.arange(10, dtype='f8')
1269        self.assertPreciseEqual(foo(a, b), (a + b) + (a + b))
1270
1271    def test_issue_713(self):
1272        def foo(x,y):
1273            return np.floor_divide(x,y)
1274
1275        cr = compile_isolated(foo, [types.complex128, types.complex128])
1276        self.assertEqual(foo(1j, 1j), cr.entry_point(1j, 1j))
1277
1278    def test_issue_2006(self):
1279        """
1280        <float32 ** int> should return float32, not float64.
1281        """
1282        def foo(x, y):
1283            return np.power(x, y)
1284        pyfunc = foo
1285        cfunc = jit(nopython=True)(pyfunc)
1286
1287        def check(x, y):
1288            got = cfunc(x, y)
1289            np.testing.assert_array_almost_equal(got, pyfunc(x, y))
1290            # Check the power operation conserved the input's dtype
1291            # (this is different from Numpy, whose behaviour depends on
1292            #  the *values* of the arguments -- see PyArray_CanCastArrayTo).
1293            self.assertEqual(got.dtype, x.dtype)
1294
1295        xs = [np.float32([1, 2, 3]), np.complex64([1j, 2, 3-3j])]
1296        for x in xs:
1297            check(x, 3)
1298            check(x, np.uint64(3))
1299            check(x, np.int64([2, 2, 3]))
1300
1301
1302class _LoopTypesTester(TestCase):
1303    """Test code generation for the different loop types defined by ufunc.
1304
1305    This test relies on class variables to configure the test. Subclasses
1306    of this class can just override some of these variables to check other
1307    ufuncs in a different compilation context. The variables supported are:
1308
1309    _funcs: the ufuncs to test
1310    _compile_flags: compilation flags to use (to force nopython mode)
1311    _skip_types: letter types that force skipping the loop when testing
1312                 if present in the NumPy ufunc signature.
1313    _supported_types: only test loops where all the types in the loop
1314                      signature are in this collection. If unset, all.
1315
1316    Note that both, _skip_types and _supported_types must be met for a loop
1317    to be tested.
1318
1319    The NumPy ufunc signature has a form like 'ff->f' (for a binary ufunc
1320    loop taking 2 floats and resulting in a float). In a NumPy ufunc object
1321    you can get a list of supported signatures by accessing the attribute
1322    'types'.
1323    """
1324    _skip_types = 'OegG'
1325
1326    # Allowed deviation between Numpy and Numba results
1327    _ulps = {('arccos', 'F'): 2,
1328             ('arcsin', 'D'): 4,
1329             ('arcsin', 'F'): 4,
1330             ('log10', 'D'): 5,
1331             ('tanh', 'F'): 2,
1332             }
1333
1334    def _arg_for_type(self, a_letter_type, index=0):
1335        """return a suitable array argument for testing the letter type"""
1336        # Note all possible arrays must have the same size, since they
1337        # may be used as inputs to the same func.
1338        if a_letter_type in 'bhilq':
1339            # an integral
1340            return np.array([1, 4, 0, -2], dtype=a_letter_type)
1341        if a_letter_type in 'BHILQ':
1342            return np.array([1, 2, 4, 0], dtype=a_letter_type)
1343        elif a_letter_type in '?':
1344            # a boolean
1345            return np.array([True, False, False, True], dtype=a_letter_type)
1346        elif a_letter_type[0] == 'm':
1347            # timedelta64
1348            if len(a_letter_type) == 1:
1349                a_letter_type = 'm8[D]'
1350            return np.array([2, -3, 'NaT', 0], dtype=a_letter_type)
1351        elif a_letter_type[0] == 'M':
1352            # datetime64
1353            if len(a_letter_type) == 1:
1354                a_letter_type = 'M8[D]'
1355            return np.array(['Nat', 1, 25, 0], dtype=a_letter_type)
1356        elif a_letter_type in 'fd':
1357            # floating point
1358            return np.array([1.5, -3.5, 0.0, float('nan')],
1359                            dtype=a_letter_type)
1360        elif a_letter_type in 'FD':
1361            # complex
1362            # Note `-1j` is different on 2.x and 3.x, hence the explicit spelling
1363            if sys.platform != 'win32':
1364                # Other platforms have better handling of negative zeros,
1365                # test them
1366                negzero = -(0.0 + 1.0j)
1367            else:
1368                negzero = 0.0 - 1.0j
1369            return np.array([negzero, 1.5 + 1.5j, 1j * float('nan'), 0j],
1370                            dtype=a_letter_type)
1371        else:
1372            raise RuntimeError("type %r not understood" % (a_letter_type,))
1373
1374    def _check_loop(self, fn, ufunc, loop):
1375        # the letter types for the args
1376        letter_types = loop[:ufunc.nin] + loop[-ufunc.nout:]
1377
1378        # ignore the loops containing an object argument. They will always
1379        # fail in no python mode. Usually the last loop in ufuncs is an all
1380        # object fallback
1381        supported_types = getattr(self, '_supported_types', [])
1382        if (supported_types and
1383            any(l not in supported_types for l in letter_types)):
1384            return
1385        skip_types = getattr(self, '_skip_types', [])
1386        if any(l in skip_types for l in letter_types):
1387            return
1388        # if the test case requires some types to be present, skip loops
1389        # not involving any of those types.
1390        required_types = getattr(self, '_required_types', [])
1391        if required_types and not any(l in letter_types
1392                                      for l in required_types):
1393            return
1394
1395        self._check_ufunc_with_dtypes(fn, ufunc, letter_types)
1396
1397    def _check_ufunc_with_dtypes(self, fn, ufunc, dtypes):
1398        arg_dty = [np.dtype(t) for t in dtypes]
1399        arg_nbty = [types.Array(from_dtype(t), 1, 'C') for t in arg_dty]
1400        cr = compile_isolated(fn, arg_nbty, flags=self._compile_flags)
1401
1402        # Ensure a good mix of input values
1403        c_args = [self._arg_for_type(t, index=index).repeat(2)
1404                  for index, t in enumerate(dtypes)]
1405        for arr in c_args:
1406            self.random.shuffle(arr)
1407        py_args = [a.copy() for a in c_args]
1408
1409        cr.entry_point(*c_args)
1410        fn(*py_args)
1411
1412        # Check each array (including inputs, to ensure they weren't
1413        # mutated).
1414        for dtype, py_arg, c_arg in zip(arg_dty, py_args, c_args):
1415            py_arg, c_arg = self._fixup_results(dtype, py_arg, c_arg)
1416            typechar = c_arg.dtype.char
1417            ulps = self._ulps.get((ufunc.__name__, typechar), 1)
1418            prec = 'single' if typechar in 'fF' else 'exact'
1419            prec = 'double' if typechar in 'dD' else prec
1420            msg = '\n'.join(["ufunc '{0}' arrays differ ({1}):",
1421                             "args: {2}", "expected {3}", "got {4}"])
1422            msg = msg.format(ufunc.__name__, c_args, prec, py_arg, c_arg)
1423            self.assertPreciseEqual(py_arg, c_arg, prec=prec, msg=msg,
1424                                    ulps=ulps)
1425
1426    def _fixup_results(self, dtype, py_arg, c_arg):
1427        return py_arg, c_arg
1428
1429    @classmethod
1430    def _check_ufunc_loops(cls, ufunc):
1431        for loop in ufunc.types:
1432            cls._inject_test(ufunc, loop)
1433
1434    @classmethod
1435    def _inject_test(cls, ufunc, loop):
1436        def test_template(self):
1437            fn = _make_ufunc_usecase(ufunc)
1438            self._check_loop(fn, ufunc, loop)
1439        setattr(cls, "test_{0}_{1}".format(ufunc.__name__,
1440                                           loop.replace('->', '_')),
1441                test_template)
1442
1443    @classmethod
1444    def autogenerate(cls):
1445        for ufunc in cls._ufuncs:
1446            cls._check_ufunc_loops(ufunc)
1447
1448
1449class TestLoopTypesIntNoPython(_LoopTypesTester):
1450    _compile_flags = no_pyobj_flags
1451    _ufuncs = supported_ufuncs[:]
1452    # reciprocal and power need a special test due to issue #757
1453    _ufuncs.remove(np.power)
1454    _ufuncs.remove(np.reciprocal)
1455    _ufuncs.remove(np.left_shift) # has its own test class
1456    _ufuncs.remove(np.right_shift) # has its own test class
1457    # special test for bool subtract/negative
1458    _ufuncs.remove(np.subtract)
1459    _ufuncs.remove(np.negative)
1460    _required_types = '?bBhHiIlLqQ'
1461    _skip_types = 'fdFDmMO' + _LoopTypesTester._skip_types
1462
1463TestLoopTypesIntNoPython.autogenerate()
1464
1465
1466class TestLoopTypesSubtractAndNegativeNoPython(_LoopTypesTester):
1467    _compile_flags = no_pyobj_flags
1468    _ufuncs = [np.subtract, np.negative]
1469    _required_types = '?bBhHiIlLqQfdFD'
1470    _skip_types = 'mMO' + _LoopTypesTester._skip_types + '?'
1471
1472TestLoopTypesSubtractAndNegativeNoPython.autogenerate()
1473
1474
1475class TestLoopTypesReciprocalNoPython(_LoopTypesTester):
1476    _compile_flags = no_pyobj_flags
1477    _ufuncs = [np.reciprocal] # issue #757
1478    _required_types = 'bBhHiIlLqQfdFD'
1479    _skip_types = 'mMO' + _LoopTypesTester._skip_types
1480
1481    def _arg_for_type(self, a_letter_type, index=0):
1482        res = super(self.__class__, self)._arg_for_type(a_letter_type,
1483                                                        index=index)
1484        if a_letter_type in 'bBhHiIlLqQ':
1485            # For integer reciprocal, avoid 0 as argument, as it triggers
1486            # undefined behavior that may differ in results from Numba
1487            # to the compiler used to compile NumPy.
1488            res[res == 0] = 42
1489        return res
1490
1491TestLoopTypesReciprocalNoPython.autogenerate()
1492
1493
1494class TestLoopTypesPowerNoPython(_LoopTypesTester):
1495    _compile_flags = no_pyobj_flags
1496    _ufuncs = [np.power] # issue #757
1497    _required_types = 'bBhHiIlLqQfdFD'
1498    _skip_types = 'mMO' + _LoopTypesTester._skip_types
1499
1500    def _arg_for_type(self, a_letter_type, index=0):
1501        res = super(self.__class__, self)._arg_for_type(a_letter_type,
1502                                                        index=index)
1503        if a_letter_type in 'bBhHiIlLqQ' and index == 1:
1504            # For integer power, avoid a negative exponent, as it triggers
1505            # undefined behavior that may differ in results from Numba
1506            # to the compiler used to compile NumPy
1507            res[res < 0] = 3
1508        return res
1509
1510TestLoopTypesPowerNoPython.autogenerate()
1511
1512
1513class TestLoopTypesIntLeftShiftNoPython(_LoopTypesTester):
1514    _compile_flags = no_pyobj_flags
1515    _ufuncs = [np.left_shift]
1516    _required_types = 'bBhHiIlLqQ'
1517    _skip_types = 'fdFDmMO' + _LoopTypesTester._skip_types
1518
1519    def _arg_for_type(self, a_letter_type, index=0):
1520        res = super(self.__class__, self)._arg_for_type(a_letter_type,
1521                                                        index=index)
1522        # Shifting by a negative amount (argument with index 1) is undefined
1523        # behavior in C. It is also undefined behavior in numba. In the same
1524        # sense, it is also undefined behavior when the shift amount is larger
1525        # than the number of bits in the shifted integer.
1526        # To avoid problems in the test, the values are clamped (clipped) so
1527        # that 0 <= shift_amount < bitcount(shifted_integer)
1528        if index == 1:
1529            bit_count = res.dtype.itemsize * 8
1530            res = np.clip(res, 0, bit_count-1)
1531        return res
1532
1533TestLoopTypesIntLeftShiftNoPython.autogenerate()
1534
1535
1536class TestLoopTypesIntRightShiftNoPython(_LoopTypesTester):
1537    _compile_flags = no_pyobj_flags
1538    _ufuncs = [np.right_shift]
1539    _required_types = 'bBhHiIlLqQ'
1540    _skip_types = 'fdFDmMO' + _LoopTypesTester._skip_types
1541
1542    def _arg_for_type(self, a_letter_type, index=0):
1543        res = super(self.__class__, self)._arg_for_type(a_letter_type,
1544                                                        index=index)
1545        # Shifting by a negative amount (argument with index 1) is undefined
1546        # behavior in C. It is also undefined behavior in numba. In the same
1547        # sense, it is also undefined behavior when the shift amount is larger
1548        # than the number of bits in the shifted integer.
1549        # To avoid problems in the test, the values are clamped (clipped) so
1550        # that 0 <= shift_amount < bitcount(shifted_integer)
1551        if index == 1:
1552            bit_count = res.dtype.itemsize * 8
1553            res = np.clip(res, 0, bit_count-1)
1554
1555        # Right shift has "implementation defined behavior" when the number
1556        # shifted is negative (in C). In numba, right shift for signed integers
1557        # is "arithmetic" while for unsigned integers is "logical".
1558        # This test compares against the NumPy implementation, that relies
1559        # on "implementation defined behavior", so the test could be a false
1560        # failure if the compiler used to compile NumPy doesn't follow the same
1561        # policy.
1562        # Hint: do not rely on right shifting negative numbers in NumPy.
1563        if index == 0:
1564            res = np.abs(res)
1565        return res
1566
1567TestLoopTypesIntRightShiftNoPython.autogenerate()
1568
1569
1570class TestLoopTypesFloorDivideNoPython(_LoopTypesTester):
1571    _compile_flags = no_pyobj_flags
1572    _ufuncs = [np.floor_divide, np.remainder, np.divmod]
1573    _required_types = 'bBhHiIlLqQfdFD'
1574    _skip_types = 'mMO' + _LoopTypesTester._skip_types
1575
1576    def _fixup_results(self, dtype, py_arg, c_arg):
1577        if dtype.kind == 'f':
1578            # Discrepancies on floating-point floor division and remainder:
1579            # Numpy may return nan where Numba returns inf, e.g. 1. // 0.
1580            pred = (np.isinf(c_arg) & np.isnan(py_arg))
1581            # Numpy and Numba may differ in signed zeros, e.g. -0. // -1.
1582            pred |= (py_arg == 0.0) & (c_arg == 0.0)
1583            c_arg[pred] = py_arg[pred]
1584        return py_arg, c_arg
1585
1586TestLoopTypesFloorDivideNoPython.autogenerate()
1587
1588
1589class TestLoopTypesFloatNoPython(_LoopTypesTester):
1590    _compile_flags = no_pyobj_flags
1591    _ufuncs = supported_ufuncs[:]
1592    if iswindows:
1593        _ufuncs.remove(np.signbit) # TODO: fix issue #758
1594    _ufuncs.remove(np.floor_divide) # has its own test class
1595    _ufuncs.remove(np.remainder) # has its own test class
1596    _ufuncs.remove(np.divmod) # has its own test class
1597    _ufuncs.remove(np.mod) # same as np.remainder
1598    _required_types = 'fd'
1599    _skip_types = 'FDmMO' + _LoopTypesTester._skip_types
1600
1601TestLoopTypesFloatNoPython.autogenerate()
1602
1603
1604class TestLoopTypesComplexNoPython(_LoopTypesTester):
1605    _compile_flags = no_pyobj_flags
1606    _ufuncs = supported_ufuncs[:]
1607
1608    # Test complex types
1609    # Every loop containing a complex argument must be tested
1610    _required_types = 'FD'
1611    _skip_types = 'mMO' + _LoopTypesTester._skip_types
1612
1613TestLoopTypesComplexNoPython.autogenerate()
1614
1615
1616class TestLoopTypesDatetimeNoPython(_LoopTypesTester):
1617    _compile_flags = no_pyobj_flags
1618    _ufuncs = supported_ufuncs[:]
1619
1620    _ufuncs.remove(np.divmod)  # not implemented yet
1621
1622    # NOTE: the full list of ufuncs supporting datetime64 and timedelta64
1623    # types in Numpy is:
1624    # ['absolute', 'add', 'divide', 'equal', 'floor_divide', 'fmax', 'fmin',
1625    #  'greater', 'greater_equal', 'less', 'less_equal', 'maximum',
1626    #  'minimum', 'multiply', 'negative', 'not_equal', 'sign', 'subtract',
1627    #  'true_divide']
1628
1629    # Test datetime64 and timedelta64 types.
1630    _required_types = 'mM'
1631
1632    # Test various units combinations (TestLoopTypes is only able to test
1633    # homogeneous units).
1634
1635    def test_add(self):
1636        ufunc = np.add
1637        fn = _make_ufunc_usecase(ufunc)
1638        # heterogeneous inputs
1639        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[s]', 'm8[m]', 'm8[s]'])
1640        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[m]', 'm8[s]', 'm8[s]'])
1641        # heterogeneous inputs, scaled output
1642        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[s]', 'm8[m]', 'm8[ms]'])
1643        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[m]', 'm8[s]', 'm8[ms]'])
1644        # Cannot upscale result (Numpy would accept this)
1645        with self.assertRaises(LoweringError):
1646            self._check_ufunc_with_dtypes(fn, ufunc, ['m8[m]', 'm8[s]', 'm8[m]'])
1647
1648    def test_subtract(self):
1649        ufunc = np.subtract
1650        fn = _make_ufunc_usecase(ufunc)
1651        # heterogeneous inputs
1652        self._check_ufunc_with_dtypes(fn, ufunc, ['M8[s]', 'M8[m]', 'm8[s]'])
1653        self._check_ufunc_with_dtypes(fn, ufunc, ['M8[m]', 'M8[s]', 'm8[s]'])
1654        # heterogeneous inputs, scaled output
1655        self._check_ufunc_with_dtypes(fn, ufunc, ['M8[s]', 'M8[m]', 'm8[ms]'])
1656        self._check_ufunc_with_dtypes(fn, ufunc, ['M8[m]', 'M8[s]', 'm8[ms]'])
1657        # Cannot upscale result (Numpy would accept this)
1658        with self.assertRaises(LoweringError):
1659            self._check_ufunc_with_dtypes(fn, ufunc, ['M8[m]', 'M8[s]', 'm8[m]'])
1660
1661    def test_multiply(self):
1662        ufunc = np.multiply
1663        fn = _make_ufunc_usecase(ufunc)
1664        # scaled output
1665        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[s]', 'q', 'm8[us]'])
1666        self._check_ufunc_with_dtypes(fn, ufunc, ['q', 'm8[s]', 'm8[us]'])
1667        # Cannot upscale result (Numpy would accept this)
1668        with self.assertRaises(LoweringError):
1669            self._check_ufunc_with_dtypes(fn, ufunc, ['m8[s]', 'q', 'm8[m]'])
1670
1671    def test_true_divide(self):
1672        ufunc = np.true_divide
1673        fn = _make_ufunc_usecase(ufunc)
1674        # heterogeneous inputs
1675        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[m]', 'm8[s]', 'd'])
1676        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[s]', 'm8[m]', 'd'])
1677        # scaled output
1678        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[m]', 'q', 'm8[s]'])
1679        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[m]', 'd', 'm8[s]'])
1680        # Cannot upscale result (Numpy would accept this)
1681        with self.assertRaises(LoweringError):
1682            self._check_ufunc_with_dtypes(fn, ufunc, ['m8[s]', 'q', 'm8[m]'])
1683
1684    def test_floor_divide(self):
1685        ufunc = np.floor_divide
1686        fn = _make_ufunc_usecase(ufunc)
1687        # scaled output
1688        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[m]', 'q', 'm8[s]'])
1689        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[m]', 'd', 'm8[s]'])
1690        # Cannot upscale result (Numpy would accept this)
1691        with self.assertRaises(LoweringError):
1692            self._check_ufunc_with_dtypes(fn, ufunc, ['m8[s]', 'q', 'm8[m]'])
1693
1694    def _check_comparison(self, ufunc):
1695        fn = _make_ufunc_usecase(ufunc)
1696        # timedelta
1697        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[m]', 'm8[s]', '?'])
1698        self._check_ufunc_with_dtypes(fn, ufunc, ['m8[s]', 'm8[m]', '?'])
1699        # datetime
1700        self._check_ufunc_with_dtypes(fn, ufunc, ['M8[m]', 'M8[s]', '?'])
1701        self._check_ufunc_with_dtypes(fn, ufunc, ['M8[s]', 'M8[m]', '?'])
1702
1703    def test_comparisons(self):
1704        for ufunc in [np.equal, np.not_equal, np.less, np.less_equal,
1705                      np.greater, np.greater_equal]:
1706            self._check_comparison(ufunc)
1707
1708TestLoopTypesDatetimeNoPython.autogenerate()
1709
1710
1711class TestUFuncBadArgsNoPython(TestCase):
1712    _compile_flags = no_pyobj_flags
1713
1714    def test_missing_args(self):
1715        def func(x):
1716            """error: np.add requires two args"""
1717            result = np.add(x)
1718            return result
1719
1720        self.assertRaises(TypingError, compile_isolated, func, [types.float64],
1721                          return_type=types.float64, flags=self._compile_flags)
1722
1723
1724    def test_too_many_args(self):
1725        def func(x, out, out2):
1726            """error: too many args"""
1727            result = np.add(x, x, out, out2)
1728            return result
1729
1730        array_type = types.Array(types.float64, 1, 'C')
1731        self.assertRaises(TypingError, compile_isolated, func, [array_type] *3,
1732                          return_type=array_type, flags=self._compile_flags)
1733
1734    def test_no_scalar_result_by_reference(self):
1735        def func(x):
1736            """error: scalar as a return value is not supported"""
1737            y = 0
1738            np.add(x, x, y)
1739        self.assertRaises(TypingError, compile_isolated, func, [types.float64],
1740                          return_type=types.float64, flags=self._compile_flags)
1741
1742class TestUFuncCompilationThreadSafety(TestCase):
1743
1744    def test_lock(self):
1745        """
1746        Test that (lazy) compiling from several threads at once doesn't
1747        produce errors (see issue #2403).
1748        """
1749        errors = []
1750
1751        @vectorize
1752        def foo(x):
1753            return x + 1
1754
1755        def wrapper():
1756            try:
1757                a = np.ones((10,), dtype = np.float64)
1758                expected = np.ones((10,), dtype = np.float64) + 1.
1759                np.testing.assert_array_equal(foo(a), expected)
1760            except Exception as e:
1761                errors.append(e)
1762
1763        threads = [threading.Thread(target=wrapper) for i in range(16)]
1764        for t in threads:
1765            t.start()
1766        for t in threads:
1767            t.join()
1768        self.assertFalse(errors)
1769
1770
1771if __name__ == '__main__':
1772    unittest.main()
1773