In my code, I want to set pointers to OpenBLAS subroutines, to compile the code either in single or double precisions.
To do so, I define two modules and I define function interfaces for single(sgemv) or double precision(dgemv) OpenBLAS function:
module DoublePrecision
implicit none
integer, parameter :: precision = selected_real_kind(15, 307)
external dgemv
abstract interface
subroutine gemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy)
import :: precision
character :: trans
integer(4), value :: m, n, lda, incx, incy
real(precision), value :: alpha, beta
real(precision), dimension(lda, *), intent(in) :: a
real(precision), dimension(*), intent(in) :: x
real(precision), dimension(*), intent(inout) :: y
end subroutine gemv
end interface
procedure(gemv), pointer :: MatrixVectorMultiplication => null()
end module DoublePrecision
and
module SinglePrecision
implicit none
integer, parameter :: precision = selected_real_kind(6, 37)
external sgemv
abstract interface
subroutine gemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy)
import :: precision
character :: trans
integer(4), value :: m, n, lda, incx, incy
real(precision), value :: alpha, beta
real(precision), dimension(lda, *), intent(in) :: a
real(precision), dimension(*), intent(in) :: x
real(precision), dimension(*), intent(inout) :: y
end subroutine gemv
end interface
procedure(gemv), pointer :: MatrixVectorMultiplication => null()
end module SinglePrecision
In the main program, I take a flag in the compile command, and I use the appropriate module:
program test_MatrixMultiplication
#ifdef single
use SinglePrecision
MatrixVectorMultiplication => sgemv
#else
use DoublePrecision
MatrixVectorMultiplication => dgemv
#endif
end program test_MatrixMultiplication
To compile, I use the following command:
! for single precision
gfortran -Dsingle -cpp -L/usr/local/opt/openblas/lib/ -I/usr/local/opt/openblas/include/ -lopenblas -ffree-line-length-1024 SingleDoublePrecisionMatrixMultiplication.f90
! or for double precision
gfortran -Ddouble -cpp -L/usr/local/opt/openblas/lib/ -I/usr/local/opt/openblas/include/ -lopenblas -ffree-line-length-1024 SingleDoublePrecisionMatrixMultiplication.f90
I suppose that now I can use MatrixVectorMultiplication in my code, and the MatrixVectorMultiplication in the later development becomes independent of its dependecy to either sgemv, or dgemv.
But the compiler raise the following error:
Error: Explicit interface required for ‘sgemv’ at (1): value argument
I would appreciate anyone who can help me to find a solution to this problem.
As noted in the comments
value
. The blas interfaces are designed to be Fortran 77 callable, and thus do not use valueHere is how I would do it. If you look carefully at the two versions of the code you will see is all that has changed is setting the working precision kind wp
to single ( sp
) or double ( dp
)
ijb@ijb-Latitude-5410:~/work/stack$ cat blas.f90
Module blas_overload_module
Implicit None
integer, parameter :: sp = selected_real_kind( 6, 37 )
integer, parameter :: dp = selected_real_kind( 15, 307 )
Interface gemv
subroutine sgemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy)
import :: sp
character :: trans
integer :: m, n, lda, incx, incy
real(sp) :: alpha, beta
real(sp), dimension(lda, *), intent(in) :: a
real(sp), dimension(*), intent(in) :: x
real(sp), dimension(*), intent(inout) :: y
end subroutine sgemv
subroutine dgemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy)
import :: dp
character :: trans
integer :: m, n, lda, incx, incy
real(dp) :: alpha, beta
real(dp), dimension(lda, *), intent(in) :: a
real(dp), dimension(*), intent(in) :: x
real(dp), dimension(*), intent(inout) :: y
end subroutine dgemv
End Interface gemv
End Module blas_overload_module
Program testit
Use blas_overload_module, Only : sp, dp, gemv
Implicit None
Integer, Parameter :: n = 3
Real( sp ), Dimension( 1:n, 1:n ) :: as
Real( sp ), Dimension( 1:n ) :: xs
Real( sp ), Dimension( 1:n ) :: ys_mm, ys_blas
Real( dp ), Dimension( 1:n, 1:n ) :: ad
Real( dp ), Dimension( 1:n ) :: xd
Real( dp ), Dimension( 1:n ) :: yd_mm, yd_blas
! Change this line to set the precision - best put in a module
Integer, Parameter :: wp = sp
Real( wp ), Dimension( 1:n, 1:n ) :: aw
Real( wp ), Dimension( 1:n ) :: xw
Real( wp ), Dimension( 1:n ) :: yw_mm, yw_blas
Call Random_number( as )
Call Random_number( xs )
Call gemv( 'N', n, n, 1.0_sp, as, n, xs, 1, 0.0_sp, ys_blas, 1 )
ys_mm = Matmul( as, xs )
Write( *, * ) 'Single: ', ys_mm - ys_blas
Call Random_number( ad )
Call Random_number( xd )
Call gemv( 'N', n, n, 1.0_dp, ad, n, xd, 1, 0.0_dp, yd_blas, 1 )
yd_mm = Matmul( ad, xd )
Write( *, * ) 'Double: ', yd_mm - yd_blas
Call Random_number( aw )
Call Random_number( xw )
Call gemv( 'N', n, n, 1.0_wp, aw, n, xw, 1, 0.0_wp, yw_blas, 1 )
yw_mm = Matmul( aw, xw )
Write( *, * ) 'Working: ', yw_mm - yw_blas
End Program testit
ijb@ijb-Latitude-5410:~/work/stack$ gfortran -Wall -Wextra -fcheck=all -std=f2018 -O -g blas.f90 -lopenblas
ijb@ijb-Latitude-5410:~/work/stack$ ./a.out
Single: 0.00000000 0.00000000 0.00000000
Double: 0.0000000000000000 0.0000000000000000 0.0000000000000000
Working: 0.00000000 0.00000000 0.00000000
ijb@ijb-Latitude-5410:~/work/stack$ gfortran -Wall -Wextra -fcheck=all -std=f2018 -O -g blas.f90 -lopenblas
ijb@ijb-Latitude-5410:~/work/stack$ cat blas.f90
Module blas_overload_module
Implicit None
integer, parameter :: sp = selected_real_kind( 6, 37 )
integer, parameter :: dp = selected_real_kind( 15, 307 )
Interface gemv
subroutine sgemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy)
import :: sp
character :: trans
integer :: m, n, lda, incx, incy
real(sp) :: alpha, beta
real(sp), dimension(lda, *), intent(in) :: a
real(sp), dimension(*), intent(in) :: x
real(sp), dimension(*), intent(inout) :: y
end subroutine sgemv
subroutine dgemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy)
import :: dp
character :: trans
integer :: m, n, lda, incx, incy
real(dp) :: alpha, beta
real(dp), dimension(lda, *), intent(in) :: a
real(dp), dimension(*), intent(in) :: x
real(dp), dimension(*), intent(inout) :: y
end subroutine dgemv
End Interface gemv
End Module blas_overload_module
Program testit
Use blas_overload_module, Only : sp, dp, gemv
Implicit None
Integer, Parameter :: n = 3
Real( sp ), Dimension( 1:n, 1:n ) :: as
Real( sp ), Dimension( 1:n ) :: xs
Real( sp ), Dimension( 1:n ) :: ys_mm, ys_blas
Real( dp ), Dimension( 1:n, 1:n ) :: ad
Real( dp ), Dimension( 1:n ) :: xd
Real( dp ), Dimension( 1:n ) :: yd_mm, yd_blas
! Change this line to set the precision - best put in a module
Integer, Parameter :: wp = dp
Real( wp ), Dimension( 1:n, 1:n ) :: aw
Real( wp ), Dimension( 1:n ) :: xw
Real( wp ), Dimension( 1:n ) :: yw_mm, yw_blas
Call Random_number( as )
Call Random_number( xs )
Call gemv( 'N', n, n, 1.0_sp, as, n, xs, 1, 0.0_sp, ys_blas, 1 )
ys_mm = Matmul( as, xs )
Write( *, * ) 'Single: ', ys_mm - ys_blas
Call Random_number( ad )
Call Random_number( xd )
Call gemv( 'N', n, n, 1.0_dp, ad, n, xd, 1, 0.0_dp, yd_blas, 1 )
yd_mm = Matmul( ad, xd )
Write( *, * ) 'Double: ', yd_mm - yd_blas
Call Random_number( aw )
Call Random_number( xw )
Call gemv( 'N', n, n, 1.0_wp, aw, n, xw, 1, 0.0_wp, yw_blas, 1 )
yw_mm = Matmul( aw, xw )
Write( *, * ) 'Working: ', yw_mm - yw_blas
End Program testit
ijb@ijb-Latitude-5410:~/work/stack$ gfortran -Wall -Wextra -fcheck=all -std=f2018 -O -g blas.f90 -lopenblas
ijb@ijb-Latitude-5410:~/work/stack$ ./a.out
Single: 0.00000000 0.00000000 0.00000000
Double: 0.0000000000000000 0.0000000000000000 0.0000000000000000
Working: 0.0000000000000000 0.0000000000000000 0.0000000000000000
ijb@ijb-Latitude-5410:~/work/stack$