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

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()