(git:08825b9)
Loading...
Searching...
No Matches
quadratic_pseudospectrum.F
Go to the documentation of this file.
1!--------------------------------------------------------------------------------------------------!
2! CP2K: A general program to perform molecular dynamics simulations !
3! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4! !
5! SPDX-License-Identifier: GPL-2.0-or-later !
6!--------------------------------------------------------------------------------------------------!
7
8! **************************************************************************************************
9!> \brief Metric-aware quadratic pseudospectra for finite AO operators.
10!> \author Thomas D. Kuehne, tkuehne@cp2k.org
11! **************************************************************************************************
13 USE ieee_arithmetic, ONLY: ieee_is_finite
14#if defined(__HAS_IEEE_EXCEPTIONS)
15 USE ieee_exceptions, ONLY: ieee_all, ieee_get_halting_mode, ieee_set_halting_mode
16#endif
17 USE kinds, ONLY: dp
18 IMPLICIT NONE
19 PRIVATE
21
23 REAL(dp), ALLOCATABLE :: eigenvalues(:), residuals(:), errors(:, :), means(:, :)
24 REAL(dp), ALLOCATABLE :: projected_errors(:, :), leakage(:, :)
25 COMPLEX(dp), ALLOCATABLE :: states(:, :), complex_means(:, :)
26 INTEGER :: iterations = 0
28
30 MODULE PROCEDURE quadratic_dense_real, quadratic_dense_complex
31 END INTERFACE
33 MODULE PROCEDURE quadratic_solve_real, quadratic_solve_complex
34 END INTERFACE
35
36 abstract INTERFACE
37! **************************************************************************************************
38!> \brief Apply a covariant AO observable or the overlap to a block of coefficients.
39!> \param OPERATOR Zero for overlap, otherwise the requested observable index
40!> \param v Input coefficients
41!> \param y Covariant output vectors
42!> \param status Zero on success
43! **************************************************************************************************
44 SUBROUTINE operator_apply(OPERATOR, v, y, status)
45 IMPORT dp
46 INTEGER, INTENT(IN) :: operator
47 COMPLEX(dp), INTENT(IN) :: v(:, :)
48 COMPLEX(dp), INTENT(OUT) :: y(:, :)
49 INTEGER, INTENT(OUT) :: status
50 END SUBROUTINE operator_apply
51! **************************************************************************************************
52!> \brief Solve the positive AO metric for a block of covariant right hand sides.
53!> \param v Right hand sides
54!> \param y Contravariant solutions
55!> \param status Zero on success
56! **************************************************************************************************
57 SUBROUTINE metric_solve(v, y, status)
58 IMPORT dp
59 COMPLEX(dp), INTENT(IN) :: v(:, :)
60 COMPLEX(dp), INTENT(OUT) :: y(:, :)
61 INTEGER, INTENT(OUT) :: status
62 END SUBROUTINE metric_solve
63 END INTERFACE
64
65 TYPE, ABSTRACT :: quadratic_operator_type
66 CONTAINS
67 PROCEDURE(operator_method), DEFERRED :: apply
68 PROCEDURE(operator_method), DEFERRED :: adjoint
69 PROCEDURE(metric_method), DEFERRED :: solve
70 END TYPE quadratic_operator_type
71
72 abstract INTERFACE
73! **************************************************************************************************
74!> \brief ...
75!> \param this ...
76!> \param OPERATOR ...
77!> \param v ...
78!> \param y ...
79!> \param status ...
80! **************************************************************************************************
81 SUBROUTINE operator_method(this, OPERATOR, v, y, status)
82 IMPORT dp, quadratic_operator_type
83 CLASS(quadratic_operator_type), INTENT(INOUT) :: this
84 INTEGER, INTENT(IN) :: operator
85 COMPLEX(dp), INTENT(IN) :: v(:, :)
86 COMPLEX(dp), INTENT(OUT) :: y(:, :)
87 INTEGER, INTENT(OUT) :: status
88 END SUBROUTINE operator_method
89
90! **************************************************************************************************
91!> \brief ...
92!> \param this ...
93!> \param v ...
94!> \param y ...
95!> \param status ...
96! **************************************************************************************************
97 SUBROUTINE metric_method(this, v, y, status)
98 IMPORT dp, quadratic_operator_type
99 CLASS(quadratic_operator_type), INTENT(INOUT) :: this
100 COMPLEX(dp), INTENT(IN) :: v(:, :)
101 COMPLEX(dp), INTENT(OUT) :: y(:, :)
102 INTEGER, INTENT(OUT) :: status
103 END SUBROUTINE metric_method
104 END INTERFACE
105
106 TYPE, EXTENDS(quadratic_operator_type) :: dense_operator_type
107 COMPLEX(dp), POINTER :: operators(:, :, :) => null(), metric(:, :) => null()
108 COMPLEX(dp), ALLOCATABLE :: factor(:, :)
109 CONTAINS
110 PROCEDURE :: apply => dense_apply
111 PROCEDURE :: adjoint => dense_adjoint
112 PROCEDURE :: solve => dense_solve
113 END TYPE dense_operator_type
114
115 TYPE, EXTENDS(quadratic_operator_type) :: callback_operator_type
116 PROCEDURE(operator_apply), POINTER, NOPASS :: apply_callback => null()
117 PROCEDURE(operator_apply), POINTER, NOPASS :: adjoint_callback => null()
118 PROCEDURE(metric_solve), POINTER, NOPASS :: solve_callback => null()
119 CONTAINS
120 PROCEDURE :: apply => callback_apply
121 PROCEDURE :: adjoint => callback_adjoint
122 PROCEDURE :: solve => callback_solve
123 END TYPE callback_operator_type
124
125CONTAINS
126
127! **************************************************************************************************
128!> \brief Dense AO oracle, also providing a dense-metric reference for the iterative solver.
129!> \param operators Hermitian observables in a covariant AO basis
130!> \param metric Positive AO overlap
131!> \param shift Query value for each observable
132!> \param weights Nonnegative observable scales
133!> \param nstates Number of lowest eigenpairs
134!> \param iterative Use the matrix-free Ritz iteration rather than full diagonalization
135!> \param max_subspace Maximum trial-space dimension
136!> \param max_iter Maximum Ritz iterations
137!> \param tolerance Relative eigenpair residual threshold
138!> \param metric_tolerance Minimum overlap eigenvalue ratio
139!> \param RESULT Eigenpairs, separate observable residuals and expectations
140!> \param status 0 success, 1 unconverged, -1 input, -2 metric, -4 numerical failure
141! **************************************************************************************************
142 SUBROUTINE quadratic_dense_real(operators, metric, shift, weights, nstates, iterative, max_subspace, &
143 max_iter, tolerance, metric_tolerance, RESULT, status)
144 COMPLEX(dp), INTENT(IN) :: operators(:, :, :), metric(:, :)
145 REAL(dp), INTENT(IN) :: shift(:), weights(:)
146 INTEGER, INTENT(IN) :: nstates
147 LOGICAL, INTENT(IN) :: iterative
148 INTEGER, INTENT(IN) :: max_subspace, max_iter
149 REAL(dp), INTENT(IN) :: tolerance, metric_tolerance
150 TYPE(quadratic_result_type), INTENT(OUT) :: result
151 INTEGER, INTENT(OUT) :: status
152
153 INTEGER :: j
154
155 status = -1
156 IF (SIZE(operators, 1) < 1 .OR. SIZE(operators, 1) /= SIZE(operators, 2)) RETURN
157 DO j = 1, SIZE(operators, 3)
158 IF (.NOT. hermitian(operators(:, :, j))) RETURN
159 END DO
160 CALL quadratic_dense_complex(operators, metric, cmplx(shift, 0.0_dp, dp), weights, nstates, iterative, &
161 max_subspace, max_iter, tolerance, metric_tolerance, result, status)
162 END SUBROUTINE quadratic_dense_real
163
164! **************************************************************************************************
165!> \brief Dense oracle for general complex queries and possibly non-Hermitian AO operators.
166!> \param operators Covariant AO operators, not necessarily normal or Hermitian
167!> \param metric Positive AO overlap
168!> \param shift Complex query value for each operator
169!> \param weights Nonnegative residual scales
170!> \param nstates Number of lowest eigenpairs
171!> \param iterative Whether to use the bounded Ritz iteration
172!> \param max_subspace Maximum trial-space dimension
173!> \param max_iter Maximum Ritz iterations
174!> \param tolerance Relative eigenpair residual threshold
175!> \param metric_tolerance Minimum overlap eigenvalue ratio
176!> \param RESULT Eigenpairs, residuals and complex expectations; means holds their real parts
177!> \param status 0 success, 1 unconverged, -1 input, -2 metric, -4 numerical failure
178!> \param unitary Optional mask for compressions of continuum unitary operators, whose full normal Gram is S
179! **************************************************************************************************
180 SUBROUTINE quadratic_dense_complex(operators, metric, shift, weights, nstates, iterative, max_subspace, &
181 max_iter, tolerance, metric_tolerance, RESULT, status, unitary)
182 COMPLEX(dp), INTENT(IN), TARGET :: operators(:, :, :), metric(:, :)
183 COMPLEX(dp), INTENT(IN) :: shift(:)
184 REAL(dp), INTENT(IN) :: weights(:), tolerance, metric_tolerance
185 INTEGER, INTENT(IN) :: nstates, max_subspace, max_iter
186 LOGICAL, INTENT(IN) :: iterative
187 TYPE(quadratic_result_type), INTENT(OUT) :: result
188 INTEGER, INTENT(OUT) :: status
189 LOGICAL, INTENT(IN), OPTIONAL :: unitary(:)
190 TYPE(dense_operator_type) :: operator_backend
191 REAL(dp), ALLOCATABLE :: eigenvalues(:)
192 INTEGER :: n
193#if defined(__HAS_IEEE_EXCEPTIONS)
194 LOGICAL :: halt(SIZE(IEEE_ALL))
195#endif
196
197 status = -1
198 n = SIZE(metric, 1)
199 IF (n < 1 .OR. SIZE(metric, 2) /= n) RETURN
200 IF (SIZE(shift) < 1 .OR. SIZE(weights) /= SIZE(shift)) RETURN
201 IF (any(shape(operators) /= [n, n, SIZE(shift)])) RETURN
202 IF (.NOT. ieee_is_finite(metric_tolerance)) RETURN
203 IF (metric_tolerance <= 0.0_dp .OR. metric_tolerance >= 1.0_dp) RETURN
204 IF (.NOT. hermitian(metric)) RETURN
205 IF (.NOT. all(ieee_is_finite(real(operators, dp))) .OR. &
206 .NOT. all(ieee_is_finite(aimag(operators)))) RETURN
207 operator_backend%operators => operators
208 operator_backend%metric => metric
209 ALLOCATE (operator_backend%factor(n, n), eigenvalues(n))
210 operator_backend%factor(:, :) = metric
211 CALL diagonalize(operator_backend%factor, eigenvalues, status)
212 IF (status /= 0) RETURN
213 status = -2
214 IF (eigenvalues(n) <= 0.0_dp) RETURN
215 IF (eigenvalues(1) <= metric_tolerance*eigenvalues(n)) RETURN
216 operator_backend%factor(:, :) = metric
217#if defined(__HAS_IEEE_EXCEPTIONS)
218 CALL ieee_get_halting_mode(ieee_all, halt)
219 CALL ieee_set_halting_mode(ieee_all, .false.)
220#endif
221 CALL zpotrf('L', n, operator_backend%factor, n, status)
222#if defined(__HAS_IEEE_EXCEPTIONS)
223 CALL ieee_set_halting_mode(ieee_all, halt)
224#endif
225 IF (status /= 0) THEN
226 status = -2
227 RETURN
228 END IF
229 CALL quadratic_solve_core(n, shift, weights, nstates, iterative, max_subspace, max_iter, tolerance, &
230 operator_backend, result, status, unitary)
231 END SUBROUTINE quadratic_dense_complex
232
233! **************************************************************************************************
234!> \brief Apply a dense covariant operator or the metric.
235!> \param this Dense operator backend
236!> \param OPERATOR Zero for metric, otherwise an observable index
237!> \param v Input block
238!> \param y Output block
239!> \param status Return status
240! **************************************************************************************************
241 SUBROUTINE dense_apply(this, OPERATOR, v, y, status)
242 CLASS(dense_operator_type), INTENT(INOUT) :: this
243 INTEGER, INTENT(IN) :: operator
244 COMPLEX(dp), INTENT(IN) :: v(:, :)
245 COMPLEX(dp), INTENT(OUT) :: y(:, :)
246 INTEGER, INTENT(OUT) :: status
247
248 status = 0
249 IF (operator == 0) THEN
250 y = matmul(this%metric, v)
251 ELSE
252 y = matmul(this%operators(:, :, operator), v)
253 END IF
254 END SUBROUTINE dense_apply
255
256! **************************************************************************************************
257!> \brief Apply the actual dense covariant adjoint, without assuming a normal operator.
258!> \param this Dense operator backend
259!> \param OPERATOR Observable index
260!> \param v Input block
261!> \param y Adjoint output block
262!> \param status Return status
263! **************************************************************************************************
264 SUBROUTINE dense_adjoint(this, OPERATOR, v, y, status)
265 CLASS(dense_operator_type), INTENT(INOUT) :: this
266 INTEGER, INTENT(IN) :: operator
267 COMPLEX(dp), INTENT(IN) :: v(:, :)
268 COMPLEX(dp), INTENT(OUT) :: y(:, :)
269 INTEGER, INTENT(OUT) :: status
270
271 y = matmul(conjg(transpose(this%operators(:, :, operator))), v)
272 status = 0
273 END SUBROUTINE dense_adjoint
274
275! **************************************************************************************************
276!> \brief Reuse one dense Cholesky factorization for AO-metric solutions.
277!> \param this Dense operator backend
278!> \param v Right hand sides
279!> \param y Solutions
280!> \param status Return status
281! **************************************************************************************************
282 SUBROUTINE dense_solve(this, v, y, status)
283 CLASS(dense_operator_type), INTENT(INOUT) :: this
284 COMPLEX(dp), INTENT(IN) :: v(:, :)
285 COMPLEX(dp), INTENT(OUT) :: y(:, :)
286 INTEGER, INTENT(OUT) :: status
287
288 INTEGER :: n
289#if defined(__HAS_IEEE_EXCEPTIONS)
290 LOGICAL :: halt(SIZE(IEEE_ALL))
291#endif
292
293 n = SIZE(this%factor, 1)
294 y = v
295#if defined(__HAS_IEEE_EXCEPTIONS)
296 CALL ieee_get_halting_mode(ieee_all, halt)
297 CALL ieee_set_halting_mode(ieee_all, .false.)
298#endif
299 CALL zpotrs('L', n, SIZE(v, 2), this%factor, n, y, n, status)
300#if defined(__HAS_IEEE_EXCEPTIONS)
301 CALL ieee_set_halting_mode(ieee_all, halt)
302#endif
303 IF (status /= 0) status = -4
304 END SUBROUTINE dense_solve
305
306! **************************************************************************************************
307!> \brief Solve sum_j w_j^2 A_j S^-1 A_j c = lambda S c without squaring sparse operators.
308!> \param n AO/spinor dimension
309!> \param shift Query values for the observables
310!> \param weights Nonnegative observable scales
311!> \param nstates Number of lowest eigenpairs
312!> \param iterative Whether to use a bounded restarted block Ritz space
313!> \param max_subspace Maximum trial-space size
314!> \param max_iter Maximum Ritz iterations
315!> \param tolerance Relative S^-1 eigenpair residual threshold
316!> \param apply Apply covariant observables or metric (operator zero)
317!> \param solve Apply inverse metric using already validated factors
318!> \param RESULT Normalized eigenstates and separated physical residuals
319!> \param status 0 success, 1 unconverged, -1 invalid input, -4 numerical failure
320! **************************************************************************************************
321 SUBROUTINE quadratic_solve_real(n, shift, weights, nstates, iterative, max_subspace, max_iter, tolerance, &
322 apply, solve, RESULT, status)
323 INTEGER, INTENT(IN) :: n
324 REAL(dp), INTENT(IN) :: shift(:), weights(:)
325 INTEGER, INTENT(IN) :: nstates
326 LOGICAL, INTENT(IN) :: iterative
327 INTEGER, INTENT(IN) :: max_subspace, max_iter
328 REAL(dp), INTENT(IN) :: tolerance
329
330 PROCEDURE(operator_apply) :: apply
331 PROCEDURE(metric_solve) :: solve
332 TYPE(quadratic_result_type), INTENT(OUT) :: result
333 INTEGER, INTENT(OUT) :: status
334
335 CALL quadratic_solve_complex(n, cmplx(shift, 0.0_dp, dp), weights, nstates, iterative, max_subspace, &
336 max_iter, tolerance, apply, solve, result, status, apply)
337 END SUBROUTINE quadratic_solve_real
338
339! **************************************************************************************************
340!> \brief Solve sum_j w_j^2 (A_j-z_j S)^dagger S^-1 (A_j-z_j S) c = lambda S c.
341!> \param n AO/spinor dimension
342!> \param shift Complex queries for arbitrary, possibly nonnormal operators
343!> \param weights Nonnegative residual scales
344!> \param nstates Number of lowest eigenpairs
345!> \param iterative Whether to use a bounded restarted block Ritz space
346!> \param max_subspace Maximum trial-space size
347!> \param max_iter Maximum Ritz iterations
348!> \param tolerance Relative S^-1 eigenpair residual threshold
349!> \param apply Apply covariant operators or the positive metric (operator zero)
350!> \param solve Solve with the already validated positive metric
351!> \param RESULT Eigenpairs, residuals and complex expectations; means holds their real parts
352!> \param status 0 success, 1 unconverged, -1 input, -4 numerical failure
353!> \param adjoint Apply A_j^dagger, not the coefficient-space S-adjoint; positive operator indices
354!> \param unitary True for compressions of continuum unitary operators; retain full norms and projection leakage
355! **************************************************************************************************
356 SUBROUTINE quadratic_solve_complex(n, shift, weights, nstates, iterative, max_subspace, max_iter, tolerance, &
357 apply, solve, RESULT, status, adjoint, unitary)
358 INTEGER, INTENT(IN) :: n
359 COMPLEX(dp), INTENT(IN) :: shift(:)
360 REAL(dp), INTENT(IN) :: weights(:)
361 INTEGER, INTENT(IN) :: nstates
362 LOGICAL, INTENT(IN) :: iterative
363 INTEGER, INTENT(IN) :: max_subspace, max_iter
364 REAL(dp), INTENT(IN) :: tolerance
365
366 PROCEDURE(operator_apply) :: apply, adjoint
367 PROCEDURE(metric_solve) :: solve
368 TYPE(quadratic_result_type), INTENT(OUT) :: result
369 INTEGER, INTENT(OUT) :: status
370 LOGICAL, INTENT(IN), OPTIONAL :: unitary(:)
371
372 TYPE(callback_operator_type) :: operator_backend
373
374 operator_backend%apply_callback => apply
375 operator_backend%adjoint_callback => adjoint
376 operator_backend%solve_callback => solve
377 CALL quadratic_solve_core(n, shift, weights, nstates, iterative, max_subspace, max_iter, tolerance, &
378 operator_backend, result, status, unitary)
379 END SUBROUTINE quadratic_solve_complex
380
381! **************************************************************************************************
382!> \brief Shared quadratic solver using an explicit operator backend.
383!> \param n AO/spinor dimension
384!> \param shift Complex query values
385!> \param weights Nonnegative observable scales
386!> \param nstates Number of lowest eigenpairs
387!> \param iterative Whether to use a bounded restarted block Ritz space
388!> \param max_subspace Maximum trial-space size
389!> \param max_iter Maximum Ritz iterations
390!> \param tolerance Relative S^-1 eigenpair residual threshold
391!> \param operator_backend Operator application and metric-solve backend
392!> \param RESULT Eigenpairs, residuals and expectations
393!> \param status 0 success, 1 unconverged, -1 input, -4 numerical failure
394!> \param unitary Optional continuum-unitary mask
395! **************************************************************************************************
396 SUBROUTINE quadratic_solve_core(n, shift, weights, nstates, iterative, max_subspace, max_iter, tolerance, &
397 operator_backend, RESULT, status, unitary)
398 INTEGER, INTENT(IN) :: n, nstates, max_subspace, max_iter
399 COMPLEX(dp), INTENT(IN) :: shift(:)
400 REAL(dp), INTENT(IN) :: weights(:), tolerance
401 LOGICAL, INTENT(IN) :: iterative
402 CLASS(quadratic_operator_type), INTENT(INOUT) :: operator_backend
403 TYPE(quadratic_result_type), INTENT(OUT) :: result
404 INTEGER, INTENT(OUT) :: status
405 LOGICAL, INTENT(IN), OPTIONAL :: unitary(:)
406 COMPLEX(dp), ALLOCATABLE :: v(:, :), sv(:, :), qv(:, :), projected(:, :), sc(:, :), qc(:, :), &
407 r(:, :), z(:, :), trial(:, :), strial(:, :), work(:, :), invwork(:, :), &
408 restarted(:, :)
409 REAL(dp), ALLOCATABLE :: eigenvalues(:)
410 REAL(dp) :: square, scale, norm_before, norm_after, full_square, lost_square
411 COMPLEX(dp) :: coefficient
412 INTEGER :: capacity, m, old, i, j, p, iteration, seed, initial, pass, keep
413 LOGICAL :: accepted
414 LOGICAL, ALLOCATABLE :: continuum(:)
415
416 status = -1
417 IF (n < 1 .OR. nstates < 1 .OR. nstates > n .OR. max_iter < 1) RETURN
418 IF (SIZE(shift) < 1 .OR. SIZE(weights) /= SIZE(shift)) RETURN
419 ALLOCATE (continuum(SIZE(shift)))
420 continuum = .false.
421 IF (PRESENT(unitary)) THEN
422 IF (SIZE(unitary) /= SIZE(shift)) RETURN
423 continuum(:) = unitary
424 END IF
425 IF (.NOT. all(ieee_is_finite([real(shift, dp), aimag(shift), weights, tolerance]))) RETURN
426 IF (any(weights < 0.0_dp) .OR. maxval(weights) <= 0.0_dp .OR. tolerance <= 0.0_dp) RETURN
427 capacity = n
428 IF (iterative) capacity = min(n, max_subspace)
429 IF (capacity < nstates .OR. (capacity < n .AND. capacity < 2*nstates)) RETURN
430 ALLOCATE (v(n, capacity), sv(n, capacity), qv(n, capacity), sc(n, nstates), qc(n, nstates), &
431 r(n, nstates), z(n, nstates), trial(n, 1), strial(n, 1), work(n, nstates), invwork(n, nstates))
432 ALLOCATE (result%states(n, nstates), result%eigenvalues(nstates), result%residuals(nstates), &
433 result%errors(SIZE(shift), nstates), result%means(SIZE(shift), nstates), &
434 result%complex_means(SIZE(shift), nstates), &
435 result%projected_errors(SIZE(shift), nstates), result%leakage(SIZE(shift), nstates))
436 result%leakage = 0.0_dp
437 m = 0
438 seed = 0
439 initial = n
440 IF (iterative) initial = min(capacity, max(nstates + 2, 2*nstates))
441 DO j = 1, initial
442 trial = cmplx(0.0_dp, 0.0_dp, dp)
443 IF (.NOT. iterative) THEN
444 trial(j, 1) = 1.0_dp
445 ELSE
446 seed = seed + 1
447 CALL seed_vector()
448 END IF
449 CALL append_vector(accepted)
450 IF (status /= 0) RETURN
451 IF (.NOT. accepted) THEN
452 status = -4
453 RETURN
454 END IF
455 END DO
456 CALL apply_q(v(:, :m), qv(:, :m), status)
457 IF (status /= 0) RETURN
458 DO iteration = 1, max_iter
459 result%iterations = iteration
460 ALLOCATE (projected(m, m), eigenvalues(m))
461 projected(:, :) = matmul(conjg(transpose(v(:, :m))), qv(:, :m))
462 scale = max(1.0_dp, maxval(abs(projected)))
463 IF (maxval(abs(projected - conjg(transpose(projected)))) > 1.e-8_dp*scale) THEN
464 status = -4
465 RETURN
466 END IF
467 projected(:, :) = 0.5_dp*(projected + conjg(transpose(projected)))
468 CALL diagonalize(projected, eigenvalues, status)
469 IF (status /= 0) RETURN
470 result%eigenvalues(:) = eigenvalues(:nstates)
471 result%states(:, :) = matmul(v(:, :m), projected(:, :nstates))
472 sc(:, :) = matmul(sv(:, :m), projected(:, :nstates))
473 qc(:, :) = matmul(qv(:, :m), projected(:, :nstates))
474 DO pass = 1, 2
475 IF (pass == 2) THEN
476 ! Cached metric/operator images can drift through repeated Ritz restarts.
477 CALL operator_backend%apply(0, result%states, sc, status)
478 IF (status /= 0) RETURN
479 DO j = 1, nstates
480 square = real(dot_product(result%states(:, j), sc(:, j)), dp)
481 IF (.NOT. ieee_is_finite(square) .OR. square <= 0.0_dp) THEN
482 status = -4
483 RETURN
484 END IF
485 result%states(:, j) = result%states(:, j)/sqrt(square)
486 END DO
487 CALL operator_backend%apply(0, result%states, sc, status)
488 IF (status /= 0) RETURN
489 CALL apply_q(result%states, qc, status)
490 IF (status /= 0) RETURN
491 END IF
492 DO j = 1, nstates
493 r(:, j) = qc(:, j) - result%eigenvalues(j)*sc(:, j)
494 END DO
495 CALL operator_backend%solve(r, z, status)
496 IF (status /= 0) RETURN
497 DO j = 1, nstates
498 square = real(dot_product(r(:, j), z(:, j)), dp)
499 IF (.NOT. ieee_is_finite(square) .OR. square < -epsilon(square)*scale) THEN
500 status = -4
501 RETURN
502 END IF
503 result%residuals(j) = sqrt(max(0.0_dp, square))
504 END DO
505 IF (any(result%residuals > tolerance*max(1.0_dp, abs(result%eigenvalues)))) EXIT
506 END DO
507 IF (all(result%residuals <= tolerance*max(1.0_dp, abs(result%eigenvalues)))) THEN
508 DEALLOCATE (projected, eigenvalues)
509 EXIT
510 END IF
511 IF (.NOT. iterative .OR. m == n .OR. iteration == max_iter) THEN
512 status = 1
513 RETURN
514 END IF
515 ! Fill remaining slots with a partial residual block before restarting.
516 IF (m == capacity) THEN
517 ! Retain nearby Ritz vectors across restarts, important for clustered spectra.
518 keep = min(m, max(nstates, capacity/2))
519 ALLOCATE (restarted(n, keep))
520 restarted(:, :) = matmul(v(:, :m), projected(:, :keep))
521 v(:, :keep) = restarted
522 restarted(:, :) = matmul(sv(:, :m), projected(:, :keep))
523 sv(:, :keep) = restarted
524 restarted(:, :) = matmul(qv(:, :m), projected(:, :keep))
525 qv(:, :keep) = restarted
526 DEALLOCATE (restarted)
527 m = keep
528 END IF
529 DEALLOCATE (projected, eigenvalues)
530 old = m
531 DO j = 1, nstates
532 IF (m == capacity) EXIT
533 trial(:, 1) = z(:, j)
534 CALL append_vector(accepted)
535 IF (status /= 0) RETURN
536 END DO
537 IF (m == old) THEN
538 seed = seed + 1
539 CALL seed_vector()
540 CALL append_vector(accepted)
541 IF (status /= 0) RETURN
542 IF (.NOT. accepted) THEN
543 status = 1
544 RETURN
545 END IF
546 END IF
547 CALL apply_q(v(:, old + 1:m), qv(:, old + 1:m), status)
548 IF (status /= 0) RETURN
549 END DO
550 IF (any(result%eigenvalues < -tolerance*max(1.0_dp, maxval(abs(result%eigenvalues))))) THEN
551 status = -4
552 RETURN
553 END IF
554 result%eigenvalues = max(0.0_dp, result%eigenvalues)
555 DO p = 1, SIZE(shift)
556 CALL operator_backend%apply(p, result%states, work, status)
557 IF (status /= 0) RETURN
558 DO j = 1, nstates
559 result%complex_means(p, j) = dot_product(result%states(:, j), work(:, j))
560 result%means(p, j) = real(result%complex_means(p, j), dp)
561 END DO
562 work(:, :) = work - shift(p)*sc
563 CALL operator_backend%solve(work, invwork, status)
564 IF (status /= 0) RETURN
565 DO j = 1, nstates
566 square = real(dot_product(work(:, j), invwork(:, j)), dp)
567 IF (.NOT. ieee_is_finite(square) .OR. square < -tolerance) THEN
568 status = -4
569 RETURN
570 END IF
571 result%projected_errors(p, j) = sqrt(max(0.0_dp, square))
572 IF (continuum(p)) THEN
573 full_square = (1.0_dp + abs(shift(p))**2)*real(dot_product(result%states(:, j), sc(:, j)), dp) &
574 - 2.0_dp*real(conjg(shift(p))*result%complex_means(p, j), dp)
575 lost_square = full_square - square
576 scale = max(1.0_dp, abs(full_square), abs(square))
577 IF (.NOT. ieee_is_finite(full_square) .OR. full_square < -tolerance*scale .OR. &
578 lost_square < -tolerance*scale) THEN
579 status = -4
580 RETURN
581 END IF
582 result%leakage(p, j) = sqrt(max(0.0_dp, lost_square))
583 square = full_square
584 END IF
585 result%errors(p, j) = sqrt(max(0.0_dp, square))
586 END DO
587 END DO
588 DO j = 1, nstates
589 square = sum((weights*result%errors(:, j))**2)
590 IF (abs(square - result%eigenvalues(j)) > 10.0_dp*tolerance*max(1.0_dp, square)) THEN
591 status = -4
592 RETURN
593 END IF
594 END DO
595 status = 0
596 CONTAINS
597! **************************************************************************************************
598!> \brief Deterministic complex trial block, independent of compiler random-number generators.
599! **************************************************************************************************
600 SUBROUTINE seed_vector()
601 INTEGER :: k
602
603 DO k = 1, n
604 trial(k, 1) = cmplx(sin(real(k, dp)*real(seed + 1, dp)*1.4142135623730951_dp), &
605 cos(real(k, dp)*real(seed + 2, dp)*1.7320508075688772_dp), dp)
606 END DO
607 END SUBROUTINE seed_vector
608! **************************************************************************************************
609!> \brief Twice-reorthogonalized S-metric expansion, rejecting linearly dependent vectors.
610!> \param accepted Whether a new basis vector was appended
611! **************************************************************************************************
612 SUBROUTINE append_vector(accepted)
613 LOGICAL, INTENT(OUT) :: accepted
614
615 accepted = .false.
616 CALL operator_backend%apply(0, trial, strial, status)
617 IF (status /= 0) RETURN
618 norm_before = real(dot_product(trial(:, 1), strial(:, 1)), dp)
619 IF (.NOT. ieee_is_finite(norm_before) .OR. norm_before < 0.0_dp) THEN
620 status = -4
621 RETURN
622 END IF
623 DO pass = 1, 2
624 DO i = 1, m
625 coefficient = dot_product(v(:, i), strial(:, 1))
626 trial(:, 1) = trial(:, 1) - coefficient*v(:, i)
627 strial(:, 1) = strial(:, 1) - coefficient*sv(:, i)
628 END DO
629 END DO
630 norm_after = real(dot_product(trial(:, 1), strial(:, 1)), dp)
631 IF (.NOT. ieee_is_finite(norm_after)) THEN
632 status = -4
633 RETURN
634 END IF
635 IF (norm_after <= 1.e-24_dp*max(tiny(1.0_dp), norm_before)) RETURN
636 m = m + 1
637 v(:, m) = trial(:, 1)/sqrt(norm_after)
638 sv(:, m) = strial(:, 1)/sqrt(norm_after)
639 accepted = .true.
640 END SUBROUTINE append_vector
641! **************************************************************************************************
642!> \brief Matrix-free covariant normal products, reusing the positive metric factorization.
643!> \param block Input AO coefficients
644!> \param image Covariant Q times the input
645!> \param status Return status
646! **************************************************************************************************
647 SUBROUTINE apply_q(block, image, status)
648 COMPLEX(dp), INTENT(IN) :: block(:, :)
649 COMPLEX(dp), INTENT(OUT) :: image(:, :)
650 INTEGER, INTENT(OUT) :: status
651
652 COMPLEX(dp), ALLOCATABLE :: ab(:, :), sb(:, :), second(:, :), &
653 solved(:, :), ss(:, :)
654 INTEGER :: nrhs, op
655
656 nrhs = SIZE(block, 2)
657 ALLOCATE (sb(n, nrhs), ab(n, nrhs), solved(n, nrhs), second(n, nrhs), ss(n, nrhs))
658 CALL operator_backend%apply(0, block, sb, status)
659 IF (status /= 0) RETURN
660 image = cmplx(0.0_dp, 0.0_dp, dp)
661 DO op = 1, SIZE(shift)
662 IF (weights(op) == 0.0_dp) cycle
663 CALL operator_backend%apply(op, block, ab, status)
664 IF (status /= 0) RETURN
665 IF (continuum(op)) THEN
666 CALL operator_backend%adjoint(op, block, second, status)
667 IF (status /= 0) RETURN
668 image = image + weights(op)**2*((1.0_dp + abs(shift(op))**2)*sb &
669 - conjg(shift(op))*ab - shift(op)*second)
670 cycle
671 END IF
672 ab(:, :) = ab - shift(op)*sb
673 CALL operator_backend%solve(ab, solved, status)
674 IF (status /= 0) RETURN
675 CALL operator_backend%adjoint(op, solved, second, status)
676 IF (status /= 0) RETURN
677 CALL operator_backend%apply(0, solved, ss, status)
678 IF (status /= 0) RETURN
679 image = image + weights(op)**2*(second - conjg(shift(op))*ss)
680 END DO
681 END SUBROUTINE apply_q
682 END SUBROUTINE quadratic_solve_core
683
684! **************************************************************************************************
685!> \brief Forward an operator application to the public callback interface.
686!> \param this ...
687!> \param OPERATOR ...
688!> \param v ...
689!> \param y ...
690!> \param status ...
691! **************************************************************************************************
692 SUBROUTINE callback_apply(this, OPERATOR, v, y, status)
693 CLASS(callback_operator_type), INTENT(INOUT) :: this
694 INTEGER, INTENT(IN) :: operator
695 COMPLEX(dp), INTENT(IN) :: v(:, :)
696 COMPLEX(dp), INTENT(OUT) :: y(:, :)
697 INTEGER, INTENT(OUT) :: status
698
699 CALL this%apply_callback(operator, v, y, status)
700 END SUBROUTINE callback_apply
701
702! **************************************************************************************************
703!> \brief Forward an adjoint application to the public callback interface.
704!> \param this ...
705!> \param OPERATOR ...
706!> \param v ...
707!> \param y ...
708!> \param status ...
709! **************************************************************************************************
710 SUBROUTINE callback_adjoint(this, OPERATOR, v, y, status)
711 CLASS(callback_operator_type), INTENT(INOUT) :: this
712 INTEGER, INTENT(IN) :: operator
713 COMPLEX(dp), INTENT(IN) :: v(:, :)
714 COMPLEX(dp), INTENT(OUT) :: y(:, :)
715 INTEGER, INTENT(OUT) :: status
716
717 CALL this%adjoint_callback(operator, v, y, status)
718 END SUBROUTINE callback_adjoint
719
720! **************************************************************************************************
721!> \brief Forward a metric solve to the public callback interface.
722!> \param this ...
723!> \param v ...
724!> \param y ...
725!> \param status ...
726! **************************************************************************************************
727 SUBROUTINE callback_solve(this, v, y, status)
728 CLASS(callback_operator_type), INTENT(INOUT) :: this
729 COMPLEX(dp), INTENT(IN) :: v(:, :)
730 COMPLEX(dp), INTENT(OUT) :: y(:, :)
731 INTEGER, INTENT(OUT) :: status
732
733 CALL this%solve_callback(v, y, status)
734 END SUBROUTINE callback_solve
735
736! **************************************************************************************************
737!> \brief Hermiticity and finiteness check for the dense reference.
738!> \param matrix Input square matrix
739!> \return Whether the matrix is finite Hermitian
740! **************************************************************************************************
741 FUNCTION hermitian(matrix) RESULT(valid)
742 COMPLEX(dp), INTENT(IN) :: matrix(:, :)
743 LOGICAL :: valid
744
745 valid = .false.
746 IF (.NOT. all(ieee_is_finite(real(matrix, dp))) .OR. .NOT. all(ieee_is_finite(aimag(matrix)))) RETURN
747 valid = maxval(abs(matrix - conjg(transpose(matrix)))) <= 1.e-10_dp*max(tiny(1.0_dp), maxval(abs(matrix)))
748 END FUNCTION hermitian
749
750! **************************************************************************************************
751!> \brief Small dense Hermitian eigensolve shared by the oracle and Ritz projection.
752!> \param matrix Overwritten with eigenvectors
753!> \param values Ascending eigenvalues
754!> \param status Return status
755! **************************************************************************************************
756 SUBROUTINE diagonalize(matrix, values, status)
757 COMPLEX(dp), INTENT(INOUT) :: matrix(:, :)
758 REAL(dp), INTENT(OUT) :: values(:)
759 INTEGER, INTENT(OUT) :: status
760 COMPLEX(dp), ALLOCATABLE :: workspace(:)
761 REAL(dp), ALLOCATABLE :: rwork(:)
762 INTEGER :: n
763#if defined(__HAS_IEEE_EXCEPTIONS)
764 LOGICAL :: halt(SIZE(IEEE_ALL))
765#endif
766 n = SIZE(values)
767 ALLOCATE (workspace(max(1, 2*n)), rwork(max(1, 3*n - 2)))
768#if defined(__HAS_IEEE_EXCEPTIONS)
769 CALL ieee_get_halting_mode(ieee_all, halt)
770 CALL ieee_set_halting_mode(ieee_all, .false.)
771#endif
772 CALL zheev('V', 'L', n, matrix, n, values, workspace, SIZE(workspace), rwork, status)
773#if defined(__HAS_IEEE_EXCEPTIONS)
774 CALL ieee_set_halting_mode(ieee_all, halt)
775#endif
776 IF (status /= 0 .OR. .NOT. all(ieee_is_finite(values))) status = -4
777 END SUBROUTINE diagonalize
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Metric-aware quadratic pseudospectra for finite AO operators.
subroutine quadratic_dense_real(operators, metric, shift, weights, nstates, iterative, max_subspace, max_iter, tolerance, metric_tolerance, result, status)
Dense AO oracle, also providing a dense-metric reference for the iterative solver.