Hcat with matrices as elements

If I want a matrix with a single row of numbers I can do [1 2 3 4 5] or the equivalen function call hcat(1, 2, 3, 4, 5). However, what if I want a matrix with a single row of matrices as elements? If a,b, c, d and e are matrices, then doing [a b c d e] just concatenates them into one single big matrix, instead of giving a 1x5 matrix with the matrices a, b, c, d and e as elements.

What would be the recommended way to create such a matrix of matrices? Is there a short, fast and convenient way?

Curious: why not a vector of matrices?

If you start with a vector you can reshape it into a matrix, BTW.

A vector seems to be interpreted by most functions as equivalent to a matrix with a single column, but I need a matrix with a single row. (For example vcat will concatenate vectors into a single long vector, whereas it will concatenate matrices with a single row and multiple columns into a matrix with multiple rows and multiple columns)

Creating a vector and reshaping does work, but it adds a layer of indirection. I was curious if there is a way to create such a single row matrix directly.

Maybe something like this?

getfield.(hcat(Ref(rand(2,2)),Ref(rand(3,3))),:x)

[ a, b, c, d, e][:,:]

1 Like

But this is basically the same as reshape, just slower.

And, doesn’t it make a 5x1 matrix instead of a 1x5?

Speed is not the only criterion in life. :wink:

As if😄

Direct:

[[a] [b] [c] [d] [e]]

Faster:

permutedims([a, b, c, d, e])
1 Like

Update: I added some asserts.

module A
using BenchmarkTools

const a = rand(3,3)
const b = rand(3,3)
const c = rand(3,3)
const d = rand(3,3)
const e = rand(3,3)

@btime [a,b,c,d,e]
# fails
# @assert size([a,b,c,d,e]) == (1,5)
# fails
# @assert [a,b,c,d,e] isa Matrix{Matrix{Float64}}

@btime [a,b,c,d,e][:,:]
# fails
# @assert size([a,b,c,d,e][:,:]) == (1,5)
@assert [a,b,c,d,e][:,:] isa Matrix{Matrix{Float64}}

@btime transpose([a,b,c,d,e][:,:])
@assert size(transpose([a,b,c,d,e][:,:])) == (1,5)
# fails
# @assert transpose([a,b,c,d,e][:,:]) isa Matrix{Matrix{Float64}}
  
@btime permutedims([a,b,c,d,e][:,:])                                                                                                          
@assert size(permutedims([a,b,c,d,e][:,:])) == (1,5)                                                                                          
@assert permutedims([a,b,c,d,e][:,:]) isa Matrix{Matrix{Float64}}     

@btime permutedims([a,b,c,d,e])
@assert size(permutedims([a,b,c,d,e])) == (1,5)
@assert permutedims([a,b,c,d,e]) isa Matrix{Matrix{Float64}}

@btime getfield.(hcat(Ref(a),Ref(b),Ref(c),Ref(d),Ref(e)),:x)
@assert size(getfield.(hcat(Ref(a),Ref(b),Ref(c),Ref(d),Ref(e)),:x)) == (1,5)
@assert getfield.(hcat(Ref(a),Ref(b),Ref(c),Ref(d),Ref(e)),:x) isa Matrix{Matrix{Float64}}

function mcat(args::Matrix{T}...) where T
    l = length(args)
    r = Matrix{Matrix{T}}(undef,1,l)
    for (i,arg) in enumerate(args)
        @inbounds r[1,i] = arg
    end
    r
end

@btime mcat(a,b,c,d,e)
@assert size(mcat(a,b,c,d,e)) == (1,5)
@assert mcat(a,b,c,d,e) isa Matrix{Matrix{Float64}}

end

julia> include("test.jl")
  54.884 ns (1 allocation: 128 bytes)
  140.585 ns (2 allocations: 256 bytes)
  141.619 ns (2 allocations: 256 bytes)
  233.428 ns (3 allocations: 384 bytes)
  134.188 ns (3 allocations: 224 bytes)
  329.203 ns (7 allocations: 336 bytes)
  63.907 ns (1 allocation: 128 bytes)
Main.A


This doesn’t actually do the correct thing.

That is correct, but I included it just in case one would be ok with the switched dimensions.

Should use: transpose([a,b,c,d,e][:,:])

transpose does not work on Matrix{String}, but permutedims does.

1 Like

transpose also transposes all the sub-elements, recursively, and I don’t think that is wanted here.

1 Like

@DNF, thanks - reverting back.