Skip to content

Commit 81e5583

Browse files
committed
schur: simplify interface, do not allocate where possible
1 parent dc81688 commit 81e5583

File tree

2 files changed

+144
-90
lines changed

2 files changed

+144
-90
lines changed

src/stdlib_linalg.fypp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -623,23 +623,17 @@ module stdlib_linalg
623623
!! are implemented using LAPACK's eigenvalue sorting mechanism.
624624
!!
625625
#:for rk,rt,ri in RC_KINDS_TYPES
626-
pure module subroutine stdlib_linalg_${ri}$_schur(a,t,z,sdim,output,overwrite_a,sort,storage,err)
626+
module subroutine stdlib_linalg_${ri}$_schur(a, t, z, eigvals, storage, err)
627627
!> Input matrix a[m,m]
628628
${rt}$, intent(inout), target :: a(:,:)
629629
!> Schur form of A: upper-triangular or quasi-upper-triangular matrix T
630630
${rt}$, intent(out), contiguous, target :: t(:,:)
631631
!> Unitary/orthonormal transformation matrix Z
632-
${rt}$, intent(out), contiguous, target :: z(:,:)
633-
!> [optional] Number of eigenvalues satisfying the sort condition
634-
integer(ilp), optional, intent(out) :: sdim
635-
!> [optional] Output type: 'real' or 'complex'
636-
character(*), optional, intent(in) :: output
637-
!> [optional] Can A data be overwritten and destroyed?
638-
logical(lk), optional, intent(in) :: overwrite_a
639-
!> [optional] Sorting criterion: callable or predefined values ('lhp', 'rhp', 'iuc', 'ouc', or None)
640-
class(*), optional, intent(in) :: sort
632+
${rt}$, optional, intent(out), contiguous, target :: z(:,:)
633+
!> [optional] Output eigenvalues that appear on the diagonal of T
634+
complex(${rk}$), optional, intent(out), contiguous, target :: eigvals(:)
641635
!> [optional] Provide pre-allocated workspace, size to be checked with schur_space
642-
${rt}$, intent(out), optional, target :: storage(:)
636+
${rt}$, optional, intent(inout), target :: storage(:)
643637
!> [optional] State return flag. On error if not requested, the code will stop
644638
type(linalg_state_type), optional, intent(out) :: err
645639
end subroutine stdlib_linalg_${ri}$_schur

src/stdlib_linalg_schur.fypp

Lines changed: 139 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -126,104 +126,164 @@ submodule (stdlib_linalg) stdlib_linalg_schur
126126
end subroutine get_schur_${ri}$_workspace
127127

128128
! Schur decomposition subroutine
129-
pure module subroutine stdlib_linalg_${ri}$_schur(a, t, z, lwork, overwrite_a, sort, err)
130-
!> Input matrix a[m, m]
129+
subroutine stdlib_linalg_${ri}$_schur(a, t, z, eigvals, storage, err)
130+
!> Input matrix a[m,m]
131131
${rt}$, intent(inout), target :: a(:,:)
132-
!> Schur form of the matrix A
132+
!> Schur form of A: upper-triangular or quasi-upper-triangular matrix T
133133
${rt}$, intent(out), contiguous, target :: t(:,:)
134-
!> Unitary/orthogonal matrix Z
135-
${rt}$, intent(out), contiguous, target :: z(:,:)
136-
!> Workspace size (optional)
137-
integer(ilp), optional, intent(inout) :: lwork
138-
!> Overwrite input matrix A? (optional)
139-
logical(lk), optional, intent(in) :: overwrite_a
140-
!> Sorting eigenvalues? (optional)
141-
logical(lk), optional, intent(in) :: sort
142-
!> State return flag. On error if not requested, the code will stop
143-
type(linalg_state_type), optional, intent(out) :: err
134+
!> Unitary/orthonormal transformation matrix Z
135+
${rt}$, optional, intent(out), contiguous, target :: z(:,:)
136+
!> [optional] Output eigenvalues that appear on the diagonal of T
137+
complex(${rk}$), optional, intent(out), contiguous, target :: eigvals(:)
138+
!> [optional] Provide pre-allocated workspace, size to be checked with schur_space
139+
${rt}$, optional, intent(inout), target :: storage(:)
140+
!> [optional] State return flag. On error if not requested, the code will stop
141+
type(linalg_state), optional, intent(out) :: err
144142

145143
! Local variables
146-
type(linalg_state_type) :: err0
147-
integer(ilp) :: m, lda, info, liwork
148-
logical(lk) :: overwrite_a_
149-
logical, pointer :: bwork(:)
150-
integer(ilp), allocatable :: iwork(:)
151-
${rt}$, pointer :: amat(:,:), tau(:), work(:)
152-
${rt}$ :: rwork_dummy(1) ! Dummy for real/complex cases
153-
${rt}$, allocatable :: tmat(:,:), zmat(:,:)
154-
character :: jobz
155-
144+
integer(ilp) :: m,n,mt,nt,ldvs,nvs,lde,lwork,sdim,info
145+
logical(lk), target :: bwork_dummy(1),local_eigs
146+
logical(lk), pointer :: bwork(:)
147+
real(${rk}$), allocatable :: rwork(:)
148+
${rt}$, target :: vs_dummy(1,1)
149+
${rt}$, pointer :: vs(:,:),work(:),eigs(:)#{if rt.startswith('r')}#,eigi(:)#{endif}#
150+
character :: jobvs,sort
151+
type(linalg_state) :: err0
152+
156153
! Problem size
157-
m = size(a, 1, kind=ilp)
158-
lda = size(a, 1, kind=ilp)
159-
154+
m = size(a, 1, kind=ilp)
155+
n = size(a, 2, kind=ilp)
156+
mt = size(t, 1, kind=ilp)
157+
nt = size(t, 2, kind=ilp)
158+
160159
! Validate dimensions
161-
if (size(a, 1, kind=ilp) /= size(a, 2, kind=ilp)) then
162-
err0 = linalg_state_type(this, LINALG_VALUE_ERROR, 'Matrix A must be square: a=', [size(a,1), size(a,2)])
160+
if (m/=n .or. m<=0 .or. n<=0) then
161+
err0 = linalg_state(this, LINALG_VALUE_ERROR, 'Matrix A must be square: size(a)=',[m,n])
163162
call linalg_error_handling(err0, err)
164163
return
165-
end if
166-
167-
! Set default values
168-
overwrite_a_ = .false._lk
169-
if (present(overwrite_a)) overwrite_a_ = overwrite_a
170-
171-
! Job type based on sorting request
172-
if (present(sort) .and. sort) then
173-
jobz = 'S' ! Compute and sort eigenvalues
164+
end if
165+
if (mt/=nt .or. mt/=n .or. nt/=n) then
166+
err0 = linalg_state(this, LINALG_VALUE_ERROR, 'Matrix T must be square: size(T)=',[mt,nt], &
167+
'should be',[m,n])
168+
call linalg_error_handling(err0, err)
169+
return
170+
end if
171+
172+
!> Copy data into the output array
173+
t = a
174+
175+
!> SORTING: no sorting options are currently supported
176+
sort = gees_sort_eigs(.false.)
177+
sdim = 0_ilp
178+
179+
if (sort/=GEES_NOT) then
180+
181+
allocate(bwork(n),source=.false.)
182+
174183
else
175-
jobz = 'N' ! Compute Schur decomposition without sorting
176-
end if
177-
178-
! Prepare storage
179-
allocate(tmat(m, m), source=0.0_${rk}$)
180-
allocate(zmat(m, m), source=0.0_${rk}$)
181-
182-
if (overwrite_a_) then
183-
amat => a
184+
185+
bwork => bwork_dummy
186+
187+
end if
188+
189+
!> Schur vectors
190+
jobvs = gees_vectors(present(z))
191+
if (present(z)) then
192+
vs => z
193+
194+
ldvs = size(vs, 1, kind=ilp)
195+
nvs = size(vs, 2, kind=ilp)
196+
197+
if (ldvs<n .or. nvs/=n) then
198+
err0 = linalg_state(this, LINALG_VALUE_ERROR, 'Schur vectors size=',[ldvs,nvs], &
199+
'should be n=',n)
200+
goto 1
201+
end if
202+
184203
else
185-
allocate(amat(m, m), source=a)
204+
vs => vs_dummy
205+
ldvs = size(vs, 1, kind=ilp)
206+
nvs = size(vs, 2, kind=ilp)
186207
end if
187-
188-
! Allocate workspace
189-
liwork = -1
190-
if (present(lwork)) then
191-
allocate(work(lwork))
208+
209+
!> User or self-allocated storage
210+
if (present(storage)) then
211+
212+
work => storage
213+
lwork = size(work, 1, kind=ilp)
214+
192215
else
193-
allocate(work(1)) ! Temporary workspace for querying size
216+
217+
! Query optimal workspace
218+
call get_schur_${ri}$_workspace(a,lwork,err0)
219+
if (err0%error()) goto 1
220+
allocate(work(lwork))
221+
194222
end if
195-
196-
! Workspace query
197-
call #{if rt.startswith('complex')}# zgees #{else}# gees #{endif}# &
198-
(jobz, 'N', nullify(bwork), m, amat, lda, tau, zmat, lda, work, liwork, iwork, info)
199-
call handle_gees_info(info, m, .false._lk, err0)
200-
if (err0%error()) then
201-
call linalg_error_handling(err0, err)
202-
return
223+
224+
!> User or self-allocated eigenvalue storage
225+
if (present(eigvals)) then
226+
227+
lde = size(eigvals, 1, kind=ilp)
228+
229+
#:if rt.startswith('c')
230+
eigs => eigvals
231+
local_eigs = .false.
232+
#:else
233+
allocate(eigs(n),eigi(n))
234+
local_eigs = .true.
235+
#:endif
236+
237+
else
238+
239+
allocate(eigs(n)#{if rt.startswith('r')}#,eigi(n)#{endif}#)
240+
local_eigs = .true.
241+
lde = n
242+
203243
end if
244+
245+
#:if rt.startswith('c')
246+
allocate(rwork(n))
247+
#:endif
204248

205-
! Update workspace size
206-
if (.not.present(lwork)) then
207-
liwork = ceiling(real(work(1), kind=${rk}$), kind=ilp)
208-
deallocate(work)
209-
allocate(work(liwork))
210-
end if
249+
if (lde<n) then
250+
err0 = linalg_state(this, LINALG_VALUE_ERROR, 'Insufficient eigenvalue array size=',lde, &
251+
'should be >=',n)
252+
goto 2
253+
end if
211254

212255
! Compute Schur decomposition
213-
call #{if rt.startswith('complex')}# zgees #{else}# gees #{endif}# &
214-
(jobz, 'N', nullify(bwork), m, amat, lda, tau, zmat, lda, work, liwork, iwork, info)
215-
call handle_gees_info(info, m, present(sort) .and. sort, err0)
216-
if (err0%error()) then
217-
call linalg_error_handling(err0, err)
218-
return
219-
end if
256+
call gees(jobvs,sort,eig_select,nt,t,mt,sdim,eigs,#{if rt.startswith('r')}#eigi,#{endif}# &
257+
vs,ldvs,work,lwork,#{if rt.startswith('c')}#rwork,#{endif}#bwork,info)
258+
call handle_gees_info(info,m,n,m,err0)
220259

221-
! Output results
222-
t = amat
223-
z = zmat
260+
2 eigenvalue_output: if (local_eigs) then
261+
#:if rt.startswith('r')
262+
! Build complex eigenvalues
263+
eigvals = cmplx(eigs,eigi,kind=${rk}$)
264+
deallocate(eigs,eigi)
265+
#:else
266+
deallocate(eigs)
267+
#:endif
268+
endif eigenvalue_output
269+
if (.not.present(storage)) deallocate(work)
270+
1 if (sort/=GEES_NOT) deallocate(bwork)
271+
call linalg_error_handling(err0,err)
272+
273+
contains
274+
275+
! Dummy select routine: currently, no sorting options are offered
276+
pure logical(lk) function eig_select(alpha#{if rt.startswith('r')}#r,alphai#{endif}#)
277+
#:if rt.startswith('r')
278+
${rt}$, intent(in) :: alphar,alphai
279+
complex(${rk}$) :: alpha
280+
alpha = cmplx(alphar,alphai,kind=${rk}$)
281+
#:else
282+
${rt}$, intent(in) :: alpha
283+
#:endif
284+
eig_select = .false.
285+
end function eig_select
224286

225-
if (.not.overwrite_a_) deallocate(amat)
226-
if (.not.present(lwork)) deallocate(work)
227287
end subroutine stdlib_linalg_${ri}$_schur
228288

229289
#:endfor

0 commit comments

Comments
 (0)