如何将矩阵的切片放入具有SMatrix类型内部结构的3D数组中?



假设我有这个矩阵:

julia> mat = [
1 2 3 4
5 6 7 8
9 8 7 6
];

然后我想把这个矩阵的切片放入具有SMatrix{Int64}类型的3DArray中,如下所示:

julia> using StaticArrays
julia> arr = Array{SMatrix{Int64}, 3}(undef, 3, 2, 3);
julia> col_idx = [1, 2, 3];
julia> foreach(x->arr[:, :, x] = mat[:, x:x+1], col_idx)
ERROR: MethodError: Cannot `convert` an object of type
Int64 to an object of type
SMatrix{Int64}
Closest candidates are:
convert(::Type{T}, ::LinearAlgebra.Factorization) where T<:AbstractArray at C:UsersJUL.juliajuliaupjulia-1.8.3+0.x64sharejuliastdlibv1.8LinearAlgebrasrcfactorization.jl:58
convert(::Type{SA}, ::Tuple) where SA<:StaticArray at C:UsersJUL.juliapackagesStaticArraysx7lS0srcconvert.jl:179
convert(::Type{SA}, ::SA) where SA<:StaticArray at C:UsersJUL.juliapackagesStaticArraysx7lS0srcconvert.jl:178
...
Stacktrace:
[1] setindex!
@ .array.jl:968 [inlined]
[2] macro expansion
@ .multidimensional.jl:946 [inlined]
[3] macro expansion
@ .cartesian.jl:64 [inlined]
[4] macro expansion
@ .multidimensional.jl:941 [inlined]
[5] _unsafe_setindex!(::IndexLinear, ::Array{SMatrix{Int64}, 3}, ::Matrix{Int64}, ::Base.Slice{Base.OneTo{Int64}}, ::Base.Slice{Base.OneTo{Int64}}, ::Int64)
@ Base .multidimensional.jl:953
[6] _setindex!
@ .multidimensional.jl:930 [inlined]
[7] setindex!(::Array{SMatrix{Int64}, 3}, ::Matrix{Int64}, ::Function, ::Function, ::Int64)
@ Base .abstractarray.jl:1344
[8] (::var"#5#6")(x::Int64)
@ Main .REPL[20]:1
[9] foreach(f::var"#5#6", itr::Vector{Int64})
@ Base .abstractarray.jl:2774
[10] top-level scope
@ REPL[20]:1

我怎样才能实现它?

注::
这只是一个最小的和可复制的例子。在实际意义上,我对arr有一个(10, 10, 2000)的大小,对mat也有一个大的大小(10x2000,我猜)!

如果我理解正确,你想要一个数组的SMatrices吗?

mat = [ 1 2 3 4
5 6 7 8
9 8 7 6 ];
using StaticArrays
col_idx = [1, 2, 3];
arr = [SMatrix{3,2}(mat[:, x:x+1]) for x in col_idx]
3-element Vector{SMatrix{3, 2, Int64, 6}}:
[1 2; 5 6; 9 8]
[2 3; 6 7; 8 7]
[3 4; 7 8; 7 6]

那么,如果我说:

julia> using StaticArrays
julia> mat = [
1 2 3 4
5 6 7 8
9 8 7 6
];
julia> arr = Array{Int64, 3}(undef, 3, 2, 3);
julia> foreach(x->arr[:, :, x] = mat[:, x:x+1], [1, 2, 3]);
julia> sarr = SArray{Tuple{3, 2, 3}}(arr)
3×2×3 SArray{Tuple{3, 2, 3}, Int64, 3, 18} with indices SOneTo(3)×SOneTo(2)×SOneTo(3):
[:, :, 1] =
1  2
5  6
9  8
[:, :, 2] =
2  3
6  7
8  7
[:, :, 3] =
3  4
7  8
7  6
julia> typeof(sarr[:, :, 1])
SMatrix{3, 2, Int64, 6} (alias for SArray{Tuple{3, 2}, Int64, 2, 6})

首先,我创建了一个常规的3DArray,然后在此基础上构建了一个SArray。然而,考虑到你的实际情况,我尝试了以下方法:

julia> mat = rand(10, 2000);
julia> arr = Array{Float64, 3}(undef, 10, 2, 1999);
julia> foreach(x->arr[:, :, x] = mat[:, x:x+1], 1:1999);
julia> sarr = SArray{Tuple{10, 2, 1999}}(arr);

但是构造这样的容器需要花费太多的时间。(我已经取消了,我不知道它的运行时间。)因此,在这些情况下,最好采纳@AboAmmar的建议。

受@Shayan和@AboAmmar的启发,这个答案探讨了使用BlockArrays.jl包来构建期望的结果。BlockArrays将现有数组放入'元数组'。子数组可以是SMatrix类型。

在代码:

using StaticArrays, BlockArrays
mat = rand(10,2000)   # random demo matrix
# make all the slice SArrays
arr = [SArray{Tuple{10,2,1}, Float64, 3}(mat[:,i:i+1])
for i=1:1999]
arr = reshape(arr,1,1,1999)
# glue them into a BlockArray
bricked = mortar(arr)

现在后:

julia> size(bricked)
(10, 2, 1999)
julia> bricked[:,:,25]
1×1-blocked 10×2 BlockMatrix{Float64}:
0.265972  0.258414 
0.396142  0.863366 
0.41708   0.648276 
0.960283  0.773064 
0.62513   0.268989 
0.132796  0.0493077
0.844674  0.791772 
0.59638   0.0769661
0.221536  0.388623 
0.595742  0.50732  

希望这个方法能得到你想要的性能折衷(或者至少引入一些新的想法)。

最新更新