(git:5e7fe52)
Loading...
Searching...
No Matches
qs_scf_subspace_math.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 Small, matrix-free mathematical kernels used by ADIIS.
10!>
11!> The quadratic model uses the convention
12!>
13!> f(c) = 1/2 c^T hessian c + linear^T c,
14!>
15!> with c on the probability simplex. Only the symmetric part of the
16!> supplied Hessian contributes to the objective.
17! **************************************************************************************************
19
20 USE ieee_arithmetic, ONLY: ieee_is_finite
21 USE kinds, ONLY: dp
22
23 IMPLICIT NONE
24
25 PRIVATE
26
27 INTEGER, PARAMETER, PUBLIC :: simplex_qp_success = 0
28 INTEGER, PARAMETER, PUBLIC :: simplex_qp_invalid_shape = 1
29 INTEGER, PARAMETER, PUBLIC :: simplex_qp_nonfinite_input = 2
30 INTEGER, PARAMETER, PUBLIC :: simplex_qp_iteration_limit = 3
31 INTEGER, PARAMETER, PUBLIC :: simplex_qp_pairwise_stationary = 4
32
33 INTEGER, PARAMETER :: max_exact_dimension = 12
34
38
39CONTAINS
40
41! **************************************************************************************************
42!> \brief Minimize a quadratic model over the probability simplex.
43!> \param hessian Hessian in f(c) = 1/2 c^T hessian c + linear^T c.
44!> \param linear Linear part of the quadratic model.
45!> \param coeff Resulting non-negative coefficients, normalized to sum to one.
46!> \param objective Objective value at coeff.
47!> \param status Completion status. Invalid/nonfinite inputs use the preferred-point fallback;
48!> iteration limits return the best legal point found.
49!> \param preferred_index History entry preferred for fallbacks and numerical ties; defaults to n.
50!>
51!> SCF histories are normally very small. Up to max_exact_dimension, all
52!> active faces are enumerated. A global minimizer of a quadratic over a
53!> simplex is a stationary point in the relative interior of one of these
54!> faces (a singular stationary face always contains an equivalent boundary
55!> solution). Larger problems use multistart, simplex-preserving pairwise
56!> line searches and return simplex_qp_pairwise_stationary. This only proves
57!> that no improving two-coordinate transfer was found; for an indefinite
58!> quadratic it is a heuristic result, not a general local/global optimum.
59! **************************************************************************************************
60 SUBROUTINE simplex_quadratic_minimize(hessian, linear, coeff, objective, status, preferred_index)
61
62 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: hessian
63 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: linear
64 REAL(kind=dp), DIMENSION(:), INTENT(OUT) :: coeff
65 REAL(kind=dp), INTENT(OUT), OPTIONAL :: objective
66 INTEGER, INTENT(OUT), OPTIONAL :: status
67 INTEGER, INTENT(IN), OPTIONAL :: preferred_index
68
69 INTEGER :: local_status, n, preferred
70 REAL(kind=dp) :: best_value, model_scale
71 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: working_linear
72 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: symmetric_hessian, working_hessian
73
74 n = SIZE(linear)
75 preferred = n
76 local_status = simplex_qp_success
77 best_value = huge(0.0_dp)
78
79 coeff = 0.0_dp
80 IF (PRESENT(preferred_index)) THEN
81 IF (preferred_index >= 1 .AND. preferred_index <= n) THEN
82 preferred = preferred_index
83 ELSE
84 local_status = simplex_qp_invalid_shape
85 IF (SIZE(coeff) > 0) coeff(SIZE(coeff)) = 1.0_dp
86 IF (PRESENT(objective)) objective = best_value
87 IF (PRESENT(status)) status = local_status
88 RETURN
89 END IF
90 END IF
91 IF (preferred >= 1 .AND. preferred <= SIZE(coeff)) coeff(preferred) = 1.0_dp
92
93 IF (n < 1 .OR. SIZE(coeff) /= n .OR. SIZE(hessian, 1) /= n .OR. SIZE(hessian, 2) /= n) THEN
94 local_status = simplex_qp_invalid_shape
95 IF (PRESENT(objective)) objective = best_value
96 IF (PRESENT(status)) status = local_status
97 RETURN
98 END IF
99
100 IF (.NOT. all(ieee_is_finite(hessian)) .OR. .NOT. all(ieee_is_finite(linear))) THEN
101 local_status = simplex_qp_nonfinite_input
102 IF (PRESENT(objective)) objective = best_value
103 IF (PRESENT(status)) status = local_status
104 RETURN
105 END IF
106
107 ALLOCATE (symmetric_hessian(n, n), working_hessian(n, n), working_linear(n))
108 symmetric_hessian(:, :) = 0.5_dp*hessian + 0.5_dp*transpose(hessian)
109
110 ! Remove terms that are constant on the simplex, then normalize the remaining model.
111 ! Besides improving the KKT conditioning, this makes coefficient selection invariant to
112 working_hessian(:, :) = symmetric_hessian
113 working_linear(:) = linear
114 model_scale = max(maxval(abs(working_hessian)), maxval(abs(working_linear)))
115 ! Scale first only at extreme magnitudes where subtracting opposite-signed finite
116 ! values could overflow. At ordinary scales, removing the gauge first preserves
117 IF (model_scale > 0.25_dp*huge(0.0_dp)) THEN
118 working_hessian(:, :) = working_hessian/model_scale
119 working_linear(:) = working_linear/model_scale
120 END IF
121 ! Subtracting one scalar from every Hessian element is a H_nn*1*1^T gauge
122 ! shift and is therefore constant when sum(coeff)=1.
123 working_hessian(:, :) = working_hessian - working_hessian(n, n)
124 working_linear(:) = working_linear - working_linear(n)
125 model_scale = max(maxval(abs(working_hessian)), maxval(abs(working_linear)))
126 IF (model_scale > 0.0_dp) THEN
127 working_hessian(:, :) = working_hessian/model_scale
128 working_linear(:) = working_linear/model_scale
129 END IF
130
131 CALL best_vertex(working_hessian, working_linear, preferred, coeff, best_value)
132
133 IF (n <= max_exact_dimension) THEN
134 CALL enumerate_active_faces(working_hessian, working_linear, preferred, coeff, best_value)
135 ELSE
136 CALL pairwise_minimize(working_hessian, working_linear, preferred, coeff, best_value, local_status)
137 END IF
138
139 ! Remove harmless round-off at active constraints before returning.
140 coeff = max(coeff, 0.0_dp)
141 coeff = coeff/sum(coeff)
142 best_value = quadratic_value(symmetric_hessian, linear, coeff)
143
144 IF (PRESENT(objective)) objective = best_value
145 IF (PRESENT(status)) status = local_status
146
147 END SUBROUTINE simplex_quadratic_minimize
148
149! **************************************************************************************************
150!> \brief Construct the canonical ADIIS model relative to the newest history entry.
151!> \param pf_metric Matrix T_ij=Tr(P_i F_j), already summed over spin/k-points as needed.
152!> \param newest Index of the reference history entry.
153!> \param hessian Hessian of the canonical simplex quadratic model.
154!> \param linear Linear term of the canonical simplex quadratic model.
155!> \param valid Whether all dimensions and input values were valid.
156! **************************************************************************************************
157 SUBROUTINE qs_scf_subspace_build_adiis_model(pf_metric, newest, hessian, linear, valid)
158
159 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: pf_metric
160 INTEGER, INTENT(IN) :: newest
161 REAL(kind=dp), DIMENSION(:, :), INTENT(OUT) :: hessian
162 REAL(kind=dp), DIMENSION(:), INTENT(OUT) :: linear
163 LOGICAL, INTENT(OUT), OPTIONAL :: valid
164
165 INTEGER :: i, j, n
166 LOGICAL :: local_valid
167 REAL(kind=dp) :: model_ij, model_ji, tnn
168
169 n = SIZE(linear)
170 hessian = 0.0_dp
171 linear = 0.0_dp
172
173 local_valid = n > 0 .AND. newest >= 1 .AND. newest <= n .AND. &
174 SIZE(pf_metric, 1) == n .AND. SIZE(pf_metric, 2) == n .AND. &
175 SIZE(hessian, 1) == n .AND. SIZE(hessian, 2) == n
176 IF (local_valid) local_valid = all(ieee_is_finite(pf_metric))
177 IF (.NOT. local_valid) THEN
178 IF (PRESENT(valid)) valid = .false.
179 RETURN
180 END IF
181
182 tnn = pf_metric(newest, newest)
183 DO i = 1, n
184 linear(i) = 2.0_dp*(pf_metric(i, newest) - tnn)
185 END DO
186
187 DO j = 1, n
188 DO i = 1, n
189 model_ij = (pf_metric(i, j) - pf_metric(i, newest)) + &
190 (tnn - pf_metric(newest, j))
191 model_ji = (pf_metric(j, i) - pf_metric(j, newest)) + &
192 (tnn - pf_metric(newest, i))
193 hessian(i, j) = model_ij + model_ji
194 END DO
195 END DO
196
197 IF (PRESENT(valid)) valid = .true.
198
200
201! **************************************************************************************************
202!> \brief Select the next physical slot for a strict FIFO history.
203!> \param generation Insertion generation for each physical slot; zero denotes an empty slot.
204!> \return First empty slot, otherwise the oldest occupied slot; zero for invalid input.
205! **************************************************************************************************
206 PURE FUNCTION qs_scf_subspace_fifo_slot(generation) RESULT(slot)
207
208 INTEGER, DIMENSION(:), INTENT(IN) :: generation
209 INTEGER :: slot
210
211 INTEGER :: i
212
213 slot = 0
214 IF (SIZE(generation) < 1) RETURN
215 IF (any(generation < 0)) RETURN
216
217 DO i = 1, SIZE(generation)
218 IF (generation(i) == 0) THEN
219 slot = i
220 RETURN
221 END IF
222 END DO
223 slot = minloc(generation, dim=1)
224
225 END FUNCTION qs_scf_subspace_fifo_slot
226
227! **************************************************************************************************
228!> \brief Select the best simplex vertex, preferring the newest one on ties.
229!> \param hessian Symmetric quadratic-model Hessian.
230!> \param linear Quadratic-model linear term.
231!> \param preferred History index favored when vertex values are tied.
232!> \param coeff Coefficients of the selected vertex.
233!> \param best_value Objective value at the selected vertex.
234! **************************************************************************************************
235 SUBROUTINE best_vertex(hessian, linear, preferred, coeff, best_value)
236
237 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: hessian
238 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: linear
239 INTEGER, INTENT(IN) :: preferred
240 REAL(kind=dp), DIMENSION(:), INTENT(OUT) :: coeff
241 REAL(kind=dp), INTENT(OUT) :: best_value
242
243 INTEGER :: i, n
244 REAL(kind=dp) :: value
245
246 n = SIZE(linear)
247 coeff = 0.0_dp
248 coeff(preferred) = 1.0_dp
249 best_value = 0.5_dp*hessian(preferred, preferred) + linear(preferred)
250
251 DO i = n, 1, -1
252 IF (i == preferred) cycle
253 value = 0.5_dp*hessian(i, i) + linear(i)
254 IF (strictly_better(value, best_value)) THEN
255 coeff = 0.0_dp
256 coeff(i) = 1.0_dp
257 best_value = value
258 END IF
259 END DO
260
261 END SUBROUTINE best_vertex
262
263! **************************************************************************************************
264!> \brief Enumerate stationary points on every active face of a small simplex.
265!> \param hessian Symmetric quadratic-model Hessian.
266!> \param linear Quadratic-model linear term.
267!> \param preferred History index favored when objective values are tied.
268!> \param coeff Best feasible coefficients found so far.
269!> \param best_value Objective value corresponding to coeff.
270! **************************************************************************************************
271 SUBROUTINE enumerate_active_faces(hessian, linear, preferred, coeff, best_value)
272
273 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: hessian
274 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: linear
275 INTEGER, INTENT(IN) :: preferred
276 REAL(kind=dp), DIMENSION(:), INTENT(INOUT) :: coeff
277 REAL(kind=dp), INTENT(INOUT) :: best_value
278
279 INTEGER :: i, j, k, last_mask, mask, n
280 INTEGER, ALLOCATABLE, DIMENSION(:) :: active
281 LOGICAL :: solved
282 REAL(kind=dp) :: feasibility_tolerance, scale, value
283 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: candidate, rhs, solution
284 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: kkt
285
286 n = SIZE(linear)
287 last_mask = ishft(1, n) - 1
288 ALLOCATE (active(n), candidate(n))
289
290 DO mask = 1, last_mask
291 k = popcnt(mask)
292 IF (k < 2) cycle
293
294 j = 0
295 DO i = 1, n
296 IF (btest(mask, i - 1)) THEN
297 j = j + 1
298 active(j) = i
299 END IF
300 END DO
301
302 ALLOCATE (kkt(k + 1, k + 1), rhs(k + 1), solution(k + 1))
303 kkt = 0.0_dp
304 rhs = 0.0_dp
305
306 DO j = 1, k
307 rhs(j) = -linear(active(j))
308 kkt(j, k + 1) = 1.0_dp
309 kkt(k + 1, j) = 1.0_dp
310 DO i = 1, k
311 kkt(i, j) = hessian(active(i), active(j))
312 END DO
313 END DO
314 rhs(k + 1) = 1.0_dp
315
316 CALL solve_dense_linear(kkt, rhs, solution, solved)
317 IF (solved) THEN
318 scale = max(1.0_dp, maxval(abs(solution(1:k))))
319 feasibility_tolerance = 2048.0_dp*epsilon(1.0_dp)*real(n, kind=dp)*scale
320 IF (minval(solution(1:k)) >= -feasibility_tolerance) THEN
321 candidate = 0.0_dp
322 DO i = 1, k
323 candidate(active(i)) = max(solution(i), 0.0_dp)
324 END DO
325 IF (sum(candidate) > 0.0_dp) THEN
326 candidate = candidate/sum(candidate)
327 value = quadratic_value(hessian, linear, candidate)
328 CALL consider_candidate(candidate, value, preferred, coeff, best_value)
329 END IF
330 END IF
331 END IF
332
333 DEALLOCATE (kkt, rhs, solution)
334 END DO
335
336 END SUBROUTINE enumerate_active_faces
337
338! **************************************************************************************************
339!> \brief Simplex-preserving pairwise coordinate descent for larger histories.
340!> \param hessian Symmetric quadratic-model Hessian.
341!> \param linear Quadratic-model linear term.
342!> \param preferred History index favored when objective values are tied.
343!> \param coeff Best multistart coefficients found.
344!> \param best_value Objective value corresponding to coeff.
345!> \param status Local-convergence or iteration-limit status.
346! **************************************************************************************************
347 SUBROUTINE pairwise_minimize(hessian, linear, preferred, coeff, best_value, status)
348
349 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: hessian
350 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: linear
351 INTEGER, INTENT(IN) :: preferred
352 REAL(kind=dp), DIMENSION(:), INTENT(INOUT) :: coeff
353 REAL(kind=dp), INTENT(INOUT) :: best_value
354 INTEGER, INTENT(OUT) :: status
355
356 INTEGER :: n, start, trial_status
357 LOGICAL :: select_trial
358 REAL(kind=dp) :: trial_value
359 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: trial
360
361 n = SIZE(linear)
362 ALLOCATE (trial(n))
363
364 ! Starting from every vertex avoids simple multi-coordinate traps while retaining
365 ! deterministic newest-first tie breaking. A uniform start also covers broad interiors.
366 best_value = huge(0.0_dp)
368 DO start = n, 1, -1
369 trial = 0.0_dp
370 trial(start) = 1.0_dp
371 trial_value = quadratic_value(hessian, linear, trial)
372 CALL pairwise_descent(hessian, linear, trial, trial_value, trial_status)
373
374 select_trial = strictly_better(trial_value, best_value)
375 IF (.NOT. select_trial .AND. numerically_equal(trial_value, best_value)) THEN
376 select_trial = prefer_newer(trial, coeff, preferred)
377 END IF
378 IF (select_trial) THEN
379 coeff = trial
380 best_value = trial_value
381 IF (trial_status == simplex_qp_success) THEN
383 ELSE
384 status = trial_status
385 END IF
386 END IF
387 END DO
388
389 trial = 1.0_dp/real(n, kind=dp)
390 trial_value = quadratic_value(hessian, linear, trial)
391 CALL pairwise_descent(hessian, linear, trial, trial_value, trial_status)
392 IF (strictly_better(trial_value, best_value)) THEN
393 coeff = trial
394 best_value = trial_value
395 IF (trial_status == simplex_qp_success) THEN
397 ELSE
398 status = trial_status
399 END IF
400 END IF
401
402 END SUBROUTINE pairwise_minimize
403
404! **************************************************************************************************
405!> \brief Run pairwise coordinate descent from one legal simplex point.
406!> \param hessian Symmetric quadratic-model Hessian.
407!> \param linear Quadratic-model linear term.
408!> \param coeff Initial and final legal simplex coefficients.
409!> \param value Initial and final objective value.
410!> \param status Convergence or iteration-limit status.
411! **************************************************************************************************
412 SUBROUTINE pairwise_descent(hessian, linear, coeff, value, status)
413
414 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: hessian
415 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: linear
416 REAL(kind=dp), DIMENSION(:), INTENT(INOUT) :: coeff
417 REAL(kind=dp), INTENT(INOUT) :: value
418 INTEGER, INTENT(OUT) :: status
419
420 INTEGER :: best_from, best_to, from, iter, &
421 max_iter, n, to
422 REAL(kind=dp) :: alpha, alpha_max, best_alpha, &
423 best_delta, curvature, delta, &
424 derivative, objective_tolerance
425 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: gradient
426
427 n = SIZE(linear)
428 max_iter = max(500, 50*n*n)
429 ALLOCATE (gradient(n))
430
432 DO iter = 1, max_iter
433 gradient(:) = matmul(hessian, coeff) + linear
434 best_delta = 0.0_dp
435 best_alpha = 0.0_dp
436 best_from = 0
437 best_to = 0
438
439 DO from = 1, n
440 alpha_max = coeff(from)
441 IF (alpha_max <= 64.0_dp*epsilon(1.0_dp)) cycle
442 DO to = 1, n
443 IF (to == from) cycle
444
445 derivative = gradient(to) - gradient(from)
446 curvature = hessian(to, to) + hessian(from, from) - 2.0_dp*hessian(to, from)
447
448 IF (curvature > 0.0_dp) THEN
449 ! Compare before dividing so a tiny positive curvature cannot overflow
450 ! -derivative/curvature under CP2K's Debug FPE traps.
451 IF (derivative >= 0.0_dp) THEN
452 alpha = 0.0_dp
453 ELSE IF (-derivative >= alpha_max*curvature) THEN
454 alpha = alpha_max
455 ELSE
456 alpha = -derivative/curvature
457 END IF
458 ELSE
459 alpha = alpha_max
460 END IF
461
462 delta = alpha*derivative + 0.5_dp*alpha*alpha*curvature
463 IF (delta < best_delta) THEN
464 best_delta = delta
465 best_alpha = alpha
466 best_from = from
467 best_to = to
468 END IF
469 END DO
470 END DO
471
472 objective_tolerance = 16.0_dp*epsilon(1.0_dp)*max(1.0_dp, abs(value))
473 IF (best_from == 0 .OR. best_delta >= -objective_tolerance) THEN
474 status = simplex_qp_success
475 EXIT
476 END IF
477
478 coeff(best_from) = coeff(best_from) - best_alpha
479 coeff(best_to) = coeff(best_to) + best_alpha
480 coeff = max(coeff, 0.0_dp)
481 coeff = coeff/sum(coeff)
482 value = quadratic_value(hessian, linear, coeff)
483 END DO
484
485 END SUBROUTINE pairwise_descent
486
487! **************************************************************************************************
488!> \brief Dense Gaussian elimination with partial pivoting for tiny KKT systems.
489!> \param matrix Square coefficient matrix.
490!> \param rhs Right-hand side vector.
491!> \param solution Computed solution, or zeros when the system is rejected.
492!> \param solved Whether a finite nonsingular solution was obtained.
493! **************************************************************************************************
494 SUBROUTINE solve_dense_linear(matrix, rhs, solution, solved)
495
496 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: matrix
497 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: rhs
498 REAL(kind=dp), DIMENSION(:), INTENT(OUT) :: solution
499 LOGICAL, INTENT(OUT) :: solved
500
501 INTEGER :: i, k, n, pivot
502 REAL(kind=dp) :: factor, pivot_tolerance, scale, temp
503 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: work_rhs, work_row
504 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: work_matrix
505
506 n = SIZE(rhs)
507 solved = .false.
508 solution = 0.0_dp
509 IF (SIZE(matrix, 1) /= n .OR. SIZE(matrix, 2) /= n .OR. SIZE(solution) /= n) RETURN
510
511 ALLOCATE (work_matrix(n, n), work_rhs(n), work_row(n))
512 work_matrix(:, :) = matrix
513 work_rhs(:) = rhs
514 scale = max(1.0_dp, maxval(abs(work_matrix)))
515 pivot_tolerance = 1024.0_dp*epsilon(1.0_dp)*real(n, kind=dp)*scale
516
517 DO k = 1, n
518 pivot = k - 1 + maxloc(abs(work_matrix(k:n, k)), dim=1)
519 IF (abs(work_matrix(pivot, k)) <= pivot_tolerance) RETURN
520
521 IF (pivot /= k) THEN
522 work_row(:) = work_matrix(k, :)
523 work_matrix(k, :) = work_matrix(pivot, :)
524 work_matrix(pivot, :) = work_row
525 temp = work_rhs(k)
526 work_rhs(k) = work_rhs(pivot)
527 work_rhs(pivot) = temp
528 END IF
529
530 DO i = k + 1, n
531 factor = work_matrix(i, k)/work_matrix(k, k)
532 work_matrix(i, k) = 0.0_dp
533 work_matrix(i, k + 1:n) = work_matrix(i, k + 1:n) - &
534 factor*work_matrix(k, k + 1:n)
535 work_rhs(i) = work_rhs(i) - factor*work_rhs(k)
536 END DO
537 END DO
538
539 DO i = n, 1, -1
540 solution(i) = (work_rhs(i) - dot_product(work_matrix(i, i + 1:n), solution(i + 1:n)))/ &
541 work_matrix(i, i)
542 END DO
543
544 solved = all(ieee_is_finite(solution))
545
546 END SUBROUTINE solve_dense_linear
547
548! **************************************************************************************************
549!> \brief Compare and, when appropriate, retain a finite simplex candidate.
550!> \param candidate Feasible simplex candidate.
551!> \param value Objective value at candidate.
552!> \param preferred History index favored when objective values are tied.
553!> \param coeff Best coefficients found so far.
554!> \param best_value Objective value corresponding to coeff.
555! **************************************************************************************************
556 SUBROUTINE consider_candidate(candidate, value, preferred, coeff, best_value)
557
558 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: candidate
559 REAL(kind=dp), INTENT(IN) :: value
560 INTEGER, INTENT(IN) :: preferred
561 REAL(kind=dp), DIMENSION(:), INTENT(INOUT) :: coeff
562 REAL(kind=dp), INTENT(INOUT) :: best_value
563
564 IF (.NOT. ieee_is_finite(value)) RETURN
565 IF (strictly_better(value, best_value)) THEN
566 coeff = candidate
567 best_value = value
568 ELSE IF (numerically_equal(value, best_value) .AND. prefer_newer(candidate, coeff, preferred)) THEN
569 coeff = candidate
570 best_value = value
571 END IF
572
573 END SUBROUTINE consider_candidate
574
575! **************************************************************************************************
576!> \brief Evaluate the canonical quadratic objective.
577!> \param hessian Symmetric quadratic-model Hessian.
578!> \param linear Quadratic-model linear term.
579!> \param coeff Simplex coefficient vector.
580!> \return Canonical quadratic objective value.
581! **************************************************************************************************
582 PURE FUNCTION quadratic_value(hessian, linear, coeff) RESULT(value)
583
584 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: hessian
585 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: linear, coeff
586 REAL(kind=dp) :: value
587
588 value = 0.5_dp*dot_product(coeff, matmul(hessian, coeff)) + dot_product(linear, coeff)
589
590 END FUNCTION quadratic_value
591
592! **************************************************************************************************
593!> \brief Scale-aware strict comparison used for deterministic candidate selection.
594!> \param value Candidate objective value.
595!> \param reference Current best objective value.
596!> \return Whether value improves reference beyond round-off tolerance.
597! **************************************************************************************************
598 PURE FUNCTION strictly_better(value, reference) RESULT(better)
599
600 REAL(kind=dp), INTENT(IN) :: value, reference
601 LOGICAL :: better
602
603 better = value < reference - 512.0_dp*epsilon(1.0_dp)* &
604 max(1.0_dp, abs(value), abs(reference))
605
606 END FUNCTION strictly_better
607
608! **************************************************************************************************
609!> \brief Scale-aware equality comparison.
610!> \param value Candidate objective value.
611!> \param reference Current best objective value.
612!> \return Whether both values agree within round-off tolerance.
613! **************************************************************************************************
614 PURE FUNCTION numerically_equal(value, reference) RESULT(equal)
615
616 REAL(kind=dp), INTENT(IN) :: value, reference
617 LOGICAL :: equal
618
619 equal = abs(value - reference) <= 512.0_dp*epsilon(1.0_dp)* &
620 max(1.0_dp, abs(value), abs(reference))
621
622 END FUNCTION numerically_equal
623
624! **************************************************************************************************
625!> \brief Deterministic tie break that first favors the caller's preferred history entry.
626!> \param candidate Candidate coefficient vector.
627!> \param reference Current coefficient vector.
628!> \param preferred History index favored first.
629!> \return Whether candidate wins the deterministic tie break.
630! **************************************************************************************************
631 PURE FUNCTION prefer_newer(candidate, reference, preferred) RESULT(prefer)
632
633 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: candidate, reference
634 INTEGER, INTENT(IN) :: preferred
635 LOGICAL :: prefer
636
637 INTEGER :: i
638 REAL(kind=dp) :: tolerance
639
640 prefer = .false.
641 tolerance = 512.0_dp*epsilon(1.0_dp)
642
643 IF (candidate(preferred) > reference(preferred) + tolerance) THEN
644 prefer = .true.
645 RETURN
646 ELSE IF (candidate(preferred) < reference(preferred) - tolerance) THEN
647 RETURN
648 END IF
649
650 DO i = SIZE(candidate), 1, -1
651 IF (i == preferred) cycle
652 IF (candidate(i) > reference(i) + tolerance) THEN
653 prefer = .true.
654 RETURN
655 ELSE IF (candidate(i) < reference(i) - tolerance) THEN
656 RETURN
657 END IF
658 END DO
659
660 END FUNCTION prefer_newer
661
662END MODULE qs_scf_subspace_math
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Small, matrix-free mathematical kernels used by ADIIS.
integer, parameter, public simplex_qp_success
subroutine, public qs_scf_subspace_build_adiis_model(pf_metric, newest, hessian, linear, valid)
Construct the canonical ADIIS model relative to the newest history entry.
integer, parameter, public simplex_qp_nonfinite_input
integer, parameter, public simplex_qp_iteration_limit
subroutine, public simplex_quadratic_minimize(hessian, linear, coeff, objective, status, preferred_index)
Minimize a quadratic model over the probability simplex.
pure integer function, public qs_scf_subspace_fifo_slot(generation)
Select the next physical slot for a strict FIFO history.
integer, parameter, public simplex_qp_pairwise_stationary
integer, parameter, public simplex_qp_invalid_shape