How not to concatenate in hvcat

Is there a way one can use the [...; ...] syntax which lowers to hvcat without concatenating the arguments? Ie as a plain vanilla matrix constructor.

Eg

julia> [[1] [2];
       [3] [4]]
2×2 Matrix{Int64}:
 1  2
 3  4

concatenates, I want something equivalent to

julia> reshape([[1], [3], [2], [4]], :, 2)
2×2 Matrix{Vector{Int64}}:
 [1]  [2]
 [3]  [4]

This works:

julia> [[[1]] [[2]];
       [[3]] [[4]]]
2×2 Matrix{Vector{Int64}}:
 [1]  [2]
 [3]  [4]

but, aargh.

1 Like

No, the lowering functions all copy elementwise from AbstractArray inputs.

julia> vcat.([1 3; 2 4])
2×2 Matrix{Vector{Int64}}:
 [1]  [3]
 [2]  [4]

Downside is that it isn’t visually as obvious as [[1] [2]; [3] [4]] would be (if it worked), so probably needs a comment alongside it to explain what it’s doing.

I think you misunderstood the question; the point is not create the matrix in the MWE, but to construct a matrix using the [ ; ] syntax without concatenation, eg generally

[a b; c d]

where a::Union{AbstractVector,AbstractMatrix} etc.

The simplest solution may be a wrapper type, eg along the lines of

"""
A wrapper type that should be used on the element of a `hvcat` (`[...; ...]`) call to
prevent concatenation of the arguments.
"""
struct NoCat{T}
    x::T
end

function Base.hvcat(rows::Tuple{Vararg{Int}}, x1::NoCat, xR...)
    nrow = length(rows)
    ncol = rows[1]
    @assert allequal(rows)
    xs = promote(x1.x, xR...)
    T = typeof(xs[1])
    m = Matrix{T}(undef, nrow, ncol)
    j = 1
    for row in 1:nrow
        for col in 1:ncol
            m[row, col] = xs[j]
            j += 1
        end
    end
    m
end

which works like

julia> [NoCat([1]) [2];
       [3] [4]]
2×2 Matrix{Vector{Int64}}:
 [1]  [2]
 [3]  [4]
1 Like

hm, unfortunately I think the current behavior is highly consistent:

julia> [1 2]
1×2 Matrix{Int64}:
 1  2

julia> [[1] [2]]
1×2 Matrix{Int64}:
 1  2

julia> [1, 2]
2-element Vector{Int64}:
 1
 2

julia> [[1]; [2]]
2-element Vector{Int64}:
 1
 2

I agree with this, but I was just looking for a syntax that makes a matrix using the same visual arrangement without concatenating the elements. Julia does not have (built-in) syntax for a matrix constructor that does nothing else (cf the [] syntax for vectors). Fortunately it can easily be added.

2 Likes

I think it’d be a bit cleaner to instead abuse typed_hvcat like what StaticArrays.jl does:

struct var"typeof(NoCat)" end
const NoCat = var"typeof(NoCat)"()
Base.show(io::IO, ::var"typeof(NoCat)") = print(io, "NoCat")

function Base.typed_hvcat(::var"typeof(NoCat)", rows::Tuple{Vararg{Int}}, _xs...)
    nrow = length(rows)
    ncol = rows[1]
    @assert allequal(rows)
    xs = promote(_xs...)
    T = typeof(xs[1])
    m = Matrix{T}(undef, nrow, ncol)
    j = 1
    for row in 1:nrow
        for col in 1:ncol
            m[row, col] = xs[j]
            j += 1
        end
    end
    m
end

and then

julia> NoCat[[1] [2]
             [3] [4]]
2×2 Matrix{Vector{Int64}}:
 [1]  [2]
 [3]  [4]

2 Likes

I absolutely agree, but AFAICT it is not part of the exposed API. Perhaps that should be fixed — I could not find an open issue I opened an issue:

1 Like