Skip to content

feat: [linalg] add iterative solvers #994

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 48 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
716b3c5
start iterative solvers
jalvesz Mar 2, 2025
9ed419f
simplify workspace
jalvesz Mar 3, 2025
9106676
add pccg solver and example
jalvesz Mar 8, 2025
297a18d
Merge branch 'fortran-lang:master' into iterative
jalvesz Mar 8, 2025
9eccdd8
Merge branch 'fortran-lang:master' into iterative
jalvesz Mar 28, 2025
a93f6b7
Merge branch 'fortran-lang:master' into iterative
jalvesz Apr 8, 2025
16e5cd7
Merge branch 'fortran-lang:master' into iterative
jalvesz Apr 15, 2025
e551a5d
complete cg with dirichlet flag, add example, fix di filter
jalvesz Apr 21, 2025
9309c5c
Merge branch 'fortran-lang:master' into iterative
jalvesz Apr 22, 2025
19167d5
add other sparse matrices
jalvesz Apr 23, 2025
9324971
add example for custom solver extending the generic interface
jalvesz Apr 23, 2025
71c2630
Merge branch 'iterative' of https://github.com/jalvesz/stdlib into it…
jalvesz Apr 23, 2025
85a70ba
small simplifications for working data
jalvesz Apr 25, 2025
84f4bc9
make default inner_product point to a default dot_product add enum fo…
jalvesz Apr 25, 2025
3ec23a4
make preconditionner a linop
jalvesz Apr 25, 2025
bfafaa5
use facility size
jalvesz Apr 25, 2025
0b01dbd
Add a customizable logger facility, change linop matvec to apply
jalvesz Apr 26, 2025
07b97ce
change internal procedure names for custom example
jalvesz Apr 26, 2025
379cd81
refactor to remove hard dependency on dirichlet BCs
jalvesz Apr 26, 2025
5e15a33
add forward/backward solvers for preconditionning
jalvesz Apr 28, 2025
8c2aa90
fix solve forward/backward
jalvesz Apr 29, 2025
e7bb7ce
Merge branch 'fortran-lang:master' into iterative
jalvesz Apr 29, 2025
517291a
fix colum index
jalvesz Apr 30, 2025
2c2196a
Merge branch 'iterative' of https://github.com/jalvesz/stdlib into it…
jalvesz Apr 30, 2025
367987a
add preconditionners
jalvesz Apr 30, 2025
05c076b
fix cmake
jalvesz May 1, 2025
f027017
add factorizations
jalvesz May 1, 2025
7fd2586
backward solve to use Lt marix
jalvesz May 1, 2025
c596ac0
start unit testing
jalvesz May 1, 2025
acefaaf
review csr factorization
jalvesz May 5, 2025
f413cbf
change name generic for kernel
jalvesz May 5, 2025
5cb2ad7
Merge branch 'fortran-lang:master' into iterative
jalvesz May 9, 2025
ea7d35e
shorten factorization procedures names
jalvesz May 10, 2025
8068f2d
Merge branch 'fortran-lang:master' into iterative
jalvesz May 16, 2025
eeddf7c
change precond ldl name
jalvesz May 17, 2025
b239028
rename to pcg
jalvesz May 18, 2025
724289f
Merge branch 'iterative' of https://github.com/jalvesz/stdlib into it…
jalvesz May 18, 2025
79dbbbe
Merge branch 'fortran-lang:master' into iterative
jalvesz May 23, 2025
463259b
start working on the documentation
jalvesz May 25, 2025
6627f4c
clean docstrings
jalvesz May 26, 2025
fe331f9
add cg and pcg tests
jalvesz May 26, 2025
9e5d000
Update example/linalg/example_solve_cg.f90
jalvesz May 28, 2025
0f9732e
add _type suffix
jalvesz May 28, 2025
177e545
reduce PR scope
jalvesz May 28, 2025
e68ce8e
rename wksp size constants
jalvesz May 28, 2025
1bc601c
rename constant
jalvesz May 28, 2025
64f83f8
Merge branch 'fortran-lang:master' into iterative
jalvesz May 30, 2025
a4d53e1
Merge branch 'fortran-lang:master' into iterative
jalvesz Jun 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 249 additions & 0 deletions doc/specs/stdlib_linalg_iterative_solvers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
---
title: linalg_iterative_solvers
---

# The `stdlib_linalg_iterative_solvers` module

[TOC]

## Introduction

The `stdlib_linalg_iterative_solvers` module provides base implementations for known iterative solver methods. Each method is exposed with two procedure flavors:

* A `solve_<method>_kernel` which holds the method's base implementation. The linear system argument is defined through a `linop` derived type which enables extending the method for implicit or unknown (by `stdlib`) matrices or to complex scenarios involving distributed parallelism for which the user shall extend the `inner_product` and/or matrix-vector product to account for parallel syncrhonization.

* A `solve_<method>` which proposes an off-the-shelf ready to use interface for `dense` and `CSR_<kind>_type` matrices for all `real` kinds.

<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
### The `linop` derived type

The `linop_<kind>_type` derive type is an auxiliary class enabling to abstract the definition of the linear system and the actual implementation of the solvers.

#### Type-bound procedures

The following type-bound procedures pointer enable customization of the solver:

##### `apply`

Proxy procedure for the matrix-vector product $y = alpha * M * x + beta * y$.

#### Syntax

`call ` [[stdlib_iterative_solvers(module):apply(interface)]] ` (x,y,alpha,beta)`

###### Class

Subroutine

###### Argument(s)

`x`: 1-D array of `real(<kind>)`. This argument is `intent(in)`.

`y`: 1-D array of `real(<kind>)`. This argument is `intent(inout)`.

`alpha`: scalar of `real(<kind>)`. This argument is `intent(in)`.

`beta`: scalar of `real(<kind>)`. This argument is `intent(in)`.

##### `inner_product`

Proxy procedure for the `dot_product`.

#### Syntax

`res = ` [[stdlib_iterative_solvers(module):inner_product(interface)]] ` (x,y)`

###### Class

Function

###### Argument(s)

`x`: 1-D array of `real(<kind>)`. This argument is `intent(in)`.

`y`: 1-D array of `real(<kind>)`. This argument is `intent(in)`.

###### Output value or Result value

The output is a scalar of `type` and `kind` same as to that of `x` and `y`.

<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
### The `solver_workspace` derived type

The `solver_workspace_<kind>_type` derive type is an auxiliary class enabling to hold the data associated to the working arrays needed by the solvers to operate.

#### Type-bound procedures

- `callback`: null pointer procedure enabling to pass a callback at each iteration to check on the solvers status.

##### Class

Subroutine

##### Argument(s)

`x`: 1-D array of `real(<kind>)` type with the current state of the solution vector. This argument is `intent(in)` as it should not be modified by the callback.

`norm_sq`: scalar of `real(<kind>)` type representing the squared norm of the residual at the current iteration. This argument is `intent(in)`.

`iter`: scalar of `integer` type giving the current iteration counter. This argument is `intent(in)`.

<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
### `solve_cg_kernel` subroutine

#### Description

Implements the Conjugate Gradient (CG) method for solving the linear system \( Ax = b \), where \( A \) is a symmetric positive-definite linear operator defined via the `linop` type. This is the core implementation, allowing flexibility for custom matrix types or parallel environments.

#### Syntax

`call ` [[stdlib_iterative_solvers(module):solve_cg_kernel(interface)]] ` (A, b, x, tol, maxiter, workspace)`

#### Status

Experimental

#### Class

Subroutine

#### Argument(s)

`A`: `class(linop_<kind>_type)` defining the linear operator. This argument is `intent(in)`.

`b`: 1-D array of `real(<kind>)` defining the loading conditions of the linear system. This argument is `intent(in)`.

`x`: 1-D array of `real(<kind>)` which serves as the input initial guess and the output solution. This argument is `intent(inout)`.

`tol`: scalar of type `real(<kind>)` specifying the requested tolerance with respect to the relative residual vector norm. This argument is `intent(in)`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tolerance with respect to the relative residual vector norm:
Maybe for a follow-up PR: should we support different convergence criteria?

Copy link
Contributor Author

@jalvesz jalvesz May 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which criteria would you consider adding?
I guess a monitoring on the solution vector instead of the residual could also be considered. something like

$$|| x^i - x^{i-1} ||< tol * ||x^0||$$

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, indeed. Here are a couple of others: https://doi.org/10.1186/s12711-021-00626-1 , related to the relative error in x.


`maxiter`: scalar of type `integer` defining the maximum allowed number of iterations. This argument is `intent(in)`.

`workspace`: `type(solver_workspace_<kind>_type)` holding the work temporal array for the solver. This argument is `intent(inout)`.

<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
### `solve_cg` subroutine

#### Description

Provides a user-friendly interface to the CG method for solving \( Ax = b \), supporting `dense` and `CSR_<kind>_type` matrices. It handles workspace allocation and optional parameters for customization.

#### Syntax

`call ` [[stdlib_iterative_solvers(module):solve_cg(interface)]] ` (A, b, x [, di, tol, maxiter, restart, workspace])`

#### Status

Experimental

#### Class

Subroutine

#### Argument(s)

`A`: `dense` or `CSR_<kind>_type` matrix defining the linear system. This argument is `intent(in)`.

`b`: 1-D array of `real(<kind>)` defining the loading conditions of the linear system. This argument is `intent(in)`.

`x`: 1-D array of `real(<kind>)` which serves as the input initial guess and the output solution. This argument is `intent(inout)`.

`di` (optional): 1-D mask array of type `logical(1)` defining the degrees of freedom subject to dirichlet boundary conditions. The actual boundary conditions values should be stored in the `b` load array. This argument is `intent(in)`.

`tol` (optional): scalar of type `real(<kind>)` specifying the requested tolerance with respect to the relative residual vector norm. If no value is given, a default value of `1.e-4` is set. This argument is `intent(in)`.

`maxiter` (optional): scalar of type `integer` defining the maximum allowed number of iterations. If no value is given, a default of `N` is set, where `N = size(b)`. This argument is `intent(in)`.

`workspace` (optional): `type(solver_workspace_<kind>_type)` holding the work temporal array for the solver. If the user passes its own `workspace`, then internally a pointer is set to it, otherwise, memory will be internally allocated and deallocated before exiting the procedure. This argument is `intent(inout)`.

#### Example

```fortran
{!example/linalg/example_solve_cg.f90!}
```

<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
### `solve_pcg_kernel` subroutine

#### Description

Implements the Preconditioned Conjugate Gradient (PCG) method for solving the linear system \( Ax = b \), where \( A \) is a symmetric positive-definite linear operator defined via the `linop` type. This is the core implementation, allowing flexibility for custom matrix types or parallel environments.

#### Syntax

`call ` [[stdlib_iterative_solvers(module):solve_cg_kernel(interface)]] ` (A, M, b, x, tol, maxiter, workspace)`

#### Status

Experimental

#### Class

Subroutine

#### Argument(s)

`A`: `class(linop_<kind>_type)` defining the linear operator. This argument is `intent(in)`.

`M`: `class(linop_<kind>_type)` defining the preconditioner linear operator. This argument is `intent(in)`.

`b`: 1-D array of `real(<kind>)` defining the loading conditions of the linear system. This argument is `intent(in)`.

`x`: 1-D array of `real(<kind>)` which serves as the input initial guess and the output solution. This argument is `intent(inout)`.

`tol`: scalar of type `real(<kind>)` specifying the requested tolerance with respect to the relative residual vector norm. This argument is `intent(in)`.

`maxiter`: scalar of type `integer` defining the maximum allowed number of iterations. This argument is `intent(in)`.

`workspace`: `type(solver_workspace_<kind>_type)` holding the work temporal array for the solver. This argument is `intent(inout)`.

#### Example

```fortran
{!example/linalg/example_solve_custom.f90!}
```

<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
### `solve_pcg` subroutine

#### Description

Provides a user-friendly interface to the PCG method for solving \( Ax = b \), supporting `dense` and `CSR_<kind>_type` matrices. It supports optional preconditioners and handles workspace allocation.

#### Syntax

`call ` [[stdlib_iterative_solvers(module):solve_pcg(interface)]] ` (A, b, x [, di, tol, maxiter, restart, precond, M, workspace])`

#### Status

Experimental

#### Class

Subroutine

#### Argument(s)

`A`: `dense` or `CSR_<kind>_type` matrix defining the linear system. This argument is `intent(in)`.

`b`: 1-D array of `real(<kind>)` defining the loading conditions of the linear system. This argument is `intent(in)`.

`x`: 1-D array of `real(<kind>)` which serves as the input initial guess and the output solution. This argument is `intent(inout)`.

`di` (optional): 1-D mask array of type `logical(1)` defining the degrees of freedom subject to dirichlet boundary conditions. The actual boundary conditions values should be stored in the `b` load array. This argument is `intent(in)`.

`tol` (optional): scalar of type `real(<kind>)` specifying the requested tolerance with respect to the relative residual vector norm. If no value is given, a default value of `1.e-4` is set. This argument is `intent(in)`.

`maxiter` (optional): scalar of type `integer` defining the maximum allowed number of iterations. If no value is given, a default of `N` is set, where `N = size(b)`. This argument is `intent(in)`.

`precond` (optional): scalar of type `integer` enabling to switch among the default preconditioners available. If no value is given, no preconditionning will be applied. This argument is `intent(in)`.

`M` (optional): `class(linop_<kind>_type)` defining a custom preconditioner linear operator. If given, `precond` will have no effect, a pointer is set to this custom preconditioner.

`workspace` (optional): `type(solver_workspace_<kind>_type)` holding the work temporal array for the solver. If the user passes its own `workspace`, then internally a pointer is set to it, otherwise, memory will be internally allocated and deallocated before exiting the procedure. This argument is `intent(inout)`.

#### Example

```fortran
{!example/linalg/example_solve_pcg.f90!}
```
3 changes: 3 additions & 0 deletions example/linalg/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ ADD_EXAMPLE(get_norm)
ADD_EXAMPLE(solve1)
ADD_EXAMPLE(solve2)
ADD_EXAMPLE(solve3)
ADD_EXAMPLE(solve_cg)
ADD_EXAMPLE(solve_pcg)
ADD_EXAMPLE(solve_custom)
ADD_EXAMPLE(sparse_from_ijv)
ADD_EXAMPLE(sparse_data_accessors)
ADD_EXAMPLE(sparse_spmv)
Expand Down
17 changes: 17 additions & 0 deletions example/linalg/example_solve_cg.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
program example_solve_cg
use stdlib_kinds, only: dp
use stdlib_linalg_iterative_solvers, only: solve_cg

real(dp) :: matrix(2,2)
real(dp) :: x(2), load(2)

matrix = reshape( [4, 1,&
1, 3] , [2,2])

x = dble( [2,1] ) !> initial guess
load = dble( [1,2] ) !> load vector

call solve_cg(matrix, load, x, restart=.false.)
print *, x !> solution: [0.0909, 0.6364]

end program
Loading
Loading