The behavior of appending an empty matrix to a non-empty one

For example,
A = [1 2 3; 4 5 6];

B can be either a N x 3 matrix or empty like the below:
B = [];

When B is empty, why couldn’t I append B to A
C = vcat(A, B);

Error message:
ArgumentError: number of columns of each array must match (got (3, 1))

Instead of throwing out an error, do you think Julia should instead handle this situation differently, i.e., the above equation of C = vcat(A, B) should create a C that is equal to A, when B is empty?

B is the wrong shape, so it should error.

1 Like

What’s the advantages of requiring an empty matrix to have the same column width in order to be able to be appendable?

Other languages, e.g., Matlab can handle this situation well.

What would be the shape of concatenating the empty matrix with the empty vector?

An error is a useful result because it alerts the user that something unintended may be happening in their code. Handling it well is throwing an error.

5 Likes

One issue here is that there is no single empty matrix—as long as one of the dimensions is zero, you have an empty matrix. The point of requiring the corresponding dimensions to match is consistency and catching logical errors in your code sooner rather than later. What is the condition under which it is possible to vertically concatenate two matrices? Simple: when they have the same number of columns. @leon, you are advocating for making that simple rule more complicated by ignoring the number of columns if the number of rows of one of the matrices is zero. Not only does that complicate a very simple rule, but it can hide logical errors in your code. Getting a matrix with zero rows is a natural edge case, but even then, if your code is correct in general and it concatenates two matrices they should still have the same number of columns even if one or both have no rows. There is no natural edge case where one of the matrices has the wrong number of columns. If column counts disagree, you should get an error to let you know that your code has some mistake in it. The proposed complex rule also brings up more questions. For example, if you concatenate two matrices with zero rows and different numbers of columns, how many columns should the resulting matrix have?

11 Likes

One quirk is that hcat() returns []. In something like this, you’d prefer a 3×0 Matrix, but of course it doesn’t know that:

julia> fn(n) = hcat(hcat((fill(i,3) for i in 1:n)...), hcat((fill(10i,3) for i in n+1:10)...));

julia> fn(4)
3×10 Matrix{Int64}:
 1  2  3  4  50  60  70  80  90  100
 1  2  3  4  50  60  70  80  90  100
 1  2  3  4  50  60  70  80  90  100

julia> fn(0)
ERROR: ArgumentError: number of rows of each array must match (got (0, 3))

That seems to be asking too much from Julia. The user should take care of the edge case. For example:

fn(n) = hcat(reshape(hcat((fill(i,3) for i in 1:n)...),3,n), hcat((fill(10i,3) for i in n+1:10)...));

Sure, although this makes a Matrix{Any}. Perhaps my complaint is that hcat() is defined at all, but returns something which isn’t a neutral element. I presume this is why there is no definition for *().

2 Likes