2.2.2.4 广义ufuncs

ufunc

output = elementwise_function(input)

outputinput都可以只是一个数组元素。

广义ufunc

outputinput可以是有固定维度数的数组

例如,矩阵迹(对象线元素的sum):

In [ ]:

  1. input shape = (n, n)
  2. output shape = () i.e. scalar
  3. (n, n) -> ()

矩阵乘积:

In [ ]:

  1. input_1 shape = (m, n)
  2. input_2 shape = (n, p)
  3. output shape = (m, p)
  4. (m, n), (n, p) -> (m, p)
  • 这是广义ufunc的”签名“
  • g-ufunc发挥作用的维度是“核心维度”

Numpy中的状态

  • g-ufuncs已经在Numpy中…
  • 新的可以用PyUFunc_FromFuncAndDataAndSignature来创建
  • … 但是,除了测试外,我们不会配置公用的g-ufuncs,ATM

In [4]:

  1. import numpy.core.umath_tests as ut
  2. ut.matrix_multiply.signature

Out[4]:

  1. '(m,n),(n,p)->(m,p)'

In [5]:

  1. x = np.ones((10, 2, 4))
  2. y = np.ones((10, 4, 5))
  3. ut.matrix_multiply(x, y).shape

Out[5]:

  1. (10, 2, 5)
  • 后两个维度成为了核心维度,并且根据每个签名去修改
  • 否则,g-ufunc“按元素级”运行
  • 这种方式的矩阵乘法对一次在许多小矩阵是非常有用

广义ufunc循环

矩阵相乘 (m,n),(n,p) -> (m,p)

In [ ]:

  1. void gufunc_loop(void **args, int *dimensions, int *steps, void *data)
  2. {
  3. char *input_1 = (char*)args[0]; /* these are as previously */
  4. char *input_2 = (char*)args[1];
  5. char *output = (char*)args[2];
  6. int input_1_stride_m = steps[3]; /* strides for the core dimensions */
  7. int input_1_stride_n = steps[4]; /* are added after the non-core */
  8. int input_2_strides_n = steps[5]; /* steps */
  9. int input_2_strides_p = steps[6];
  10. int output_strides_n = steps[7];
  11. int output_strides_p = steps[8];
  12. int m = dimension[1]; /* core dimensions are added after */
  13. int n = dimension[2]; /* the main dimension; order as in */
  14. int p = dimension[3]; /* signature */
  15. int i;
  16. for (i = 0; i < dimensions[0]; ++i) {
  17. matmul_for_strided_matrices(input_1, input_2, output,
  18. strides for each array...);
  19. input_1 += steps[0];
  20. input_2 += steps[1];
  21. output += steps[2];
  22. }
  23. }