module LightKrylov_IterativeSolvers !! This module provides some of the most important computational routines provided by !! `LightKrylov`. These include: !! !! - `eigs` : Compute the leading eigenpairs of a square linear operator \( A \). !! - `eighs` : Compute the leading eigenpairs of a symmetric positive definite !! operator \( A \). !! - `svds` : Compute the leading singular triplets of a linear operator \( A \). !! - `gmres` : Solve the linear system \( Ax = b \) using the *generalized minimum residual method*. !! - `cg` : Solve the linear system \( Ax = b \) where \( A \) is symmetric positive definite using the *Conjugate Gradient* method. !! !! It also provides abstract interfaces to pass user-defined solvers and preconditioners !! to `LightKrylov`. Note that these features are still experimental however. !-------------------------------------------- !----- Fortran Standard Library ----- !-------------------------------------------- use iso_fortran_env, only: output_unit use stdlib_sorting, only: sort_index use stdlib_optval, only: optval use stdlib_io_npy, only: save_npy use stdlib_stats, only: median !------------------------------- !----- LightKrylov ----- !------------------------------- use LightKrylov_Constants use LightKrylov_Utils use LightKrylov_Logger, only: log_warning, log_error, log_message, log_information, & & log_debug, stop_error, type_error, check_info use LightKrylov_Timing, only: timer => global_lightkrylov_timer, time_lightkrylov use LightKrylov_AbstractVectors use LightKrylov_AbstractLinops use LightKrylov_BaseKrylov implicit none private character(len=*), parameter :: this_module = 'LK_Solvers' character(len=*), parameter :: this_module_long = 'LightKrylov_IterativeSolvers' character(len=*), parameter :: eigs_output = 'eigs_output.txt' public :: abstract_linear_solver_rsp public :: abstract_linear_solver_rdp public :: abstract_linear_solver_csp public :: abstract_linear_solver_cdp public :: save_eigenspectrum public :: eigs public :: eighs public :: svds public :: gmres public :: gmres_rsp public :: gmres_rdp public :: gmres_csp public :: gmres_cdp public :: fgmres public :: fgmres_rsp public :: fgmres_rdp public :: fgmres_csp public :: fgmres_cdp public :: cg public :: cg_rsp public :: cg_rdp public :: cg_csp public :: cg_cdp public :: write_results_rsp public :: write_results_rdp public :: write_results_csp public :: write_results_cdp !------------------------------------------------------ !----- ABSTRACT PRECONDITIONER DEFINITION ----- !------------------------------------------------------ type, abstract, public :: abstract_precond_rsp contains private procedure(abstract_apply_rsp), pass(self), public, deferred :: apply end type abstract interface subroutine abstract_apply_rsp(self, vec, iter, current_residual, target_residual) !! Abstract interface to apply a preconditioner in `LightKrylov`. import abstract_precond_rsp, abstract_vector_rsp import sp class(abstract_precond_rsp), intent(inout) :: self !! Preconditioner. class(abstract_vector_rsp), intent(inout) :: vec !! Input/Output vector. integer, optional, intent(in) :: iter !! Current iteration number. real(sp), optional, intent(in) :: current_residual real(sp), optional, intent(in) :: target_residual end subroutine abstract_apply_rsp end interface type, abstract, public :: abstract_precond_rdp contains private procedure(abstract_apply_rdp), pass(self), public, deferred :: apply end type abstract interface subroutine abstract_apply_rdp(self, vec, iter, current_residual, target_residual) !! Abstract interface to apply a preconditioner in `LightKrylov`. import abstract_precond_rdp, abstract_vector_rdp import dp class(abstract_precond_rdp), intent(inout) :: self !! Preconditioner. class(abstract_vector_rdp), intent(inout) :: vec !! Input/Output vector. integer, optional, intent(in) :: iter !! Current iteration number. real(dp), optional, intent(in) :: current_residual real(dp), optional, intent(in) :: target_residual end subroutine abstract_apply_rdp end interface type, abstract, public :: abstract_precond_csp contains private procedure(abstract_apply_csp), pass(self), public, deferred :: apply end type abstract interface subroutine abstract_apply_csp(self, vec, iter, current_residual, target_residual) !! Abstract interface to apply a preconditioner in `LightKrylov`. import abstract_precond_csp, abstract_vector_csp import sp class(abstract_precond_csp), intent(inout) :: self !! Preconditioner. class(abstract_vector_csp), intent(inout) :: vec !! Input/Output vector. integer, optional, intent(in) :: iter !! Current iteration number. real(sp), optional, intent(in) :: current_residual real(sp), optional, intent(in) :: target_residual end subroutine abstract_apply_csp end interface type, abstract, public :: abstract_precond_cdp contains private procedure(abstract_apply_cdp), pass(self), public, deferred :: apply end type abstract interface subroutine abstract_apply_cdp(self, vec, iter, current_residual, target_residual) !! Abstract interface to apply a preconditioner in `LightKrylov`. import abstract_precond_cdp, abstract_vector_cdp import dp class(abstract_precond_cdp), intent(inout) :: self !! Preconditioner. class(abstract_vector_cdp), intent(inout) :: vec !! Input/Output vector. integer, optional, intent(in) :: iter !! Current iteration number. real(dp), optional, intent(in) :: current_residual real(dp), optional, intent(in) :: target_residual end subroutine abstract_apply_cdp end interface !-------------------------------------------------------- !----- GENERIC INTERFACE FOR LINEAR SOLVERS ----- !-------------------------------------------------------- abstract interface subroutine abstract_linear_solver_rsp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) !! Abstract interface to use a user-defined linear solver in `LightKrylov`. import abstract_linop_rsp, abstract_vector_rsp, abstract_opts, abstract_metadata, abstract_precond_rsp, sp class(abstract_linop_rsp), intent(inout) :: A !! Linear operator to invert. class(abstract_vector_rsp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_rsp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. In case of successful exit, the flag should return the number of iterations required for convergence. real(sp), optional, intent(in) :: rtol !! Relative solver tolerance real(sp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_rsp), optional, intent(inout) :: preconditioner !! Preconditioner. class(abstract_opts), optional, intent(in) :: options !! Options passed to the linear solver. logical, optional, intent(in) :: transpose !! Determine whether \(\mathbf{A}\) (`.false.`) or \(\mathbf{A}^T\) (`.true.`) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine abstract_linear_solver_rsp subroutine abstract_linear_solver_rdp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) !! Abstract interface to use a user-defined linear solver in `LightKrylov`. import abstract_linop_rdp, abstract_vector_rdp, abstract_opts, abstract_metadata, abstract_precond_rdp, dp class(abstract_linop_rdp), intent(inout) :: A !! Linear operator to invert. class(abstract_vector_rdp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_rdp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. In case of successful exit, the flag should return the number of iterations required for convergence. real(dp), optional, intent(in) :: rtol !! Relative solver tolerance real(dp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_rdp), optional, intent(inout) :: preconditioner !! Preconditioner. class(abstract_opts), optional, intent(in) :: options !! Options passed to the linear solver. logical, optional, intent(in) :: transpose !! Determine whether \(\mathbf{A}\) (`.false.`) or \(\mathbf{A}^T\) (`.true.`) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine abstract_linear_solver_rdp subroutine abstract_linear_solver_csp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) !! Abstract interface to use a user-defined linear solver in `LightKrylov`. import abstract_linop_csp, abstract_vector_csp, abstract_opts, abstract_metadata, abstract_precond_csp, sp class(abstract_linop_csp), intent(inout) :: A !! Linear operator to invert. class(abstract_vector_csp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_csp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. In case of successful exit, the flag should return the number of iterations required for convergence. real(sp), optional, intent(in) :: rtol !! Relative solver tolerance real(sp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_csp), optional, intent(inout) :: preconditioner !! Preconditioner. class(abstract_opts), optional, intent(in) :: options !! Options passed to the linear solver. logical, optional, intent(in) :: transpose !! Determine whether \(\mathbf{A}\) (`.false.`) or \(\mathbf{A}^T\) (`.true.`) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine abstract_linear_solver_csp subroutine abstract_linear_solver_cdp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) !! Abstract interface to use a user-defined linear solver in `LightKrylov`. import abstract_linop_cdp, abstract_vector_cdp, abstract_opts, abstract_metadata, abstract_precond_cdp, dp class(abstract_linop_cdp), intent(inout) :: A !! Linear operator to invert. class(abstract_vector_cdp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_cdp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. In case of successful exit, the flag should return the number of iterations required for convergence. real(dp), optional, intent(in) :: rtol !! Relative solver tolerance real(dp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_cdp), optional, intent(inout) :: preconditioner !! Preconditioner. class(abstract_opts), optional, intent(in) :: options !! Options passed to the linear solver. logical, optional, intent(in) :: transpose !! Determine whether \(\mathbf{A}\) (`.false.`) or \(\mathbf{A}^T\) (`.true.`) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine abstract_linear_solver_cdp end interface !------------------------------------------------------- !----- ----- !----- GENERALIZED MINIMUM RESIDUAL METHOD ----- !----- ----- !------------------------------------------------------- !----- Options and Metadata ----- type, extends(abstract_opts), public :: gmres_sp_opts !! GMRES options. integer :: kdim = 30 !! Dimension of the Krylov subspace (default: 30). integer :: maxiter = 10 !! Maximum number of `gmres` restarts (default: 10). logical :: if_print_metadata = .false. !! Print iteration metadata on exit (default: .false.). logical :: sanity_check = .true. !! Performs extra matrix-vector product for sanity check. end type type, extends(abstract_metadata), public :: gmres_sp_metadata !! GMRES metadata. integer :: n_iter = 0 !! Total iteration counter. integer :: n_inner = 0 !! Number of inner iterations. integer :: n_outer = 0 !! Number of outer iterations (i.e. restarts) real(sp), dimension(:), allocatable :: res !! Residual history. logical :: converged = .false. !! Convergence flag. integer :: info = 0 !! Copy of the information flag for completeness. contains procedure, pass(self), public :: print => print_gmres_sp procedure, pass(self), public :: reset => reset_gmres_sp end type interface module subroutine print_gmres_sp(self, reset_counters, verbose) class(gmres_sp_metadata), intent(inout) :: self logical, optional, intent(in) :: reset_counters !! Reset all counters to zero after printing? logical, optional, intent(in) :: verbose !! Print the full residual history? end subroutine module subroutine reset_gmres_sp(self) class(gmres_sp_metadata), intent(inout) :: self end subroutine end interface type, extends(abstract_opts), public :: gmres_dp_opts !! GMRES options. integer :: kdim = 30 !! Dimension of the Krylov subspace (default: 30). integer :: maxiter = 10 !! Maximum number of `gmres` restarts (default: 10). logical :: if_print_metadata = .false. !! Print iteration metadata on exit (default: .false.). logical :: sanity_check = .true. !! Performs extra matrix-vector product for sanity check. end type type, extends(abstract_metadata), public :: gmres_dp_metadata !! GMRES metadata. integer :: n_iter = 0 !! Total iteration counter. integer :: n_inner = 0 !! Number of inner iterations. integer :: n_outer = 0 !! Number of outer iterations (i.e. restarts) real(dp), dimension(:), allocatable :: res !! Residual history. logical :: converged = .false. !! Convergence flag. integer :: info = 0 !! Copy of the information flag for completeness. contains procedure, pass(self), public :: print => print_gmres_dp procedure, pass(self), public :: reset => reset_gmres_dp end type interface module subroutine print_gmres_dp(self, reset_counters, verbose) class(gmres_dp_metadata), intent(inout) :: self logical, optional, intent(in) :: reset_counters !! Reset all counters to zero after printing? logical, optional, intent(in) :: verbose !! Print the full residual history? end subroutine module subroutine reset_gmres_dp(self) class(gmres_dp_metadata), intent(inout) :: self end subroutine end interface !----- Interfaces for the GMRES solvers ----- interface gmres !! ### Description !! !! Solve a square linear system of equations !! !! \[ !! Ax = b !! \] !! !! using the *Generalized Minimum RESidual* (GMRES) method. !! !! **References** !! !! - Saad Y. and Schultz M. H. "GMRES: A generalized minimal residual algorithm for !! solving nonsymmetric linear systems." SIAM Journal on Scientific and Statistical !! Computing, 7(3), 1986. !! !! ### Syntax !! !! ```fortran !! call gmres(A, b, x, info [, rtol] [, atol] [, preconditioner] [, options] [, transpose]) !! ``` !! !! ### Arguments !! !! - `A` : Linear operator derived from one of the `abstract_linop` provided by the !! `AbstractLinops` module. It is an `intent(inout)` argument. !! !! - `b` : Right-hand side vector derived from one the `abstract_vector` types provided !! by the `AbstractVectors` module. It needs to have the same type and kind as `A`. !! It is an `intent(in)` argument. !! !! - `x` : On entry, initial guess for the solution. On exit, the solution computed by !! gmres. It is a vector derived from one the `abstract_vector` types provided by the !! `AbstractVectors` module. It needs to have the same type and kind as `A`. It is !! an `intent(inout)` argument. !! !! - `info` : `integer` information flag. !! !! - `rtol` (optional) : `real` relative tolerance for the solver. !! !! - `atol` (optional) : `real` absolute tolerance for the solver. !! !! - `preconditioner` (optional) : Right preconditioner used to solve the system. It needs !! to be consistent with the `abstract_preconditioner` interface. !! It is an `intent(in)` argument. !! !! - `options` (optional) : Container for the gmres options given by the `gmres_opts` type. !! It is an `intent(in)` argument. !! !! - `transpose` (optional): `logical` flag controlling whether \( Ax = b\) or \( A^H x = b \) is being solver. !! !! - `meta` (optional) : Container for the gmres metada. It needs to be of type `gmres_medata`. ! --- Interface for GMRES with Abstract Linops and Abstract Vectors --- module subroutine gmres_rsp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) class(abstract_linop_rsp), intent(inout) :: A !! Linear operator to be inverted. class(abstract_vector_rsp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_rsp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. real(sp), optional, intent(in) :: rtol !! Relative solver tolerance real(sp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_rsp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine dense_gmres_rsp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) real(sp), intent(in) :: A(:, :) !! Linear operator to be inverted. real(sp), intent(in) :: b(:) !! Right-hand side vector. real(sp), intent(inout) :: x(:) !! Solution vector. integer, intent(out) :: info !! Information flag. real(sp), optional, intent(in) :: rtol !! Relative solver tolerance real(sp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_rsp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine gmres_rdp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) class(abstract_linop_rdp), intent(inout) :: A !! Linear operator to be inverted. class(abstract_vector_rdp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_rdp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. real(dp), optional, intent(in) :: rtol !! Relative solver tolerance real(dp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_rdp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine dense_gmres_rdp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) real(dp), intent(in) :: A(:, :) !! Linear operator to be inverted. real(dp), intent(in) :: b(:) !! Right-hand side vector. real(dp), intent(inout) :: x(:) !! Solution vector. integer, intent(out) :: info !! Information flag. real(dp), optional, intent(in) :: rtol !! Relative solver tolerance real(dp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_rdp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine gmres_csp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) class(abstract_linop_csp), intent(inout) :: A !! Linear operator to be inverted. class(abstract_vector_csp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_csp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. real(sp), optional, intent(in) :: rtol !! Relative solver tolerance real(sp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_csp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine dense_gmres_csp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) complex(sp), intent(in) :: A(:, :) !! Linear operator to be inverted. complex(sp), intent(in) :: b(:) !! Right-hand side vector. complex(sp), intent(inout) :: x(:) !! Solution vector. integer, intent(out) :: info !! Information flag. real(sp), optional, intent(in) :: rtol !! Relative solver tolerance real(sp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_csp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine gmres_cdp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) class(abstract_linop_cdp), intent(inout) :: A !! Linear operator to be inverted. class(abstract_vector_cdp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_cdp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. real(dp), optional, intent(in) :: rtol !! Relative solver tolerance real(dp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_cdp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine dense_gmres_cdp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) complex(dp), intent(in) :: A(:, :) !! Linear operator to be inverted. complex(dp), intent(in) :: b(:) !! Right-hand side vector. complex(dp), intent(inout) :: x(:) !! Solution vector. integer, intent(out) :: info !! Information flag. real(dp), optional, intent(in) :: rtol !! Relative solver tolerance real(dp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_cdp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine end interface !---------------------------------------------------------------- !----- ----- !----- FLEXIBLE GENERALIZED MINIMUM RESIDUAL METHOD ----- !----- ----- !---------------------------------------------------------------- !----- Options and Metadata ----- type, extends(abstract_opts), public :: fgmres_sp_opts !! FGMRES options. integer :: kdim = 30 !! Dimension of the Krylov subspace (default: 30). integer :: maxiter = 10 !! Maximum number of `fgmres` restarts (default: 10). logical :: if_print_metadata = .false. !! Print iteration metadata on exit (default: .false.). logical :: sanity_check = .true. !! Performs extra matrix-vector product for sanity check. end type type, extends(abstract_metadata), public :: fgmres_sp_metadata !! FGMRES metadata. integer :: n_iter = 0 !! Total iteration counter. integer :: n_inner = 0 !! Number of inner iterations. integer :: n_outer = 0 !! Number of outer iterations (i.e. restarts) real(sp), dimension(:), allocatable :: res !! Residual history. logical :: converged = .false. !! Convergence flag. integer :: info = 0 !! Copy of the information flag for completeness. contains procedure, pass(self), public :: print => print_fgmres_sp procedure, pass(self), public :: reset => reset_fgmres_sp end type interface module subroutine print_fgmres_sp(self, reset_counters, verbose) class(fgmres_sp_metadata), intent(inout) :: self logical, optional, intent(in) :: reset_counters !! Reset all counters to zero after printing? logical, optional, intent(in) :: verbose !! Print the full residual history? end subroutine module subroutine reset_fgmres_sp(self) class(fgmres_sp_metadata), intent(inout) :: self end subroutine end interface type, extends(abstract_opts), public :: fgmres_dp_opts !! FGMRES options. integer :: kdim = 30 !! Dimension of the Krylov subspace (default: 30). integer :: maxiter = 10 !! Maximum number of `fgmres` restarts (default: 10). logical :: if_print_metadata = .false. !! Print iteration metadata on exit (default: .false.). logical :: sanity_check = .true. !! Performs extra matrix-vector product for sanity check. end type type, extends(abstract_metadata), public :: fgmres_dp_metadata !! FGMRES metadata. integer :: n_iter = 0 !! Total iteration counter. integer :: n_inner = 0 !! Number of inner iterations. integer :: n_outer = 0 !! Number of outer iterations (i.e. restarts) real(dp), dimension(:), allocatable :: res !! Residual history. logical :: converged = .false. !! Convergence flag. integer :: info = 0 !! Copy of the information flag for completeness. contains procedure, pass(self), public :: print => print_fgmres_dp procedure, pass(self), public :: reset => reset_fgmres_dp end type interface module subroutine print_fgmres_dp(self, reset_counters, verbose) class(fgmres_dp_metadata), intent(inout) :: self logical, optional, intent(in) :: reset_counters !! Reset all counters to zero after printing? logical, optional, intent(in) :: verbose !! Print the full residual history? end subroutine module subroutine reset_fgmres_dp(self) class(fgmres_dp_metadata), intent(inout) :: self end subroutine end interface !----- Interfaces for the FGMRES solvers ----- interface fgmres !! ### Description !! !! Solve a square linear system of equations !! !! \[ !! Ax = b !! \] !! !! using the *Flexible Generalized Minimum RESidual* (FGMRES) method. !! !! **References** !! !! - Saad Y. and Schultz M. H. "GMRES: A generalized minimal residual algorithm for !! solving nonsymmetric linear systems." SIAM Journal on Scientific and Statistical !! Computing, 7(3), 1986. !! !! ### Syntax !! !! ```fortran !! call fgmres(A, b, x, info [, rtol] [, atol] [, preconditioner] [, options] [, transpose]) !! ``` !! !! ### Arguments !! !! - `A` : Linear operator derived from one of the `abstract_linop` provided by the !! `AbstractLinops` module. It is an `intent(inout)` argument. !! !! - `b` : Right-hand side vector derived from one the `abstract_vector` types provided !! by the `AbstractVectors` module. It needs to have the same type and kind as `A`. !! It is an `intent(in)` argument. !! !! - `x` : On entry, initial guess for the solution. On exit, the solution computed by !! gmres. It is a vector derived from one the `abstract_vector` types provided by the !! `AbstractVectors` module. It needs to have the same type and kind as `A`. It is !! an `intent(inout)` argument. !! !! - `info` : `integer` information flag. !! !! - `rtol` (optional) : `real` relative tolerance for the solver. !! !! - `atol` (optional) : `real` absolute tolerance for the solver. !! !! - `preconditioner` (optional) : Right preconditioner used to solve the system. It needs to be consistent with the !! `abstract_preconditioner` interface. It is an `intent(in)` argument. !! !! - `options` (optional) : Container for the gmres options given by the `gmres_opts` type. !! It is an `intent(in)` argument. !! !! - `transpose` (optional): `logical` flag controlling whether \( Ax = b\) or !! \( A^H x = b \) is being solved. module subroutine fgmres_rsp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) class(abstract_linop_rsp), intent(inout) :: A !! Linear operator to be inverted. class(abstract_vector_rsp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_rsp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. real(sp), optional, intent(in) :: rtol !! Relative solver tolerance real(sp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_rsp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine dense_fgmres_rsp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) real(sp), intent(in) :: A(:, :) !! Linear operator to be inverted. real(sp), intent(in) :: b(:) !! Right-hand side vector. real(sp), intent(inout) :: x(:) !! Solution vector. integer, intent(out) :: info !! Information flag. real(sp), optional, intent(in) :: rtol !! Relative solver tolerance real(sp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_rsp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine fgmres_rdp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) class(abstract_linop_rdp), intent(inout) :: A !! Linear operator to be inverted. class(abstract_vector_rdp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_rdp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. real(dp), optional, intent(in) :: rtol !! Relative solver tolerance real(dp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_rdp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine dense_fgmres_rdp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) real(dp), intent(in) :: A(:, :) !! Linear operator to be inverted. real(dp), intent(in) :: b(:) !! Right-hand side vector. real(dp), intent(inout) :: x(:) !! Solution vector. integer, intent(out) :: info !! Information flag. real(dp), optional, intent(in) :: rtol !! Relative solver tolerance real(dp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_rdp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine fgmres_csp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) class(abstract_linop_csp), intent(inout) :: A !! Linear operator to be inverted. class(abstract_vector_csp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_csp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. real(sp), optional, intent(in) :: rtol !! Relative solver tolerance real(sp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_csp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine dense_fgmres_csp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) complex(sp), intent(in) :: A(:, :) !! Linear operator to be inverted. complex(sp), intent(in) :: b(:) !! Right-hand side vector. complex(sp), intent(inout) :: x(:) !! Solution vector. integer, intent(out) :: info !! Information flag. real(sp), optional, intent(in) :: rtol !! Relative solver tolerance real(sp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_csp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine fgmres_cdp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) class(abstract_linop_cdp), intent(inout) :: A !! Linear operator to be inverted. class(abstract_vector_cdp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_cdp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. real(dp), optional, intent(in) :: rtol !! Relative solver tolerance real(dp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_cdp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine dense_fgmres_cdp(A, b, x, info, rtol, atol, preconditioner, options, transpose, meta) complex(dp), intent(in) :: A(:, :) !! Linear operator to be inverted. complex(dp), intent(in) :: b(:) !! Right-hand side vector. complex(dp), intent(inout) :: x(:) !! Solution vector. integer, intent(out) :: info !! Information flag. real(dp), optional, intent(in) :: rtol !! Relative solver tolerance real(dp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_cdp), optional, intent(inout) :: preconditioner !! Preconditioner (optional). class(abstract_opts), optional, intent(in) :: options !! GMRES options. logical, optional, intent(in) :: transpose !! Whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine end interface !--------------------------------------------- !----- ----- !----- CONJUGATE GRADIENT METHOD ----- !----- ----- !--------------------------------------------- !----- Options and Metadata ----- type, extends(abstract_opts), public :: cg_sp_opts !! Conjugate gradient options. integer :: maxiter = 100 !! Maximum number of `cg` iterations (default: 100). logical :: if_print_metadata = .false. !! Print interation metadata on exit (default = .false.) end type type, extends(abstract_metadata), public :: cg_sp_metadata !! Conjugate gradient metadata. integer :: n_iter = 0 !! Iteration counter real(sp), dimension(:), allocatable :: res !! Residual history logical :: converged = .false. !! Convergence flag integer :: info = 0 !! Copy of the information flag for completeness contains procedure, pass(self), public :: print => print_cg_sp procedure, pass(self), public :: reset => reset_cg_sp end type interface module subroutine print_cg_sp(self, reset_counters, verbose) class(cg_sp_metadata), intent(inout) :: self logical, optional, intent(in) :: reset_counters !! Reset all counters to zero after printing? logical, optional, intent(in) :: verbose !! Print the residual full residual history? end subroutine module subroutine reset_cg_sp(self) class(cg_sp_metadata), intent(inout) :: self end subroutine end interface type, extends(abstract_opts), public :: cg_dp_opts !! Conjugate gradient options. integer :: maxiter = 100 !! Maximum number of `cg` iterations (default: 100). logical :: if_print_metadata = .false. !! Print interation metadata on exit (default = .false.) end type type, extends(abstract_metadata), public :: cg_dp_metadata !! Conjugate gradient metadata. integer :: n_iter = 0 !! Iteration counter real(dp), dimension(:), allocatable :: res !! Residual history logical :: converged = .false. !! Convergence flag integer :: info = 0 !! Copy of the information flag for completeness contains procedure, pass(self), public :: print => print_cg_dp procedure, pass(self), public :: reset => reset_cg_dp end type interface module subroutine print_cg_dp(self, reset_counters, verbose) class(cg_dp_metadata), intent(inout) :: self logical, optional, intent(in) :: reset_counters !! Reset all counters to zero after printing? logical, optional, intent(in) :: verbose !! Print the residual full residual history? end subroutine module subroutine reset_cg_dp(self) class(cg_dp_metadata), intent(inout) :: self end subroutine end interface !----- Interfaces for the Conjugate Gradient solvers ----- interface cg !! ### Description !! !! Given a symmetric (positive definite) matrix \( A \), solves the linear system !! !! \[ !! Ax = b !! \] !! !! using the *Conjugate Gradient* method. !! !! **References** !! !! - Hestenes, M. R., and Stiefel, E. (1952). "Methods of Conjugate Gradients for Solving !! Linear Systems," Journal of Research of the National Bureau of Standards, !! 49(6), 409–436. !! !! ### Syntax !! !! ```fortran !! call cg(A, b, x, info [, rtol] [, atol] [, preconditioner] [, options]) !! ``` !! !! ### Arguments !! !! - `A` : Linear operator derived from one of the `abstract_sym_linop` or `abstract_hermitian_linop` types provided ! by the `AbstractLinops` module. It is an `intent(inout)` argument. !! !! - `b` : Right-hand side vector derived from one the `abstract_vector` types provided !! by the `AbstractVectors` module. It needs to have the same type and kind as `A`. !! It is an `intent(in)` argument. !! !! - `x` : On entry, initial guess for the solution. On exit, the solution computed by !! cg. It is a vector derived from one the `abstract_vector` types provided by the !! `AbstractVectors` module. It needs to have the same type and kind as `A`. It is !! an `intent(inout)` argument. !! !! - `info` : `integer` information flag. !! !! - `rtol` (optional) : `real` relative tolerance for the solver. !! !! - `atol` (optional) : `real` absolute tolerance for the solver. !! !! - `preconditioner` (optional) : Right preconditioner used to solve the system. It needs to be consistent with the !! `abstract_preconditioner` interface. It is an `intent(in)` argument. !! !! - `options` (optional) : Container for the gmres options given by the `cg_opts` type. !! It is an `intent(in)` argument. module subroutine cg_rsp(A, b, x, info, rtol, atol, preconditioner, options, meta) class(abstract_sym_linop_rsp), intent(inout) :: A !! Linear operator to be inverted. class(abstract_vector_rsp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_rsp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. real(sp), optional, intent(in) :: rtol !! Relative solver tolerance real(sp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_rsp), optional, intent(inout) :: preconditioner !! Preconditioner (not yet supported). type(cg_sp_opts), optional, intent(in) :: options !! Options for the conjugate gradient solver. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine cg_rdp(A, b, x, info, rtol, atol, preconditioner, options, meta) class(abstract_sym_linop_rdp), intent(inout) :: A !! Linear operator to be inverted. class(abstract_vector_rdp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_rdp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. real(dp), optional, intent(in) :: rtol !! Relative solver tolerance real(dp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_rdp), optional, intent(inout) :: preconditioner !! Preconditioner (not yet supported). type(cg_dp_opts), optional, intent(in) :: options !! Options for the conjugate gradient solver. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine cg_csp(A, b, x, info, rtol, atol, preconditioner, options, meta) class(abstract_hermitian_linop_csp), intent(inout) :: A !! Linear operator to be inverted. class(abstract_vector_csp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_csp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. real(sp), optional, intent(in) :: rtol !! Relative solver tolerance real(sp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_csp), optional, intent(inout) :: preconditioner !! Preconditioner (not yet supported). type(cg_sp_opts), optional, intent(in) :: options !! Options for the conjugate gradient solver. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine module subroutine cg_cdp(A, b, x, info, rtol, atol, preconditioner, options, meta) class(abstract_hermitian_linop_cdp), intent(inout) :: A !! Linear operator to be inverted. class(abstract_vector_cdp), intent(in) :: b !! Right-hand side vector. class(abstract_vector_cdp), intent(inout) :: x !! Solution vector. integer, intent(out) :: info !! Information flag. real(dp), optional, intent(in) :: rtol !! Relative solver tolerance real(dp), optional, intent(in) :: atol !! Absolute solver tolerance class(abstract_precond_cdp), optional, intent(inout) :: preconditioner !! Preconditioner (not yet supported). type(cg_dp_opts), optional, intent(in) :: options !! Options for the conjugate gradient solver. class(abstract_metadata), optional, intent(out) :: meta !! Metadata. end subroutine end interface !------------------------------------------ !----- ----- !----- SINGULAR VALUE SOLVERS ----- !----- ----- !------------------------------------------ interface svds !! ### Description !! !! Computes the leading singular triplets of an arbitrary linear operator \(A\) !! using the Lanczos iterative process. Given a linear operator \(A\), it finds !! the leading singular values and singular vectors such that: !! !! \[ !! \begin{aligned} !! Av & = \sigma u \\ !! A^H u & = \sigma v. !! \end{aligned} !! \] !! !! The subspaces \(U\) and \(V\) are constructed via Lanczos factorization, resulting in !! a bidiagonal matrix \(B\). The singular values of \(A\) are approximated by those of !! \(B\) and the singular vectors are computed accordingly. !! !! **References** !! !! - Golub, G. H., & Kahan, W. (1965). "Calculating the Singular Values and !! Pseudo-Inverse of a Matrix." !! - Baglama, J., & Reichel, L. (2005). "Augmented implicitly restarted Lanczos !! bidiagonalization methods." !! - R. M. Larsen. "Lanczos bidiagonalization with partial reorthogonalization." !! Technical Report, 1998. !! !! ### Syntax !! !! ```fortran !! call svds(A, U, S, V, residuals, info [, kdim] [,tolerance]) !! ``` !! !! ### Arguments !! !! - `A` : Linear operator derived from `abstract_sym_linop_rsp`, `abstract_sym_linop_rdp`, !! `abstract_hermitian_linop_csp` or `abstract_hermitian_linop_cdp` whose leading !! eigenpairs need to be computed. It is an `intent(inout)` argument. !! !! - `U` : Array of `abstract_vectors` with the same type and kind as `A`. On exit, it !! contains the left singular vectors of `A`. Note that the dimension of `U` fixes !! the number of eigenpairs computed. !! !! - `S` : Rank-1 array of `real` numbers. On exit, it contains the leading !! singular values of `A`. It is an `intent(out)` argument. !! !! - `V` : Array of `abstract_vectors` with the same type and kind as `A`. On exit, it !! contains the left singular vectors of `A`. Note that the dimension of `U` fixes !! the number of eigenpairs computed. !! !! - `residuals` : Rank-1 array of `real` numbers. On exit, it contains the residuals !! associated with each singular triplet. It is an `intent(out)` argument. !! !! - `info` : `integer` Information flag. !! !! - `kdim` (*optional*) : `integer`, maximum dimension of the Krylov subspace used to !! approximate the leading singular triplets. It is an `intent(in)` !! argument. By default, `kdim = 4*size(X)`. !! !! - `tolerance` (*optional*) : `real` tolerance below which a triplet is considered as being converged. It is an !! `intent(in)` agument. By default, tolerance = rtol_sp` or `tolerance = rtol_dp`. !! @note !! This implementation does not currently include an automatic restarting procedure !! such as `krylov_schur` for `eigs`. This is work in progress. !! @endnote module subroutine svds_rsp(A, U, S, V, residuals, info, u0, kdim, tolerance, write_intermediate) class(abstract_linop_rsp), intent(inout) :: A !! Linear operator whose leading singular triplets need to be computed. class(abstract_vector_rsp), intent(out) :: U(:) !! Leading left singular vectors. real(sp), allocatable, intent(out) :: S(:) !! Leading singular values. class(abstract_vector_rsp), intent(out) :: V(:) !! Leading right singular vectors. real(sp), allocatable, intent(out) :: residuals(:) !! Residuals associated to each Ritz eigenpair. integer, intent(out) :: info !! Information flag. class(abstract_vector_rsp), optional, intent(in) :: u0 integer, optional, intent(in) :: kdim !! Desired number of eigenpairs. real(sp), optional, intent(in) :: tolerance !! Tolerance. logical, optional, intent(in) :: write_intermediate !! Write intermediate eigenvalues to file during iteration? end subroutine module subroutine svds_rdp(A, U, S, V, residuals, info, u0, kdim, tolerance, write_intermediate) class(abstract_linop_rdp), intent(inout) :: A !! Linear operator whose leading singular triplets need to be computed. class(abstract_vector_rdp), intent(out) :: U(:) !! Leading left singular vectors. real(dp), allocatable, intent(out) :: S(:) !! Leading singular values. class(abstract_vector_rdp), intent(out) :: V(:) !! Leading right singular vectors. real(dp), allocatable, intent(out) :: residuals(:) !! Residuals associated to each Ritz eigenpair. integer, intent(out) :: info !! Information flag. class(abstract_vector_rdp), optional, intent(in) :: u0 integer, optional, intent(in) :: kdim !! Desired number of eigenpairs. real(dp), optional, intent(in) :: tolerance !! Tolerance. logical, optional, intent(in) :: write_intermediate !! Write intermediate eigenvalues to file during iteration? end subroutine module subroutine svds_csp(A, U, S, V, residuals, info, u0, kdim, tolerance, write_intermediate) class(abstract_linop_csp), intent(inout) :: A !! Linear operator whose leading singular triplets need to be computed. class(abstract_vector_csp), intent(out) :: U(:) !! Leading left singular vectors. real(sp), allocatable, intent(out) :: S(:) !! Leading singular values. class(abstract_vector_csp), intent(out) :: V(:) !! Leading right singular vectors. real(sp), allocatable, intent(out) :: residuals(:) !! Residuals associated to each Ritz eigenpair. integer, intent(out) :: info !! Information flag. class(abstract_vector_csp), optional, intent(in) :: u0 integer, optional, intent(in) :: kdim !! Desired number of eigenpairs. real(sp), optional, intent(in) :: tolerance !! Tolerance. logical, optional, intent(in) :: write_intermediate !! Write intermediate eigenvalues to file during iteration? end subroutine module subroutine svds_cdp(A, U, S, V, residuals, info, u0, kdim, tolerance, write_intermediate) class(abstract_linop_cdp), intent(inout) :: A !! Linear operator whose leading singular triplets need to be computed. class(abstract_vector_cdp), intent(out) :: U(:) !! Leading left singular vectors. real(dp), allocatable, intent(out) :: S(:) !! Leading singular values. class(abstract_vector_cdp), intent(out) :: V(:) !! Leading right singular vectors. real(dp), allocatable, intent(out) :: residuals(:) !! Residuals associated to each Ritz eigenpair. integer, intent(out) :: info !! Information flag. class(abstract_vector_cdp), optional, intent(in) :: u0 integer, optional, intent(in) :: kdim !! Desired number of eigenpairs. real(dp), optional, intent(in) :: tolerance !! Tolerance. logical, optional, intent(in) :: write_intermediate !! Write intermediate eigenvalues to file during iteration? end subroutine end interface !------------------------------------------------ !----- ----- !----- HERMITIAN EIGENVALUE SOLVERS ----- !----- ----- !------------------------------------------------ interface eighs !! ### Description !! !! Computes the leading eigenpairs of a symmetric operator \(A\) !! using the Lanczos iterative process. Given a square linear operator \(A\), it finds !! the leading eigvalues and eigvectors such that: !! !! \[ !! Ax = \lambda x !! \] !! !! The subspace \(X\) is constructed via Lanczos factorization, resulting in a symmetric !! tridiagonal matrix \(T\). The eigenvalues of \(A\) are approximated by those of \(T\) !! and the eigenvectors are computed accordingly. !! !! **References** !! !! - Lanczos, C. (1950). "An Iteration Method for the Solution of the Eigenvalue Problem !! of Linear Differential and Integral Operators". United States Governm. Press Office. !! !! ### Syntax !! !! ```fortran !! call eighs(A, X, eigvals, residuals, info [, kdim] [,tolerance]) !! ``` !! !! ### Arguments !! !! - `A` : Linear operator derived from `abstract_sym_linop_rsp`, `abstract_sym_linop_rdp`, !! `abstract_hermitian_linop_csp` or `abstract_hermitian_linop_cdp` whose leading !! eigenpairs need to be computed. It is an `intent(inout)` argument. !! !! - `X` : Array of `abstract_vectors` with the same type and kind as `A`. On exit, it !! contains the leading eigenvectors of `A`. Note that the dimension of `X` fixes !! the number of eigenpairs computed. !! !! - `eigvals` : Rank-1 array of `real` numbers. On exit, it contains the leading !! eigenvalues of `A`. It is an `intent(out)` argument. !! !! - `residuals` : Rank-1 array of `real` numbers. On exit, it contains the residuals !! associated with each eigenpairs. It is an `intent(out)` argument. !! !! - `info` : `integer` Information flag. !! !! - `kdim` (*optional*) : `integer`, maximum dimension of the Krylov subspace used to !! approximate the leading eigenpairs. It is an `intent(in)` !! argument. By default, `kdim = 4*size(X)`. !! !! - `tolerance` (*optional*) : `real` tolerance below which an eigenpair is considered as !! being converged. It is an `intent(in)` agument. By default, !! `tolerance = rtol_sp` or `tolerance = rtol_dp`. !! @note !! This implementation does not currently include an automatic restarting procedure !! such as `krylov_schur` for `eigs`. This is work in progress. !! @endnote module subroutine eighs_rsp(A, X, eigvals, residuals, info, x0, kdim, tolerance, write_intermediate) class(abstract_sym_linop_rsp), intent(inout) :: A !! Linear operator whose leading eigenpairs need to be computed. class(abstract_vector_rsp), intent(out) :: X(:) !! Leading eigevectors of \( \mathbf{A} \). real(sp), allocatable, intent(out) :: eigvals(:) !! Leading eigenvalues of \( \mathbf{A} \). real(sp), allocatable, intent(out) :: residuals(:) !! Residuals associated to each Ritz eigenpairs. integer, intent(out) :: info !! Information flag. class(abstract_vector_rsp), optional, intent(in) :: x0 !! Optional starting vector to generate the Krylov subspace. integer, optional, intent(in) :: kdim !! Desired number of eigenpairs. real(sp), optional, intent(in) :: tolerance !! Tolerance logical, optional, intent(in) :: write_intermediate !! Write intermediate eigenvalues to file during iteration? end subroutine module subroutine eighs_rdp(A, X, eigvals, residuals, info, x0, kdim, tolerance, write_intermediate) class(abstract_sym_linop_rdp), intent(inout) :: A !! Linear operator whose leading eigenpairs need to be computed. class(abstract_vector_rdp), intent(out) :: X(:) !! Leading eigevectors of \( \mathbf{A} \). real(dp), allocatable, intent(out) :: eigvals(:) !! Leading eigenvalues of \( \mathbf{A} \). real(dp), allocatable, intent(out) :: residuals(:) !! Residuals associated to each Ritz eigenpairs. integer, intent(out) :: info !! Information flag. class(abstract_vector_rdp), optional, intent(in) :: x0 !! Optional starting vector to generate the Krylov subspace. integer, optional, intent(in) :: kdim !! Desired number of eigenpairs. real(dp), optional, intent(in) :: tolerance !! Tolerance logical, optional, intent(in) :: write_intermediate !! Write intermediate eigenvalues to file during iteration? end subroutine module subroutine eighs_csp(A, X, eigvals, residuals, info, x0, kdim, tolerance, write_intermediate) class(abstract_hermitian_linop_csp), intent(inout) :: A !! Linear operator whose leading eigenpairs need to be computed. class(abstract_vector_csp), intent(out) :: X(:) !! Leading eigevectors of \( \mathbf{A} \). real(sp), allocatable, intent(out) :: eigvals(:) !! Leading eigenvalues of \( \mathbf{A} \). real(sp), allocatable, intent(out) :: residuals(:) !! Residuals associated to each Ritz eigenpairs. integer, intent(out) :: info !! Information flag. class(abstract_vector_csp), optional, intent(in) :: x0 !! Optional starting vector to generate the Krylov subspace. integer, optional, intent(in) :: kdim !! Desired number of eigenpairs. real(sp), optional, intent(in) :: tolerance !! Tolerance logical, optional, intent(in) :: write_intermediate !! Write intermediate eigenvalues to file during iteration? end subroutine module subroutine eighs_cdp(A, X, eigvals, residuals, info, x0, kdim, tolerance, write_intermediate) class(abstract_hermitian_linop_cdp), intent(inout) :: A !! Linear operator whose leading eigenpairs need to be computed. class(abstract_vector_cdp), intent(out) :: X(:) !! Leading eigevectors of \( \mathbf{A} \). real(dp), allocatable, intent(out) :: eigvals(:) !! Leading eigenvalues of \( \mathbf{A} \). real(dp), allocatable, intent(out) :: residuals(:) !! Residuals associated to each Ritz eigenpairs. integer, intent(out) :: info !! Information flag. class(abstract_vector_cdp), optional, intent(in) :: x0 !! Optional starting vector to generate the Krylov subspace. integer, optional, intent(in) :: kdim !! Desired number of eigenpairs. real(dp), optional, intent(in) :: tolerance !! Tolerance logical, optional, intent(in) :: write_intermediate !! Write intermediate eigenvalues to file during iteration? end subroutine end interface !------------------------------------- !----- Utility functions ----- !------------------------------------- interface save_eigenspectrum !! ### Description !! !! Utility function to save the eigenspectrum computed from the Arnoldi factorization. !! It outpost a .npy file. !! !! ### Syntax !! !! ```fortran !! call save_eigenspectrum(eigvals, residuals, fname) !! ``` !! !! ### Arguments !! !! - `eigvals` : `complex` rank-1 array containing the eigenvalues. !! !! - `residuals` : `real` rank-1 array containing the residuals associated to each !! eigenvalues. !! !! `fname` : Name of the file to save the eigenspectrum. module subroutine save_eigenspectrum_rsp(lambda, residuals, fname) !! Saves the eigenspectrum and corresponding residuals to disk use the `npy` binary format. real(sp), intent(in) :: lambda(:) !! Eigenalues. real(sp), intent(in) :: residuals(:) !! Residual of the corresponding Ritz eigenpairs. character(len=*), intent(in) :: fname !! Name of the output file. end subroutine module subroutine save_eigenspectrum_rdp(lambda, residuals, fname) !! Saves the eigenspectrum and corresponding residuals to disk use the `npy` binary format. real(dp), intent(in) :: lambda(:) !! Eigenalues. real(dp), intent(in) :: residuals(:) !! Residual of the corresponding Ritz eigenpairs. character(len=*), intent(in) :: fname !! Name of the output file. end subroutine module subroutine save_eigenspectrum_csp(lambda, residuals, fname) !! Saves the eigenspectrum and corresponding residuals to disk use the `npy` binary format. complex(sp), intent(in) :: lambda(:) !! Eigenalues. real(sp), intent(in) :: residuals(:) !! Residual of the corresponding Ritz eigenpairs. character(len=*), intent(in) :: fname !! Name of the output file. end subroutine module subroutine save_eigenspectrum_cdp(lambda, residuals, fname) !! Saves the eigenspectrum and corresponding residuals to disk use the `npy` binary format. complex(dp), intent(in) :: lambda(:) !! Eigenalues. real(dp), intent(in) :: residuals(:) !! Residual of the corresponding Ritz eigenpairs. character(len=*), intent(in) :: fname !! Name of the output file. end subroutine end interface interface eigs !! ### Description !! !! Computes the leading eigenpairs of a square linear operator \(A\) !! using the Arnoldi iterative process. Given a square linear operator \(A\), it finds !! the leading eigvalues and eigvectorss such that: !! !! \[ !! Ax = \lambda x !! \] !! !! or !! !! \[ !! A^H x = \lambda x. !! \] !! !! The subspace \(X\) is constructed via Arnoldi factorization, resulting in an upper !! Hessenberg matrix \(H\). The eigenvalues of \(A\) are approximated by those of \(H\) !! and the eigenvectors are computed accordingly. !! !! **References** !! !! - Arnoldi, W. E. (1951). "The Principle of Minimized Iterations in the Solution of !! the Matrix Eigenvalue Problem." Quarterly of Applied Mathematics, 9(1), 17–29. !! !! ### Syntax !! !! ```fortran !! call eigs(A, X, eigvals, residuals, info [, kdim] [, select] [,tolerance] [, transpose]) !! ``` !! !! ### Arguments !! !! - `A` : Linear operator derived from `abstract_sym_linop_rsp`, `abstract_sym_linop_rdp`, !! `abstract_hermitian_linop_csp` or `abstract_hermitian_linop_cdp` whose leading !! eigenpairs need to be computed. It is an `intent(inout)` argument. !! !! - `X` : Array of `abstract_vectors` with the same type and kind as `A`. On exit, it !! contains the leading eigenvectors of `A`. Note that the dimension of `X` fixes !! the number of eigenpairs computed. !! !! - `eigvals` : Rank-1 array of `real` numbers. On exit, it contains the leading !! eigenvalues of `A`. It is an `intent(out)` argument. !! !! - `residuals` : Rank-1 array of `real` numbers. On exit, it contains the residuals !! associated with each eigenpairs. It is an `intent(out)` argument. !! !! - `info` : `integer` Information flag. !! !! - `kdim` (*optional*) : `integer`, maximum dimension of the Krylov subspace used to !! approximate the leading eigenpairs. It is an `intent(in)` !! argument. By default, `kdim = 4*size(X)`. !! !! - `select` (*optional*) : Function to select which eigenvalues to compute. !! !! - `tolerance` (*optional*) : `real` tolerance below which an eigenpair is considered as !! being converged. It is an `intent(in)` agument. By default, !! `tolerance = rtol_sp` or `tolerance = rtol_dp`. !! !! - `transpose` (*optional*) : `logical` flag determining whether the eigenvalues of \(A\) !! or \(A^H\) need to be computed. module procedure eigs_rsp module procedure eigs_rdp module procedure eigs_csp module procedure eigs_cdp end interface contains !------------------------------------- !----- UTILITY FUNCTIONS ----- !------------------------------------- subroutine write_results_rsp(filename, vals, res, tol) !! Prints the intermediate results of iterative eigenvalue/singular value decompositions character(len=*), intent(in) :: filename !! Output filename. This file will be overwritten real(sp), intent(in) :: vals(:) !! Intermediate values real(sp), intent(inout) :: res(:) !! Residuals real(sp), intent(in) :: tol !! Convergence tolerance ! internals integer :: i, k, idx integer, allocatable :: indices(:) character(len=*), parameter :: fmt = '(I6,2(2X,E16.9),2X,L4)' k = size(vals) if (io_rank()) then ! only master rank writes allocate(indices(k)) call sort_index(res, indices) ! res is returned in sorted order open (1234, file=filename, status='replace', action='write') write (1234, '(A6,2(A18),A6)') 'Iter', 'value', 'residual', 'conv' do i = 1, k idx = indices(i) write (1234, fmt) k, vals(idx), res(i), res(i) < tol end do close (1234) end if end subroutine subroutine write_results_rdp(filename, vals, res, tol) !! Prints the intermediate results of iterative eigenvalue/singular value decompositions character(len=*), intent(in) :: filename !! Output filename. This file will be overwritten real(dp), intent(in) :: vals(:) !! Intermediate values real(dp), intent(inout) :: res(:) !! Residuals real(dp), intent(in) :: tol !! Convergence tolerance ! internals integer :: i, k, idx integer, allocatable :: indices(:) character(len=*), parameter :: fmt = '(I6,2(2X,E16.9),2X,L4)' k = size(vals) if (io_rank()) then ! only master rank writes allocate(indices(k)) call sort_index(res, indices) ! res is returned in sorted order open (1234, file=filename, status='replace', action='write') write (1234, '(A6,2(A18),A6)') 'Iter', 'value', 'residual', 'conv' do i = 1, k idx = indices(i) write (1234, fmt) k, vals(idx), res(i), res(i) < tol end do close (1234) end if end subroutine subroutine write_results_csp(filename, vals, res, tol) !! Prints the intermediate results of iterative eigenvalue/singular value decompositions character(len=*), intent(in) :: filename !! Output filename. This file will be overwritten complex(sp), intent(in) :: vals(:) !! Intermediate values real(sp), intent(inout) :: res(:) !! Residuals real(sp), intent(in) :: tol !! Convergence tolerance ! internals integer :: i, k, idx integer, allocatable :: indices(:) real(sp) :: modulus character(len=*), parameter :: fmt = '(I6,4(2X,E16.9),2X,L4)' k = size(vals) if (io_rank()) then ! only master rank writes allocate(indices(k)) call sort_index(res, indices) ! res is returned in sorted order open (1234, file=filename, status='replace', action='write') write (1234, '(A6,4(A18),A6)') 'Iter', 'Re', 'Im', 'modulus', 'residual', 'conv' do i = 1, k idx = indices(i) modulus = sqrt(vals(idx)%re**2 + vals(idx)%im**2) write (1234, fmt) k, vals(idx)%re, vals(idx)%im, modulus, res(i), res(i) < tol end do close (1234) end if end subroutine subroutine write_results_cdp(filename, vals, res, tol) !! Prints the intermediate results of iterative eigenvalue/singular value decompositions character(len=*), intent(in) :: filename !! Output filename. This file will be overwritten complex(dp), intent(in) :: vals(:) !! Intermediate values real(dp), intent(inout) :: res(:) !! Residuals real(dp), intent(in) :: tol !! Convergence tolerance ! internals integer :: i, k, idx integer, allocatable :: indices(:) real(dp) :: modulus character(len=*), parameter :: fmt = '(I6,4(2X,E16.9),2X,L4)' k = size(vals) if (io_rank()) then ! only master rank writes allocate(indices(k)) call sort_index(res, indices) ! res is returned in sorted order open (1234, file=filename, status='replace', action='write') write (1234, '(A6,4(A18),A6)') 'Iter', 'Re', 'Im', 'modulus', 'residual', 'conv' do i = 1, k idx = indices(i) modulus = sqrt(vals(idx)%re**2 + vals(idx)%im**2) write (1234, fmt) k, vals(idx)%re, vals(idx)%im, modulus, res(i), res(i) < tol end do close (1234) end if end subroutine elemental pure function compute_residual_rsp(beta, x) result(residual) !! Computes the residual associated with a Ritz eigenpair. real(sp), intent(in) :: beta !! Norm of the residual Krylov vector. real(sp), intent(in) :: x !! Last entry of the low-dimensional Ritz eigenvector. real(sp) :: residual !! Residual associated to the corresponding Ritz eigenpair. residual = abs(beta*x) end function compute_residual_rsp elemental pure function compute_residual_rdp(beta, x) result(residual) !! Computes the residual associated with a Ritz eigenpair. real(dp), intent(in) :: beta !! Norm of the residual Krylov vector. real(dp), intent(in) :: x !! Last entry of the low-dimensional Ritz eigenvector. real(dp) :: residual !! Residual associated to the corresponding Ritz eigenpair. residual = abs(beta*x) end function compute_residual_rdp elemental pure function compute_residual_csp(beta, x) result(residual) !! Computes the residual associated with a Ritz eigenpair. complex(sp), intent(in) :: beta !! Norm of the residual Krylov vector. complex(sp), intent(in) :: x !! Last entry of the low-dimensional Ritz eigenvector. real(sp) :: residual !! Residual associated to the corresponding Ritz eigenpair. residual = abs(beta*x) end function compute_residual_csp elemental pure function compute_residual_cdp(beta, x) result(residual) !! Computes the residual associated with a Ritz eigenpair. complex(dp), intent(in) :: beta !! Norm of the residual Krylov vector. complex(dp), intent(in) :: x !! Last entry of the low-dimensional Ritz eigenvector. real(dp) :: residual !! Residual associated to the corresponding Ritz eigenpair. residual = abs(beta*x) end function compute_residual_cdp module procedure save_eigenspectrum_rsp ! Internal variables. real(sp) :: array(size(lambda), 2) array(:, 1) = lambda ; array(:, 2) = residuals ! Save the eigenspectrum to disk. call save_npy(fname, array) end procedure module procedure save_eigenspectrum_rdp ! Internal variables. real(dp) :: array(size(lambda), 2) array(:, 1) = lambda ; array(:, 2) = residuals ! Save the eigenspectrum to disk. call save_npy(fname, array) end procedure module procedure save_eigenspectrum_csp ! Internal variables. real(sp) :: array(size(lambda), 3) array(:, 1) = lambda%re ; array(:, 2) = lambda%im ; array(:, 3) = residuals ! Save the eigenspectrum to disk. call save_npy(fname, array) end procedure module procedure save_eigenspectrum_cdp ! Internal variables. real(dp) :: array(size(lambda), 3) array(:, 1) = lambda%re ; array(:, 2) = lambda%im ; array(:, 3) = residuals ! Save the eigenspectrum to disk. call save_npy(fname, array) end procedure !--------------------------------------------------- !----- GENERAL EIGENVALUE COMPUTATIONS ----- !--------------------------------------------------- subroutine eigs_rsp(A, X, eigvals, residuals, info, x0, kdim, tolerance, transpose, write_intermediate) class(abstract_linop_rsp), intent(inout) :: A !! Linear operator whose leading eigenpairs need to be computed. class(abstract_vector_rsp), intent(out) :: X(:) !! Leading eigenvectors of \(\mathbf{A}\). complex(sp), allocatable, intent(out) :: eigvals(:) !! Leading eigenvalues of \(\mathbf{A}\). real(sp), allocatable, intent(out) :: residuals(:) !! Residuals associated to each Ritz eigenpair. integer, intent(out) :: info !! Information flag. class(abstract_vector_rsp), optional, intent(in) :: x0 !! Optional starting vector for generating the Krylov subspace. integer, optional, intent(in) :: kdim !! Maximum dimension of the Krylov subspace (optional). real(sp), optional, intent(in) :: tolerance !! Tolerance. logical, optional, intent(in) :: transpose !! Determine whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. logical, optional, intent(in) :: write_intermediate !! Write intermediate eigenvalues to file during iteration? !-------------------------------------- !----- Internal variables ----- !-------------------------------------- ! Krylov subspace and Krylov subspace dimension. class(abstract_vector_rsp), allocatable :: Xwrk(:) integer :: kdim_, kstart ! Hessenberg matrix. real(sp), allocatable :: H(:, :) ! Working arrays for the eigenvectors and eigenvalues. real(sp), allocatable :: eigvecs_wrk(:, :) complex(sp), allocatable :: eigvals_wrk(:) real(sp), allocatable :: residuals_wrk(:) ! Miscellaneous. character(len=*), parameter :: this_procedure = 'eigs_rsp' integer :: nev, conv integer :: i, j, k, niter, krst real(sp) :: tol, x0_norm real(sp) :: beta real(sp) :: alpha logical :: outpost character(len=256) :: msg if (time_lightkrylov()) call timer%start(this_procedure) ! Deals with optional parameters. nev = size(X) kdim_ = optval(kdim, 4*nev) tol = optval(tolerance, rtol_sp) outpost = optval(write_intermediate, .true.) ! Allocate eigenvalues. allocate(eigvals(nev)) ; eigvals = 0.0_sp ! Allocate working variables. allocate(Xwrk(kdim_+1), source=X(1)) ; call zero_basis(Xwrk) if (present(x0)) then call copy(Xwrk(1), x0) x0_norm = x0%norm(); call Xwrk(1)%scal(one_rsp/x0_norm) else call Xwrk(1)%rand(.true.) endif allocate(H(kdim_+1, kdim_)) ; H = 0.0_sp allocate(eigvecs_wrk(kdim_, kdim_)) ; eigvecs_wrk = 0.0_sp allocate(eigvals_wrk(kdim_)) ; eigvals_wrk = 0.0_sp allocate(residuals_wrk(kdim_)) ; residuals_wrk = 0.0_sp ! Ritz eigenpairs computation. H = 0.0_sp kstart = 1 ; conv = 0 ; niter = 0 ; krst = 1 krylovschur: do while (conv < nev) arnoldi_factorization: do k = kstart, kdim_ ! Arnoldi step. call arnoldi(A, Xwrk, H, info, kstart=k, kend=k, transpose=transpose) call check_info(info, 'arnoldi', this_module, this_procedure) ! Spectral decomposition of the k x k Hessenberg matrix. eigvals_wrk = 0.0_sp ; eigvecs_wrk = 0.0_sp call eig(H(:k, :k), eigvecs_wrk(:k, :k), eigvals_wrk(:k)) ! Compute residuals. beta = H(k+1, k) do i = 1, k if (eigvals_wrk(i)%im > 0) then alpha = abs(cmplx(eigvecs_wrk(k, i), eigvecs_wrk(k, i+1), kind=sp)) else if (eigvals_wrk(i)%im < 0) then alpha = abs(cmplx(eigvecs_wrk(k, i-1), eigvecs_wrk(k, i), kind=sp)) else alpha = abs(eigvecs_wrk(k, i)) endif residuals_wrk(i) = compute_residual_rsp(beta, alpha) enddo ! Check convergence. niter = niter + 1 conv = count(residuals_wrk(:k) < tol) write(msg,'(I0,A,I0,A,I0,A)') conv, '/', nev, ' eigenvalues converged after ', niter, & & ' steps of the Arnoldi process.' call log_information(msg, this_module, this_procedure) if (outpost) call write_results_csp(eigs_output, eigvals_wrk(:k), residuals_wrk(:k), tol) if (conv >= nev) exit arnoldi_factorization enddo arnoldi_factorization write(msg,'(I0,A,I0,A,I0,A)') conv, '/', nev, ' eigenvalues converged after ', krst, & & ' Krylov-Schur restarts of the Arnoldi process.' call log_information(msg, this_module, this_procedure) ! Krylov-Schur restarting procedure. krst = krst + 1 call krylov_schur(kstart, Xwrk, H, median_selector) ; kstart = kstart + 1 end do krylovschur !-------------------------------- !----- POST-PROCESS ----- !-------------------------------- block integer :: indices(kdim_) real(sp) :: abs_eigvals(kdim_) ! Re-compute eigenvalues and eigenvectors. k = min(k, kdim_) ; call eig(H(:k, :k), eigvecs_wrk(:k, :k), eigvals_wrk(:k)) ! Sort eigenvalues. abs_eigvals = abs(eigvals_wrk) ; call sort_index(abs_eigvals, indices, reverse=.true.) eigvals_wrk = eigvals_wrk(indices) ; eigvecs_wrk = eigvecs_wrk(:, indices) residuals_wrk = residuals_wrk(indices) ! Store converged eigenvalues. eigvals = eigvals_wrk(:nev) ; residuals = residuals_wrk(:nev) end block ! Construct eigenvectors. do i = 1, nev call X(i)%zero() do j = 1, k ! call X(i)%axpby(one_rsp, Xwrk(j), eigvecs_wrk(j, i)) call X(i)%axpby(eigvecs_wrk(j, i), Xwrk(j), one_rsp) enddo enddo info = niter if (time_lightkrylov()) call timer%stop(this_procedure) contains function median_selector(lambda) result(selected) complex(sp), intent(in) :: lambda(:) logical, allocatable :: selected(:) selected = abs(lambda) > median(abs(lambda)) end function median_selector end subroutine eigs_rsp subroutine eigs_rdp(A, X, eigvals, residuals, info, x0, kdim, tolerance, transpose, write_intermediate) class(abstract_linop_rdp), intent(inout) :: A !! Linear operator whose leading eigenpairs need to be computed. class(abstract_vector_rdp), intent(out) :: X(:) !! Leading eigenvectors of \(\mathbf{A}\). complex(dp), allocatable, intent(out) :: eigvals(:) !! Leading eigenvalues of \(\mathbf{A}\). real(dp), allocatable, intent(out) :: residuals(:) !! Residuals associated to each Ritz eigenpair. integer, intent(out) :: info !! Information flag. class(abstract_vector_rdp), optional, intent(in) :: x0 !! Optional starting vector for generating the Krylov subspace. integer, optional, intent(in) :: kdim !! Maximum dimension of the Krylov subspace (optional). real(dp), optional, intent(in) :: tolerance !! Tolerance. logical, optional, intent(in) :: transpose !! Determine whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. logical, optional, intent(in) :: write_intermediate !! Write intermediate eigenvalues to file during iteration? !-------------------------------------- !----- Internal variables ----- !-------------------------------------- ! Krylov subspace and Krylov subspace dimension. class(abstract_vector_rdp), allocatable :: Xwrk(:) integer :: kdim_, kstart ! Hessenberg matrix. real(dp), allocatable :: H(:, :) ! Working arrays for the eigenvectors and eigenvalues. real(dp), allocatable :: eigvecs_wrk(:, :) complex(dp), allocatable :: eigvals_wrk(:) real(dp), allocatable :: residuals_wrk(:) ! Miscellaneous. character(len=*), parameter :: this_procedure = 'eigs_rdp' integer :: nev, conv integer :: i, j, k, niter, krst real(dp) :: tol, x0_norm real(dp) :: beta real(dp) :: alpha logical :: outpost character(len=256) :: msg if (time_lightkrylov()) call timer%start(this_procedure) ! Deals with optional parameters. nev = size(X) kdim_ = optval(kdim, 4*nev) tol = optval(tolerance, rtol_dp) outpost = optval(write_intermediate, .true.) ! Allocate eigenvalues. allocate(eigvals(nev)) ; eigvals = 0.0_dp ! Allocate working variables. allocate(Xwrk(kdim_+1), source=X(1)) ; call zero_basis(Xwrk) if (present(x0)) then call copy(Xwrk(1), x0) x0_norm = x0%norm(); call Xwrk(1)%scal(one_rdp/x0_norm) else call Xwrk(1)%rand(.true.) endif allocate(H(kdim_+1, kdim_)) ; H = 0.0_dp allocate(eigvecs_wrk(kdim_, kdim_)) ; eigvecs_wrk = 0.0_dp allocate(eigvals_wrk(kdim_)) ; eigvals_wrk = 0.0_dp allocate(residuals_wrk(kdim_)) ; residuals_wrk = 0.0_dp ! Ritz eigenpairs computation. H = 0.0_dp kstart = 1 ; conv = 0 ; niter = 0 ; krst = 1 krylovschur: do while (conv < nev) arnoldi_factorization: do k = kstart, kdim_ ! Arnoldi step. call arnoldi(A, Xwrk, H, info, kstart=k, kend=k, transpose=transpose) call check_info(info, 'arnoldi', this_module, this_procedure) ! Spectral decomposition of the k x k Hessenberg matrix. eigvals_wrk = 0.0_dp ; eigvecs_wrk = 0.0_dp call eig(H(:k, :k), eigvecs_wrk(:k, :k), eigvals_wrk(:k)) ! Compute residuals. beta = H(k+1, k) do i = 1, k if (eigvals_wrk(i)%im > 0) then alpha = abs(cmplx(eigvecs_wrk(k, i), eigvecs_wrk(k, i+1), kind=dp)) else if (eigvals_wrk(i)%im < 0) then alpha = abs(cmplx(eigvecs_wrk(k, i-1), eigvecs_wrk(k, i), kind=dp)) else alpha = abs(eigvecs_wrk(k, i)) endif residuals_wrk(i) = compute_residual_rdp(beta, alpha) enddo ! Check convergence. niter = niter + 1 conv = count(residuals_wrk(:k) < tol) write(msg,'(I0,A,I0,A,I0,A)') conv, '/', nev, ' eigenvalues converged after ', niter, & & ' steps of the Arnoldi process.' call log_information(msg, this_module, this_procedure) if (outpost) call write_results_cdp(eigs_output, eigvals_wrk(:k), residuals_wrk(:k), tol) if (conv >= nev) exit arnoldi_factorization enddo arnoldi_factorization write(msg,'(I0,A,I0,A,I0,A)') conv, '/', nev, ' eigenvalues converged after ', krst, & & ' Krylov-Schur restarts of the Arnoldi process.' call log_information(msg, this_module, this_procedure) ! Krylov-Schur restarting procedure. krst = krst + 1 call krylov_schur(kstart, Xwrk, H, median_selector) ; kstart = kstart + 1 end do krylovschur !-------------------------------- !----- POST-PROCESS ----- !-------------------------------- block integer :: indices(kdim_) real(dp) :: abs_eigvals(kdim_) ! Re-compute eigenvalues and eigenvectors. k = min(k, kdim_) ; call eig(H(:k, :k), eigvecs_wrk(:k, :k), eigvals_wrk(:k)) ! Sort eigenvalues. abs_eigvals = abs(eigvals_wrk) ; call sort_index(abs_eigvals, indices, reverse=.true.) eigvals_wrk = eigvals_wrk(indices) ; eigvecs_wrk = eigvecs_wrk(:, indices) residuals_wrk = residuals_wrk(indices) ! Store converged eigenvalues. eigvals = eigvals_wrk(:nev) ; residuals = residuals_wrk(:nev) end block ! Construct eigenvectors. do i = 1, nev call X(i)%zero() do j = 1, k ! call X(i)%axpby(one_rdp, Xwrk(j), eigvecs_wrk(j, i)) call X(i)%axpby(eigvecs_wrk(j, i), Xwrk(j), one_rdp) enddo enddo info = niter if (time_lightkrylov()) call timer%stop(this_procedure) contains function median_selector(lambda) result(selected) complex(dp), intent(in) :: lambda(:) logical, allocatable :: selected(:) selected = abs(lambda) > median(abs(lambda)) end function median_selector end subroutine eigs_rdp subroutine eigs_csp(A, X, eigvals, residuals, info, x0, kdim, tolerance, transpose, write_intermediate) class(abstract_linop_csp), intent(inout) :: A !! Linear operator whose leading eigenpairs need to be computed. class(abstract_vector_csp), intent(out) :: X(:) !! Leading eigenvectors of \(\mathbf{A}\). complex(sp), allocatable, intent(out) :: eigvals(:) !! Leading eigenvalues of \(\mathbf{A}\). real(sp), allocatable, intent(out) :: residuals(:) !! Residuals associated to each Ritz eigenpair. integer, intent(out) :: info !! Information flag. class(abstract_vector_csp), optional, intent(in) :: x0 !! Optional starting vector for generating the Krylov subspace. integer, optional, intent(in) :: kdim !! Maximum dimension of the Krylov subspace (optional). real(sp), optional, intent(in) :: tolerance !! Tolerance. logical, optional, intent(in) :: transpose !! Determine whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. logical, optional, intent(in) :: write_intermediate !! Write intermediate eigenvalues to file during iteration? !-------------------------------------- !----- Internal variables ----- !-------------------------------------- ! Krylov subspace and Krylov subspace dimension. class(abstract_vector_csp), allocatable :: Xwrk(:) integer :: kdim_, kstart ! Hessenberg matrix. complex(sp), allocatable :: H(:, :) ! Working arrays for the eigenvectors and eigenvalues. complex(sp), allocatable :: eigvecs_wrk(:, :) complex(sp), allocatable :: eigvals_wrk(:) real(sp), allocatable :: residuals_wrk(:) ! Miscellaneous. character(len=*), parameter :: this_procedure = 'eigs_csp' integer :: nev, conv integer :: i, j, k, niter, krst real(sp) :: tol, x0_norm complex(sp) :: beta logical :: outpost character(len=256) :: msg if (time_lightkrylov()) call timer%start(this_procedure) ! Deals with optional parameters. nev = size(X) kdim_ = optval(kdim, 4*nev) tol = optval(tolerance, rtol_sp) outpost = optval(write_intermediate, .true.) ! Allocate eigenvalues. allocate(eigvals(nev)) ; eigvals = 0.0_sp ! Allocate working variables. allocate(Xwrk(kdim_+1), source=X(1)) ; call zero_basis(Xwrk) if (present(x0)) then call copy(Xwrk(1), x0) x0_norm = x0%norm(); call Xwrk(1)%scal(one_csp/x0_norm) else call Xwrk(1)%rand(.true.) endif allocate(H(kdim_+1, kdim_)) ; H = 0.0_sp allocate(eigvecs_wrk(kdim_, kdim_)) ; eigvecs_wrk = 0.0_sp allocate(eigvals_wrk(kdim_)) ; eigvals_wrk = 0.0_sp allocate(residuals_wrk(kdim_)) ; residuals_wrk = 0.0_sp ! Ritz eigenpairs computation. H = 0.0_sp kstart = 1 ; conv = 0 ; niter = 0 ; krst = 1 krylovschur: do while (conv < nev) arnoldi_factorization: do k = kstart, kdim_ ! Arnoldi step. call arnoldi(A, Xwrk, H, info, kstart=k, kend=k, transpose=transpose) call check_info(info, 'arnoldi', this_module, this_procedure) ! Spectral decomposition of the k x k Hessenberg matrix. eigvals_wrk = 0.0_sp ; eigvecs_wrk = 0.0_sp call eig(H(:k, :k), eigvecs_wrk(:k, :k), eigvals_wrk(:k)) ! Compute residuals. beta = H(k+1, k) residuals_wrk(:k) = compute_residual_csp(beta, eigvecs_wrk(k,:k)) ! Check convergence. niter = niter + 1 conv = count(residuals_wrk(:k) < tol) write(msg,'(I0,A,I0,A,I0,A)') conv, '/', nev, ' eigenvalues converged after ', niter, & & ' steps of the Arnoldi process.' call log_information(msg, this_module, this_procedure) if (outpost) call write_results_csp(eigs_output, eigvals_wrk(:k), residuals_wrk(:k), tol) if (conv >= nev) exit arnoldi_factorization enddo arnoldi_factorization write(msg,'(I0,A,I0,A,I0,A)') conv, '/', nev, ' eigenvalues converged after ', krst, & & ' Krylov-Schur restarts of the Arnoldi process.' call log_information(msg, this_module, this_procedure) ! Krylov-Schur restarting procedure. krst = krst + 1 call krylov_schur(kstart, Xwrk, H, median_selector) ; kstart = kstart + 1 end do krylovschur !-------------------------------- !----- POST-PROCESS ----- !-------------------------------- block integer :: indices(kdim_) real(sp) :: abs_eigvals(kdim_) ! Re-compute eigenvalues and eigenvectors. k = min(k, kdim_) ; call eig(H(:k, :k), eigvecs_wrk(:k, :k), eigvals_wrk(:k)) ! Sort eigenvalues. abs_eigvals = abs(eigvals_wrk) ; call sort_index(abs_eigvals, indices, reverse=.true.) eigvals_wrk = eigvals_wrk(indices) ; eigvecs_wrk = eigvecs_wrk(:, indices) residuals_wrk = residuals_wrk(indices) ! Store converged eigenvalues. eigvals = eigvals_wrk(:nev) ; residuals = residuals_wrk(:nev) end block ! Construct eigenvectors. do i = 1, nev call X(i)%zero() do j = 1, k ! call X(i)%axpby(one_csp, Xwrk(j), eigvecs_wrk(j, i)) call X(i)%axpby(eigvecs_wrk(j, i), Xwrk(j), one_csp) enddo enddo info = niter if (time_lightkrylov()) call timer%stop(this_procedure) contains function median_selector(lambda) result(selected) complex(sp), intent(in) :: lambda(:) logical, allocatable :: selected(:) selected = abs(lambda) > median(abs(lambda)) end function median_selector end subroutine eigs_csp subroutine eigs_cdp(A, X, eigvals, residuals, info, x0, kdim, tolerance, transpose, write_intermediate) class(abstract_linop_cdp), intent(inout) :: A !! Linear operator whose leading eigenpairs need to be computed. class(abstract_vector_cdp), intent(out) :: X(:) !! Leading eigenvectors of \(\mathbf{A}\). complex(dp), allocatable, intent(out) :: eigvals(:) !! Leading eigenvalues of \(\mathbf{A}\). real(dp), allocatable, intent(out) :: residuals(:) !! Residuals associated to each Ritz eigenpair. integer, intent(out) :: info !! Information flag. class(abstract_vector_cdp), optional, intent(in) :: x0 !! Optional starting vector for generating the Krylov subspace. integer, optional, intent(in) :: kdim !! Maximum dimension of the Krylov subspace (optional). real(dp), optional, intent(in) :: tolerance !! Tolerance. logical, optional, intent(in) :: transpose !! Determine whether \(\mathbf{A}\) or \(\mathbf{A}^H\) is being used. logical, optional, intent(in) :: write_intermediate !! Write intermediate eigenvalues to file during iteration? !-------------------------------------- !----- Internal variables ----- !-------------------------------------- ! Krylov subspace and Krylov subspace dimension. class(abstract_vector_cdp), allocatable :: Xwrk(:) integer :: kdim_, kstart ! Hessenberg matrix. complex(dp), allocatable :: H(:, :) ! Working arrays for the eigenvectors and eigenvalues. complex(dp), allocatable :: eigvecs_wrk(:, :) complex(dp), allocatable :: eigvals_wrk(:) real(dp), allocatable :: residuals_wrk(:) ! Miscellaneous. character(len=*), parameter :: this_procedure = 'eigs_cdp' integer :: nev, conv integer :: i, j, k, niter, krst real(dp) :: tol, x0_norm complex(dp) :: beta logical :: outpost character(len=256) :: msg if (time_lightkrylov()) call timer%start(this_procedure) ! Deals with optional parameters. nev = size(X) kdim_ = optval(kdim, 4*nev) tol = optval(tolerance, rtol_dp) outpost = optval(write_intermediate, .true.) ! Allocate eigenvalues. allocate(eigvals(nev)) ; eigvals = 0.0_dp ! Allocate working variables. allocate(Xwrk(kdim_+1), source=X(1)) ; call zero_basis(Xwrk) if (present(x0)) then call copy(Xwrk(1), x0) x0_norm = x0%norm(); call Xwrk(1)%scal(one_cdp/x0_norm) else call Xwrk(1)%rand(.true.) endif allocate(H(kdim_+1, kdim_)) ; H = 0.0_dp allocate(eigvecs_wrk(kdim_, kdim_)) ; eigvecs_wrk = 0.0_dp allocate(eigvals_wrk(kdim_)) ; eigvals_wrk = 0.0_dp allocate(residuals_wrk(kdim_)) ; residuals_wrk = 0.0_dp ! Ritz eigenpairs computation. H = 0.0_dp kstart = 1 ; conv = 0 ; niter = 0 ; krst = 1 krylovschur: do while (conv < nev) arnoldi_factorization: do k = kstart, kdim_ ! Arnoldi step. call arnoldi(A, Xwrk, H, info, kstart=k, kend=k, transpose=transpose) call check_info(info, 'arnoldi', this_module, this_procedure) ! Spectral decomposition of the k x k Hessenberg matrix. eigvals_wrk = 0.0_dp ; eigvecs_wrk = 0.0_dp call eig(H(:k, :k), eigvecs_wrk(:k, :k), eigvals_wrk(:k)) ! Compute residuals. beta = H(k+1, k) residuals_wrk(:k) = compute_residual_cdp(beta, eigvecs_wrk(k,:k)) ! Check convergence. niter = niter + 1 conv = count(residuals_wrk(:k) < tol) write(msg,'(I0,A,I0,A,I0,A)') conv, '/', nev, ' eigenvalues converged after ', niter, & & ' steps of the Arnoldi process.' call log_information(msg, this_module, this_procedure) if (outpost) call write_results_cdp(eigs_output, eigvals_wrk(:k), residuals_wrk(:k), tol) if (conv >= nev) exit arnoldi_factorization enddo arnoldi_factorization write(msg,'(I0,A,I0,A,I0,A)') conv, '/', nev, ' eigenvalues converged after ', krst, & & ' Krylov-Schur restarts of the Arnoldi process.' call log_information(msg, this_module, this_procedure) ! Krylov-Schur restarting procedure. krst = krst + 1 call krylov_schur(kstart, Xwrk, H, median_selector) ; kstart = kstart + 1 end do krylovschur !-------------------------------- !----- POST-PROCESS ----- !-------------------------------- block integer :: indices(kdim_) real(dp) :: abs_eigvals(kdim_) ! Re-compute eigenvalues and eigenvectors. k = min(k, kdim_) ; call eig(H(:k, :k), eigvecs_wrk(:k, :k), eigvals_wrk(:k)) ! Sort eigenvalues. abs_eigvals = abs(eigvals_wrk) ; call sort_index(abs_eigvals, indices, reverse=.true.) eigvals_wrk = eigvals_wrk(indices) ; eigvecs_wrk = eigvecs_wrk(:, indices) residuals_wrk = residuals_wrk(indices) ! Store converged eigenvalues. eigvals = eigvals_wrk(:nev) ; residuals = residuals_wrk(:nev) end block ! Construct eigenvectors. do i = 1, nev call X(i)%zero() do j = 1, k ! call X(i)%axpby(one_cdp, Xwrk(j), eigvecs_wrk(j, i)) call X(i)%axpby(eigvecs_wrk(j, i), Xwrk(j), one_cdp) enddo enddo info = niter if (time_lightkrylov()) call timer%stop(this_procedure) contains function median_selector(lambda) result(selected) complex(dp), intent(in) :: lambda(:) logical, allocatable :: selected(:) selected = abs(lambda) > median(abs(lambda)) end function median_selector end subroutine eigs_cdp end module LightKrylov_IterativeSolvers