如何在Julia中对多维数组/矩阵进行乘法运算

3

将两个多维数组相乘,比如一个一维数组和一个三维数组:

[1 2] * reshape(1:8,2,2,2)

出现错误消息:

LoadError: MethodError: `*` has no method matching *(::Array{Int64,2}, ::Array{Int64,3})
Closest candidates are:
  *(::Any, ::Any, !Matched::Any, !Matched::Any...)
  *{TA,TB}(::Union{DenseArray{TA,1},DenseArray{TA,2},SubArray{TA,1,A<:DenseArray{T,N},I<:Tuple{Vararg{Union{Colon,Int64,Range{Int64}}}},LD},SubArray{TA,2,A<:DenseArray{T,N},I<:Tuple{Vararg{Union{Colon,Int64,Range{Int64}}}},LD}}, !Matched::Base.LinAlg.AbstractTriangular{TB,S<:AbstractArray{T,2}})
  *{TA,TQ,N}(::Union{DenseArray{TA,N},SubArray{TA,N,A<:DenseArray{T,N},I<:Tuple{Vararg{Union{Colon,Int64,Range{Int64}}}},LD}}, !Matched::Union{Base.LinAlg.QRCompactWYQ{TQ,M<:AbstractArray{T,2}},Base.LinAlg.QRPackedQ{TQ,S<:AbstractArray{T,2}}})
  ...
while loading In[167], in expression starting on line 1

 in Ac_mul_B at operators.jl:157

使用多维矩阵代数的数学定义,对于(1乘2)*(2乘2乘2)的矩阵/数组相乘,可以得到以下结果。稍微通用一些的例子是A*B=C,其中sum_k A_{i,j,k} B_{k,l,m} = C_{i,j,l,m},其中A是一个3索引矩阵或张量,B是一个3索引矩阵或张量,生成的C是一个四索引矩阵/张量,但一般来说,可以有任意数量的维度和任意大小的维度(在合理范围内)。关于矩阵乘积的定义,请查看矩阵乘积张量缩并的更多信息。
在Julia中,这种乘法的正确语法是什么?

1
use .* instead of * - Gnimuc
2
“多维矩阵代数的数学定义”是什么意思?是指张量积吗?矩阵之间的张量积可以通过“kron”获得。还是指逐元素相乘的“.*”操作? - mschauer
为了让大家理解得更加通俗易懂,我这里指的是 A*B = C 的含义是 sum_k A_{i,j,k} B_{k,l,m} = C_{i,j,l,m}。其中,A 是一个三元素矩阵(或者你可以称之为张量),B 是一个三元素矩阵,而结果 C 则是一个四元素矩阵/张量。详见:https://en.wikipedia.org/wiki/Matrix_multiplication#General_definition_of_the_matrix_product 中的矩阵乘法定义。我的上述定义只是对于 n>2 维度的矩阵而言的一般化表达方式。如果你熟悉 Matlab,那么这个操作可以通过 * 完成。 - Ferenc
1
那是一个张量缩并。 - Vincent Zoonekynd
@Vincent Zoonekynd 没错! - Ferenc
2个回答

2
你可以使用reshape将多维数组转换为矩阵,进行乘法运算,并将结果转换回多维数组。
A = [1 2]
B = reshape(1:8,2,2,2)
reshape( reshape(A,2,1)' * reshape(B,2,4), 2, 2 )

在这个例子中,由于A已经是一个矩阵,实际上不需要对其进行重新整形。

1
这是最快的方法吗?也就是说,编译器会聪明到reshape操作是零成本的吗? - a06e
reshape 应该非常便宜。https://github.com/JuliaMath/TensorCore.jl 已经整洁地包装了这个版本(并且小心处理伴随向量等),您只需编写 A ⊡ B 即可使用。 - mcabbott

1

矩阵乘法:使用*

矩阵逐元素相乘:使用.*

如果您想要将两个矩阵相乘A*B,则两者的大小必须匹配:例如, A的大小为(a1,a2), B的大小为(b1,b2); 为了使乘法可行,a2必须与b1相同。

在您的情况下:

julia> A=[1 2]
1x2 Array{Int64,2}:
 1  2

julia> size(A) #remember that size(A') is (2,1)
(1,2)

julia> B=reshape(1:8,2,2,2)
2x2x2 Array{Int64,3}:
[:, :, 1] =
 1  3
 2  4

[:, :, 2] =
 5  7
 6  8

julia> size(B)
(2,2,2)

julia> size(B[:,:,1])
(2,2)   

julia> A*B[:,:,1]
1x2 Array{Int64,2}:
 5  11

julia> A*B[:,:,2]
1x2 Array{Int64,2}:
 17  23

编辑:一次性乘法(为了清晰起见而分开):
julia> A*B[:,:,1]
1x2 Array{Int64,2}:
 5  11

julia> A*B[:,:,2]
1x2 Array{Int64,2}:
 17  23

julia> C=A*B[:,:]
1x4 Array{Int64,2}:
 5  11  17  23

julia> size(C)
(1,4)

julia> D1 = reshape(C, 2,2)
2x2 Array{Int64,2}:
  5  17
 11  23

julia> D2 = reshape(C, 2,2)'
2x2 Array{Int64,2}:
  5  11
 17  23

真正的一次性:

julia> D = reshape(A*B[:,:], 2,2)
2x2 Array{Int64,2}:
  5  17
 11  23

是的,那些是我期望得到的 AB 的两个层的数字,但我不知道如何像 AB 一样在“一次操作”中完成这个乘法,而不是做 AB [:,:,1] 和 AB [:,:,2]。 - Ferenc
我已经更新了答案。老实说,由于我不理解整个问题,很难给你一个答案 :) - Maciek Leks
请看我在问题后的评论,以获取精确的定义 :) - Ferenc
如果你这么说,我很高兴 :) - Maciek Leks
至少D1是这个特定例子的解决方案。但如果矩阵的大小事先不知道,比如A是m1乘以m2,B是n1乘以n2乘以n3的矩阵,其中m1=n1,则需要使用矩阵的大小引入更多的噪声:A = [1 2] B = reshape(1:12,2,2,3) reshape(A*B[:,:],size(B,2),size(B,3))。但可能有一种重新定义*运算符用于这种矩阵操作的方法。 - Ferenc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接