Python中快速、小且重复的矩阵乘法

Python中快速、小且重复的矩阵乘法,python,optimization,numpy,cython,scientific-computing,Python,Optimization,Numpy,Cython,Scientific Computing,我正在寻找一种使用Python/Cython/Numpy将许多4x4矩阵快速相乘的方法,有人能给我一些建议吗 为了显示我当前的尝试,我有一个需要计算的算法 A_1 * A_2 * A_3 * ... * A_N 凡 A_i != A_j Python中的一个示例: means = array([0.0, 0.0, 34.28, 0.0, 0.0, 3.4]) stds = array([ 4.839339, 4.839339, 4.092728, 0.141421, 0.141421, 0

我正在寻找一种使用Python/Cython/Numpy将许多4x4矩阵快速相乘的方法,有人能给我一些建议吗

为了显示我当前的尝试,我有一个需要计算的算法

A_1 * A_2 * A_3 * ... * A_N 

A_i != A_j
Python中的一个示例:

means = array([0.0, 0.0, 34.28, 0.0, 0.0, 3.4])
stds = array([ 4.839339, 4.839339, 4.092728, 0.141421, 0.141421, 0.141421])

def fn():
    steps = means+stds*numpy.random.normal(size=(60,6))
    A = identity(4)
    for step in steps:
        A = dot(A, transform_step_to_4by4(step))
%timeit fn()

1000 loops, best of 3: 570 us per loop

在Cython/Numpy中实现该算法比使用Eigen/C++进行所有优化的等效代码慢大约100倍。不过,我真的不想使用C++。

< P>我不能把速度和你的方法比较,因为我不知道你如何把你的< >(60,6)< /C>数组变成一个<代码>(4/4)< />代码,但是这是用序列的点:

A = np.arange(16).reshape(4,4)
B = np.arange(4,20).reshape(4,4)
C = np.arange(8,24).reshape(4,4)

arrs = [A, B, C]

P = reduce(np.dot, arrs)
这相当于,但应该比:

P = np.identity(4, dtype = arrs[0].dtype)
for arr in arrs:
    P = np.dot(P, arr)
定时测试:

In [52]: def byloop(arrs):
   ....:     P = np.identity(4)
   ....:     for arr in arrs:
   ....:         P = np.dot(P, arr)
   ....:     return P
   ....: 

In [53]: def byreduce(arrs):
   ....:     return reduce(np.dot, arrs)
   ....: 

In [54]: byloop(arrs)
Out[54]: 
array([[  5104,   5460,   5816,   6172],
       [ 15728,  16820,  17912,  19004],
       [ 26352,  28180,  30008,  31836],
       [ 36976,  39540,  42104,  44668]])

In [55]: byreduce(arrs)
Out[55]: 
array([[ 5104,  5460,  5816,  6172],
       [15728, 16820, 17912, 19004],
       [26352, 28180, 30008, 31836],
       [36976, 39540, 42104, 44668]])
其中
len(arrs)=1000

In [56]: timeit byloop(arrs)
1000 loops, best of 3: 1.26 ms per loop

In [57]: timeit byreduce(arrs)
1000 loops, best of 3: 656 us per loop

如果您必须进行Python函数调用以生成要乘法的每个矩阵,那么您基本上是在性能方面。但是,如果您可以将
transform\u step\u矢量化为\u 4by4
函数,并让它返回一个具有形状
(n,4,4)
的数组,那么您可以使用
矩阵乘法来节省一些时间:

import numpy as np
from numpy.core.umath_tests import matrix_multiply

matrices = np.random.rand(64, 4, 4) - 0.5

def mat_loop_reduce(m):
    ret = m[0]
    for x in m[1:]:
        ret = np.dot(ret, x)
    return ret

def mat_reduce(m):
    while len(m) % 2 == 0:
        m = matrix_multiply(m[::2], m[1::2])
    return mat_loop_reduce(m)

In [2]: %timeit mat_reduce(matrices)
1000 loops, best of 3: 287 us per loop

In [3]: %timeit mat_loop_reduce(matrices)
1000 loops, best of 3: 721 us per loop

In [4]: np.allclose(mat_loop_reduce(matrices), mat_reduce(matrices))
Out[4]: True
现在有了log(n)Python调用,而不是n,这有利于2.5倍的加速,对于n=1024,这将接近10倍。显然,
matrix\u multiply
是一个ufunc,因此有一个
.reduce
方法,它允许您的代码在Python中不运行循环。但我无法让它运行,不断出现一个神秘的错误:

In [7]: matrix_multiply.reduce(matrices)
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
RuntimeError: Reduction not defined on ufunc with signature
[7]中的
:矩阵_乘.减(矩阵)
------------------------------------------------------------
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
RuntimeError:未在带有签名的ufunc上定义缩减

对于如此小的矩阵,避免使用BLAS可能是有利的。例如,有一些小的矩阵乘法库(还有更多)


用f2py或cython包装它们可能是可行的,或者——用cython或fortran/f2py自己包装可能更容易

用C编写插件?Python有一个非常简单的C API。它应该做什么<代码>将步骤转换为步骤。
@askewchan,可以说这是从6D向量到4x4矩阵的坐标转换。@Mike如果你能将它应用到列表中的所有数组,然后你可以使用我的答案中的reduce,它应该会加快速度。优化一个你不知道最里面的迭代会发生什么的循环是一个暗中摸索。
将步骤转换为4by4的功能是什么?“足够说”是不够的,除非你至少说它本身是否可以矢量化。byloop做3,通过减少2个点积。它可能会更快,但它并不像你的计时让它看起来那么快。而且类型不一样,标识需要花费不必要的时间(ARR的长度为3,这些时间加起来很多)。感谢@seberg Timing发布了更长的列表。仍然是数据类型问题(这不是小问题),但是考虑到纯python中没有太多其他东西可以说明…@seberg,数据类型不匹配仅仅来自默认的
np.identity
返回一个浮点值。编辑:在帖子中修复了它。@seberg-hm,我明白了。您认为在当前的实现中,
np.identity
采用
dtype=arrs[0].dtype
,这仍然是正确的吗?