Matlab 多维阵列的元素矩阵乘法

Matlab 多维阵列的元素矩阵乘法,matlab,multidimensional-array,sum,elementwise-operations,numpy-einsum,Matlab,Multidimensional Array,Sum,Elementwise Operations,Numpy Einsum,我想在MATLAB中实现组件式矩阵乘法,可以在Python中实现,如下所示: 将numpy导入为np M=2 N=4 I=2000 J=300 A=np.random.randn(M,M,I) B=np.random.randn(M,M,N,J,I) C=np.random.randn(M,J,I) #使用einsum D=np.einsum('mki,klnji,lji->mnji',A,B,C) #天真的循环 E=np.零(M,N,J,I) 对于范围(i)中的i: 对于范围(j)内的j: 对

我想在MATLAB中实现组件式矩阵乘法,可以在Python中实现,如下所示:

将numpy导入为np
M=2
N=4
I=2000
J=300
A=np.random.randn(M,M,I)
B=np.random.randn(M,M,N,J,I)
C=np.random.randn(M,J,I)
#使用einsum
D=np.einsum('mki,klnji,lji->mnji',A,B,C)
#天真的循环
E=np.零(M,N,J,I)
对于范围(i)中的i:
对于范围(j)内的j:
对于范围内的n(n):
E[:,n,j,i]=B[:,:,i]@A[,:,n,j,i]@C[:,j,i]
打印(np.sum(np.abs(D-E))#预计足够小
到目前为止,我使用了
I
j
n
的循环,但我不想,至少对于
n
的循环,选项1:从MATLAB调用numpy 假设您的系统已设置,并且安装了numpy软件包,您可以(在MATLAB中)执行以下操作:

np=py.importlib.import_模块('numpy');
M=2;
N=4;
I=2000;
J=300;
A=matpy.mat2nparray(randn(M,M,I));
B=matpy.mat2nparray(randn(M,M,N,J,I));
C=matpy.mat2nparray(randn(M,J,I));
D=matpy.nparray2mat(np.einsum('mki,klnji,lji->mnji',A,B,C));
其中可以找到
matpy

选项2:原生MATLAB 这里最重要的部分是正确排列,所以我们需要跟踪我们的维度。我们将使用以下顺序:

I(1) J(2) K(3) L(4) M(5) N(6)
现在,我将解释如何获得正确的排列顺序(让我们以
A
为例):
einsum
希望维度顺序是
mki
,根据我们的编号是
5 3 1
。这告诉我们
A
的第一个维度需要是第五个维度,第二个维度需要是第三个维度,第三个维度需要是第一个维度(简而言之
1->5,2->3,3->1
)。这也意味着“无源维度”(指那些没有原始维度的维度;在本例中为2 4 6)应该是单态的。使用
ipermute
这非常简单:

pA = ipermute(A, [5,3,1,2,4,6]);
在上面的例子中,
1->5
意味着我们先写
5
,其他两个维度也是如此(产生[5,3,1])。然后我们只需在末尾添加单例(2,4,6)即可得到
[5,3,1,2,4,6]
。最后:

A=randn(M,M,I);
B=随机数N(M,M,N,J,I);
C=randn(M,J,I);
%参考尺寸顺序:I(1)J(2)K(3)L(4)M(5)N(6)
pA=ipermute(A[5,3,1,2,4,6]);%1->5, 2->3, 3->1; 第二、第四和第六是单身
pB=ipermute(B[3,4,6,2,1,5]);%1->3, 2->4, 3->6, 4->2, 5->1; 第五是单身
pC=ipermute(C[4,2,1,3,5,6]);%1->4, 2->2, 3->1; 第三、第五和第六是单身
pD=总和(。。。
排列(pA.*pB.*pC,[5,6,2,1,3,4]),…1->5,2->6,3->2,4->1;第三和第四是单态
[5,6]);
(请参阅文章底部关于
sum
的注释。)

在MATLAB中执行此操作的另一种方法是:

rD=压缩(和(重塑(A[M,M,1,1,1,1,1,I])。*。。。
重塑(B,[1,M,M,N,J,I]).*。。。
…%与:重塑(B,[1,尺寸(B)]).*。。。
…%与:shiftim(B,-1)。*。。。
重塑(C[1,1,M,1,J,I]),[2,3]);
另见:


下面是一个完整的示例,它显示测试这些方法是否等效:

功能q55913093
M=2;
N=4;
I=2000;
J=300;
mA=randn(M,M,I);
mB=randn(M,M,N,J,I);
mC=randn(M,J,I);
%%选项1-使用numpy:
np=py.importlib.import_模块('numpy');
A=最大功率。最大功率(mA);
B=平均功率。平均功率(mB);
C=材料、材料和材料(mC);
D=matpy.nparray2mat(np.einsum('mki,klnji,lji->mnji',A,B,C));
%%选项2-本机MATLAB:
%%%参考尺寸顺序:I(1)J(2)K(3)L(4)M(5)N(6)
pA=ipermute(mA,[5,3,1,2,4,6]);%1->5, 2->3, 3->1; 第二、第四和第六是单身
pB=ipermute(mB[3,4,6,2,1,5]);%1->3, 2->4, 3->6, 4->2, 5->1; 第五是单身
pC=ipermute(mC[4,2,1,3,5,6]);%1->4, 2->2, 3->1; 第三、第五和第六是单身
pD=总和(排列(。。。
pA.*pB.*pC,[5,6,2,1,3,4]),…%1->5,2->6,3->2,4->1;第三和第四是单态
[5,6]);
rD=挤压(和(重塑(mA,[M,M,1,1,I])。*。。。
重塑(mB,[1,M,M,N,J,I]).*。。。
重塑(mC[1,1,M,1,J,I]),[2,3]);
%%比较:
总和(abs(pD-D),“全部”)
等质量(pD,rD)
运行上述步骤,我们得到的结果确实是等效的:

>q55913093
ans=
2.1816e-10
ans=
符合逻辑的
1.
请注意,最近的版本中引入了这两种调用
sum
的方法,因此,如果您的MATLAB相对较旧,则可能需要替换它们:

S = sum(A,'all')   % can be replaced by ` sum(A(:)) `
S = sum(A,vecdim)  % can be replaced by ` sum( sum(A, dim1), dim2) `

根据评论中的要求,这里有一个比较方法的基准:

函数t=q55913093_基准(M,N,I,J)
如果nargin==0
M=2;
N=4;
I=2000;
J=300;
结束
%在MATLAB中定义阵列
mA=randn(M,M,I);
mB=randn(M,M,N,J,I);
mC=randn(M,J,I);
%在numpy中定义数组
np=py.importlib.import_模块('numpy');
pA=matpy.mat2nparray(mA);
pB=matpy.mat2nparray(mB);
pC=matpy.mat2nparray(mC);
%等效性检验
D=类别(5,M1(),M2(),M3());
断言(sum(abs(D(:,:,:,:,:,:,:,1)-D(:,,,,,,,,,,,,,,,,,,,2)),'all')<1E-8;
断言(isequal(D(:,:,:,:,,:,2),D(:,,,,,,,,,,3));
%时间
t=[timeit(@M1,1)、timeit(@M2,1)、timeit(@M3,1)];
函数out=M1()
out=matpy.nparray2mat(np.einsum('mki,klnji,lji->mnji',pA,pB,pC));
结束
函数out=M2()
输出=排列(。。。
总和(。。。
ipermute(硕士,[5,3,1,2,4,6])。*。。。
ipermute(mB,[3,4,6,2,1,5]).*。。。
ipermute(mC[4,2,1,3,5,6]),[3,4]。。。
), [5,6,2,1,3,4]...
);  
结束
功能输出=M3()
out=挤压(和(重塑(mA,[M,M,1,1,I])。*。。。
重塑(mB,[1,M,M,N,J,I]).*。。。
重塑(mC[1,1,M,1,J,I]),[2,3]);
结束
结束
在我的系统上,这会导致:

>q55913093\u基准
ans=
1.3964    0.1864    0.2