Fpm build with BLAS, local and CI

AFAIK, fpm has a link option in the toml file to support linking with BLAS and LAPACK.

[build]
...
link = ["blas"]

However, I don’t understand two important things:

  • How do I tell fpm where BLAS is to be found?
  • How can the linking be made to work locally (I have OpenBlas in MSYS2) as well as in the CI, in a transparent manner?

Thanks.

These might help

1 Like

The Fortran Standard Library now ships a full BLAS and LAPACK implementation in generic precision, that you can use either with the original API or with a NumPy-styled high-level API.

External BLAS/LAPACK libraries are supported via a cpp macro. For example, you can define:

[dependencies]
stdlib = { git="https://github.com/fortran-lang/stdlib", branch="stdlib-fpm", preprocess.cpp.macros= ["STDLIB_EXTERNAL_BLAS", "STDLIB_EXTERNAL_LAPACK"] }

In this case, both the original API and the high-level API will use the external library. On macOS, that would be running with --flag '-framework Accelerate'.

The link approach you’re suggesting also works, provided that such libraries can be found by the linker in one of the LD_LIBRARY_PATH paths.

3 Likes

Thank you both. I was able solve the issue.

Essentially, my problem was that I didn’t understand the practical result of adding link = ["mylib"] to the toml file. In the meantime, I realized that there is no magic: it “just” adds the flag -lmylib (for gcc). So, in my case, I just had to change to :man_facepalming::

[build]
...
link = ["openblas"]

This works just fine in Windows+MSYS2. To make it work in the CI/CD, I just had to add 2 command lines to install OpenBLAS.

...
 jobs:
  linux-gcc-build:
      - name: Install GFortran, OpenBLAS
        if: contains(matrix.os, 'ubuntu')
        run: |
          sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_V} 100 \
          --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${GCC_V} \
          --slave /usr/bin/gcov gcov /usr/bin/gcov-${GCC_V}
          sudo apt-get install libopenblas-dev  # <<<<<<

...      
  msys2-gcc-build:
      
      - name: Install MSYS2, GFortran, fpm, OpenBLAS
        uses: msys2/setup-msys2@v2
        with:
          msystem: MINGW64
          update: true
          path-type: inherit
          install: |
            mingw-w64-x86_64-gcc-fortran
            mingw-w64-x86_64-fpm
            mingw-w64-x86_64-openblas      # <<<<<<

I just had a look. It’s amazing work. Yet another good reason to use stdlib.

How did you guys do that? Did you parse the reference BLAS and LAPACK from netlib, converted that to fyyp and… and … and…?

2 Likes

The Netlib version is great in that the code is very well structured (although old). For example, if you have an external function, it will be in a block preceded by an “external functions” comment line, etc.

So I did it with a custom Python script with a combination of code modernization, information gathering from the documentation, plus some final hand made finishes. You can check the script out at the wip repository here:

1 Like

While employed at NAG, this was known as “NAG style”, and there were scripts that would convert any FORTRAN 77 subprogram source to that style. LRZ still holds on to documentation for those tools nag_decs - NAGWare Fortran Tools - f77 Tools

1 Like

Hi Federico, I’m new to fortran and tried this a few days ago, worked like a charm if the use statements where done from app in the fpm project, but I’m still tryoing to make them work in src. perhaps you know what needs to be done

Hi @mcditoos, sorry I’ve just left for vacation. If you can show your issue with an example, I can take a look when I’m back thanks!

No worries, thanks for replying on your vacation, Enjoy it ! On another note. is there any plans to include matrix exponentials/ square roots and related functions in stdlib_linear algebra?

As you mentioned all functions of LAPACK and BLAS are available. Can you please point how I can I use dgeev? When I am using its shows an error that

Error: Procedure ‘dgeev’ called with an implicit interface at (1) [-Werror=implicit-interface]
compilation terminated due to -fmax-errors=1.
Compilation failed for object " app_main.f90.o "
stopping due to failed compilation
STOP 1

Thank you for your help.

@Bhanu you can find several examples in the stdlib examples folder.

Is what you want really to fine-tune geev, or do you just seek the eigenvalues to a general matrix? Because if so, I suggest you to use the high-level API for eigenvalues: eig and eigvals:

If you have a real symmetric or complex Hermitian matrix, you may want to use eigh and eigvalsh. The full help is here.

PS as you mentioned elsewhere, if you build your project with fpm please ensure to have stdlib as a dependency.

Thank you for your reply however, I wanted to solve a generalized eigenvalue problem (also known as the generalized eigenpair problem), which involves finding eigenvalues and eigenvectors for a pair of matrices A and B, such that:

A⋅x=λ⋅B⋅x

Where A and B are square matrices, λ are the eigenvalues, and x are the corresponding eigenvectors (in general dgeev is used for this purpose).

We don’t have a high-level API for that yet (only SciPy has it afaict), but it’s not a problem as you can just use the generalized LAPACK interface, see this example:

program example_dggev
  use iso_fortran_env, only: dp => real64
  use stdlib_linalg_lapack, only: ggev ! generic-type wrapper to *GGEV functions
  implicit none
  real(dp), allocatable :: A(:,:), B(:,:), ALPHAR(:), ALPHAI(:), BETA(:), VL(:,:), VR(:,:), WORK(:)
  integer :: N, LDA, LDB, LDVL, LDVR, INFO, LWORK, i

  ! Define matrix sizes
  N = 2

  ! Allocate matrices and vectors
  allocate(A(N, N), B(N, N), ALPHAR(N), ALPHAI(N), BETA(N), VL(N, N), VR(N, N))

  ! Define matrices A and B
  A = reshape([1.0_dp, 2.0_dp, 3.0_dp, 4.0_dp], shape=[N, N]) ! Example matrix A
  B = reshape([2.0_dp, 0.0_dp, 0.0_dp, 2.0_dp], shape=[N, N]) ! Example matrix B

  ! Leading dimensions
  LDA = N
  LDB = N
  LDVL = N
  LDVR = N

  ! Query workspace (LWORK)
  LWORK = -1
  allocate(WORK(1))
  call ggev('N', 'V', N, A, LDA, B, LDB, ALPHAR, ALPHAI, BETA, VL, LDVL, VR, LDVR, WORK, LWORK, INFO)
  LWORK = nint(WORK(1))
  deallocate(WORK); allocate(WORK(LWORK))
  print *, 'LWORK = ',LWORK
  
  ! Solve the generalized eigenvalue problem A*x = λ*B*x
  call ggev('N', 'V', N, A, LDA, B, LDB, ALPHAR, ALPHAI, BETA, VL, LDVL, VR, LDVR, WORK, LWORK, INFO)

  ! Check for success
  if (INFO == 0) then
    print *, "Generalized eigenvalues (lam = ALPHAR/BETA):"
    print *, (ALPHAR(i)/BETA(i), ALPHAI(i)/BETA(i), i=1, N)
    print *, "Right eigenvectors (VR):"
    print *, VR
  else
    print *, "Error: ggev failed with INFO =", INFO
  end if

  ! Deallocate arrays
  deallocate(A, B, ALPHAR, ALPHAI, BETA, VL, VR)
end program example_dggev

Note the correct LAPACK function is GGEV, not the simpler GEEV.

Please open an Issue on the stdlib page and I’m happy to help add handling of the non-identity eigenvector case!

1 Like