How to call blas getrf, getri properly ? i want to create a benchmark inverse matrix using gauss, crout, native julia inv(A) and BLAS direct

Roberto_Bagyo
Tue, 06 May 2025 21:16:14 +0000
New to Julia

hello, i am new here. fortran based but just for fun actually. i tried to convert test_fpu.f90 from polyhedron benchmark inverse matrix.
i heard julia is multihreading and easier to code, then dgemm performance is higher said octavian github, so i want to compare with this test_fpu.f90 . basically it is benchmarking gauss 250x250 real random double inverse and check the difference as average error 1000 times; crout 250x250 real random double inverse and check the difference as average error 1000 times; crout 2000x2000 real random matrix double inverse and check the difference as average error 1 times, inverse matrix with lapack dgetrf and dgetri twice and check the difference as average error 1 times. I have succefully using inv(A) native but can’t to use directly

using LinearAlgebra
using Random
function average_error(A, A2)
    n = size(A, 1)
    return sum(abs.(A .- A2)) / (n * n)
end

function gauss_inverse!(A::Matrix{Float64})
    n = size(A, 1)
    B = copy(A)
    ipvt = collect(1:n)
    temp = zeros(n)

    for k in 1:n
        # Partial pivoting
        imax = argmax(abs.(B[k:n, k])) + k - 1
        m = imax
        if m != k
            ipvt[[m, k]] = ipvt[[k, m]]
            B[[m, k], :] .= B[[k, m], :]
        end

        d = 1.0 / B[k, k]
        temp .= B[:, k]  # Store current column

        # Perform elimination
        for j in 1:n
            c = B[k, j] * d
            B[:, j] .-= temp .* c
            B[k, j] = c
        end

        # Store back the transformed column
        B[:, k] .= temp .* (-d)
        B[k, k] = d
    end

    # Apply final permutation
    A[:, ipvt] .= B
    return A
end

function crout_inverse(A::Matrix{Float64})
    n = size(A, 1)
    L = zeros(n, n)
    U = Matrix{Float64}(I, n, n)
    Ainv = zeros(n, n)

    for j in 1:n
        # Compute column j of L
        for i in j:n
            L[i,j] = A[i,j] - sum(L[i,1:j-1] .* U[1:j-1,j])
        end

        # Compute row j of U
        for i in j+1:n
            U[j,i] = (A[j,i] - sum(L[j,1:j-1] .* U[1:j-1,i])) / L[j,j]
        end
    end

    # Solve for the inverse column by column
    for i in 1:n
        e = zeros(n)
        e[i] = 1.0
        y = L \ e
        Ainv[:,i] = U \ y
    end
    return Ainv
end

# Function to compute the inverse using LAPACK's dgetrf! and dgetri!
# dgetrf! computes the LU factorization with partial pivoting
# dgetri! computes the inverse from the LU factorization
function lapack_inverse(A::Matrix{Float64})
    n = size(A, 1)
    n != size(A, 2) && error("Matrix must be square")

    # Create a copy since we modify in-place
    A_copy = copy(A)

    # Step 1: LU factorization (getrf!)
    # In Julia, getrf! modifies A_copy in-place and returns (A_copy, ipiv, info)
    result = LinearAlgebra.LAPACK.getrf!(A_copy)
    A_lu = result[1] # This is the modified A_copy
    ipiv = result[2]
    info_getrf = result[3] # Use a specific variable name

    # Debug prints for info from getrf!
    println("DEBUG: info_getrf value after getrf!: ", info_getrf)
    println("DEBUG: info_getrf type after getrf!: ", typeof(info_getrf))

    # Check for errors from getrf!
    if info_getrf > 0
        error("Matrix is singular in getrf! at position $info_getrf (from getrf!)")
    elseif info_getrf < 0
        error("Invalid argument in getrf! at position $(-info_getrf) (from getrf!)")
    end

    # Step 2: Matrix inversion (getri!)
    # In Julia, getri! modifies A_lu in-place with the inverse and returns (A_lu, info)
    inv_result = LinearAlgebra.LAPACK.getri!(A_lu, ipiv)
    A_inv = inv_result[1] # This is the modified A_lu, which is now the inverse
    info_getri = inv_result[2] # Use a specific variable name

    # Debug prints for info from getri!
    println("DEBUG: info_getri value after getri!: ", info_getri)
    println("DEBUG: info_getri type after getri!: ", typeof(info_getri))


    # Check for errors in getri!
    if info_getri > 0
         error("Matrix is singular in getri! at position $info_getri (from getri!)")
    elseif info_getri < 0
        error("Invalid argument in getri! at position $(-info_getri) (from getri!)")
    end

    return A_inv
end


function run_benchmarks()
    small_n = 250
    large_n = 2000
    reps = 1000

    println("Small matrix benchmark ($small_n×$small_n), $reps repetitions")

    Random.seed!(42)
    A = randn(small_n, small_n)

    # === [1] Gauss ===
    println("\n[1] Gauss Elimination:")
    err_sum = 0.0
    t1 = @elapsed for i in 1:reps
     Ainv = copy(A)
     gauss_inverse!(Ainv)
     A2 = copy(Ainv)
     gauss_inverse!(A2)
     err_sum += average_error(A, A2)
     if i % 100 == 0
        println("  Iteration $i / $reps")
     end
    end
    println("Time: ", round(t1, digits=3), " seconds")
    println("Average error: ", err_sum / reps)


    # === [2] Crout ===
    println("\n[2] Crout Inversion:")
    err_sum = 0.0
    t2 = @elapsed for i in 1:reps
      Ainv = crout_inverse(A)
      A2 = crout_inverse(Ainv)
      err_sum += average_error(A, A2)
      if i % 100 == 0
        println("  Iteration $i / $reps")
      end
     end
    println("Time: ", round(t2, digits=3), " seconds")
    println("Average error: ", err_sum / reps)

    println("\nLarge matrix benchmark ($large_n×$large_n), 1 repetition")

    Random.seed!(123)
    B = randn(large_n, large_n)

    # === [3] Crout Large ===
    println("\n[3] Crout Large:")
    t3 = @elapsed begin
        Binv = crout_inverse(B)
        B2 = crout_inverse(Binv)
        err = average_error(B, B2)
        println("Average error: ", err)
    end
    println("Time: ", round(t3, digits=3), " seconds")

    # === [4] INV native Julia Large ===
    println("\n[4] Native Julia (inv) Large:")
    t4 = @elapsed begin
        Binv = inv(B)
        B2 = inv(Binv)
        err = average_error(B, B2)
        println("Average error: ", err)
    end
    println("Time: ", round(t4, digits=3), " seconds")


    # === [5] Lapack manual Large ===
    println("\n[5] LAPACK (LU + inv) Large:")
    t5 = @elapsed begin
        Binv = lapack_inverse(B)
        B2 = lapack_inverse(Binv)
        err = average_error(B, B2)
        println("Average error: ", err)
    end
    println("Time: ", round(t5, digits=3), " seconds")

end

run_benchmarks()

i got error like

C:\Temp>julia test_fpu_1corev3.jl
Small matrix benchmark (250×250), 1000 repetitions

[1] Gauss Elimination:
  Iteration 100 / 1000
  Iteration 200 / 1000
  Iteration 300 / 1000
  Iteration 400 / 1000
  Iteration 500 / 1000
  Iteration 600 / 1000
  Iteration 700 / 1000
  Iteration 800 / 1000
  Iteration 900 / 1000
  Iteration 1000 / 1000
Time: 37.984 seconds
Average error: 7.235891152085948e-15

[2] Crout Inversion:
  Iteration 100 / 1000
  Iteration 200 / 1000
  Iteration 300 / 1000
  Iteration 400 / 1000
  Iteration 500 / 1000
  Iteration 600 / 1000
  Iteration 700 / 1000
  Iteration 800 / 1000
  Iteration 900 / 1000
  Iteration 1000 / 1000
Time: 79.338 seconds
Average error: 3.599229967813043e-11

Large matrix benchmark (2000×2000), 1 repetition

[3] Crout Large:
Average error: 9.277946949708182e-10
Time: 44.639 seconds

[4] Native Julia (inv) Large:
Average error: 5.759763066411798e-13
Time: 2.297 seconds

[5] LAPACK (LU + inv) Large:
DEBUG: info_getrf value after getrf!: 0
DEBUG: info_getrf type after getrf!: Int64
DEBUG: info_getri value after getri!: 0.014762497648100295
DEBUG: info_getri type after getri!: Float64
ERROR: LoadError: Matrix is singular in getri! at position 0.014762497648100295 (from getri!)
Stacktrace:
 [1] error(s::String)
   @ Base .\error.jl:35
 [2] lapack_inverse(A::Matrix{Float64})
   @ Main C:\Temp\test_fpu_1corev3.jl:112
 [3] macro expansion
   @ C:\Temp\test_fpu_1corev3.jl:191 [inlined]
 [4] macro expansion
   @ .\timing.jl:421 [inlined]
 [5] run_benchmarks()
   @ Main C:\Temp\test_fpu_1corev3.jl:190
 [6] top-level scope
   @ C:\Temp\test_fpu_1corev3.jl:200
in expression starting at C:\Temp\test_fpu_1corev3.jl:200

C:\Temp>notepad test_fpu_1corev3.jl

that is using internal blas, i dont know to call using others. help how to call blas function, internally, openblas, mkl, octavian.?

6 posts - 2 participants