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