1614 lines
49 KiB
Python
1614 lines
49 KiB
Python
|
import copy
|
||
|
import itertools
|
||
|
import operator
|
||
|
import unittest
|
||
|
|
||
|
import numpy as np
|
||
|
|
||
|
from numba import jit, njit
|
||
|
from numba.core import types, utils, errors
|
||
|
from numba.core.types.functions import _header_lead
|
||
|
from numba.tests.support import TestCase, tag, needs_blas
|
||
|
from numba.tests.matmul_usecase import (matmul_usecase, imatmul_usecase,
|
||
|
DumbMatrix,)
|
||
|
|
||
|
Noflags = {'nopython': True}
|
||
|
|
||
|
force_pyobj_flags = {'forceobj': True}
|
||
|
|
||
|
|
||
|
def make_static_power(exp):
|
||
|
def pow_usecase(x):
|
||
|
return x ** exp
|
||
|
return pow_usecase
|
||
|
|
||
|
|
||
|
class LiteralOperatorImpl(object):
|
||
|
|
||
|
@staticmethod
|
||
|
def add_usecase(x, y):
|
||
|
return x + y
|
||
|
|
||
|
@staticmethod
|
||
|
def iadd_usecase(x, y):
|
||
|
x += y
|
||
|
return x
|
||
|
|
||
|
@staticmethod
|
||
|
def sub_usecase(x, y):
|
||
|
return x - y
|
||
|
|
||
|
@staticmethod
|
||
|
def isub_usecase(x, y):
|
||
|
x -= y
|
||
|
return x
|
||
|
|
||
|
@staticmethod
|
||
|
def mul_usecase(x, y):
|
||
|
return x * y
|
||
|
|
||
|
@staticmethod
|
||
|
def imul_usecase(x, y):
|
||
|
x *= y
|
||
|
return x
|
||
|
|
||
|
@staticmethod
|
||
|
def floordiv_usecase(x, y):
|
||
|
return x // y
|
||
|
|
||
|
@staticmethod
|
||
|
def ifloordiv_usecase(x, y):
|
||
|
x //= y
|
||
|
return x
|
||
|
|
||
|
@staticmethod
|
||
|
def truediv_usecase(x, y):
|
||
|
return x / y
|
||
|
|
||
|
@staticmethod
|
||
|
def itruediv_usecase(x, y):
|
||
|
x /= y
|
||
|
return x
|
||
|
|
||
|
if matmul_usecase:
|
||
|
matmul_usecase = staticmethod(matmul_usecase)
|
||
|
imatmul_usecase = staticmethod(imatmul_usecase)
|
||
|
|
||
|
@staticmethod
|
||
|
def mod_usecase(x, y):
|
||
|
return x % y
|
||
|
|
||
|
@staticmethod
|
||
|
def imod_usecase(x, y):
|
||
|
x %= y
|
||
|
return x
|
||
|
|
||
|
@staticmethod
|
||
|
def pow_usecase(x, y):
|
||
|
return x ** y
|
||
|
|
||
|
@staticmethod
|
||
|
def ipow_usecase(x, y):
|
||
|
x **= y
|
||
|
return x
|
||
|
|
||
|
@staticmethod
|
||
|
def bitshift_left_usecase(x, y):
|
||
|
return x << y
|
||
|
|
||
|
@staticmethod
|
||
|
def bitshift_ileft_usecase(x, y):
|
||
|
x <<= y
|
||
|
return x
|
||
|
|
||
|
@staticmethod
|
||
|
def bitshift_right_usecase(x, y):
|
||
|
return x >> y
|
||
|
|
||
|
@staticmethod
|
||
|
def bitshift_iright_usecase(x, y):
|
||
|
x >>= y
|
||
|
return x
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_and_usecase(x, y):
|
||
|
return x & y
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_iand_usecase(x, y):
|
||
|
x &= y
|
||
|
return x
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_or_usecase(x, y):
|
||
|
return x | y
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_ior_usecase(x, y):
|
||
|
x |= y
|
||
|
return x
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_xor_usecase(x, y):
|
||
|
return x ^ y
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_ixor_usecase(x, y):
|
||
|
x ^= y
|
||
|
return x
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_not_usecase_binary(x, _unused):
|
||
|
return ~x
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_not_usecase(x):
|
||
|
return ~x
|
||
|
|
||
|
@staticmethod
|
||
|
def not_usecase(x):
|
||
|
return not(x)
|
||
|
|
||
|
@staticmethod
|
||
|
def negate_usecase(x):
|
||
|
return -x
|
||
|
|
||
|
@staticmethod
|
||
|
def unary_positive_usecase(x):
|
||
|
return +x
|
||
|
|
||
|
@staticmethod
|
||
|
def lt_usecase(x, y):
|
||
|
return x < y
|
||
|
|
||
|
@staticmethod
|
||
|
def le_usecase(x, y):
|
||
|
return x <= y
|
||
|
|
||
|
@staticmethod
|
||
|
def gt_usecase(x, y):
|
||
|
return x > y
|
||
|
|
||
|
@staticmethod
|
||
|
def ge_usecase(x, y):
|
||
|
return x >= y
|
||
|
|
||
|
@staticmethod
|
||
|
def eq_usecase(x, y):
|
||
|
return x == y
|
||
|
|
||
|
@staticmethod
|
||
|
def ne_usecase(x, y):
|
||
|
return x != y
|
||
|
|
||
|
@staticmethod
|
||
|
def in_usecase(x, y):
|
||
|
return x in y
|
||
|
|
||
|
@staticmethod
|
||
|
def not_in_usecase(x, y):
|
||
|
return x not in y
|
||
|
|
||
|
@staticmethod
|
||
|
def is_usecase(x, y):
|
||
|
return x is y
|
||
|
|
||
|
|
||
|
class FunctionalOperatorImpl(object):
|
||
|
|
||
|
@staticmethod
|
||
|
def add_usecase(x, y):
|
||
|
return operator.add(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def iadd_usecase(x, y):
|
||
|
return operator.iadd(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def sub_usecase(x, y):
|
||
|
return operator.sub(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def isub_usecase(x, y):
|
||
|
return operator.isub(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def mul_usecase(x, y):
|
||
|
return operator.mul(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def imul_usecase(x, y):
|
||
|
return operator.imul(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def floordiv_usecase(x, y):
|
||
|
return operator.floordiv(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def ifloordiv_usecase(x, y):
|
||
|
return operator.ifloordiv(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def truediv_usecase(x, y):
|
||
|
return operator.truediv(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def itruediv_usecase(x, y):
|
||
|
return operator.itruediv(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def mod_usecase(x, y):
|
||
|
return operator.mod(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def imod_usecase(x, y):
|
||
|
return operator.imod(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def pow_usecase(x, y):
|
||
|
return operator.pow(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def ipow_usecase(x, y):
|
||
|
return operator.ipow(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def matmul_usecase(x, y):
|
||
|
return operator.matmul(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def imatmul_usecase(x, y):
|
||
|
return operator.imatmul(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def bitshift_left_usecase(x, y):
|
||
|
return operator.lshift(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def bitshift_ileft_usecase(x, y):
|
||
|
return operator.ilshift(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def bitshift_right_usecase(x, y):
|
||
|
return operator.rshift(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def bitshift_iright_usecase(x, y):
|
||
|
return operator.irshift(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_and_usecase(x, y):
|
||
|
return operator.and_(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_iand_usecase(x, y):
|
||
|
return operator.iand(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_or_usecase(x, y):
|
||
|
return operator.or_(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_ior_usecase(x, y):
|
||
|
return operator.ior(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_xor_usecase(x, y):
|
||
|
return operator.xor(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_ixor_usecase(x, y):
|
||
|
return operator.ixor(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_not_usecase_binary(x, _unused):
|
||
|
return operator.invert(x)
|
||
|
|
||
|
@staticmethod
|
||
|
def bitwise_not_usecase(x):
|
||
|
return operator.invert(x)
|
||
|
|
||
|
@staticmethod
|
||
|
def not_usecase(x):
|
||
|
return operator.not_(x)
|
||
|
|
||
|
@staticmethod
|
||
|
def negate_usecase(x):
|
||
|
return operator.neg(x)
|
||
|
|
||
|
@staticmethod
|
||
|
def unary_positive_usecase(x):
|
||
|
return operator.pos(x)
|
||
|
|
||
|
@staticmethod
|
||
|
def lt_usecase(x, y):
|
||
|
return operator.lt(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def le_usecase(x, y):
|
||
|
return operator.le(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def gt_usecase(x, y):
|
||
|
return operator.gt(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def ge_usecase(x, y):
|
||
|
return operator.ge(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def eq_usecase(x, y):
|
||
|
return operator.eq(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def ne_usecase(x, y):
|
||
|
return operator.ne(x, y)
|
||
|
|
||
|
@staticmethod
|
||
|
def in_usecase(x, y):
|
||
|
return operator.contains(y, x)
|
||
|
|
||
|
@staticmethod
|
||
|
def not_in_usecase(x, y):
|
||
|
return not operator.contains(y, x)
|
||
|
|
||
|
@staticmethod
|
||
|
def is_usecase(x, y):
|
||
|
return operator.is_(x, y)
|
||
|
|
||
|
|
||
|
class TestOperators(TestCase):
|
||
|
"""
|
||
|
Test standard Python operators on scalars.
|
||
|
|
||
|
NOTE: operators on array are generally tested in test_ufuncs.
|
||
|
"""
|
||
|
|
||
|
op = LiteralOperatorImpl
|
||
|
|
||
|
_bitwise_opnames = {
|
||
|
'bitshift_left_usecase': operator.lshift,
|
||
|
'bitshift_ileft_usecase': operator.ilshift,
|
||
|
'bitshift_right_usecase': operator.rshift,
|
||
|
'bitshift_iright_usecase': operator.irshift,
|
||
|
'bitwise_and_usecase': operator.and_,
|
||
|
'bitwise_iand_usecase': operator.iand,
|
||
|
'bitwise_or_usecase': operator.or_,
|
||
|
'bitwise_ior_usecase': operator.ior,
|
||
|
'bitwise_xor_usecase': operator.xor,
|
||
|
'bitwise_ixor_usecase': operator.ixor,
|
||
|
'bitwise_not_usecase_binary': operator.invert,
|
||
|
}
|
||
|
|
||
|
def run_test_ints(self, pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=force_pyobj_flags):
|
||
|
for arg_types in types_list:
|
||
|
cfunc = jit(arg_types, **flags)(pyfunc)
|
||
|
for x, y in itertools.product(x_operands, y_operands):
|
||
|
# For inplace ops, we check that the first operand
|
||
|
# was correctly mutated.
|
||
|
x_got = copy.copy(x)
|
||
|
x_expected = copy.copy(x)
|
||
|
got = cfunc(x_got, y)
|
||
|
expected = pyfunc(x_expected, y)
|
||
|
self.assertPreciseEqual(
|
||
|
got, expected,
|
||
|
msg="mismatch for (%r, %r) with types %s: %r != %r"
|
||
|
% (x, y, arg_types, got, expected))
|
||
|
self.assertPreciseEqual(
|
||
|
x_got, x_expected,
|
||
|
msg="mismatch for (%r, %r) with types %s: %r != %r"
|
||
|
% (x, y, arg_types, x_got, x_expected))
|
||
|
|
||
|
def run_test_floats(self, pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=force_pyobj_flags):
|
||
|
for arg_types in types_list:
|
||
|
cfunc = jit(arg_types, **flags)(pyfunc)
|
||
|
for x, y in itertools.product(x_operands, y_operands):
|
||
|
# For inplace ops, we check that the first operand
|
||
|
# was correctly mutated.
|
||
|
x_got = copy.copy(x)
|
||
|
x_expected = copy.copy(x)
|
||
|
got = cfunc(x_got, y)
|
||
|
expected = pyfunc(x_expected, y)
|
||
|
np.testing.assert_allclose(got, expected, rtol=1e-5)
|
||
|
np.testing.assert_allclose(x_got, x_expected, rtol=1e-5)
|
||
|
|
||
|
def coerce_operand(self, op, numba_type):
|
||
|
if hasattr(op, "dtype"):
|
||
|
return numba_type.cast_python_value(op)
|
||
|
elif numba_type in types.unsigned_domain:
|
||
|
return abs(int(op.real))
|
||
|
elif numba_type in types.integer_domain:
|
||
|
return int(op.real)
|
||
|
elif numba_type in types.real_domain:
|
||
|
return float(op.real)
|
||
|
else:
|
||
|
return op
|
||
|
|
||
|
def run_test_scalar_compare(self, pyfunc, flags=force_pyobj_flags,
|
||
|
ordered=True):
|
||
|
ops = self.compare_scalar_operands
|
||
|
types_list = self.compare_types
|
||
|
if not ordered:
|
||
|
types_list = types_list + self.compare_unordered_types
|
||
|
for typ in types_list:
|
||
|
cfunc = jit((typ, typ), **flags)(pyfunc)
|
||
|
for x, y in itertools.product(ops, ops):
|
||
|
x = self.coerce_operand(x, typ)
|
||
|
y = self.coerce_operand(y, typ)
|
||
|
expected = pyfunc(x, y)
|
||
|
got = cfunc(x, y)
|
||
|
# Scalar ops => scalar result
|
||
|
self.assertIs(type(got), type(expected))
|
||
|
self.assertEqual(got, expected,
|
||
|
"mismatch with %r (%r, %r)"
|
||
|
% (typ, x, y))
|
||
|
|
||
|
|
||
|
#
|
||
|
# Comparison operators
|
||
|
#
|
||
|
|
||
|
compare_scalar_operands = [-0.5, -1.0 + 1j, -1.0 + 2j, -0.5 + 1j, 1.5]
|
||
|
compare_types = [types.int32, types.int64,
|
||
|
types.uint32, types.uint64,
|
||
|
types.float32, types.float64]
|
||
|
compare_unordered_types = [types.complex64, types.complex128]
|
||
|
|
||
|
def test_lt_scalar(self, flags=force_pyobj_flags):
|
||
|
self.run_test_scalar_compare(self.op.lt_usecase, flags)
|
||
|
|
||
|
def test_lt_scalar_npm(self):
|
||
|
self.test_lt_scalar(flags=Noflags)
|
||
|
|
||
|
def test_le_scalar(self, flags=force_pyobj_flags):
|
||
|
self.run_test_scalar_compare(self.op.le_usecase, flags)
|
||
|
|
||
|
def test_le_scalar_npm(self):
|
||
|
self.test_le_scalar(flags=Noflags)
|
||
|
|
||
|
def test_gt_scalar(self, flags=force_pyobj_flags):
|
||
|
self.run_test_scalar_compare(self.op.gt_usecase, flags)
|
||
|
|
||
|
def test_gt_scalar_npm(self):
|
||
|
self.test_gt_scalar(flags=Noflags)
|
||
|
|
||
|
def test_ge_scalar(self, flags=force_pyobj_flags):
|
||
|
self.run_test_scalar_compare(self.op.ge_usecase, flags)
|
||
|
|
||
|
def test_ge_scalar_npm(self):
|
||
|
self.test_ge_scalar(flags=Noflags)
|
||
|
|
||
|
def test_eq_scalar(self, flags=force_pyobj_flags):
|
||
|
self.run_test_scalar_compare(self.op.eq_usecase, flags, ordered=False)
|
||
|
|
||
|
def test_eq_scalar_npm(self):
|
||
|
self.test_eq_scalar(flags=Noflags)
|
||
|
|
||
|
def test_ne_scalar(self, flags=force_pyobj_flags):
|
||
|
self.run_test_scalar_compare(self.op.ne_usecase, flags, ordered=False)
|
||
|
|
||
|
def test_ne_scalar_npm(self):
|
||
|
self.test_ne_scalar(flags=Noflags)
|
||
|
|
||
|
def test_is_ellipsis(self):
|
||
|
cfunc = njit((types.ellipsis, types.ellipsis))(self.op.is_usecase)
|
||
|
self.assertTrue(cfunc(Ellipsis, Ellipsis))
|
||
|
|
||
|
def test_is_void_ptr(self):
|
||
|
# can't call this directly from python, as void cannot be unboxed
|
||
|
cfunc_void = jit(
|
||
|
(types.voidptr, types.voidptr), nopython=True
|
||
|
)(self.op.is_usecase)
|
||
|
|
||
|
# this wrapper performs the casts from int to voidptr for us
|
||
|
@jit(nopython=True)
|
||
|
def cfunc(x, y):
|
||
|
return cfunc_void(x, y)
|
||
|
|
||
|
self.assertTrue(cfunc(1, 1))
|
||
|
self.assertFalse(cfunc(1, 2))
|
||
|
|
||
|
#
|
||
|
# Arithmetic operators
|
||
|
#
|
||
|
|
||
|
def run_binop_bools(self, pyfunc, flags=force_pyobj_flags):
|
||
|
x_operands = [False, False, True, True]
|
||
|
y_operands = [False, True, False, True]
|
||
|
|
||
|
types_list = [(types.boolean, types.boolean)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
def run_binop_ints(self, pyfunc, flags=force_pyobj_flags):
|
||
|
x_operands = [-5, 0, 1, 2]
|
||
|
y_operands = [-3, -1, 1, 3]
|
||
|
|
||
|
types_list = [(types.int32, types.int32),
|
||
|
(types.int64, types.int64)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = [2, 3]
|
||
|
y_operands = [1, 2]
|
||
|
|
||
|
types_list = [(types.byte, types.byte),
|
||
|
(types.uint32, types.uint32),
|
||
|
(types.uint64, types.uint64)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
def run_binop_floats(self, pyfunc, flags=force_pyobj_flags):
|
||
|
x_operands = [-1.1, 0.0, 1.1]
|
||
|
y_operands = [-1.5, 0.8, 2.1]
|
||
|
|
||
|
types_list = [(types.float32, types.float32),
|
||
|
(types.float64, types.float64)]
|
||
|
|
||
|
self.run_test_floats(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
def run_binop_floats_floordiv(self, pyfunc, flags=force_pyobj_flags):
|
||
|
self.run_binop_floats(pyfunc, flags=flags)
|
||
|
|
||
|
def run_binop_complex(self, pyfunc, flags=force_pyobj_flags):
|
||
|
x_operands = [-1.1 + 0.3j, 0.0 + 0.0j, 1.1j]
|
||
|
y_operands = [-1.5 - 0.7j, 0.8j, 2.1 - 2.0j]
|
||
|
|
||
|
types_list = [(types.complex64, types.complex64),
|
||
|
(types.complex128, types.complex128)]
|
||
|
|
||
|
self.run_test_floats(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
def generate_binop_tests(ns, usecases, tp_runners, npm_array=False):
|
||
|
for usecase in usecases:
|
||
|
for tp_name, runner_name in tp_runners.items():
|
||
|
for nopython in (False, True):
|
||
|
test_name = "test_%s_%s" % (usecase, tp_name)
|
||
|
if nopython:
|
||
|
test_name += "_npm"
|
||
|
flags = Noflags if nopython else force_pyobj_flags
|
||
|
usecase_name = "%s_usecase" % usecase
|
||
|
|
||
|
def inner(self, runner_name=runner_name,
|
||
|
usecase_name=usecase_name, flags=flags):
|
||
|
runner = getattr(self, runner_name)
|
||
|
op_usecase = getattr(self.op, usecase_name)
|
||
|
runner(op_usecase, flags)
|
||
|
|
||
|
if nopython and 'array' in tp_name and not npm_array:
|
||
|
def test_meth(self):
|
||
|
with self.assertTypingError():
|
||
|
inner()
|
||
|
else:
|
||
|
test_meth = inner
|
||
|
|
||
|
test_meth.__name__ = test_name
|
||
|
|
||
|
if nopython:
|
||
|
test_meth = tag('important')(test_meth)
|
||
|
|
||
|
ns[test_name] = test_meth
|
||
|
|
||
|
|
||
|
generate_binop_tests(locals(),
|
||
|
('add', 'iadd', 'sub', 'isub', 'mul', 'imul'),
|
||
|
{'ints': 'run_binop_ints',
|
||
|
'floats': 'run_binop_floats',
|
||
|
'complex': 'run_binop_complex',
|
||
|
})
|
||
|
|
||
|
generate_binop_tests(locals(),
|
||
|
('truediv', 'itruediv'),
|
||
|
{'ints': 'run_binop_ints',
|
||
|
'floats': 'run_binop_floats',
|
||
|
'complex': 'run_binop_complex',
|
||
|
})
|
||
|
|
||
|
# NOTE: floordiv and mod unsupported for complex numbers
|
||
|
generate_binop_tests(locals(),
|
||
|
('floordiv', 'ifloordiv', 'mod', 'imod'),
|
||
|
{'ints': 'run_binop_ints',
|
||
|
'floats': 'run_binop_floats_floordiv',
|
||
|
})
|
||
|
|
||
|
def check_div_errors(self, usecase_name, msg, flags=force_pyobj_flags,
|
||
|
allow_complex=False):
|
||
|
pyfunc = getattr(self.op, usecase_name)
|
||
|
# Signed and unsigned division can take different code paths,
|
||
|
# test them both.
|
||
|
arg_types = [types.int32, types.uint32, types.float64]
|
||
|
if allow_complex:
|
||
|
arg_types.append(types.complex128)
|
||
|
for tp in arg_types:
|
||
|
cfunc = jit((tp, tp), **flags)(pyfunc)
|
||
|
with self.assertRaises(ZeroDivisionError) as cm:
|
||
|
cfunc(1, 0)
|
||
|
# Test exception message if not in object mode
|
||
|
if flags is not force_pyobj_flags:
|
||
|
self.assertIn(msg, str(cm.exception))
|
||
|
|
||
|
def test_truediv_errors(self, flags=force_pyobj_flags):
|
||
|
self.check_div_errors("truediv_usecase", "division by zero", flags=flags,
|
||
|
allow_complex=True)
|
||
|
|
||
|
def test_truediv_errors_npm(self):
|
||
|
self.test_truediv_errors(flags=Noflags)
|
||
|
|
||
|
def test_floordiv_errors(self, flags=force_pyobj_flags):
|
||
|
self.check_div_errors("floordiv_usecase", "division by zero", flags=flags)
|
||
|
|
||
|
def test_floordiv_errors_npm(self):
|
||
|
self.test_floordiv_errors(flags=Noflags)
|
||
|
|
||
|
def test_mod_errors(self, flags=force_pyobj_flags):
|
||
|
self.check_div_errors("mod_usecase", "modulo by zero", flags=flags)
|
||
|
|
||
|
def test_mod_errors_npm(self):
|
||
|
self.test_mod_errors(flags=Noflags)
|
||
|
|
||
|
def run_pow_ints(self, pyfunc, flags=force_pyobj_flags):
|
||
|
x_operands = [-2, -1, 0, 1, 2]
|
||
|
y_operands = [0, 1, 2]
|
||
|
|
||
|
types_list = [(types.int32, types.int32),
|
||
|
(types.int64, types.int64)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = [0, 1, 2]
|
||
|
y_operands = [0, 1, 2]
|
||
|
|
||
|
types_list = [(types.byte, types.byte),
|
||
|
(types.uint32, types.uint32),
|
||
|
(types.uint64, types.uint64)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
def run_pow_floats(self, pyfunc, flags=force_pyobj_flags):
|
||
|
x_operands = [-222.222, -111.111, 111.111, 222.222]
|
||
|
y_operands = [-2, -1, 0, 1, 2]
|
||
|
|
||
|
types_list = [(types.float32, types.float32),
|
||
|
(types.float64, types.float64)]
|
||
|
|
||
|
self.run_test_floats(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = [0.0]
|
||
|
y_operands = [0, 1, 2] # TODO native handling of 0 ** negative power
|
||
|
|
||
|
types_list = [(types.float32, types.float32),
|
||
|
(types.float64, types.float64)]
|
||
|
|
||
|
self.run_test_floats(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
# XXX power operator is unsupported on complex numbers (see issue #488)
|
||
|
generate_binop_tests(locals(),
|
||
|
('pow', 'ipow'),
|
||
|
{'ints': 'run_pow_ints',
|
||
|
'floats': 'run_pow_floats',
|
||
|
})
|
||
|
|
||
|
def test_add_complex(self, flags=force_pyobj_flags):
|
||
|
pyfunc = self.op.add_usecase
|
||
|
|
||
|
x_operands = [1+0j, 1j, -1-1j]
|
||
|
y_operands = x_operands
|
||
|
|
||
|
types_list = [(types.complex64, types.complex64),
|
||
|
(types.complex128, types.complex128),]
|
||
|
|
||
|
self.run_test_floats(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
def test_add_complex_npm(self):
|
||
|
self.test_add_complex(flags=Noflags)
|
||
|
|
||
|
def test_sub_complex(self, flags=force_pyobj_flags):
|
||
|
pyfunc = self.op.sub_usecase
|
||
|
|
||
|
x_operands = [1+0j, 1j, -1-1j]
|
||
|
y_operands = [1, 2, 3]
|
||
|
|
||
|
types_list = [(types.complex64, types.complex64),
|
||
|
(types.complex128, types.complex128),]
|
||
|
|
||
|
self.run_test_floats(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
def test_sub_complex_npm(self):
|
||
|
self.test_sub_complex(flags=Noflags)
|
||
|
|
||
|
def test_mul_complex(self, flags=force_pyobj_flags):
|
||
|
pyfunc = self.op.mul_usecase
|
||
|
|
||
|
x_operands = [1+0j, 1j, -1-1j]
|
||
|
y_operands = [1, 2, 3]
|
||
|
|
||
|
types_list = [(types.complex64, types.complex64),
|
||
|
(types.complex128, types.complex128),]
|
||
|
|
||
|
self.run_test_floats(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
def test_mul_complex_npm(self):
|
||
|
self.test_mul_complex(flags=Noflags)
|
||
|
|
||
|
def test_truediv_complex(self, flags=force_pyobj_flags):
|
||
|
pyfunc = self.op.truediv_usecase
|
||
|
|
||
|
x_operands = [1+0j, 1j, -1-1j]
|
||
|
y_operands = [1, 2, 3]
|
||
|
|
||
|
types_list = [(types.complex64, types.complex64),
|
||
|
(types.complex128, types.complex128),]
|
||
|
|
||
|
self.run_test_floats(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
def test_truediv_complex_npm(self):
|
||
|
self.test_truediv_complex(flags=Noflags)
|
||
|
|
||
|
def test_mod_complex(self, flags=force_pyobj_flags):
|
||
|
pyfunc = self.op.mod_usecase
|
||
|
cres = jit((types.complex64, types.complex64), **flags)(pyfunc)
|
||
|
with self.assertRaises(TypeError) as raises:
|
||
|
cres(4j, 2j)
|
||
|
|
||
|
# error message depends on Python version.
|
||
|
if utils.PYVERSION in ((3, 9),):
|
||
|
msg = "can't mod complex numbers"
|
||
|
elif utils.PYVERSION in ((3, 10), (3, 11), (3, 12)):
|
||
|
msg = "unsupported operand type(s) for %"
|
||
|
else:
|
||
|
raise NotImplementedError(utils.PYVERSION)
|
||
|
|
||
|
self.assertIn(msg, str(raises.exception))
|
||
|
|
||
|
def test_mod_complex_npm(self):
|
||
|
pyfunc = self.op.mod_usecase
|
||
|
with self.assertTypingError():
|
||
|
njit((types.complex64, types.complex64))(pyfunc)
|
||
|
|
||
|
#
|
||
|
# Matrix multiplication
|
||
|
# (just check with simple values; computational tests are in test_linalg)
|
||
|
#
|
||
|
|
||
|
def check_matmul_objmode(self, pyfunc, inplace):
|
||
|
# Use dummy objects, to work with any NumPy / SciPy version
|
||
|
cfunc = jit((), **force_pyobj_flags)(pyfunc)
|
||
|
a = DumbMatrix(3)
|
||
|
b = DumbMatrix(4)
|
||
|
got = cfunc(a, b)
|
||
|
self.assertEqual(got.value, 12)
|
||
|
if inplace:
|
||
|
self.assertIs(got, a)
|
||
|
else:
|
||
|
self.assertIsNot(got, a)
|
||
|
self.assertIsNot(got, b)
|
||
|
|
||
|
def test_matmul(self):
|
||
|
self.check_matmul_objmode(self.op.matmul_usecase, inplace=False)
|
||
|
|
||
|
def test_imatmul(self):
|
||
|
self.check_matmul_objmode(self.op.imatmul_usecase, inplace=True)
|
||
|
|
||
|
@needs_blas
|
||
|
def check_matmul_npm(self, pyfunc):
|
||
|
arrty = types.Array(types.float32, 1, 'C')
|
||
|
cfunc = njit((arrty, arrty))(pyfunc)
|
||
|
a = np.float32([1, 2])
|
||
|
b = np.float32([3, 4])
|
||
|
got = cfunc(a, b)
|
||
|
self.assertPreciseEqual(got, np.dot(a, b))
|
||
|
# Never inplace
|
||
|
self.assertIsNot(got, a)
|
||
|
self.assertIsNot(got, b)
|
||
|
|
||
|
def test_matmul_npm(self):
|
||
|
self.check_matmul_npm(self.op.matmul_usecase)
|
||
|
|
||
|
def test_imatmul_npm(self):
|
||
|
with self.assertTypingError() as raises:
|
||
|
self.check_matmul_npm(self.op.imatmul_usecase)
|
||
|
|
||
|
#
|
||
|
# Bitwise operators
|
||
|
#
|
||
|
|
||
|
def run_bitshift_left(self, pyfunc, flags=force_pyobj_flags):
|
||
|
x_operands = [0, 1]
|
||
|
y_operands = [0, 1, 2, 4, 8, 16, 31]
|
||
|
|
||
|
types_list = [(types.uint32, types.uint32)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = [0, 1]
|
||
|
y_operands = [0, 1, 2, 4, 8, 16, 32, 63]
|
||
|
|
||
|
types_list = [(types.uint64, types.uint64)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = [0, -1]
|
||
|
y_operands = [0, 1, 2, 4, 8, 16, 31]
|
||
|
|
||
|
types_list = [(types.int32, types.int32)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = [0, -1]
|
||
|
y_operands = [0, 1, 2, 4, 8, 16, 32, 63]
|
||
|
|
||
|
types_list = [(types.int64, types.int64)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
generate_binop_tests(locals(),
|
||
|
('bitshift_left', 'bitshift_ileft'),
|
||
|
{'ints': 'run_bitshift_left',
|
||
|
})
|
||
|
|
||
|
def run_bitshift_right(self, pyfunc, flags=force_pyobj_flags):
|
||
|
x_operands = [0, 1, 2**32 - 1]
|
||
|
y_operands = [0, 1, 2, 4, 8, 16, 31]
|
||
|
|
||
|
types_list = [(types.uint32, types.uint32)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = [0, 1, 2**64 - 1]
|
||
|
y_operands = [0, 1, 2, 4, 8, 16, 32, 63]
|
||
|
|
||
|
types_list = [(types.uint64, types.uint64)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = [0, 1, -(2**31)]
|
||
|
y_operands = [0, 1, 2, 4, 8, 16, 31]
|
||
|
|
||
|
types_list = [(types.int32, types.int32)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = [0, -1, -(2**31)]
|
||
|
y_operands = [0, 1, 2, 4, 8, 16, 32, 63]
|
||
|
|
||
|
types_list = [(types.int64, types.int64)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
generate_binop_tests(locals(),
|
||
|
('bitshift_right', 'bitshift_iright'),
|
||
|
{'ints': 'run_bitshift_right',
|
||
|
})
|
||
|
|
||
|
def run_logical(self, pyfunc, flags=force_pyobj_flags):
|
||
|
x_operands = list(range(0, 8)) + [2**32 - 1]
|
||
|
y_operands = list(range(0, 8)) + [2**32 - 1]
|
||
|
|
||
|
types_list = [(types.uint32, types.uint32)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = list(range(0, 8)) + [2**64 - 1]
|
||
|
y_operands = list(range(0, 8)) + [2**64 - 1]
|
||
|
|
||
|
types_list = [(types.uint64, types.uint64)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = list(range(-4, 4)) + [-(2**31), 2**31 - 1]
|
||
|
y_operands = list(range(-4, 4)) + [-(2**31), 2**31 - 1]
|
||
|
|
||
|
types_list = [(types.int32, types.int32)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = list(range(-4, 4)) + [-(2**63), 2**63 - 1]
|
||
|
y_operands = list(range(-4, 4)) + [-(2**63), 2**63 - 1]
|
||
|
|
||
|
types_list = [(types.int64, types.int64)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
generate_binop_tests(locals(),
|
||
|
('bitwise_and', 'bitwise_iand',
|
||
|
'bitwise_or', 'bitwise_ior',
|
||
|
'bitwise_xor', 'bitwise_ixor'),
|
||
|
{'ints': 'run_logical',
|
||
|
'bools': 'run_binop_bools',
|
||
|
})
|
||
|
|
||
|
#
|
||
|
# Unary operators
|
||
|
#
|
||
|
|
||
|
def test_bitwise_not(self, flags=force_pyobj_flags):
|
||
|
pyfunc = self.op.bitwise_not_usecase_binary
|
||
|
|
||
|
x_operands = list(range(0, 8)) + [2**32 - 1]
|
||
|
x_operands = [np.uint32(x) for x in x_operands]
|
||
|
y_operands = [0]
|
||
|
|
||
|
types_list = [(types.uint32, types.uint32)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = list(range(-4, 4)) + [-(2**31), 2**31 - 1]
|
||
|
y_operands = [0]
|
||
|
|
||
|
types_list = [(types.int32, types.int32)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = list(range(0, 8)) + [2**64 - 1]
|
||
|
x_operands = [np.uint64(x) for x in x_operands]
|
||
|
y_operands = [0]
|
||
|
|
||
|
types_list = [(types.uint64, types.uint64)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
x_operands = list(range(-4, 4)) + [-(2**63), 2**63 - 1]
|
||
|
y_operands = [0]
|
||
|
|
||
|
types_list = [(types.int64, types.int64)]
|
||
|
|
||
|
self.run_test_ints(pyfunc, x_operands, y_operands, types_list,
|
||
|
flags=flags)
|
||
|
|
||
|
# For booleans, we follow Numpy semantics (i.e. ~True == False,
|
||
|
# not ~True == -2)
|
||
|
values = [False, False, True, True]
|
||
|
values = list(map(np.bool_, values))
|
||
|
|
||
|
pyfunc = self.op.bitwise_not_usecase
|
||
|
cfunc = jit((types.boolean,), **flags)(pyfunc)
|
||
|
for val in values:
|
||
|
self.assertPreciseEqual(pyfunc(val), cfunc(val))
|
||
|
|
||
|
def test_bitwise_not_npm(self):
|
||
|
self.test_bitwise_not(flags=Noflags)
|
||
|
|
||
|
def test_bitwise_float(self):
|
||
|
"""
|
||
|
Make sure that bitwise float operations are not allowed
|
||
|
"""
|
||
|
def assert_reject_compile(pyfunc, argtypes, opname):
|
||
|
msg = 'expecting TypingError when compiling {}'.format(pyfunc)
|
||
|
with self.assertRaises(errors.TypingError, msg=msg) as raises:
|
||
|
njit(argtypes)(pyfunc)
|
||
|
# check error message
|
||
|
fmt = _header_lead + ' {}'
|
||
|
expecting = fmt.format(opname
|
||
|
if isinstance(opname, str)
|
||
|
else 'Function({})'.format(opname))
|
||
|
self.assertIn(expecting, str(raises.exception))
|
||
|
|
||
|
methods = [
|
||
|
'bitshift_left_usecase',
|
||
|
'bitshift_ileft_usecase',
|
||
|
'bitshift_right_usecase',
|
||
|
'bitshift_iright_usecase',
|
||
|
'bitwise_and_usecase',
|
||
|
'bitwise_iand_usecase',
|
||
|
'bitwise_or_usecase',
|
||
|
'bitwise_ior_usecase',
|
||
|
'bitwise_xor_usecase',
|
||
|
'bitwise_ixor_usecase',
|
||
|
'bitwise_not_usecase_binary',
|
||
|
]
|
||
|
|
||
|
for name in methods:
|
||
|
pyfunc = getattr(self.op, name)
|
||
|
assert_reject_compile(pyfunc, (types.float32, types.float32),
|
||
|
opname=self._bitwise_opnames[name])
|
||
|
|
||
|
def test_not(self):
|
||
|
pyfunc = self.op.not_usecase
|
||
|
|
||
|
values = [
|
||
|
1,
|
||
|
2,
|
||
|
3,
|
||
|
1.2,
|
||
|
3.4j,
|
||
|
]
|
||
|
|
||
|
cfunc = jit((), **force_pyobj_flags)(pyfunc)
|
||
|
for val in values:
|
||
|
self.assertEqual(pyfunc(val), cfunc(val))
|
||
|
|
||
|
def test_not_npm(self):
|
||
|
pyfunc = self.op.not_usecase
|
||
|
# test native mode
|
||
|
argtys = [
|
||
|
types.int8,
|
||
|
types.int32,
|
||
|
types.int64,
|
||
|
types.float32,
|
||
|
types.complex128,
|
||
|
]
|
||
|
values = [
|
||
|
1,
|
||
|
2,
|
||
|
3,
|
||
|
1.2,
|
||
|
3.4j,
|
||
|
]
|
||
|
for ty, val in zip(argtys, values):
|
||
|
cfunc = njit((ty,))(pyfunc)
|
||
|
self.assertEqual(cfunc.nopython_signatures[0].return_type,
|
||
|
types.boolean)
|
||
|
self.assertEqual(pyfunc(val), cfunc(val))
|
||
|
|
||
|
# XXX test_negate should check for negative and positive zeros and infinities
|
||
|
|
||
|
def test_negate_npm(self):
|
||
|
pyfunc = self.op.negate_usecase
|
||
|
# test native mode
|
||
|
argtys = [
|
||
|
types.int8,
|
||
|
types.int32,
|
||
|
types.int64,
|
||
|
types.float32,
|
||
|
types.float64,
|
||
|
types.complex128,
|
||
|
types.boolean,
|
||
|
types.boolean,
|
||
|
]
|
||
|
values = [
|
||
|
1,
|
||
|
2,
|
||
|
3,
|
||
|
1.2,
|
||
|
2.4,
|
||
|
3.4j,
|
||
|
True,
|
||
|
False,
|
||
|
]
|
||
|
for ty, val in zip(argtys, values):
|
||
|
cfunc = njit((ty,))(pyfunc)
|
||
|
self.assertAlmostEqual(pyfunc(val), cfunc(val))
|
||
|
|
||
|
|
||
|
def test_negate(self):
|
||
|
pyfunc = self.op.negate_usecase
|
||
|
values = [
|
||
|
1,
|
||
|
2,
|
||
|
3,
|
||
|
1.2,
|
||
|
3.4j,
|
||
|
True,
|
||
|
False,
|
||
|
]
|
||
|
cfunc = jit((), **force_pyobj_flags)(pyfunc)
|
||
|
for val in values:
|
||
|
self.assertEqual(pyfunc(val), cfunc(val))
|
||
|
|
||
|
def test_unary_positive_npm(self):
|
||
|
pyfunc = self.op.unary_positive_usecase
|
||
|
# test native mode
|
||
|
argtys = [
|
||
|
types.int8,
|
||
|
types.int32,
|
||
|
types.int64,
|
||
|
types.float32,
|
||
|
types.float64,
|
||
|
types.complex128,
|
||
|
types.boolean,
|
||
|
types.boolean,
|
||
|
]
|
||
|
values = [
|
||
|
1,
|
||
|
2,
|
||
|
3,
|
||
|
1.2,
|
||
|
2.4,
|
||
|
3.4j,
|
||
|
True,
|
||
|
False
|
||
|
]
|
||
|
for ty, val in zip(argtys, values):
|
||
|
cfunc = njit((ty,))(pyfunc)
|
||
|
self.assertAlmostEqual(pyfunc(val), cfunc(val))
|
||
|
|
||
|
def test_unary_positive(self):
|
||
|
pyfunc = self.op.unary_positive_usecase
|
||
|
values = [
|
||
|
1,
|
||
|
2,
|
||
|
3,
|
||
|
1.2,
|
||
|
3.4j,
|
||
|
True,
|
||
|
False,
|
||
|
]
|
||
|
cfunc = jit((), **force_pyobj_flags)(pyfunc)
|
||
|
for val in values:
|
||
|
self.assertEqual(pyfunc(val), cfunc(val))
|
||
|
|
||
|
def _check_in(self, pyfunc, flags):
|
||
|
dtype = types.int64
|
||
|
cfunc = jit((dtype, types.UniTuple(dtype, 3)), **flags)(pyfunc)
|
||
|
for i in (3, 4, 5, 6, 42):
|
||
|
tup = (3, 42, 5)
|
||
|
self.assertPreciseEqual(pyfunc(i, tup), cfunc(i, tup))
|
||
|
|
||
|
def test_in(self, flags=force_pyobj_flags):
|
||
|
self._check_in(self.op.in_usecase, flags)
|
||
|
|
||
|
def test_in_npm(self):
|
||
|
self.test_in(flags=Noflags)
|
||
|
|
||
|
def test_not_in(self, flags=force_pyobj_flags):
|
||
|
self._check_in(self.op.not_in_usecase, flags)
|
||
|
|
||
|
def test_not_in_npm(self):
|
||
|
self.test_not_in(flags=Noflags)
|
||
|
|
||
|
|
||
|
class TestOperatorModule(TestOperators):
|
||
|
|
||
|
op = FunctionalOperatorImpl
|
||
|
|
||
|
_bitwise_opnames = {
|
||
|
'bitshift_left_usecase': operator.lshift,
|
||
|
'bitshift_ileft_usecase': operator.ilshift,
|
||
|
'bitshift_right_usecase': operator.rshift,
|
||
|
'bitshift_iright_usecase': operator.irshift,
|
||
|
'bitwise_and_usecase': operator.and_,
|
||
|
'bitwise_iand_usecase': operator.iand,
|
||
|
'bitwise_or_usecase': operator.or_,
|
||
|
'bitwise_ior_usecase': operator.ior,
|
||
|
'bitwise_xor_usecase': operator.xor,
|
||
|
'bitwise_ixor_usecase': operator.ixor,
|
||
|
'bitwise_not_usecase_binary': operator.invert,
|
||
|
}
|
||
|
|
||
|
|
||
|
class TestMixedInts(TestCase):
|
||
|
"""
|
||
|
Tests for operator calls with mixed integer types.
|
||
|
"""
|
||
|
|
||
|
op = LiteralOperatorImpl
|
||
|
|
||
|
int_samples = [0, 1, 3, 10, 42, 127, 10000, -1, -3, -10, -42, -127, -10000]
|
||
|
|
||
|
int_types = [types.int8, types.uint8, types.int64, types.uint64]
|
||
|
signed_types = [tp for tp in int_types if tp.signed]
|
||
|
unsigned_types = [tp for tp in int_types if not tp.signed]
|
||
|
type_pairs = list(itertools.product(int_types, int_types))
|
||
|
signed_pairs = [(u, v) for u, v in type_pairs
|
||
|
if u.signed or v.signed]
|
||
|
unsigned_pairs = [(u, v) for u, v in type_pairs
|
||
|
if not (u.signed or v.signed)]
|
||
|
|
||
|
def get_numpy_signed_upcast(self, *vals):
|
||
|
bitwidth = max(v.dtype.itemsize * 8 for v in vals)
|
||
|
bitwidth = max(bitwidth, types.intp.bitwidth)
|
||
|
return getattr(np, "int%d" % bitwidth)
|
||
|
|
||
|
def get_numpy_unsigned_upcast(self, *vals):
|
||
|
bitwidth = max(v.dtype.itemsize * 8 for v in vals)
|
||
|
bitwidth = max(bitwidth, types.intp.bitwidth)
|
||
|
return getattr(np, "uint%d" % bitwidth)
|
||
|
|
||
|
def get_typed_int(self, typ, val):
|
||
|
return getattr(np, typ.name)(val)
|
||
|
|
||
|
def get_control_signed(self, opname):
|
||
|
op = getattr(operator, opname)
|
||
|
def control_signed(a, b):
|
||
|
tp = self.get_numpy_signed_upcast(a, b)
|
||
|
return op(tp(a), tp(b))
|
||
|
return control_signed
|
||
|
|
||
|
def get_control_unsigned(self, opname):
|
||
|
op = getattr(operator, opname)
|
||
|
def control_unsigned(a, b):
|
||
|
tp = self.get_numpy_unsigned_upcast(a, b)
|
||
|
return op(tp(a), tp(b))
|
||
|
return control_unsigned
|
||
|
|
||
|
def run_binary(self, pyfunc, control_func, operands, types,
|
||
|
expected_type=int, force_type=lambda x: x,
|
||
|
**assertPreciseEqualArgs):
|
||
|
for xt, yt in types:
|
||
|
cfunc = njit((xt, yt))(pyfunc)
|
||
|
for x, y in itertools.product(operands, operands):
|
||
|
# Get Numpy typed scalars for the given types and values
|
||
|
x = self.get_typed_int(xt, x)
|
||
|
y = self.get_typed_int(yt, y)
|
||
|
expected = control_func(x, y)
|
||
|
got = cfunc(x, y)
|
||
|
self.assertIsInstance(got, expected_type)
|
||
|
msg = ("mismatch for (%r, %r) with types %s"
|
||
|
% (x, y, (xt, yt)))
|
||
|
got, expected = force_type(got), force_type(expected)
|
||
|
self.assertPreciseEqual(got, expected, msg=msg,
|
||
|
**assertPreciseEqualArgs)
|
||
|
|
||
|
def run_unary(self, pyfunc, control_func, operands, types,
|
||
|
expected_type=int):
|
||
|
for xt in types:
|
||
|
cfunc = njit((xt,))(pyfunc)
|
||
|
for x in operands:
|
||
|
x = self.get_typed_int(xt, x)
|
||
|
expected = control_func(x)
|
||
|
got = cfunc(x)
|
||
|
self.assertIsInstance(got, expected_type)
|
||
|
self.assertPreciseEqual(
|
||
|
got, expected,
|
||
|
msg="mismatch for %r with type %s: %r != %r"
|
||
|
% (x, xt, got, expected))
|
||
|
|
||
|
def run_arith_binop(self, pyfunc, opname, samples,
|
||
|
expected_type=int, force_type=lambda x: x,
|
||
|
**assertPreciseEqualArgs):
|
||
|
self.run_binary(pyfunc, self.get_control_signed(opname),
|
||
|
samples, self.signed_pairs, expected_type,
|
||
|
force_type=force_type,
|
||
|
**assertPreciseEqualArgs)
|
||
|
self.run_binary(pyfunc, self.get_control_unsigned(opname),
|
||
|
samples, self.unsigned_pairs, expected_type,
|
||
|
force_type=force_type,
|
||
|
**assertPreciseEqualArgs)
|
||
|
|
||
|
def test_add(self):
|
||
|
self.run_arith_binop(self.op.add_usecase, 'add', self.int_samples)
|
||
|
|
||
|
def test_sub(self):
|
||
|
self.run_arith_binop(self.op.sub_usecase, 'sub', self.int_samples)
|
||
|
|
||
|
def test_mul(self):
|
||
|
self.run_arith_binop(self.op.mul_usecase, 'mul', self.int_samples)
|
||
|
|
||
|
def test_floordiv(self):
|
||
|
samples = [x for x in self.int_samples if x != 0]
|
||
|
self.run_arith_binop(self.op.floordiv_usecase, 'floordiv', samples)
|
||
|
|
||
|
def test_mod(self):
|
||
|
samples = [x for x in self.int_samples if x != 0]
|
||
|
self.run_arith_binop(self.op.mod_usecase, 'mod', samples)
|
||
|
|
||
|
def test_pow(self):
|
||
|
extra_cast = {}
|
||
|
if utils.PYVERSION == (3, 11):
|
||
|
extra_cast["force_type"] = float
|
||
|
pyfunc = self.op.pow_usecase
|
||
|
# Only test with positive values, as otherwise trying to write the
|
||
|
# control function in terms of Python or Numpy power turns out insane.
|
||
|
samples = [x for x in self.int_samples if x >= 0]
|
||
|
self.run_arith_binop(pyfunc, 'pow', samples, **extra_cast)
|
||
|
|
||
|
# Now test all non-zero values, but only with signed types
|
||
|
def control_signed(a, b):
|
||
|
tp = self.get_numpy_signed_upcast(a, b)
|
||
|
if b >= 0:
|
||
|
return tp(a) ** tp(b)
|
||
|
else:
|
||
|
inv = tp(a) ** tp(-b)
|
||
|
if inv == 0:
|
||
|
# Overflow
|
||
|
return 0
|
||
|
return np.intp(1.0 / inv)
|
||
|
samples = [x for x in self.int_samples if x != 0]
|
||
|
signed_pairs = [(u, v) for u, v in self.type_pairs
|
||
|
if u.signed and v.signed]
|
||
|
self.run_binary(pyfunc, control_signed,
|
||
|
samples, signed_pairs, **extra_cast)
|
||
|
|
||
|
def test_truediv(self):
|
||
|
|
||
|
def control(a, b):
|
||
|
return float(a) / float(b)
|
||
|
samples = [x for x in self.int_samples if x != 0]
|
||
|
pyfunc = self.op.truediv_usecase
|
||
|
|
||
|
# Note: there can be precision issues on x87
|
||
|
# e.g. for `1 / 18446744073709541616`
|
||
|
# -> 0x1.0000000000002p-64 vs. 0x1.0000000000003p-64.
|
||
|
self.run_binary(pyfunc, control, samples, self.signed_pairs,
|
||
|
expected_type=float, prec='double')
|
||
|
self.run_binary(pyfunc, control, samples, self.unsigned_pairs,
|
||
|
expected_type=float, prec='double')
|
||
|
|
||
|
def test_and(self):
|
||
|
self.run_arith_binop(self.op.bitwise_and_usecase, 'and_', self.int_samples)
|
||
|
|
||
|
def test_or(self):
|
||
|
self.run_arith_binop(self.op.bitwise_or_usecase, 'or_', self.int_samples)
|
||
|
|
||
|
def test_xor(self):
|
||
|
self.run_arith_binop(self.op.bitwise_xor_usecase, 'xor', self.int_samples)
|
||
|
|
||
|
def run_shift_binop(self, pyfunc, opname):
|
||
|
opfunc = getattr(operator, opname)
|
||
|
def control_signed(a, b):
|
||
|
tp = self.get_numpy_signed_upcast(a, b)
|
||
|
return opfunc(tp(a), tp(b))
|
||
|
def control_unsigned(a, b):
|
||
|
tp = self.get_numpy_unsigned_upcast(a, b)
|
||
|
return opfunc(tp(a), tp(b))
|
||
|
|
||
|
samples = self.int_samples
|
||
|
|
||
|
def check(xt, yt, control_func):
|
||
|
cfunc = njit((xt, yt))(pyfunc)
|
||
|
for x in samples:
|
||
|
# Avoid shifting by more than the shiftand's bitwidth, as
|
||
|
# we would hit undefined behaviour.
|
||
|
maxshift = xt.bitwidth - 1
|
||
|
for y in (0, 1, 3, 5, maxshift - 1, maxshift):
|
||
|
# Get Numpy typed scalars for the given types and values
|
||
|
x = self.get_typed_int(xt, x)
|
||
|
y = self.get_typed_int(yt, y)
|
||
|
expected = control_func(x, y)
|
||
|
got = cfunc(x, y)
|
||
|
msg = ("mismatch for (%r, %r) with types %s"
|
||
|
% (x, y, (xt, yt)))
|
||
|
self.assertPreciseEqual(got, expected, msg=msg)
|
||
|
|
||
|
# For bitshifts, only the first operand's signedness matters
|
||
|
# to choose the operation's signedness.
|
||
|
signed_pairs = [(u, v) for u, v in self.type_pairs
|
||
|
if u.signed]
|
||
|
unsigned_pairs = [(u, v) for u, v in self.type_pairs
|
||
|
if not u.signed]
|
||
|
|
||
|
for xt, yt in signed_pairs:
|
||
|
check(xt, yt, control_signed)
|
||
|
for xt, yt in unsigned_pairs:
|
||
|
check(xt, yt, control_unsigned)
|
||
|
|
||
|
def test_lshift(self):
|
||
|
self.run_shift_binop(self.op.bitshift_left_usecase, 'lshift')
|
||
|
|
||
|
def test_rshift(self):
|
||
|
self.run_shift_binop(self.op.bitshift_right_usecase, 'rshift')
|
||
|
|
||
|
def test_unary_positive(self):
|
||
|
def control(a):
|
||
|
return a
|
||
|
samples = self.int_samples
|
||
|
pyfunc = self.op.unary_positive_usecase
|
||
|
|
||
|
self.run_unary(pyfunc, control, samples, self.int_types)
|
||
|
|
||
|
def test_unary_negative(self):
|
||
|
def control_signed(a):
|
||
|
tp = self.get_numpy_signed_upcast(a)
|
||
|
return tp(-a)
|
||
|
def control_unsigned(a):
|
||
|
tp = self.get_numpy_unsigned_upcast(a)
|
||
|
return tp(-a)
|
||
|
samples = self.int_samples
|
||
|
pyfunc = self.op.negate_usecase
|
||
|
|
||
|
self.run_unary(pyfunc, control_signed, samples, self.signed_types)
|
||
|
self.run_unary(pyfunc, control_unsigned, samples, self.unsigned_types)
|
||
|
|
||
|
def test_invert(self):
|
||
|
def control_signed(a):
|
||
|
tp = self.get_numpy_signed_upcast(a)
|
||
|
return tp(~a)
|
||
|
def control_unsigned(a):
|
||
|
tp = self.get_numpy_unsigned_upcast(a)
|
||
|
return tp(~a)
|
||
|
samples = self.int_samples
|
||
|
pyfunc = self.op.bitwise_not_usecase
|
||
|
|
||
|
self.run_unary(pyfunc, control_signed, samples, self.signed_types)
|
||
|
self.run_unary(pyfunc, control_unsigned, samples, self.unsigned_types)
|
||
|
|
||
|
|
||
|
class TestMixedIntsOperatorModule(TestMixedInts):
|
||
|
|
||
|
op = FunctionalOperatorImpl
|
||
|
|
||
|
|
||
|
class TestStaticPower(TestCase):
|
||
|
"""
|
||
|
Test the ** operator with a static exponent, to exercise a
|
||
|
dedicated optimization.
|
||
|
"""
|
||
|
|
||
|
def _check_pow(self, exponents, values):
|
||
|
for exp in exponents:
|
||
|
# test against non-static version of the @jit-ed function
|
||
|
regular_func = LiteralOperatorImpl.pow_usecase
|
||
|
static_func = make_static_power(exp)
|
||
|
|
||
|
static_cfunc = jit(nopython=True)(static_func)
|
||
|
regular_cfunc = jit(nopython=True)(regular_func)
|
||
|
for v in values:
|
||
|
try:
|
||
|
expected = regular_cfunc(v, exp)
|
||
|
except ZeroDivisionError:
|
||
|
with self.assertRaises(ZeroDivisionError):
|
||
|
static_cfunc(v)
|
||
|
else:
|
||
|
got = static_cfunc(v)
|
||
|
self.assertPreciseEqual(expected, got, prec='double')
|
||
|
|
||
|
def test_int_values(self):
|
||
|
exponents = [1, 2, 3, 5, 17, 0, -1, -2, -3]
|
||
|
vals = [0, 1, 3, -1, -4, np.int8(-3), np.uint16(4)]
|
||
|
|
||
|
self._check_pow(exponents, vals)
|
||
|
|
||
|
def test_real_values(self):
|
||
|
exponents = [1, 2, 3, 5, 17, 0, -1, -2, -3, 0x111111, -0x111112]
|
||
|
vals = [1.5, 3.25, -1.25, np.float32(-2.0), float('inf'), float('nan')]
|
||
|
|
||
|
self._check_pow(exponents, vals)
|
||
|
|
||
|
class TestStringConstComparison(TestCase):
|
||
|
"""
|
||
|
Test comparison of string constants
|
||
|
"""
|
||
|
def test_eq(self):
|
||
|
def test_impl1():
|
||
|
s = 'test'
|
||
|
return s == 'test'
|
||
|
|
||
|
def test_impl2():
|
||
|
s = 'test1'
|
||
|
return s == 'test'
|
||
|
|
||
|
cfunc1 = jit(nopython=True)(test_impl1)
|
||
|
cfunc2 = jit(nopython=True)(test_impl2)
|
||
|
self.assertEqual(test_impl1(), cfunc1())
|
||
|
self.assertEqual(test_impl2(), cfunc2())
|
||
|
|
||
|
def test_neq(self):
|
||
|
def test_impl1():
|
||
|
s = 'test'
|
||
|
return s != 'test'
|
||
|
|
||
|
def test_impl2():
|
||
|
s = 'test1'
|
||
|
return s != 'test'
|
||
|
|
||
|
cfunc1 = jit(nopython=True)(test_impl1)
|
||
|
cfunc2 = jit(nopython=True)(test_impl2)
|
||
|
self.assertEqual(test_impl1(), cfunc1())
|
||
|
self.assertEqual(test_impl2(), cfunc2())
|
||
|
|
||
|
class TestBooleanLiteralOperators(TestCase):
|
||
|
"""
|
||
|
Test operators with Boolean constants
|
||
|
"""
|
||
|
def test_eq(self):
|
||
|
|
||
|
def test_impl1(b):
|
||
|
return a_val == b
|
||
|
|
||
|
def test_impl2(a):
|
||
|
return a == b_val
|
||
|
|
||
|
def test_impl3():
|
||
|
r1 = True == True
|
||
|
r2 = True == False
|
||
|
r3 = False == True
|
||
|
r4 = False == False
|
||
|
return (r1, r2, r3, r4)
|
||
|
|
||
|
for a_val, b in itertools.product([True, False], repeat=2):
|
||
|
cfunc1 = jit(nopython=True)(test_impl1)
|
||
|
self.assertEqual(test_impl1(b), cfunc1(b))
|
||
|
|
||
|
for a, b_val in itertools.product([True, False], repeat=2):
|
||
|
cfunc2 = jit(nopython=True)(test_impl2)
|
||
|
self.assertEqual(test_impl2(a), cfunc2(a))
|
||
|
|
||
|
cfunc3 = jit(nopython=True)(test_impl3)
|
||
|
self.assertEqual(test_impl3(), cfunc3())
|
||
|
|
||
|
def test_ne(self):
|
||
|
|
||
|
def test_impl1(b):
|
||
|
return a_val != b
|
||
|
|
||
|
def test_impl2(a):
|
||
|
return a != b_val
|
||
|
|
||
|
def test_impl3():
|
||
|
r1 = True != True
|
||
|
r2 = True != False
|
||
|
r3 = False != True
|
||
|
r4 = False != False
|
||
|
return (r1, r2, r3, r4)
|
||
|
|
||
|
for a_val, b in itertools.product([True, False], repeat=2):
|
||
|
cfunc1 = jit(nopython=True)(test_impl1)
|
||
|
self.assertEqual(test_impl1(b), cfunc1(b))
|
||
|
|
||
|
for a, b_val in itertools.product([True, False], repeat=2):
|
||
|
cfunc2 = jit(nopython=True)(test_impl2)
|
||
|
self.assertEqual(test_impl2(a), cfunc2(a))
|
||
|
|
||
|
cfunc3 = jit(nopython=True)(test_impl3)
|
||
|
self.assertEqual(test_impl3(), cfunc3())
|
||
|
|
||
|
def test_is(self):
|
||
|
|
||
|
def test_impl1(b):
|
||
|
return a_val is b
|
||
|
|
||
|
def test_impl2():
|
||
|
r1 = True is True
|
||
|
r2 = True is False
|
||
|
r3 = False is True
|
||
|
r4 = False is False
|
||
|
return (r1, r2, r3, r4)
|
||
|
|
||
|
for a_val, b in itertools.product([True, False], repeat=2):
|
||
|
cfunc1 = jit(nopython=True)(test_impl1)
|
||
|
self.assertEqual(test_impl1(b), cfunc1(b))
|
||
|
|
||
|
cfunc2 = jit(nopython=True)(test_impl2)
|
||
|
self.assertEqual(test_impl2(), cfunc2())
|
||
|
|
||
|
def test_not(self):
|
||
|
|
||
|
def test_impl():
|
||
|
a, b = False, True
|
||
|
return (not a, not b)
|
||
|
|
||
|
cfunc = jit(nopython=True)(test_impl)
|
||
|
self.assertEqual(test_impl(), cfunc())
|
||
|
|
||
|
def test_bool(self):
|
||
|
|
||
|
def test_impl():
|
||
|
a, b = False, True
|
||
|
return (bool(a), bool(b))
|
||
|
|
||
|
cfunc = jit(nopython=True)(test_impl)
|
||
|
self.assertEqual(test_impl(), cfunc())
|
||
|
|
||
|
def test_bool_to_str(self):
|
||
|
|
||
|
def test_impl():
|
||
|
a, b = False, True
|
||
|
return (str(a), str(b))
|
||
|
|
||
|
cfunc = jit(nopython=True)(test_impl)
|
||
|
self.assertEqual(test_impl(), cfunc())
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
unittest.main()
|