numba-vectorize

参考文档: https://apachecn.github.io/numba-doc-zh/#/docs/17

vectorize装饰器:

允许python函数的标量入参使用 numpy 的 ufuncs。

import numba as nb


@nb.vectorize([nb.int32(nb.int32, nb.int32)], nopython=True)
def f(x, y):
    return x + y


a = [1, 2, 3]
b = [4, 5, 6]
print("a + b: ", a + b)  # a + b:  [1, 2, 3, 4, 5, 6]
print("f(a, b): ", f(a, b))  # f(a, b):  [5 7 9]
print("type(f(a, b)): ", type(f(a, b)))  # type(f(a, b)):  <class 'numpy.ndarray'>

c = [[1, 2, 3], [4, 5, 6]]
d = [[7, 8, 9], [10, 11, 12]]
print("f(c,d): ", f(c, d))  # [[8 10 12] [14 16 18]]

vectorize支持多个函数签名,多如果用多个函数签名的时候注意顺序,比如单精度浮点数在双精度浮点数之前。

@vectorize([int32(int32, int32),
            int64(int64, int64),
            float32(float32, float32),
            float64(float64, float64)])

使用vectorize装饰之后,函数会自动获得numpy ufuncs的其他功能,比如缩小,累计或广播。ufunc的标准功能

print(
    "f.reduce(c) axis=0: %s, axis=1: %s" % (f.reduce(c), f.reduce(c, axis=1))
)  # f.reduce(c) axis=0: [5 7 9], axis=1: [ 6 15]

guvectorize 装饰器

vectorize 是一次写一个元素的 ufunc, guvectorize 是允许编写输入任意数量数组元素的 ufunc,并获取和返回不同维度的数组。典型的例子是中值或卷积滤波器。
guvectorize 不返回结果,是将其作为函数参数,必须有函数填充。

@nb.guvectorize([(nb.int32[:], nb.int32, nb.int32[:])], "(n), ()->(n)", nopython=True)
def g(x, y, r):
    for i in range(x.shape[0]):
        r[i] = x[i] + y


a = [1, 2, 3, 4]
b = 10
print("g(a, b): ", g(a, b))  # g(a, b):  [11 12 13 14]

底层的 python 代码是将一个给定的标量 y 添加到一维数组的所有元素中。
"(n),()->(n)" 表示输入一个n个元素的一维数组和一个标量(用符号()表示)并返回n个元素的一维数组。
1D 数组类型也可以接收标量参数,在上边例子中,第二个参数也可以声明为int[:] (), 在这种情况下,该值必须由 y[0] 读取。

我们可以检查一下能否支持不同维度的数组

# 2d + int
c = [[1, 2, 3], [4, 5, 6]]
print("g(c, 10): ", g(c, 10))  # g(c, 10):  [[11 12 13] [14 15 16]]

# 2d + 1d
d = [10, 20]
print("g(c, d): ", g(c, d))  # g(c, d):  [[11 12 13] [24 25 26]]

动态通用功能

如果给装饰器 vectorize 没有传递任何签名,python 函数将会构建动态通用函数或者DUDunc。

@nb.vectorize()
def d(x, y):
    return x + y


print("d(1, 2)", d(1, 2))  # d(1, 2) 3
print(d.types)  # ['ll->q']

print("d(1, 2.0)", d(1, 2.0))  # d(1, 2) 3.0
print(d.types)  # ['ll->q', 'ld->d']

print("d([1, 2, 3], 10): ", d([1, 2, 3], 10))  # d([1, 2, 3], 10):  [11 12 13]
print(d.types)  # ['ll->q', 'ld->d']

在这种情况下,调用的顺序很重要,比如先传入浮点参数,那么任何带有整形的参数都会被转换成双精度浮点值。如: 先调用 d(1.0, 2.0) 得到3.0,再调用 d(1, 2) 也会得到3.0。

完整代码
import numba as nb


@nb.vectorize(
    [nb.int32(nb.int32, nb.int32), nb.float64(nb.float64, nb.float64)], nopython=True
)
def f(x, y):
    return x + y


a = [1.0, 2.0, 3.0]
b = [4, 5, 6]
# b = [4.0, 5.0, 6.0]
print("a + b: ", a + b)  # a + b:  [1, 2, 3, 4, 5, 6]
print("f(a, b): ", f(a, b))  # f(a, b):  [5 7 9]
print("type(f(a, b)): ", type(f(a, b)))  # type(f(a, b)):  <class 'numpy.ndarray'>

c = [[1, 2, 3], [4, 5, 6]]
d = [[7, 8, 9], [10, 11, 12]]
print("f(c,d): ", f(c, d))  # [[8 10 12] [14 16 18]]

print(
    "f.reduce(c) axis=0: %s, axis=1: %s" % (f.reduce(c), f.reduce(c, axis=1))
)  # f.reduce(c) axis=0: [5 7 9], axis=1: [ 6 15]

print("-" * 20)


@nb.guvectorize([(nb.int32[:], nb.int32, nb.int32[:])], "(n), ()->(n)", nopython=True)
def g(x, y, r):
    for i in range(x.shape[0]):
        r[i] = x[i] + y


a = [1, 2, 3, 4]
b = 10
print("g(a, b): ", g(a, b))  # g(a, b):  [11 12 13 14]

# 2d + int
c = [[1, 2, 3], [4, 5, 6]]
print("g(c, 10): ", g(c, 10))  # g(c, 10):  [[11 12 13] [14 15 16]]

# 2d + 1d
d = [10, 20]
print("g(c, d): ", g(c, d))  # g(c, d):  [[11 12 13] [24 25 26]]

print("-" * 20)


@nb.vectorize()
def d(x, y):
    return x + y


print("d(1, 2)", d(1, 2))  # d(1, 2) 3
print(d.types)  # ['ll->q']

print("d(1, 2.0)", d(1, 2.0))  # d(1, 2) 3.0
print(d.types)  # ['ll->q', 'ld->d']

print("d([1, 2, 3], 10): ", d([1, 2, 3], 10))  # d([1, 2, 3], 10):  [11 12 13]
print(d.types)  # ['ll->q', 'ld->d']

posted @ 2024-05-12 12:52  一枚码农  阅读(32)  评论(0编辑  收藏  举报