|
| 1 | +#:include "common.fypp" |
| 2 | +#:set RC_KINDS_TYPES = REAL_KINDS_TYPES + CMPLX_KINDS_TYPES |
| 3 | +submodule (stdlib_linalg) stdlib_linalg_schur |
| 4 | + use stdlib_linalg_constants |
| 5 | + use stdlib_linalg_lapack, only: gees |
| 6 | + use stdlib_linalg_state, only: linalg_state_type, linalg_error_handling, LINALG_ERROR, & |
| 7 | + LINALG_INTERNAL_ERROR, LINALG_VALUE_ERROR |
| 8 | + implicit none(type,external) |
| 9 | + |
| 10 | + character(*), parameter :: this = 'schur' |
| 11 | + |
| 12 | + contains |
| 13 | + |
| 14 | + elemental subroutine handle_gees_info(info, m, sort, err) |
| 15 | + integer(ilp), intent(in) :: info, m |
| 16 | + logical, intent(in) :: sort |
| 17 | + type(linalg_state_type), intent(out) :: err |
| 18 | + |
| 19 | + ! Process GEES output |
| 20 | + select case (info) |
| 21 | + case (0) |
| 22 | + ! Success |
| 23 | + case (-1) |
| 24 | + err = linalg_state_type(this, LINALG_VALUE_ERROR, 'invalid matrix size m=', m) |
| 25 | + case default |
| 26 | + if (sort .and. info > 0) then |
| 27 | + err = linalg_state_type(this, LINALG_INTERNAL_ERROR, 'sorting eigenvalues failed at index ', info) |
| 28 | + else |
| 29 | + err = linalg_state_type(this, LINALG_INTERNAL_ERROR, 'GEES catastrophic error: info=', info) |
| 30 | + end if |
| 31 | + end select |
| 32 | + end subroutine handle_gees_info |
| 33 | + |
| 34 | + #:for rk, rt, ri in RC_KINDS_TYPES |
| 35 | + |
| 36 | + ! Schur decomposition subroutine |
| 37 | + pure module subroutine stdlib_linalg_${ri}$_schur(a, t, z, lwork, overwrite_a, sort, err) |
| 38 | + !> Input matrix a[m, m] |
| 39 | + ${rt}$, intent(inout), target :: a(:,:) |
| 40 | + !> Schur form of the matrix A |
| 41 | + ${rt}$, intent(out), contiguous, target :: t(:,:) |
| 42 | + !> Unitary/orthogonal matrix Z |
| 43 | + ${rt}$, intent(out), contiguous, target :: z(:,:) |
| 44 | + !> Workspace size (optional) |
| 45 | + integer(ilp), optional, intent(inout) :: lwork |
| 46 | + !> Overwrite input matrix A? (optional) |
| 47 | + logical(lk), optional, intent(in) :: overwrite_a |
| 48 | + !> Sorting eigenvalues? (optional) |
| 49 | + logical(lk), optional, intent(in) :: sort |
| 50 | + !> State return flag. On error if not requested, the code will stop |
| 51 | + type(linalg_state_type), optional, intent(out) :: err |
| 52 | + |
| 53 | + ! Local variables |
| 54 | + type(linalg_state_type) :: err0 |
| 55 | + integer(ilp) :: m, lda, info, liwork |
| 56 | + logical(lk) :: overwrite_a_ |
| 57 | + logical, pointer :: bwork(:) |
| 58 | + integer(ilp), allocatable :: iwork(:) |
| 59 | + ${rt}$, pointer :: amat(:,:), tau(:), work(:) |
| 60 | + ${rt}$ :: rwork_dummy(1) ! Dummy for real/complex cases |
| 61 | + ${rt}$, allocatable :: tmat(:,:), zmat(:,:) |
| 62 | + character :: jobz |
| 63 | + |
| 64 | + ! Problem size |
| 65 | + m = size(a, 1, kind=ilp) |
| 66 | + lda = size(a, 1, kind=ilp) |
| 67 | + |
| 68 | + ! Validate dimensions |
| 69 | + if (size(a, 1, kind=ilp) /= size(a, 2, kind=ilp)) then |
| 70 | + err0 = linalg_state_type(this, LINALG_VALUE_ERROR, 'Matrix A must be square: a=', [size(a,1), size(a,2)]) |
| 71 | + call linalg_error_handling(err0, err) |
| 72 | + return |
| 73 | + end if |
| 74 | + |
| 75 | + ! Set default values |
| 76 | + overwrite_a_ = .false._lk |
| 77 | + if (present(overwrite_a)) overwrite_a_ = overwrite_a |
| 78 | + |
| 79 | + ! Job type based on sorting request |
| 80 | + if (present(sort) .and. sort) then |
| 81 | + jobz = 'S' ! Compute and sort eigenvalues |
| 82 | + else |
| 83 | + jobz = 'N' ! Compute Schur decomposition without sorting |
| 84 | + end if |
| 85 | + |
| 86 | + ! Prepare storage |
| 87 | + allocate(tmat(m, m), source=0.0_${rk}$) |
| 88 | + allocate(zmat(m, m), source=0.0_${rk}$) |
| 89 | + |
| 90 | + if (overwrite_a_) then |
| 91 | + amat => a |
| 92 | + else |
| 93 | + allocate(amat(m, m), source=a) |
| 94 | + end if |
| 95 | + |
| 96 | + ! Allocate workspace |
| 97 | + liwork = -1 |
| 98 | + if (present(lwork)) then |
| 99 | + allocate(work(lwork)) |
| 100 | + else |
| 101 | + allocate(work(1)) ! Temporary workspace for querying size |
| 102 | + end if |
| 103 | + |
| 104 | + ! Workspace query |
| 105 | + call #{if rt.startswith('complex')}# zgees #{else}# gees #{endif}# & |
| 106 | + (jobz, 'N', nullify(bwork), m, amat, lda, tau, zmat, lda, work, liwork, iwork, info) |
| 107 | + call handle_gees_info(info, m, .false._lk, err0) |
| 108 | + if (err0%error()) then |
| 109 | + call linalg_error_handling(err0, err) |
| 110 | + return |
| 111 | + end if |
| 112 | + |
| 113 | + ! Update workspace size |
| 114 | + if (.not.present(lwork)) then |
| 115 | + liwork = ceiling(real(work(1), kind=${rk}$), kind=ilp) |
| 116 | + deallocate(work) |
| 117 | + allocate(work(liwork)) |
| 118 | + end if |
| 119 | + |
| 120 | + ! Compute Schur decomposition |
| 121 | + call #{if rt.startswith('complex')}# zgees #{else}# gees #{endif}# & |
| 122 | + (jobz, 'N', nullify(bwork), m, amat, lda, tau, zmat, lda, work, liwork, iwork, info) |
| 123 | + call handle_gees_info(info, m, present(sort) .and. sort, err0) |
| 124 | + if (err0%error()) then |
| 125 | + call linalg_error_handling(err0, err) |
| 126 | + return |
| 127 | + end if |
| 128 | + |
| 129 | + ! Output results |
| 130 | + t = amat |
| 131 | + z = zmat |
| 132 | + |
| 133 | + if (.not.overwrite_a_) deallocate(amat) |
| 134 | + if (.not.present(lwork)) deallocate(work) |
| 135 | + end subroutine stdlib_linalg_${ri}$_schur |
| 136 | + |
| 137 | + #:endfor |
| 138 | + |
| 139 | +end submodule stdlib_linalg_schur |
| 140 | + |
0 commit comments