在Cython中调用点积和线性代数运算?

时间:2013-04-19 21:56:51

标签: python numpy scipy cython blas

我正在尝试使用点积,矩阵求逆和其他基本的线性代数运算,这些运算可以在Cython的numpy中找到。函数如numpy.linalg.inv(反转),numpy.dot(点积),X.t(矩阵/数组的转置)。从Cython函数调用numpy.*会产生很大的开销,而其余的函数都是用Cython编写的,所以我想避免这种情况。

如果我假设用户安装了numpy,是否可以采用以下方法:

#include "numpy/npy_math.h"

作为extern,并调用这些函数?或者直接调用BLAS(或者numpy调用这些核心操作的任何东西)?

举一个例子,想象一下你在Cython中有一个函数做了很多事情,最后需要进行涉及点积和矩阵求逆的计算:

cdef myfunc(...):
  # ... do many things faster than Python could
  # ...
  # compute one value using dot products and inv
  # without using 
  #   import numpy as np 
  #   np.*
  val = gammaln(sum(v)) - sum(gammaln(v)) + dot((v - 1).T, log(x).T)

怎么办呢?如果有一个库已经在Cython中实现了这些,我也可以使用它,但没有找到任何东西。即使这些过程没有直接优化BLAS,没有从Cython调用numpy Python模块的开销仍然会使整体事情变得更快。

我想打电话的示例函数:

  • 点积(np.dot
  • 矩阵求逆(np.linalg.inv
  • 矩阵乘法
  • 进行转置(相当于numpy中的x.T
  • gammaln函数(如scipy.gammaln等效函数,应该在C中可用)

我在numpy邮件列表(https://groups.google.com/forum/?fromgroups=#!topic/cython-users/XZjMVSIQnTE)上发现,如果你在大型矩阵上调用这些函数,那么从Cython中做这件事是没有意义的,因为从numpy调用它只会导致大多数在numpy调用的优化C代码中花费的时间。但是,在我的情况下,我在小矩阵上有很多调用这些线性代数运算的东西 - 在这种情况下,从Cython重复进入numpy并返回Cython引入的开销将远远超过实际从BLAS计算操作所花费的时间。因此,我想保留C / Cython级别的所有内容以进行这些简单的操作,而不是通过python。

我不想通过GSL,因为这增加了另一种依赖性,因为不清楚GSL是否得到了积极维护。由于我假设代码的用户已经安装了scipy / numpy,我可以放心地假设他们拥有与这些库一起使用的所有相关C代码,所以我只想能够使用该代码并调用它来自Cython。

编辑:我找到了一个在Cython(https://github.com/tokyo/tokyo)中包装BLAS的库,它很接近但不是我想要的。我想直接调用numpy / scipy C函数(我假设用户安装了这些函数。)

3 个答案:

答案 0 :(得分:22)

调用与Scipy捆绑在一起的BLAS是“相当”直截了当的,这是调用DGEMM来计算矩阵乘法的一个例子:https://gist.github.com/pv/5437087注意BLAS和LAPACK期望所有数组都是Fortran连续的(模数为lda / b / c参数),因此正确运行所需的order="F"double[::1,:]

通过在单位矩阵上应用LAPACK函数dgesv,可以类似地完成计算逆。有关签名,请参阅here。所有这些都需要降低到相当低级别的编码,你需要自己分配临时工作数组等等.---但是这些可以封装到你自己的便利函数中,或者只是通过替换来重用tokyo中的代码具有函数指针的lib_*函数以上述方式从Scipy获得。

如果您使用Cython的memoryview语法(double[::1,:]),则转置与往常一样x.T。或者,您可以通过编写自己的函数来计算转置,该函数在对角线上交换数组的元素。 Numpy实际上并不包含此操作,x.T仅更改数组的步幅而不会移动数据。

可能有可能重写tokyo模块以使用Scipy导出的BLAS / LAPACK并将其捆绑在scipy.linalg中,这样您就可以from scipy.linalg.blas cimport dgemm。如果有人想要了解它,我会接受Pull requests


正如您所看到的,这一切都归结为传递函数指针。如上所述,Cython确实提供了自己的协议来交换函数指针。举个例子,考虑from scipy.spatial import qhull; print(qhull.__pyx_capi__) ---这些函数可以通过Cython中的from scipy.spatial.qhull cimport XXXX访问(虽然它们是私有的,所以不要这样做。)

但是,目前,scipy.special不提供此C-API。然而,实际上提供它是非常简单的,因为scipy.special中的接口模块是用Cython编写的。

我认为目前没有任何理智且可移植的方式来访问函数,为gamln进行繁重的工作,(尽管你可以窥探UFunc对象,但这不是一个理智的解决方案: ),所以目前最好从scipy.special中获取源代码的相关部分并将其与您的项目捆绑在一起,或者使用例如GSL。

答案 1 :(得分:1)

如果您接受使用GSL,最简单的方法可能是使用此GSL-> cython接口https://github.com/twiecki/CythonGSL并从那里调用BLAS(参见示例https://github.com/twiecki/CythonGSL/blob/master/examples/blas2.pyx)。它还应该关注Fortran vs C的排序。 新的GSL功能并不多,但您可以放心地假设它是主动维护的。与东京相比,CythonGSL更加完整;例如,它具有numpy中不存在的对称矩阵产品。

答案 2 :(得分:0)

由于我刚刚遇到同样的问题,并且写了一些额外的功能,我会在这里包含它们以防其他人发现它们有用。我编写了一些矩阵乘法,并调用LAPACK函数进行矩阵求逆,行列式和cholesky分解。但你应该考虑尝试在任何循环之外做线性代数的东西,如果你有的话,就像我做here一样。顺便说一下,如果你有建议,这里的决定性函数并不是很有效。此外,请注意,我不会检查输入是否符合要求。

race