(git:6344619)
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,&
19 USE ieee_arithmetic, ONLY: ieee_is_finite
23 USE iso_fortran_env, ONLY: error_unit
24 USE kinds, ONLY: dp
32
33 IMPLICIT NONE
34
35 INTEGER, PARAMETER :: k = 3, n = 5
36 REAL(kind=dp), PARAMETER :: eps = 2.0e-11_dp
37
38 INTEGER :: i, j, rank_used
39 REAL(kind=dp), DIMENSION(n, k) :: gradient, gradient_rotated, hessian_x, &
40 output, output_reference, &
41 output_rotated, x_reference
42 REAL(kind=dp), DIMENSION(n) :: full_evals
43 REAL(kind=dp), DIMENSION(k) :: occ_evals
44 REAL(kind=dp), DIMENSION(k, k) :: occupied_h, q, rotation, rotation_2, &
45 rotation_gauge
46 REAL(kind=dp), DIMENSION(n, n) :: inverse, overlap_inverse, spectral_vectors
47 TYPE(cp_blacs_env_type), POINTER :: blacs_env
48 TYPE(cp_fm_struct_type), POINTER :: fm_struct
49 TYPE(cp_fm_type) :: matrix_in, matrix_out
50 TYPE(mp_para_env_type), POINTER :: para_env
51 TYPE(preconditioner_type) :: preconditioner_env
52
53 NULLIFY (blacs_env, fm_struct, para_env)
54 ALLOCATE (para_env)
55 CALL mp_world_init(para_env)
56 CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=para_env)
57 CALL init_preconditioner(preconditioner_env, para_env, blacs_env)
58
59 CALL cp_fm_struct_create(fm_struct, nrow_global=n, ncol_global=k, &
60 context=blacs_env, para_env=para_env)
61 CALL cp_fm_create(matrix_in, fm_struct, name="covariant test input")
62 CALL cp_fm_create(matrix_out, fm_struct, name="covariant test output")
63 CALL cp_fm_struct_release(fm_struct)
64
65 CALL cp_fm_struct_create(fm_struct, nrow_global=n, ncol_global=n, &
66 context=blacs_env, para_env=para_env)
67 ALLOCATE (preconditioner_env%fm)
68 CALL cp_fm_create(preconditioner_env%fm, fm_struct, name="spectral eigenvectors")
69 CALL cp_fm_struct_release(fm_struct)
70
71 CALL cp_fm_struct_create(fm_struct, nrow_global=k, ncol_global=k, &
72 context=blacs_env, para_env=para_env)
73 ALLOCATE (preconditioner_env%occ_rotation)
74 CALL cp_fm_create(preconditioner_env%occ_rotation, fm_struct, name="occupied rotation")
75 CALL cp_fm_struct_release(fm_struct)
76
77 preconditioner_env%in_use = ot_precond_full_all_covariant
78 preconditioner_env%energy_gap = 0.10_dp
79 ALLOCATE (preconditioner_env%full_evals(n), preconditioner_env%occ_evals(k))
80 full_evals = [0.8_dp, 1.3_dp, 2.1_dp, 3.0_dp, 4.4_dp]
81 occ_evals = [-0.9_dp, -0.2_dp, 0.35_dp]
82 preconditioner_env%full_evals = full_evals
83 preconditioner_env%occ_evals = occ_evals
84
85 spectral_vectors = 0.0_dp
86 DO i = 1, n
87 spectral_vectors(i, i) = 1.0_dp
88 END DO
89 CALL cp_fm_set_submatrix(preconditioner_env%fm, spectral_vectors)
90 CALL plane_rotation(0.43_dp, 1, 2, rotation)
91 CALL plane_rotation(-0.31_dp, 2, 3, q)
92 rotation = matmul(rotation, q)
93 CALL cp_fm_set_submatrix(preconditioner_env%occ_rotation, rotation)
94
95 DO j = 1, k
96 DO i = 1, n
97 gradient(i, j) = sin(0.37_dp*real(2*i + j, dp)) + &
98 0.2_dp*cos(0.19_dp*real(i - 3*j, dp))
99 x_reference(i, j) = cos(0.23_dp*real(i + 2*j, dp)) - &
100 0.1_dp*sin(0.41_dp*real(3*i - j, dp))
101 END DO
102 END DO
103
104 CALL apply_fm(preconditioner_env, matrix_in, matrix_out, gradient, output_reference)
105
106 ! Invert the frozen-H Sylvester operator A X - X B exactly.
107 occupied_h = 0.0_dp
108 DO j = 1, k
109 occupied_h = occupied_h + occ_evals(j)*outer_product(rotation(:, j), rotation(:, j))
110 END DO
111 hessian_x = -matmul(x_reference, occupied_h)
112 DO i = 1, n
113 hessian_x(i, :) = hessian_x(i, :) + full_evals(i)*x_reference(i, :)
114 END DO
115 CALL apply_fm(preconditioner_env, matrix_in, matrix_out, hessian_x, output)
116 CALL assert_close(output, x_reference, eps, "Frozen-H Sylvester inverse")
117
118 ! Rotate the occupied gauge and verify P_(C R)(G R) = P_C(G) R.
119 CALL plane_rotation(-0.37_dp, 1, 2, rotation_gauge)
120 CALL plane_rotation(0.28_dp, 2, 3, q)
121 rotation_gauge = matmul(rotation_gauge, q)
122 rotation_2 = matmul(transpose(rotation_gauge), rotation)
123 gradient_rotated = matmul(gradient, rotation_gauge)
124 CALL cp_fm_set_submatrix(preconditioner_env%occ_rotation, rotation_2)
125 CALL apply_fm(preconditioner_env, matrix_in, matrix_out, gradient_rotated, output_rotated)
126 CALL assert_close(output_rotated, matmul(output_reference, rotation_gauge), eps, &
127 "Occupied-gauge covariance")
128
129 ! The gap floor must leave the inverse positive definite.
130 preconditioner_env%energy_gap = 0.75_dp
131 preconditioner_env%occ_evals = [0.7_dp, 0.9_dp, 1.1_dp]
132 CALL cp_fm_set_submatrix(preconditioner_env%occ_rotation, rotation)
133 CALL apply_fm(preconditioner_env, matrix_in, matrix_out, gradient, output)
134 IF (sum(gradient*output) <= 0.0_dp) THEN
135 error stop "Gap-floor inverse is not positive definite"
136 END IF
137
138 ! Exercise the production low-rank FM path with a nontrivial overlap base,
139 ! including zero rank, truncation, and a gap-clipped complementary eigenvalue.
140 preconditioner_env%in_use = ot_precond_fermi_low_rank
141 preconditioner_env%low_rank_base = ot_low_rank_base_overlap
142 preconditioner_env%energy_gap = 0.1_dp
143 preconditioner_env%spectral_reference = full_evals(k + 1) - 0.04_dp
144 preconditioner_env%spectral_base_scale = 1.25_dp
145 CALL plane_rotation(0.29_dp, 2, 4, spectral_vectors)
146 spectral_vectors(:, 2) = 1.2_dp*spectral_vectors(:, 2)
147 spectral_vectors(:, 4) = 0.8_dp*spectral_vectors(:, 4)
148 overlap_inverse = matmul(spectral_vectors, transpose(spectral_vectors))
149 CALL cp_fm_set_submatrix(preconditioner_env%fm, spectral_vectors)
150 ALLOCATE (preconditioner_env%base_fm)
151 CALL cp_fm_create(preconditioner_env%base_fm, preconditioner_env%fm%matrix_struct)
152 CALL cp_fm_set_submatrix(preconditioner_env%base_fm, overlap_inverse)
153 DO rank_used = 0, n - k
154 preconditioner_env%spectral_rank = rank_used
155 inverse = preconditioner_env%spectral_base_scale*overlap_inverse
156 DO i = k + 1, k + rank_used
157 inverse = inverse + (1.0_dp/max(preconditioner_env%energy_gap, &
158 full_evals(i) - preconditioner_env%spectral_reference) - &
159 preconditioner_env%spectral_base_scale)* &
160 outer_product(spectral_vectors(:, i), spectral_vectors(:, i))
161 END DO
162 output_reference = matmul(inverse, gradient)
163 CALL apply_fm(preconditioner_env, matrix_in, matrix_out, gradient, output)
164 CALL assert_close(output, output_reference, eps, "Low-rank spectral inverse")
165 END DO
166
167 CALL cp_fm_release(matrix_in)
168 CALL cp_fm_release(matrix_out)
169 CALL destroy_preconditioner(preconditioner_env)
170 CALL cp_blacs_env_release(blacs_env)
171 CALL mp_world_finalize()
172 DEALLOCATE (para_env)
173
174CONTAINS
175
176! **************************************************************************************************
177!> \brief Apply the production FM path to a replicated test matrix.
178!> \param preconditioner_env ...
179!> \param matrix_in ...
180!> \param matrix_out ...
181!> \param input ...
182!> \param RESULT ...
183! **************************************************************************************************
184 SUBROUTINE apply_fm(preconditioner_env, matrix_in, matrix_out, input, RESULT)
185
186 TYPE(preconditioner_type) :: preconditioner_env
187 TYPE(cp_fm_type), INTENT(IN) :: matrix_in, matrix_out
188 REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: input
189 REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: result
190
191 CALL cp_fm_set_submatrix(matrix_in, input)
192 CALL apply_preconditioner_fm(preconditioner_env, matrix_in, matrix_out)
193 CALL cp_fm_get_submatrix(matrix_out, result)
194
195 END SUBROUTINE apply_fm
196
197! **************************************************************************************************
198!> \brief Create an orthogonal plane rotation.
199!> \param angle ...
200!> \param axis_1 ...
201!> \param axis_2 ...
202!> \param rotation ...
203! **************************************************************************************************
204 SUBROUTINE plane_rotation(angle, axis_1, axis_2, rotation)
205
206 REAL(KIND=dp), INTENT(IN) :: angle
207 INTEGER, INTENT(IN) :: axis_1, axis_2
208 REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: rotation
209
210 INTEGER :: i
211
212 rotation = 0.0_dp
213 DO i = 1, SIZE(rotation, 1)
214 rotation(i, i) = 1.0_dp
215 END DO
216 rotation(axis_1, axis_1) = cos(angle)
217 rotation(axis_2, axis_2) = cos(angle)
218 rotation(axis_1, axis_2) = -sin(angle)
219 rotation(axis_2, axis_1) = sin(angle)
220
221 END SUBROUTINE plane_rotation
222
223! **************************************************************************************************
224!> \brief Form a real outer product.
225!> \param left ...
226!> \param right ...
227!> \return ...
228! **************************************************************************************************
229 PURE FUNCTION outer_product(left, right) RESULT(product)
230
231 REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: left, right
232 REAL(KIND=dp), DIMENSION(SIZE(left), SIZE(right)) :: product
233
234 INTEGER :: i, j
235
236 DO j = 1, SIZE(right)
237 DO i = 1, SIZE(left)
238 product(i, j) = left(i)*right(j)
239 END DO
240 END DO
241
242 END FUNCTION outer_product
243
244! **************************************************************************************************
245!> \brief Abort when two matrices differ beyond the requested tolerance.
246!> \param actual ...
247!> \param reference ...
248!> \param tolerance ...
249!> \param label ...
250! **************************************************************************************************
251 SUBROUTINE assert_close(actual, reference, tolerance, label)
252
253 REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: actual, reference
254 REAL(KIND=dp), INTENT(IN) :: tolerance
255 CHARACTER(LEN=*), INTENT(IN) :: label
256
257 INTEGER :: io_unit
258
259 IF (.NOT. all(ieee_is_finite(actual)) .OR. &
260 .NOT. all(ieee_is_finite(reference)) .OR. maxval(abs(actual - reference)) > tolerance) THEN
261 io_unit = error_unit
262 WRITE (io_unit, '(A,2(1X,ES13.6))') trim(label)//": error/tolerance", &
263 maxval(abs(actual - reference)), tolerance
264 error stop "Matrix comparison failed"
265 END IF
266
267 END SUBROUTINE assert_close
268
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_low_rank_base_overlap
integer, parameter, public ot_precond_full_all_covariant
integer, parameter, public ot_precond_fermi_low_rank
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