使用来自 Cython 的 Scipy cython_blas 接口不适用于向量 Mx1 1xN
Using the Scipy cython_blas interface from Cython not working on vectors Mx1 1xN
这里必须处理类似的问题: but is different because I'm using the actual code in the SciPy example here _test_dgemm
: https://github.com/scipy/scipy/blob/master/scipy/linalg/cython_blas.pyx 非常快(当输入输出矩阵时比 numpy.dot
快 5 倍,否则快 20 倍)。如果传递 Mx1 1xN 向量,它不会产生任何结果。它产生与传递矩阵的 numpy.dot
相同的值。我已经最小化了代码,因为为了清楚起见没有发布任何答案。这是 dgemm.pyx.
:
import numpy as np
cimport numpy as np
from scipy.linalg.cython_blas cimport dgemm
from cython cimport boundscheck
@boundscheck(False)
cpdef int fast_dgemm(double[:,::1] a, double[:,::1] b, double[:,::1] c, double alpha=1.0, double beta=0.0) nogil except -1:
cdef:
char *transa = 'n'
char *transb = 'n'
int m, n, k, lda, ldb, ldc
double *a0=&a[0,0]
double *b0=&b[0,0]
double *c0=&c[0,0]
ldb = (&a[1,0]) - a0 if a.shape[0] > 1 else 1
lda = (&b[1,0]) - b0 if b.shape[0] > 1 else 1
k = b.shape[0]
if k != a.shape[1]:
with gil:
raise ValueError("Shape mismatch in input arrays.")
m = b.shape[1]
n = a.shape[0]
if n != c.shape[0] or m != c.shape[1]:
with gil:
raise ValueError("Output array does not have the correct shape.")
ldc = (&c[1,0]) - c0 if c.shape[0] > 1 else 1
dgemm(transa, transb, &m, &n, &k, &alpha, b0, &lda, a0,
&ldb, &beta, c0, &ldc)
return 0
这是一个示例测试脚本:
import numpy as np;
a=np.random.randn(1000);
b=np.random.randn(1000);
a.resize(len(a),1);
a=np.array(a, order='c');
b.resize(1,len(b));
b=np.array(b, order='c');
c = np.empty((a.shape[0],b.shape[1]), float, order='c');
from dgemm import _test_dgemm;
_test_dgemm(a,b,c);
如果你想在 Windows 上用 Python 3.5 x64 玩它,这里是 setup.py
通过命令提示符输入 python setup.py build_ext --inplace --compiler=msvc
[=22 来构建它=]
from Cython.Distutils import build_ext
import numpy as np
import os
try:
from setuptools import setup
from setuptools import Extension
except ImportError:
from distutils.core import setup
from distutils.extension import Extension
module = 'dgemm'
ext_modules = [Extension(module, sources=[module + '.pyx'],
include_dirs=['C://Program Files (x86)//Windows Kits//10//Include//10.0.10240.0//ucrt','C://Program Files (x86)//Microsoft Visual Studio 14.0//VC//include','C://Program Files (x86)//Windows Kits//8.1//Include//shared'],
library_dirs=['C://Program Files (x86)//Windows Kits//8.1//bin//x64', 'C://Windows//System32', 'C://Program Files (x86)//Microsoft Visual Studio 14.0//VC//lib//amd64', 'C://Program Files (x86)//Windows Kits//8.1//Lib//winv6.3//um//x64', 'C://Program Files (x86)//Windows Kits//10//Lib//10.0.10240.0//ucrt//x64'],
extra_compile_args=['/Ot', '/favor:INTEL64', '/EHsc', '/GA'],
language='c++')]
setup(
name = module,
ext_modules = ext_modules,
cmdclass = {'build_ext': build_ext},
include_dirs = [np.get_include(), os.path.join(np.get_include(), 'numpy')]
)
非常感谢任何帮助!
如果我没看错,你可以尝试使用 fortran-routines 处理带有 c-memory-layout 的数组。
就算你明明知道,我还是想先详细说一下行主序(c-memory-layout)和列主序(fortran-memory-layout),在为了推断出我的答案。
因此,如果我们有一个 2x3
矩阵(即 2 行和 3 列)A
,并将其存储在某个连续的内存中,我们将得到:
row-major-order(A) = A11, A12, A13, A21, A22, A23
col-major-order(A) = A11, A21, A12, A22, A13, A33
这意味着如果我们得到一个连续的记忆,它表示一个行主序的矩阵,并将其解释为一个列主序的矩阵,我们将得到一个完全不同的矩阵!
但是,我们看一下转置矩阵A^t
我们可以很容易的看出:
row-major-order(A) = col-major-order(A^t)
col-major-order(A) = row-major-order(A^t)
这意味着,如果我们想得到行优先顺序的矩阵 C
作为结果,blas 例程应该以列优先顺序写入转置矩阵 C
(毕竟我们不能改变)到这个记忆中。但是,C^t=(AB)^t=B^t*A^t
和 B^t
和 A^t
是按列优先顺序重新解释的原始矩阵。
现在,设A
为n x k
-矩阵,B
为k x m
-矩阵,dgemm例程的调用应如下所示:
dgemm(transa, transb, &m, &n, &k, &alpha, b0, &m, a0, &k, &beta, c0, &m)
如您所见,您在代码中切换了一些 n
和 m
。
这里必须处理类似的问题:_test_dgemm
: https://github.com/scipy/scipy/blob/master/scipy/linalg/cython_blas.pyx 非常快(当输入输出矩阵时比 numpy.dot
快 5 倍,否则快 20 倍)。如果传递 Mx1 1xN 向量,它不会产生任何结果。它产生与传递矩阵的 numpy.dot
相同的值。我已经最小化了代码,因为为了清楚起见没有发布任何答案。这是 dgemm.pyx.
:
import numpy as np
cimport numpy as np
from scipy.linalg.cython_blas cimport dgemm
from cython cimport boundscheck
@boundscheck(False)
cpdef int fast_dgemm(double[:,::1] a, double[:,::1] b, double[:,::1] c, double alpha=1.0, double beta=0.0) nogil except -1:
cdef:
char *transa = 'n'
char *transb = 'n'
int m, n, k, lda, ldb, ldc
double *a0=&a[0,0]
double *b0=&b[0,0]
double *c0=&c[0,0]
ldb = (&a[1,0]) - a0 if a.shape[0] > 1 else 1
lda = (&b[1,0]) - b0 if b.shape[0] > 1 else 1
k = b.shape[0]
if k != a.shape[1]:
with gil:
raise ValueError("Shape mismatch in input arrays.")
m = b.shape[1]
n = a.shape[0]
if n != c.shape[0] or m != c.shape[1]:
with gil:
raise ValueError("Output array does not have the correct shape.")
ldc = (&c[1,0]) - c0 if c.shape[0] > 1 else 1
dgemm(transa, transb, &m, &n, &k, &alpha, b0, &lda, a0,
&ldb, &beta, c0, &ldc)
return 0
这是一个示例测试脚本:
import numpy as np;
a=np.random.randn(1000);
b=np.random.randn(1000);
a.resize(len(a),1);
a=np.array(a, order='c');
b.resize(1,len(b));
b=np.array(b, order='c');
c = np.empty((a.shape[0],b.shape[1]), float, order='c');
from dgemm import _test_dgemm;
_test_dgemm(a,b,c);
如果你想在 Windows 上用 Python 3.5 x64 玩它,这里是 setup.py
通过命令提示符输入 python setup.py build_ext --inplace --compiler=msvc
[=22 来构建它=]
from Cython.Distutils import build_ext
import numpy as np
import os
try:
from setuptools import setup
from setuptools import Extension
except ImportError:
from distutils.core import setup
from distutils.extension import Extension
module = 'dgemm'
ext_modules = [Extension(module, sources=[module + '.pyx'],
include_dirs=['C://Program Files (x86)//Windows Kits//10//Include//10.0.10240.0//ucrt','C://Program Files (x86)//Microsoft Visual Studio 14.0//VC//include','C://Program Files (x86)//Windows Kits//8.1//Include//shared'],
library_dirs=['C://Program Files (x86)//Windows Kits//8.1//bin//x64', 'C://Windows//System32', 'C://Program Files (x86)//Microsoft Visual Studio 14.0//VC//lib//amd64', 'C://Program Files (x86)//Windows Kits//8.1//Lib//winv6.3//um//x64', 'C://Program Files (x86)//Windows Kits//10//Lib//10.0.10240.0//ucrt//x64'],
extra_compile_args=['/Ot', '/favor:INTEL64', '/EHsc', '/GA'],
language='c++')]
setup(
name = module,
ext_modules = ext_modules,
cmdclass = {'build_ext': build_ext},
include_dirs = [np.get_include(), os.path.join(np.get_include(), 'numpy')]
)
非常感谢任何帮助!
如果我没看错,你可以尝试使用 fortran-routines 处理带有 c-memory-layout 的数组。
就算你明明知道,我还是想先详细说一下行主序(c-memory-layout)和列主序(fortran-memory-layout),在为了推断出我的答案。
因此,如果我们有一个 2x3
矩阵(即 2 行和 3 列)A
,并将其存储在某个连续的内存中,我们将得到:
row-major-order(A) = A11, A12, A13, A21, A22, A23
col-major-order(A) = A11, A21, A12, A22, A13, A33
这意味着如果我们得到一个连续的记忆,它表示一个行主序的矩阵,并将其解释为一个列主序的矩阵,我们将得到一个完全不同的矩阵!
但是,我们看一下转置矩阵A^t
我们可以很容易的看出:
row-major-order(A) = col-major-order(A^t)
col-major-order(A) = row-major-order(A^t)
这意味着,如果我们想得到行优先顺序的矩阵 C
作为结果,blas 例程应该以列优先顺序写入转置矩阵 C
(毕竟我们不能改变)到这个记忆中。但是,C^t=(AB)^t=B^t*A^t
和 B^t
和 A^t
是按列优先顺序重新解释的原始矩阵。
现在,设A
为n x k
-矩阵,B
为k x m
-矩阵,dgemm例程的调用应如下所示:
dgemm(transa, transb, &m, &n, &k, &alpha, b0, &m, a0, &k, &beta, c0, &m)
如您所见,您在代码中切换了一些 n
和 m
。