Julia is slower than matlab when it comes to matrix diagonalization?

The following julia script is a literal translation of the original matlab code,

using LinearAlgebra, SparseArrays
using Arpack, Plots

function bhm_basis(N, M, dim, weight)
    basis = zeros(UInt8, M, dim)
    basis[1, 1] = N
    s = 1
    while basis[M, s] < N
        s1 = M - 1
        while basis[s1, s] == 0
            s1 = s1 - 1
        end

        basis[1:s1-1, s+1] = basis[1:s1-1, s]
        basis[s1, s+1] = basis[s1, s] - 1
        basis[s1+1, s+1] = N - sum(basis[1:s1, s+1])

        s = s + 1
    end
    table = (weight * basis)'
    ind = sortperm(table)
    table = table[ind]
    mini = minimum(table[2:end] - table[1:end-1]) / 4.0
    return basis, table, ind, mini
end

function hint(dim, basis, M)
    COO1 = zeros(3, dim)
    for s1 = 1:dim
        COO1[1, s1] = s1
        COO1[2, s1] = s1
        COO1[3, s1] = 0.5 * sum(basis[:, s1] .^ 2 - basis[:, s1])
    end
    return Interaction = sparse(COO1[1, :], COO1[2, :], COO1[3, :], dim, dim)
end

function hkin(dim, basis, M, table, ind, mini)
    COO2 = zeros(3, dim * M * 2)
    s = 0
    target = [Array(2:M); 1]
    for s1 = 1:dim
        if mod(s1, 10000) == 0
            println(s1)
        end
        for s2 = 1:M
            if basis[s2, s1] > 0
                s = s + 1
                final = basis[:, s1]
                final[s2] = final[s2] - 1
                s3 = target[s2]
                final[s3] = final[s3] + 1
                value = weight * final
                index = searchsortedfirst(table, value - mini)

                COO2[1, s] = s1
                COO2[2, s] = ind[index]
                COO2[3, s] = -sqrt(final[s3] * (final[s2] + 1))
            end
        end
    end
    COO2 = COO2[:, 1:s]
    Kin = sparse(COO2[1, :], COO2[2, :], COO2[3, :], dim, dim)
    return Kin = Kin + Kin'
end

function gstate(Kin, Interaction, Vlist)
    Cmax = zeros(length(Vlist))
    ge = zeros(length(Vlist))
    for s1 = 1:length(Vlist)
          print(s1, '\n')
        H = Kin + Vlist[s1] * Interaction
        d, gstate = eigs(H, nev=1, which=:SR)    # to solve the smallest algebraic eigenvalues
        gstate2 = abs2.(gstate)
        Cmax[s1] = maximum(gstate2)
        ge[s1] = d[1]
    end
    return ge, Cmax
end

@time begin
    N = 12
    M = 12
    dim = binomial(N + M - 1, M - 1)
    weight = sqrt.(100 * collect(1:M) .+ 3.0)'
    basis, table, ind, mini = bhm_basis(N, M, dim, weight)
    Interaction = hint(dim, basis, M)
    Kin = hkin(dim, basis, M, table, ind, mini)

    Vlist = 0:0.5:20
    ge, Cmax = gstate(Kin, Interaction, Vlist)
end

plot(Vlist, Cmax)
plot(Vlist, ge)

It is slower than matlab. Actually, it is much faster than matlab for all functions except for the function ‘gstate’, which calls the ‘eigs’ function from Arpack.

It seems that matlab is really good at matrix computation. I checked it. On my desktop, with matlab, each eigs call costs 9 seconds, while for julia, it is 14-18 seconds.

Does using MKL at the top fix it?

3 Likes

Note that Arpack.jl is a wrapper around the FORTRAN Arpack code, so what you seem to observe is FORTRAN running slower than Matlab, which is a bit surprising. Deep inside, Matlab is probably calling the same library. Worth also trying out ArnoldiMethod.jl, which is a Julia translation of Arpack, and provides eigs as well.

Is it possible that the tolerance that Matlab uses to assess convergence is different, or that it runs fewer iterations?

2 Likes

There are some opportunities to create view here instead of allocating new arrays.

We can avoid the creation an intermediate array.

5 Likes

actually, even without ‘view’, julia is much faster than matlab here. The problem is the eigs calls.

no.

no improvement with using MKL

Could you try using MKLSparse instead of MKL?

What is the size of H? I’m not entirely sure that eigs is the best function to use.

the size of the matrix is about 1.35 million.

1.35 million bytes? Elements? On one side?

1.35 million by 1.35 million

This might be an explanation, in matlab the default tolerance seems to be 1E-14

whereas in Julia it is set to 0.0

?eigs

# eigs(A; nev=6, ncv=max(20,2*nev+1), which=:LM, tol=0.0, maxiter=300, sigma=nothing, ritzvec=true, explicittransform=:auto, v0=zeros((0,)), check=0) -> (d,[v,],nconv,niter,nmult,resid)
2 Likes

I believe that means that in Arpack (eigs) the tolerance is reset to machine precision. Perhaps the OP could try setting tol to the same number?

Recent versions of Matlab don’t use Arpack. Perhaps the algorithms in KrylovKit.jl or ArnoldiMethod.jl would be more efficient for this problem (IIRC they are similar to the newer scheme in Matlab).

1 Like

I’ve had mixed success with ArnoldiMethod. The modes were not at all mass-orthogonal. Still not sure why.

KrylovKit does not converge at all.

As far as I know Arpack is still the only package in julia that actually works.

1 Like

That isn’t clear to me. Arpack is still cited in the Matlab manual today.

Here is my source:

Further down that thread is an updated way to get the faster sparse solution, which may help @jiang_ming_zhang as well.

3 Likes

Interesting. Did you get the eigenvectors to converge with ArnoldiMethod? I did not.

Yes

Could you possibly provide the code and the data?