Skip to content
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

linalg: QR factorization #832

Merged
merged 35 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6cac0cc
add `qr` module
perazz Jun 3, 2024
d4b5dbc
add examples
perazz Jun 3, 2024
fe0e243
submodule
perazz Jun 3, 2024
ec6f687
fix the examples
perazz Jun 3, 2024
a6a6838
exclude `xdp`
perazz Jun 3, 2024
feecc14
fix `module` `subroutine`
perazz Jun 3, 2024
f3d74ff
enforce full/reduced problem sizes
perazz Jun 5, 2024
5735b05
add tests
perazz Jun 5, 2024
7c9d5b4
remove `xdp` from tests
perazz Jun 5, 2024
cda4b0d
document `qr_space`
perazz Jun 5, 2024
8df204f
document `qr`
perazz Jun 5, 2024
b4889c5
relax tol
perazz Jun 5, 2024
9ac97d1
deactivate full tol check
perazz Jun 5, 2024
364b9ca
export error amount
perazz Jun 5, 2024
8ed1fe4
Fix: return full `Q` from `*ORGQR`
perazz Jun 5, 2024
3db022d
add test: init NaNs, check correct return
perazz Jun 5, 2024
fcb5306
Merge branch 'master' into qr
perazz Jun 21, 2024
68ece36
Merge branch 'master' into qr
perazz Jul 6, 2024
86d15bc
lift `xdp` restriction
perazz Jul 6, 2024
381d024
Update doc/specs/stdlib_linalg.md
perazz Jul 8, 2024
931f07a
Update doc/specs/stdlib_linalg.md
perazz Jul 8, 2024
5f15913
Update doc/specs/stdlib_linalg.md
perazz Jul 8, 2024
997021c
Update doc/specs/stdlib_linalg.md
perazz Jul 8, 2024
789e6d0
Update doc/specs/stdlib_linalg.md
perazz Jul 8, 2024
e5999f2
Update doc/specs/stdlib_linalg.md
perazz Jul 8, 2024
47f70e6
Update doc/specs/stdlib_linalg.md
perazz Jul 8, 2024
478f68d
Update doc/specs/stdlib_linalg.md
perazz Jul 8, 2024
e3db36d
improve qr description
perazz Jul 8, 2024
e69e767
`qr` workspace: make `a` `intent(in)`
perazz Jul 8, 2024
8827bf7
`qr` temporary storage: make `intent(out)`
perazz Jul 8, 2024
7393a18
Merge branch 'master' into qr
perazz Jul 8, 2024
c6bb171
Update src/stdlib_linalg_qr.fypp
perazz Jul 11, 2024
6fc0e1d
use IEEE NaNs
perazz Jul 11, 2024
df4016a
Merge branch 'master' into qr
perazz Aug 19, 2024
d3ae5a7
Merge branch 'fortran-lang:master' into qr
perazz Aug 23, 2024
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
79 changes: 78 additions & 1 deletion doc/specs/stdlib_linalg.md
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,83 @@ Exceptions trigger an `error stop`.
{!example/linalg/example_determinant2.f90!}
```

## `qr` - Compute the QR factorization of a matrix

### Status

Experimental

### Description

This subroutine computes the QR factorization of a `real` or `complex` matrix: \( A = Q R \) where \( Q \)
is orthonormal and \( R \) is upper-triangular. Matrix \( A \) has size `[m,n]`, with \( m \ge n \).

The results are returned in output matrices \( Q \) and \(R \), that have the same type and kind as \( A \).
Given `k = min(m,n)`, one can write \( A = \( Q_1 Q_2 \) \cdot \( \frac{R_1}{0}\) \).
Because the lower rows of \( R \) are zeros, a reduced problem \( A = Q_1 R_1 \) may be solved. The size of
the input arguments determines what problem is solved: on full matrices (`shape(Q)==[m,m]`, `shape(R)==[m,n]`),
the full problem is solved. On reduced matrices (`shape(Q)==[m,k]`, `shape(R)==[k,n]`), the reduced problem is solved.

### Syntax

`call ` [[stdlib_linalg(module):qr(interface)]] `(a, q, r, [, storage] [, overwrite_a] [, err])`

### Arguments

`a`: Shall be a rank-2 `real` or `complex` array containing the coefficient matrix of size `[m,n]`. It is an `intent(in)` argument, if `overwrite_a=.false.`. Otherwise, it is an `intent(inout)` argument, and is destroyed upon return.

`q`: Shall be a rank-2 array of the same kind as `a`, containing the orthonormal matrix `q`. It is an `intent(out)` argument. It should have a shape equal to either `[m,m]` or `[m,k]`, whether the full or the reduced problem is sought for.

`r`: Shall be a rank-2 array of the same kind as `a`, containing the upper triangular matrix `r`. It is an `intent(out)` argument. It should have a shape equal to either `[m,n]` or `[k,n]`, whether the full or the reduced problem is sought for.

`storage` (optional): Shall be a rank-1 array of the same type and kind as `a`, providing working storage for the solver. Its minimum size can be determined with a call to [[stdlib_linalg(module):qr_space(interface)]]. It is an `intent(out)` argument.

`overwrite_a` (optional): Shall be an input `logical` flag (default: `.false.`). If `.true.`, input matrix `a` will be used as temporary storage and overwritten. This avoids internal data allocation. It is an `intent(in)` argument.

`err` (optional): Shall be a `type(linalg_state_type)` value. It is an `intent(out)` argument.

### Return value

Returns the QR factorization matrices into the \( Q \) and \( R \) arguments.

Raises `LINALG_VALUE_ERROR` if any of the matrices has invalid or unsuitable size for the full/reduced problem.
Raises `LINALG_ERROR` on insufficient user storage space.
If the state argument `err` is not present, exceptions trigger an `error stop`.

### Example

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

## `qr_space` - Compute internal working space requirements for the QR factorization.

### Status

Experimental

### Description

This subroutine computes the internal working space requirements for the QR factorization, [[stdlib_linalg(module):qr(interface)]] .

### Syntax

`call ` [[stdlib_linalg(module):qr_space(interface)]] `(a, lwork, [, err])`

### Arguments

`a`: Shall be a rank-2 `real` or `complex` array containing the coefficient matrix. It is an `intent(in)` argument.

`lwork`: Shall be an `integer` scalar, that returns the minimum array size required for the working storage in [[stdlib_linalg(module):qr(interface)]] to factorize `a`.

`err` (optional): Shall be a `type(linalg_state_type)` value. This is an `intent(out)` argument.

### Example

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

## `eig` - Eigenvalues and Eigenvectors of a Square Matrix

### Status
Expand Down Expand Up @@ -1028,7 +1105,6 @@ Raises `LINALG_ERROR` if the calculation did not converge.
Raises `LINALG_VALUE_ERROR` if any matrix or arrays have invalid/incompatible sizes.
If `err` is not present, exceptions trigger an `error stop`.


### Example

```fortran
Expand Down Expand Up @@ -1096,6 +1172,7 @@ If requested, `vt` contains the right singular vectors, as rows of \( V^T \).
`call ` [[stdlib_linalg(module):svd(interface)]] `(a, s, [, u, vt, overwrite_a, full_matrices, err])`

### Class

Subroutine

### Arguments
Expand Down
2 changes: 2 additions & 0 deletions example/linalg/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,7 @@ ADD_EXAMPLE(svd)
ADD_EXAMPLE(svdvals)
ADD_EXAMPLE(determinant)
ADD_EXAMPLE(determinant2)
ADD_EXAMPLE(qr)
ADD_EXAMPLE(qr_space)
ADD_EXAMPLE(cholesky)
ADD_EXAMPLE(chol)
15 changes: 15 additions & 0 deletions example/linalg/example_qr.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
program example_qr
use stdlib_linalg, only: qr
implicit none(type,external)
real :: A(104, 32), Q(104,32), R(32,32)

! Create a random matrix
call random_number(A)

! Compute its QR factorization (reduced)
call qr(A,Q,R)

! Test factorization: Q*R = A
print *, maxval(abs(matmul(Q,R)-A))

end program example_qr
25 changes: 25 additions & 0 deletions example/linalg/example_qr_space.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
! QR example with pre-allocated storage
program example_qr_space
use stdlib_linalg_constants, only: ilp
use stdlib_linalg, only: qr, qr_space, linalg_state_type
implicit none(type,external)
real :: A(104, 32), Q(104,32), R(32,32)
real, allocatable :: work(:)
integer(ilp) :: lwork
type(linalg_state_type) :: err

! Create a random matrix
call random_number(A)

! Prepare QR workspace
call qr_space(A,lwork)
allocate(work(lwork))

! Compute its QR factorization (reduced)
call qr(A,Q,R,storage=work,err=err)

! Test factorization: Q*R = A
print *, maxval(abs(matmul(Q,R)-A))
print *, err%print()

end program example_qr_space
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ set(fppFiles
stdlib_linalg_eigenvalues.fypp
stdlib_linalg_solve.fypp
stdlib_linalg_determinant.fypp
stdlib_linalg_qr.fypp
stdlib_linalg_inverse.fypp
stdlib_linalg_state.fypp
stdlib_linalg_svd.fypp
Expand Down
71 changes: 71 additions & 0 deletions src/stdlib_linalg.fypp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ module stdlib_linalg
public :: outer_product
public :: kronecker_product
public :: cross_product
public :: qr
public :: qr_space
public :: is_square
public :: is_diagonal
public :: is_symmetric
Expand Down Expand Up @@ -526,6 +528,75 @@ module stdlib_linalg
#:endfor
end interface lstsq_space

! QR factorization of rank-2 array A
interface qr
!! version: experimental
!!
!! Computes the QR factorization of matrix \( A = Q R \).
!! ([Specification](../page/specs/stdlib_linalg.html#qr-compute-the-qr-factorization-of-a-matrix))
!!
!!### Summary
!! Compute the QR factorization of a `real` or `complex` matrix: \( A = Q R \), where \( Q \) is orthonormal
!! and \( R \) is upper-triangular. Matrix \( A \) has size `[m,n]`, with \( m\ge n \).
!!
!!### Description
!!
!! This interface provides methods for computing the QR factorization of a matrix.
!! Supported data types include `real` and `complex`. If a pre-allocated work space
!! is provided, no internal memory allocations take place when using this interface.
!!
!! Given `k = min(m,n)`, one can write \( A = \( Q_1 Q_2 \) \cdot \( \frac{R_1}{0}\) \).
!! The user may want the full problem (provide `shape(Q)==[m,m]`, `shape(R)==[m,n]`) or the reduced
!! problem only: \( A = Q_1 R_1 \) (provide `shape(Q)==[m,k]`, `shape(R)==[k,n]`).
!!
!!@note The solution is based on LAPACK's QR factorization (`*GEQRF`) and ordered matrix output (`*ORGQR`, `*UNGQR`).
!!
#:for rk,rt,ri in RC_KINDS_TYPES
pure module subroutine stdlib_linalg_${ri}$_qr(a,q,r,overwrite_a,storage,err)
!> Input matrix a[m,n]
${rt}$, intent(inout), target :: a(:,:)
!> Orthogonal matrix Q ([m,m], or [m,k] if reduced)
${rt}$, intent(out), contiguous, target :: q(:,:)
!> Upper triangular matrix R ([m,n], or [k,n] if reduced)
${rt}$, intent(out), contiguous, target :: r(:,:)
!> [optional] Can A data be overwritten and destroyed?
logical(lk), optional, intent(in) :: overwrite_a
!> [optional] Provide pre-allocated workspace, size to be checked with qr_space
${rt}$, intent(out), optional, target :: storage(:)
!> [optional] state return flag. On error if not requested, the code will stop
type(linalg_state_type), optional, intent(out) :: err
end subroutine stdlib_linalg_${ri}$_qr
#:endfor
end interface qr

! Return the working array space required by the QR factorization solver
interface qr_space
!! version: experimental
!!
!! Computes the working array space required by the QR factorization solver
!! ([Specification](../page/specs/stdlib_linalg.html#qr-space-compute-internal-working-space-requirements-for-the-qr-factorization))
!!
!!### Description
!!
!! This interface returns the size of the `real` or `complex` working storage required by the
!! QR factorization solver. The working size only depends on the kind (`real` or `complex`) and size of
!! the matrix being factorized. Storage size can be used to pre-allocate a working array in case several
!! repeated QR factorizations to a same-size matrix are sought. If pre-allocated working arrays
!! are provided, no internal allocations will take place during the factorization.
!!
#:for rk,rt,ri in RC_KINDS_TYPES
pure module subroutine get_qr_${ri}$_workspace(a,lwork,err)
!> Input matrix a[m,n]
${rt}$, intent(in), target :: a(:,:)
!> Minimum workspace size for both operations
integer(ilp), intent(out) :: lwork
!> State return flag. Returns an error if the query failed
type(linalg_state_type), optional, intent(out) :: err
end subroutine get_qr_${ri}$_workspace
#:endfor
end interface qr_space


interface det
!! version: experimental
!!
Expand Down
Loading
Loading