Search code examples
fortranmatrix-multiplicationlapackblasmatrix-inverse

Computing inverse of a matrix using LAPACK,BLAS in Fortran90


I am new to using LAPACK/BLAS. I want to compute the solution of an equation:

AU=F

I want to know what is the logical error in this part of the code. Where I use the solvers to input the matrix A which is of size ((xdiv-1)(ydiv-1),(xdiv-1)(ydiv-1)). Then, subsequently solve for the Equation. U=inverse(A) *f.

Where U and f are of the same size.(u((xdiv-1)(ydiv-1),1),f((xdiv-1)(ydiv-1),1)). I am getting a segmentation fault error while performing the matrix inverse.

Here is my code:

program main
double precision, allocatable :: A(:,:)
double precision, allocatable :: u(:,:), f(:,:)
double precision mesh(2), dx, dy
integer  xdiv, ydiv
 xdiv=55
 ydiv=55
 mesh(1)=.001
 mesh(2)=.001
 dx=mesh(1)
 dy=mesh(2)

allocate (A((xdiv-1)*(ydiv-1),(xdiv-1)*(ydiv-1))) 
allocate (Ainv((xdiv-1)*(ydiv-1),(xdiv-1)*(ydiv-1)))
allocate (u((xdiv-1)*(ydiv-1),1),f((xdiv-1)*(ydiv-1),1))

         do i =1,(xdiv-1)*(ydiv-1)
         A(i,i)=-2.d0*(1.d0/(dx**2)+1.d0/(dy**2))
         enddo
         do i=1,(xdiv-2)
         do j=1,(ydiv-1)
         A(i+(j-1)*(xdiv-1),i+(j-1)*(xdiv-1)+1)=1.d0/(dx**2)
         A(i+(j-1)*(xdiv-1)+1,i+(j-1)*(xdiv-1))=1.d0/(dx**2)
         enddo
         enddo
         do i=1,(xdiv-1)
         do j=1,(ydiv-2)
         A(i+(j-1)*(xdiv-1),i+(j)*(xdiv-1))=1.d0/(dy**2)
         A(i+(j)*(xdiv-1),i+(j-1)*(xdiv))=1.d0/(dy**2)
         enddo
         enddo

        do i=1,(xdiv-1)
         do j=1,(ydiv-1)
       xcoord = (i-1)*mesh(1)
       ycoord = (j-1)*mesh(2)
       xd=sin(2.0*3.14*xcoord)
       yd=sin(2.0*3.14*ycoord)
       f((i-1)*(xdiv-1)+j,1)= 8.0*3.1415*3.1415*xd*yd
         enddo
        enddo
    do i=1,(xdiv-1)*(ydiv-1)
     do j=1,(xdiv-1)*(ydiv-1)
      Ainv(i,j)=A(i,j)
     end do
    end do

 call DGETRF((xdiv-1)*(ydiv-1), (xdiv-1)*(ydiv-1), Ainv,  & 
  (xdiv-1)*(ydiv-1), ipiv, info)

 call DGETRI((xdiv-1)*(ydiv-1), Ainv, (xdiv-1)*(ydiv-1), ipiv, &
  work,(xdiv-1)*(ydiv-1), info)

 call DGEMV(N, (xdiv-1)*(ydiv-1), (xdiv-1)*(ydiv-1), 1.d0, Ainv, &
  (xdiv-1)*(ydiv-1), f, 1.d0, 0.d0, u, 1.d0)

  do i=1,(xdiv-1)*(ydiv-1)
   write(*,*) "u", u(i,1)
  enddo
 end program main

Basically, I compute the LU Decomposition, then invert it and then multiply. Please help me find the error and do suggest a better way to do this calculation if any.

NB: Some parts of variable defining/assigning values may seem redundant or inefficient that is because this is a part of a bigger code. I just extracted portions of it, to focus on the matrix inversion issue.


Solution

  • For the inversion of a general squared matrix have a look at this function (which uses LAPACK)

      function inv(A) result(Ainv)
        implicit none
        real,intent(in) :: A(:,:)
        real            :: Ainv(size(A,1),size(A,2))
        real            :: work(size(A,1))            ! work array for LAPACK
        integer         :: n,info,ipiv(size(A,1))     ! pivot indices
    
        ! Store A in Ainv to prevent it from being overwritten by LAPACK
        Ainv = A
        n = size(A,1)
        ! SGETRF computes an LU factorization of a general M-by-N matrix A
        ! using partial pivoting with row interchanges.
        call SGETRF(n,n,Ainv,n,ipiv,info)
        if (info.ne.0) stop 'Matrix is numerically singular!'
        ! SGETRI computes the inverse of a matrix using the LU factorization
        ! computed by SGETRF.
        call SGETRI(n,Ainv,n,ipiv,work,n,info)
        if (info.ne.0) stop 'Matrix inversion failed!'
    end function inv
    

    Also, you can find other useful Fortran subroutines and functions of matrix operations here.

    Basically, this follows the same procedure as you did except that, as mentioned in some comments, the arguments you are passing to the LAPACK subroutines are not correct. Have a look at those and fix them or just use this function.

    EDIT: Note that this is for single precision data types. You can easily adapt it for double precision.