NumPy 源码解析(八十八)
.\numpy\numpy\_core\tests\test_umath.py
# 导入标准库和第三方库
import platform # 导入 platform 库,用于获取平台信息
import warnings # 导入 warnings 库,用于警告处理
import fnmatch # 导入 fnmatch 库,用于 Unix 文件名匹配
import itertools # 导入 itertools 库,用于迭代操作
import pytest # 导入 pytest 库,用于编写和运行测试用例
import sys # 导入 sys 库,用于系统相关的参数和函数
import os # 导入 os 库,提供了操作系统相关的功能
import operator # 导入 operator 模块,提供了一系列对应 Python 操作符的函数
from fractions import Fraction # 导入 Fraction 类,用于处理有理数
from functools import reduce # 导入 reduce 函数,用于对列表进行累积计算
from collections import namedtuple # 导入 namedtuple,用于创建命名元组
# 导入 NumPy 相关库
import numpy._core.umath as ncu # 导入 NumPy 核心的数学函数
from numpy._core import _umath_tests as ncu_tests, sctypes # 导入 NumPy 的内部测试函数和类型定义
import numpy as np # 导入 NumPy 库
from numpy.testing import ( # 导入 NumPy 测试模块的多个断言函数
assert_, assert_equal, assert_raises, assert_raises_regex,
assert_array_equal, assert_almost_equal, assert_array_almost_equal,
assert_array_max_ulp, assert_allclose, assert_no_warnings, suppress_warnings,
_gen_alignment_data, assert_array_almost_equal_nulp, IS_WASM, IS_MUSL,
IS_PYPY, HAS_REFCOUNT
)
from numpy.testing._private.utils import _glibc_older_than # 导入 NumPy 测试私有模块中的一个函数
# 获取所有的 NumPy 通用函数 (ufuncs)
UFUNCS = [obj for obj in np._core.umath.__dict__.values()
if isinstance(obj, np.ufunc)]
# 将 NumPy 一元通用函数筛选出来
UFUNCS_UNARY = [
uf for uf in UFUNCS if uf.nin == 1
]
# 进一步筛选出 NumPy 浮点数一元通用函数
UFUNCS_UNARY_FP = [
uf for uf in UFUNCS_UNARY if 'f->f' in uf.types
]
# 将 NumPy 二元通用函数筛选出来
UFUNCS_BINARY = [
uf for uf in UFUNCS if uf.nin == 2
]
# 筛选出具有累积属性的 NumPy 二元通用函数
UFUNCS_BINARY_ACC = [
uf for uf in UFUNCS_BINARY if hasattr(uf, "accumulate") and uf.nout == 1
]
def interesting_binop_operands(val1, val2, dtype):
"""
Helper to create "interesting" operands to cover common code paths:
* scalar inputs
* only first "values" is an array (e.g. scalar division fast-paths)
* Longer array (SIMD) placing the value of interest at different positions
* Oddly strided arrays which may not be SIMD compatible
It does not attempt to cover unaligned access or mixed dtypes.
These are normally handled by the casting/buffering machinery.
This is not a fixture (currently), since I believe a fixture normally
only yields once?
"""
fill_value = 1 # 可能是一个参数,但可能不是一个可选参数
# 创建长度为 10003 的数组,并填充为指定的值和数据类型
arr1 = np.full(10003, dtype=dtype, fill_value=fill_value)
arr2 = np.full(10003, dtype=dtype, fill_value=fill_value)
arr1[0] = val1 # 在数组中的第一个位置设置值 val1
arr2[0] = val2 # 在数组中的第一个位置设置值 val2
# 定义一个结果提取函数
extractor = lambda res: res
# 返回包含不同情况下的操作数及其描述的生成器对象
yield arr1[0], arr2[0], extractor, "scalars"
# 重新定义结果提取函数
extractor = lambda res: res
# 返回包含不同情况下的操作数及其描述的生成器对象
yield arr1[0, ...], arr2[0, ...], extractor, "scalar-arrays"
# 将数组值重置为 fill_value
arr1[0] = fill_value
arr2[0] = fill_value
# 遍历指定的位置列表
for pos in [0, 1, 2, 3, 4, 5, -1, -2, -3, -4]:
arr1[pos] = val1 # 在指定位置设置值 val1
arr2[pos] = val2 # 在指定位置设置值 val2
# 定义一个结果提取函数
extractor = lambda res: res[pos]
# 返回包含不同情况下的操作数及其描述的生成器对象
yield arr1, arr2, extractor, f"off-{pos}"
yield arr1, arr2[pos], extractor, f"off-{pos}-with-scalar"
# 将数组值重置为 fill_value
arr1[pos] = fill_value
arr2[pos] = fill_value
# 遍历指定的步长列表
for stride in [-1, 113]:
op1 = arr1[::stride]
op2 = arr2[::stride]
op1[10] = val1 # 在指定位置设置值 val1
op2[10] = val2 # 在指定位置设置值 val2
# 定义一个结果提取函数
extractor = lambda res: res[10]
# 返回包含不同情况下的操作数及其描述的生成器对象
yield op1, op2, extractor, f"stride-{stride}"
# 将数组值重置为 fill_value
op1[10] = fill_value
op2[10] = fill_value
def on_powerpc():
""" True if we are running on a Power PC platform."""
# 检查当前平台的处理器是否为 'powerpc',或者是否机器类型以 'ppc' 开头
return platform.processor() == 'powerpc' or \
platform.machine().startswith('ppc')
# 定义函数 bad_arcsinh,用于检查不同平台上的 arcsinh 函数的精度问题
def bad_arcsinh():
# 检查当前平台是否为 aarch64
if platform.machine() == 'aarch64':
# 如果是 aarch64,设置 x 的值
x = 1.78e-10
elif on_powerpc():
# 如果是 PowerPC 平台,设置 x 的值
x = 2.16e-10
else:
# 如果不是以上两种平台,返回 False
return False
# 计算 np.arcsinh(np.float128(x)) 的值
v1 = np.arcsinh(np.float128(x))
# 计算 np.arcsinh(np.complex256(x)).real 的值
v2 = np.arcsinh(np.complex256(x)).real
# 比较 v1 和 v2 的绝对误差是否大于给定阈值
# The eps for float128 is 1-e33, so this is way bigger
return abs((v1 / v2) - 1.0) > 1e-23
# 定义一个类 _FilterInvalids 用于处理无效值的过滤器设置和恢复
class _FilterInvalids:
# 设置方法,在测试前设置 np 的错误处理方式,将无效值设为忽略
def setup_method(self):
self.olderr = np.seterr(invalid='ignore')
# 拆卸方法,在测试后恢复 np 的错误处理方式
def teardown_method(self):
np.seterr(**self.olderr)
# 定义一个测试类 TestConstants,用于测试数学常数
class TestConstants:
# 测试 pi 值是否接近预期值
def test_pi(self):
assert_allclose(ncu.pi, 3.141592653589793, 1e-15)
# 测试 e 值是否接近预期值
def test_e(self):
assert_allclose(ncu.e, 2.718281828459045, 1e-15)
# 测试 Euler's gamma 值是否接近预期值
def test_euler_gamma(self):
assert_allclose(ncu.euler_gamma, 0.5772156649015329, 1e-15)
# 定义一个测试方法 `test_out_subok`,用于测试 `np.add` 和 `np.frexp` 函数的输出参数功能
def test_out_subok(self):
# 对于 `subok` 参数分别为 True 和 False 两种情况进行测试
for subok in (True, False):
# 创建一个包含单个浮点数 0.5 的 NumPy 数组 `a`
a = np.array(0.5)
# 创建一个空的 NumPy 数组 `o`
o = np.empty(())
# 使用 `np.add` 函数,将 `a` 和标量 2 相加,将结果存储在 `o` 中,指定 `subok` 参数
r = np.add(a, 2, o, subok=subok)
# 断言 `r` 和 `o` 是同一个对象
assert_(r is o)
# 再次使用 `np.add` 函数,将 `a` 和标量 2 相加,将结果存储在 `o` 中,指定 `out` 和 `subok` 参数
r = np.add(a, 2, out=o, subok=subok)
# 断言 `r` 和 `o` 是同一个对象
assert_(r is o)
# 继续使用 `np.add` 函数,将 `a` 和标量 2 相加,将结果存储在元组 `(o,)` 中的第一个元素中,指定 `out` 和 `subok` 参数
r = np.add(a, 2, out=(o,), subok=subok)
# 断言 `r` 和 `o` 是同一个对象
assert_(r is o)
# 创建一个包含单个浮点数 5.7 的 NumPy 数组 `d`
d = np.array(5.7)
# 创建一个空的 NumPy 数组 `o1`
o1 = np.empty(())
# 创建一个空的 NumPy 数组 `o2`,指定数据类型为 `np.int32`
o2 = np.empty((), dtype=np.int32)
# 使用 `np.frexp` 函数,对 `d` 进行浮点数表示的分解,将结果存储在 `o1` 和 `None` 中,指定 `subok` 参数
r1, r2 = np.frexp(d, o1, None, subok=subok)
# 断言 `r1` 和 `o1` 是同一个对象
assert_(r1 is o1)
# 再次使用 `np.frexp` 函数,对 `d` 进行浮点数表示的分解,将结果存储在 `None` 和 `o2` 中,指定 `subok` 参数
r1, r2 = np.frexp(d, None, o2, subok=subok)
# 断言 `r2` 和 `o2` 是同一个对象
assert_(r2 is o2)
# 继续使用 `np.frexp` 函数,对 `d` 进行浮点数表示的分解,将结果存储在 `o1` 和 `o2` 中,指定 `subok` 参数
r1, r2 = np.frexp(d, o1, o2, subok=subok)
# 断言 `r1` 和 `o1` 是同一个对象
assert_(r1 is o1)
# 断言 `r2` 和 `o2` 是同一个对象
assert_(r2 is o2)
# 使用 `np.frexp` 函数,对 `d` 进行浮点数表示的分解,将结果存储在元组 `(o1, None)` 中的第一个元素中,指定 `out` 和 `subok` 参数
r1, r2 = np.frexp(d, out=(o1, None), subok=subok)
# 断言 `r1` 和 `o1` 是同一个对象
assert_(r1 is o1)
# 再次使用 `np.frexp` 函数,对 `d` 进行浮点数表示的分解,将结果存储在元组 `(None, o2)` 中的第二个元素中,指定 `out` 和 `subok` 参数
r1, r2 = np.frexp(d, out=(None, o2), subok=subok)
# 断言 `r2` 和 `o2` 是同一个对象
assert_(r2 is o2)
# 继续使用 `np.frexp` 函数,对 `d` 进行浮点数表示的分解,将结果存储在元组 `(o1, o2)` 中,指定 `out` 和 `subok` 参数
r1, r2 = np.frexp(d, out=(o1, o2), subok=subok)
# 断言 `r1` 和 `o1` 是同一个对象
assert_(r1 is o1)
# 断言 `r2` 和 `o2` 是同一个对象
assert_(r2 is o2)
# 使用 `assert_raises` 上下文管理器,断言 `np.frexp` 函数在传递单个输出参数 `o1` 时会抛出 `TypeError` 异常
with assert_raises(TypeError):
r1, r2 = np.frexp(d, out=o1, subok=subok)
# 使用 `assert_raises` 函数,断言 `np.add` 函数在传递多个输出参数 `o, o` 时会抛出 `TypeError` 异常
assert_raises(TypeError, np.add, a, 2, o, o, subok=subok)
# 使用 `assert_raises` 函数,断言 `np.add` 函数在传递多个输出参数 `o, out=o` 时会抛出 `TypeError` 异常
assert_raises(TypeError, np.add, a, 2, o, out=o, subok=subok)
# 使用 `assert_raises` 函数,断言 `np.add` 函数在传递多个输出参数 `None, out=o` 时会抛出 `TypeError` 异常
assert_raises(TypeError, np.add, a, 2, None, out=o, subok=subok)
# 使用 `assert_raises` 函数,断言 `np.add` 函数在传递多个输出参数 `(o, o)` 时会抛出 `ValueError` 异常
assert_raises(ValueError, np.add, a, 2, out=(o, o), subok=subok)
# 使用 `assert_raises` 函数,断言 `np.add` 函数在传递多个输出参数 `()` 时会抛出 `ValueError` 异常
assert_raises(ValueError, np.add, a, 2, out=(), subok=subok)
# 使用 `assert_raises` 函数,断言 `np.add` 函数在传递多个输出参数 `[]` 时会抛出 `TypeError` 异常
assert_raises(TypeError, np.add, a, 2, [], subok=subok)
# 使用 `assert_raises` 函数,断言 `np.add` 函数在传递多个输出参数 `out=[]` 时会抛出 `TypeError` 异常
assert_raises(TypeError, np.add, a, 2, out=[], subok=subok)
# 使用 `assert_raises` 函数,断言 `np.add` 函数在传递多个输出参数 `out=([],)` 时会抛出 `TypeError` 异常
assert_raises(TypeError, np.add, a, 2, out=([],), subok=subok)
# 设置 `o` 的标志 `writeable` 为 `False`
o.flags.writeable = False
# 使用 `assert_raises` 函数,断言 `np.add` 函数在输出参数 `o` 不可写时会抛出 `ValueError` 异常
assert_raises(ValueError, np.add, a, 2, o, subok=subok)
# 使用 `assert_raises` 函数,断言 `np.add` 函数在输出参数 `out=o` 不可写时会抛出 `ValueError` 异常
assert_raises(ValueError, np.add, a, 2, out=o, subok=subok)
# 使用 `assert_raises` 函数,断言 `np.add` 函数在输出参数 `out=(o,)` 中的 `o` 不可写时会抛出 `ValueError` 异常
assert_raises(ValueError, np.add, a, 2, out=(o,), subok=subok)
# 定义测试函数 test_out_wrap_subok,用于测试带有自定义行为的子类 ArrayWrap
def test_out_wrap_subok(self):
# 定义一个数组包装类 ArrayWrap,继承自 np.ndarray
class ArrayWrap(np.ndarray):
# 设置数组包装类的优先级
__array_priority__ = 10
# 构造函数,将输入数组 arr 转换为 ArrayWrap 类型的实例并复制其内容
def __new__(cls, arr):
return np.asarray(arr).view(cls).copy()
# 定义数组包装类的 __array_wrap__ 方法,以自定义的方式处理数组操作的返回值
def __array_wrap__(self, arr, context=None, return_scalar=False):
return arr.view(type(self))
# 循环遍历 subok 取值为 True 和 False
for subok in (True, False):
# 创建 ArrayWrap 实例 a,包装 [0.5]
a = ArrayWrap([0.5])
# 使用 np.add 进行数组加法操作,测试 subok 参数的影响
r = np.add(a, 2, subok=subok)
# 如果 subok 为 True,断言返回值 r 类型为 ArrayWrap
if subok:
assert_(isinstance(r, ArrayWrap))
else:
# 如果 subok 为 False,断言返回值 r 类型为 np.ndarray
assert_(type(r) == np.ndarray)
# 同上,使用 np.add 进行数组加法操作,测试 None 参数和 subok 的影响
r = np.add(a, 2, None, subok=subok)
if subok:
assert_(isinstance(r, ArrayWrap))
else:
assert_(type(r) == np.ndarray)
# 同上,使用 np.add 进行数组加法操作,测试 out=None 和 subok 的影响
r = np.add(a, 2, out=None, subok=subok)
if subok:
assert_(isinstance(r, ArrayWrap))
else:
assert_(type(r) == np.ndarray)
# 同上,使用 np.add 进行数组加法操作,测试 out=(None,) 和 subok 的影响
r = np.add(a, 2, out=(None,), subok=subok)
if subok:
assert_(isinstance(r, ArrayWrap))
else:
assert_(type(r) == np.ndarray)
# 创建 ArrayWrap 实例 d,包装 [5.7]
d = ArrayWrap([5.7])
# 创建 np.ndarray 实例 o1 和 o2
o1 = np.empty((1,))
o2 = np.empty((1,), dtype=np.int32)
# 使用 np.frexp 对 d 进行浮点数拆分操作,测试 subok 参数的影响
r1, r2 = np.frexp(d, o1, subok=subok)
if subok:
assert_(isinstance(r2, ArrayWrap))
else:
assert_(type(r2) == np.ndarray)
# 同上,使用 np.frexp 对 d 进行浮点数拆分操作,测试 None 参数和 subok 的影响
r1, r2 = np.frexp(d, o1, None, subok=subok)
if subok:
assert_(isinstance(r2, ArrayWrap))
else:
assert_(type(r2) == np.ndarray)
# 同上,使用 np.frexp 对 d 进行浮点数拆分操作,测试 None 和 o2 参数的影响
r1, r2 = np.frexp(d, None, o2, subok=subok)
if subok:
assert_(isinstance(r1, ArrayWrap))
else:
assert_(type(r1) == np.ndarray)
# 同上,使用 np.frexp 对 d 进行浮点数拆分操作,测试 out=(o1, None) 和 subok 的影响
r1, r2 = np.frexp(d, out=(o1, None), subok=subok)
if subok:
assert_(isinstance(r2, ArrayWrap))
else:
assert_(type(r2) == np.ndarray)
# 同上,使用 np.frexp 对 d 进行浮点数拆分操作,测试 out=(None, o2) 和 subok 的影响
r1, r2 = np.frexp(d, out=(None, o2), subok=subok)
if subok:
assert_(isinstance(r1, ArrayWrap))
else:
assert_(type(r1) == np.ndarray)
# 使用 assert_raises 断言捕获 TypeError 异常,测试对于非 tuple 类型的 out 参数的处理
with assert_raises(TypeError):
# 运行 np.frexp,其中 out 参数为非 tuple 类型,预期抛出 TypeError 异常
r1, r2 = np.frexp(d, out=o1, subok=subok)
# 定义一个测试类 TestComparisons,用于测试比较函数的行为
class TestComparisons:
# 导入 operator 模块,用于比较操作符的测试
import operator
# 使用 pytest 的参数化装饰器,测试不同数据类型的比较
@pytest.mark.parametrize('dtype', sctypes['uint'] + sctypes['int'] +
sctypes['float'] + [np.bool])
# 参数化测试比较函数,比较 Python 内建操作符与 NumPy 操作函数
@pytest.mark.parametrize('py_comp,np_comp', [
(operator.lt, np.less),
(operator.le, np.less_equal),
(operator.gt, np.greater),
(operator.ge, np.greater_equal),
(operator.eq, np.equal),
(operator.ne, np.not_equal)
])
# 定义测试比较函数的方法
def test_comparison_functions(self, dtype, py_comp, np_comp):
# 初始化输入数组
if dtype == np.bool:
a = np.random.choice(a=[False, True], size=1000)
b = np.random.choice(a=[False, True], size=1000)
scalar = True
else:
a = np.random.randint(low=1, high=10, size=1000).astype(dtype)
b = np.random.randint(low=1, high=10, size=1000).astype(dtype)
scalar = 5
# 使用 NumPy 类型创建标量
np_scalar = np.dtype(dtype).type(scalar)
# 转换数组为列表
a_lst = a.tolist()
b_lst = b.tolist()
# (Binary) Comparison (x1=array, x2=array)
# 进行二进制比较,并将结果视图转换为无符号整数列表
comp_b = np_comp(a, b).view(np.uint8)
comp_b_list = [int(py_comp(x, y)) for x, y in zip(a_lst, b_lst)]
# (Scalar1) Comparison (x1=scalar, x2=array)
# 进行标量与数组的比较,并将结果视图转换为无符号整数列表
comp_s1 = np_comp(np_scalar, b).view(np.uint8)
comp_s1_list = [int(py_comp(scalar, x)) for x in b_lst]
# (Scalar2) Comparison (x1=array, x2=scalar)
# 进行数组与标量的比较,并将结果视图转换为无符号整数列表
comp_s2 = np_comp(a, np_scalar).view(np.uint8)
comp_s2_list = [int(py_comp(x, scalar)) for x in a_lst]
# 检查三种比较的结果是否符合预期
assert_(comp_b.tolist() == comp_b_list,
f"Failed comparison ({py_comp.__name__})")
assert_(comp_s1.tolist() == comp_s1_list,
f"Failed comparison ({py_comp.__name__})")
assert_(comp_s2.tolist() == comp_s2_list,
f"Failed comparison ({py_comp.__name__})")
# 测试在相等比较中忽略对象的身份
def test_ignore_object_identity_in_equal(self):
# 检查比较相同对象时抛出异常,例如数组的元素逐个比较
a = np.array([np.array([1, 2, 3]), None], dtype=object)
assert_raises(ValueError, np.equal, a, a)
# 检查比较不可比较的相同对象时抛出异常
class FunkyType:
def __eq__(self, other):
raise TypeError("I won't compare")
a = np.array([FunkyType()])
assert_raises(TypeError, np.equal, a, a)
# 检查 NaN 的身份不会覆盖比较不匹配
a = np.array([np.nan], dtype=object)
assert_equal(np.equal(a, a), [False])
def test_ignore_object_identity_in_not_equal(self):
# 检查对比较复杂的对象进行比较时,如逐元素比较的数组。
a = np.array([np.array([1, 2, 3]), None], dtype=object)
# 断言应该抛出 ValueError 异常,因为比较的对象是相同的。
assert_raises(ValueError, np.not_equal, a, a)
# 检查当比较不可比较的相同对象时是否抛出错误。
class FunkyType:
def __ne__(self, other):
raise TypeError("I won't compare")
a = np.array([FunkyType()])
# 断言应该抛出 TypeError 异常,因为比较的对象不可比较。
assert_raises(TypeError, np.not_equal, a, a)
# 检查即使是相同的对象,其身份也不会覆盖比较不匹配。
a = np.array([np.nan], dtype=object)
# 断言 np.not_equal 返回 [True],即使比较的对象相同也应该如此。
assert_equal(np.not_equal(a, a), [True])
def test_error_in_equal_reduce(self):
# gh-20929
# 确保当传递未指定 dtype 的数组给 np.equal.reduce 时会引发 TypeError。
a = np.array([0, 0])
assert_equal(np.equal.reduce(a, dtype=bool), True)
assert_raises(TypeError, np.equal.reduce, a)
def test_object_dtype(self):
# 断言 np.equal 的结果 dtype 应该是 object。
assert np.equal(1, [1], dtype=object).dtype == object
# 使用 signature 参数检查 np.equal 的结果 dtype 是否为 object。
assert np.equal(1, [1], signature=(None, None, "O")).dtype == object
def test_object_nonbool_dtype_error(self):
# bool 输出 dtype 是正常的:
assert np.equal(1, [1], dtype=bool).dtype == bool
# 但以下示例没有匹配的循环:
with pytest.raises(TypeError, match="No loop matching"):
np.equal(1, 1, dtype=np.int64)
with pytest.raises(TypeError, match="No loop matching"):
np.equal(1, 1, sig=(None, None, "l"))
@pytest.mark.parametrize("dtypes", ["qQ", "Qq"])
@pytest.mark.parametrize('py_comp, np_comp', [
(operator.lt, np.less),
(operator.le, np.less_equal),
(operator.gt, np.greater),
(operator.ge, np.greater_equal),
(operator.eq, np.equal),
(operator.ne, np.not_equal)
])
@pytest.mark.parametrize("vals", [(2**60, 2**60+1), (2**60+1, 2**60)])
def test_large_integer_direct_comparison(
self, dtypes, py_comp, np_comp, vals):
# 注意 float(2**60) + 1 == float(2**60)。
a1 = np.array([2**60], dtype=dtypes[0])
a2 = np.array([2**60 + 1], dtype=dtypes[1])
expected = py_comp(2**60, 2**60+1)
assert py_comp(a1, a2) == expected
assert np_comp(a1, a2) == expected
# 还要检查标量:
s1 = a1[0]
s2 = a2[0]
# 断言 s1 和 s2 是 np.integer 类型。
assert isinstance(s1, np.integer)
assert isinstance(s2, np.integer)
# Python 操作符在这里是主要的兴趣点:
assert py_comp(s1, s2) == expected
assert np_comp(s1, s2) == expected
@pytest.mark.parametrize("dtype", np.typecodes['UnsignedInteger'])
# 使用 pytest 的参数化功能,定义了多组测试参数,每组参数包含一个 Python 操作符和一个 NumPy 操作符
@pytest.mark.parametrize('py_comp_func, np_comp_func', [
(operator.lt, np.less),
(operator.le, np.less_equal),
(operator.gt, np.greater),
(operator.ge, np.greater_equal),
(operator.eq, np.equal),
(operator.ne, np.not_equal)
])
# 再次使用 pytest 的参数化功能,为 flip 参数定义了两个测试用例:True 和 False
@pytest.mark.parametrize("flip", [True, False])
# 定义了一个测试方法,用于测试无符号和有符号整数的直接比较
def test_unsigned_signed_direct_comparison(
self, dtype, py_comp_func, np_comp_func, flip):
# 根据 flip 参数的值,选择性地反转 py_comp_func 和 np_comp_func 的参数位置
if flip:
py_comp = lambda x, y: py_comp_func(y, x)
np_comp = lambda x, y: np_comp_func(y, x)
else:
py_comp = py_comp_func
np_comp = np_comp_func
# 创建一个包含单个最大值的 NumPy 数组,使用指定的 dtype 类型
arr = np.array([np.iinfo(dtype).max], dtype=dtype)
# 计算预期结果,用 Python 操作符对数组的最大值和 -1 进行比较
expected = py_comp(int(arr[0]), -1)
# 断言语句:使用 Python 操作符和 NumPy 操作符对数组和 -1 进行比较,验证结果是否符合预期
assert py_comp(arr, -1) == expected
assert np_comp(arr, -1) == expected
# 获取数组的第一个元素作为标量
scalar = arr[0]
# 断言语句:验证标量是否为 NumPy 的整数类型
assert isinstance(scalar, np.integer)
# 断言语句:再次使用 Python 操作符对标量和 -1 进行比较,验证结果是否符合预期
# 这里主要关注 Python 操作符的使用
assert py_comp(scalar, -1) == expected
assert np_comp(scalar, -1) == expected
```python`
class TestAdd:
def test_reduce_alignment(self):
# 测试用例标识为 gh-9876
# 确保具有奇怪步幅的数组在 pairwise_sum_@TYPE@ 优化中能正常工作。
# 在 x86 上,'b' 字段将以 4 字节偏移量对齐,尽管其 itemsize 是 8。
# 创建一个包含两个元素的零数组,数据类型为 [('a', np.int32), ('b', np.float64)]
a = np.zeros(2, dtype=[('a', np.int32), ('b', np.float64)])
# 将 'a' 字段全部设为 -1
a['a'] = -1
# 断言 'b' 字段的总和为 0
assert_equal(a['b'].sum(), 0)
class TestDivision:
def test_division_int(self):
# 整数除法应遵循 Python 的规则
# 创建一个包含各种整数的数组
x = np.array([5, 10, 90, 100, -5, -10, -90, -100, -120])
# 如果 5 / 10 等于 0.5,则断言 x / 100 的结果符合预期
if 5 / 10 == 0.5:
assert_equal(x / 100, [0.05, 0.1, 0.9, 1,
-0.05, -0.1, -0.9, -1, -1.2])
else:
# 否则,断言 x / 100 的结果应为 [0, 0, 0, 1, -1, -1, -1, -1, -2]
assert_equal(x / 100, [0, 0, 0, 1, -1, -1, -1, -1, -2])
# 断言 x // 100 的结果应为 [0, 0, 0, 1, -1, -1, -1, -1, -2]
assert_equal(x // 100, [0, 0, 0, 1, -1, -1, -1, -1, -2])
# 断言 x % 100 的结果应为 [5, 10, 90, 0, 95, 90, 10, 0, 80]
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
@pytest.mark.parametrize("dtype,ex_val", itertools.product(
sctypes['int'] + sctypes['uint'], (
(
# 被除数
"np.array(range(fo.max-lsize, fo.max)).astype(dtype),"
# 除数
"np.arange(lsize).astype(dtype),"
# 标量除数
"range(15)"
),
(
# 被除数
"np.arange(fo.min, fo.min+lsize).astype(dtype),"
# 除数
"np.arange(lsize//-2, lsize//2).astype(dtype),"
# 标量除数
"range(fo.min, fo.min + 15)"
), (
# 被除数
"np.array(range(fo.max-lsize, fo.max)).astype(dtype),"
# 除数
"np.arange(lsize).astype(dtype),"
# 标量除数
"[1,3,9,13astype(dtype),"
# 注释:除数数组
"np.arange(lsize).astype(dtype),"
# 注释:标量除数
"[1,3,9,13,neg, fo.min+1, fo.min//2, fo.max//3, fo.max//4]"
)
)
))
# 定义一个测试方法,用于测试整数除法的边界情况
def test_division_int_boundary(self, dtype, ex_val):
# 获取指定数据类型的整数信息
fo = np.iinfo(dtype)
# 确定负数的符号,如果fo.min小于0则为-1,否则为1
neg = -1 if fo.min < 0 else 1
# 设置一个足够大的数组大小,以测试SIMD循环和余数元素
lsize = 512 + 7
# 解析传入的测试数据,分别为数组a、b和除数列表divisors
a, b, divisors = eval(ex_val)
# 将数组a和b转换为列表形式
a_lst, b_lst = a.tolist(), b.tolist()
# 定义一个匿名函数c_div,用于计算整数除法,处理除数为0或特定边界情况
c_div = lambda n, d: (
0 if d == 0 else (
fo.min if (n and n == fo.min and d == -1) else n//d
)
)
# 在处理除零错误时,忽略除法相关的错误
with np.errstate(divide='ignore'):
# 复制数组a,对其执行原地整除运算
ac = a.copy()
ac //= b
# 计算a与b的整数除法结果
div_ab = a // b
# 使用列表推导式计算a_lst与b_lst的每个元素的整数除法结果,存储在div_lst中
div_lst = [c_div(x, y) for x, y in zip(a_lst, b_lst)]
# 断言整数数组的整数除法结果与预期的div_lst相等
msg = "Integer arrays floor division check (//)"
assert all(div_ab == div_lst), msg
# 断言原地整数除法的结果与预期的div_lst相等
msg_eq = "Integer arrays floor division check (//=)"
assert all(ac == div_lst), msg_eq
# 遍历除数列表,对数组a执行整数除法操作,再次验证结果与预期相等
for divisor in divisors:
ac = a.copy()
with np.errstate(divide='ignore', over='ignore'):
div_a = a // divisor
ac //= divisor
div_lst = [c_div(i, divisor) for i in a_lst]
# 断言整数数组的整数除法结果与预期的div_lst相等
assert all(div_a == div_lst), msg
# 断言原地整数除法的结果与预期的div_lst相等
assert all(ac == div_lst), msg_eq
# 在处理除零或溢出错误时,引发特定异常
with np.errstate(divide='raise', over='raise'):
if 0 in b:
# 如果b中包含0,则验证除零错误情况
with pytest.raises(FloatingPointError,
match="divide by zero encountered in floor_divide"):
a // b
else:
a // b
if fo.min and fo.min in a:
# 如果fo.min不为0且在a中,则验证溢出错误情况
with pytest.raises(FloatingPointError,
match='overflow encountered in floor_divide'):
a // -1
elif fo.min:
a // -1
# 验证除零错误情况
with pytest.raises(FloatingPointError,
match="divide by zero encountered in floor_divide"):
a // 0
# 验证原地除零错误情况
with pytest.raises(FloatingPointError,
match="divide by zero encountered in floor_divide"):
ac = a.copy()
ac //= 0
# 创建一个空数组,并验证其与零的整数除法
np.array([], dtype=dtype) // 0
# 定义一个测试方法,用于测试整数除法的缩减操作,传入数据类型和表达式值
def test_division_int_reduce(self, dtype, ex_val):
# 获取给定数据类型的整数信息对象
fo = np.iinfo(dtype)
# 使用给定表达式值计算得到数组 a
a = eval(ex_val)
# 将数组 a 转换为列表形式
lst = a.tolist()
# 定义一个 lambda 函数 c_div,用于整数除法,处理特殊情况
c_div = lambda n, d: (
0 if d == 0 or (n and n == fo.min and d == -1) else n//d
)
# 忽略除法警告,计算数组 a 的缩减操作结果 div_a
with np.errstate(divide='ignore'):
div_a = np.floor_divide.reduce(a)
# 使用 reduce 函数和自定义的 c_div 函数计算列表 lst 的缩减操作结果 div_lst
div_lst = reduce(c_div, lst)
# 设置断言消息
msg = "Reduce floor integer division check"
# 断言 div_a 和 div_lst 相等,如果不相等则抛出断言错误
assert div_a == div_lst, msg
# 恢复默认的除法错误处理方式,测试浮点错误的情况
with np.errstate(divide='raise', over='raise'):
# 检查在数组范围内的整数除法操作,是否会引发浮点错误
with pytest.raises(FloatingPointError,
match="divide by zero encountered in reduce"):
np.floor_divide.reduce(np.arange(-100, 100).astype(dtype))
# 如果数据类型的最小值不为零,再次测试是否会引发浮点错误
if fo.min:
with pytest.raises(FloatingPointError,
match='overflow encountered in reduce'):
np.floor_divide.reduce(
np.array([fo.min, 1, -1], dtype=dtype)
)
@pytest.mark.parametrize(
"dividend,divisor,quotient",
[(np.timedelta64(2,'Y'), np.timedelta64(2,'M'), 12),
(np.timedelta64(2,'Y'), np.timedelta64(-2,'M'), -12),
(np.timedelta64(-2,'Y'), np.timedelta64(2,'M'), -12),
(np.timedelta64(-2,'Y'), np.timedelta64(-2,'M'), 12),
(np.timedelta64(2,'M'), np.timedelta64(-2,'Y'), -1),
(np.timedelta64(2,'Y'), np.timedelta64(0,'M'), 0),
(np.timedelta64(2,'Y'), 2, np.timedelta64(1,'Y')),
(np.timedelta64(2,'Y'), -2, np.timedelta64(-1,'Y')),
(np.timedelta64(-2,'Y'), 2, np.timedelta64(-1,'Y')),
(np.timedelta64(-2,'Y'), -2, np.timedelta64(1,'Y')),
(np.timedelta64(-2,'Y'), -2, np.timedelta64(1,'Y')),
(np.timedelta64(-2,'Y'), -3, np.timedelta64(0,'Y')),
(np.timedelta64(-2,'Y'), 0, np.timedelta64('Nat','Y')),
])
# 定义一个测试方法,用于测试时间增量的整数除法操作
def test_division_int_timedelta(self, dividend, divisor, quotient):
# 如果除数不为零且商不是 NaN 时间,进行除法测试
if divisor and (isinstance(quotient, int) or not np.isnat(quotient)):
# 设置断言消息
msg = "Timedelta floor division check"
# 断言时间增量 dividend 除以 divisor 的结果等于 quotient
assert dividend // divisor == quotient, msg
# 进一步测试数组形式的时间增量除法
msg = "Timedelta arrays floor division check"
dividend_array = np.array([dividend]*5)
quotient_array = np.array([quotient]*5)
# 断言数组形式的时间增量除法结果与预期相符
assert all(dividend_array // divisor == quotient_array), msg
else:
# 如果是在 WASM 环境下,跳过浮点错误测试
if IS_WASM:
pytest.skip("fp errors don't work in wasm")
# 恢复默认的除法错误处理方式,测试除数为零的情况
with np.errstate(divide='raise', invalid='raise'):
with pytest.raises(FloatingPointError):
dividend // divisor
# 定义一个测试函数,用于测试复数除法
def test_division_complex(self):
# 设置测试的错误消息
msg = "Complex division implementation check"
# 创建一个复数类型的 NumPy 数组,用于测试
x = np.array([1. + 1.*1j, 1. + .5*1j, 1. + 2.*1j], dtype=np.complex128)
# 断言复数的平方除以自身应该等于自身,用于检查复数除法实现的正确性
assert_almost_equal(x**2/x, x, err_msg=msg)
# 检查复数除法可能出现的溢出和下溢情况
msg = "Complex division overflow/underflow check"
# 创建一个复数类型的 NumPy 数组,用于测试溢出和下溢
x = np.array([1.e+110, 1.e-110], dtype=np.complex128)
y = x**2/x
# 断言除以自身应该得到 1,用于检查溢出和下溢情况
assert_almost_equal(y/x, [1, 1], err_msg=msg)
# 定义一个测试函数,用于测试复数除以零的情况
def test_zero_division_complex(self):
# 忽略复数除以零的警告和错误
with np.errstate(invalid="ignore", divide="ignore"):
# 创建一个复数类型的 NumPy 数组,测试除以零的各种情况
x = np.array([0.0], dtype=np.complex128)
y = 1.0/x
# 断言除以零得到无穷大
assert_(np.isinf(y)[0])
y = complex(np.inf, np.nan)/x
assert_(np.isinf(y)[0])
y = complex(np.nan, np.inf)/x
assert_(np.isinf(y)[0])
y = complex(np.inf, np.inf)/x
assert_(np.isinf(y)[0])
y = 0.0/x
# 断言除以零得到 NaN
assert_(np.isnan(y)[0])
# 定义一个测试函数,用于测试复数的地板除法
def test_floor_division_complex(self):
# 检查地板除法、divmod 和取余操作是否会引发类型错误
x = np.array([.9 + 1j, -.1 + 1j, .9 + .5*1j, .9 + 2.*1j], dtype=np.complex128)
with pytest.raises(TypeError):
x // 7
with pytest.raises(TypeError):
np.divmod(x, 7)
with pytest.raises(TypeError):
np.remainder(x, 7)
# 定义一个测试函数,用于测试地板除法中的带符号零
def test_floor_division_signed_zero(self):
# 检查当正零和负零除以一时,符号位是否设置正确
x = np.zeros(10)
assert_equal(np.signbit(x//1), 0)
assert_equal(np.signbit((-x)//1), 1)
# 根据条件标记跳过测试
@pytest.mark.skipif(hasattr(np.__config__, "blas_ssl2_info"),
reason="gh-22982")
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
@pytest.mark.parametrize('dtype', np.typecodes['Float'])
# 定义一个测试函数,用于测试地板除法中的错误情况
def test_floor_division_errors(self, dtype):
# 创建各种类型的 NumPy 数组,用于测试地板除法中的错误情况
fnan = np.array(np.nan, dtype=dtype)
fone = np.array(1.0, dtype=dtype)
fzer = np.array(0.0, dtype=dtype)
finf = np.array(np.inf, dtype=dtype)
# 检查除以零是否会引发浮点数错误
with np.errstate(divide='raise', invalid='ignore'):
assert_raises(FloatingPointError, np.floor_divide, fone, fzer)
with np.errstate(divide='ignore', invalid='raise'):
np.floor_divide(fone, fzer)
# 下面的操作已经包含 NaN,不应该警告
with np.errstate(all='raise'):
np.floor_divide(fnan, fone)
np.floor_divide(fone, fnan)
np.floor_divide(fnan, fzer)
np.floor_divide(fzer, fnan)
# 根据参数化设定,使用不同类型的浮点数进行测试
@pytest.mark.parametrize('dtype', np.typecodes['Float'])
# 定义一个测试函数,用于测试整除操作的边界情况
def test_floor_division_corner_cases(self, dtype):
# 创建一个长度为10的零数组,指定数据类型为参数 dtype
x = np.zeros(10, dtype=dtype)
# 创建一个长度为10的全一数组,指定数据类型为参数 dtype
y = np.ones(10, dtype=dtype)
# 创建一个包含 NaN 的数组,指定数据类型为参数 dtype
fnan = np.array(np.nan, dtype=dtype)
# 创建一个包含浮点数 1.0 的数组,指定数据类型为参数 dtype
fone = np.array(1.0, dtype=dtype)
# 创建一个包含浮点数 0.0 的数组,指定数据类型为参数 dtype
fzer = np.array(0.0, dtype=dtype)
# 创建一个包含正无穷大的数组,指定数据类型为参数 dtype
finf = np.array(np.inf, dtype=dtype)
# 使用 suppress_warnings 上下文管理器,过滤特定的运行时警告信息
with suppress_warnings() as sup:
# 过滤在 floor_divide 操作中遇到的无效值警告
sup.filter(RuntimeWarning, "invalid value encountered in floor_divide")
# 对 NaN 除以 1.0 进行 floor_divide 操作,预期结果应为 NaN
div = np.floor_divide(fnan, fone)
assert(np.isnan(div)), "dt: %s, div: %s" % (dt, div)
# 对 1.0 除以 NaN 进行 floor_divide 操作,预期结果应为 NaN
div = np.floor_divide(fone, fnan)
assert(np.isnan(div)), "dt: %s, div: %s" % (dt, div)
# 对 NaN 除以 0.0 进行 floor_divide 操作,预期结果应为 NaN
div = np.floor_divide(fnan, fzer)
assert(np.isnan(div)), "dt: %s, div: %s" % (dt, div)
# 在忽略除法运算中的除以零警告的情况下,验证 1.0 除以 0.0 的 floor_divide 操作结果是否为正无穷
with np.errstate(divide='ignore'):
z = np.floor_divide(y, x)
assert_(np.isinf(z).all())
# 定义一个函数,用于对两个数执行 floor division 和 remainder 操作,返回结果的元组
def floor_divide_and_remainder(x, y):
return (np.floor_divide(x, y), np.remainder(x, y))
# 根据数据类型判断是否为无符号整数,返回相应的符号元组
def _signs(dt):
if dt in np.typecodes['UnsignedInteger']:
return (+1,)
else:
return (+1, -1)
# 定义一个测试类 TestRemainder
class TestRemainder:
# 测试基本的 remainder 操作
def test_remainder_basic(self):
# 定义需要测试的数据类型组合
dt = np.typecodes['AllInteger'] + np.typecodes['Float']
# 遍历操作函数和数据类型的笛卡尔积
for op in [floor_divide_and_remainder, np.divmod]:
for dt1, dt2 in itertools.product(dt, dt):
# 遍历数据类型的符号组合
for sg1, sg2 in itertools.product(_signs(dt1), _signs(dt2)):
# 格式化测试消息
fmt = 'op: %s, dt1: %s, dt2: %s, sg1: %s, sg2: %s'
msg = fmt % (op.__name__, dt1, dt2, sg1, sg2)
# 创建两个数组 a 和 b,分别代表两个操作数
a = np.array(sg1*71, dtype=dt1)
b = np.array(sg2*19, dtype=dt2)
# 执行操作并获取结果
div, rem = op(a, b)
# 断言操作结果满足等式 div*b + rem == a
assert_equal(div*b + rem, a, err_msg=msg)
# 对于 sg2 为 -1 的情况,断言 rem 在区间 (b, 0] 中
if sg2 == -1:
assert_(b < rem <= 0, msg)
else:
assert_(b > rem >= 0, msg)
# 测试浮点数的 exact remainder 操作
def test_float_remainder_exact(self):
# 定义测试用的小整数列表
nlst = list(range(-127, 0))
plst = list(range(1, 128))
dividend = nlst + [0] + plst
divisor = nlst + plst
arg = list(itertools.product(dividend, divisor))
tgt = list(divmod(*t) for t in arg)
# 将参数转换为数组
a, b = np.array(arg, dtype=int).T
# 将 Python 的 exact integer 结果转换为浮点数以便处理 signed zero
tgtdiv, tgtrem = np.array(tgt, dtype=float).T
tgtdiv = np.where((tgtdiv == 0.0) & ((b < 0) ^ (a < 0)), -0.0, tgtdiv)
tgtrem = np.where((tgtrem == 0.0) & (b < 0), -0.0, tgtrem)
# 遍历操作函数和浮点数数据类型
for op in [floor_divide_and_remainder, np.divmod]:
for dt in np.typecodes['Float']:
msg = 'op: %s, dtype: %s' % (op.__name__, dt)
# 将数组 a 和 b 转换为指定数据类型的浮点数
fa = a.astype(dt)
fb = b.astype(dt)
# 执行操作并获取结果
div, rem = op(fa, fb)
# 断言操作结果与预期结果相等
assert_equal(div, tgtdiv, err_msg=msg)
assert_equal(rem, tgtrem, err_msg=msg)
# 定义测试方法,用于测试浮点数的余数和舍入
def test_float_remainder_roundoff(self):
# 标识问题编号为 gh-6127
dt = np.typecodes['Float']
# 遍历操作函数列表,包括 floor_divide_and_remainder 和 np.divmod
for op in [floor_divide_and_remainder, np.divmod]:
# 遍历浮点数类型组合,使用 itertools.product 进行排列组合
for dt1, dt2 in itertools.product(dt, dt):
# 遍历符号组合,+1 和 -1
for sg1, sg2 in itertools.product((+1, -1), (+1, -1)):
# 格式化消息字符串,显示操作函数名、浮点数类型、符号信息
fmt = 'op: %s, dt1: %s, dt2: %s, sg1: %s, sg2: %s'
msg = fmt % (op.__name__, dt1, dt2, sg1, sg2)
# 创建数组 a 和 b,分别使用指定的浮点数类型和符号
a = np.array(sg1*78*6e-8, dtype=dt1)
b = np.array(sg2*6e-8, dtype=dt2)
# 调用操作函数,获取除法结果和余数
div, rem = op(a, b)
# 使用 assert_equal 断言,验证等式 div*b + rem == a 是否成立,显示错误消息 msg
assert_equal(div*b + rem, a, err_msg=msg)
# 如果 sg2 为 -1,则额外断言 b < rem <= 0,显示消息 msg
if sg2 == -1:
assert_(b < rem <= 0, msg)
else:
assert_(b > rem >= 0, msg)
# 使用 pytest.mark.skipif 标记,条件是 IS_WASM 为真,原因是 wasm 平台上的浮点数错误不起作用
# 同时使用 pytest.mark.xfail 标记,对 macOS 平台做标记,因为它似乎不能正确显示 `fmod` 的 'invalid' 警告
# 参数化测试方法,对于每种浮点数类型进行测试
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
@pytest.mark.xfail(sys.platform.startswith("darwin"),
reason="MacOS seems to not give the correct 'invalid' warning for "
"`fmod`. Hopefully, others always do.")
@pytest.mark.parametrize('dtype', np.typecodes['Float'])
def test_float_divmod_errors(self, dtype):
# 检查 divmod 和 remainder 是否引发有效的错误
fzero = np.array(0.0, dtype=dtype)
fone = np.array(1.0, dtype=dtype)
finf = np.array(np.inf, dtype=dtype)
fnan = np.array(np.nan, dtype=dtype)
# 使用 np.errstate 设置浮点数错误状态
# 第一个断言:使用 np.divmod 计算 fone / fzero 时应引发 FloatingPointError
with np.errstate(divide='raise', invalid='ignore'):
assert_raises(FloatingPointError, np.divmod, fone, fzero)
# 第二个断言:使用 np.divmod 计算 fone / fzero 时应引发 FloatingPointError
with np.errstate(divide='ignore', invalid='raise'):
assert_raises(FloatingPointError, np.divmod, fone, fzero)
# 第三个断言:使用 np.divmod 计算 fzero / fzero 时应引发 FloatingPointError
with np.errstate(invalid='raise'):
assert_raises(FloatingPointError, np.divmod, fzero, fzero)
# 第四个断言:使用 np.divmod 计算 finf / finf 时应引发 FloatingPointError
with np.errstate(invalid='raise'):
assert_raises(FloatingPointError, np.divmod, finf, finf)
# 第五个断言:使用 np.divmod 计算 finf / fzero 时应引发 FloatingPointError
with np.errstate(divide='ignore', invalid='raise'):
assert_raises(FloatingPointError, np.divmod, finf, fzero)
# 第六个断言:np.divmod(finf, fzero) 时,inf / 0 不会设置任何标志,只有模数会创建 NaN
with np.errstate(divide='raise', invalid='ignore'):
np.divmod(finf, fzero)
# 使用 pytest.mark.skipif 标记,条件是 np.__config__ 中存在 "blas_ssl2_info" 属性,原因是问题 gh-22982
# 使用 pytest.mark.skipif 标记,条件是 IS_WASM 为真,原因是 wasm 平台上的浮点数错误不起作用
# 同时使用 pytest.mark.xfail 标记,对 macOS 平台做标记,因为它似乎不能正确显示 `fmod` 的 'invalid' 警告
# 参数化测试方法,对于每种浮点数类型以及 np.fmod 和 np.remainder 两个函数进行测试
@pytest.mark.skipif(hasattr(np.__config__, "blas_ssl2_info"),
reason="gh-22982")
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
@pytest.mark.xfail(sys.platform.startswith("darwin"),
reason="MacOS seems to not give the correct 'invalid' warning for "
"`fmod`. Hopefully, others always do.")
@pytest.mark.parametrize('dtype', np.typecodes['Float'])
@pytest.mark.parametrize('fn', [np.fmod, np.remainder])
def test_float_divmod_corner_cases(self):
# 针对浮点数除法和取模函数的边界情况进行测试
# 遍历所有浮点数类型
for dt in np.typecodes['Float']:
fnan = np.array(np.nan, dtype=dt)
fone = np.array(1.0, dtype=dt)
fzer = np.array(0.0, dtype=dt)
finf = np.array(np.inf, dtype=dt)
# 忽略特定警告信息
with suppress_warnings() as sup:
sup.filter(RuntimeWarning, "invalid value encountered in divmod")
sup.filter(RuntimeWarning, "divide by zero encountered in divmod")
# 测试 np.divmod 函数的行为
div, rem = np.divmod(fone, fzer)
assert(np.isinf(div)), 'dt: %s, div: %s' % (dt, rem)
assert(np.isnan(rem)), 'dt: %s, rem: %s' % (dt, rem)
div, rem = np.divmod(fzer, fzer)
assert(np.isnan(rem)), 'dt: %s, rem: %s' % (dt, rem)
assert_(np.isnan(div)), 'dt: %s, rem: %s' % (dt, rem)
div, rem = np.divmod(finf, finf)
assert(np.isnan(div)), 'dt: %s, rem: %s' % (dt, rem)
assert(np.isnan(rem)), 'dt: %s, rem: %s' % (dt, rem)
div, rem = np.divmod(finf, fzer)
assert(np.isinf(div)), 'dt: %s, rem: %s' % (dt, rem)
assert(np.isnan(rem)), 'dt: %s, rem: %s' % (dt, rem)
div, rem = np.divmod(fnan, fone)
assert(np.isnan(rem)), "dt: %s, rem: %s" % (dt, rem)
assert(np.isnan(div)), "dt: %s, rem: %s" % (dt, rem)
div, rem = np.divmod(fone, fnan)
assert(np.isnan(rem)), "dt: %s, rem: %s" % (dt, rem)
assert(np.isnan(div)), "dt: %s, rem: %s" % (dt, rem)
div, rem = np.divmod(fnan, fzer)
assert(np.isnan(rem)), "dt: %s, rem: %s" % (dt, rem)
assert(np.isnan(div)), "dt: %s, rem: %s" % (dt, rem)
def test_float_remainder_corner_cases(self):
# 检查浮点数取余的边缘情况。
# 对于每种浮点数类型进行迭代检查
for dt in np.typecodes['Float']:
# 创建值为 1.0 的浮点数组,指定数据类型为 dt
fone = np.array(1.0, dtype=dt)
# 创建值为 0.0 的浮点数组,指定数据类型为 dt
fzer = np.array(0.0, dtype=dt)
# 创建 NaN(非数字)的浮点数组,指定数据类型为 dt
fnan = np.array(np.nan, dtype=dt)
# 创建值为 1.0 的浮点数组,指定数据类型为 dt
b = np.array(1.0, dtype=dt)
# 创建尽量接近 0.0 且小于 0.0 的浮点数数组,指定数据类型为 dt
a = np.nextafter(np.array(0.0, dtype=dt), -b)
# 计算 a 对 b 的取余
rem = np.remainder(a, b)
# 断言余数 rem 小于等于 b,如果不成立则抛出异常,附带数据类型信息 dt
assert_(rem <= b, 'dt: %s' % dt)
# 计算 -a 对 -b 的取余
rem = np.remainder(-a, -b)
# 断言余数 rem 大于等于 -b,如果不成立则抛出异常,附带数据类型信息 dt
assert_(rem >= -b, 'dt: %s' % dt)
# 检查 NaN 和无穷大的情况
with suppress_warnings() as sup:
# 忽略特定的运行时警告,如遇到取余操作中的无效值或浮点模操作中的无效值
sup.filter(RuntimeWarning, "invalid value encountered in remainder")
sup.filter(RuntimeWarning, "invalid value encountered in fmod")
# 对于每种浮点数类型进行迭代检查
for dt in np.typecodes['Float']:
# 创建值为 1.0 的浮点数组,指定数据类型为 dt
fone = np.array(1.0, dtype=dt)
# 创建值为 0.0 的浮点数组,指定数据类型为 dt
fzer = np.array(0.0, dtype=dt)
# 创建正无穷大的浮点数组,指定数据类型为 dt
finf = np.array(np.inf, dtype=dt)
# 创建 NaN(非数字)的浮点数组,指定数据类型为 dt
fnan = np.array(np.nan, dtype=dt)
# 计算 fone 对 fzer 的取余
rem = np.remainder(fone, fzer)
# 断言余数 rem 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和余数值 rem
assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem))
# MSVC 2008 在这里返回 NaN,因此禁用此检查。
#rem = np.remainder(fone, finf)
#assert_(rem == fone, 'dt: %s, rem: %s' % (dt, rem))
# 计算 finf 对 fone 的取余
rem = np.remainder(finf, fone)
# 计算 finf 对 fone 的浮点模操作
fmod = np.fmod(finf, fone)
# 断言 fmod 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和模值 fmod
assert_(np.isnan(fmod), 'dt: %s, fmod: %s' % (dt, fmod))
# 断言 rem 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和余数值 rem
assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem))
# 计算 finf 对 finf 的取余
rem = np.remainder(finf, finf)
# 计算 finf 对 fone 的浮点模操作
fmod = np.fmod(finf, fone)
# 断言 rem 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和余数值 rem
assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem))
# 断言 fmod 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和模值 fmod
assert_(np.isnan(fmod), 'dt: %s, fmod: %s' % (dt, fmod))
# 计算 finf 对 fzer 的取余
rem = np.remainder(finf, fzer)
# 计算 finf 对 fzer 的浮点模操作
fmod = np.fmod(finf, fzer)
# 断言 rem 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和余数值 rem
assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem))
# 断言 fmod 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和模值 fmod
assert_(np.isnan(fmod), 'dt: %s, fmod: %s' % (dt, fmod))
# 计算 fone 对 fnan 的取余
rem = np.remainder(fone, fnan)
# 计算 fone 对 fnan 的浮点模操作
fmod = np.fmod(fone, fnan)
# 断言 rem 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和余数值 rem
assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem))
# 断言 fmod 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和模值 fmod
assert_(np.isnan(fmod), 'dt: %s, fmod: %s' % (dt, fmod))
# 计算 fnan 对 fzer 的取余
rem = np.remainder(fnan, fzer)
# 计算 fnan 对 fzer 的浮点模操作
fmod = np.fmod(fnan, fzer)
# 断言 rem 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和余数值 rem
assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem))
# 断言 fmod 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和模值 fmod
assert_(np.isnan(fmod), 'dt: %s, fmod: %s' % (dt, rem))
# 计算 fnan 对 fone 的取余
rem = np.remainder(fnan, fone)
# 计算 fnan 对 fone 的浮点模操作
fmod = np.fmod(fnan, fone)
# 断言 rem 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和余数值 rem
assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem))
# 断言 fmod 是 NaN,如果不成立则抛出异常,附带数据类型信息 dt 和模值 fmod
assert_(np.isnan(fmod), 'dt: %s, fmod: %s' % (dt, rem))
# 定义一个测试类,用于测试整数溢出和除以零的情况
class TestDivisionIntegerOverflowsAndDivideByZero:
# 定义一个命名元组 result_type,包含两个字段:nocast 和 casted
result_type = namedtuple('result_type',
['nocast', 'casted'])
# 定义一个包含不同 lambda 函数的字典,用于生成特定数值
helper_lambdas = {
'zero': lambda dtype: 0,
'min': lambda dtype: np.iinfo(dtype).min,
'neg_min': lambda dtype: -np.iinfo(dtype).min,
'min-zero': lambda dtype: (np.iinfo(dtype).min, 0),
'neg_min-zero': lambda dtype: (-np.iinfo(dtype).min, 0),
}
# 定义一个包含各种算术操作和对应结果的字典
overflow_results = {
np.remainder: result_type(
helper_lambdas['zero'], helper_lambdas['zero']),
np.fmod: result_type(
helper_lambdas['zero'], helper_lambdas['zero']),
operator.mod: result_type(
helper_lambdas['zero'], helper_lambdas['zero']),
operator.floordiv: result_type(
helper_lambdas['min'], helper_lambdas['neg_min']),
np.floor_divide: result_type(
helper_lambdas['min'], helper_lambdas['neg_min']),
np.divmod: result_type(
helper_lambdas['min-zero'], helper_lambdas['neg_min-zero'])
}
# 使用 pytest.mark.skipif 装饰器标记条件跳过的测试用例(当 IS_WASM 为真时跳过)
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
# 使用 pytest.mark.parametrize 标记参数化测试用例,参数为 np.typecodes["Integer"] 中的数据类型
def test_signed_division_overflow(self, dtype):
# 获取一组感兴趣的操作数对,用于测试
to_check = interesting_binop_operands(np.iinfo(dtype).min, -1, dtype)
# 对每一组操作数进行测试
for op1, op2, extractor, operand_identifier in to_check:
# 使用 pytest.warns 检查是否会发出 RuntimeWarning 警告,匹配 "overflow encountered"
with pytest.warns(RuntimeWarning, match="overflow encountered"):
res = op1 // op2
# 断言结果的数据类型与操作数 op1 的数据类型相同
assert res.dtype == op1.dtype
# 断言提取的结果与操作数 op1 的最小值相同
assert extractor(res) == np.iinfo(op1.dtype).min
# 对取余操作进行测试,不应发出警告
res = op1 % op2
assert res.dtype == op1.dtype
assert extractor(res) == 0
# 对 np.fmod 函数进行测试
res = np.fmod(op1, op2)
assert extractor(res) == 0
# 对 divmod 函数进行测试,需要发出警告
with pytest.warns(RuntimeWarning, match="overflow encountered"):
res1, res2 = np.divmod(op1, op2)
# 断言结果的数据类型与操作数 op1 和 op2 的数据类型相同
assert res1.dtype == res2.dtype == op1.dtype
# 断言提取的结果与操作数 op1 的最小值相同
assert extractor(res1) == np.iinfo(op1.dtype).min
assert extractor(res2) == 0
# 使用 pytest.mark.skipif 装饰器标记条件跳过的测试用例(当 IS_WASM 为真时跳过)
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
# 使用 pytest.mark.parametrize 标记参数化测试用例,参数为 np.typecodes["AllInteger"] 中的数据类型
def test_signed_division(self, dtype):
# 定义一个测试方法,用于测试在除以零时的行为
def test_divide_by_zero(self, dtype):
# 注意:这里返回值的定义并不明确,但是 NumPy 目前一致使用 0。这可能会改变。
# 获取一组感兴趣的操作数对,其中包含了除法运算的操作数
to_check = interesting_binop_operands(1, 0, dtype)
# 遍历每对操作数及其相关的信息
for op1, op2, extractor, operand_identifier in to_check:
# 使用 pytest 捕获 RuntimeWarning,匹配 "divide by zero"
with pytest.warns(RuntimeWarning, match="divide by zero"):
res = op1 // op2 # 执行整数除法操作
# 断言结果的数据类型与 op1 的数据类型一致
assert res.dtype == op1.dtype
# 断言提取的结果为 0,即整数除法的结果应为 0
assert extractor(res) == 0
# 再次使用 pytest 捕获 RuntimeWarning,匹配 "divide by zero"
with pytest.warns(RuntimeWarning, match="divide by zero"):
res1, res2 = np.divmod(op1, op2) # 执行 divmod 操作
# 断言结果 res1 和 res2 的数据类型与 op1 的数据类型一致
assert res1.dtype == res2.dtype == op1.dtype
# 断言提取的结果为 0,即 divmod 操作的结果应为 0
assert extractor(res1) == 0
assert extractor(res2) == 0
# 使用 pytest 的标记来跳过在 WASM 环境下的测试,原因是浮点数错误在 WASM 中无法正常工作
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
# 使用 sctypes['int'] 的参数化测试被除数和除数的数据类型
@pytest.mark.parametrize("dividend_dtype", sctypes['int'])
@pytest.mark.parametrize("divisor_dtype", sctypes['int'])
# 使用一组操作函数进行参数化测试,包括 remainder, fmod, divmod, floor_divide,
# operator.mod, operator.floordiv
@pytest.mark.parametrize("operation",
[np.remainder, np.fmod, np.divmod, np.floor_divide,
operator.mod, operator.floordiv])
# 设置 NumPy 的错误状态,当除法操作出现警告时发出警告
@np.errstate(divide='warn', over='warn')
# 定义一个测试函数,用于测试溢出情况
def test_overflows(self, dividend_dtype, divisor_dtype, operation):
# SIMD 尝试在尽可能多的元素上执行操作,
# 这些元素的数量是寄存器大小的倍数。对于剩余的元素,我们使用默认的实现。
# 我们在此尝试覆盖所有可能的路径。
# 创建一系列数组,每个数组包含从最小值开始递增的整数,
# 数据类型为 dividend_dtype,数组长度从 1 到 128
arrays = [np.array([np.iinfo(dividend_dtype).min]*i,
dtype=dividend_dtype) for i in range(1, 129)]
# 创建一个包含单个元素 -1 的数组,数据类型为 divisor_dtype
divisor = np.array([-1], dtype=divisor_dtype)
# 如果 dividend_dtype 的数据类型大小大于等于 divisor_dtype 的数据类型大小,
# 且 operation 是 divmod、floor_divide 或者 operator.floordiv 中的一种,
# 则执行以下代码块
if np.dtype(dividend_dtype).itemsize >= np.dtype(
divisor_dtype).itemsize and operation in (
np.divmod, np.floor_divide, operator.floordiv):
# 使用 pytest.warns 检测 RuntimeWarning,确保输出中包含 "overflow encountered in"
with pytest.warns(
RuntimeWarning,
match="overflow encountered in"):
# 执行操作,传入最小值和 -1 作为参数
result = operation(
dividend_dtype(np.iinfo(dividend_dtype).min),
divisor_dtype(-1)
)
# 断言结果等于预期结果,使用 self.overflow_results 获取预期值
assert result == self.overflow_results[operation].nocast(
dividend_dtype)
# 遍历数组
for a in arrays:
# 对于 divmod 操作,需要先展开结果列,
# 因为结果是商和余数的列向量,而预期结果是展开后的数组
with pytest.warns(
RuntimeWarning,
match="overflow encountered in"):
# 执行操作,传入数组和除数,然后展开结果
result = np.array(operation(a, divisor)).flatten('f')
# 构建预期结果数组
expected_array = np.array(
[self.overflow_results[operation].nocast(
dividend_dtype)]*len(a)).flatten()
# 断言结果数组等于预期数组
assert_array_equal(result, expected_array)
else:
# 如果 dividend_dtype 的数据类型大小小于 divisor_dtype 的数据类型大小,
# 或者 operation 不是 divmod、floor_divide 或 operator.floordiv 中的一种,
# 则执行以下代码块
# 执行操作,传入最小值和 -1 作为参数
result = operation(
dividend_dtype(np.iinfo(dividend_dtype).min),
divisor_dtype(-1)
)
# 断言结果等于预期结果,使用 self.overflow_results 获取预期值
assert result == self.overflow_results[operation].casted(
dividend_dtype)
# 遍历数组
for a in arrays:
# 同上,参考上面的展开结果的注释
result = np.array(operation(a, divisor)).flatten('f')
expected_array = np.array(
[self.overflow_results[operation].casted(
dividend_dtype)]*len(a)).flatten()
assert_array_equal(result, expected_array)
class TestCbrt:
# 测试立方根函数对标量值的计算
def test_cbrt_scalar(self):
# 断言近似相等:计算 -2.5 的立方根是否近似等于 -2.5
assert_almost_equal((np.cbrt(np.float32(-2.5)**3)), -2.5)
# 测试立方根函数对数组的计算
def test_cbrt(self):
# 创建包含不同类型数值的数组
x = np.array([1., 2., -3., np.inf, -np.inf])
# 断言近似相等:计算数组每个元素的立方根后应该近似等于原始数组
assert_almost_equal(np.cbrt(x**3), x)
# 断言 NaN 的立方根应该是 NaN
assert_(np.isnan(np.cbrt(np.nan)))
# 断言正无穷的立方根应该是正无穷
assert_equal(np.cbrt(np.inf), np.inf)
# 断言负无穷的立方根应该是负无穷
assert_equal(np.cbrt(-np.inf), -np.inf)
class TestPower:
# 测试幂函数对浮点数的计算
def test_power_float(self):
# 创建浮点数数组
x = np.array([1., 2., 3.])
# 断言:任何数的零次幂应该是 1
assert_equal(x**0, [1., 1., 1.])
# 断言:任何数的一次幂应该是它自己
assert_equal(x**1, x)
# 断言:计算平方
assert_equal(x**2, [1., 4., 9.])
# 创建副本并计算平方
y = x.copy()
y **= 2
assert_equal(y, [1., 4., 9.])
# 断言:计算倒数
assert_almost_equal(x**(-1), [1., 0.5, 1./3])
# 断言:计算平方根,使用 numpy 的数值计算库
assert_almost_equal(x**(0.5), [1., ncu.sqrt(2), ncu.sqrt(3)])
# 对于每组数据,生成对齐数据并进行测试
for out, inp, msg in _gen_alignment_data(dtype=np.float32,
type='unary',
max_size=11):
# 期望的结果是输入的每个元素的平方根
exp = [ncu.sqrt(i) for i in inp]
assert_almost_equal(inp**(0.5), exp, err_msg=msg)
# 使用 numpy 计算平方根,将结果与期望进行比较
np.sqrt(inp, out=out)
assert_equal(out, exp, err_msg=msg)
# 对于每组数据,生成对齐数据并进行测试
for out, inp, msg in _gen_alignment_data(dtype=np.float64,
type='unary',
max_size=7):
# 期望的结果是输入的每个元素的平方根
exp = [ncu.sqrt(i) for i in inp]
assert_almost_equal(inp**(0.5), exp, err_msg=msg)
# 使用 numpy 计算平方根,将结果与期望进行比较
np.sqrt(inp, out=out)
assert_equal(out, exp, err_msg=msg)
# 测试幂函数对复数的计算
def test_power_complex(self):
# 创建复数数组
x = np.array([1+2j, 2+3j, 3+4j])
# 断言:任何数的零次幂应该是 1
assert_equal(x**0, [1., 1., 1.])
# 断言:任何数的一次幂应该是它自己
assert_equal(x**1, x)
# 断言:计算平方
assert_almost_equal(x**2, [-3+4j, -5+12j, -7+24j])
# 断言:计算立方
assert_almost_equal(x**3, [(1+2j)**3, (2+3j)**3, (3+4j)**3])
# 断言:计算四次方
assert_almost_equal(x**4, [(1+2j)**4, (2+3j)**4, (3+4j)**4])
# 断言:计算倒数
assert_almost_equal(x**(-1), [1/(1+2j), 1/(2+3j), 1/(3+4j)])
# 断言:计算平方的倒数
assert_almost_equal(x**(-2), [1/(1+2j)**2, 1/(2+3j)**2, 1/(3+4j)**2])
# 断言:计算立方的倒数
assert_almost_equal(x**(-3), [(-11+2j)/125, (-46-9j)/2197,
(-117-44j)/15625])
# 断言:计算平方根,使用 numpy 的数值计算库
assert_almost_equal(x**(0.5), [ncu.sqrt(1+2j), ncu.sqrt(2+3j),
ncu.sqrt(3+4j)])
# 计算标准化因子,使 x 的 14 次幂第一个元素等于标准化因子
norm = 1./((x**14)[0])
assert_almost_equal(x**14 * norm,
[i * norm for i in [-76443+16124j, 23161315+58317492j,
5583548873 + 2465133864j]])
# Ticket #836
# 定义一个函数来断言复数相等
def assert_complex_equal(x, y):
assert_array_equal(x.real, y.real)
assert_array_equal(x.imag, y.imag)
# 对于每个复数 z 进行测试
for z in [complex(0, np.inf), complex(1, np.inf)]:
# 将 z 转换为复数数组,并忽略无效值错误
z = np.array([z], dtype=np.complex128)
with np.errstate(invalid="ignore"):
# 断言 z 的一次幂应该等于 z
assert_complex_equal(z**1, z)
# 断言 z 的平方等于 z*z
assert_complex_equal(z**2, z*z)
# 断言 z 的立方等于 z*z*z
assert_complex_equal(z**3, z*z*z)
# 定义一个测试用例,验证零次幂的计算
def test_power_zero(self):
# ticket #1271
# 创建一个复数数组,包含一个复数0j
zero = np.array([0j])
# 创建一个复数数组,包含一个实数1+0j
one = np.array([1+0j])
# 创建一个复数数组,包含一个复数NaN + NaNj
cnan = np.array([complex(np.nan, np.nan)])
# FIXME cinf not tested.
# cinf = np.array([complex(np.inf, 0)])
# 定义一个函数,用来断言两个复数数组相等
def assert_complex_equal(x, y):
x, y = np.asarray(x), np.asarray(y)
# 断言两个复数数组的实部和虚部相等
assert_array_equal(x.real, y.real)
assert_array_equal(x.imag, y.imag)
# 正幂的测试
# 对于指数p列表中的每个值,验证零的p次幂等于零
for p in [0.33, 0.5, 1, 1.5, 2, 3, 4, 5, 6.6]:
assert_complex_equal(np.power(zero, p), zero)
# 零次幂
# 验证零的零次幂等于一
assert_complex_equal(np.power(zero, 0), one)
with np.errstate(invalid="ignore"):
# 验证零的复数次幂生成NaN
assert_complex_equal(np.power(zero, 0+1j), cnan)
# 负幂的测试
# 对于指数p列表中的每个值,验证零的-p次幂生成NaN
for p in [0.33, 0.5, 1, 1.5, 2, 3, 4, 5, 6.6]:
assert_complex_equal(np.power(zero, -p), cnan)
# 验证零的负1+0.2j次幂生成NaN
assert_complex_equal(np.power(zero, -1+0.2j), cnan)
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
# 测试用例,验证零的非零次幂
def test_zero_power_nonzero(self):
# Testing 0^{Non-zero} issue 18378
# 创建一个复数数组,包含一个实数0.0+0.0j
zero = np.array([0.0+0.0j])
# 创建一个复数数组,包含一个复数NaN + NaNj
cnan = np.array([complex(np.nan, np.nan)])
# 定义一个函数,用来断言两个复数数组相等
def assert_complex_equal(x, y):
# 断言两个复数数组的实部和虚部相等
assert_array_equal(x.real, y.real)
assert_array_equal(x.imag, y.imag)
# 复数次幂,当实部为正时不会生成警告
assert_complex_equal(np.power(zero, 1+4j), zero)
assert_complex_equal(np.power(zero, 2-3j), zero)
# 测试零值当实部大于零时
assert_complex_equal(np.power(zero, 1+1j), zero)
assert_complex_equal(np.power(zero, 1+0j), zero)
assert_complex_equal(np.power(zero, 1-1j), zero)
# 复数次幂,当实部为负或零(且虚部非零)时生成NaN,会触发RUNTIME警告
with pytest.warns(expected_warning=RuntimeWarning) as r:
assert_complex_equal(np.power(zero, -1+1j), cnan)
assert_complex_equal(np.power(zero, -2-3j), cnan)
assert_complex_equal(np.power(zero, -7+0j), cnan)
assert_complex_equal(np.power(zero, 0+1j), cnan)
assert_complex_equal(np.power(zero, 0-1j), cnan)
# 断言收到5个警告
assert len(r) == 5
# 测试用例,验证快速幂运算
def test_fast_power(self):
# 创建一个整数数组
x = np.array([1, 2, 3], np.int16)
# 计算x的2.0次幂
res = x**2.0
# 断言结果的数据类型与res的数据类型相同
assert_((x**2.00001).dtype is res.dtype)
# 断言结果与期望结果相等
assert_array_equal(res, [1, 4, 9])
# 检查在类型转换后的复制上进行原位操作不会影响x
assert_(not np.may_share_memory(res, x))
assert_array_equal(x, [1, 2, 3])
# 检查快速路径是否忽略1元素非0维数组
res = x ** np.array([[[2]]])
assert_equal(res.shape, (1, 1, 3))
# 测试用例,验证整数幂运算
def test_integer_power(self):
# 创建一个长整型数组
a = np.array([15, 15], 'i8')
# 计算a的a次幂
b = np.power(a, a)
# 断言结果与期望结果相等
assert_equal(b, [437893890380859375, 437893890380859375])
# 定义测试函数,验证整数以0为指数的幂运算
def test_integer_power_with_integer_zero_exponent(self):
# 获取所有整数类型的数据类型码
dtypes = np.typecodes['Integer']
# 遍历每种数据类型
for dt in dtypes:
# 创建一个从-10到9的数组,使用当前数据类型
arr = np.arange(-10, 10, dtype=dt)
# 断言数组元素求0次幂后结果为全1数组
assert_equal(np.power(arr, 0), np.ones_like(arr))
# 获取所有无符号整数类型的数据类型码
dtypes = np.typecodes['UnsignedInteger']
# 遍历每种数据类型
for dt in dtypes:
# 创建一个从0到9的数组,使用当前数据类型
arr = np.arange(10, dtype=dt)
# 断言数组元素求0次幂后结果为全1数组
assert_equal(np.power(arr, 0), np.ones_like(arr))
# 定义测试函数,验证整数以1为底数的幂运算
def test_integer_power_of_1(self):
# 获取所有整数类型的数据类型码
dtypes = np.typecodes['AllInteger']
# 遍历每种数据类型
for dt in dtypes:
# 创建一个从0到9的数组,使用当前数据类型
arr = np.arange(10, dtype=dt)
# 断言1的任意整数次幂结果为全1数组
assert_equal(np.power(1, arr), np.ones_like(arr))
# 定义测试函数,验证整数以0为底数的幂运算
def test_integer_power_of_zero(self):
# 获取所有整数类型的数据类型码
dtypes = np.typecodes['AllInteger']
# 遍历每种数据类型
for dt in dtypes:
# 创建一个从1到9的数组,使用当前数据类型
arr = np.arange(1, 10, dtype=dt)
# 断言0的任意正整数次幂结果为全0数组
assert_equal(np.power(0, arr), np.zeros_like(arr))
# 定义测试函数,验证整数以负整数为指数的幂运算
def test_integer_to_negative_power(self):
# 获取所有整数类型的数据类型码
dtypes = np.typecodes['Integer']
# 遍历每种数据类型
for dt in dtypes:
# 创建一个包含0到3的整数数组,使用当前数据类型
a = np.array([0, 1, 2, 3], dtype=dt)
# 创建一个包含0、1、2和-3的整数数组,使用当前数据类型
b = np.array([0, 1, 2, -3], dtype=dt)
# 创建一个整数1,使用当前数据类型
one = np.array(1, dtype=dt)
# 创建一个整数-1,使用当前数据类型
minusone = np.array(-1, dtype=dt)
# 断言对于负指数或1为底数,幂运算会引发值错误
assert_raises(ValueError, np.power, a, b)
assert_raises(ValueError, np.power, a, minusone)
assert_raises(ValueError, np.power, one, b)
assert_raises(ValueError, np.power, one, minusone)
# 定义测试函数,验证浮点数到无穷大为指数的幂运算
def test_float_to_inf_power(self):
# 遍历每种浮点数数据类型
for dt in [np.float32, np.float64]:
# 创建一个包含1、1、2、2、-2、-2、正无穷和负无穷的浮点数组,使用当前数据类型
a = np.array([1, 1, 2, 2, -2, -2, np.inf, -np.inf], dt)
# 创建一个包含正无穷、负无穷、正无穷、负无穷、正无穷、负无穷、正无穷、负无穷的浮点数组,使用当前数据类型
b = np.array([np.inf, -np.inf, np.inf, -np.inf,
np.inf, -np.inf, np.inf, -np.inf], dt)
# 创建一个包含1、1、正无穷、0、正无穷、0、正无穷、0的浮点数组,使用当前数据类型
r = np.array([1, 1, np.inf, 0, np.inf, 0, np.inf, 0], dt)
# 断言浮点数到无穷大为指数的幂运算结果为预期的结果数组
assert_equal(np.power(a, b), r)
# 定义测试函数,验证快速路径的幂运算
def test_power_fast_paths(self):
# gh-26055
# 遍历每种浮点数数据类型
for dt in [np.float32, np.float64]:
# 创建一个包含0、1.1、2、12e12、-10.、正无穷、负无穷的浮点数组,使用当前数据类型
a = np.array([0, 1.1, 2, 12e12, -10., np.inf, -np.inf], dt)
# 创建一个包含0.0、1.21、4.、1.44e+26、100、正无穷、正无穷的浮点数组,使用当前数据类型
expected = np.array([0.0, 1.21, 4., 1.44e+26, 100, np.inf, np.inf])
# 对数组进行平方运算,使用当前数据类型
result = np.power(a, 2.)
# 断言数组按最大ULP的数组与预期类型的最大ULP为1
assert_array_max_ulp(result, expected.astype(dt), maxulp=1)
# 创建一个包含0、1.1、2、12e12的浮点数组,使用当前数据类型
a = np.array([0, 1.1, 2, 12e12], dt)
# 创建一个对a数组进行平方根运算并转换成当前数据类型的数组
expected = np.sqrt(a).astype(dt)
# 对数组进行0.5次方运算,使用当前数据类型
result = np.power(a, 0.5)
# 断言数组按最大ULP的数组与预期数组进行最大ULP为1的检验
assert_array_max_ulp(result, expected, maxulp=1)
class TestFloat_power:
# 测试类,用于测试 np.float_power 函数的类型转换功能
def test_type_conversion(self):
# 输入参数类型和输出结果类型的字符串表示
arg_type = '?bhilBHILefdgFDG'
res_type = 'ddddddddddddgDDG'
# 遍历输入参数类型和输出结果类型,逐一测试
for dtin, dtout in zip(arg_type, res_type):
# 构建测试消息
msg = "dtin: %s, dtout: %s" % (dtin, dtout)
# 创建具有给定数据类型的包含一个元素的数组
arg = np.ones(1, dtype=dtin)
# 计算 np.float_power 的结果
res = np.float_power(arg, arg)
# 断言结果的数据类型与期望的输出数据类型相符
assert_(res.dtype.name == np.dtype(dtout).name, msg)
class TestLog2:
# 测试类,用于测试 np.log2 函数的各种情况
@pytest.mark.parametrize('dt', ['f', 'd', 'g'])
def test_log2_values(self, dt):
# 输入数组和预期输出结果
x = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 将输入数组转换为指定数据类型的 numpy 数组
xf = np.array(x, dtype=dt)
yf = np.array(y, dtype=dt)
# 断言 np.log2 的计算结果与预期输出结果相近
assert_almost_equal(np.log2(xf), yf)
@pytest.mark.parametrize("i", range(1, 65))
def test_log2_ints(self, i):
# 测试对整数进行 np.log2 计算的正确性
v = np.log2(2.**i)
assert_equal(v, float(i), err_msg='at exponent %d' % i)
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
def test_log2_special(self):
# 测试 np.log2 函数在特殊情况下的行为
assert_equal(np.log2(1.), 0.)
assert_equal(np.log2(np.inf), np.inf)
assert_(np.isnan(np.log2(np.nan)))
# 测试在警告情况下的 np.log2 的行为
with warnings.catch_warnings(record=True) as w:
warnings.filterwarnings('always', '', RuntimeWarning)
assert_(np.isnan(np.log2(-1.)))
assert_(np.isnan(np.log2(-np.inf)))
assert_equal(np.log2(0.), -np.inf)
# 检查警告类型
assert_(w[0].category is RuntimeWarning)
assert_(w[1].category is RuntimeWarning)
assert_(w[2].category is RuntimeWarning)
class TestExp2:
# 测试类,用于测试 np.exp2 函数的功能
def test_exp2_values(self):
# 输入数组和预期输出结果
x = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 遍历数据类型,逐一测试
for dt in ['f', 'd', 'g']:
# 将输入数组转换为指定数据类型的 numpy 数组
xf = np.array(x, dtype=dt)
yf = np.array(y, dtype=dt)
# 断言 np.exp2 的计算结果与预期输出结果相近
assert_almost_equal(np.exp2(yf), xf)
class TestLogAddExp2(_FilterInvalids):
# 测试类,继承自 _FilterInvalids,用于测试 np.logaddexp2 函数的功能
# 需要测试中间精度的情况
def test_logaddexp2_values(self):
# 输入数组和预期输出结果
x = [1, 2, 3, 4, 5]
y = [5, 4, 3, 2, 1]
z = [6, 6, 6, 6, 6]
# 遍历数据类型和精度值,逐一测试
for dt, dec_ in zip(['f', 'd', 'g'], [6, 15, 15]):
# 计算 np.log2 的结果
xf = np.log2(np.array(x, dtype=dt))
yf = np.log2(np.array(y, dtype=dt))
zf = np.log2(np.array(z, dtype=dt))
# 断言 np.logaddexp2 的计算结果与预期输出结果相近
assert_almost_equal(np.logaddexp2(xf, yf), zf, decimal=dec_)
def test_logaddexp2_range(self):
# 输入数组和预期输出结果
x = [1000000, -1000000, 1000200, -1000200]
y = [1000200, -1000200, 1000000, -1000000]
z = [1000200, -1000000, 1000200, -1000000]
# 遍历数据类型,逐一测试
for dt in ['f', 'd', 'g']:
# 计算 np.log2 的结果
logxf = np.array(x, dtype=dt)
logyf = np.array(y, dtype=dt)
logzf = np.array(z, dtype=dt)
# 断言 np.logaddexp2 的计算结果与预期输出结果相近
assert_almost_equal(np.logaddexp2(logxf, logyf), logzf)
# 定义一个测试函数,用于测试 np.logaddexp2 函数在不同情况下的行为
def test_inf(self):
# 定义无穷大常量
inf = np.inf
# 创建包含各种无穷大、有限值的列表 x
x = [inf, -inf, inf, -inf, inf, 1, -inf, 1]
# 创建包含各种无穷大、有限值的列表 y
y = [inf, inf, -inf, -inf, 1, inf, 1, -inf]
# 创建包含各种无穷大、有限值的列表 z
z = [inf, inf, inf, -inf, inf, inf, 1, 1]
# 在处理中遇到无效操作时抛出异常
with np.errstate(invalid='raise'):
# 遍历数据类型列表 ['f', 'd', 'g']
for dt in ['f', 'd', 'g']:
# 使用指定数据类型创建数组 logxf,并赋予 x 的值
logxf = np.array(x, dtype=dt)
# 使用指定数据类型创建数组 logyf,并赋予 y 的值
logyf = np.array(y, dtype=dt)
# 使用指定数据类型创建数组 logzf,并赋予 z 的值
logzf = np.array(z, dtype=dt)
# 断言 np.logaddexp2(logxf, logyf) 等于 logzf
assert_equal(np.logaddexp2(logxf, logyf), logzf)
# 定义测试函数,用于测试 np.logaddexp2 函数处理 NaN 值的行为
def test_nan(self):
# 断言 np.logaddexp2(np.nan, np.inf) 的结果是 NaN
assert_(np.isnan(np.logaddexp2(np.nan, np.inf)))
# 断言 np.logaddexp2(np.inf, np.nan) 的结果是 NaN
assert_(np.isnan(np.logaddexp2(np.inf, np.nan)))
# 断言 np.logaddexp2(np.nan, 0) 的结果是 NaN
assert_(np.isnan(np.logaddexp2(np.nan, 0)))
# 断言 np.logaddexp2(0, np.nan) 的结果是 NaN
assert_(np.isnan(np.logaddexp2(0, np.nan)))
# 断言 np.logaddexp2(np.nan, np.nan) 的结果是 NaN
assert_(np.isnan(np.logaddexp2(np.nan, np.nan)))
# 定义测试函数,用于测试 np.logaddexp2 函数的 reduce 方法
def test_reduce(self):
# 断言 np.logaddexp2.identity 等于负无穷大
assert_equal(np.logaddexp2.identity, -np.inf)
# 断言对空列表使用 np.logaddexp2.reduce 的结果是负无穷大
assert_equal(np.logaddexp2.reduce([]), -np.inf)
# 断言对包含单个 -np.inf 的列表使用 np.logaddexp2.reduce 的结果是负无穷大
assert_equal(np.logaddexp2.reduce([-np.inf]), -np.inf)
# 断言对包含 -np.inf 和 0 的列表使用 np.logaddexp2.reduce 的结果是 0
assert_equal(np.logaddexp2.reduce([-np.inf, 0]), 0)
# 定义一个测试日志类 TestLog
class TestLog:
# 测试对数函数的值
def test_log_values(self):
# 定义 x 和 y 数组
x = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 遍历不同的数据类型
for dt in ['f', 'd', 'g']:
# 定义常数 log2_
log2_ = 0.69314718055994530943
# 将 x 转换为指定数据类型的数组 xf
xf = np.array(x, dtype=dt)
# 计算 y 数组,每个元素乘以 log2_
yf = np.array(y, dtype=dt) * log2_
# 断言计算的自然对数与预期的 yf 数组接近
assert_almost_equal(np.log(xf), yf)
# 测试别名问题(问题编号 #17761)
x = np.array([2, 0.937500, 3, 0.947500, 1.054697])
# 计算 xf 数组的自然对数
xf = np.log(x)
# 将计算的结果放入现有数组 x,然后断言结果与 xf 数组接近
assert_almost_equal(np.log(x, out=x), xf)
# 测试对数函数在各数据类型的最大值上的行为
def test_log_values_maxofdtype(self):
# 定义数据类型列表
dtypes = [np.float32, np.float64]
# 在非 x86-64 平台上,对 longdouble 的检查并不太有用
if platform.machine() == 'x86_64':
dtypes += [np.longdouble]
# 遍历各种数据类型
for dt in dtypes:
# 恢复所有错误状态为默认行为,并在运算过程中引发错误
with np.errstate(all='raise'):
# 获取当前数据类型的最大值 x
x = np.finfo(dt).max
# 计算 x 的自然对数
np.log(x)
# 测试对数函数在不同步长和数据大小下的行为
def test_log_strides(self):
np.random.seed(42)
# 定义步长数组和尺寸数组
strides = np.array([-4, -3, -2, -1, 1, 2, 3, 4])
sizes = np.arange(2, 100)
# 遍历尺寸数组
for ii in sizes:
# 生成指定范围内均匀分布的随机数,以 float64 类型存储在 x_f64
x_f64 = np.float64(np.random.uniform(low=0.01, high=100.0, size=ii))
# 创建 x_special,复制自 x_f64
x_special = x_f64.copy()
# 将 x_special 中指定范围的元素设置为 1.0
x_special[3:-1:4] = 1.0
# 计算 x_f64 和 x_special 的自然对数
y_true = np.log(x_f64)
y_special = np.log(x_special)
# 遍历步长数组
for jj in strides:
# 断言对 np.log(x_f64[::jj]) 和 y_true[::jj] 进行近似相等性检查
assert_array_almost_equal_nulp(np.log(x_f64[::jj]), y_true[::jj], nulp=2)
# 断言对 np.log(x_special[::jj]) 和 y_special[::jj] 进行近似相等性检查
assert_array_almost_equal_nulp(np.log(x_special[::jj]), y_special[::jj], nulp=2)
# 使用 mpmath 计算的参考值进行测试精度(数据类型为 float64)
@pytest.mark.parametrize(
'z, wref',
[(1 + 1e-12j, 5e-25 + 1e-12j),
(1.000000000000001 + 3e-08j,
1.5602230246251546e-15 + 2.999999999999996e-08j),
(0.9999995000000417 + 0.0009999998333333417j,
7.831475869017683e-18 + 0.001j),
(0.9999999999999996 + 2.999999999999999e-08j,
5.9107901499372034e-18 + 3e-08j),
(0.99995000042 - 0.009999833j,
-7.015159763822903e-15 - 0.009999999665816696j)],
)
def test_log_precision_float64(self, z, wref):
# 计算 z 的自然对数 w
w = np.log(z)
# 断言计算的结果 w 与参考值 wref 的接近程度
assert_allclose(w, wref, rtol=1e-15)
# 使用 mpmath 计算的参考值进行测试精度(数据类型为 float32)
@pytest.mark.parametrize(
'z, wref',
[(np.complex64(1.0 + 3e-6j), np.complex64(4.5e-12+3e-06j)),
(np.complex64(1.0 - 2e-5j), np.complex64(1.9999999e-10 - 2e-5j)),
(np.complex64(0.9999999 + 1e-06j),
np.complex64(-1.192088e-07+1.0000001e-06j))],
)
def test_log_precision_float32(self, z, wref):
# 计算 z 的自然对数 w
w = np.log(z)
# 断言计算的结果 w 与参考值 wref 的接近程度
assert_allclose(w, wref, rtol=1e-6)
# 定义一个测试函数,用于验证对指定数据类型的指数函数计算是否正确
def test_exp_values(self):
# 定义输入的 x 和对应的指数值 y
x = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 对于每种数据类型 ['f', 'd', 'g'] 进行测试
for dt in ['f', 'd', 'g']:
# 定义常数 log2_
log2_ = 0.69314718055994530943
# 使用 numpy 创建包含 x 数组的指定数据类型的数组 xf
xf = np.array(x, dtype=dt)
# 根据对应的数据类型和 log2_ 常数创建 yf 数组
yf = np.array(y, dtype=dt) * log2_
# 断言 numpy 的 exp 函数应用于 yf 后结果与 xf 相等
assert_almost_equal(np.exp(yf), xf)
# 定义一个测试函数,用于验证对指定步长下的指数函数计算是否正确
def test_exp_strides(self):
# 设定随机数种子,确保每次运行结果一致
np.random.seed(42)
# 定义步长数组 strides
strides = np.array([-4, -3, -2, -1, 1, 2, 3, 4])
# 创建从 2 到 99 的数组 sizes
sizes = np.arange(2, 100)
# 对于 sizes 中的每个元素 ii 进行测试
for ii in sizes:
# 生成指定范围内的随机浮点数数组 x_f64
x_f64 = np.float64(np.random.uniform(low=0.01, high=709.1, size=ii))
# 计算 x_f64 的指数函数结果作为参考结果 y_true
y_true = np.exp(x_f64)
# 对于 strides 中的每个元素 jj 进行测试
for jj in strides:
# 断言使用指定步长 jj 下,对 x_f64 的指数函数结果应与 y_true[::jj] 相近
assert_array_almost_equal_nulp(np.exp(x_f64[::jj]), y_true[::jj], nulp=2)
# 定义一个测试类 TestSpecialFloats,用于测试处理特殊浮点数的函数
class TestSpecialFloats:
# 定义测试函数 test_exp_values,测试指数函数 np.exp 对特殊浮点数的处理
def test_exp_values(self):
# 设置 numpy 错误状态,当发生特定错误时抛出异常
with np.errstate(under='raise', over='raise'):
# 定义输入数组 x 和 y,包含 NaN、正负无穷和零
x = [np.nan, np.nan, np.inf, 0.]
y = [np.nan, -np.nan, np.inf, -np.inf]
# 遍历数据类型 ['e', 'f', 'd', 'g']
for dt in ['e', 'f', 'd', 'g']:
# 将 x 和 y 转换为指定数据类型的 numpy 数组
xf = np.array(x, dtype=dt)
yf = np.array(y, dtype=dt)
# 断言 np.exp(yf) 的结果与 xf 相等
assert_equal(np.exp(yf), xf)
# 标记为预期失败的测试函数,参考 https://github.com/numpy/numpy/issues/19192
@pytest.mark.xfail(
_glibc_older_than("2.17"),
reason="Older glibc versions may not raise appropriate FP exceptions"
)
# 定义测试函数 test_exp_exceptions,测试 np.exp 在特定情况下是否会引发浮点数异常
def test_exp_exceptions(self):
# 设置 numpy 错误状态,当发生上溢出错误时抛出异常
with np.errstate(over='raise'):
# 断言 np.exp 对各种浮点数类型的输入会引发 FloatingPointError 异常
assert_raises(FloatingPointError, np.exp, np.float16(11.0899))
assert_raises(FloatingPointError, np.exp, np.float32(100.))
assert_raises(FloatingPointError, np.exp, np.float32(1E19))
assert_raises(FloatingPointError, np.exp, np.float64(800.))
assert_raises(FloatingPointError, np.exp, np.float64(1E19))
# 设置 numpy 错误状态,当发生下溢出错误时抛出异常
with np.errstate(under='raise'):
# 断言 np.exp 对各种浮点数类型的输入会引发 FloatingPointError 异常
assert_raises(FloatingPointError, np.exp, np.float16(-17.5))
assert_raises(FloatingPointError, np.exp, np.float32(-1000.))
assert_raises(FloatingPointError, np.exp, np.float32(-1E19))
assert_raises(FloatingPointError, np.exp, np.float64(-1000.))
assert_raises(FloatingPointError, np.exp, np.float64(-1E19))
# 标记测试函数为在 WASM 环境下跳过执行的条件
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
# 定义一个测试方法,用于测试对数函数在不同数据类型和异常条件下的行为
def test_log_values(self):
# 忽略所有的数值错误
with np.errstate(all='ignore'):
# 定义不同的测试数据集合
x = [np.nan, np.nan, np.inf, np.nan, -np.inf, np.nan]
y = [np.nan, -np.nan, np.inf, -np.inf, 0.0, -1.0]
y1p = [np.nan, -np.nan, np.inf, -np.inf, -1.0, -2.0]
# 遍历不同的数据类型
for dt in ['e', 'f', 'd', 'g']:
# 将数据转换为指定数据类型的数组
xf = np.array(x, dtype=dt)
yf = np.array(y, dtype=dt)
yf1p = np.array(y1p, dtype=dt)
# 断言对数函数的计算结果与预期相等
assert_equal(np.log(yf), xf)
assert_equal(np.log2(yf), xf)
assert_equal(np.log10(yf), xf)
assert_equal(np.log1p(yf1p), xf)
# 抛出除法相关的浮点数错误
with np.errstate(divide='raise'):
# 再次遍历不同的数据类型
for dt in ['e', 'f', 'd']:
# 断言对数函数对零值进行计算时会抛出浮点数错误
assert_raises(FloatingPointError, np.log,
np.array(0.0, dtype=dt))
assert_raises(FloatingPointError, np.log2,
np.array(0.0, dtype=dt))
assert_raises(FloatingPointError, np.log10,
np.array(0.0, dtype=dt))
assert_raises(FloatingPointError, np.log1p,
np.array(-1.0, dtype=dt))
# 抛出无效操作相关的浮点数错误
with np.errstate(invalid='raise'):
# 再次遍历不同的数据类型
for dt in ['e', 'f', 'd']:
# 断言对数函数对负无穷大和负数进行计算时会抛出浮点数错误
assert_raises(FloatingPointError, np.log,
np.array(-np.inf, dtype=dt))
assert_raises(FloatingPointError, np.log,
np.array(-1.0, dtype=dt))
assert_raises(FloatingPointError, np.log2,
np.array(-np.inf, dtype=dt))
assert_raises(FloatingPointError, np.log2,
np.array(-1.0, dtype=dt))
assert_raises(FloatingPointError, np.log10,
np.array(-np.inf, dtype=dt))
assert_raises(FloatingPointError, np.log10,
np.array(-1.0, dtype=dt))
assert_raises(FloatingPointError, np.log1p,
np.array(-np.inf, dtype=dt))
assert_raises(FloatingPointError, np.log1p,
np.array(-2.0, dtype=dt))
# 参考:https://github.com/numpy/numpy/issues/18005
# 确保在没有警告的情况下运行以下代码块
with assert_no_warnings():
# 创建一个 float32 类型的数组 a,其值为 1e9
a = np.array(1e9, dtype='float32')
# 对数组 a 进行对数运算
np.log(a)
# 使用 pytest 的标记功能,条件为 IS_WASM 为真时跳过此测试方法
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
# 参数化测试方法,参数为不同的数据类型 ['e', 'f', 'd', 'g']
def test_sincos_values(self, dtype):
# 忽略所有的数值错误
with np.errstate(all='ignore'):
# 定义测试数据集合
x = [np.nan, np.nan, np.nan, np.nan]
y = [np.nan, -np.nan, np.inf, -np.inf]
# 将测试数据转换为指定数据类型的数组
xf = np.array(x, dtype=dtype)
yf = np.array(y, dtype=dtype)
# 断言正弦和余弦函数的计算结果与预期相等
assert_equal(np.sin(yf), xf)
assert_equal(np.cos(yf), xf)
# 使用 pytest 的标记功能,条件为 IS_WASM 为真时跳过此测试方法
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
# 使用 pytest 的 xfail 标记,当运行在 macOS 上时标记为预期失败,原因是触发了标量 'sin' 的下溢
@pytest.mark.xfail(
sys.platform.startswith("darwin"),
reason="underflow is triggered for scalar 'sin'"
)
# 测试处理 'sin' 和 'cos' 函数下溢的情况
def test_sincos_underflow(self):
# 设置浮点错误状态为下溢时抛出异常
with np.errstate(under='raise'):
# 创建一个浮点数数组,使用十六进制表示的小数值,作为下溢触发器
underflow_trigger = np.array(
float.fromhex("0x1.f37f47a03f82ap-511"),
dtype=np.float64
)
# 调用 sin 函数
np.sin(underflow_trigger)
# 调用 cos 函数
np.cos(underflow_trigger)
# 使用 pytest 的 skipif 标记,在运行在 wasm 环境下跳过测试,原因是浮点错误在 wasm 中无效
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
# 测试处理 'sin' 和 'cos' 函数的浮点错误情况
@pytest.mark.parametrize('callable', [np.sin, np.cos])
@pytest.mark.parametrize('dtype', ['e', 'f', 'd'])
@pytest.mark.parametrize('value', [np.inf, -np.inf])
def test_sincos_errors(self, callable, dtype, value):
# 设置浮点错误状态为无效时抛出异常
with np.errstate(invalid='raise'):
# 断言调用 callable 函数时,传入包含特定值的数组会引发浮点错误异常
assert_raises(FloatingPointError, callable,
np.array([value], dtype=dtype))
# 测试 'sin' 和 'cos' 函数处理重叠数据的情况
@pytest.mark.parametrize('callable', [np.sin, np.cos])
@pytest.mark.parametrize('dtype', ['f', 'd'])
@pytest.mark.parametrize('stride', [-1, 1, 2, 4, 5])
def test_sincos_overlaps(self, callable, dtype, stride):
# 定义数组大小为 N
N = 100
# 根据步幅计算 M
M = N // abs(stride)
# 使用随机数生成器创建随机正态分布的数组 x
rng = np.random.default_rng(42)
x = rng.standard_normal(N, dtype)
# 调用 callable 函数处理数组 x 的子集,并将结果存储到 y 中
y = callable(x[::stride])
# 再次调用 callable 函数,使用相同的子集作为输入,并将结果存储回 x 的前 M 个元素
callable(x[::stride], out=x[:M])
# 断言 x 的前 M 个元素等于 y
assert_equal(x[:M], y)
# 测试处理 'sqrt' 函数的异常值情况
@pytest.mark.parametrize('dt', ['e', 'f', 'd', 'g'])
def test_sqrt_values(self, dt):
# 忽略所有浮点错误状态
with np.errstate(all='ignore'):
# 创建包含 NaN, Inf 和 0 的数组 x 和 y
x = [np.nan, np.nan, np.inf, np.nan, 0.]
y = [np.nan, -np.nan, np.inf, -np.inf, 0.]
# 创建对应数据类型的数组 xf 和 yf
xf = np.array(x, dtype=dt)
yf = np.array(y, dtype=dt)
# 断言对 yf 使用 sqrt 函数的结果等于 xf
assert_equal(np.sqrt(yf), xf)
# 以下代码段被注释掉,原本是测试传入负数时 sqrt 函数抛出浮点错误异常的情况
# 测试处理 'abs' 函数的异常值情况
def test_abs_values(self):
# 创建包含 NaN, Inf 和 0 的数组 x 和 y
x = [np.nan, np.nan, np.inf, np.inf, 0., 0., 1.0, 1.0]
y = [np.nan, -np.nan, np.inf, -np.inf, 0., -0., -1.0, 1.0]
# 遍历不同的数据类型
for dt in ['e', 'f', 'd', 'g']:
# 创建对应数据类型的数组 xf 和 yf
xf = np.array(x, dtype=dt)
yf = np.array(y, dtype=dt)
# 断言对 yf 使用 abs 函数的结果等于 xf
# 使用 pytest 的 skipif 标记,在运行在 wasm 环境下跳过测试,原因是浮点错误在 wasm 中无效
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
# 测试处理 'square' 函数的异常值情况
def test_square_values(self):
# 创建包含 NaN, Inf 的数组 x 和 y
x = [np.nan, np.nan, np.inf, np.inf]
y = [np.nan, -np.nan, np.inf, -np.inf]
# 忽略所有浮点错误状态
with np.errstate(all='ignore'):
# 遍历不同的数据类型
for dt in ['e', 'f', 'd', 'g']:
# 创建对应数据类型的数组 xf 和 yf
xf = np.array(x, dtype=dt)
yf = np.array(y, dtype=dt)
# 断言对 yf 使用 square 函数的结果等于 xf
# 设置浮点错误状态为溢出时抛出异常
with np.errstate(over='raise'):
# 断言对传入特定值时调用 square 函数会引发浮点错误异常
assert_raises(FloatingPointError, np.square,
np.array(1E3, dtype='e'))
assert_raises(FloatingPointError, np.square,
np.array(1E32, dtype='f'))
assert_raises(FloatingPointError, np.square,
np.array(1E200, dtype='d'))
# 跳过在 WebAssembly 环境下执行,因为浮点数错误不起作用
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
# 测试 np.reciprocal 函数的行为
def test_reciprocal_values(self):
# 忽略所有错误状态
with np.errstate(all='ignore'):
# 定义输入和期望输出数组
x = [np.nan, np.nan, 0.0, -0.0, np.inf, -np.inf]
y = [np.nan, -np.nan, np.inf, -np.inf, 0., -0.]
# 对于每种数据类型进行测试
for dt in ['e', 'f', 'd', 'g']:
# 将输入转换为指定数据类型的数组
xf = np.array(x, dtype=dt)
yf = np.array(y, dtype=dt)
# 断言 np.reciprocal(yf) 的结果等于 xf
assert_equal(np.reciprocal(yf), xf)
# 设置除法错误状态为抛出异常
with np.errstate(divide='raise'):
# 对于每种数据类型进行测试
for dt in ['e', 'f', 'd', 'g']:
# 断言对于 np.array(-0.0, dtype=dt),调用 np.reciprocal 会抛出 FloatingPointError 异常
assert_raises(FloatingPointError, np.reciprocal,
np.array(-0.0, dtype=dt))
# 跳过在 WebAssembly 环境下执行,因为浮点数错误不起作用
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
# 测试 np.tan 函数的行为
def test_tan(self):
# 忽略所有错误状态
with np.errstate(all='ignore'):
# 定义输入和期望输出数组
in_ = [np.nan, -np.nan, 0.0, -0.0, np.inf, -np.inf]
out = [np.nan, np.nan, 0.0, -0.0, np.nan, np.nan]
# 对于每种数据类型进行测试
for dt in ['e', 'f', 'd']:
# 将输入转换为指定数据类型的数组
in_arr = np.array(in_, dtype=dt)
out_arr = np.array(out, dtype=dt)
# 断言 np.tan(in_arr) 的结果等于 out_arr
assert_equal(np.tan(in_arr), out_arr)
# 设置无效输入状态为抛出异常
with np.errstate(invalid='raise'):
# 对于每种数据类型进行测试
for dt in ['e', 'f', 'd']:
# 断言对于 np.array(np.inf, dtype=dt),调用 np.tan 会抛出 FloatingPointError 异常
assert_raises(FloatingPointError, np.tan,
np.array(np.inf, dtype=dt))
# 断言对于 np.array(-np.inf, dtype=dt),调用 np.tan 会抛出 FloatingPointError 异常
assert_raises(FloatingPointError, np.tan,
np.array(-np.inf, dtype=dt))
# 跳过在 WebAssembly 环境下执行,因为浮点数错误不起作用
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
# 测试 np.arcsin 和 np.arccos 函数的行为
def test_arcsincos(self):
# 忽略所有错误状态
with np.errstate(all='ignore'):
# 定义输入和期望输出数组
in_ = [np.nan, -np.nan, np.inf, -np.inf]
out = [np.nan, np.nan, np.nan, np.nan]
# 对于每种数据类型进行测试
for dt in ['e', 'f', 'd']:
# 将输入转换为指定数据类型的数组
in_arr = np.array(in_, dtype=dt)
out_arr = np.array(out, dtype=dt)
# 断言 np.arcsin(in_arr) 的结果等于 out_arr
assert_equal(np.arcsin(in_arr), out_arr)
# 断言 np.arccos(in_arr) 的结果等于 out_arr
assert_equal(np.arccos(in_arr), out_arr)
# 对于 np.arcsin 和 np.arccos 函数,使用不合法的值进行测试
for callable in [np.arcsin, np.arccos]:
for value in [np.inf, -np.inf, 2.0, -2.0]:
# 对于每种数据类型进行测试
for dt in ['e', 'f', 'd']:
# 设置无效输入状态为抛出异常
with np.errstate(invalid='raise'):
# 断言对于 np.array(value, dtype=dt),调用 callable 会抛出 FloatingPointError 异常
assert_raises(FloatingPointError, callable,
np.array(value, dtype=dt))
# 测试 np.arctan 函数的行为
def test_arctan(self):
# 忽略所有错误状态
with np.errstate(all='ignore'):
# 定义输入和期望输出数组
in_ = [np.nan, -np.nan]
out = [np.nan, np.nan]
# 对于每种数据类型进行测试
for dt in ['e', 'f', 'd']:
# 将输入转换为指定数据类型的数组
in_arr = np.array(in_, dtype=dt)
out_arr = np.array(out, dtype=dt)
# 断言 np.arctan(in_arr) 的结果等于 out_arr
assert_equal(np.arctan(in_arr), out_arr)
def test_sinh(self):
# 输入数组,包含 NaN 和无穷大等特殊值
in_ = [np.nan, -np.nan, np.inf, -np.inf]
# 预期输出数组,与输入相同的特殊值
out = [np.nan, np.nan, np.inf, -np.inf]
# 对于每种数据类型 'e', 'f', 'd',执行以下测试
for dt in ['e', 'f', 'd']:
# 创建指定数据类型的输入数组
in_arr = np.array(in_, dtype=dt)
# 创建预期输出数组
out_arr = np.array(out, dtype=dt)
# 断言 np.sinh(in_arr) 等于 out_arr
assert_equal(np.sinh(in_arr), out_arr)
# 在上下文中处理浮点数异常,设置 'over' 为 'raise' 模式
with np.errstate(over='raise'):
# 断言对于浮点数 12.0,使用 'e' 数据类型,调用 np.sinh 会引发 FloatingPointError
assert_raises(FloatingPointError, np.sinh,
np.array(12.0, dtype='e'))
# 断言对于浮点数 120.0,使用 'f' 数据类型,调用 np.sinh 会引发 FloatingPointError
assert_raises(FloatingPointError, np.sinh,
np.array(120.0, dtype='f'))
# 断言对于浮点数 1200.0,使用 'd' 数据类型,调用 np.sinh 会引发 FloatingPointError
assert_raises(FloatingPointError, np.sinh,
np.array(1200.0, dtype='d'))
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
@pytest.mark.skipif('bsd' in sys.platform,
reason="fallback implementation may not raise, see gh-2487")
def test_cosh(self):
# 输入数组,包含 NaN 和无穷大等特殊值
in_ = [np.nan, -np.nan, np.inf, -np.inf]
# 预期输出数组,其中无穷大的输出值不同
out = [np.nan, np.nan, np.inf, np.inf]
# 对于每种数据类型 'e', 'f', 'd',执行以下测试
for dt in ['e', 'f', 'd']:
# 创建指定数据类型的输入数组
in_arr = np.array(in_, dtype=dt)
# 创建预期输出数组
out_arr = np.array(out, dtype=dt)
# 断言 np.cosh(in_arr) 等于 out_arr
assert_equal(np.cosh(in_arr), out_arr)
# 在上下文中处理浮点数异常,设置 'over' 为 'raise' 模式
with np.errstate(over='raise'):
# 断言对于浮点数 12.0,使用 'e' 数据类型,调用 np.cosh 会引发 FloatingPointError
assert_raises(FloatingPointError, np.cosh,
np.array(12.0, dtype='e'))
# 断言对于浮点数 120.0,使用 'f' 数据类型,调用 np.cosh 会引发 FloatingPointError
assert_raises(FloatingPointError, np.cosh,
np.array(120.0, dtype='f'))
# 断言对于浮点数 1200.0,使用 'd' 数据类型,调用 np.cosh 会引发 FloatingPointError
assert_raises(FloatingPointError, np.cosh,
np.array(1200.0, dtype='d'))
def test_tanh(self):
# 输入数组,包含 NaN 和无穷大等特殊值
in_ = [np.nan, -np.nan, np.inf, -np.inf]
# 预期输出数组,对应于 np.tanh 的输出
out = [np.nan, np.nan, 1.0, -1.0]
# 对于每种数据类型 'e', 'f', 'd',执行以下测试
for dt in ['e', 'f', 'd']:
# 创建指定数据类型的输入数组
in_arr = np.array(in_, dtype=dt)
# 创建预期输出数组
out_arr = np.array(out, dtype=dt)
# 断言 np.tanh(in_arr) 与 out_arr 之间的最大ulp误差不超过3
assert_array_max_ulp(np.tanh(in_arr), out_arr, 3)
def test_arcsinh(self):
# 输入数组,包含 NaN 和无穷大等特殊值
in_ = [np.nan, -np.nan, np.inf, -np.inf]
# 预期输出数组,与输入相同的特殊值
out = [np.nan, np.nan, np.inf, -np.inf]
# 对于每种数据类型 'e', 'f', 'd',执行以下测试
for dt in ['e', 'f', 'd']:
# 创建指定数据类型的输入数组
in_arr = np.array(in_, dtype=dt)
# 创建预期输出数组
out_arr = np.array(out, dtype=dt)
# 断言 np.arcsinh(in_arr) 等于 out_arr
assert_equal(np.arcsinh(in_arr), out_arr)
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
def test_arccosh(self):
# 在上下文中忽略所有错误
with np.errstate(all='ignore'):
# 输入数组,包含 NaN 和无穷大等特殊值,以及额外的 1.0 和 0.0
in_ = [np.nan, -np.nan, np.inf, -np.inf, 1.0, 0.0]
# 预期输出数组,与输入相同的特殊值,以及额外的 0.0 和 NaN
out = [np.nan, np.nan, np.inf, np.nan, 0.0, np.nan]
# 对于每种数据类型 'e', 'f', 'd',执行以下测试
for dt in ['e', 'f', 'd']:
# 创建指定数据类型的输入数组
in_arr = np.array(in_, dtype=dt)
# 创建预期输出数组
out_arr = np.array(out, dtype=dt)
# 断言 np.arccosh(in_arr) 等于 out_arr
assert_equal(np.arccosh(in_arr), out_arr)
# 对于值为 0.0 和 -无穷大,在上下文中设置无效操作 'invalid' 为 'raise' 模式
for value in [0.0, -np.inf]:
with np.errstate(invalid='raise'):
# 对于每种数据类型 'e', 'f', 'd',断言调用 np.arccosh(np.array(value, dtype=dt)) 会引发 FloatingPointError
for dt in ['e', 'f', 'd']:
assert_raises(FloatingPointError, np.arccosh,
np.array(value, dtype=dt))
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
def test_arctanh(self):
# 忽略所有的浮点错误
with np.errstate(all='ignore'):
# 输入数组,包括 NaN、无限大和常规浮点数
in_ = [np.nan, -np.nan, np.inf, -np.inf, 1.0, -1.0, 2.0]
# 预期输出数组,与输入对应位置有关的预期输出值
out = [np.nan, np.nan, np.nan, np.nan, np.inf, -np.inf, np.nan]
# 对于每种数据类型进行测试
for dt in ['e', 'f', 'd']:
# 创建输入数组,指定数据类型
in_arr = np.array(in_, dtype=dt)
# 创建预期输出数组,指定数据类型
out_arr = np.array(out, dtype=dt)
# 断言 np.arctanh(in_arr) 等于 out_arr
assert_equal(np.arctanh(in_arr), out_arr)
# 对于特定数值,验证是否会引发浮点错误异常
for value in [1.01, np.inf, -np.inf, 1.0, -1.0]:
# 设置无效(invalid)和除零(divide)错误时引发异常
with np.errstate(invalid='raise', divide='raise'):
# 对于每种数据类型进行测试
for dt in ['e', 'f', 'd']:
# 断言调用 np.arctanh(np.array(value, dtype=dt)) 会引发 FloatingPointError 异常
assert_raises(FloatingPointError, np.arctanh,
np.array(value, dtype=dt))
# 确保 glibc < 2.18 的情况下不会使用 atanh,参考 issue 25087
assert np.signbit(np.arctanh(-1j).real)
# 查看:https://github.com/numpy/numpy/issues/20448
@pytest.mark.xfail(
_glibc_older_than("2.17"),
reason="Older glibc versions may not raise appropriate FP exceptions"
)
def test_exp2(self):
# 忽略所有的浮点错误
with np.errstate(all='ignore'):
# 输入数组,包括 NaN、无限大
in_ = [np.nan, -np.nan, np.inf, -np.inf]
# 预期输出数组,与输入对应位置有关的预期输出值
out = [np.nan, np.nan, np.inf, 0.0]
# 对于每种数据类型进行测试
for dt in ['e', 'f', 'd']:
# 创建输入数组,指定数据类型
in_arr = np.array(in_, dtype=dt)
# 创建预期输出数组,指定数据类型
out_arr = np.array(out, dtype=dt)
# 断言 np.exp2(in_arr) 等于 out_arr
assert_equal(np.exp2(in_arr), out_arr)
# 对于特定数值,验证是否会引发浮点错误异常
for value in [2000.0, -2000.0]:
# 设置溢出(over)和下溢(under)错误时引发异常
with np.errstate(over='raise', under='raise'):
# 对于每种数据类型进行测试
for dt in ['e', 'f', 'd']:
# 断言调用 np.exp2(np.array(value, dtype=dt)) 会引发 FloatingPointError 异常
assert_raises(FloatingPointError, np.exp2,
np.array(value, dtype=dt))
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
def test_expm1(self):
# 忽略所有的浮点错误
with np.errstate(all='ignore'):
# 输入数组,包括 NaN、无限大
in_ = [np.nan, -np.nan, np.inf, -np.inf]
# 预期输出数组,与输入对应位置有关的预期输出值
out = [np.nan, np.nan, np.inf, -1.0]
# 对于每种数据类型进行测试
for dt in ['e', 'f', 'd']:
# 创建输入数组,指定数据类型
in_arr = np.array(in_, dtype=dt)
# 创建预期输出数组,指定数据类型
out_arr = np.array(out, dtype=dt)
# 断言 np.expm1(in_arr) 等于 out_arr
assert_equal(np.expm1(in_arr), out_arr)
# 对于特定数值,验证是否会引发浮点错误异常
for value in [200.0, 2000.0]:
# 设置溢出(over)错误时引发异常
with np.errstate(over='raise'):
# 对于每种数据类型进行测试
for dt in ['e', 'f']:
# 断言调用 np.expm1(np.array(value, dtype=dt)) 会引发 FloatingPointError 异常
assert_raises(FloatingPointError, np.expm1,
np.array(value, dtype=dt))
# 测试以确保 SIMD 不会由于浮点错误引发意外异常
INF_INVALID_ERR = [
np.cos, np.sin, np.tan, np.arccos, np.arcsin, np.spacing, np.arctanh
]
NEG_INVALID_ERR = [
np.log, np.log2, np.log10, np.log1p, np.sqrt, np.arccosh,
np.arctanh
]
ONE_INVALID_ERR = [
np.arctanh,
]
LTONE_INVALID_ERR = [
np.arccosh,
]
BYZERO_ERR = [
np.log, np.log2, np.log10, np.reciprocal, np.arccosh
]
@pytest.mark.parametrize("ufunc", UFUNCS_UNARY_FP)
@pytest.mark.parametrize("dtype", ('e', 'f', 'd'))
# 使用 pytest 的参数化装饰器,为单元测试函数提供不同的测试数据和期望结果
@pytest.mark.parametrize("data, escape", (
# 测试小于或等于0.03的浮点数数组,期望触发LTONE_INVALID_ERR异常
([0.03], LTONE_INVALID_ERR),
([0.03]*32, LTONE_INVALID_ERR),
# 测试负数数组,期望触发NEG_INVALID_ERR异常
([-1.0], NEG_INVALID_ERR),
([-1.0]*32, NEG_INVALID_ERR),
# 测试正数等于1.0的数组,期望触发ONE_INVALID_ERR异常
([1.0], ONE_INVALID_ERR),
([1.0]*32, ONE_INVALID_ERR),
# 测试零值数组,期望触发BYZERO_ERR异常
([0.0], BYZERO_ERR),
([0.0]*32, BYZERO_ERR),
([-0.0], BYZERO_ERR),
([-0.0]*32, BYZERO_ERR),
# 测试包含NaN的数组,期望触发LTONE_INVALID_ERR异常
([0.5, 0.5, 0.5, np.nan], LTONE_INVALID_ERR),
([0.5, 0.5, 0.5, np.nan]*32, LTONE_INVALID_ERR),
([np.nan, 1.0, 1.0, 1.0], ONE_INVALID_ERR),
([np.nan, 1.0, 1.0, 1.0]*32, ONE_INVALID_ERR),
([np.nan], []),
([np.nan]*32, []),
# 测试包含正无穷大的数组,期望触发INF_INVALID_ERR异常或其组合
([0.5, 0.5, 0.5, np.inf], INF_INVALID_ERR + LTONE_INVALID_ERR),
([0.5, 0.5, 0.5, np.inf]*32, INF_INVALID_ERR + LTONE_INVALID_ERR),
([np.inf, 1.0, 1.0, 1.0], INF_INVALID_ERR),
([np.inf, 1.0, 1.0, 1.0]*32, INF_INVALID_ERR),
([np.inf], INF_INVALID_ERR),
([np.inf]*32, INF_INVALID_ERR),
# 测试包含负无穷大的数组,期望触发NEG_INVALID_ERR、INF_INVALID_ERR和LTONE_INVALID_ERR的组合
([0.5, 0.5, 0.5, -np.inf],
NEG_INVALID_ERR + INF_INVALID_ERR + LTONE_INVALID_ERR),
([0.5, 0.5, 0.5, -np.inf]*32,
NEG_INVALID_ERR + INF_INVALID_ERR + LTONE_INVALID_ERR),
([-np.inf, 1.0, 1.0, 1.0], NEG_INVALID_ERR + INF_INVALID_ERR),
([-np.inf, 1.0, 1.0, 1.0]*32, NEG_INVALID_ERR + INF_INVALID_ERR),
([-np.inf], NEG_INVALID_ERR + INF_INVALID_ERR),
([-np.inf]*32, NEG_INVALID_ERR + INF_INVALID_ERR),
))
# 定义测试浮点数异常处理的单元测试函数
def test_unary_spurious_fpexception(self, ufunc, dtype, data, escape):
# 如果 escape 非空且 ufunc 在 escape 列表中,则跳过测试
if escape and ufunc in escape:
return
# TODO: 如果 ufunc 是 np.spacing 或 np.ceil 且 dtype 是 'e',则跳过测试
if ufunc in (np.spacing, np.ceil) and dtype == 'e':
return
# 创建 numpy 数组,使用给定的数据和数据类型
array = np.array(data, dtype=dtype)
# 使用 assert_no_warnings 上下文确保在运行 ufunc 时不会触发警告
with assert_no_warnings():
ufunc(array)
# 使用 pytest 的参数化装饰器,为单元测试函数提供不同的数据类型
@pytest.mark.parametrize("dtype", ('e', 'f', 'd'))
# 定义测试除法异常处理的单元测试函数
def test_divide_spurious_fpexception(self, dtype):
# 获取给定数据类型的信息
dt = np.dtype(dtype)
dt_info = np.finfo(dt)
# 获取最小子正常数值
subnorm = dt_info.smallest_subnormal
# 使用 assert_no_warnings 上下文确保在运行除法时不会触发警告
with assert_no_warnings():
# 创建一个具有128 + 1个元素的全零数组,并使用给定数据类型进行除法运算
np.zeros(128 + 1, dtype=dt) / subnorm
# 定义一个测试类 TestFPClass,用于测试浮点数处理的各种情况
class TestFPClass:
# 使用 pytest 的参数化装饰器,对 test_fpclass 方法进行参数化测试,参数为 stride
@pytest.mark.parametrize("stride", [-5, -4, -3, -2, -1, 1,
2, 4, 5, 6, 7, 8, 9, 10])
# 测试方法,测试浮点数分类函数
def test_fpclass(self, stride):
# 创建一个双精度浮点数数组 arr_f64,包含 NaN、±∞、正负数等特殊值
arr_f64 = np.array([np.nan, -np.nan, np.inf, -np.inf, -1.0, 1.0, -0.0, 0.0, 2.2251e-308, -2.2251e-308], dtype='d')
# 创建一个单精度浮点数数组 arr_f32,包含 NaN、±∞、正负数等特殊值
arr_f32 = np.array([np.nan, -np.nan, np.inf, -np.inf, -1.0, 1.0, -0.0, 0.0, 1.4013e-045, -1.4013e-045], dtype='f')
# 创建一个布尔数组 nan,用于比较 arr_f32 和 arr_f64 中的 NaN 值
nan = np.array([True, True, False, False, False, False, False, False, False, False])
# 创建一个布尔数组 inf,用于比较 arr_f32 和 arr_f64 中的 ±∞ 值
inf = np.array([False, False, True, True, False, False, False, False, False, False])
# 创建一个布尔数组 sign,用于比较 arr_f32 和 arr_f64 中的符号位
sign = np.array([False, True, False, True, True, False, True, False, False, True])
# 创建一个布尔数组 finite,用于比较 arr_f32 和 arr_f64 中的有限数值
finite = np.array([False, False, False, False, True, True, True, True, True, True])
# 断言函数,验证 arr_f32 和 arr_f64 中提取出来的 NaN 值是否与预期的 nan 数组一致
assert_equal(np.isnan(arr_f32[::stride]), nan[::stride])
assert_equal(np.isnan(arr_f64[::stride]), nan[::stride])
# 断言函数,验证 arr_f32 和 arr_f64 中提取出来的 ±∞ 值是否与预期的 inf 数组一致
assert_equal(np.isinf(arr_f32[::stride]), inf[::stride])
assert_equal(np.isinf(arr_f64[::stride]), inf[::stride])
# 如果运行环境是 RISC-V 64 位架构
if platform.machine() == 'riscv64':
# 在 RISC-V 架构上,许多产生 NaN 的操作(例如从 f64 转换为 f32 的 -NaN),返回规范化的 NaN。
# 规范化的 NaN 总是正数。详见 RISC-V Unprivileged ISA 第 11.3 节 NaN 生成和传播。
# 在这些测试中,我们禁用 riscv64 上 -np.nan 的符号位测试,因为我们不能假设其符号在这些测试中被尊重。
# 复制 arr_f64 和 arr_f32 数组
arr_f64_rv = np.copy(arr_f64)
arr_f32_rv = np.copy(arr_f32)
arr_f64_rv[1] = -1.0
arr_f32_rv[1] = -1.0
# 断言函数,验证在 RISC-V 环境下,arr_f32_rv 和 arr_f64_rv 中提取出来的符号位是否与预期的 sign 数组一致
assert_equal(np.signbit(arr_f32_rv[::stride]), sign[::stride])
assert_equal(np.signbit(arr_f64_rv[::stride]), sign[::stride])
else:
# 断言函数,验证在非 RISC-V 环境下,arr_f32 和 arr_f64 中提取出来的符号位是否与预期的 sign 数组一致
assert_equal(np.signbit(arr_f32[::stride]), sign[::stride])
assert_equal(np.signbit(arr_f64[::stride]), sign[::stride])
# 断言函数,验证 arr_f32 和 arr_f64 中提取出来的有限数值是否与预期的 finite 数组一致
assert_equal(np.isfinite(arr_f32[::stride]), finite[::stride])
assert_equal(np.isfinite(arr_f64[::stride]), finite[::stride])
# 使用 pytest 的参数化装饰器,对 TestLDExp 类的 test_ldexp 方法进行参数化测试,参数为 dtype 和 stride
@pytest.mark.parametrize("dtype", ['d', 'f'])
class TestLDExp:
# 使用 pytest 的参数化装饰器,对 test_ldexp 方法进行参数化测试,参数为 stride 和 dtype
@pytest.mark.parametrize("stride", [-4,-2,-1,1,2,4])
@pytest.mark.parametrize("dtype", ['f', 'd'])
# 测试方法,测试 ldexp 函数
def test_ldexp(self, dtype, stride):
# 创建一个浮点数数组 mant,用于 ldexp 函数的尾数参数,根据 dtype 决定精度
mant = np.array([0.125, 0.25, 0.5, 1., 1., 2., 4., 8.], dtype=dtype)
# 创建一个整数数组 exp,用于 ldexp 函数的指数参数
exp = np.array([3, 2, 1, 0, 0, -1, -2, -3], dtype='i')
# 创建一个输出数组 out,用于接收 ldexp 函数的输出结果
out = np.zeros(8, dtype=dtype)
# 断言函数,验证 ldexp 函数的输出是否与预期的全为 1 的数组一致
assert_equal(np.ldexp(mant[::stride], exp[::stride], out=out[::stride]), np.ones(8, dtype=dtype)[::stride])
# 断言函数,验证 out 数组是否与预期的全为 1 的数组一致
assert_equal(out[::stride], np.ones(8, dtype=dtype)[::stride])
# 定义一个测试类 TestFRExp,用于测试 frexp 函数,但本代码中未提供完整的实现和测试代码
class TestFRExp:
@pytest.mark.parametrize("stride", [-4,-2,-1,1,2,4])
@pytest.mark.parametrize("dtype", ['f', 'd'])
# 标记测试用例,仅在运行环境为 Linux 时执行;否则跳过测试并给出理由
@pytest.mark.skipif(not sys.platform.startswith('linux'),
reason="np.frexp gives different answers for NAN/INF on windows and linux")
# 标记测试预期会失败,针对 Musl libc(一个 C 库)中的问题 gh23049
@pytest.mark.xfail(IS_MUSL, reason="gh23049")
# 定义测试函数,接受数据类型和步长作为参数
def test_frexp(self, dtype, stride):
# 创建包含各种特殊浮点数和常规浮点数的 numpy 数组
arr = np.array([np.nan, np.nan, np.inf, -np.inf, 0.0, -0.0, 1.0, -1.0], dtype=dtype)
# 创建预期的 mantissa 数组,与 arr 数组对应
mant_true = np.array([np.nan, np.nan, np.inf, -np.inf, 0.0, -0.0, 0.5, -0.5], dtype=dtype)
# 创建预期的 exponent 数组,与 arr 数组对应
exp_true = np.array([0, 0, 0, 0, 0, 0, 1, 1], dtype='i')
# 创建输出 mantissa 的数组,初始化为全 1
out_mant = np.ones(8, dtype=dtype)
# 创建输出 exponent 的数组,初始化为全 2
out_exp = 2*np.ones(8, dtype='i')
# 调用 numpy 中的 frexp 函数,计算 arr 数组的 mantissa 和 exponent,
# 将结果存储到 out_mant 和 out_exp 数组中
mant, exp = np.frexp(arr[::stride], out=(out_mant[::stride], out_exp[::stride]))
# 断言计算得到的 mantissa 与预期的 mantissa 相等
assert_equal(mant_true[::stride], mant)
# 断言计算得到的 exponent 与预期的 exponent 相等
assert_equal(exp_true[::stride], exp)
# 断言输出的 mantissa 与预期的 mantissa 相等
assert_equal(out_mant[::stride], mant_true[::stride])
# 断言输出的 exponent 与预期的 exponent 相等
assert_equal(out_exp[::stride], exp_true[::stride])
# 定义 AVX 支持的数学函数及其测试参数
avx_ufuncs = {'sqrt' :[1, 0., 100.], # 平方根函数的最大误差、最小值和最大值范围
'absolute' :[0, -100., 100.], # 绝对值函数的最大误差、最小值和最大值范围
'reciprocal' :[1, 1., 100.], # 倒数函数的最大误差、最小值和最大值范围
'square' :[1, -100., 100.], # 平方函数的最大误差、最小值和最大值范围
'rint' :[0, -100., 100.], # 最接近整数的函数的最大误差、最小值和最大值范围
'floor' :[0, -100., 100.], # 向下取整函数的最大误差、最小值和最大值范围
'ceil' :[0, -100., 100.], # 向上取整函数的最大误差、最小值和最大值范围
'trunc' :[0, -100., 100.]} # 截断函数的最大误差、最小值和最大值范围
class TestAVXUfuncs:
def test_avx_based_ufunc(self):
strides = np.array([-4,-3,-2,-1,1,2,3,4]) # 不同步长用于测试 AVX 中的掩码指令
np.random.seed(42)
for func, prop in avx_ufuncs.items():
maxulperr = prop[0] # 最大单位最小误差
minval = prop[1] # 输入数组的最小值
maxval = prop[2] # 输入数组的最大值
# 不同大小的数组确保在 AVX 中的掩码测试
for size in range(1,32):
myfunc = getattr(np, func)
x_f32 = np.random.uniform(low=minval, high=maxval,
size=size).astype(np.float32)
x_f64 = x_f32.astype(np.float64)
x_f128 = x_f32.astype(np.longdouble)
y_true128 = myfunc(x_f128)
if maxulperr == 0:
assert_equal(myfunc(x_f32), y_true128.astype(np.float32)) # 如果最大单位最小误差为零,检查单精度浮点数的相等性
assert_equal(myfunc(x_f64), y_true128.astype(np.float64)) # 检查双精度浮点数的相等性
else:
assert_array_max_ulp(myfunc(x_f32),
y_true128.astype(np.float32),
maxulp=maxulperr) # 否则,使用最大单位最小误差检查单精度浮点数数组的最大单位最小误差
assert_array_max_ulp(myfunc(x_f64),
y_true128.astype(np.float64),
maxulp=maxulperr) # 使用最大单位最小误差检查双精度浮点数数组的最大单位最小误差
# 不同的步长来测试 gather 指令
if size > 1:
y_true32 = myfunc(x_f32)
y_true64 = myfunc(x_f64)
for jj in strides:
assert_equal(myfunc(x_f64[::jj]), y_true64[::jj]) # 检查使用不同步长的双精度浮点数数组的相等性
assert_equal(myfunc(x_f32[::jj]), y_true32[::jj]) # 检查使用不同步长的单精度浮点数数组的相等性
class TestAVXFloat32Transcendental:
def test_exp_float32(self):
np.random.seed(42)
x_f32 = np.float32(np.random.uniform(low=0.0,high=88.1,size=1000000))
x_f64 = np.float64(x_f32)
assert_array_max_ulp(np.exp(x_f32), np.float32(np.exp(x_f64)), maxulp=3) # 检查单精度浮点数的指数函数的最大单位最小误差
def test_log_float32(self):
np.random.seed(42)
x_f32 = np.float32(np.random.uniform(low=0.0,high=1000,size=1000000))
x_f64 = np.float64(x_f32)
assert_array_max_ulp(np.log(x_f32), np.float32(np.log(x_f64)), maxulp=4) # 检查单精度浮点数的对数函数的最大单位最小误差
# 定义一个测试函数,用于测试单精度浮点数的 sin 和 cos 函数
def test_sincos_float32(self):
# 设定随机数种子,确保可重复性
np.random.seed(42)
# 设定数组大小
N = 1000000
# 计算 M 的值,M 是 N 的二十分之一
M = np.int_(N/20)
# 从 [0, N) 的范围内生成 M 个随机整数作为索引
index = np.random.randint(low=0, high=N, size=M)
# 生成 N 个在 [-100.0, 100.0) 范围内的随机单精度浮点数数组
x_f32 = np.float32(np.random.uniform(low=-100., high=100., size=N))
# 如果不是旧版 glibc (小于 2.17),则覆盖大于 117435.992 的元素,这些情况下会使用 glibc
if not _glibc_older_than("2.17"):
# 将索引处的元素设置为较大的随机单精度浮点数,避免旧版 glibc 的问题
x_f32[index] = np.float32(10E+10 * np.random.rand(M))
# 将单精度浮点数数组转换为双精度浮点数数组
x_f64 = np.float64(x_f32)
# 断言单精度浮点数数组和双精度浮点数数组的 sin 值最大误差不超过 2 个单位最后位 (ULP)
assert_array_max_ulp(np.sin(x_f32), np.float32(np.sin(x_f64)), maxulp=2)
# 断言单精度浮点数数组和双精度浮点数数组的 cos 值最大误差不超过 2 个单位最后位 (ULP)
assert_array_max_ulp(np.cos(x_f32), np.float32(np.cos(x_f64)), maxulp=2)
# 测试别名问题(issue #17761)
# 复制 x_f32 到 tx_f32
tx_f32 = x_f32.copy()
# 将 sin 计算结果存入 x_f32 本身,断言其与双精度浮点数的 sin 值的最大误差不超过 2 个 ULP
assert_array_max_ulp(np.sin(x_f32, out=x_f32), np.float32(np.sin(x_f64)), maxulp=2)
# 将 cos 计算结果存入 tx_f32 本身,断言其与双精度浮点数的 cos 值的最大误差不超过 2 个 ULP
assert_array_max_ulp(np.cos(tx_f32, out=tx_f32), np.float32(np.cos(x_f64)), maxulp=2)
# 定义一个测试函数,用于测试步长变化下的单精度浮点数函数
def test_strided_float32(self):
# 设定随机数种子,确保可重复性
np.random.seed(42)
# 设定步长数组
strides = np.array([-4, -3, -2, -1, 1, 2, 3, 4])
# 设定尺寸数组,从 2 到 99
sizes = np.arange(2, 100)
# 对每个尺寸进行循环
for ii in sizes:
# 生成一个包含 ii 个在 [0.01, 88.1) 范围内的随机单精度浮点数数组
x_f32 = np.float32(np.random.uniform(low=0.01, high=88.1, size=ii))
# 将 x_f32 复制到 x_f32_large
x_f32_large = x_f32.copy()
# 将 x_f32_large 中索引为 3 到倒数第二个的每隔 4 个元素设置为 120000.0
x_f32_large[3:-1:4] = 120000.0
# 计算期望的 exp、log、sin、cos 值
exp_true = np.exp(x_f32)
log_true = np.log(x_f32)
sin_true = np.sin(x_f32_large)
cos_true = np.cos(x_f32_large)
# 对步长数组进行循环
for jj in strides:
# 断言 exp 函数的步长计算结果和期望值的最大 ULP 误差不超过 2 个单位最后位
assert_array_almost_equal_nulp(np.exp(x_f32[::jj]), exp_true[::jj], nulp=2)
# 断言 log 函数的步长计算结果和期望值的最大 ULP 误差不超过 2 个单位最后位
assert_array_almost_equal_nulp(np.log(x_f32[::jj]), log_true[::jj], nulp=2)
# 断言 sin 函数的步长计算结果和期望值的最大 ULP 误差不超过 2 个单位最后位
assert_array_almost_equal_nulp(np.sin(x_f32_large[::jj]), sin_true[::jj], nulp=2)
# 断言 cos 函数的步长计算结果和期望值的最大 ULP 误差不超过 2 个单位最后位
assert_array_almost_equal_nulp(np.cos(x_f32_large[::jj]), cos_true[::jj], nulp=2)
class TestLogAddExp(_FilterInvalids):
# 测试 logaddexp 函数的值
def test_logaddexp_values(self):
# 定义输入数组
x = [1, 2, 3, 4, 5]
y = [5, 4, 3, 2, 1]
z = [6, 6, 6, 6, 6]
# 对于不同的数据类型和小数精度
for dt, dec_ in zip(['f', 'd', 'g'], [6, 15, 15]):
# 将 x, y, z 数组转换为对数值数组
xf = np.log(np.array(x, dtype=dt))
yf = np.log(np.array(y, dtype=dt))
zf = np.log(np.array(z, dtype=dt))
# 断言 logaddexp 函数计算的结果与期望的 zf 数组接近
assert_almost_equal(np.logaddexp(xf, yf), zf, decimal=dec_)
# 测试 logaddexp 函数的边界情况
def test_logaddexp_range(self):
# 定义输入数组
x = [1000000, -1000000, 1000200, -1000200]
y = [1000200, -1000200, 1000000, -1000000]
z = [1000200, -1000000, 1000200, -1000000]
# 对于不同的数据类型
for dt in ['f', 'd', 'g']:
# 将 x, y, z 数组转换为对数值数组
logxf = np.array(x, dtype=dt)
logyf = np.array(y, dtype=dt)
logzf = np.array(z, dtype=dt)
# 断言 logaddexp 函数计算的结果与期望的 logzf 数组相等
assert_almost_equal(np.logaddexp(logxf, logyf), logzf)
# 测试包含无穷值的情况
def test_inf(self):
inf = np.inf
# 定义输入数组
x = [inf, -inf, inf, -inf, inf, 1, -inf, 1]
y = [inf, inf, -inf, -inf, 1, inf, 1, -inf]
z = [inf, inf, inf, -inf, inf, inf, 1, 1]
# 在错误状态为无效时
with np.errstate(invalid='raise'):
# 对于不同的数据类型
for dt in ['f', 'd', 'g']:
# 将 x, y, z 数组转换为对数值数组
logxf = np.array(x, dtype=dt)
logyf = np.array(y, dtype=dt)
logzf = np.array(z, dtype=dt)
# 断言 logaddexp 函数计算的结果与期望的 logzf 数组相等
assert_equal(np.logaddexp(logxf, logyf), logzf)
# 测试包含 NaN 的情况
def test_nan(self):
# 断言 logaddexp 函数处理 NaN 值时返回 NaN
assert_(np.isnan(np.logaddexp(np.nan, np.inf)))
assert_(np.isnan(np.logaddexp(np.inf, np.nan)))
assert_(np.isnan(np.logaddexp(np.nan, 0)))
assert_(np.isnan(np.logaddexp(0, np.nan)))
assert_(np.isnan(np.logaddexp(np.nan, np.nan)))
# 测试 logaddexp 函数的 reduce 方法
def test_reduce(self):
# 断言 logaddexp 函数的 identity 属性为负无穷大
assert_equal(np.logaddexp.identity, -np.inf)
# 断言空数组调用 reduce 方法后返回负无穷大
assert_equal(np.logaddexp.reduce([]), -np.inf)
class TestLog1p:
# 测试 log1p 函数
def test_log1p(self):
# 断言 log1p 函数的正确性
assert_almost_equal(ncu.log1p(0.2), ncu.log(1.2))
assert_almost_equal(ncu.log1p(1e-6), ncu.log(1+1e-6))
# 测试 log1p 函数的特殊情况
def test_special(self):
# 在忽略无效和除法错误状态下,断言 log1p 处理 NaN 和无穷大时的返回值
with np.errstate(invalid="ignore", divide="ignore"):
assert_equal(ncu.log1p(np.nan), np.nan)
assert_equal(ncu.log1p(np.inf), np.inf)
assert_equal(ncu.log1p(-1.), -np.inf)
assert_equal(ncu.log1p(-2.), np.nan)
assert_equal(ncu.log1p(-np.inf), np.nan)
class TestExpm1:
# 测试 expm1 函数
def test_expm1(self):
# 断言 expm1 函数的正确性
assert_almost_equal(ncu.expm1(0.2), ncu.exp(0.2)-1)
assert_almost_equal(ncu.expm1(1e-6), ncu.exp(1e-6)-1)
# 测试 expm1 函数的特殊情况
def test_special(self):
# 断言 expm1 处理特殊输入时的返回值
assert_equal(ncu.expm1(np.inf), np.inf)
assert_equal(ncu.expm1(0.), 0.)
assert_equal(ncu.expm1(-0.), -0.)
assert_equal(ncu.expm1(np.inf), np.inf)
assert_equal(ncu.expm1(-np.inf), -1.)
# 测试复数输入的情况
def test_complex(self):
x = np.asarray(1e-12)
# 断言复数输入时 expm1 的正确性
assert_allclose(x, ncu.expm1(x))
x = x.astype(np.complex128)
assert_allclose(x, ncu.expm1(x))
# 定义单元测试方法,测试简单情况下的 hypot 函数调用
def test_simple(self):
# 断言 hypot 函数计算结果接近 sqrt(2)
assert_almost_equal(ncu.hypot(1, 1), ncu.sqrt(2))
# 断言 hypot 函数计算 (0, 0) 时结果为 0
assert_almost_equal(ncu.hypot(0, 0), 0)
# 定义单元测试方法,测试 hypot 函数的 reduce 方法
def test_reduce(self):
# 断言 hypot 函数对于数组 [3.0, 4.0] 的 reduce 结果接近 5.0
assert_almost_equal(ncu.hypot.reduce([3.0, 4.0]), 5.0)
# 断言 hypot 函数对于数组 [3.0, 4.0, 0] 的 reduce 结果接近 5.0
assert_almost_equal(ncu.hypot.reduce([3.0, 4.0, 0]), 5.0)
# 断言 hypot 函数对于数组 [9.0, 12.0, 20.0] 的 reduce 结果接近 25.0
assert_almost_equal(ncu.hypot.reduce([9.0, 12.0, 20.0]), 25.0)
# 断言 hypot 函数对于空数组 [] 的 reduce 结果为 0.0
assert_equal(ncu.hypot.reduce([]), 0.0)
# 定义一个函数,用于检查通过忽略无效操作状态后计算的 hypot(x, y) 是否为 NaN
def assert_hypot_isnan(x, y):
with np.errstate(invalid='ignore'): # 设置 numpy 的错误状态为忽略无效操作
assert_(np.isnan(ncu.hypot(x, y)), # 断言 hypot(x, y) 的结果是否为 NaN
"hypot(%s, %s) is %s, not nan" % (x, y, ncu.hypot(x, y))) # 如果断言失败,输出错误信息
# 定义一个函数,用于检查通过忽略无效操作状态后计算的 hypot(x, y) 是否为无穷大
def assert_hypot_isinf(x, y):
with np.errstate(invalid='ignore'): # 设置 numpy 的错误状态为忽略无效操作
assert_(np.isinf(ncu.hypot(x, y)), # 断言 hypot(x, y) 的结果是否为无穷大
"hypot(%s, %s) is %s, not inf" % (x, y, ncu.hypot(x, y))) # 如果断言失败,输出错误信息
# 定义一个测试类,用于测试 hypot 函数对特殊值的处理
class TestHypotSpecialValues:
# 测试当输入为 NaN 时,hypot 函数的输出是否为 NaN
def test_nan_outputs(self):
assert_hypot_isnan(np.nan, np.nan)
assert_hypot_isnan(np.nan, 1)
# 测试当输入为特定组合包括 NaN 和无穷大时,hypot 函数的输出是否为无穷大
def test_nan_outputs2(self):
assert_hypot_isinf(np.nan, np.inf)
assert_hypot_isinf(np.inf, np.nan)
assert_hypot_isinf(np.inf, 0)
assert_hypot_isinf(0, np.inf)
assert_hypot_isinf(np.inf, np.inf)
assert_hypot_isinf(np.inf, 23.0)
# 测试当输入不引发浮点异常时,hypot 函数的输出是否正常,不会产生浮点异常
def test_no_fpe(self):
assert_no_warnings(ncu.hypot, np.inf, 0)
# 定义一个函数,用于检查通过断言 arctan2(x, y) 是否为 NaN
def assert_arctan2_isnan(x, y):
assert_(np.isnan(ncu.arctan2(x, y)), # 断言 arctan2(x, y) 的结果是否为 NaN
"arctan(%s, %s) is %s, not nan" % (x, y, ncu.arctan2(x, y))) # 如果断言失败,输出错误信息
# 定义一个函数,用于检查通过断言 arctan2(x, y) 是否为正无穷
def assert_arctan2_ispinf(x, y):
assert_((np.isinf(ncu.arctan2(x, y)) and ncu.arctan2(x, y) > 0), # 断言 arctan2(x, y) 的结果是否为正无穷
"arctan(%s, %s) is %s, not +inf" % (x, y, ncu.arctan2(x, y))) # 如果断言失败,输出错误信息
# 定义一个函数,用于检查通过断言 arctan2(x, y) 是否为负无穷
def assert_arctan2_isninf(x, y):
assert_((np.isinf(ncu.arctan2(x, y)) and ncu.arctan2(x, y) < 0), # 断言 arctan2(x, y) 的结果是否为负无穷
"arctan(%s, %s) is %s, not -inf" % (x, y, ncu.arctan2(x, y))) # 如果断言失败,输出错误信息
# 定义一个函数,用于检查通过断言 arctan2(x, y) 是否为正零
def assert_arctan2_ispzero(x, y):
assert_((ncu.arctan2(x, y) == 0 and not np.signbit(ncu.arctan2(x, y))), # 断言 arctan2(x, y) 的结果是否为正零
"arctan(%s, %s) is %s, not +0" % (x, y, ncu.arctan2(x, y))) # 如果断言失败,输出错误信息
# 定义一个函数,用于检查通过断言 arctan2(x, y) 是否为负零
def assert_arctan2_isnzero(x, y):
assert_((ncu.arctan2(x, y) == 0 and np.signbit(ncu.arctan2(x, y))), # 断言 arctan2(x, y) 的结果是否为负零
"arctan(%s, %s) is %s, not -0" % (x, y, ncu.arctan2(x, y))) # 如果断言失败,输出错误信息
# 定义一个测试类,用于测试 arctan2 函数对特殊值的处理
class TestArctan2SpecialValues:
# 测试 arctan2(1, 1) 的输出是否接近 pi/4
def test_one_one(self):
assert_almost_equal(ncu.arctan2(1, 1), 0.25 * np.pi)
assert_almost_equal(ncu.arctan2(-1, 1), -0.25 * np.pi)
assert_almost_equal(ncu.arctan2(1, -1), 0.75 * np.pi)
# 测试特定的零值组合输入时,arctan2 函数的输出是否符合预期
def test_zero_nzero(self):
assert_almost_equal(ncu.arctan2(ncu.PZERO, ncu.NZERO), np.pi)
assert_almost_equal(ncu.arctan2(ncu.NZERO, ncu.NZERO), -np.pi)
# 测试特定的零值组合输入时,arctan2 函数的输出是否符合预期
def test_zero_pzero(self):
assert_arctan2_ispzero(ncu.PZERO, ncu.PZERO)
assert_arctan2_isnzero(ncu.NZERO, ncu.PZERO)
# 测试当零值与负数组合输入时,arctan2 函数的输出是否符合预期
def test_zero_negative(self):
assert_almost_equal(ncu.arctan2(ncu.PZERO, -1), np.pi)
assert_almost_equal(ncu.arctan2(ncu.NZERO, -1), -np.pi)
# 测试当零值与正数组合输入时,arctan2 函数的输出是否符合预期
def test_zero_positive(self):
assert_arctan2_ispzero(ncu.PZERO, 1)
assert_arctan2_isnzero(ncu.NZERO, 1)
# 测试当正数与零值组合输入时,arctan2 函数的输出是否接近 +pi/2
def test_positive_zero(self):
assert_almost_equal(ncu.arctan2(1, ncu.PZERO), 0.5 * np.pi)
assert_almost_equal(ncu.arctan2(1, ncu.NZERO), 0.5 * np.pi)
def test_negative_zero(self):
# 测试 atan2(y, +-0),对于 y < 0 返回 -pi/2
assert_almost_equal(ncu.arctan2(-1, ncu.PZERO), -0.5 * np.pi)
assert_almost_equal(ncu.arctan2(-1, ncu.NZERO), -0.5 * np.pi)
def test_any_ninf(self):
# 测试 atan2(+-y, -infinity),对于有限的 y > 0 返回 +-pi
assert_almost_equal(ncu.arctan2(1, -np.inf), np.pi)
assert_almost_equal(ncu.arctan2(-1, -np.inf), -np.pi)
def test_any_pinf(self):
# 测试 atan2(+-y, +infinity),对于有限的 y > 0 返回 +-0
assert_arctan2_ispzero(1, np.inf)
assert_arctan2_isnzero(-1, np.inf)
def test_inf_any(self):
# 测试 atan2(+-infinity, x),对于有限的 x 返回 +-pi/2
assert_almost_equal(ncu.arctan2( np.inf, 1), 0.5 * np.pi)
assert_almost_equal(ncu.arctan2(-np.inf, 1), -0.5 * np.pi)
def test_inf_ninf(self):
# 测试 atan2(+-infinity, -infinity),返回 +-3*pi/4
assert_almost_equal(ncu.arctan2( np.inf, -np.inf), 0.75 * np.pi)
assert_almost_equal(ncu.arctan2(-np.inf, -np.inf), -0.75 * np.pi)
def test_inf_pinf(self):
# 测试 atan2(+-infinity, +infinity),返回 +-pi/4
assert_almost_equal(ncu.arctan2( np.inf, np.inf), 0.25 * np.pi)
assert_almost_equal(ncu.arctan2(-np.inf, np.inf), -0.25 * np.pi)
def test_nan_any(self):
# 测试 atan2(nan, x),对于任何 x(包括 inf)返回 nan
assert_arctan2_isnan(np.nan, np.inf)
assert_arctan2_isnan(np.inf, np.nan)
assert_arctan2_isnan(np.nan, np.nan)
class TestLdexp:
# 定义测试类 TestLdexp,用于测试 ldexp 函数
def _check_ldexp(self, tp):
# 定义辅助函数 _check_ldexp,用于检查 ldexp 函数在不同类型上的计算结果
assert_almost_equal(ncu.ldexp(np.array(2., np.float32),
np.array(3, tp)), 16.)
# 断言 ldexp 在 np.float32 类型上的计算结果接近于 16.0
assert_almost_equal(ncu.ldexp(np.array(2., np.float64),
np.array(3, tp)), 16.)
# 断言 ldexp 在 np.float64 类型上的计算结果接近于 16.0
assert_almost_equal(ncu.ldexp(np.array(2., np.longdouble),
np.array(3, tp)), 16.)
# 断言 ldexp 在 np.longdouble 类型上的计算结果接近于 16.0
def test_ldexp(self):
# 定义测试 ldexp 函数的方法 test_ldexp
# 默认的 Python int 类型应该正常工作
assert_almost_equal(ncu.ldexp(2., 3), 16.)
# 断言 ldexp 在输入参数为 2.0 和 3 的情况下的计算结果接近于 16.0
# 以下不同类型的整数应该都能被接受
self._check_ldexp(np.int8)
# 调用 _check_ldexp 函数,检查 np.int8 类型的 ldexp 计算结果
self._check_ldexp(np.int16)
# 调用 _check_ldexp 函数,检查 np.int16 类型的 ldexp 计算结果
self._check_ldexp(np.int32)
# 调用 _check_ldexp 函数,检查 np.int32 类型的 ldexp 计算结果
self._check_ldexp('i')
# 调用 _check_ldexp 函数,检查 'i' 类型的 ldexp 计算结果
self._check_ldexp('l')
# 调用 _check_ldexp 函数,检查 'l' 类型的 ldexp 计算结果
def test_ldexp_overflow(self):
# 定义测试 ldexp 函数在溢出情况下的方法 test_ldexp_overflow
# 在溢出时忽略警告信息
with np.errstate(over="ignore"):
imax = np.iinfo(np.dtype('l')).max
# 获取 np.long 类型的最大值
imin = np.iinfo(np.dtype('l')).min
# 获取 np.long 类型的最小值
# 断言 ldexp 在输入参数为 2.0 和 imax 时的计算结果为正无穷大
assert_equal(ncu.ldexp(2., imax), np.inf)
# 断言 ldexp 在输入参数为 2.0 和 imin 时的计算结果为 0
assert_equal(ncu.ldexp(2., imin), 0)
class TestMaximum(_FilterInvalids):
# 定义测试类 TestMaximum,继承自 _FilterInvalids 类
def test_reduce(self):
# 定义测试 reduce 方法
dflt = np.typecodes['AllFloat']
# 获取所有浮点数类型的类型码
dint = np.typecodes['AllInteger']
# 获取所有整数类型的类型码
seq1 = np.arange(11)
# 创建一个包含 0 到 10 的整数序列
seq2 = seq1[::-1]
# 将 seq1 反向排列得到 seq2
func = np.maximum.reduce
# 获取 np.maximum 函数的 reduce 方法
for dt in dint:
tmp1 = seq1.astype(dt)
# 将 seq1 转换为指定类型 dt 的数组 tmp1
tmp2 = seq2.astype(dt)
# 将 seq2 转换为指定类型 dt 的数组 tmp2
# 断言对 tmp1 使用 np.maximum.reduce 方法的结果为 10
assert_equal(func(tmp1), 10)
# 断言对 tmp2 使用 np.maximum.reduce 方法的结果为 10
for dt in dflt:
tmp1 = seq1.astype(dt)
# 将 seq1 转换为指定类型 dt 的数组 tmp1
tmp2 = seq2.astype(dt)
# 将 seq2 转换为指定类型 dt 的数组 tmp2
# 断言对 tmp1 使用 np.maximum.reduce 方法的结果为 10
assert_equal(func(tmp1), 10)
# 断言对 tmp2 使用 np.maximum.reduce 方法的结果为 10
tmp1[::2] = np.nan
# 将 tmp1 中偶数索引位置的值设置为 NaN
tmp2[::2] = np.nan
# 将 tmp2 中偶数索引位置的值设置为 NaN
# 断言对 tmp1 使用 np.maximum.reduce 方法的结果为 NaN
assert_equal(func(tmp1), np.nan)
# 断言对 tmp2 使用 np.maximum.reduce 方法的结果为 NaN
def test_reduce_complex(self):
# 定义测试复数类型的 reduce 方法
assert_equal(np.maximum.reduce([1, 2j]), 1)
# 断言对 [1, 2j] 使用 np.maximum.reduce 方法的结果为 1
assert_equal(np.maximum.reduce([1+3j, 2j]), 1+3j)
# 断言对 [1+3j, 2j] 使用 np.maximum.reduce 方法的结果为 1+3j
def test_float_nans(self):
# 定义测试包含 NaN 的浮点数数组的方法
nan = np.nan
# 定义 NaN 常量
arg1 = np.array([0, nan, nan])
# 创建包含 NaN 的数组 arg1
arg2 = np.array([nan, 0, nan])
# 创建包含 NaN 的数组 arg2
out = np.array([nan, nan, nan])
# 创建期望的输出数组 out
# 断言对 arg1 和 arg2 使用 np.maximum 方法的结果与 out 相等
assert_equal(np.maximum(arg1, arg2), out)
def test_object_nans(self):
# 定义测试包含 NaN 的对象数组的方法
# 多次检查以确保如果使用了比较而不是富比较,则可能失败
for i in range(1):
x = np.array(float('nan'), object)
# 创建包含 NaN 的对象数组 x
y = 1.0
# 定义一个浮点数 y
z = np.array(float('nan'), object)
# 创建包含 NaN 的对象数组 z
# 断言对 x 和 y 使用 np.maximum 方法的结果为 1.0
assert_(np.maximum(x, y) == 1.0)
# 断言对 z 和 y 使用 np.maximum 方法的结果为 1.0
def test_complex_nans(self):
# 定义测试包含复数 NaN 的方法
nan = np.nan
# 定义 NaN 常量
for cnan in [complex(nan, 0), complex(0, nan), complex(nan, nan)]:
arg1 = np.array([0, cnan, cnan], dtype=complex)
# 创建包含复数 NaN 的数组 arg1
arg2 = np.array([cnan, 0, cnan], dtype=complex)
# 创建包含复数 NaN 的数组 arg2
out = np.array([nan, nan, nan], dtype=complex)
# 创建期望的输出数组 out
# 断言对 arg1 和 arg2 使用 np.maximum 方法的结果与 out 相等
assert_equal(np.maximum(arg1, arg2), out)
def test_object_array(self):
# 创建一个包含对象类型的 NumPy 数组,从 0 到 4
arg1 = np.arange(5, dtype=object)
# 将 arg1 中每个元素加 1,形成新的数组 arg2
arg2 = arg1 + 1
# 断言 np.maximum 函数对 arg1 和 arg2 的操作结果等于 arg2
assert_equal(np.maximum(arg1, arg2), arg2)
def test_strided_array(self):
# 创建包含特殊浮点值的 NumPy 数组
arr1 = np.array([-4.0, 1.0, 10.0, 0.0, np.nan, -np.nan, np.inf, -np.inf])
arr2 = np.array([-2.0,-1.0, np.nan, 1.0, 0.0, np.nan, 1.0, -3.0])
maxtrue = np.array([-2.0, 1.0, np.nan, 1.0, np.nan, np.nan, np.inf, -3.0])
# 创建初始输出数组
out = np.ones(8)
# 验证 np.maximum 函数的多个断言
assert_equal(np.maximum(arr1,arr2), maxtrue)
assert_equal(np.maximum(arr1[::2],arr2[::2]), maxtrue[::2])
assert_equal(np.maximum(arr1[:4:], arr2[::2]), np.array([-2.0, np.nan, 10.0, 1.0]))
assert_equal(np.maximum(arr1[::3], arr2[:3:]), np.array([-2.0, 0.0, np.nan]))
assert_equal(np.maximum(arr1[:6:2], arr2[::3], out=out[::3]), np.array([-2.0, 10., np.nan]))
assert_equal(out, out_maxtrue)
def test_precision(self):
# 定义多种浮点类型
dtypes = [np.float16, np.float32, np.float64, np.longdouble]
for dt in dtypes:
# 获取当前数据类型的最小值和最大值
dtmin = np.finfo(dt).min
dtmax = np.finfo(dt).max
d1 = dt(0.1)
# 找到 d1 的下一个浮点数
d1_next = np.nextafter(d1, np.inf)
# 定义测试用例列表
test_cases = [
# v1 v2 expected
(dtmin, -np.inf, dtmin),
(dtmax, -np.inf, dtmax),
(d1, d1_next, d1_next),
(dtmax, np.nan, np.nan),
]
for v1, v2, expected in test_cases:
# 断言 np.maximum 函数对各个测试用例的操作结果
assert_equal(np.maximum([v1], [v2]), [expected])
assert_equal(np.maximum.reduce([v1, v2]), expected)
class TestMinimum(_FilterInvalids):
# 继承自_FilterInvalids类的TestMinimum测试类,用于测试np.minimum函数
def test_reduce(self):
# 定义test_reduce方法,用于测试np.minimum.reduce功能
dflt = np.typecodes['AllFloat']
# 获取所有浮点类型码
dint = np.typecodes['AllInteger']
# 获取所有整数类型码
seq1 = np.arange(11)
# 创建一个包含0到10的数组seq1
seq2 = seq1[::-1]
# 创建seq1的逆序数组seq2
func = np.minimum.reduce
# 将np.minimum.reduce函数赋给变量func,用于最小值的reduce操作
for dt in dint:
# 遍历所有整数类型码
tmp1 = seq1.astype(dt)
# 将seq1转换为当前整数类型dt的数组tmp1
tmp2 = seq2.astype(dt)
# 将seq2转换为当前整数类型dt的数组tmp2
assert_equal(func(tmp1), 0)
# 断言np.minimum.reduce(tmp1)的结果为0
assert_equal(func(tmp2), 0)
# 断言np.minimum.reduce(tmp2)的结果为0
for dt in dflt:
# 遍历所有浮点类型码
tmp1 = seq1.astype(dt)
# 将seq1转换为当前浮点类型dt的数组tmp1
tmp2 = seq2.astype(dt)
# 将seq2转换为当前浮点类型dt的数组tmp2
assert_equal(func(tmp1), 0)
# 断言np.minimum.reduce(tmp1)的结果为0
assert_equal(func(tmp2), 0)
# 断言np.minimum.reduce(tmp2)的结果为0
tmp1[::2] = np.nan
# 将tmp1数组的偶数索引位置设置为NaN
tmp2[::2] = np.nan
# 将tmp2数组的偶数索引位置设置为NaN
assert_equal(func(tmp1), np.nan)
# 断言np.minimum.reduce(tmp1)的结果为NaN
assert_equal(func(tmp2), np.nan)
# 断言np.minimum.reduce(tmp2)的结果为NaN
def test_reduce_complex(self):
# 定义test_reduce_complex方法,用于测试复数情况下的np.minimum.reduce功能
assert_equal(np.minimum.reduce([1, 2j]), 2j)
# 断言np.minimum.reduce([1, 2j])的结果为2j
assert_equal(np.minimum.reduce([1+3j, 2j]), 2j)
# 断言np.minimum.reduce([1+3j, 2j])的结果为2j
def test_float_nans(self):
# 定义test_float_nans方法,用于测试浮点数中NaN的情况
nan = np.nan
# 定义变量nan为NaN
arg1 = np.array([0, nan, nan])
# 创建包含0和两个NaN的数组arg1
arg2 = np.array([nan, 0, nan])
# 创建包含NaN、0和NaN的数组arg2
out = np.array([nan, nan, nan])
# 创建期望输出的数组out,全为NaN
assert_equal(np.minimum(arg1, arg2), out)
# 断言np.minimum(arg1, arg2)的结果与out相等
def test_object_nans(self):
# 定义test_object_nans方法,用于测试对象中NaN的情况
# 多次检查以确保使用富比较而不是cmp,否则可能失败
for i in range(1):
# 循环一次(实际上只执行一次)
x = np.array(float('nan'), object)
# 创建一个包含NaN的对象数组x
y = 1.0
# 创建一个包含1.0的变量y
z = np.array(float('nan'), object)
# 创建一个包含NaN的对象数组z
assert_(np.minimum(x, y) == 1.0)
# 断言np.minimum(x, y)的结果为1.0
assert_(np.minimum(z, y) == 1.0)
# 断言np.minimum(z, y)的结果为1.0
def test_complex_nans(self):
# 定义test_complex_nans方法,用于测试复数中NaN的情况
nan = np.nan
# 定义变量nan为NaN
for cnan in [complex(nan, 0), complex(0, nan), complex(nan, nan)]:
# 遍历包含不同NaN组合的列表
arg1 = np.array([0, cnan, cnan], dtype=complex)
# 创建一个包含0和复数NaN的数组arg1
arg2 = np.array([cnan, 0, cnan], dtype=complex)
# 创建一个包含复数NaN、0和复数NaN的数组arg2
out = np.array([nan, nan, nan], dtype=complex)
# 创建期望输出的复数数组out,全为NaN
assert_equal(np.minimum(arg1, arg2), out)
# 断言np.minimum(arg1, arg2)的结果与out相等
def test_object_array(self):
# 定义test_object_array方法,用于测试对象数组的情况
arg1 = np.arange(5, dtype=object)
# 创建一个包含0到4的对象数组arg1
arg2 = arg1 + 1
# 创建一个arg1每个元素加1的数组arg2
assert_equal(np.minimum(arg1, arg2), arg1)
# 断言np.minimum(arg1, arg2)的结果与arg1相等
def test_strided_array(self):
# 定义test_strided_array方法,用于测试跨步数组的情况
arr1 = np.array([-4.0, 1.0, 10.0, 0.0, np.nan, -np.nan, np.inf, -np.inf])
# 创建包含各种数值的数组arr1
arr2 = np.array([-2.0,-1.0, np.nan, 1.0, 0.0, np.nan, 1.0, -3.0])
# 创建包含各种数值的数组arr2
mintrue = np.array([-4.0, -1.0, np.nan, 0.0, np.nan, np.nan, 1.0, -np.inf])
# 创建期望最小值的数组mintrue
out = np.ones(8)
# 创建全为1的数组out,长度为8
out_mintrue = np.array([-4.0, 1.0, 1.0, 1.0, 1.0, 1.0, np.nan, 1.0])
# 创建期望输出的数组out_mintrue
assert_equal(np.minimum(arr1,arr2), mintrue)
# 断言np.minimum(arr1,arr2)的结果与mintrue相等
assert_equal(np.minimum(arr1[::2],arr2[::2]), mintrue[::2])
# 断言np.minimum(arr1[::2],arr2[::2])的结果与mintrue[::2]相等
assert_equal(np.minimum(arr1[:4:], arr2[::2]), np.array([-4.0, np.nan, 0.0, 0.0]))
# 断言np.minimum(arr1[:4:], arr2[::2])的结果与预期数组相等
assert_equal(np.minimum(arr1[::3], arr2[:3:]), np.array([-4.0, -1.0, np.nan]))
# 断言np.minimum(arr1[::3], arr2[:3:])的结果与预期数组相等
assert_equal(np.minimum(arr1[:6:2], arr2[::3], out=out[::3]), np.array([-4.0, 1.0, np.nan]))
# 断言np.minimum(arr1[:6:2], arr2[::3], out=out[::3])的结果与预期数组相等
assert_equal(out, out_mintrue)
# 断言out与out_mintrue相等
# 定义测试精度的方法
def test_precision(self):
# 定义浮点数类型列表
dtypes = [np.float16, np.float32, np.float64, np.longdouble]
# 遍历每种浮点数类型
for dt in dtypes:
# 获取当前浮点数类型的最小值和最大值
dtmin = np.finfo(dt).min
dtmax = np.finfo(dt).max
# 创建一个浮点数值为 0.1 的对象
d1 = dt(0.1)
# 找到比 d1 大但最接近的浮点数
d1_next = np.nextafter(d1, np.inf)
# 定义测试用例列表
test_cases = [
# 测试用例 (v1, v2, expected)
(dtmin, np.inf, dtmin), # 当 v1 是 dtmin, v2 是正无穷大时,预期结果是 dtmin
(dtmax, np.inf, dtmax), # 当 v1 是 dtmax, v2 是正无穷大时,预期结果是 dtmax
(d1, d1_next, d1), # 当 v1 是 d1, v2 是 d1_next 时,预期结果是 d1
(dtmin, np.nan, np.nan), # 当 v1 是 dtmin, v2 是 NaN 时,预期结果是 NaN
]
# 遍历每个测试用例
for v1, v2, expected in test_cases:
# 断言调用 np.minimum 函数,将 v1 和 v2 作为输入,预期输出是一个列表 [expected]
assert_equal(np.minimum([v1], [v2]), [expected])
# 断言调用 np.minimum.reduce 函数,将 v1 和 v2 作为输入,预期输出是 expected
assert_equal(np.minimum.reduce([v1, v2]), expected)
# 定义一个名为 TestFmax 的测试类,继承自 _FilterInvalids 类
class TestFmax(_FilterInvalids):
# 定义 test_reduce 方法,用于测试 np.fmax.reduce 函数
def test_reduce(self):
# 获取所有整数类型码
dint = np.typecodes['AllInteger']
# 创建一个长度为 11 的整数序列
seq1 = np.arange(11)
# 将 seq1 倒序排列得到 seq2
seq2 = seq1[::-1]
# func 设置为 np.fmax.reduce 函数
func = np.fmax.reduce
# 遍历所有整数类型码
for dt in dint:
# 将 seq1 和 seq2 转换为当前整数类型 dt
tmp1 = seq1.astype(dt)
tmp2 = seq2.astype(dt)
# 断言 np.fmax.reduce(tmp1) 的结果为 10
assert_equal(func(tmp1), 10)
# 断言 np.fmax.reduce(tmp2) 的结果为 10
assert_equal(func(tmp2), 10)
# 获取所有浮点数类型码
dflt = np.typecodes['AllFloat']
# 再次遍历所有浮点数类型码
for dt in dflt:
# 将 seq1 和 seq2 转换为当前浮点数类型 dt
tmp1 = seq1.astype(dt)
tmp2 = seq2.astype(dt)
# 断言 np.fmax.reduce(tmp1) 的结果为 10
assert_equal(func(tmp1), 10)
# 断言 np.fmax.reduce(tmp2) 的结果为 10
assert_equal(func(tmp2), 10)
# 将 tmp1 和 tmp2 中的偶数索引位置设置为 NaN
tmp1[::2] = np.nan
tmp2[::2] = np.nan
# 断言 np.fmax.reduce(tmp1) 的结果为 9
assert_equal(func(tmp1), 9)
# 断言 np.fmax.reduce(tmp2) 的结果为 9
assert_equal(func(tmp2), 9)
# 定义 test_reduce_complex 方法,测试复数数组的 np.fmax.reduce 函数
def test_reduce_complex(self):
# 断言 np.fmax.reduce([1, 2j]) 的结果为 1
assert_equal(np.fmax.reduce([1, 2j]), 1)
# 断言 np.fmax.reduce([1+3j, 2j]) 的结果为 1+3j
assert_equal(np.fmax.reduce([1+3j, 2j]), 1+3j)
# 定义 test_float_nans 方法,测试带 NaN 的浮点数数组的 np.fmax 函数
def test_float_nans(self):
# 定义 NaN
nan = np.nan
# 创建包含 NaN 的两个浮点数数组
arg1 = np.array([0, nan, nan])
arg2 = np.array([nan, 0, nan])
# 预期的输出数组
out = np.array([0, 0, nan])
# 断言 np.fmax(arg1, arg2) 的结果等于 out
assert_equal(np.fmax(arg1, arg2), out)
# 定义 test_complex_nans 方法,测试带 NaN 的复数数组的 np.fmax 函数
def test_complex_nans(self):
# 定义 NaN
nan = np.nan
# 遍历包含不同 NaN 复数的列表
for cnan in [complex(nan, 0), complex(0, nan), complex(nan, nan)]:
# 创建包含 NaN 的复数数组 arg1 和 arg2
arg1 = np.array([0, cnan, cnan], dtype=complex)
arg2 = np.array([cnan, 0, cnan], dtype=complex)
# 预期的输出数组
out = np.array([0, 0, nan], dtype=complex)
# 断言 np.fmax(arg1, arg2) 的结果等于 out
assert_equal(np.fmax(arg1, arg2), out)
# 定义 test_precision 方法,测试浮点数精度相关的 np.fmax 函数
def test_precision(self):
# 定义一组浮点数类型
dtypes = [np.float16, np.float32, np.float64, np.longdouble]
# 遍历每种浮点数类型
for dt in dtypes:
# 获取当前浮点数类型的最小值和最大值
dtmin = np.finfo(dt).min
dtmax = np.finfo(dt).max
# 创建一个浮点数值和其下一个可表示值
d1 = dt(0.1)
d1_next = np.nextafter(d1, np.inf)
# 测试用例列表
test_cases = [
# v1 v2 期望的结果
(dtmin, -np.inf, dtmin),
(dtmax, -np.inf, dtmax),
(d1, d1_next, d1_next),
(dtmax, np.nan, dtmax),
]
# 遍历每个测试用例
for v1, v2, expected in test_cases:
# 断言 np.fmax([v1], [v2]) 的结果等于 [expected]
assert_equal(np.fmax([v1], [v2]), [expected])
# 断言 np.fmax.reduce([v1, v2]) 的结果等于 expected
assert_equal(np.fmax.reduce([v1, v2]), expected)
# 定义一个名为 TestFmin 的测试类,继承自 _FilterInvalids 类
class TestFmin(_FilterInvalids):
# 定义 test_reduce 方法,用于测试 np.fmin.reduce 函数
def test_reduce(self):
# 获取所有整数类型码
dint = np.typecodes['AllInteger']
# 创建一个长度为 11 的整数序列
seq1 = np.arange(11)
# 将 seq1 倒序排列得到 seq2
seq2 = seq1[::-1]
# func 设置为 np.fmin.reduce 函数
func = np.fmin.reduce
# 遍历所有整数类型码
for dt in dint:
# 将 seq1 和 seq2 转换为当前整数类型 dt
tmp1 = seq1.astype(dt)
tmp2 = seq2.astype(dt)
# 断言 np.fmin.reduce(tmp1) 的结果为 0
assert_equal(func(tmp1), 0)
# 断言 np.fmin.reduce(tmp2) 的结果为 0
assert_equal(func(tmp2), 0)
# 获取所有浮点数类型码
dflt = np.typecodes['AllFloat']
# 再次遍历所有浮点数类型码
for dt in dflt:
# 将 seq1 和 seq2 转换为当前浮点数类型 dt
tmp1 = seq1.astype(dt)
tmp2 = seq2.astype(dt)
# 断言 np.fmin.reduce(tmp1) 的结果为 0
assert_equal(func(tmp1), 0)
# 断言 np.fmin.reduce(tmp2) 的结果为 0
assert_equal(func(tmp2), 0)
# 将 tmp1 和 tmp2 中的偶数索引位置设置为 NaN
tmp1[::2] = np.nan
tmp2[::2] = np.nan
# 断言 np.fmin.reduce(tmp1) 的结果为 1
assert_equal(func(tmp1), 1)
# 断言 np.fmin.reduce(tmp2) 的结果为 1
assert_equal(func(tmp2), 1)
# 定义一个测试函数,用于测试 np.fmin.reduce 函数的行为
def test_reduce_complex(self):
# 断言:对复数列表进行 np.fmin.reduce 操作,返回最小的复数
assert_equal(np.fmin.reduce([1, 2j]), 2j)
# 断言:对复数列表进行 np.fmin.reduce 操作,返回最小的复数
assert_equal(np.fmin.reduce([1+3j, 2j]), 2j)
# 定义一个测试函数,用于测试处理浮点数包含 NaN 的情况
def test_float_nans(self):
# 定义 NaN
nan = np.nan
# 创建包含 NaN 的数组
arg1 = np.array([0, nan, nan])
arg2 = np.array([nan, 0, nan])
out = np.array([0, 0, nan])
# 断言:对包含 NaN 的数组进行 np.fmin 操作,返回预期的输出
assert_equal(np.fmin(arg1, arg2), out)
# 定义一个测试函数,用于测试处理复数包含 NaN 的情况
def test_complex_nans(self):
# 定义 NaN
nan = np.nan
# 循环处理各种包含复数 NaN 的情况
for cnan in [complex(nan, 0), complex(0, nan), complex(nan, nan)]:
arg1 = np.array([0, cnan, cnan], dtype=complex)
arg2 = np.array([cnan, 0, cnan], dtype=complex)
out = np.array([0, 0, nan], dtype=complex)
# 断言:对包含复数 NaN 的数组进行 np.fmin 操作,返回预期的输出
assert_equal(np.fmin(arg1, arg2), out)
# 定义一个测试函数,用于测试 np.fmin 函数在不同精度下的行为
def test_precision(self):
# 定义各种浮点数类型
dtypes = [np.float16, np.float32, np.float64, np.longdouble]
# 遍历不同的浮点数类型
for dt in dtypes:
# 获取当前浮点数类型的最小值和最大值
dtmin = np.finfo(dt).min
dtmax = np.finfo(dt).max
d1 = dt(0.1)
d1_next = np.nextafter(d1, np.inf)
# 定义测试用例:(值1, 值2, 预期结果)
test_cases = [
(dtmin, np.inf, dtmin),
(dtmax, np.inf, dtmax),
(d1, d1_next, d1),
(dtmin, np.nan, dtmin),
]
# 遍历测试用例
for v1, v2, expected in test_cases:
# 断言:对包含两个值的数组进行 np.fmin 操作,返回预期的输出
assert_equal(np.fmin([v1], [v2]), [expected])
# 断言:对列表中的值进行 np.fmin.reduce 操作,返回预期的输出
assert_equal(np.fmin.reduce([v1, v2]), expected)
class TestBool:
def test_exceptions(self):
# 创建一个长度为1且元素为True的布尔型数组
a = np.ones(1, dtype=np.bool)
# 断言对np.negative应用于布尔数组会引发TypeError异常
assert_raises(TypeError, np.negative, a)
# 断言对np.positive应用于布尔数组会引发TypeError异常
assert_raises(TypeError, np.positive, a)
# 断言对np.subtract应用于布尔数组和自身会引发TypeError异常
assert_raises(TypeError, np.subtract, a, a)
def test_truth_table_logical(self):
# 2、3和4被视为True值
input1 = [0, 0, 3, 2]
input2 = [0, 4, 0, 2]
# 创建一个包含所有浮点和整数类型的typecodes,并加上布尔类型'?'
typecodes = (np.typecodes['AllFloat']
+ np.typecodes['AllInteger']
+ '?') # boolean
for dtype in map(np.dtype, typecodes):
# 将input1和input2转换为指定dtype的ndarray
arg1 = np.asarray(input1, dtype=dtype)
arg2 = np.asarray(input2, dtype=dtype)
# 对于逻辑或和最大值函数,验证其结果与预期输出相等
out = [False, True, True, True]
for func in (np.logical_or, np.maximum):
assert_equal(func(arg1, arg2).astype(bool), out)
# 对于逻辑与和最小值函数,验证其结果与预期输出相等
out = [False, False, False, True]
for func in (np.logical_and, np.minimum):
assert_equal(func(arg1, arg2).astype(bool), out)
# 对于逻辑异或和不等于函数,验证其结果与预期输出相等
out = [False, True, True, False]
for func in (np.logical_xor, np.not_equal):
assert_equal(func(arg1, arg2).astype(bool), out)
def test_truth_table_bitwise(self):
# 设置两个布尔数组作为操作数
arg1 = [False, False, True, True]
arg2 = [False, True, False, True]
# 预期的按位或运算结果
out = [False, True, True, True]
assert_equal(np.bitwise_or(arg1, arg2), out)
# 预期的按位与运算结果
out = [False, False, False, True]
assert_equal(np.bitwise_and(arg1, arg2), out)
# 预期的按位异或运算结果
out = [False, True, True, False]
assert_equal(np.bitwise_xor(arg1, arg2), out)
def test_reduce(self):
# 不包含True值的布尔数组
none = np.array([0, 0, 0, 0], bool)
# 包含部分True值的布尔数组
some = np.array([1, 0, 1, 1], bool)
# 全部是True值的布尔数组
every = np.array([1, 1, 1, 1], bool)
# 空的布尔数组
empty = np.array([], bool)
# 各种布尔数组的集合
arrs = [none, some, every, empty]
# 对每个布尔数组,验证逻辑与的reduce结果等于all函数的结果
for arr in arrs:
assert_equal(np.logical_and.reduce(arr), all(arr))
# 对每个布尔数组,验证逻辑或的reduce结果等于any函数的结果
for arr in arrs:
assert_equal(np.logical_or.reduce(arr), any(arr))
# 对每个布尔数组,验证逻辑异或的reduce结果等于对元素求和后模2等于1的结果
for arr in arrs:
assert_equal(np.logical_xor.reduce(arr), arr.sum() % 2 == 1)
class TestBitwiseUFuncs:
_all_ints_bits = [
np.dtype(c).itemsize * 8 for c in np.typecodes["AllInteger"]]
# 定义按位操作函数要处理的数据类型列表
bitwise_types = [
np.dtype(c) for c in '?' + np.typecodes["AllInteger"] + 'O']
# 每种按位操作函数的预期位数列表
bitwise_bits = [
2, # boolean type
*_all_ints_bits, # All integers
max(_all_ints_bits) + 1, # Object_ type
]
# 定义测试函数,用于验证不同数据类型的位运算函数的正确性
def test_values(self):
# 遍历每种位运算数据类型
for dt in self.bitwise_types:
# 创建一个包含单个零的数组,数据类型为当前迭代的数据类型
zeros = np.array([0], dtype=dt)
# 创建一个包含单个负一的数组,数据类型同样为当前迭代的数据类型
ones = np.array([-1]).astype(dt)
# 创建错误消息,用于测试失败时的输出
msg = "dt = '%s'" % dt.char
# 测试 np.bitwise_not 函数,验证其对零的取反是否等于负一
assert_equal(np.bitwise_not(zeros), ones, err_msg=msg)
# 测试 np.bitwise_not 函数,验证其对负一的取反是否等于零
assert_equal(np.bitwise_not(ones), zeros, err_msg=msg)
# 测试 np.bitwise_or 函数,验证其对两个零的按位或操作结果是否等于零
assert_equal(np.bitwise_or(zeros, zeros), zeros, err_msg=msg)
# 测试 np.bitwise_or 函数,验证其对零和负一的按位或操作结果是否等于负一
assert_equal(np.bitwise_or(zeros, ones), ones, err_msg=msg)
# 测试 np.bitwise_or 函数,验证其对负一和零的按位或操作结果是否等于负一
assert_equal(np.bitwise_or(ones, zeros), ones, err_msg=msg)
# 测试 np.bitwise_or 函数,验证其对两个负一的按位或操作结果是否等于负一
assert_equal(np.bitwise_or(ones, ones), ones, err_msg=msg)
# 测试 np.bitwise_xor 函数,验证其对两个零的按位异或操作结果是否等于零
assert_equal(np.bitwise_xor(zeros, zeros), zeros, err_msg=msg)
# 测试 np.bitwise_xor 函数,验证其对零和负一的按位异或操作结果是否等于负一
assert_equal(np.bitwise_xor(zeros, ones), ones, err_msg=msg)
# 测试 np.bitwise_xor 函数,验证其对负一和零的按位异或操作结果是否等于负一
assert_equal(np.bitwise_xor(ones, zeros), ones, err_msg=msg)
# 测试 np.bitwise_xor 函数,验证其对两个负一的按位异或操作结果是否等于零
assert_equal(np.bitwise_xor(ones, ones), zeros, err_msg=msg)
# 测试 np.bitwise_and 函数,验证其对两个零的按位与操作结果是否等于零
assert_equal(np.bitwise_and(zeros, zeros), zeros, err_msg=msg)
# 测试 np.bitwise_and 函数,验证其对零和负一的按位与操作结果是否等于零
assert_equal(np.bitwise_and(zeros, ones), zeros, err_msg=msg)
# 测试 np.bitwise_and 函数,验证其对负一和零的按位与操作结果是否等于零
assert_equal(np.bitwise_and(ones, zeros), zeros, err_msg=msg)
# 测试 np.bitwise_and 函数,验证其对两个负一的按位与操作结果是否等于负一
assert_equal(np.bitwise_and(ones, ones), ones, err_msg=msg)
# 定义测试函数,用于验证不同数据类型的位运算函数返回结果的数据类型是否正确
def test_types(self):
# 遍历每种位运算数据类型
for dt in self.bitwise_types:
# 创建一个包含单个零的数组,数据类型为当前迭代的数据类型
zeros = np.array([0], dtype=dt)
# 创建一个包含单个负一的数组,数据类型同样为当前迭代的数据类型
ones = np.array([-1]).astype(dt)
# 创建错误消息,用于测试失败时的输出
msg = "dt = '%s'" % dt.char
# 测试 np.bitwise_not 函数返回的结果的数据类型是否与当前数据类型相符
assert_(np.bitwise_not(zeros).dtype == dt, msg)
# 测试 np.bitwise_or 函数返回的结果的数据类型是否与当前数据类型相符
assert_(np.bitwise_or(zeros, zeros).dtype == dt, msg)
# 测试 np.bitwise_xor 函数返回的结果的数据类型是否与当前数据类型相符
assert_(np.bitwise_xor(zeros, zeros).dtype == dt, msg)
# 测试 np.bitwise_and 函数返回的结果的数据类型是否与当前数据类型相符
assert_(np.bitwise_and(zeros, zeros).dtype == dt, msg)
# 定义测试函数,用于验证位或、位异或和位与的身份元素是否正确
def test_identity(self):
# 验证 np.bitwise_or 函数的身份元素是否为零
assert_(np.bitwise_or.identity == 0, 'bitwise_or')
# 验证 np.bitwise_xor 函数的身份元素是否为零
assert_(np.bitwise_xor.identity == 0, 'bitwise_xor')
# 验证 np.bitwise_and 函数的身份元素是否为负一
assert_(np.bitwise_and.identity == -1, 'bitwise_and')
# 定义测试函数 test_reduction,用于测试位运算函数的归约操作
def test_reduction(self):
# 定义三个位运算函数:按位或、按位异或、按位与
binary_funcs = (np.bitwise_or, np.bitwise_xor, np.bitwise_and)
# 遍历位运算类型列表
for dt in self.bitwise_types:
# 创建一个包含单个零的数组,数据类型为 dt
zeros = np.array([0], dtype=dt)
# 创建一个包含单个 -1 的数组,转换为数据类型 dt
ones = np.array([-1]).astype(dt)
# 遍历位运算函数列表
for f in binary_funcs:
# 构造测试消息
msg = "dt: '%s', f: '%s'" % (dt, f)
# 断言归约函数应用在 zeros 上的结果等于 zeros,错误消息为 msg
assert_equal(f.reduce(zeros), zeros, err_msg=msg)
# 断言归约函数应用在 ones 上的结果等于 ones,错误消息为 msg
assert_equal(f.reduce(ones), ones, err_msg=msg)
# 测试空数组的归约操作,排除对象类型数组
for dt in self.bitwise_types[:-1]:
# 创建一个空数组,数据类型为 dt
empty = np.array([], dtype=dt)
# 遍历位运算函数列表
for f in binary_funcs:
# 构造测试消息
msg = "dt: '%s', f: '%s'" % (dt, f)
# 获取归约函数的单位元素,并转换为数据类型 dt
tgt = np.array(f.identity).astype(dt)
# 对空数组应用归约函数,比较结果和目标值 tgt,错误消息为 msg
res = f.reduce(empty)
assert_equal(res, tgt, err_msg=msg)
# 断言结果的数据类型与目标值的数据类型相同,错误消息为 msg
assert_(res.dtype == tgt.dtype, msg)
# 对象类型数组中的空数组使用单位元素进行归约操作
for f in binary_funcs:
# 构造测试消息
msg = "dt: '%s'" % (f,)
# 创建一个空的对象类型数组
empty = np.array([], dtype=object)
# 获取归约函数的单位元素
tgt = f.identity
# 对空数组应用归约函数,比较结果和目标值 tgt,错误消息为 msg
res = f.reduce(empty)
assert_equal(res, tgt, err_msg=msg)
# 非空对象类型数组不使用单位元素进行归约操作
for f in binary_funcs:
# 构造测试消息
msg = "dt: '%s'" % (f,)
# 创建一个包含 True 的对象类型数组
btype = np.array([True], dtype=object)
# 断言归约函数应用在 btype 上的结果的类型为布尔型,错误消息为 msg
assert_(type(f.reduce(btype)) is bool, msg)
# 使用 pytest 的参数化装饰器,定义测试函数 test_bitwise_count,用于测试位计数函数
@pytest.mark.parametrize("input_dtype_obj, bitsize",
zip(bitwise_types, bitwise_bits))
def test_bitwise_count(self, input_dtype_obj, bitsize):
# 获取输入数据类型的基本类型
input_dtype = input_dtype_obj.type
# 如果 Python 版本小于 3.10 并且数据类型为对象类型,则跳过测试
if sys.version_info < (3, 10) and input_dtype == np.object_:
pytest.skip("Required Python >=3.10")
# 对指定范围内的数进行位计数测试
for i in range(1, bitsize):
# 计算 2 的 i 次方减 1
num = 2**i - 1
# 构造测试消息
msg = f"bitwise_count for {num}"
# 断言位计数函数应用在 num 上的结果等于 i,错误消息为 msg
assert i == np.bitwise_count(input_dtype(num)), msg
# 如果数据类型是有符号整数或对象类型,则进一步断言应用在 -num 上的结果等于 i,错误消息为 msg
if np.issubdtype(input_dtype, np.signedinteger) or input_dtype == np.object_:
assert i == np.bitwise_count(input_dtype(-num)), msg
# 创建一个数组,包含从 1 到 bitsize-1 的整数,数据类型为 input_dtype
a = np.array([2**i-1 for i in range(1, bitsize)], dtype=input_dtype)
# 对数组应用位计数函数
bitwise_count_a = np.bitwise_count(a)
# 生成期望的结果数组,包含从 1 到 bitsize-1 的整数,数据类型为 input_dtype
expected = np.arange(1, bitsize, dtype=input_dtype)
# 构造测试消息
msg = f"array bitwise_count for {input_dtype}"
# 断言位计数函数应用在数组 a 上的结果与期望的结果数组相等,错误消息为 msg
assert all(bitwise_count_a == expected), msg
class TestInt:
# 测试逻辑非操作函数
def test_logical_not(self):
# 创建一个长度为10的numpy数组,元素类型为int16,值均为1
x = np.ones(10, dtype=np.int16)
# 创建一个长度为20的numpy数组,元素类型为bool,值均为True
o = np.ones(10 * 2, dtype=bool)
# 复制o数组,生成tgt数组
tgt = o.copy()
# 将tgt数组中偶数索引位置的元素改为False
tgt[::2] = False
# 从o数组中选取偶数索引位置的元素形成os数组
os = o[::2]
# 断言np.logical_not函数应用于x数组,结果应与False数组相等
assert_array_equal(np.logical_not(x, out=os), False)
# 断言o数组应与tgt数组相等
assert_array_equal(o, tgt)
class TestFloatingPoint:
# 测试浮点数支持情况
def test_floating_point(self):
# 断言浮点数支持常量的值为1
assert_equal(ncu.FLOATING_POINT_SUPPORT, 1)
class TestDegrees:
# 测试角度转换为度数
def test_degrees(self):
# 断言将π转换为度数后的值接近180.0
assert_almost_equal(ncu.degrees(np.pi), 180.0)
# 断言将-0.5π转换为度数后的值接近-90.0
assert_almost_equal(ncu.degrees(-0.5*np.pi), -90.0)
class TestRadians:
# 测试度数转换为弧度
def test_radians(self):
# 断言将180.0度转换为弧度后的值接近π
assert_almost_equal(ncu.radians(180.0), np.pi)
# 断言将-90.0度转换为弧度后的值接近-0.5π
assert_almost_equal(ncu.radians(-90.0), -0.5*np.pi)
class TestHeavside:
# 测试Heaviside函数
def test_heaviside(self):
# 创建包含不同类型浮点数的二维numpy数组x
x = np.array([[-30.0, -0.1, 0.0, 0.2], [7.5, np.nan, np.inf, -np.inf]])
# 预期的结果数组,使用0.5作为阈值
expectedhalf = np.array([[0.0, 0.0, 0.5, 1.0], [1.0, np.nan, 1.0, 0.0]])
# 复制expectedhalf数组生成expected1数组,并将其第一行第三列元素改为1
expected1 = expectedhalf.copy()
expected1[0, 2] = 1
# 计算x数组在阈值0.5下的Heaviside函数结果,并与expectedhalf数组进行断言
h = ncu.heaviside(x, 0.5)
assert_equal(h, expectedhalf)
# 计算x数组在阈值1.0下的Heaviside函数结果,并与expected1数组进行断言
h = ncu.heaviside(x, 1.0)
assert_equal(h, expected1)
# 将x数组转换为float32类型后再次计算Heaviside函数结果,并与相应的float32类型的expectedhalf数组进行断言
x = x.astype(np.float32)
h = ncu.heaviside(x, np.float32(0.5))
assert_equal(h, expectedhalf.astype(np.float32))
h = ncu.heaviside(x, np.float32(1.0))
assert_equal(h, expected1.astype(np.float32))
class TestSign:
# 测试符号函数
def test_sign(self):
# 创建包含不同类型元素的numpy数组a
a = np.array([np.inf, -np.inf, np.nan, 0.0, 3.0, -3.0])
# 创建与a相同形状的零数组out
out = np.zeros(a.shape)
# 预期的结果数组tgt
tgt = np.array([1., -1., np.nan, 0.0, 1.0, -1.0])
# 使用np.errstate(invalid='ignore')上下文管理器处理无效值异常
with np.errstate(invalid='ignore'):
# 计算a数组的符号函数结果并与tgt数组进行断言
res = ncu.sign(a)
assert_equal(res, tgt)
# 使用out数组计算a数组的符号函数结果并与tgt数组进行断言
res = ncu.sign(a, out)
assert_equal(res, tgt)
# 断言out数组与tgt数组相等
assert_equal(out, tgt)
# 测试复数类型输入的符号函数
def test_sign_complex(self):
# 创建包含复数元素的numpy数组a
a = np.array([
np.inf, -np.inf, complex(0, np.inf), complex(0, -np.inf),
complex(np.inf, np.inf), complex(np.inf, -np.inf), # nan
np.nan, complex(0, np.nan), complex(np.nan, np.nan), # nan
0.0, # 0.
3.0, -3.0, -2j, 3.0+4.0j, -8.0+6.0j
])
# 创建与a相同形状和数据类型的零数组out
out = np.zeros(a.shape, a.dtype)
# 预期的结果数组tgt
tgt = np.array([
1., -1., 1j, -1j,
] + [complex(np.nan, np.nan)] * 5 + [
0.0,
1.0, -1.0, -1j, 0.6+0.8j, -0.8+0.6j])
# 使用np.errstate(invalid='ignore')上下文管理器处理无效值异常
with np.errstate(invalid='ignore'):
# 计算a数组的符号函数结果并与tgt数组进行断言
res = ncu.sign(a)
assert_equal(res, tgt)
# 使用out数组计算a数组的符号函数结果,并断言res与out是同一个对象
res = ncu.sign(a, out)
assert_(res is out)
# 断言out数组与tgt数组相等
assert_equal(res, tgt)
# 测试对象类型输入的符号函数
def test_sign_dtype_object(self):
# 参考github问题#6229
# 创建包含浮点数元素的numpy数组foo
foo = np.array([-.1, 0, .1])
# 使用object类型计算foo数组的符号函数结果,并赋值给数组a
a = np.sign(foo.astype(object))
# 使用默认类型计算foo数组的符号函数结果,并赋值给数组b
b = np.sign(foo)
# 断言a数组与b数组相等
assert_array_equal(a, b)
def test_sign_dtype_nan_object(self):
# 在参考 GitHub 问题 #6229 的情况下定义了一个测试函数 test_nan
def test_nan():
# 创建一个包含 NaN 值的 NumPy 数组
foo = np.array([np.nan])
# FIXME: a not used
# 对数组进行类型转换为 object 后,计算其符号
a = np.sign(foo.astype(object))
# 断言捕获 TypeError 异常,确保 test_nan 函数抛出异常
assert_raises(TypeError, test_nan)
class TestMinMax:
def test_minmax_blocked(self):
# SIMD tests on max/min, testing all alignments, although slow, it's crucial
# Calculation for loop iterations: 2 * vz + 2 * (vs - 1) + 1 (unrolled once)
for dt, sz in [(np.float32, 15), (np.float64, 7)]:
# Generate alignment data for unary operations with specified dtype and size
for out, inp, msg in _gen_alignment_data(dtype=dt, type='unary', max_size=sz):
# Iterate over elements in input array
for i in range(inp.size):
# Populate inp with array indices of dtype
inp[:] = np.arange(inp.size, dtype=dt)
# Set inp[i] to NaN
inp[i] = np.nan
# Custom error message function
emsg = lambda: '%r\n%s' % (inp, msg)
# Suppress warnings related to invalid values in reduce operations
with suppress_warnings() as sup:
sup.filter(RuntimeWarning, "invalid value encountered in reduce")
# Assert that maximum value in inp is NaN
assert_(np.isnan(inp.max()), msg=emsg)
# Assert that minimum value in inp is NaN
assert_(np.isnan(inp.min()), msg=emsg)
# Set inp[i] to 1e10 and assert its maximum value
inp[i] = 1e10
assert_equal(inp.max(), 1e10, err_msg=msg)
# Set inp[i] to -1e10 and assert its minimum value
inp[i] = -1e10
assert_equal(inp.min(), -1e10, err_msg=msg)
def test_lower_align(self):
# Check data that is not aligned to element size, e.g., doubles on i386
d = np.zeros(23 * 8, dtype=np.int8)[4:-4].view(np.float64)
# Assert that maximum value in d equals its first element
assert_equal(d.max(), d[0])
# Assert that minimum value in d equals its first element
assert_equal(d.min(), d[0])
def test_reduce_reorder(self):
# GitHub issues 10370, 11029: Some compilers may reorder npy_getfloatstatus
# causing invalid status to be set before intrinsic function calls.
# Also ensure no warnings are emitted during operations.
for n in (2, 4, 8, 16, 32):
for dt in (np.float32, np.float16, np.complex64):
# Create diagonal matrix with NaN values of specified dtype
for r in np.diagflat(np.array([np.nan] * n, dtype=dt)):
# Assert that minimum value in r is NaN
assert_equal(np.min(r), np.nan)
def test_minimize_no_warns(self):
# Calculate minimum of NaN and 1 without generating warnings
a = np.minimum(np.nan, 1)
# Assert that result equals NaN
assert_equal(a, np.nan)
class TestAbsoluteNegative:
def test_abs_neg_blocked(self):
# 进行 SIMD 测试,测试绝对值和取负操作在不同对齐方式下的表现
for dt, sz in [(np.float32, 11), (np.float64, 5)]:
# 生成指定数据类型和最大大小的数据集,用于一元操作的测试
for out, inp, msg in _gen_alignment_data(dtype=dt, type='unary',
max_size=sz):
# 计算目标结果,使用 ncu 模块计算每个输入元素的绝对值
tgt = [ncu.absolute(i) for i in inp]
# 使用 NumPy 计算每个输入元素的绝对值,将结果存储在输出数组中
np.absolute(inp, out=out)
# 断言输出数组与目标结果数组相等,用于验证测试通过
assert_equal(out, tgt, err_msg=msg)
# 断言输出数组中所有元素均为非负数
assert_((out >= 0).all())
# 计算目标结果,使用 ncu 模块计算每个输入元素的相反数
tgt = [-1*(i) for i in inp]
# 使用 NumPy 计算每个输入元素的相反数,将结果存储在输出数组中
np.negative(inp, out=out)
# 断言输出数组与目标结果数组相等,用于验证测试通过
assert_equal(out, tgt, err_msg=msg)
# 对特定值(NaN、-Inf、Inf)进行测试,验证对应情况下的数组操作
for v in [np.nan, -np.inf, np.inf]:
for i in range(inp.size):
d = np.arange(inp.size, dtype=dt)
inp[:] = -d
inp[i] = v
d[i] = -v if v == -np.inf else v
# 使用 NumPy 计算每个输入数组的绝对值,与预期结果进行比较
assert_array_equal(np.abs(inp), d, err_msg=msg)
np.abs(inp, out=out)
assert_array_equal(out, d, err_msg=msg)
# 断言取反操作后数组元素与预期相符
assert_array_equal(-inp, -1*inp, err_msg=msg)
d = -1 * inp
np.negative(inp, out=out)
# 断言输出数组与预期结果相等,用于验证测试通过
assert_array_equal(out, d, err_msg=msg)
def test_lower_align(self):
# 检查未按元素大小对齐的数据情况
# 即在 i386 上,double 类型数据按 4 字节对齐
d = np.zeros(23 * 8, dtype=np.int8)[4:-4].view(np.float64)
# 断言对数组取绝对值后结果与原数组相等
assert_equal(np.abs(d), d)
# 断言对数组取负后结果与负原数组相等
assert_equal(np.negative(d), -d)
# 在原数组上执行取负操作,并将结果存储在原数组中
np.negative(d, out=d)
# 在原数组上执行取负操作,将结果存储在原数组中
np.negative(np.ones_like(d), out=d)
# 在原数组上执行取绝对值操作,并将结果存储在原数组中
np.abs(d, out=d)
# 在原数组上执行取绝对值操作,将结果存储在原数组中
np.abs(np.ones_like(d), out=d)
@pytest.mark.parametrize("dtype", ['d', 'f', 'int32', 'int64'])
@pytest.mark.parametrize("big", [True, False])
# 定义测试方法,用于测试非连续数组的操作
def test_noncontiguous(self, dtype, big):
# 创建包含特定数据的 NumPy 数组,指定数据类型
data = np.array([-1.0, 1.0, -0.0, 0.0, 2.2251e-308, -2.5, 2.5, -6,
6, -2.2251e-308, -8, 10], dtype=dtype)
# 创建期望的结果数组,与 data 数组的内容相反
expect = np.array([1.0, -1.0, 0.0, -0.0, -2.2251e-308, 2.5, -2.5, 6,
-6, 2.2251e-308, 8, -10], dtype=dtype)
# 如果 big 为 True,则对数据和期望结果数组进行重复操作
if big:
data = np.repeat(data, 10)
expect = np.repeat(expect, 10)
# 创建与 data 相同形状的空数组 out
out = np.ndarray(data.shape, dtype=dtype)
# 从 data 中选取非连续元素,作为输入的非连续数组 ncontig_in
ncontig_in = data[1::2]
# 从 out 中选取非连续元素,作为输出的非连续数组 ncontig_out
ncontig_out = out[1::2]
# 将 ncontig_in 转换为 NumPy 数组,作为连续数组 contig_in
contig_in = np.array(ncontig_in)
# 断言:对 contig_in 中的元素取负,与期望的非连续数组元素 expect[1::2] 相等
assert_array_equal(np.negative(contig_in), expect[1::2])
# 断言:对 contig_in 中的元素取负,输出到 ncontig_out 中,与期望的非连续数组元素 expect[1::2] 相等
assert_array_equal(np.negative(contig_in, out=ncontig_out),
expect[1::2])
# 断言:对 ncontig_in 中的元素取负,与期望的非连续数组元素 expect[1::2] 相等
assert_array_equal(np.negative(ncontig_in), expect[1::2])
# 断言:对 ncontig_in 中的元素取负,输出到 ncontig_out 中,与期望的非连续数组元素 expect[1::2] 相等
assert_array_equal(np.negative(ncontig_in, out=ncontig_out),
expect[1::2])
# 断言:对 data 分割成两部分后,分别取负,与期望的分割后的结果 expect_split 相等
data_split = np.array(np.array_split(data, 2))
expect_split = np.array(np.array_split(expect, 2))
assert_equal(np.negative(data_split), expect_split)
# 定义一个测试类 TestPositive,用于测试 np.positive 函数的行为
class TestPositive:
# 定义一个测试方法 test_valid,用于测试 np.positive 对于有效数据类型的行为
def test_valid(self):
# 定义有效的数据类型列表
valid_dtypes = [int, float, complex, object]
# 遍历每种数据类型
for dtype in valid_dtypes:
# 创建一个长度为 5 的数组,指定数据类型为当前遍历的数据类型 dtype
x = np.arange(5, dtype=dtype)
# 对数组 x 应用 np.positive 函数
result = np.positive(x)
# 使用 assert_equal 断言 x 和 result 相等,如果不等则输出错误信息,并附带数据类型的字符串表示
assert_equal(x, result, err_msg=str(dtype))
# 定义一个测试方法 test_invalid,用于测试 np.positive 对于无效数据类型抛出 TypeError 的行为
def test_invalid(self):
# 使用 assert_raises 断言调用 np.positive(True) 会抛出 TypeError 异常
with assert_raises(TypeError):
np.positive(True)
# 使用 assert_raises 断言调用 np.positive(np.datetime64('2000-01-01')) 会抛出 TypeError 异常
with assert_raises(TypeError):
np.positive(np.datetime64('2000-01-01'))
# 使用 assert_raises 断言调用 np.positive(np.array(['foo'], dtype=str)) 会抛出 TypeError 异常
with assert_raises(TypeError):
np.positive(np.array(['foo'], dtype=str))
# 使用 assert_raises 断言调用 np.positive(np.array(['bar'], dtype=object)) 会抛出 TypeError 异常
with assert_raises(TypeError):
np.positive(np.array(['bar'], dtype=object))
# 定义一个测试类 TestSpecialMethods,用于测试自定义类实现特殊方法的行为
class TestSpecialMethods:
# 定义一个测试方法 test_wrap,用于测试自定义类实现 __array__ 和 __array_wrap__ 方法的包装行为
def test_wrap(self):
# 定义一个内嵌类 with_wrap,实现 __array__ 和 __array_wrap__ 方法
class with_wrap:
# 定义 __array__ 方法,返回一个包含一个零的 numpy 数组
def __array__(self, dtype=None, copy=None):
return np.zeros(1)
# 定义 __array_wrap__ 方法,接受一个数组 arr、上下文 context 和返回标量 return_scalar,返回一个新的 with_wrap 对象
def __array_wrap__(self, arr, context, return_scalar):
r = with_wrap()
r.arr = arr # 将 arr 存储在新对象的 arr 属性中
r.context = context # 将 context 存储在新对象的 context 属性中
return r
# 创建一个 with_wrap 的实例 a
a = with_wrap()
# 调用 ncu.minimum 函数,传入 a 和 a 作为参数,并将结果存储在变量 x 中
x = ncu.minimum(a, a)
# 使用 assert_equal 断言 x.arr 等于一个包含一个零的 numpy 数组
assert_equal(x.arr, np.zeros(1))
# 解构 x.context,并将结果存储在 func、args、i 变量中
func, args, i = x.context
# 使用 assert_ 断言 func 是 ncu.minimum 函数
assert_(func is ncu.minimum)
# 使用 assert_equal 断言 args 的长度为 2
assert_equal(len(args), 2)
# 使用 assert_equal 断言 args[0] 等于 a
assert_equal(args[0], a)
# 使用 assert_equal 断言 args[1] 等于 a
assert_equal(args[1], a)
# 使用 assert_equal 断言 i 等于 0
assert_equal(i, 0)
def test_wrap_out(self):
# out 参数的调用约定不应影响特殊方法的调用方式
class StoreArrayPrepareWrap(np.ndarray):
_wrap_args = None # 初始化 _wrap_args 为 None
_prepare_args = None # 初始化 _prepare_args 为 None
def __new__(cls):
# 创建一个全零的 numpy 数组,并以当前类的视图返回
return np.zeros(()).view(cls)
def __array_wrap__(self, obj, context, return_scalar):
# 在数组对象被返回之前,记录下 context 中的第二个参数到 _wrap_args
self._wrap_args = context[1]
return obj # 返回对象本身
@property
def args(self):
# 确保在任何其他 ufunc 被调用之前获取这些参数
return self._wrap_args
def __repr__(self):
return "a" # 用于简短的测试输出
def do_test(f_call, f_expected):
a = StoreArrayPrepareWrap() # 创建 StoreArrayPrepareWrap 的实例
f_call(a) # 调用传入的函数 f_call
w = a.args # 获取存储的参数
expected = f_expected(a) # 获取预期的结果
try:
assert w == expected # 断言参数与预期结果相等
except AssertionError as e:
# assert_equal 生成的错误信息非常无用
raise AssertionError("\n".join([
"Bad arguments passed in ufunc call",
" expected: {}".format(expected),
" __array_wrap__ got: {}".format(w)
]))
# 不涉及 out 参数的方法调用
do_test(lambda a: np.add(a, 0), lambda a: (a, 0))
do_test(lambda a: np.add(a, 0, None), lambda a: (a, 0))
do_test(lambda a: np.add(a, 0, out=None), lambda a: (a, 0))
do_test(lambda a: np.add(a, 0, out=(None,)), lambda a: (a, 0))
# 涉及 out 参数的方法调用
do_test(lambda a: np.add(0, 0, a), lambda a: (0, 0, a))
do_test(lambda a: np.add(0, 0, out=a), lambda a: (0, 0, a))
do_test(lambda a: np.add(0, 0, out=(a,)), lambda a: (0, 0, a))
# 同时检查 where mask 处理:
do_test(lambda a: np.add(a, 0, where=False), lambda a: (a, 0))
do_test(lambda a: np.add(0, 0, a, where=False), lambda a: (0, 0, a))
def test_wrap_with_iterable(self):
# 测试修复 Bug #1026:
class with_wrap(np.ndarray):
__array_priority__ = 10 # 设置数组优先级为 10
def __new__(cls):
# 创建一个视图为 cls 类型的数组,初始为 np.asarray(1) 的拷贝
return np.asarray(1).view(cls).copy()
def __array_wrap__(self, arr, context, return_scalar):
# 在数组被返回前,将其视图转换为与当前类相同的类型
return arr.view(type(self))
a = with_wrap() # 创建 with_wrap 的实例
x = ncu.multiply(a, (1, 2, 3)) # 使用 ncu 对 a 与 (1, 2, 3) 进行乘法操作
assert_(isinstance(x, with_wrap)) # 断言 x 是 with_wrap 的实例
assert_array_equal(x, np.array((1, 2, 3))) # 断言 x 与 np.array((1, 2, 3)) 相等
def test_priority_with_scalar(self):
# 测试修复 Bug #826:
class A(np.ndarray):
__array_priority__ = 10 # 设置数组优先级为 10
def __new__(cls):
# 创建一个 float64 类型的数组,初始为 np.asarray(1.0, 'float64') 的拷贝
return np.asarray(1.0, 'float64').view(cls).copy()
a = A() # 创建 A 的实例
x = np.float64(1)*a # 使用 np.float64(1) 乘以 a
assert_(isinstance(x, A)) # 断言 x 是 A 的实例
assert_array_equal(x, np.array(1)) # 断言 x 与 np.array(1) 相等
def test_priority(self):
# 定义测试方法 test_priority,用于测试优先级相关功能
class A:
def __array__(self, dtype=None, copy=None):
# 定义 __array__ 方法,返回一个包含一个元素的零数组
return np.zeros(1)
def __array_wrap__(self, arr, context, return_scalar):
# 定义 __array_wrap__ 方法,返回一个新的类型为 self 的实例,包含传入的数组和上下文信息
r = type(self)()
r.arr = arr
r.context = context
return r
class B(A):
__array_priority__ = 20. # 设置数组优先级为 20.0
class C(A):
__array_priority__ = 40. # 设置数组优先级为 40.0
x = np.zeros(1)
a = A()
b = B()
c = C()
f = ncu.minimum
assert_(type(f(x, x)) is np.ndarray) # 断言调用 ncu.minimum 返回的是 np.ndarray 类型
assert_(type(f(x, a)) is A) # 断言调用 ncu.minimum 返回的是 A 类型
assert_(type(f(x, b)) is B) # 断言调用 ncu.minimum 返回的是 B 类型
assert_(type(f(x, c)) is C) # 断言调用 ncu.minimum 返回的是 C 类型
assert_(type(f(a, x)) is A) # 断言调用 ncu.minimum 返回的是 A 类型
assert_(type(f(b, x)) is B) # 断言调用 ncu.minimum 返回的是 B 类型
assert_(type(f(c, x)) is C) # 断言调用 ncu.minimum 返回的是 C 类型
assert_(type(f(a, a)) is A) # 断言调用 ncu.minimum 返回的是 A 类型
assert_(type(f(a, b)) is B) # 断言调用 ncu.minimum 返回的是 B 类型
assert_(type(f(b, a)) is B) # 断言调用 ncu.minimum 返回的是 B 类型
assert_(type(f(b, b)) is B) # 断言调用 ncu.minimum 返回的是 B 类型
assert_(type(f(b, c)) is C) # 断言调用 ncu.minimum 返回的是 C 类型
assert_(type(f(c, b)) is C) # 断言调用 ncu.minimum 返回的是 C 类型
assert_(type(f(c, c)) is C) # 断言调用 ncu.minimum 返回的是 C 类型
assert_(type(ncu.exp(a)) is A) # 断言调用 ncu.exp 返回的是 A 类型
assert_(type(ncu.exp(b)) is B) # 断言调用 ncu.exp 返回的是 B 类型
assert_(type(ncu.exp(c)) is C) # 断言调用 ncu.exp 返回的是 C 类型
def test_failing_wrap(self):
# 定义测试方法 test_failing_wrap,用于测试异常情况下的包装器
class A:
def __array__(self, dtype=None, copy=None):
# 定义 __array__ 方法,返回一个包含两个元素的零数组
return np.zeros(2)
def __array_wrap__(self, arr, context, return_scalar):
# 定义 __array_wrap__ 方法,抛出 RuntimeError 异常
raise RuntimeError
a = A()
assert_raises(RuntimeError, ncu.maximum, a, a) # 断言调用 ncu.maximum 抛出 RuntimeError 异常
assert_raises(RuntimeError, ncu.maximum.reduce, a) # 断言调用 ncu.maximum.reduce 抛出 RuntimeError 异常
def test_failing_out_wrap(self):
# 定义测试方法 test_failing_out_wrap,用于测试异常情况下的输出包装器
singleton = np.array([1.0])
class Ok(np.ndarray):
def __array_wrap__(self, obj, context, return_scalar):
# 定义 __array_wrap__ 方法,返回 singleton
return singleton
class Bad(np.ndarray):
def __array_wrap__(self, obj, context, return_scalar):
# 定义 __array_wrap__ 方法,抛出 RuntimeError 异常
raise RuntimeError
ok = np.empty(1).view(Ok)
bad = np.empty(1).view(Bad)
# 断言调用 ncu.frexp 函数时,若 bad 引发异常,则导致 "ok" 双重释放(段错误)
for i in range(10):
assert_raises(RuntimeError, ncu.frexp, 1, ok, bad)
def test_none_wrap(self):
# 定义测试方法 test_none_wrap,测试解决问题 #8507。先前,这会导致段错误。
class A:
def __array__(self, dtype=None, copy=None):
# 定义 __array__ 方法,返回一个包含一个元素的零数组
return np.zeros(1)
def __array_wrap__(self, arr, context=None, return_scalar=False):
# 定义 __array_wrap__ 方法,返回 None
return None
a = A()
assert_equal(ncu.maximum(a, a), None) # 断言调用 ncu.maximum 返回 None
def test_default_prepare(self):
# 定义测试方法 test_default_prepare,测试默认准备功能
class with_wrap:
__array_priority__ = 10 # 设置数组优先级为 10
def __array__(self, dtype=None, copy=None):
# 定义 __array__ 方法,返回一个包含一个元素的零数组
return np.zeros(1)
def __array_wrap__(self, arr, context, return_scalar):
# 定义 __array_wrap__ 方法,返回传入的数组 arr
return arr
a = with_wrap()
x = ncu.minimum(a, a)
assert_equal(x, np.zeros(1)) # 断言 x 等于包含一个元素的零数组
assert_equal(type(x), np.ndarray) # 断言 x 的类型是 np.ndarray
def test_ufunc_override(self):
# 定义一个测试函数,用于验证重载 __array_ufunc__ 方法的效果。
# 定义一个类 A,该类重载了 __array_ufunc__ 方法
class A:
def __array_ufunc__(self, func, method, *inputs, **kwargs):
return self, func, method, inputs, kwargs
# 定义一个类 MyNDArray,继承自 numpy.ndarray,设置了较高的 __array_priority__ 值为 100
class MyNDArray(np.ndarray):
__array_priority__ = 100
# 创建一个类 A 的实例
a = A()
# 创建一个 numpy 数组 b,并将其视图转换为 MyNDArray 类型
b = np.array([1]).view(MyNDArray)
# 使用 np.multiply 函数进行数组元素相乘操作,分别赋值给 res0 和 res1
res0 = np.multiply(a, b)
res1 = np.multiply(b, b, out=a)
# 断言语句,验证 res0[0] 和 res1[0] 均为对象 a
assert_equal(res0[0], a)
assert_equal(res1[0], a)
# 断言语句,验证 res0[1] 和 res1[1] 均为 np.multiply 函数
assert_equal(res0[1], np.multiply)
assert_equal(res1[1], np.multiply)
# 断言语句,验证 res0[2] 和 res1[2] 均为 '__call__'
assert_equal(res0[2], '__call__')
assert_equal(res1[2], '__call__')
# 断言语句,验证 res0[3] 和 res1[3] 均为元组 (a, b) 和 (b, b)
assert_equal(res0[3], (a, b))
assert_equal(res1[3], (b, b))
# 断言语句,验证 res0[4] 和 res1[4] 均为空字典和 {'out': (a,)} 字典
assert_equal(res0[4], {})
assert_equal(res1[4], {'out': (a,)})
# 定义一个测试函数 test_ufunc_override_out
def test_ufunc_override_out(self):
# 定义类 A,实现 __array_ufunc__ 方法,返回关键字参数 kwargs
class A:
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
return kwargs
# 定义类 B,实现 __array_ufunc__ 方法,返回关键字参数 kwargs
class B:
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
return kwargs
# 创建 A 类的实例 a 和 B 类的实例 b
a = A()
b = B()
# 使用 np.multiply 进行乘法运算,测试不同形式的输出参数 'out_arg'
res0 = np.multiply(a, b, 'out_arg')
res1 = np.multiply(a, b, out='out_arg')
res2 = np.multiply(2, b, 'out_arg')
res3 = np.multiply(3, b, out='out_arg')
res4 = np.multiply(a, 4, 'out_arg')
res5 = np.multiply(a, 5, out='out_arg')
# 断言检查每个乘法运算结果的 'out' 键是否为 'out_arg'
assert_equal(res0['out'][0], 'out_arg')
assert_equal(res1['out'][0], 'out_arg')
assert_equal(res2['out'][0], 'out_arg')
assert_equal(res3['out'][0], 'out_arg')
assert_equal(res4['out'][0], 'out_arg')
assert_equal(res5['out'][0], 'out_arg')
# 使用 np.modf 和 np.frexp 函数,测试多输出模式 'out0' 和 'out1'
res6 = np.modf(a, 'out0', 'out1')
res7 = np.frexp(a, 'out0', 'out1')
# 断言检查每个函数结果的 'out' 键对应的值是否符合预期 'out0' 和 'out1'
assert_equal(res6['out'][0], 'out0')
assert_equal(res6['out'][1], 'out1')
assert_equal(res7['out'][0], 'out0')
assert_equal(res7['out'][1], 'out1')
# 检查默认输出参数为 None 时的返回值
assert_(np.sin(a, None) == {})
assert_(np.sin(a, out=None) == {})
assert_(np.sin(a, out=(None,)) == {})
assert_(np.modf(a, None) == {})
assert_(np.modf(a, None, None) == {})
assert_(np.modf(a, out=(None, None)) == {})
# 使用 assert_raises 检查 TypeError 异常,确保 out 参数为元组形式
with assert_raises(TypeError):
np.modf(a, out=None)
# 使用 assert_raises 检查 TypeError 和 ValueError 异常,确保参数组合正确性
assert_raises(TypeError, np.multiply, a, b, 'one', out='two')
assert_raises(TypeError, np.multiply, a, b, 'one', 'two')
assert_raises(ValueError, np.multiply, a, b, out=('one', 'two'))
assert_raises(TypeError, np.multiply, a, out=())
assert_raises(TypeError, np.modf, a, 'one', out=('two', 'three'))
assert_raises(TypeError, np.modf, a, 'one', 'two', 'three')
assert_raises(ValueError, np.modf, a, out=('one', 'two', 'three'))
assert_raises(ValueError, np.modf, a, out=('one',))
def test_ufunc_override_where(self):
# 定义一个继承自 np.ndarray 的类 OverriddenArrayOld,用于重载特定的数组操作
class OverriddenArrayOld(np.ndarray):
# 解包方法,用于将输入对象解包成适当的数组形式
def _unwrap(self, objs):
cls = type(self)
result = []
for obj in objs:
if isinstance(obj, cls):
obj = np.array(obj)
elif type(obj) != np.ndarray:
return NotImplemented
result.append(obj)
return result
# 重载 __array_ufunc__ 方法,处理数组操作符重载
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
# 解包输入参数
inputs = self._unwrap(inputs)
if inputs is NotImplemented:
return NotImplemented
kwargs = kwargs.copy()
# 如果输出指定了 out 参数,也进行解包处理
if "out" in kwargs:
kwargs["out"] = self._unwrap(kwargs["out"])
if kwargs["out"] is NotImplemented:
return NotImplemented
# 调用父类的 __array_ufunc__ 方法处理数组操作
r = super().__array_ufunc__(ufunc, method, *inputs, **kwargs)
if r is not NotImplemented:
r = r.view(type(self))
return r
# 定义一个继承自 OverriddenArrayOld 的类 OverriddenArrayNew,进一步重载 __array_ufunc__ 方法
class OverriddenArrayNew(OverriddenArrayOld):
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
kwargs = kwargs.copy()
# 如果 kwargs 中包含 where 参数,解包并处理
if "where" in kwargs:
kwargs["where"] = self._unwrap((kwargs["where"], ))
if kwargs["where"] is NotImplemented:
return NotImplemented
else:
kwargs["where"] = kwargs["where"][0]
# 调用父类的 __array_ufunc__ 方法处理数组操作
r = super().__array_ufunc__(ufunc, method, *inputs, **kwargs)
if r is not NotImplemented:
r = r.view(type(self))
return r
# 定义一个被重载的数组操作函数 ufunc
ufunc = np.negative
# 创建一个普通的 numpy 数组
array = np.array([1, 2, 3])
# 创建一个用于 where 参数的布尔数组
where = np.array([True, False, True])
# 计算预期的操作结果
expected = ufunc(array, where=where)
# 使用 pytest 来测试预期的 TypeError 异常
with pytest.raises(TypeError):
ufunc(array, where=where.view(OverriddenArrayOld))
# 测试第一种重载情况,期望返回值为 OverriddenArrayNew 类型,并且结果符合预期
result_1 = ufunc(
array,
where=where.view(OverriddenArrayNew)
)
assert isinstance(result_1, OverriddenArrayNew)
assert np.all(np.array(result_1) == expected, where=where)
# 测试第二种重载情况,输入为 OverriddenArrayNew 类型的数组,并且结果符合预期
result_2 = ufunc(
array.view(OverriddenArrayNew),
where=where.view(OverriddenArrayNew)
)
assert isinstance(result_2, OverriddenArrayNew)
assert np.all(np.array(result_2) == expected, where=where)
def test_ufunc_override_exception(self):
# 定义一个简单的类 A,实现 __array_ufunc__ 方法抛出 ValueError 异常
class A:
def __array_ufunc__(self, *a, **kwargs):
raise ValueError("oops")
# 创建类 A 的实例
a = A()
# 使用 assert_raises 测试 np.negative 在指定的输出 out=a 时抛出 ValueError 异常
assert_raises(ValueError, np.negative, 1, out=a)
# 使用 assert_raises 测试 np.negative 在输入为 a 时抛出 ValueError 异常
assert_raises(ValueError, np.negative, a)
# 使用 assert_raises 测试 np.divide 在除数为 a 时抛出 ValueError 异常
assert_raises(ValueError, np.divide, 1., a)
def test_ufunc_override_not_implemented(self):
# 定义一个测试方法,验证在自定义类中未实现 __array_ufunc__ 方法时的行为
class A:
def __array_ufunc__(self, *args, **kwargs):
return NotImplemented
# 定义一个特殊类 A,其 __array_ufunc__ 方法返回 NotImplemented
# 第一个测试用例:验证对 np.negative 函数的行为
msg = ("operand type(s) all returned NotImplemented from "
"__array_ufunc__(<ufunc 'negative'>, '__call__', <*>): 'A'")
with assert_raises_regex(TypeError, fnmatch.translate(msg)):
np.negative(A())
# 断言调用 np.negative(A()) 会引发 TypeError,错误消息符合预期格式
# 第二个测试用例:验证对 np.add 函数的行为
msg = ("operand type(s) all returned NotImplemented from "
"__array_ufunc__(<ufunc 'add'>, '__call__', <*>, <object *>, "
"out=(1,)): 'A', 'object', 'int'")
with assert_raises_regex(TypeError, fnmatch.translate(msg)):
np.add(A(), object(), out=1)
# 断言调用 np.add(A(), object(), out=1) 会引发 TypeError,错误消息符合预期格式
def test_ufunc_override_disabled(self):
# 定义一个测试方法,验证当 __array_ufunc__ 被设置为 None 时的行为
class OptOut:
__array_ufunc__ = None
# 定义一个类 OptOut,其 __array_ufunc__ 属性为 None
opt_out = OptOut()
# 第一个测试用例:验证 ufuncs 对 OptOut 类型的行为
msg = "operand 'OptOut' does not support ufuncs"
with assert_raises_regex(TypeError, msg):
np.add(opt_out, 1)
# 断言调用 np.add(opt_out, 1) 会引发 TypeError,错误消息符合预期格式
with assert_raises_regex(TypeError, msg):
np.add(1, opt_out)
# 断言调用 np.add(1, opt_out) 会引发 TypeError,错误消息符合预期格式
with assert_raises_regex(TypeError, msg):
np.negative(opt_out)
# 断言调用 np.negative(opt_out) 会引发 TypeError,错误消息符合预期格式
# 第二个测试用例:验证即使其他参数具有异常的 __array_ufunc__ 实现时,OptOut 类型的行为仍然有效
class GreedyArray:
def __array_ufunc__(self, *args, **kwargs):
return self
# 定义一个类 GreedyArray,其 __array_ufunc__ 方法返回 self
greedy = GreedyArray()
assert_(np.negative(greedy) is greedy)
# 断言调用 np.negative(greedy) 返回的是 greedy 本身
with assert_raises_regex(TypeError, msg):
np.add(greedy, opt_out)
# 断言调用 np.add(greedy, opt_out) 会引发 TypeError,错误消息符合预期格式
with assert_raises_regex(TypeError, msg):
np.add(greedy, 1, out=opt_out)
# 断言调用 np.add(greedy, 1, out=opt_out) 会引发 TypeError,错误消息符合预期格式
def test_gufunc_override(self):
# 定义一个测试方法,验证 gufunc 的行为
# gufunc 实际上是 ufunc 实例,但是它们遵循不同的路径,因此需要检查 __array_ufunc__ 是否正确覆盖
class A:
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
return self, ufunc, method, inputs, kwargs
# 定义一个类 A,其 __array_ufunc__ 方法返回自身及其他参数
inner1d = ncu_tests.inner1d
a = A()
# 第一个测试用例:验证 inner1d(a, a) 的行为
res = inner1d(a, a)
assert_equal(res[0], a)
assert_equal(res[1], inner1d)
assert_equal(res[2], '__call__')
assert_equal(res[3], (a, a))
assert_equal(res[4], {})
# 断言调用 inner1d(a, a) 返回的结果符合预期
# 第二个测试用例:验证 inner1d(1, 1, out=a) 的行为
res = inner1d(1, 1, out=a)
assert_equal(res[0], a)
assert_equal(res[1], inner1d)
assert_equal(res[2], '__call__')
assert_equal(res[3], (1, 1))
assert_equal(res[4], {'out': (a,)})
# 断言调用 inner1d(1, 1, out=a) 返回的结果符合预期
# 错误的参数数量是一个错误
assert_raises(TypeError, inner1d, a, out='two')
assert_raises(TypeError, inner1d, a, a, 'one', out='two')
assert_raises(TypeError, inner1d, a, a, 'one', 'two')
assert_raises(ValueError, inner1d, a, a, out=('one', 'two'))
assert_raises(ValueError, inner1d, a, a, out=())
# 断言调用 inner1d 时传入错误参数会引发预期的异常
# 定义一个测试方法,用于直接调用数组的 __array_ufunc__ 方法进行功能回归测试
def test_array_ufunc_direct_call(self):
# 这是对 gh-24023 的回归测试,主要是防止出现段错误
a = np.array(1)
# 使用 pytest 的断言检查是否会抛出 TypeError 异常
with pytest.raises(TypeError):
a.__array_ufunc__()
# 当不传入关键字参数时,kwargs 在 C 级别可能为 NULL
with pytest.raises(TypeError):
a.__array_ufunc__(1, 2)
# 同样的情况适用于有效调用的情况:
# 调用 a 对象的 __array_ufunc__ 方法,使用 np.add 函数,调用 "__call__" 方法,对 a 和 a 进行操作
res = a.__array_ufunc__(np.add, "__call__", a, a)
# 使用断言检查结果是否与预期的 a + a 相等
assert_array_equal(res, a + a)
class TestChoose:
# 测试选择函数 np.choose 的混合用例
def test_mixed(self):
# 创建布尔选择数组 c 和备选数组 a
c = np.array([True, True])
a = np.array([True, True])
# 断言选择函数的结果与期望结果相等
assert_equal(np.choose(c, (a, 1)), np.array([1, 1]))
class TestRationalFunctions:
# 有关有理数函数的测试类
# 测试最小公倍数函数 np.lcm 的基本用例
def test_lcm(self):
# 使用指定数据类型进行最小公倍数函数的内部测试
self._test_lcm_inner(np.int16)
self._test_lcm_inner(np.uint16)
# 测试最小公倍数函数 np.lcm 在对象数组上的应用
def test_lcm_object(self):
# 使用对象数组进行最小公倍数函数的内部测试
self._test_lcm_inner(np.object_)
# 测试最大公约数函数 np.gcd 的基本用例
def test_gcd(self):
# 使用指定数据类型进行最大公约数函数的内部测试
self._test_gcd_inner(np.int16)
self._test_lcm_inner(np.uint16)
# 测试最大公约数函数 np.gcd 在对象数组上的应用
def test_gcd_object(self):
# 使用对象数组进行最大公约数函数的内部测试
self._test_gcd_inner(np.object_)
# 内部函数,用于测试最小公倍数函数 np.lcm 的不同情况
def _test_lcm_inner(self, dtype):
# 基本用例:使用指定数据类型的数组进行测试
a = np.array([12, 120], dtype=dtype)
b = np.array([20, 200], dtype=dtype)
assert_equal(np.lcm(a, b), [60, 600])
if not issubclass(dtype, np.unsignedinteger):
# 负数被忽略的情况
a = np.array([12, -12, 12, -12], dtype=dtype)
b = np.array([20, 20, -20, -20], dtype=dtype)
assert_equal(np.lcm(a, b), [60]*4)
# 缩减用例
a = np.array([3, 12, 20], dtype=dtype)
assert_equal(np.lcm.reduce([3, 12, 20]), 60)
# 广播以及包含 0 的测试
a = np.arange(6).astype(dtype)
b = 20
assert_equal(np.lcm(a, b), [0, 20, 20, 60, 20, 20])
# 内部函数,用于测试最大公约数函数 np.gcd 的不同情况
def _test_gcd_inner(self, dtype):
# 基本用例:使用指定数据类型的数组进行测试
a = np.array([12, 120], dtype=dtype)
b = np.array([20, 200], dtype=dtype)
assert_equal(np.gcd(a, b), [4, 40])
if not issubclass(dtype, np.unsignedinteger):
# 负数被忽略的情况
a = np.array([12, -12, 12, -12], dtype=dtype)
b = np.array([20, 20, -20, -20], dtype=dtype)
assert_equal(np.gcd(a, b), [4]*4)
# 缩减用例
a = np.array([15, 25, 35], dtype=dtype)
assert_equal(np.gcd.reduce(a), 5)
# 广播以及包含 0 的测试
a = np.arange(6).astype(dtype)
b = 20
assert_equal(np.gcd(a, b), [20, 1, 2, 1, 4, 5])
# 测试最小公倍数函数 np.lcm 在溢出情况下的行为
def test_lcm_overflow(self):
# 验证在 a*b 溢出时,最小公倍数函数不会溢出
big = np.int32(np.iinfo(np.int32).max // 11)
a = 2*big
b = 5*big
assert_equal(np.lcm(a, b), 10*big)
# 测试最大公约数函数 np.gcd 在溢出情况下的行为
def test_gcd_overflow(self):
for dtype in (np.int32, np.int64):
# 验证在取 abs(x) 时不会溢出
# 对于最小公倍数函数而言,结果无法表示
a = dtype(np.iinfo(dtype).min) # 负的二次幂
q = -(a // 4)
assert_equal(np.gcd(a, q*3), q)
assert_equal(np.gcd(a, -q*3), q)
# 测试十进制数的最大公约数和最小公倍数函数
def test_decimal(self):
from decimal import Decimal
# 创建十进制数组 a 和 b
a = np.array([1, 1, -1, -1]) * Decimal('0.20')
b = np.array([1, -1, 1, -1]) * Decimal('0.12')
# 断言最大公约数函数的结果与期望结果相等
assert_equal(np.gcd(a, b), 4*[Decimal('0.04')])
# 断言最小公倍数函数的结果与期望结果相等
assert_equal(np.lcm(a, b), 4*[Decimal('0.60')])
def test_float(self):
# 浮点数在计算中由于舍入误差而不精确定义
assert_raises(TypeError, np.gcd, 0.3, 0.4)
assert_raises(TypeError, np.lcm, 0.3, 0.4)
def test_huge_integers(self):
# 将整数转换为数组后,由于显式对象数据类型的存在,行为略有不同:
assert_equal(np.array(2**200), 2**200)
# 特殊的提升规则应确保即使对两个 Python 整数也有效(尽管速度较慢)。
# (我们在比较时这样做,因为结果始终是布尔值,我们还特别处理数组与Python整数的比较)
np.equal(2**200, 2**200)
# 但是,当影响结果的数据类型时,我们无法这样做:
with pytest.raises(OverflowError):
np.gcd(2**100, 3**100)
# 明确要求使用 `object` 类型是可以的:
assert np.gcd(2**100, 3**100, dtype=object) == 1
# 到目前为止,以下操作是有效的,因为它们使用了数组(将是对象数组)
a = np.array(2**100 * 3**5)
b = np.array([2**100 * 5**7, 2**50 * 3**10])
assert_equal(np.gcd(a, b), [2**100, 2**50 * 3**5])
assert_equal(np.lcm(a, b), [2**100 * 3**5 * 5**7, 2**100 * 3**10])
# 定义一个测试类 TestRoundingFunctions,用于测试四舍五入相关函数
class TestRoundingFunctions:
# 定义测试对象直接实现魔术方法的方法
def test_object_direct(self):
""" test direct implementation of these magic methods """
# 定义一个内部类 C,实现 __floor__、__ceil__ 和 __trunc__ 方法
class C:
def __floor__(self):
return 1
def __ceil__(self):
return 2
def __trunc__(self):
return 3
# 创建一个包含两个 C 类对象的 NumPy 数组 arr
arr = np.array([C(), C()])
# 断言 np.floor(arr) 返回值与预期 [1, 1] 相等
assert_equal(np.floor(arr), [1, 1])
# 断言 np.ceil(arr) 返回值与预期 [2, 2] 相等
assert_equal(np.ceil(arr), [2, 2])
# 断言 np.trunc(arr) 返回值与预期 [3, 3] 相等
assert_equal(np.trunc(arr), [3, 3])
# 定义测试对象间接通过 __float__ 方法实现的方法
def test_object_indirect(self):
""" test implementations via __float__ """
# 定义一个内部类 C,实现 __float__ 方法返回 -2.5
class C:
def __float__(self):
return -2.5
# 创建一个包含两个 C 类对象的 NumPy 数组 arr
arr = np.array([C(), C()])
# 断言 np.floor(arr) 返回值与预期 [-3, -3] 相等
assert_equal(np.floor(arr), [-3, -3])
# 断言 np.ceil(arr) 返回值与预期 [-2, -2] 相等
assert_equal(np.ceil(arr), [-2, -2])
# 使用 pytest.raises 检查 np.trunc(arr) 抛出 TypeError 异常,与 math.trunc 保持一致
with pytest.raises(TypeError):
np.trunc(arr) # consistent with math.trunc
# 定义测试 Fraction 对象的方法
def test_fraction(self):
# 创建 Fraction 对象 f,值为 -4/3
f = Fraction(-4, 3)
# 断言 np.floor(f) 返回值与预期 -2 相等
assert_equal(np.floor(f), -2)
# 断言 np.ceil(f) 返回值与预期 -1 相等
assert_equal(np.ceil(f), -1)
# 断言 np.trunc(f) 返回值与预期 -1 相等
assert_equal(np.trunc(f), -1)
# 定义测试复数函数的测试类 TestComplexFunctions
class TestComplexFunctions:
# 定义包含多个函数的列表 funcs
funcs = [np.arcsin, np.arccos, np.arctan, np.arcsinh, np.arccosh,
np.arctanh, np.sin, np.cos, np.tan, np.exp,
np.exp2, np.log, np.sqrt, np.log10, np.log2,
np.log1p]
# 定义测试方法 test_it,测试 funcs 中所有函数的实现
def test_it(self):
for f in self.funcs:
# 根据不同的函数 f 设置变量 x 的值
if f is np.arccosh:
x = 1.5
else:
x = .5
# 分别计算函数 f 在实数 x 和复数 complex(x) 上的结果
fr = f(x)
fz = f(complex(x))
# 断言 fz 的实部与 fr 相等,错误消息包含函数名
assert_almost_equal(fz.real, fr, err_msg='real part %s' % f)
# 断言 fz 的虚部为 0.,错误消息包含函数名
assert_almost_equal(fz.imag, 0., err_msg='imag part %s' % f)
# 定义测试方法 test_precisions_consistent,测试 funcs 中所有函数在不同精度下的一致性
@pytest.mark.xfail(IS_WASM, reason="doesn't work")
def test_precisions_consistent(self):
# 定义复数 z
z = 1 + 1j
for f in self.funcs:
# 分别计算 f 在 np.csingle(z)、np.cdouble(z) 和 np.clongdouble(z) 上的结果
fcf = f(np.csingle(z))
fcd = f(np.cdouble(z))
fcl = f(np.clongdouble(z))
# 断言 fcf 和 fcd 的结果在小数点后 6 位精度下相等,错误消息包含函数名
assert_almost_equal(fcf, fcd, decimal=6, err_msg='fch-fcd %s' % f)
# 断言 fcl 和 fcd 的结果在小数点后 15 位精度下相等,错误消息包含函数名
assert_almost_equal(fcl, fcd, decimal=15, err_msg='fch-fcl %s' % f)
# 定义测试方法 test_precisions_consistent,测试 funcs 中所有函数在不同精度下的一致性
@pytest.mark.xfail(IS_WASM, reason="doesn't work")
def test_branch_cuts(self):
# 检查分支切割和它们的连续性
_check_branch_cut(np.log, -0.5, 1j, 1, -1, True)
_check_branch_cut(np.log2, -0.5, 1j, 1, -1, True)
_check_branch_cut(np.log10, -0.5, 1j, 1, -1, True)
_check_branch_cut(np.log1p, -1.5, 1j, 1, -1, True)
_check_branch_cut(np.sqrt, -0.5, 1j, 1, -1, True)
_check_branch_cut(np.arcsin, [ -2, 2], [1j, 1j], 1, -1, True)
_check_branch_cut(np.arccos, [ -2, 2], [1j, 1j], 1, -1, True)
_check_branch_cut(np.arctan, [0-2j, 2j], [1, 1], -1, 1, True)
_check_branch_cut(np.arcsinh, [0-2j, 2j], [1, 1], -1, 1, True)
_check_branch_cut(np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1, True)
_check_branch_cut(np.arctanh, [ -2, 2], [1j, 1j], 1, -1, True)
# 检查错误的分支切割:断言象限之间的连续性
_check_branch_cut(np.arcsin, [0-2j, 2j], [ 1, 1], 1, 1)
_check_branch_cut(np.arccos, [0-2j, 2j], [ 1, 1], 1, 1)
_check_branch_cut(np.arctan, [ -2, 2], [1j, 1j], 1, 1)
_check_branch_cut(np.arcsinh, [ -2, 2, 0], [1j, 1j, 1], 1, 1)
_check_branch_cut(np.arccosh, [0-2j, 2j, 2], [1, 1, 1j], 1, 1)
_check_branch_cut(np.arctanh, [0-2j, 2j, 0], [1, 1, 1j], 1, 1)
@pytest.mark.xfail(IS_WASM, reason="doesn't work")
def test_branch_cuts_complex64(self):
# 检查复数类型 complex64 的分支切割和连续性
_check_branch_cut(np.log, -0.5, 1j, 1, -1, True, np.complex64)
_check_branch_cut(np.log2, -0.5, 1j, 1, -1, True, np.complex64)
_check_branch_cut(np.log10, -0.5, 1j, 1, -1, True, np.complex64)
_check_branch_cut(np.log1p, -1.5, 1j, 1, -1, True, np.complex64)
_check_branch_cut(np.sqrt, -0.5, 1j, 1, -1, True, np.complex64)
_check_branch_cut(np.arcsin, [ -2, 2], [1j, 1j], 1, -1, True, np.complex64)
_check_branch_cut(np.arccos, [ -2, 2], [1j, 1j], 1, -1, True, np.complex64)
_check_branch_cut(np.arctan, [0-2j, 2j], [1, 1], -1, 1, True, np.complex64)
_check_branch_cut(np.arcsinh, [0-2j, 2j], [1, 1], -1, 1, True, np.complex64)
_check_branch_cut(np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1, True, np.complex64)
_check_branch_cut(np.arctanh, [ -2, 2], [1j, 1j], 1, -1, True, np.complex64)
# 检查错误的分支切割:断言复数类型 complex64 在象限之间的连续性
_check_branch_cut(np.arcsin, [0-2j, 2j], [ 1, 1], 1, 1, False, np.complex64)
_check_branch_cut(np.arccos, [0-2j, 2j], [ 1, 1], 1, 1, False, np.complex64)
_check_branch_cut(np.arctan, [ -2, 2], [1j, 1j], 1, 1, False, np.complex64)
_check_branch_cut(np.arcsinh, [ -2, 2, 0], [1j, 1j, 1], 1, 1, False, np.complex64)
_check_branch_cut(np.arccosh, [0-2j, 2j, 2], [1, 1, 1j], 1, 1, False, np.complex64)
_check_branch_cut(np.arctanh, [0-2j, 2j, 0], [1, 1, 1j], 1, 1, False, np.complex64)
# 测试自定义函数与cmath库中对应函数的精度
def test_against_cmath(self):
# 导入cmath库
import cmath
# 定义复数坐标点
points = [-1-1j, -1+1j, +1-1j, +1+1j]
# 函数名映射,用于处理cmath库中函数名与自定义函数可能不同的情况
name_map = {'arcsin': 'asin', 'arccos': 'acos', 'arctan': 'atan',
'arcsinh': 'asinh', 'arccosh': 'acosh', 'arctanh': 'atanh'}
# 计算绝对误差的阈值,使用numpy库中复数类型的机器精度
atol = 4*np.finfo(complex).eps
# 遍历所有待测试的自定义函数
for func in self.funcs:
# 获取函数名,并提取最后一部分(去除命名空间)
fname = func.__name__.split('.')[-1]
# 根据映射表获取对应的cmath函数名,如果没有映射,则使用原函数名
cname = name_map.get(fname, fname)
try:
# 获取cmath库中对应的函数对象
cfunc = getattr(cmath, cname)
except AttributeError:
# 如果cmath库中不存在对应函数,则跳过当前函数的测试
continue
# 对每个复数坐标点执行测试
for p in points:
# 调用自定义函数并转换为复数类型
a = complex(func(np.complex128(p)))
# 调用cmath库中对应函数
b = cfunc(p)
# 断言自定义函数与cmath库计算结果的绝对误差小于预设阈值
assert_(
abs(a - b) < atol,
"%s %s: %s; cmath: %s" % (fname, p, a, b)
)
@pytest.mark.xfail(
# manylinux2014 使用 glibc2.17
_glibc_older_than("2.18"),
reason="旧版glibc版本不精确(也许使用SIMD能通过?)"
)
@pytest.mark.xfail(IS_WASM, reason="不适用于WASM")
@pytest.mark.parametrize('dtype', [
np.complex64, np.complex128, np.clongdouble
])
@np.errstate(all="ignore")
# 测试特殊情况下的类型提升
def test_promotion_corner_cases(self):
# 对每个自定义函数执行测试
for func in self.funcs:
# 断言自定义函数对np.float16(1)的输出类型为np.float16
assert func(np.float16(1)).dtype == np.float16
# 断言整数转低精度浮点数的提升是一个值得商榷的选择
assert func(np.uint8(1)).dtype == np.float16
assert func(np.int16(1)).dtype == np.float32
class TestAttributes:
# 测试属性的类
def test_attributes(self):
# 获取全局变量 ncu.add 的引用
add = ncu.add
# 断言 add 函数的名称为 'add'
assert_equal(add.__name__, 'add')
# 断言 add 函数支持的类型数至少为 18
assert_(add.ntypes >= 18) # 如果添加类型不会失败
# 断言 'ii->i' 在 add 函数的类型列表中
assert_('ii->i' in add.types)
# 断言 add 函数的输入参数个数为 2
assert_equal(add.nin, 2)
# 断言 add 函数的输出参数个数为 1
assert_equal(add.nout, 1)
# 断言 add 函数的身份元素为 0
assert_equal(add.identity, 0)
def test_doc(self):
# 测试文档字符串的方法
# 不需要检查可能会变化的大量关键字参数
assert_(ncu.add.__doc__.startswith(
"add(x1, x2, /, out=None, *, where=True"))
assert_(ncu.frexp.__doc__.startswith(
"frexp(x[, out1, out2], / [, out=(None, None)], *, where=True"))
class TestSubclass:
# 测试子类的类
def test_subclass_op(self):
# 测试子类操作的方法
class simple(np.ndarray):
# 简单的子类,继承自 np.ndarray
def __new__(subtype, shape):
# 根据指定形状创建新的实例
self = np.ndarray.__new__(subtype, shape, dtype=object)
# 将实例填充为 0
self.fill(0)
return self
# 创建一个 simple 类的实例 a,形状为 (3, 4)
a = simple((3, 4))
# 断言 a 加上自身等于 a
assert_equal(a+a, a)
class TestFrompyfunc:
# 测试从 Python 函数创建 ufunc 的类
def test_identity(self):
# 测试标识性参数的方法
def mul(a, b):
return a * b
# 使用 identity=1 创建 mul 函数的 ufunc
mul_ufunc = np.frompyfunc(mul, nin=2, nout=1, identity=1)
# 断言对数组 [2, 3, 4] 使用 mul_ufunc.reduce 的结果为 24
assert_equal(mul_ufunc.reduce([2, 3, 4]), 24)
# 断言对 np.ones((2, 2)) 使用 mul_ufunc.reduce 沿着轴 (0, 1) 的结果为 1
assert_equal(mul_ufunc.reduce(np.ones((2, 2)), axis=(0, 1)), 1)
# 断言对空数组使用 mul_ufunc.reduce 会引发 ValueError
assert_equal(mul_ufunc.reduce([]), 1)
# 使用 identity=None 创建 mul 函数的 ufunc(可重新排序)
mul_ufunc = np.frompyfunc(mul, nin=2, nout=1, identity=None)
# 断言对数组 [2, 3, 4] 使用 mul_ufunc.reduce 的结果为 24
assert_equal(mul_ufunc.reduce([2, 3, 4]), 24)
# 断言对 np.ones((2, 2)) 使用 mul_ufunc.reduce 沿着轴 (0, 1) 的结果为 1
assert_equal(mul_ufunc.reduce(np.ones((2, 2)), axis=(0, 1)), 1)
# 断言对空数组使用 mul_ufunc.reduce 会引发 ValueError
assert_raises(ValueError, lambda: mul_ufunc.reduce([]))
# 使用无 identity 创建 mul 函数的 ufunc(不可重新排序)
mul_ufunc = np.frompyfunc(mul, nin=2, nout=1)
# 断言对数组 [2, 3, 4] 使用 mul_ufunc.reduce 的结果为 24
assert_equal(mul_ufunc.reduce([2, 3, 4]), 24)
# 断言对 np.ones((2, 2)) 使用 mul_ufunc.reduce 沿着轴 (0, 1) 会引发 ValueError
assert_raises(ValueError, lambda: mul_ufunc.reduce(np.ones((2, 2)), axis=(0, 1)))
# 断言对空数组使用 mul_ufunc.reduce 会引发 ValueError
assert_raises(ValueError, lambda: mul_ufunc.reduce([]))
def _check_branch_cut(f, x0, dx, re_sign=1, im_sign=-1, sig_zero_ok=False,
dtype=complex):
"""
Check for a branch cut in a function.
Assert that `x0` lies on a branch cut of function `f` and `f` is
continuous from the direction `dx`.
Parameters
----------
f : func
Function to check
x0 : array-like
Point on branch cut
dx : array-like
Direction to check continuity in
re_sign, im_sign : {1, -1}
Change of sign of the real or imaginary part expected
sig_zero_ok : bool
Whether to check if the branch cut respects signed zero (if applicable)
dtype : dtype
Dtype to check (should be complex)
"""
# 将 x0 和 dx 转换为至少一维数组,并指定 dtype
x0 = np.atleast_1d(x0).astype(dtype)
dx = np.atleast_1d(dx).astype(dtype)
# 如果 dtype 是复数类型
if np.dtype(dtype).char == 'F':
# 设置 scale 和 atol 的值
scale = np.finfo(dtype).eps * 1e2
atol = np.float32(1e-2)
else:
scale = np.finfo(dtype).eps * 1e3
atol = 1e-4
# 计算 f(x0) 的值并赋给 y0
y0 = f(x0)
# 计算正向偏移点的函数值
yp = f(x0 + dx * scale * np.absolute(x0) / np.absolute(dx))
# 计算反向偏移点的函数值
ym = f(x0 - dx * scale * np.absolute(x0) / np.absolute(dx))
# 断言:验证正向偏移的实部与原始点的实部之差小于给定的误差容限
assert_(np.all(np.absolute(y0.real - yp.real) < atol), (y0, yp))
# 断言:验证正向偏移的虚部与原始点的虚部之差小于给定的误差容限
assert_(np.all(np.absolute(y0.imag - yp.imag) < atol), (y0, yp))
# 断言:验证反向偏移的实部与原始点的实部之差小于给定的误差容限
assert_(np.all(np.absolute(y0.real - ym.real * re_sign) < atol), (y0, ym))
# 断言:验证反向偏移的虚部与原始点的虚部之差小于给定的误差容限
assert_(np.all(np.absolute(y0.imag - ym.imag * im_sign) < atol), (y0, ym))
if sig_zero_ok:
# 检查有符号零值是否可以作为位移
# 对于实部为零但偏移不为零的情况
jr = (x0.real == 0) & (dx.real != 0)
if np.any(jr):
# 取出实部为零的元素,并将其设为零
x = x0[jr]
x.real = ncu.NZERO
# 计算函数在零实部元素上的值
ym = f(x)
# 断言:验证在零实部元素上,反向偏移的实部与原始点的实部之差小于给定的误差容限
assert_(np.all(np.absolute(y0[jr].real - ym.real * re_sign) < atol), (y0[jr], ym))
# 断言:验证在零实部元素上,反向偏移的虚部与原始点的虚部之差小于给定的误差容限
assert_(np.all(np.absolute(y0[jr].imag - ym.imag * im_sign) < atol), (y0[jr], ym))
# 对于虚部为零但偏移不为零的情况
ji = (x0.imag == 0) & (dx.imag != 0)
if np.any(ji):
# 取出虚部为零的元素,并将其设为零
x = x0[ji]
x.imag = ncu.NZERO
# 计算函数在零虚部元素上的值
ym = f(x)
# 断言:验证在零虚部元素上,反向偏移的实部与原始点的实部之差小于给定的误差容限
assert_(np.all(np.absolute(y0[ji].real - ym.real * re_sign) < atol), (y0[ji], ym))
# 断言:验证在零虚部元素上,反向偏移的虚部与原始点的虚部之差小于给定的误差容限
assert_(np.all(np.absolute(y0[ji].imag - ym.imag * im_sign) < atol), (y0[ji], ym))
# 定义一个测试函数,用于验证 np.copysign() 的功能
def test_copysign():
# 断言 np.copysign(1, -1) 的结果应该是 -1
assert_(np.copysign(1, -1) == -1)
# 在忽略除法错误的情况下,验证 1 / np.copysign(0, -1) 结果小于 0
with np.errstate(divide="ignore"):
assert_(1 / np.copysign(0, -1) < 0)
# 验证 1 / np.copysign(0, 1) 结果大于 0
assert_(1 / np.copysign(0, 1) > 0)
# 验证 np.copysign(np.nan, -1) 结果为负数
assert_(np.signbit(np.copysign(np.nan, -1)))
# 验证 np.copysign(np.nan, 1) 结果为非负数
assert_(not np.signbit(np.copysign(np.nan, 1)))
# 定义一个内部测试函数,用于测试 np.nextafter() 的功能
def _test_nextafter(t):
one = t(1)
two = t(2)
zero = t(0)
eps = np.finfo(t).eps
# 验证 np.nextafter(one, two) - one 结果等于 eps
assert_(np.nextafter(one, two) - one == eps)
# 验证 np.nextafter(one, zero) - one 结果小于 0
assert_(np.nextafter(one, zero) - one < 0)
# 验证 np.nextafter(np.nan, one) 和 np.nextafter(one, np.nan) 结果为 NaN
assert_(np.isnan(np.nextafter(np.nan, one)))
assert_(np.isnan(np.nextafter(one, np.nan)))
# 验证 np.nextafter(one, one) 结果等于 one
assert_(np.nextafter(one, one) == one)
# 定义测试函数,调用 _test_nextafter() 并返回结果
def test_nextafter():
return _test_nextafter(np.float64)
# 定义测试函数,调用 _test_nextafter() 并返回结果
def test_nextafterf():
return _test_nextafter(np.float32)
# 根据特定条件跳过或标记失败的测试函数,验证 np.longdouble 的 np.nextafter() 功能
@pytest.mark.skipif(np.finfo(np.double) == np.finfo(np.longdouble),
reason="long double is same as double")
@pytest.mark.xfail(condition=platform.machine().startswith("ppc64"),
reason="IBM double double")
def test_nextafterl():
return _test_nextafter(np.longdouble)
# 定义测试函数,验证 np.nextafter() 在边界值情况下的行为
def test_nextafter_0():
for t, direction in itertools.product(np._core.sctypes['float'], (1, -1)):
# 对于双倍精度浮点数的极小值,需要特别处理
with suppress_warnings() as sup:
sup.filter(UserWarning)
if not np.isnan(np.finfo(t).tiny):
tiny = np.finfo(t).tiny
# 验证 direction * np.nextafter(t(0), t(direction)) 结果在 0 和 tiny 之间
assert_(0. < direction * np.nextafter(t(0), t(direction)) < tiny)
# 验证 np.nextafter(t(0), t(direction)) / t(2.1) 结果等于 direction * 0.0
assert_equal(np.nextafter(t(0), t(direction)) / t(2.1), direction * 0.0)
# 定义内部测试函数,用于验证 np.spacing() 的功能
def _test_spacing(t):
one = t(1)
eps = np.finfo(t).eps
nan = t(np.nan)
inf = t(np.inf)
with np.errstate(invalid='ignore'):
# 验证 np.spacing(one) 结果等于 eps
assert_equal(np.spacing(one), eps)
# 验证 np.spacing(nan) 和 np.spacing(inf) 结果为 NaN
assert_(np.isnan(np.spacing(nan)))
assert_(np.isnan(np.spacing(inf)))
assert_(np.isnan(np.spacing(-inf)))
# 验证 np.spacing(t(1e30)) 不等于 0
assert_(np.spacing(t(1e30)) != 0)
# 定义测试函数,调用 _test_spacing() 并返回结果
def test_spacing():
return _test_spacing(np.float64)
# 定义测试函数,调用 _test_spacing() 并返回结果
def test_spacingf():
return _test_spacing(np.float32)
# 根据特定条件跳过或标记失败的测试函数,验证 np.longdouble 的 np.spacing() 功能
@pytest.mark.skipif(np.finfo(np.double) == np.finfo(np.longdouble),
reason="long double is same as double")
@pytest.mark.xfail(condition=platform.machine().startswith("ppc64"),
reason="IBM double double")
def test_spacingl():
return _test_spacing(np.longdouble)
# 定义测试函数,验证 np.spacing() 在不同条件下的行为
def test_spacing_gfortran():
# 参考自 gfortran 编译的 Fortran 文件,在不同情况下验证 np.spacing() 结果
# 这段注释仅作为参考,不需要在代码中体现
pass
# 创建一个字典 `ref`,键为 np.float64 和 np.float32,对应的值是列表,分别包含特定精度下的 np.spacing(x) 的参考值
ref = {np.float64: [1.69406589450860068E-021,
2.22044604925031308E-016,
1.13686837721616030E-013,
1.81898940354585648E-012],
np.float32: [9.09494702E-13,
1.19209290E-07,
6.10351563E-05,
9.76562500E-04]}
# 针对每个数据类型 `dt` 和相应的小数精度 `dec_`,执行以下操作
for dt, dec_ in zip([np.float32, np.float64], (10, 20)):
# 创建一个 numpy 数组 `x`,包含指定数据类型 `dt` 的四个不同数值
x = np.array([1e-5, 1, 1000, 10500], dtype=dt)
# 使用 assert_array_almost_equal 函数验证 np.spacing(x) 的结果几乎等于 `ref[dt]` 中的值,使用 `decimal=dec_` 的精度
assert_array_almost_equal(np.spacing(x), ref[dt], decimal=dec_)
def test_nextafter_vs_spacing():
# XXX: spacing does not handle long double yet
# 循环测试不同类型(np.float32, np.float64)和不同浮点数值(1, 1e-5, 1000)
for t in [np.float32, np.float64]:
for _f in [1, 1e-5, 1000]:
f = t(_f)
f1 = t(_f + 1)
# 断言 np.nextafter(f, f1) - f 等于 np.spacing(f)
assert_(np.nextafter(f, f1) - f == np.spacing(f))
def test_pos_nan():
"""Check np.nan is a positive nan."""
# 断言 np.nan 的符号位为 0,即它是一个正的 NaN
assert_(np.signbit(np.nan) == 0)
def test_reduceat():
"""Test bug in reduceat when structured arrays are not copied."""
# 定义结构化数据类型 db,并创建空数组 a
db = np.dtype([('name', 'S11'), ('time', np.int64), ('value', np.float32)])
a = np.empty([100], dtype=db)
a['name'] = 'Simple'
a['time'] = 10
a['value'] = 100
indx = [0, 7, 15, 25]
h2 = []
val1 = indx[0]
# 对于 indx 中的索引,使用 np.add.reduce 计算 a['value'] 的和,存入 h2
for val2 in indx[1:]:
h2.append(np.add.reduce(a['value'][val1:val2]))
val1 = val2
h2.append(np.add.reduce(a['value'][val1:]))
h2 = np.array(h2)
# 断言 np.add.reduceat(a['value'], indx) 的结果与 h2 数组几乎相等
h1 = np.add.reduceat(a['value'], indx)
assert_array_almost_equal(h1, h2)
# 设置缓冲区大小为 32,再次计算 reduceat 结果,断言几乎相等
np.setbufsize(32)
h1 = np.add.reduceat(a['value'], indx)
np.setbufsize(ncu.UFUNC_BUFSIZE_DEFAULT)
assert_array_almost_equal(h1, h2)
def test_reduceat_empty():
"""Reduceat should work with empty arrays"""
indices = np.array([], 'i4')
x = np.array([], 'f8')
# 对空数组 x 使用 reduceat,断言结果的数据类型与 x 相同且形状为 (0,)
result = np.add.reduceat(x, indices)
assert_equal(result.dtype, x.dtype)
assert_equal(result.shape, (0,))
# 另一种情况,x 是一个全为 1 的 5x2 数组,对其使用 reduceat,断言结果形状为 (0, 2)
x = np.ones((5, 2))
result = np.add.reduceat(x, [], axis=0)
assert_equal(result.dtype, x.dtype)
assert_equal(result.shape, (0, 2))
result = np.add.reduceat(x, [], axis=1)
assert_equal(result.dtype, x.dtype)
assert_equal(result.shape, (5, 0))
def test_complex_nan_comparisons():
nans = [complex(np.nan, 0), complex(0, np.nan), complex(np.nan, np.nan)]
fins = [complex(1, 0), complex(-1, 0), complex(0, 1), complex(0, -1),
complex(1, 1), complex(-1, -1), complex(0, 0)]
with np.errstate(invalid='ignore'):
for x in nans + fins:
x = np.array([x])
for y in nans + fins:
y = np.array([y])
# 如果 x 和 y 都是有限的数,则跳过
if np.isfinite(x) and np.isfinite(y):
continue
# 断言 x < y, x > y, x <= y, x >= y, x == y 都为 False
assert_equal(x < y, False, err_msg="%r < %r" % (x, y))
assert_equal(x > y, False, err_msg="%r > %r" % (x, y))
assert_equal(x <= y, False, err_msg="%r <= %r" % (x, y))
assert_equal(x >= y, False, err_msg="%r >= %r" % (x, y))
assert_equal(x == y, False, err_msg="%r == %r" % (x, y))
def test_rint_big_int():
# np.rint bug for large integer values on Windows 32-bit and MKL
# https://github.com/numpy/numpy/issues/6685
val = 4607998452777363968
# 这个值在浮点数中可以精确表示
assert_equal(val, int(float(val)))
# np.rint 不应改变这个值
assert_equal(val, np.rint(val))
@pytest.mark.parametrize('ftype', [np.float32, np.float64])
# 使用 pytest 的 parametrize 装饰器,为 test_memoverlap_accumulate 函数创建多个参数化的测试实例
def test_memoverlap_accumulate(ftype):
# 重现 bug https://github.com/numpy/numpy/issues/15597
# 创建一个包含指定数据类型的 numpy 数组
arr = np.array([0.61, 0.60, 0.77, 0.41, 0.19], dtype=ftype)
# 创建预期的最大值累积结果数组
out_max = np.array([0.61, 0.61, 0.77, 0.77, 0.77], dtype=ftype)
# 创建预期的最小值累积结果数组
out_min = np.array([0.61, 0.60, 0.60, 0.41, 0.19], dtype=ftype)
# 使用 assert_equal 断言函数比较 numpy 的最大值累积和最小值累积函数的结果
assert_equal(np.maximum.accumulate(arr), out_max)
assert_equal(np.minimum.accumulate(arr), out_min)
@pytest.mark.parametrize("ufunc, dtype", [
# 为 test_memoverlap_accumulate_cmp 函数创建多个参数化的测试实例,用于比较二进制操作函数的累积结果
(ufunc, t[0])
for ufunc in UFUNCS_BINARY_ACC
for t in ufunc.types
if t[-1] == '?' and t[0] not in 'DFGMmO'
])
def test_memoverlap_accumulate_cmp(ufunc, dtype):
if ufunc.signature:
# 如果 ufunc 有特定签名,跳过该测试
pytest.skip('For generic signatures only')
# 对于指定的大小进行循环测试
for size in (2, 8, 32, 64, 128, 256):
# 创建包含指定数据类型的 numpy 数组
arr = np.array([0, 1, 1]*size, dtype=dtype)
# 调用 ufunc 的累积函数,将结果转换为 uint8 类型
acc = ufunc.accumulate(arr, dtype='?')
# 将累积结果视图转换为 uint8 类型
acc_u8 = acc.view(np.uint8)
# 创建预期的累积结果数组
exp = np.array(list(itertools.accumulate(arr, ufunc)), dtype=np.uint8)
# 使用 assert_equal 断言函数比较预期结果和累积结果数组
assert_equal(exp, acc_u8)
@pytest.mark.parametrize("ufunc, dtype", [
# 为 test_memoverlap_accumulate_symmetric 函数创建多个参数化的测试实例,用于比较对称数据类型的累积结果
(ufunc, t[0])
for ufunc in UFUNCS_BINARY_ACC
for t in ufunc.types
if t[0] == t[1] and t[0] == t[-1] and t[0] not in 'DFGMmO?'
])
def test_memoverlap_accumulate_symmetric(ufunc, dtype):
if ufunc.signature:
# 如果 ufunc 有特定签名,跳过该测试
pytest.skip('For generic signatures only')
# 忽略所有的 numpy 错误
with np.errstate(all='ignore'):
# 对于指定的大小进行循环测试
for size in (2, 8, 32, 64, 128, 256):
# 创建包含指定数据类型的 numpy 数组
arr = np.array([0, 1, 2]*size).astype(dtype)
# 调用 ufunc 的累积函数,将结果转换为指定数据类型
acc = ufunc.accumulate(arr, dtype=dtype)
# 创建预期的累积结果数组
exp = np.array(list(itertools.accumulate(arr, ufunc)), dtype=dtype)
# 使用 assert_equal 断言函数比较预期结果和累积结果数组
assert_equal(exp, acc)
def test_signaling_nan_exceptions():
# 使用 assert_no_warnings 上下文管理器来测试 numpy 是否正确处理信号 NaN 异常
with assert_no_warnings():
# 创建一个包含信号 NaN 的 float32 类型的 numpy 数组
a = np.ndarray(shape=(), dtype='float32', buffer=b'\x00\xe0\xbf\xff')
# 使用 np.isnan 函数来检测是否存在 NaN 值
np.isnan(a)
@pytest.mark.parametrize("arr", [
# 为 test_outer_subclass_preserve 函数创建多个参数化的测试实例,用于测试外部子类的保留
np.arange(2),
np.matrix([0, 1]),
np.matrix([[0, 1], [2, 5]]),
])
def test_outer_subclass_preserve(arr):
# 为了 gh-8661
# 创建一个名为 foo 的 numpy 子类
class foo(np.ndarray): pass
# 调用 np.multiply.outer 函数生成外部乘积,并使用 foo 类型的视图
actual = np.multiply.outer(arr.view(foo), arr.view(foo))
# 使用 assert 断言函数比较实际结果的类名是否为 'foo'
assert actual.__class__.__name__ == 'foo'
def test_outer_bad_subclass():
# 创建 BadArr1 类作为 np.ndarray 的子类
class BadArr1(np.ndarray):
def __array_finalize__(self, obj):
# 外部调用会将形状重塑为 3 维,尝试进行错误的重塑
if self.ndim == 3:
self.shape = self.shape + (1,)
# 创建 BadArr2 类作为 np.ndarray 的子类
class BadArr2(np.ndarray):
def __array_finalize__(self, obj):
if isinstance(obj, BadArr2):
# 外部插入 1 大小的维度,干扰这些维度
if self.shape[-1] == 1:
self.shape = self.shape[::-1]
for cls in [BadArr1, BadArr2]:
# 使用给定的类(BadArr1 或 BadArr2)创建一个形状为 (2, 3) 的全 1 数组,并将其视图转换为指定类的对象
arr = np.ones((2, 3)).view(cls)
# 使用 assert_raises 来断言在执行下面的操作时会抛出 TypeError 异常
with assert_raises(TypeError) as a:
# 对第一个数组进行外积运算(第二个数组不会被重新形状)
np.add.outer(arr, [1, 2])
# 这个断言可以成功,因为我们只会看到重新形状的错误:
# 再次创建一个形状为 (2, 3) 的全 1 数组,并将其视图转换为指定类的对象
arr = np.ones((2, 3)).view(cls)
# 使用 np.add.outer 对数组 [1, 2] 和 arr 进行外积运算,并断言结果的类型是指定的类对象
assert type(np.add.outer([1, 2], arr)) is cls
def test_outer_exceeds_maxdims():
# 创建一个形状为 (1,)*33 的多维数组,所有元素为1,深度为33
deep = np.ones((1,) * 33)
# 使用 assert_raises 检查是否会引发 ValueError 异常
with assert_raises(ValueError):
# 对 deep 数组使用 np.add.outer 进行外积操作
np.add.outer(deep, deep)
def test_bad_legacy_ufunc_silent_errors():
# legacy ufuncs 无法报告错误,并且 NumPy 无法检查 GIL 是否已释放。
# 因此,NumPy 必须在释放 GIL 后检查,以确保覆盖所有情况。
# `np.power` 之前/现在使用了这种方式。
# 创建一个长度为3的浮点型数组 arr
arr = np.arange(3).astype(np.float64)
# 使用 pytest.raises 检查是否会引发 RuntimeError 异常,并且异常信息匹配指定的正则表达式
with pytest.raises(RuntimeError, match=r"How unexpected :\)!"):
# 调用 ncu_tests.always_error 函数,传入 arr 两次作为参数
ncu_tests.always_error(arr, arr)
with pytest.raises(RuntimeError, match=r"How unexpected :\)!"):
# 创建一个非连续的数组 non_contig,使得无法采用快速路径
non_contig = arr.repeat(20).reshape(-1, 6)[:, ::2]
# 调用 ncu_tests.always_error 函数,传入 non_contig 和 arr 作为参数
ncu_tests.always_error(non_contig, arr)
with pytest.raises(RuntimeError, match=r"How unexpected :\)!"):
# 调用 ncu_tests.always_error.outer 函数,传入 arr 两次作为参数
ncu_tests.always_error.outer(arr, arr)
with pytest.raises(RuntimeError, match=r"How unexpected :\)!"):
# 调用 ncu_tests.always_error.reduce 函数,传入 arr 作为参数
ncu_tests.always_error.reduce(arr)
with pytest.raises(RuntimeError, match=r"How unexpected :\)!"):
# 调用 ncu_tests.always_error.reduceat 函数,传入 arr 和 [0, 1] 作为参数
ncu_tests.always_error.reduceat(arr, [0, 1])
with pytest.raises(RuntimeError, match=r"How unexpected :\)!"):
# 调用 ncu_tests.always_error.accumulate 函数,传入 arr 作为参数
ncu_tests.always_error.accumulate(arr)
with pytest.raises(RuntimeError, match=r"How unexpected :\)!"):
# 调用 ncu_tests.always_error.at 函数,传入 arr、[0, 1, 2] 和 arr 作为参数
ncu_tests.always_error.at(arr, [0, 1, 2], arr)
@pytest.mark.parametrize('x1', [np.arange(3.0), [0.0, 1.0, 2.0]])
def test_bad_legacy_gufunc_silent_errors(x1):
# 验证 gufunc 循环中引发的异常是否正确传播。
# always_error_gufunc 的签名为 '(i),()->()'
with pytest.raises(RuntimeError, match=r"How unexpected :\)!"):
# 调用 ncu_tests.always_error_gufunc 函数,传入 x1 和 0.0 作为参数
ncu_tests.always_error_gufunc(x1, 0.0)
class TestAddDocstring:
@pytest.mark.skipif(sys.flags.optimize == 2, reason="Python running -OO")
@pytest.mark.skipif(IS_PYPY, reason="PyPy does not modify tp_doc")
def test_add_same_docstring(self):
# 测试属性(这些是 C 级别定义的)
# 调用 ncu.add_docstring 函数,传入 np.ndarray.flat 和 np.ndarray.flat.__doc__ 作为参数
ncu.add_docstring(np.ndarray.flat, np.ndarray.flat.__doc__)
# 典型函数:
def func():
"""docstring"""
return
# 调用 ncu.add_docstring 函数,传入 func 和 func.__doc__ 作为参数
ncu.add_docstring(func, func.__doc__)
@pytest.mark.skipif(sys.flags.optimize == 2, reason="Python running -OO")
def test_different_docstring_fails(self):
# 测试属性(这些是 C 级别定义的)
with assert_raises(RuntimeError):
# 调用 ncu.add_docstring 函数,传入 np.ndarray.flat 和 "different docstring" 作为参数
ncu.add_docstring(np.ndarray.flat, "different docstring")
# 典型函数:
def func():
"""docstring"""
return
with assert_raises(RuntimeError):
# 调用 ncu.add_docstring 函数,传入 func 和 "different docstring" 作为参数
ncu.add_docstring(func, "different docstring")
class TestAdd_newdoc_ufunc:
def test_ufunc_arg(self):
# 检查是否会引发 TypeError 异常,传入 2 和 "blah" 作为参数
assert_raises(TypeError, ncu._add_newdoc_ufunc, 2, "blah")
# 检查是否会引发 ValueError 异常,传入 np.add 和 "blah" 作为参数
assert_raises(ValueError, ncu._add_newdoc_ufunc, np.add, "blah")
# 定义一个测试方法 `test_string_arg(self)`
def test_string_arg(self):
# 使用 assert_raises 检查是否会引发 TypeError 异常
assert_raises(TypeError, ncu._add_newdoc_ufunc, np.add, 3)