Matlab如何转换稀疏矩阵?
我一直在想这个问题,但找不到一个参考:既然稀疏矩阵是以CSC(compressed sparse column,压缩稀疏列)格式存储的,那么Matlab是如何快速转换稀疏矩阵的 还验证了稀疏矩阵变换的效率: 要做到这一点(逐行访问),您可以转置矩阵,对列执行操作,然后重新传输结果……转置矩阵所需的时间可以忽略不计 后续行动(根据@Mikhail的建议进行修改): 我同意@Roger和@Milhail的观点,即设置一个标志就其接口而言足以用于许多操作,例如BLAS或稀疏BLAS操作。但在我看来,Matlab做的是“实际的”换位。例如,我有一个大小为m*n=7984*12411的稀疏矩阵X,我想缩放每一列和每一行:Matlab如何转换稀疏矩阵?,matlab,bsxfun,Matlab,Bsxfun,我一直在想这个问题,但找不到一个参考:既然稀疏矩阵是以CSC(compressed sparse column,压缩稀疏列)格式存储的,那么Matlab是如何快速转换稀疏矩阵的 还验证了稀疏矩阵变换的效率: 要做到这一点(逐行访问),您可以转置矩阵,对列执行操作,然后重新传输结果……转置矩阵所需的时间可以忽略不计 后续行动(根据@Mikhail的建议进行修改): 我同意@Roger和@Milhail的观点,即设置一个标志就其接口而言足以用于许多操作,例如BLAS或稀疏BLAS操作。但在我看来,M
% scaling each column
t = 0;
for i = 1 : 1000
A = X; t0 = tic;
A = bsxfun(@times, A, rand(1,n));
t = t + toc(t0);
end
t=0.023636秒
% scaling each row
t = 0;
for i = 1 : 1000
A = X; t0 = tic;
A = bsxfun(@times, A, rand(m,1));
t = t + toc(t0);
end
t=138.3586秒
% scaling each row by transposing X and transforming back
t = 0;
for i = 1 : 1000
A = X; t0 = tic;
A = A'; A = bsxfun(@times, A, rand(1,m)); A = A';
t = t + toc(t0);
end
t=19.5433秒
这意味着逐列访问要比逐行访问快。这是有意义的,因为稀疏矩阵是逐列存储的。因此,X'列缩放速度快的唯一原因应该是X实际上被转置到X'而不是设置标志
此外,如果每个稀疏矩阵都以CSC格式存储,那么简单地设置一个标志不能使X'成为CSC格式
有什么评论吗?提前谢谢。我同意罗杰·罗兰在评论中提到的。为了支持这一建议,您可以检查BLAS接口中的一些函数,MATLAB将其用于矩阵运算。我不确定它使用的是什么实现,但既然他们使用Intel IPP进行图像处理,我想他们也可以使用Intel MKL来加快矩阵运算
这是函数的文档,它以CSC格式为稀疏矩阵求解线性方程组。请注意
transa
输入标志,它明确定义了提供的矩阵是否应被视为转置。经过一周的探索,我对转置矩阵的内部机制的猜测是排序
假设A
是稀疏矩阵
[I, J, S] = find(A);
[sorted_I, idx] = sort(I);
J = J(idx);
S = S(idx);
B = sparse(J, sorted_I, S);
然后B
是A
的转置
上述实现的效率大约是我的机器上Matlab内置的
转置
的一半。考虑到Matlab的内置函数是多线程的,我的猜测可能是合理的。我意识到我玩游戏有点晚了,但我想我可以帮助阐明这个问题。转换稀疏矩阵实际上是一项简单的任务,可以在与输入矩阵中非零元素数量成比例的时间内完成。假设A是以CSC格式存储的m x n矩阵,即A由三个数组定义:
for j = 1,...,n
for k = pcolA(j),...,pcolA(j + 1) - 1
prowB(tmp(prowA(k))) = j
elemsB(tmp(prowA(k))) = elemsA(k)
tmp(prowA(k)) = tmp(prowA(k)) + 1
end
end
< P >数组TMP在添加新元素时被用来索引PROWB和ELSB,然后相应地更新,将其完全写出来,我们可以在C++中编写一个MEX文件,实现转置算法:
#include "mex.h"
#include <vector>
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
// check input output
if (nrhs != 1)
mexErrMsgTxt("One input argument required");
if (nlhs > 1)
mexErrMsgTxt("Too many output arguments");
// get input sparse matrix A
if (mxIsEmpty(prhs[0])) { // is A empty?
plhs[0] = mxCreateSparse(0, 0, 0, mxREAL);
return;
}
if (!mxIsSparse(prhs[0]) || mxIsComplex(prhs[0])) // is A real and sparse?
mexErrMsgTxt("Input matrix must be real and sparse");
double* A = mxGetPr(prhs[0]); // real vector for A
mwIndex* prowA = mxGetIr(prhs[0]); // row indices for elements of A
mwIndex* pcolindexA = mxGetJc(prhs[0]); // index into the columns
mwSize M = mxGetM(prhs[0]); // number of rows in A
mwSize N = mxGetN(prhs[0]); // number of columns in A
// allocate memory for A^T
plhs[0] = mxCreateSparse(N, M, pcolindexA[N], mxREAL);
double* outAt = mxGetPr(plhs[0]);
mwIndex* outprowAt = mxGetIr(plhs[0]);
mwIndex* outpcolindexAt = mxGetJc(plhs[0]);
// temp[j + 1] stores the number of nonzero elements in row j of A
std::vector<mwSize> temp(M + 1, 0);
for(mwIndex i = 0; i != N; ++i) {
for(mwIndex j = pcolindexA[i]; j < pcolindexA[i + 1]; ++j)
++temp[prowA[j] + 1];
}
outpcolindexAt[0] = 0;
for(mwIndex i = 1; i <= M; ++i) {
outpcolindexAt[i] = outpcolindexAt[i - 1] + temp[i];
temp[i] = outpcolindexAt[i];
}
for(mwIndex i = 0; i != N; ++i) {
for(mwIndex j = pcolindexA[i]; j < pcolindexA[i + 1]; ++j) {
outprowAt[temp[prowA[j]]] = i;
outAt[temp[prowA[j]]++] = A[j];
}
}
}
#包括“mex.h”
#包括
void-mexFunction(int-nlhs,mxArray*plhs[],int-nrhs,const-mxArray*prhs[]){
//检查输入输出
如果(nrhs!=1)
mexErrMsgTxt(“需要一个输入参数”);
如果(nlhs>1)
mexErrMsgTxt(“太多输出参数”);
//获取输入稀疏矩阵A
如果(mxiempty(prhs[0]){//是空的?
plhs[0]=mxCreateSparse(0,0,0,mxREAL);
返回;
}
如果(!mxIsSparse(prhs[0])| | mxicomplex(prhs[0])//是实数和稀疏数?
mexErrMsgTxt(“输入矩阵必须是实的和稀疏的”);
double*A=mxGetPr(prhs[0]);//A的实向量
MWDINDEX*prowA=mxGetIr(prhs[0]);//一个元素的行索引
mwIndex*pcolindexA=mxGetJc(prhs[0]);//索引到列中
mwSize M=mxGetM(prhs[0]);//一行中的行数
mwSize N=mxGetN(prhs[0]);//数据中的列数
//为^T分配内存
plhs[0]=mxCreateSparse(N,M,pcolindexA[N],mxREAL);
double*outAt=mxGetPr(plhs[0]);
MWDINDEX*outprowAt=mxGetIr(plhs[0]);
mwIndex*outpcolindexAt=mxGetJc(plhs[0]);
//temp[j+1]存储A的第j行中非零元素的数量
标准:向量温度(M+1,0);
对于(MWI指数i=0;i!=N;++i){
对于(mw指数j=pcolindexA[i];j 对于(MWDINDEX i=1;i它可能只是设置了一个控制其数组访问行为的标志——在访问时交换行/列索引,并保留数据非常快。@Rogerowland请参阅我在上面添加的后续内容。谢谢。我建议做一些试验。20毫秒不是一个可靠的时间度量。@Mikhail我做了1000次迭代现在,修改问题文本above@DaKuang第一个测试是rand(1,n)
,第三个测试是rand(1,m)
。此外,当您进行大量测试时,tic/toc
应该放在循环之外。它们使用哪个BLAS/LAPACK实现并不重要,因为afaik几乎都是