ai-content-maker/.venv/Lib/site-packages/numba/tests/test_operators.py

1614 lines
49 KiB
Python
Raw Normal View History

2024-05-03 04:18:51 +03:00
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()