Kronecker doesn't work, kron yes, in this case

This is the program I made:

import Random
using LinearAlgebra

N::Int64 = 100
matr_A = Array{Float64}(undef, N, N)
matr_B = Array{Float64}(undef, N, N)
matr_C = Array{Float64}(undef, N*N, N*N)

Random.seed!(1)

for j = 1:N
    for i = 1:N
        matr_A[i,j] = rand(Float64)
        matr_B[i,j] = rand(Float64)
    end
end

matr_C = kron(matr_A, matr_B)

If you instead use Kronecker:

import Random
using LinearAlgebra
using Kronecker

N::Int64 = 100
matr_A = Array{Float64}(undef, N, N)
matr_B = Array{Float64}(undef, N, N)
matr_C = Array{Float64}(undef, N*N, N*N)

Random.seed!(1)

for j = 1:N
    for i = 1:N
        matr_A[i,j] = rand(Float64)
        matr_B[i,j] = rand(Float64)
    end
end

matr_C = Kronecker(matr_A, matr_B)

It gives this error:

ERROR: LoadError: MethodError: objects of type Module are not callable
Stacktrace:
 [1] top-level scope
   @ ~/Desktop/Programmi/MODULO_05/executable_and_libraries/bin/parova1.jl:22

You should use the kronecker function or the binary operator that they define and export. By using Kronecker you are refering to the module itself and not the function it exports. Take a look at the docs.

4 Likes

Ok so there was a typo with the cap letter K.
No, it works, however i can’t really understand how it can be so fast.

you can display matrices with this and change kron with kroncecker to see the output is the same.

import Random
using LinearAlgebra
using BenchmarkTools
using Kronecker

# using MKL

N::Int64 = 2
matr_A = Array{Float64}(undef, N, N)
matr_B = Array{Float64}(undef, N, N)
matr_C = Array{Float64}(undef, N*N, N*N)

Random.seed!(1)

for j = 1:N
    for i = 1:N
        matr_A[i,j] = rand(Float64)
        matr_B[i,j] = rand(Float64)
    end
end

@time matr_C = kronecker(matr_A, matr_B)
display(matr_A)
display(matr_B)
display(matr_C)

However , setting N=100 (and removing the display), when I use @time, kronecker is 100 times faster and it’s mostly compilation time. I don’t know if most of the allocations are made in some arcane places (cause @time should see even in stack), cause it time sees kronecker allocates 1000 less than kron, while @btime doesn’t even understand

The reason kronecker is so fast is that it is lazy. I.e., it doesn’t actually do the creation of the product’s elements until one is requested. From the documentation:

kronecker(A, B) used to obtain an instance of the lazy GeneralizedKroneckerProduct type. In contrast to the native Julia function kron(A, B), this does not compute the Kronecker product but instead stores the matrices in a specialized structure

4 Likes

For what it’s worth, just in case: according to the docs for rand you should be able to replace all of this with two lines

matr_A = rand(Float64, (N,N))
matr_A = rand(Float64, (N,N))
2 Likes

Also, your pre-allocation of matr_C isn’t effective or needed, since that allocation is not used. Unlike in Fortran or Matlab, when you write matr_C = ..., this does not copy the right-hand side into matr_C. Instead, the label matr_C in effect is “re-pointed” to whatever is on the right-hand side, in this case a KroneckerProduct type as defined in the Kronecker package. So the memory you allocated for matr_C is discarded and eventually cleaned up by Julia’s garbage collector.

This is an important point to understand about Julia, and it is often discussed in this forum, e.g. here.

1 Like

Isn’t it possible to preallocate it previously?

Yes, it is possible to preallocate first and then fill the existing array using dot syntax,

matr_C .= kronecker(matr_A, matr_B)

however the function call on the right-hand side will still allocate a new object, which is then copied elementwise into matr_C. If you want to completely eliminate allocations, then you need to call a version of the function that accepts a preallocated argument and modifies (“mutates” in Julia parlance) it in place. See, e.g. kron!. It is conventional in Julia to name functions which modify one or more of their arguements with a “!” (bang, or exclamation point) at the end of the function name. The modified arguments are typically the first one(s) in the argument list.

1 Like

There seems to be Kronecker.collect! to assign to a preallocated array. It seems that the broadcasted assignment is overloaded to use that. See here

1 Like