psfun_d_arnoldi.f90 Source File


This file depends on

sourcefile~~psfun_d_arnoldi.f90~~EfferentGraph sourcefile~psfun_d_arnoldi.f90 psfun_d_arnoldi.f90 sourcefile~psfun_d_serial_mod.f90 psfun_d_serial_mod.F90 sourcefile~psfun_d_arnoldi.f90->sourcefile~psfun_d_serial_mod.f90 sourcefile~psfun_d_krylov_mod.f90 psfun_d_krylov_mod.F90 sourcefile~psfun_d_arnoldi.f90->sourcefile~psfun_d_krylov_mod.f90 sourcefile~psfun_d_krylov_mod.f90->sourcefile~psfun_d_serial_mod.f90

Contents

Source Code


Source Code

! BSD 3-Clause License
!
! Copyright (c) 2020, Fabio Durastante
! All rights reserved.
!
! Redistribution and use in source and binary forms, with or without
! modification, are permitted provided that the following conditions are met:
!
! 1. Redistributions of source code must retain the above copyright notice, this
!    list of conditions and the following disclaimer.
!
! 2. Redistributions in binary form must reproduce the above copyright notice,
!    this list of conditions and the following disclaimer in the documentation
!    and/or other materials provided with the distribution.
!
! 3. Neither the name of the copyright holder nor the names of its
!    contributors may be used to endorse or promote products derived from
!    this software without specific prior written permission.
!
! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

submodule (psfun_d_krylov_mod) psfun_d_arnoldi_mod
  !! This modules implements the variant of the Arnoldi method for the
  !! computation of \(f(A)b\).

contains

module subroutine psfun_d_arnoldi(fun,a,desc_a,y,x,eps,info,itmax,itrace,istop,iter,err,res)
  !! Simple polynomial method based on the Arnoldi orthogonalization procedure,
  !! the method builds a basis \(V_k\) for the Krylov subspace
  !
  !! \begin{equation*}
  !! \mathcal{K}_k(A,x) = \{x,Ax,\ldots,A^{k-1}x\},
  !! \end{equation*}
  !
  !! and approximates \(y = f(\alpha A)x \approx \beta_1 V_k f(H_k) e_1\),for
  !! \(\beta_1 = \|x\|_2\), \(e_1\) the first vector of the canonical
  !! base of \(\mathbb{R}^k\), and \(H_k\) the Hessemberg matrix given
  !! by \(H_k = V_k^T A V_k\).
  use psb_base_mod
  use psfun_d_serial_mod
  use psb_d_krylov_conv_mod, only: log_header, log_conv, log_end
  implicit none

  type(psfun_d_serial), intent(inout)  :: fun  ! Function object
  type(psb_dspmat_type), intent(in)    :: a    ! Distribute sparse matrix
  type(psb_desc_type), intent(in)      :: desc_a ! Descriptor for the sparse matrix
  type(psb_d_vect_type), intent(inout) :: y ! Output vector
  type(psb_d_vect_type), intent(inout) :: x ! Input vector
  real(psb_dpk_), intent(in)           :: eps ! Requested tolerance
  integer(psb_ipk_), intent(out)       :: info  ! Output flag
  integer(psb_ipk_), optional, intent(in)  :: itmax ! Maximum number of iteration
  integer(psb_ipk_), optional, intent(in)  :: itrace ! Trace for logoutput
  integer(psb_ipk_), optional, intent(in)  :: istop ! Stop criterion
  integer(psb_ipk_), optional, intent(out) :: iter ! Number of iteration
  real(psb_dpk_), optional, intent(out) :: err ! Last estimate error
  real(psb_dpk_), optional, allocatable, intent(out) :: res(:) ! Vector of the residuals

  !Local Variables
  integer(psb_ipk_) :: litmax, itrace_, istop_
  integer(psb_lpk_) :: mglob
  integer(psb_ipk_) :: n_row, n_col, nl, naux, itx, i, k
  real(psb_dpk_)    :: scaling
  !Variables of possible large size:
  real(psb_dpk_), allocatable   :: aux(:)
  real(psb_dpk_), allocatable   :: h(:,:), rs(:), e(:), yk(:)
  type(psb_d_vect_type), allocatable :: v(:)
  type(psb_d_vect_type)              :: w, xt
  ! Error log
  real(psb_dpk_) :: deps, errden, errnum, derr
  integer(psb_ipk_) :: err_act
  type(psb_ctxt_type) :: ctxt
  integer(psb_ipk_) :: iam, np

  character(len=20)           :: name
  character(len=60)           :: methdname

  ! debug
  ! real(psb_dpk_), allocatable    :: va(:)

  ! Information on the distributed environment
  info = psb_success_
  name = 'ARNOLDI'
  methdname = trim(name) // trim("+") // trim(fun%fname) // trim("+") // trim(fun%variant)
  call psb_erractionsave(err_act)
  ctxt = desc_a%get_context()
  call psb_info(ctxt, iam, np)

  mglob = desc_a%get_global_rows()
  n_row = desc_a%get_local_rows()
  n_col = desc_a%get_local_cols()

  ! Select the stop-criterion
  if (present(istop)) then
    istop_ = istop
  else
    istop_ = 1
  endif

  if ((istop_ < 1 ).or.(istop_ > 2 ) ) then
    info=psb_err_invalid_istop_
    err=info
    call psb_errpush(info,name,i_err=(/istop_/))
    goto 9999
  endif

  ! Maximum number of iterations
  if (present(itmax)) then
    litmax = itmax
  else
    litmax = 200
  endif
  nl = litmax

  naux = 4*n_col

  ! What do i print?
  if (present(itrace)) then
    itrace_ = itrace
  else
    itrace_ = 0
  end if

  ! Chek sanity of the inputs both on the local and the global
  if (.not.allocated(x%v)) then
    info = psb_err_invalid_vect_state_
    call psb_errpush(info,name)
    goto 9999
  endif
  if (.not.allocated(y%v)) then
    info = psb_err_invalid_vect_state_
    call psb_errpush(info,name)
    goto 9999
  endif
  call psb_chkvect(mglob,lone,x%get_nrows(),lone,lone,desc_a,info)
  if(info /= psb_success_) then
    info=psb_err_from_subroutine_
    call psb_errpush(info,name,a_err='psb_chkvect on X')
    goto 9999
  end if
  call psb_chkvect(mglob,lone,y%get_nrows(),lone,lone,desc_a,info)
  if(info /= psb_success_) then
    info=psb_err_from_subroutine_
    call psb_errpush(info,name,a_err='psb_chkvect on Y')
    goto 9999
  end if

  ! Allocate memory for the Krylov basis and the auxiliary quantities
  allocate(aux(naux),h(nl+1,nl+1),rs(nl+1),stat=info)

  if (info == psb_success_) call psb_geall(v,desc_a,info,n=nl+1)
  if (info == psb_success_) call psb_geall(w,desc_a,info)
  if (info == psb_success_) call psb_geall(xt,desc_a,info)
  if (info == psb_success_) call psb_geasb(v,desc_a,info,mold=x%v)
  if (info == psb_success_) call psb_geasb(w,desc_a,info,mold=x%v)
  if (info == psb_success_) call psb_geasb(xt,desc_a,info,mold=x%v)
  if (info /= psb_success_) then
    info=psb_err_from_subroutine_non_
    call psb_errpush(info,name)
    goto 9999
  end if

  ! We use the scaling in the computation of the Krylov subspace
  scaling = fun%scaling
  call fun%set("SCALING",done,info)

  ! LogVariables
  errnum = dzero
  errden = done
  deps   = eps
  ! Start the iteration
  if ((itrace_ > 0).and.(iam == 0)) call log_header(methdname)

  call psb_geaxpby(done,x,dzero,v(1),desc_a,info) ! v(1) = x
  if (info /= psb_success_) then
    info=psb_err_from_subroutine_non_
    call psb_errpush(info,name)
    goto 9999
  end if
  rs(1) = psb_genrm2(v(1),desc_a,info) ! rs(1) = ||v(1)||_2
  rs(2:) = dzero
  if (info /= psb_success_) then
    info=psb_err_from_subroutine_non_
    call psb_errpush(info,name)
    goto 9999
  end if

  call v(1)%scal(done/rs(1)) !v(1) = v(1)/||v(1)||_2
  itx = 0
  arnoldicycle: do i = 2, nl, 1
    itx = itx + 1
    ! write(*,'("Iteration ",i2)')i
    call psb_spmm(scaling,a,v(i-1),dzero,w,desc_a,info,work=aux) ! w = A v(i)
    ! va = w%get_vect()
    ! write(*,'("w(1) =",f8.2)')va(1)
    if (info /= psb_success_) then
      info=psb_err_from_subroutine_non_
      call psb_errpush(info,name)
      goto 9999
    end if
    do k = 1, i-1, 1
      h(k,i-1) = psb_gedot(v(k),w,desc_a,info)            !h_{k,i-1}=w^T*v(k)
      ! write(*,'("h(",i2,","i2") =",f8.2)')k,i-1,h(k,i-1)
      call psb_geaxpby(-h(k,i-1),v(k),done,w,desc_a,info) !w = w - h_{k,i-1}v(k)
    end do
    h(i,i-1) = psb_genrm2(w,desc_a,info)                  !h_{i,i-1} = ||w||_2
    ! write(*,'("h(",i2,","i2") =",f8.2)')i,i-1,h(i,i-1)
    call psb_geaxpby(done/h(i,i-1),w,dzero,v(i),desc_a,info) !v(i) = w/h_{i,i-1}

    ! A posteriori error estimate:
    ! This is the default a posteriori-error estimate, it costs the computation
    ! of the matrix function at each iteration to use the computed values
    if( istop_ == 1) then
      !
      ! build y and then compute the residual as
      ! rs(j) = \| x \|_2 h_{i,i-1} | e_{i-1}^T f(H_{i-1}) e_1 |
      !
      allocate(e(i-1), stat=info)
      if (info /= 0) then
        info=psb_err_from_subroutine_non_
        call psb_errpush(info,name)
        goto 9999
      end if
      e(:) = dzero

      allocate(yk(i-1), stat=info)
      if (info /= 0) then
        info=psb_err_from_subroutine_non_
        call psb_errpush(info,name)
        goto 9999
      end if

      e(1) = done
      call fun%apply(h(1:i-1,1:i-1),yk,e,info)
      if (info /= psb_success_) then
        info=psb_err_from_subroutine_non_
        call psb_errpush(info,name)
        goto 9999
      end if

      errnum = rs(1)*h(i,i-1)*abs( yk(i-1) );
      errden = norm2(yk)
      rs(i) = errnum/errden

      ! log of the error estimate
      if (itrace_ > 0) &
           & call log_conv(methdname,iam,itx,itrace_,errnum,errden,deps)

      if (allocated(e)) deallocate(e, stat=info)
      if (info /= 0) then
        info=psb_err_from_subroutine_non_
        call psb_errpush(info,name)
        goto 9999
      end if
      ! Check convergence
      if(rs(i) < deps) then
        exit arnoldicycle
      else
        if (allocated(yk)) deallocate(yk, stat=info)
        if (info /= 0) then
          info=psb_err_from_subroutine_non_
          call psb_errpush(info,name)
          goto 9999
        end if
      end if

    end if

  end do arnoldicycle
  !
  ! Assemble the final solution
  !
  if(.not.allocated(yk)) then
    ! First we look if we have arrived here having not used an a posteriori
    ! error estimate, and thus not having computed the solution in the Krylov
    ! subspace.
    allocate(e(itx-1), stat=info)
    if (info /= 0) then
      info=psb_err_from_subroutine_non_
      call psb_errpush(info,name)
      goto 9999
    end if
    e(:) = dzero

    allocate(yk(itx-1), stat=info)
    if (info /= 0) then
      info=psb_err_from_subroutine_non_
      call psb_errpush(info,name)
      goto 9999
    end if
    e(1) = done
    call fun%apply(h(1:itx-1,1:itx-1),yk,e,info)
    if (info /= psb_success_) then
      info=psb_err_from_subroutine_non_
      call psb_errpush(info,name)
      goto 9999
    end if

    if (allocated(e)) deallocate(e, stat=info)
    if (info /= 0) then
      info=psb_err_from_subroutine_non_
      call psb_errpush(info,name)
      goto 9999
    end if
  end if

  ! Final Log
  call log_end(methdname,iam,itx,itrace_,errnum,errden,deps,err=derr,iter=iter)
  if (present(err)) err = derr
  if (present(res)) then
    allocate(res(itx+1),stat=info)
    res(1:itx) = rs(1:itx)
    res(itx+1) = derr
  end if

  call  psb_geaxpby(yk(1),v(1),dzero,y,desc_a,info)
  do i=2,itx-1,1
    call  psb_geaxpby(yk(i),v(i),done,y,desc_a,info)
  end do
  call y%scal(rs(1))

  ! Set back the scaling
  call fun%set("SCALING",scaling,info)

  ! Free the memory
  if (info == psb_success_) call psb_gefree(v,desc_a,info)
  if (info == psb_success_) call psb_gefree(w,desc_a,info)
  if (info == psb_success_) call psb_gefree(xt,desc_a,info)
  if (info == psb_success_) deallocate(aux,h,rs,stat=info)
  if (info /= psb_success_) then
    info=psb_err_from_subroutine_non_
    call psb_errpush(info,name)
    goto 9999
  end if

  ! Close and return
  call psb_erractionrestore(err_act)
return

9999 call psb_error_handler(err_act)
return

end subroutine psfun_d_arnoldi

end submodule