(git:5e7fe52)
Loading...
Searching...
No Matches
low_rank_preconditioner_unittest.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
9 USE kinds, ONLY: dp
13 USE mathconstants, ONLY: sqrthalf
14
15 IMPLICIT NONE
16
17 INTEGER, PARAMETER :: nao = 5, nocc = 3, rank = 2
18 REAL(kind=dp), PARAMETER :: tolerance = 2.0e-14_dp
19 INTEGER :: i, selected_rank
20 REAL(kind=dp) :: angle, common_shift, energy_gap, mu, window
21 REAL(kind=dp), DIMENSION(7) :: full_eigenvalues
22 REAL(kind=dp), DIMENSION(rank) :: eigenvalues
23 REAL(kind=dp), DIMENSION(nao, nao) :: complement_operator, &
24 complement_operator_rotated, hamiltonian, &
25 identity, overlap_inverse, projector, &
26 projector_rotated
27 REAL(kind=dp), DIMENSION(nao, 2) :: occupied, occupied_rotated, vectors
28 REAL(kind=dp), DIMENSION(nao, nocc) :: gradient, gradient_rotated, result, &
29 result_rotated
30 REAL(kind=dp), DIMENSION(nocc, nocc) :: rotation
31 REAL(kind=dp), DIMENSION(2, 2) :: occupied_rotation
32
33 identity = 0.0_dp
34 DO i = 1, nao
35 identity(i, i) = 1.0_dp
36 END DO
37
38 overlap_inverse = identity
39 overlap_inverse(1, 1) = 1.4_dp
40 overlap_inverse(2, 2) = 0.8_dp
41 overlap_inverse(1, 2) = 0.1_dp
42 overlap_inverse(2, 1) = 0.1_dp
43
44 vectors = 0.0_dp
45 vectors(3, 1) = 1.0_dp
46 vectors(4, 2) = 0.8_dp
47 vectors(5, 2) = 0.6_dp
48 eigenvalues = [0.35_dp, 0.8_dp]
49 mu = -0.2_dp
50 energy_gap = 0.08_dp
51 window = 1.0_dp
52
53 gradient = reshape([0.2_dp, -0.1_dp, 0.4_dp, 0.3_dp, -0.2_dp, &
54 0.6_dp, 0.5_dp, -0.3_dp, 0.1_dp, 0.7_dp, &
55 -0.4_dp, 0.8_dp, 0.2_dp, -0.5_dp, 0.9_dp], [nao, nocc])
56 angle = 0.37_dp
57 rotation = 0.0_dp
58 rotation(1, 1) = cos(angle)
59 rotation(1, 2) = -sin(angle)
60 rotation(2, 1) = sin(angle)
61 rotation(2, 2) = cos(angle)
62 rotation(3, 3) = 1.0_dp
63 gradient_rotated = matmul(gradient, rotation)
64
65 CALL apply_low_rank_dense(overlap_inverse, vectors, eigenvalues, mu, energy_gap, window, &
66 gradient, result)
67 CALL apply_low_rank_dense(overlap_inverse, vectors, eigenvalues, mu, energy_gap, window, &
68 gradient_rotated, result_rotated)
69 IF (maxval(abs(result_rotated - matmul(result, rotation))) > tolerance) THEN
70 error stop "Low-rank preconditioner is not right-covariant under occupied rotations"
71 END IF
72
73 ! Verify that the projected complementary operator depends only on the occupied subspace.
74 occupied = 0.0_dp
75 occupied(1, 1) = sqrthalf
76 occupied(2, 1) = sqrthalf
77 occupied(3, 2) = sqrthalf
78 occupied(4, 2) = sqrthalf
79 occupied_rotation = reshape([cos(angle), sin(angle), -sin(angle), cos(angle)], [2, 2])
80 occupied_rotated = matmul(occupied, occupied_rotation)
81
82 hamiltonian = reshape([1.0_dp, 0.2_dp, 0.1_dp, 0.0_dp, 0.3_dp, &
83 0.2_dp, 1.4_dp, 0.0_dp, 0.2_dp, 0.1_dp, &
84 0.1_dp, 0.0_dp, 2.0_dp, 0.4_dp, 0.2_dp, &
85 0.0_dp, 0.2_dp, 0.4_dp, 2.3_dp, 0.1_dp, &
86 0.3_dp, 0.1_dp, 0.2_dp, 0.1_dp, 3.0_dp], [nao, nao])
87 projector = identity - matmul(occupied, transpose(occupied))
88 projector_rotated = identity - matmul(occupied_rotated, transpose(occupied_rotated))
89 common_shift = -10.2_dp
90 complement_operator = matmul(transpose(projector), matmul(hamiltonian, projector)) + &
91 common_shift*matmul(occupied, transpose(occupied))
92 complement_operator_rotated = &
93 matmul(transpose(projector_rotated), matmul(hamiltonian, projector_rotated)) + &
94 common_shift*matmul(occupied_rotated, transpose(occupied_rotated))
95 IF (maxval(abs(complement_operator_rotated - complement_operator)) > tolerance) THEN
96 error stop "Complementary-state construction depends on the occupied orbital gauge"
97 END IF
98
99 full_eigenvalues = [-0.8_dp, -0.2_dp, 0.2_dp, 0.4_dp, 0.7_dp, 0.7_dp, 1.4_dp]
100 selected_rank = low_rank_select_rank(full_eigenvalues, 2, 3, 1.0e-8_dp)
101 IF (selected_rank /= 2) error stop "Rank cap split a degenerate complementary manifold"
102 selected_rank = low_rank_select_rank(full_eigenvalues, 2, 5, 1.0e-8_dp)
103 IF (selected_rank /= 5) error stop "Full complementary space did not retain its rank"
104 IF (abs(low_rank_inverse_weight(1.5_dp, 0.0_dp, 0.08_dp) - 2.0_dp/3.0_dp) > tolerance) THEN
105 error stop "Common-reference inverse weight differs from the spectral model"
106 END IF
107
program low_rank_preconditioner_unittest
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Small algebraic helpers for the rotationally covariant low-rank OT preconditioner.
pure elemental real(kind=dp) function, public low_rank_inverse_weight(eigenvalue, reference, energy_gap)
Spectral inverse weight relative to a common occupied reference level.
pure subroutine, public apply_low_rank_dense(overlap_inverse, vectors, eigenvalues, reference, energy_gap, spectral_window, matrix_in, matrix_out)
Dense reference application used to verify right covariance under orbital rotations.
pure integer function, public low_rank_select_rank(eigenvalues, nocc, max_rank, degeneracy_tolerance)
Select a bounded spectral rank without cutting a degenerate boundary manifold.
Definition of mathematical constants and functions.
real(kind=dp), parameter, public sqrthalf