stdlib_linalg_cholesky.fypp Source File


Source Code

#:include "common.fypp"
#:set RC_KINDS_TYPES = REAL_KINDS_TYPES + CMPLX_KINDS_TYPES
! Cholesky factorization of a matrix, based on LAPACK *POTRF functions
submodule (stdlib_linalg) stdlib_linalg_cholesky
     use stdlib_linalg_constants
     use stdlib_linalg_lapack, only: potrf
     use stdlib_linalg_state, only: linalg_state_type, linalg_error_handling, LINALG_ERROR, &
         LINALG_INTERNAL_ERROR, LINALG_VALUE_ERROR
     implicit none(type,external)

     
     character(*), parameter :: this = 'cholesky'

     contains

     elemental subroutine handle_potrf_info(info,triangle,lda,n,err)
         character, intent(in) :: triangle
         integer(ilp), intent(in) :: info,lda,n
         type(linalg_state_type), intent(out) :: err

         ! Process output
         select case (info)
            case (0)
                ! Success
            case (-1)
                err = linalg_state_type(this,LINALG_INTERNAL_ERROR,'invalid triangle selection: ', &
                                   triangle,'. should be U/L')
            case (-2)
                err = linalg_state_type(this,LINALG_VALUE_ERROR,'invalid matrix size n=',n)
            case (-4)
                err = linalg_state_type(this,LINALG_VALUE_ERROR,'invalid lda=',lda,': is < n = ',n)
            case (1:)
                err = linalg_state_type(this,LINALG_ERROR,'cannot complete factorization:',info, &
                                   '-th order leading minor is not positive definite')
            case default
                err = linalg_state_type(this,LINALG_INTERNAL_ERROR,'catastrophic error')
         end select

     end subroutine handle_potrf_info
     
     #:for rk,rt,ri in RC_KINDS_TYPES
     
     ! Compute the Cholesky factorization of a symmetric / Hermitian matrix, A = L*L^T = U^T*U. 
     ! The factorization is returned in-place, overwriting matrix A
     pure module subroutine stdlib_linalg_${ri}$_cholesky_inplace(a,lower,other_zeroed,err) 
         !> Input matrix a[m,n]
         ${rt}$, intent(inout), target :: a(:,:)
         !> [optional] is the lower or upper triangular factor required? Default = lower
         logical(lk), optional, intent(in) :: lower
         !> [optional] should the unused half of the return matrix be zeroed out? Default: yes
         logical(lk), optional, intent(in) :: other_zeroed
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err

         !> Local variables
         type(linalg_state_type) :: err0
         integer(ilp) :: lda,n,info,i,j
         logical(lk) :: lower_,other_zeroed_
         character :: triangle
         ${rt}$, parameter :: zero = 0.0_${rk}$
         
         !> Check if the lower or upper factor is required. 
         !> Default: use lower factor
         lower_ = .true.
         if (present(lower)) lower_ = lower
         triangle = merge('L','U',lower_)
         
         !> Check if the unused half of the return matrix should be zeroed out (default).
         !> Otherwise it is unused and will contain garbage. 
         other_zeroed_ = .true.
         if (present(other_zeroed)) other_zeroed_ = other_zeroed

         !> Problem size
         lda  = size(a,1,kind=ilp)
         n    = size(a,2,kind=ilp)

         ! Check sizes
         if (n<1 .or. lda<1 .or. lda<n) then
             
             err0 = linalg_state_type(this,LINALG_VALUE_ERROR, &
                                      'invalid matrix size: a(m,n)=',[lda,n])
            
         else
         
             ! Compute factorization
             call potrf(triangle,n,a,lda,info)
             call handle_potrf_info(info,triangle,lda,n,err0)
                      
             ! Zero-out the unused part of matrix A
             clean_unused: if (other_zeroed_ .and. err0%ok()) then    
                 if (lower_) then 
                    forall (j=2:n) a(:j-1,j) = zero
                 else
                    forall (j=1:n-1) a(j+1:,j) = zero
                 endif
             endif clean_unused  
         
         endif 

         ! Process output and return
         call linalg_error_handling(err0,err)

     end subroutine stdlib_linalg_${ri}$_cholesky_inplace

     ! Compute the Cholesky factorization of a symmetric / Hermitian matrix, A = L*L^T = U^T*U. 
     ! The factorization is returned as a separate matrix
     pure module subroutine stdlib_linalg_${ri}$_cholesky(a,c,lower,other_zeroed,err) 
         !> Input matrix a[n,n]
         ${rt}$, intent(in) :: a(:,:)
         !> Output matrix with Cholesky factors c[n,n]
         ${rt}$, intent(out) :: c(:,:)         
         !> [optional] is the lower or upper triangular factor required? Default = lower
         logical(lk), optional, intent(in) :: lower
         !> [optional] should the unused half of the return matrix be zeroed out? Default: yes
         logical(lk), optional, intent(in) :: other_zeroed
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
         
         type(linalg_state_type) :: err0
         integer(ilp) :: lda,n,ldc,nc
         
         ! Check C sizes
         lda = size(a,1,kind=ilp)
           n = size(a,2,kind=ilp)
         ldc = size(c,1,kind=ilp)
          nc = size(c,2,kind=ilp)
          
         if (lda<1 .or. n<1 .or. lda<n .or. ldc<n .or. nc<n) then 
            
            err0 = linalg_state_type(this,LINALG_VALUE_ERROR, &
                                     'invalid matrix sizes: a=',[lda,n], &
                                                          ' c=',[ldc,nc])
        
         else
            
            ! Copy data in
            c(:n,:n) = a(:n,:n)
            
            ! Get cholesky factors
            call stdlib_linalg_${ri}$_cholesky_inplace(c,lower,other_zeroed,err0)            
            
         end if            
         
         ! Process output and return
         call linalg_error_handling(err0,err)
         
     end subroutine stdlib_linalg_${ri}$_cholesky

     ! Compute the Cholesky factorization of a symmetric / Hermitian matrix, A = L*L^T = U^T*U. 
     ! Function interface
     pure module function stdlib_linalg_${ri}$_cholesky_fun(a,lower,other_zeroed) result(c)
         !> Input matrix a[n,n]
         ${rt}$, intent(in) :: a(:,:)
         !> Output matrix with Cholesky factors c[n,n]
         ${rt}$ :: c(size(a, 1),size(a, 2))         
         !> [optional] is the lower or upper triangular factor required? Default = lower
         logical(lk), optional, intent(in) :: lower
         !> [optional] should the unused half of the return matrix be zeroed out? Default: yes
         logical(lk), optional, intent(in) :: other_zeroed
         
         c = a
         
         call stdlib_linalg_${ri}$_cholesky_inplace(c,lower,other_zeroed)

     end function stdlib_linalg_${ri}$_cholesky_fun

     #:endfor

end submodule stdlib_linalg_cholesky