@@ -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