Generalized Universal Function API

通常需要不仅循环不仅是标量上的函数,而且还包括向量(或数组)上的函数。这个概念在NumPy中通过泛化通用函数(ufuncs)来实现。在常规ufuncs中,基本函数限于逐个元素操作,而广义版本(gufuncs)通过“子数组”操作支持“子数组”。Perl向量库PDL提供了类似的功能,其术语在下面被重复使用。

每个广义ufunc具有与其相关联的信息,其具有输入的“核心”维度以及输出的对应维度(元素方式ufunc具有零核心维度)。所有参数的核心维度列表称为ufunc的“签名”。例如,ufunc numpy.add具有定义两个标量输入和一个标量输出的签名(),()->()

Another example is the function inner1d(a, b) with a signature of (i),(i)->(). 这将沿着每个输入的最后一个轴应用内积,但保持其余索引完整。例如,其中a是形状(3, 5, N)b的形状为(5, N),将返回形状(3,5)底层基本函数称为3 * 5次。在签名中,我们为每个输入指定一个核心维度(i),对于输出指定零个核心维度(),因为它需要两个1-d数组,标量。通过使用相同的名称i,我们指定两个相应的尺寸应具有相同的大小。

超过核心尺寸的尺寸称为“环”尺寸。在上述示例中,这对应于(3, 5)

签名确定每个输入/输出数组的尺寸如何分割为核心和环路尺寸:

  1. 签名中的每个维度都与对应的传入数组的维度相匹配,从形状元组的结尾开始。这些是核心维度,它们必须存在于数组中,否则会引发错误。
  2. Core dimensions assigned to the same label in the signature (e.g. the i in inner1d‘s (i),(i)->()) must have exactly matching sizes, no broadcasting is performed.
  3. 核心维度从所有输入中删除,剩余维度一起广播,定义循环维度。
  4. 每个输出的形状由回路尺寸加上输出的核心尺寸确定

通常,输出中所有核心维度的大小将由输入数组中具有相同标签的核心维度的大小确定。这不是一个要求,并且可以在输出中首次定义标签时定义签名,但是当调用这样的函数时必须采取一些预防措施。An example would be the function euclidean_pdist(a), with signature (n,d)->(p), that given an array of n d-dimensional vectors, computes all unique pairwise Euclidean distances among them. 输出尺寸p因此必须等于n * (n t6> 1) / 2,但是呼叫者负责传递正确大小的输出数组。如果不能从传入的输入或输出数组中确定输出的核心维度的大小,则会出现错误。

注意:在NumPy 1.10.0之前,不太严格的检查:缺少核心维度是通过根据需要将1预先添加到形状中创建的,具有相同标签的核心维度一起广播,未确定的维度使用大小1创建。

Definitions

基本功能
每个ufunc由一个基本函数组成,该函数对数组参数的最小部分执行最基本的操作(例如,添加两个数字是添加两个数组的最基本的操作)。ufunc在数组的不同部分上多次应用基本函数。基本函数的输入/输出可以是向量;例如,inner1d的基本函数采用两个向量作为输入。
签名
签名是描述ufunc的基本函数的输入/输出维度的字符串。有关详细信息,请参阅下面的部分。
核心维度
基本函数的每个输入/输出的维数由其核心维度(零核心维度对应于标量输入/输出)来定义。核心维度映射到输入/输出数组的最后一个维度。
尺寸名称
维度名称表示签名中的核心维度。不同的维度可以共享名称,指示它们具有相同的大小。
维度索引
维度索引是表示维度名称的整数。它根据签名中每个名称第一次出现的顺序枚举维名称。

Details of Signature

签名定义了输入和输出变量的“核心”维度,从而也定义了维度的收缩。签名由以下格式的字符串表示:

  • 每个输入或输出数组的核心维度由括号中的维名称列表(i_1,...,i_N)表示;标量输入/输出由()表示。代替i_1i_2等,可以使用任何有效的Python变量名称。
  • 不同参数的维列表由","“分隔。输入/输出参数由"->"分隔。
  • 如果在多个位置使用相同的维度名称,则会强制使用相同尺寸的相应维度。

签名的形式语法如下:

<Signature>            ::= <Input arguments> "->" <Output arguments>
<Input arguments>      ::= <Argument list>
<Output arguments>     ::= <Argument list>
<Argument list>        ::= nil | <Argument> | <Argument> "," <Argument list>
<Argument>             ::= "(" <Core dimension list> ")"
<Core dimension list>  ::= nil | <Dimension name> |
                           <Dimension name> "," <Core dimension list>
<Dimension name>       ::= valid Python variable name

笔记:

  1. 所有报价都是为了清楚。
  2. 共享相同名称的核心维度必须具有完全相同的大小。每个维度名称通常对应于基本函数实现中的一个循环级。
  3. 空白被忽略。

这里有一些签名的例子:

(),()->()  
inner1d (i),(i)->()  
sum1d (i)->()  
dot2d (m,n),(n,p)->(m,p) 矩阵乘法
outer_inner (i,t),(j,t)->(i,j) 内部在最后一维,外部在第二到最后,和循环/广播在休息。

C-API for implementing Elementary Functions

当前接口保持不变,并且PyUFunc_FromFuncAndData仍然可以用于实现(专用)ufunc,由标量基本函数组成。

可以使用PyUFunc_FromFuncAndDataAndSignature来声明一个更通用的ufunc。参数列表与PyUFunc_FromFuncAndData相同,还有一个参数将签名指定为C字符串。

此外,回调函数的类型与之前相同,void (* foo)(char ** args, intp * dimensions, intp * steps, void * func) t9>当调用时,args是包含所有输入/输出参数的数据的长度nargs的列表。对于标量基本函数,steps也具有长度nargs,表示用于参数的步幅。dimensions是指向定义要循环的轴的大小的单个整数的指针。

对于非平凡签名,dimensions也将包含核心维度的大小,从第二个条目开始。每个唯一维度名称仅提供一个大小,并且大小根据签名中维度名称的第一次出现而给出。

steps的第一个nargs元素保持与标量ufuncs相同。以下元素按顺序包含所有参数的所有核心维度的步长。

例如,考虑具有签名(i,j),(i)->()的ufunc。In this case, args will contain three pointers to the data of the input/output arrays a, b, c. Furthermore, dimensions will be [N, I, J] to define the size of N of the loop and the sizes I and J for the core dimensions i and j. 最后,steps将是[a_N, b_N, c_N, a_i, t6> a_j, b_i],包含所有必要的步幅。