(git:5e7fe52)
Loading...
Searching...
No Matches
ot_covariant_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!--------------------------------------------------------------------------------------------------!
14 USE cp_fm_types, ONLY: cp_fm_create,&
20 USE kinds, ONLY: dp
28
29 IMPLICIT NONE
30
31 INTEGER, PARAMETER :: k = 3, n = 5
32 REAL(kind=dp), PARAMETER :: eps = 2.0e-11_dp
33
34 INTEGER :: i, j
35 REAL(kind=dp), DIMENSION(n, k) :: gradient, gradient_rotated, hessian_x, &
36 output, output_reference, &
37 output_rotated, x_reference
38 REAL(kind=dp), DIMENSION(n) :: full_evals
39 REAL(kind=dp), DIMENSION(k) :: occ_evals
40 REAL(kind=dp), DIMENSION(k, k) :: occupied_h, q, rotation, rotation_2, &
41 rotation_gauge
42 TYPE(cp_blacs_env_type), POINTER :: blacs_env
43 TYPE(cp_fm_struct_type), POINTER :: fm_struct
44 TYPE(cp_fm_type) :: matrix_in, matrix_out
45 TYPE(mp_para_env_type), POINTER :: para_env
46 TYPE(preconditioner_type) :: preconditioner_env
47
48 NULLIFY (blacs_env, fm_struct, para_env)
49 ALLOCATE (para_env)
50 CALL mp_world_init(para_env)
51 CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=para_env)
52 CALL init_preconditioner(preconditioner_env, para_env, blacs_env)
53
54 CALL cp_fm_struct_create(fm_struct, nrow_global=n, ncol_global=k, &
55 context=blacs_env, para_env=para_env)
56 CALL cp_fm_create(matrix_in, fm_struct, name="covariant test input")
57 CALL cp_fm_create(matrix_out, fm_struct, name="covariant test output")
58 CALL cp_fm_struct_release(fm_struct)
59
60 CALL cp_fm_struct_create(fm_struct, nrow_global=n, ncol_global=n, &
61 context=blacs_env, para_env=para_env)
62 ALLOCATE (preconditioner_env%fm)
63 CALL cp_fm_create(preconditioner_env%fm, fm_struct, name="spectral eigenvectors")
64 CALL cp_fm_struct_release(fm_struct)
65
66 CALL cp_fm_struct_create(fm_struct, nrow_global=k, ncol_global=k, &
67 context=blacs_env, para_env=para_env)
68 ALLOCATE (preconditioner_env%occ_rotation)
69 CALL cp_fm_create(preconditioner_env%occ_rotation, fm_struct, name="occupied rotation")
70 CALL cp_fm_struct_release(fm_struct)
71
72 preconditioner_env%in_use = ot_precond_full_all_covariant
73 preconditioner_env%energy_gap = 0.10_dp
74 ALLOCATE (preconditioner_env%full_evals(n), preconditioner_env%occ_evals(k))
75 full_evals = [0.8_dp, 1.3_dp, 2.1_dp, 3.0_dp, 4.4_dp]
76 occ_evals = [-0.9_dp, -0.2_dp, 0.35_dp]
77 preconditioner_env%full_evals = full_evals
78 preconditioner_env%occ_evals = occ_evals
79
80 CALL set_identity(preconditioner_env%fm, n)
81 CALL make_rotation(0.43_dp, -0.31_dp, rotation)
82 CALL cp_fm_set_submatrix(preconditioner_env%occ_rotation, rotation)
83
84 DO j = 1, k
85 DO i = 1, n
86 gradient(i, j) = sin(0.37_dp*real(2*i + j, dp)) + &
87 0.2_dp*cos(0.19_dp*real(i - 3*j, dp))
88 x_reference(i, j) = cos(0.23_dp*real(i + 2*j, dp)) - &
89 0.1_dp*sin(0.41_dp*real(3*i - j, dp))
90 END DO
91 END DO
92
93 ! Check the production FM wrapper against its explicit spectral formula.
94 CALL apply_model(gradient, rotation, full_evals, occ_evals, &
95 preconditioner_env%energy_gap, output_reference)
96 CALL apply_fm(preconditioner_env, matrix_in, matrix_out, gradient, output)
97 CALL assert_close(output, output_reference, eps, "Explicit covariant spectral inverse")
98
99 ! Invert the frozen-H Sylvester operator A X - X B exactly.
100 occupied_h = 0.0_dp
101 DO j = 1, k
102 occupied_h = occupied_h + occ_evals(j)*outer_product(rotation(:, j), rotation(:, j))
103 END DO
104 hessian_x = -matmul(x_reference, occupied_h)
105 DO i = 1, n
106 hessian_x(i, :) = hessian_x(i, :) + full_evals(i)*x_reference(i, :)
107 END DO
108 CALL apply_fm(preconditioner_env, matrix_in, matrix_out, hessian_x, output)
109 CALL assert_close(output, x_reference, eps, "Frozen-H Sylvester inverse")
110
111 ! Rotate the occupied gauge and verify P_(C R)(G R) = P_C(G) R.
112 CALL make_rotation(-0.37_dp, 0.28_dp, rotation_gauge)
113 rotation_2 = matmul(transpose(rotation_gauge), rotation)
114 gradient_rotated = matmul(gradient, rotation_gauge)
115 CALL cp_fm_set_submatrix(preconditioner_env%occ_rotation, rotation_2)
116 CALL apply_fm(preconditioner_env, matrix_in, matrix_out, gradient_rotated, output_rotated)
117 CALL assert_close(output_rotated, matmul(output_reference, rotation_gauge), eps, &
118 "Occupied-gauge covariance")
119
120 ! Eigenvectors may rotate freely inside a degenerate occupied eigenspace.
121 occ_evals = [-0.4_dp, -0.4_dp, 0.2_dp]
122 preconditioner_env%occ_evals = occ_evals
123 CALL cp_fm_set_submatrix(preconditioner_env%occ_rotation, rotation)
124 CALL apply_fm(preconditioner_env, matrix_in, matrix_out, gradient, output)
125 CALL plane_rotation(0.61_dp, 1, 2, q)
126 rotation_2 = matmul(rotation, q)
127 CALL cp_fm_set_submatrix(preconditioner_env%occ_rotation, rotation_2)
128 CALL apply_fm(preconditioner_env, matrix_in, matrix_out, gradient, output_rotated)
129 CALL assert_close(output_rotated, output, eps, "Degenerate occupied subspace")
130
131 ! The gap floor must leave the inverse positive definite.
132 preconditioner_env%energy_gap = 0.75_dp
133 preconditioner_env%occ_evals = [0.7_dp, 0.9_dp, 1.1_dp]
134 CALL cp_fm_set_submatrix(preconditioner_env%occ_rotation, rotation)
135 CALL apply_fm(preconditioner_env, matrix_in, matrix_out, gradient, output)
136 IF (sum(gradient*output) <= 0.0_dp) THEN
137 error stop "Gap-floor inverse is not positive definite"
138 END IF
139
140 CALL cp_fm_release(matrix_in)
141 CALL cp_fm_release(matrix_out)
142 CALL destroy_preconditioner(preconditioner_env)
143 CALL cp_blacs_env_release(blacs_env)
144 CALL mp_world_finalize()
145 DEALLOCATE (para_env)
146
147CONTAINS
148
149! **************************************************************************************************
150!> \brief Apply the production FM path to a replicated test matrix.
151!> \param preconditioner_env ...
152!> \param matrix_in ...
153!> \param matrix_out ...
154!> \param input ...
155!> \param RESULT ...
156! **************************************************************************************************
157 SUBROUTINE apply_fm(preconditioner_env, matrix_in, matrix_out, input, RESULT)
158
159 TYPE(preconditioner_type) :: preconditioner_env
160 TYPE(cp_fm_type), INTENT(IN) :: matrix_in, matrix_out
161 REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: input
162 REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: result
163
164 CALL cp_fm_set_submatrix(matrix_in, input)
165 CALL apply_preconditioner_fm(preconditioner_env, matrix_in, matrix_out)
166 CALL cp_fm_get_submatrix(matrix_out, result)
167
168 END SUBROUTINE apply_fm
169
170! **************************************************************************************************
171!> \brief Explicit reference for the covariant state-selective spectral inverse.
172!> \param input ...
173!> \param rotation ...
174!> \param full_evals ...
175!> \param occ_evals ...
176!> \param gap ...
177!> \param RESULT ...
178! **************************************************************************************************
179 SUBROUTINE apply_model(input, rotation, full_evals, occ_evals, gap, RESULT)
180
181 REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: input, rotation
182 REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: full_evals, occ_evals
183 REAL(KIND=dp), INTENT(IN) :: gap
184 REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: result
185
186 INTEGER :: i, j
187 REAL(KIND=dp), &
188 DIMENSION(SIZE(input, 1), SIZE(input, 2)) :: canonical
189
190 canonical = matmul(input, rotation)
191 DO j = 1, SIZE(input, 2)
192 DO i = 1, SIZE(input, 1)
193 canonical(i, j) = canonical(i, j)/max(gap, full_evals(i) - occ_evals(j))
194 END DO
195 END DO
196 result = matmul(canonical, transpose(rotation))
197
198 END SUBROUTINE apply_model
199
200! **************************************************************************************************
201!> \brief Create a product of two plane rotations.
202!> \param angle_12 ...
203!> \param angle_23 ...
204!> \param rotation ...
205! **************************************************************************************************
206 SUBROUTINE make_rotation(angle_12, angle_23, rotation)
207
208 REAL(KIND=dp), INTENT(IN) :: angle_12, angle_23
209 REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: rotation
210
211 REAL(KIND=dp), &
212 DIMENSION(SIZE(rotation, 1), SIZE(rotation, 2)) :: r12, r23
213
214 CALL plane_rotation(angle_12, 1, 2, r12)
215 CALL plane_rotation(angle_23, 2, 3, r23)
216 rotation = matmul(r12, r23)
217
218 END SUBROUTINE make_rotation
219
220! **************************************************************************************************
221!> \brief Create an orthogonal plane rotation.
222!> \param angle ...
223!> \param axis_1 ...
224!> \param axis_2 ...
225!> \param rotation ...
226! **************************************************************************************************
227 SUBROUTINE plane_rotation(angle, axis_1, axis_2, rotation)
228
229 REAL(KIND=dp), INTENT(IN) :: angle
230 INTEGER, INTENT(IN) :: axis_1, axis_2
231 REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: rotation
232
233 INTEGER :: i
234
235 rotation = 0.0_dp
236 DO i = 1, SIZE(rotation, 1)
237 rotation(i, i) = 1.0_dp
238 END DO
239 rotation(axis_1, axis_1) = cos(angle)
240 rotation(axis_2, axis_2) = cos(angle)
241 rotation(axis_1, axis_2) = -sin(angle)
242 rotation(axis_2, axis_1) = sin(angle)
243
244 END SUBROUTINE plane_rotation
245
246! **************************************************************************************************
247!> \brief Set a distributed full matrix to the identity.
248!> \param matrix ...
249!> \param n ...
250! **************************************************************************************************
251 SUBROUTINE set_identity(matrix, n)
252
253 TYPE(cp_fm_type), INTENT(IN) :: matrix
254 INTEGER, INTENT(IN) :: n
255
256 INTEGER :: i
257 REAL(KIND=dp), DIMENSION(n, n) :: identity
258
259 identity = 0.0_dp
260 DO i = 1, n
261 identity(i, i) = 1.0_dp
262 END DO
263 CALL cp_fm_set_submatrix(matrix, identity)
264
265 END SUBROUTINE set_identity
266
267! **************************************************************************************************
268!> \brief Form a real outer product.
269!> \param left ...
270!> \param right ...
271!> \return ...
272! **************************************************************************************************
273 PURE FUNCTION outer_product(left, right) RESULT(product)
274
275 REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: left, right
276 REAL(KIND=dp), DIMENSION(SIZE(left), SIZE(right)) :: product
277
278 INTEGER :: i, j
279
280 DO j = 1, SIZE(right)
281 DO i = 1, SIZE(left)
282 product(i, j) = left(i)*right(j)
283 END DO
284 END DO
285
286 END FUNCTION outer_product
287
288! **************************************************************************************************
289!> \brief Abort when two matrices differ beyond the requested tolerance.
290!> \param actual ...
291!> \param reference ...
292!> \param tolerance ...
293!> \param label ...
294! **************************************************************************************************
295 SUBROUTINE assert_close(actual, reference, tolerance, label)
296
297 REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: actual, reference
298 REAL(KIND=dp), INTENT(IN) :: tolerance
299 CHARACTER(LEN=*), INTENT(IN) :: label
300
301 IF (maxval(abs(actual - reference)) > tolerance) THEN
302 WRITE (*, '(A)') trim(label)
303 error stop "Matrix comparison failed"
304 END IF
305
306 END SUBROUTINE assert_close
307
methods related to the blacs parallel environment
subroutine, public cp_blacs_env_release(blacs_env)
releases the given blacs_env
subroutine, public cp_blacs_env_create(blacs_env, para_env, blacs_grid_layout, blacs_repeatable, row_major, grid_2d)
allocates and initializes a type that represent a blacs context
represent the structure of a full matrix
subroutine, public cp_fm_struct_create(fmstruct, para_env, context, nrow_global, ncol_global, nrow_block, ncol_block, descriptor, first_p_pos, local_leading_dimension, template_fmstruct, square_blocks, force_block)
allocates and initializes a full matrix structure
subroutine, public cp_fm_struct_release(fmstruct)
releases a full matrix structure
represent a full matrix distributed on many processors
Definition cp_fm_types.F:15
subroutine, public cp_fm_set_submatrix(fm, new_values, start_row, start_col, n_rows, n_cols, alpha, beta, transpose)
sets a submatrix of a full matrix fm(start_row:start_row+n_rows,start_col:start_col+n_cols) = alpha*o...
subroutine, public cp_fm_create(matrix, matrix_struct, name, nrow, ncol, set_zero)
creates a new full matrix with the given structure
subroutine, public cp_fm_get_submatrix(fm, target_m, start_row, start_col, n_rows, n_cols, transpose)
gets a submatrix of a full matrix op(target_m)(1:n_rows,1:n_cols) =fm(start_row:start_row+n_rows,...
collects all constants needed in input so that they can be used without circular dependencies
integer, parameter, public ot_precond_full_all_covariant
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Interface to the message passing library MPI.
subroutine, public mp_world_init(mp_comm)
initializes the system default communicator
subroutine, public mp_world_finalize()
Finalize the system default communicator and MPI when CP2K owns MPI.
computes preconditioners, and implements methods to apply them currently used in qs_ot
subroutine, public apply_preconditioner_fm(preconditioner_env, matrix_in, matrix_out)
applies a previously created preconditioner to a full matrix
types of preconditioners
subroutine, public init_preconditioner(preconditioner_env, para_env, blacs_env)
...
subroutine, public destroy_preconditioner(preconditioner_env)
...
subroutine apply_fm(preconditioner_env, matrix_in, matrix_out, input, result)
Apply the production FM path to a replicated test matrix.
program ot_covariant_preconditioner_unittest
represent a blacs multidimensional parallel environment (for the mpi corrispective see cp_paratypes/m...
keeps the information about the structure of a full matrix
represent a full matrix
stores all the informations relevant to an mpi environment