Is there an easy way to fill with zeros in array concatenation?

For example,

julia> [rand(2,2) zeros(2,3)
        zeros(3,2) rand(3,3)]
5×5 Matrix{Float64}:
 0.717944  0.840054  0.0       0.0       0.0
 0.513488  0.62959   0.0       0.0       0.0
 0.0       0.0       0.553575  0.129365  0.998488
 0.0       0.0       0.493924  0.634678  0.267029
 0.0       0.0       0.643076  0.394884  0.650393

In this case I want the off-diagonal blocks to be zero, and would prefer to leave the sizes implicit. Is there a way to use some type such that the sizes are inferred automatically, and the expression may look something like

[A ZerosFill()
ZerosFill() B]

where ZerosFill() would be replaced by zeros of compatible sizes (or perhaps Zeros from FillArrays).

Had these been square matrices, I could have used the UniformScaling 0I to represent the zeros as

julia> [rand(2,2) 0I
       0I rand(2,2)]
4×4 Matrix{Float64}:
 0.39555   0.0482989  0.0       0.0
 0.766527  0.386412   0.0       0.0
 0.0       0.0        0.705164  0.436469
 0.0       0.0        0.188006  0.566772

however I don’t know of a way to do this for rectangular matrices.

A = randn(2,2)
B = randn(3,3)
iA, jA = size(A)
iB, jB = size(B)
C = [A zeros(iA,jB);
     zeros(iB,jA) B]

For this specific case you can use the BlockDiagonals package.

2 Likes

That’s true. The actual case that I’m dealing with is not block diagonal, but a block array. I was hoping that a more general solution might exist

I realize that in this case I can read the sizes off the arrays, however this becomes difficult if there are more arrays involved. In any case I was hoping that I may leave the sizes implicit, and have it inferred from the context.

If your final matrix is something like an irregular-sized checker-board with one field per column/row, you still can infer it before assembly (Tuple, for loop).
If there are several filled subarrays per column/row, you need more code, but should be doable.

Based on the code for I (UniformScaling), it shouldn’t be very hard to do: julia/uniformscaling.jl

When there is only one per row, it should be quite easy, but if there is multiple on one row, we can’t know their widths because the submatrix widths are not required to match with the previous line

meaning this is possible:

julia> [fill(9,2) 0I
       0I fill(8,2)]
4×3 Matrix{Int64}:
 9  0  0
 9  0  0
 0  0  8
 0  0  8

julia> [fill(9,2) 0I 0I
       0I fill(8,2) 0I]
4×5 Matrix{Int64}:
 9  0  0  0  0
 9  0  0  0  0
 0  0  8  0  0
 0  0  8  0  0

this means there would be ambiguity with ZerosFill() or only accept stricter dimensions

In the absence of a smarter ZeroFill, there’s https://github.com/JuliaArrays/FillArrays.jl which at least allows you to avoid the allocations of zero matrices.

Could you define a function to handle n-matrices?

For example
function blockmatrix(A::Vector{Matrix{Int}})
  sa = size.(A)
  sz = cumsum.((first.(sa), last.(sa)))
  R = zeros(eltype(A[1]),last.(sz))
  r0, c0 = 1, 1
  for i in 1:length(A)
    ri, ci = getindex.(sz,i)
    R[r0:ri, c0:ci] .= A[i]
    r0, c0 = ri + 1, ci + 1
  end
  return R
end

n = 3  # example with 3 matrices:
A = Vector{Matrix{Int}}(undef,n)
A[1] = rand((1:4), 2,3)
A[2] = rand((-4:-1), 1,2)
A[3] = rand(7:8, 4,5)

julia> blockmatrix(A)
7×10 Matrix{Int64}:
 3  4  1   0   0  0  0  0  0  0
 1  1  4   0   0  0  0  0  0  0
 0  0  0  -3  -2  0  0  0  0  0
 0  0  0   0   0  7  8  7  7  8
 0  0  0   0   0  7  7  8  7  7
 0  0  0   0   0  7  7  8  8  8
 0  0  0   0   0  8  7  8  8  7

Nobody has suggested

julia> cat(rand(2,2),rand(3,3);dims=(1,2))
5×5 Matrix{Float64}:
 0.104583  0.266817  0.0       0.0       0.0
 0.915434  0.785553  0.0       0.0       0.0
 0.0       0.0       0.89667   0.760755  0.778991
 0.0       0.0       0.18982   0.439059  0.489084
 0.0       0.0       0.937842  0.298687  0.669757
4 Likes

@Jean_Michel, brilliant :slight_smile: , with an array of matrices it even takes splatting: cat(A...;dims=(1,2))