20 USE ieee_arithmetic,
ONLY: ieee_is_finite
33 INTEGER,
PARAMETER :: max_exact_dimension = 12
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
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
77 best_value = huge(0.0_dp)
80 IF (
PRESENT(preferred_index))
THEN
81 IF (preferred_index >= 1 .AND. preferred_index <= n)
THEN
82 preferred = preferred_index
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
91 IF (preferred >= 1 .AND. preferred <=
SIZE(coeff)) coeff(preferred) = 1.0_dp
93 IF (n < 1 .OR.
SIZE(coeff) /= n .OR.
SIZE(hessian, 1) /= n .OR.
SIZE(hessian, 2) /= n)
THEN
95 IF (
PRESENT(objective)) objective = best_value
96 IF (
PRESENT(status)) status = local_status
100 IF (.NOT. all(ieee_is_finite(hessian)) .OR. .NOT. all(ieee_is_finite(linear)))
THEN
102 IF (
PRESENT(objective)) objective = best_value
103 IF (
PRESENT(status)) status = local_status
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)
112 working_hessian(:, :) = symmetric_hessian
113 working_linear(:) = linear
114 model_scale = max(maxval(abs(working_hessian)), maxval(abs(working_linear)))
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
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
131 CALL best_vertex(working_hessian, working_linear, preferred, coeff, best_value)
133 IF (n <= max_exact_dimension)
THEN
134 CALL enumerate_active_faces(working_hessian, working_linear, preferred, coeff, best_value)
136 CALL pairwise_minimize(working_hessian, working_linear, preferred, coeff, best_value, local_status)
140 coeff = max(coeff, 0.0_dp)
141 coeff = coeff/sum(coeff)
142 best_value = quadratic_value(symmetric_hessian, linear, coeff)
144 IF (
PRESENT(objective)) objective = best_value
145 IF (
PRESENT(status)) status = local_status
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
166 LOGICAL :: local_valid
167 REAL(kind=
dp) :: model_ij, model_ji, tnn
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.
182 tnn = pf_metric(newest, newest)
184 linear(i) = 2.0_dp*(pf_metric(i, newest) - tnn)
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
197 IF (
PRESENT(valid)) valid = .true.
208 INTEGER,
DIMENSION(:),
INTENT(IN) :: generation
214 IF (
SIZE(generation) < 1)
RETURN
215 IF (any(generation < 0))
RETURN
217 DO i = 1,
SIZE(generation)
218 IF (generation(i) == 0)
THEN
223 slot = minloc(generation, dim=1)
235 SUBROUTINE best_vertex(hessian, linear, preferred, coeff, best_value)
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
244 REAL(kind=
dp) ::
value
248 coeff(preferred) = 1.0_dp
249 best_value = 0.5_dp*hessian(preferred, preferred) + linear(preferred)
252 IF (i == preferred) cycle
253 value = 0.5_dp*hessian(i, i) + linear(i)
254 IF (strictly_better(
value, best_value))
THEN
261 END SUBROUTINE best_vertex
271 SUBROUTINE enumerate_active_faces(hessian, linear, preferred, coeff, best_value)
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
279 INTEGER :: i, j, k, last_mask, mask, n
280 INTEGER,
ALLOCATABLE,
DIMENSION(:) :: active
282 REAL(kind=
dp) :: feasibility_tolerance, scale,
value
283 REAL(kind=
dp),
ALLOCATABLE,
DIMENSION(:) :: candidate, rhs, solution
284 REAL(kind=
dp),
ALLOCATABLE,
DIMENSION(:, :) :: kkt
287 last_mask = ishft(1, n) - 1
288 ALLOCATE (active(n), candidate(n))
290 DO mask = 1, last_mask
296 IF (btest(mask, i - 1))
THEN
302 ALLOCATE (kkt(k + 1, k + 1), rhs(k + 1), solution(k + 1))
307 rhs(j) = -linear(active(j))
308 kkt(j, k + 1) = 1.0_dp
309 kkt(k + 1, j) = 1.0_dp
311 kkt(i, j) = hessian(active(i), active(j))
316 CALL solve_dense_linear(kkt, rhs, solution, solved)
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
323 candidate(active(i)) = max(solution(i), 0.0_dp)
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)
333 DEALLOCATE (kkt, rhs, solution)
336 END SUBROUTINE enumerate_active_faces
347 SUBROUTINE pairwise_minimize(hessian, linear, preferred, coeff, best_value, status)
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
356 INTEGER :: n, start, trial_status
357 LOGICAL :: select_trial
358 REAL(kind=
dp) :: trial_value
359 REAL(kind=
dp),
ALLOCATABLE,
DIMENSION(:) :: trial
366 best_value = huge(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)
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)
378 IF (select_trial)
THEN
380 best_value = trial_value
384 status = trial_status
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
394 best_value = trial_value
398 status = trial_status
402 END SUBROUTINE pairwise_minimize
412 SUBROUTINE pairwise_descent(hessian, linear, coeff, value, status)
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
420 INTEGER :: best_from, best_to, from, iter, &
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
428 max_iter = max(500, 50*n*n)
429 ALLOCATE (gradient(n))
432 DO iter = 1, max_iter
433 gradient(:) = matmul(hessian, coeff) + linear
440 alpha_max = coeff(from)
441 IF (alpha_max <= 64.0_dp*epsilon(1.0_dp)) cycle
443 IF (to == from) cycle
445 derivative = gradient(to) - gradient(from)
446 curvature = hessian(to, to) + hessian(from, from) - 2.0_dp*hessian(to, from)
448 IF (curvature > 0.0_dp)
THEN
451 IF (derivative >= 0.0_dp)
THEN
453 ELSE IF (-derivative >= alpha_max*curvature)
THEN
456 alpha = -derivative/curvature
462 delta = alpha*derivative + 0.5_dp*alpha*alpha*curvature
463 IF (delta < best_delta)
THEN
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
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)
485 END SUBROUTINE pairwise_descent
494 SUBROUTINE solve_dense_linear(matrix, rhs, solution, solved)
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
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
509 IF (
SIZE(matrix, 1) /= n .OR.
SIZE(matrix, 2) /= n .OR.
SIZE(solution) /= n)
RETURN
511 ALLOCATE (work_matrix(n, n), work_rhs(n), work_row(n))
512 work_matrix(:, :) = matrix
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
518 pivot = k - 1 + maxloc(abs(work_matrix(k:n, k)), dim=1)
519 IF (abs(work_matrix(pivot, k)) <= pivot_tolerance)
RETURN
522 work_row(:) = work_matrix(k, :)
523 work_matrix(k, :) = work_matrix(pivot, :)
524 work_matrix(pivot, :) = work_row
526 work_rhs(k) = work_rhs(pivot)
527 work_rhs(pivot) = temp
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)
540 solution(i) = (work_rhs(i) - dot_product(work_matrix(i, i + 1:n), solution(i + 1:n)))/ &
544 solved = all(ieee_is_finite(solution))
546 END SUBROUTINE solve_dense_linear
556 SUBROUTINE consider_candidate(candidate, value, preferred, coeff, best_value)
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
564 IF (.NOT. ieee_is_finite(
value))
RETURN
565 IF (strictly_better(
value, best_value))
THEN
568 ELSE IF (numerically_equal(
value, best_value) .AND. prefer_newer(candidate, coeff, preferred))
THEN
573 END SUBROUTINE consider_candidate
582 PURE FUNCTION quadratic_value(hessian, linear, coeff)
RESULT(value)
584 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(IN) :: hessian
585 REAL(kind=
dp),
DIMENSION(:),
INTENT(IN) :: linear, coeff
586 REAL(kind=
dp) ::
value
588 value = 0.5_dp*dot_product(coeff, matmul(hessian, coeff)) + dot_product(linear, coeff)
590 END FUNCTION quadratic_value
598 PURE FUNCTION strictly_better(value, reference)
RESULT(better)
600 REAL(kind=
dp),
INTENT(IN) ::
value, reference
603 better =
value < reference - 512.0_dp*epsilon(1.0_dp)* &
604 max(1.0_dp, abs(
value), abs(reference))
606 END FUNCTION strictly_better
614 PURE FUNCTION numerically_equal(value, reference)
RESULT(equal)
616 REAL(kind=
dp),
INTENT(IN) ::
value, reference
619 equal = abs(
value - reference) <= 512.0_dp*epsilon(1.0_dp)* &
620 max(1.0_dp, abs(
value), abs(reference))
622 END FUNCTION numerically_equal
631 PURE FUNCTION prefer_newer(candidate, reference, preferred)
RESULT(prefer)
633 REAL(kind=
dp),
DIMENSION(:),
INTENT(IN) :: candidate, reference
634 INTEGER,
INTENT(IN) :: preferred
638 REAL(kind=
dp) :: tolerance
641 tolerance = 512.0_dp*epsilon(1.0_dp)
643 IF (candidate(preferred) > reference(preferred) + tolerance)
THEN
646 ELSE IF (candidate(preferred) < reference(preferred) - tolerance)
THEN
650 DO i =
SIZE(candidate), 1, -1
651 IF (i == preferred) cycle
652 IF (candidate(i) > reference(i) + tolerance)
THEN
655 ELSE IF (candidate(i) < reference(i) - tolerance)
THEN
660 END FUNCTION prefer_newer
Defines the basic variable types.
integer, parameter, public dp
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