Expanding a matrix by duplicating its elements

Hi!

I am trying to create a “higher resolution” matrix by duplicating, in both dimensions by a factor n, the elements of a “lower resolution” matrix. A naive implementation looks like this:

function lr2hr(
    inarray::AbstractMatrix,
    factor::Integer
    )

    # Determine the type and size of the input argument `inarray`, and define the output array `outarray`:
    in_type = typeof(inarray)
    in_size = size(inarray)
    outarray = Matrix{in_type}(undef, factor * in_size[1], factor * in_size[2])

    for i = 1:in_size[1]
        for j = 1:in_size[2]
            for k = 1 + (i - 1) * factor:(i * factor)
                for l = 1 + (j - 1) * factor:(j * factor)
                    outarray[k, l] = inarray[i, j]
                end
            end
        end
    end

    return outarray

end

This function generates an error message complaining about “Cannot convert an object of type Int64 to an object of type Matrix{Int64}”, although it seems to me that outarray[k, l] and inarray[i, j] are both integers (though each is a matrix element):

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

julia> outarray = lr2hr(inarray, 2)
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type Matrix{Int64}
Closest candidates are:
  convert(::Type{T}, ::LinearAlgebra.Factorization) where T<:AbstractArray at ~/.julia/juliaup/julia-1.8.5+0.x64.apple.darwin14/share/julia/stdlib/v1.8/LinearAlgebra/src/factorization.jl:58
  convert(::Type{Array{T, N}}, ::StaticArraysCore.SizedArray{S, T, N, N, Array{T, N}}) where {S, T, N} at ~/.julia/packages/StaticArrays/J9itA/src/SizedArray.jl:88
  convert(::Type{Array{T, N}}, ::StaticArraysCore.SizedArray{S, T, N, M, TData} where {M, TData<:AbstractArray{T, M}}) where {T, S, N} at ~/.julia/packages/StaticArrays/J9itA/src/SizedArray.jl:82
  ...
Stacktrace:
 [1] setindex!
   @ ./array.jl:968 [inlined]
 [2] lr2hr(inarray::Matrix{Int64}, factor::Int64)
   @ JMTools ~/Projects/MISR/MISR_Tools/JMTools/src/lr2hr.jl:81
 [3] top-level scope
   @ REPL[14]:1

where line 81 points to the statement outarray[k, l] = inarray[i, j]. The matrix outarray should look like this:

1 1 2 2
1 1 2 2
3 3 4 4
3 3 4 4

Questions:

  1. How should I assign the elements of the small matrix to the larger one?
  2. Is there a more efficient way to construct the larger matrix?

Your problem is here:

in_type = typeof(inarray)
outarray = Matrix{in_type}(undef, factor * in_size[1], factor * in_size[2])

the type of inarray is Matrix, you probably meant eltype here (i.e. the type of the elements of inarray, not of the array itself).

1 Like

As for efficiency, you probably don’t want to construct this matrix at all, consider expressing it as a Kronecker product with LazyArrays

2 Likes

Thanks a lot, @nilshg. That’s right, and it works well. I did not know about the command eltype.

Regarding the efficiency question, my application is this: the process that generates inarray actually creates a geophysical field, for instance of 512 by 128 pixels. The goal is to create a map of that field at a size more convenient for visualization or printing. In that case, factor would be 4, for instance, to generate a reasonable image on the screen.

I’ll look into the link to Kronecker product with LazyArrays. Thanks again for your help.

By the way, you can use Base.repeat for this:

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

julia> repeat(inarray, inner=(2,2))
4×4 Matrix{Int64}:
 1  1  2  2
 1  1  2  2
 3  3  4  4
 3  3  4  4
4 Likes

Even better! Thanks @Jollywatt for this elegant solution.