Skip to content

Commit 3c776d7

Browse files
authored
Merge pull request #25 from NLESC-JCER/opt
implementing deflation method
2 parents b9fe82a + afcb0b6 commit 3c776d7

File tree

10 files changed

+1923
-40
lines changed

10 files changed

+1923
-40
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11

2+
# Version 0.0.5
3+
4+
### New
5+
6+
* Select the initial orthonormal basis set based on the lowest diagonal elements of the matrix
7+
28
# Version 0.0.4
39

410
## changed

src/array_utils.f90

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ module array_utils
22

33
use numeric_kinds, only: dp
44
use lapack_wrapper, only: lapack_generalized_eigensolver, lapack_matmul, lapack_matrix_vector, &
5-
lapack_qr, lapack_solver
5+
lapack_qr, lapack_solver, lapack_sort
66
implicit none
77

88
!> \private
99
private
1010
!> \public
11-
public :: concatenate, diagonal,eye, generate_diagonal_dominant, norm
11+
public :: concatenate, diagonal,eye, generate_diagonal_dominant, norm, &
12+
generate_preconditioner
1213

1314
contains
1415

@@ -33,7 +34,7 @@ pure function eye(m, n, alpha)
3334
do i=1, m
3435
do j=1, n
3536
if (i /= j) then
36-
eye(i, j) = 0
37+
eye(i, j) = 0.d0
3738
else
3839
eye(i, i) = x
3940
end if
@@ -132,4 +133,49 @@ function diagonal(matrix)
132133

133134
end function diagonal
134135

136+
function generate_preconditioner(diag, dim_sub) result(precond)
137+
!> \brief generates a diagonal preconditioner for `matrix`.
138+
!> \return diagonal matrix
139+
140+
! input variable
141+
real(dp), dimension(:), intent(inout) :: diag
142+
integer, intent(in) :: dim_sub
143+
144+
! local variables
145+
real(dp), dimension(size(diag), dim_sub) :: precond
146+
integer, dimension(size(diag)) :: keys
147+
integer :: i, k
148+
149+
! sort diagonal
150+
keys = lapack_sort('I', diag)
151+
! Fill matrix with zeros
152+
precond = 0.0_dp
153+
154+
! Add one depending on the order of the matrix diagonal
155+
do i=1, dim_sub
156+
k = search_key(keys, i)
157+
precond(k, i) = 1.d0
158+
end do
159+
160+
end function generate_preconditioner
161+
162+
function search_key(keys, i) result(k)
163+
!> \brief Search for a given index `i` in a vector `keys`
164+
!> \param keys Vector of index
165+
!> \param i Index to search for
166+
!> \return index of i inside keys
167+
168+
integer, dimension(:), intent(in) :: keys
169+
integer, intent(in) :: i
170+
integer :: j, k
171+
172+
do j=1,size(keys)
173+
if (keys(j) == i) then
174+
k = j
175+
exit
176+
end if
177+
end do
178+
179+
end function search_key
180+
135181
end module array_utils

src/davidson.f90

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
!> \namespace Davidson eigensolver
22
!> \author Felipe Zapata
3+
!> The current implementation uses a general davidson algorithm, meaning
4+
!> that it compute all the eigenvalues simultaneusly using a variable size block approach.
5+
!> The family of Davidson algorithm only differ in the way that the correction
6+
!> vector is computed.
7+
!> Computed pairs of eigenvalues/eigenvectors are deflated using algorithm
8+
!> described at: https://doi.org/10.1023/A:101919970
9+
10+
311
module davidson_dense
412
!> Submodule containing the implementation of the Davidson diagonalization method
513
!> for dense matrices
614
use numeric_kinds, only: dp
715
use lapack_wrapper, only: lapack_generalized_eigensolver, lapack_matmul, lapack_matrix_vector, &
8-
lapack_qr, lapack_solver
9-
use array_utils, only: concatenate, eye, norm
16+
lapack_qr, lapack_solver, lapack_sort
17+
use array_utils, only: concatenate, diagonal, eye, generate_preconditioner, norm
1018

1119
implicit none
1220

@@ -41,11 +49,8 @@ end function compute_correction_generalized_dense
4149

4250
subroutine generalized_eigensolver_dense(mtx, eigenvalues, ritz_vectors, lowest, method, max_iters, &
4351
tolerance, iters, max_dim_sub, stx)
44-
!> The current implementation uses a general davidson algorithm, meaning
45-
!> that it compute all the eigenvalues simultaneusly using a block approach.
46-
!> The family of Davidson algorithm only differ in the way that the correction
47-
!> vector is computed.
48-
52+
!> Implementation storing in memory the initial densed matrix mtx.
53+
4954
!> \param[in] mtx: Matrix to diagonalize
5055
!> \param[in, opt] Optional matrix to solve the general eigenvalue problem:
5156
!> \f$ mtx \lambda = V stx \lambda \f$
@@ -78,6 +83,7 @@ subroutine generalized_eigensolver_dense(mtx, eigenvalues, ritz_vectors, lowest,
7883

7984
!local variables
8085
integer :: i, j, dim_sub, max_dim
86+
integer :: n_converged ! Number of converged eigenvalue/eigenvector pairs
8187

8288
! Basis of subspace of approximants
8389
real(dp), dimension(size(mtx, 1)) :: guess, rs
@@ -87,12 +93,22 @@ subroutine generalized_eigensolver_dense(mtx, eigenvalues, ritz_vectors, lowest,
8793
real(dp), dimension(:), allocatable :: eigenvalues_sub
8894
real(dp), dimension(:, :), allocatable :: correction, eigenvectors_sub, mtx_proj, stx_proj, V
8995

96+
! Diagonal matrix
97+
real(dp), dimension(size(mtx, 1)) :: d
98+
9099
! generalize problem
91100
logical :: gev
92101

102+
! indices of the eigenvalues/eigenvectors pair that have not converged
103+
logical, dimension(lowest) :: has_converged
104+
93105
! Iteration subpsace dimension
94106
dim_sub = lowest * 2
95107

108+
! Initial number of converged eigenvalue/eigenvector pairs
109+
n_converged = 0
110+
has_converged = .False.
111+
96112
! maximum dimension of the basis for the subspace
97113
if (present(max_dim_sub)) then
98114
max_dim = max_dim_sub
@@ -104,8 +120,10 @@ subroutine generalized_eigensolver_dense(mtx, eigenvalues, ritz_vectors, lowest,
104120
gev = present(stx)
105121

106122
! 1. Variables initialization
107-
V = eye(size(ritz_vectors, 1), dim_sub) ! Initial orthonormal basis
108-
123+
! Select the initial ortogonal subspace based on lowest elements
124+
! of the diagonal of the matrix
125+
d = diagonal(mtx)
126+
V = generate_preconditioner(d, dim_sub)
109127

110128
! 2. Generate subpace matrix problem by projecting into V
111129
mtx_proj = lapack_matmul('T', 'N', V, lapack_matmul('N', 'N', mtx, V))
@@ -145,9 +163,16 @@ subroutine generalized_eigensolver_dense(mtx, eigenvalues, ritz_vectors, lowest,
145163
end if
146164
rs = lapack_matrix_vector('N', mtx, ritz_vectors(:, j)) - guess
147165
errors(j) = norm(rs)
166+
! Check which eigenvalues has converged
167+
if (errors(j) < tolerance) then
168+
has_converged(j) = .true.
169+
end if
148170
end do
149-
150-
if (all(errors < tolerance)) then
171+
172+
! Count converged pairs of eigenvalues/eigenvectors
173+
n_converged = n_converged + count(errors < tolerance)
174+
175+
if (all(has_converged)) then
151176
iters = i
152177
exit
153178
end if
@@ -197,7 +222,8 @@ subroutine generalized_eigensolver_dense(mtx, eigenvalues, ritz_vectors, lowest,
197222
end do outer_loop
198223

199224
! 8. Check convergence
200-
if (i > max_iters / dim_sub) then
225+
if (i > max_iters) then
226+
iters = i
201227
print *, "Warning: Algorithm did not converge!!"
202228
end if
203229

@@ -265,7 +291,7 @@ module davidson_free
265291
use numeric_kinds, only: dp
266292
use lapack_wrapper, only: lapack_generalized_eigensolver, lapack_matmul, lapack_matrix_vector, &
267293
lapack_qr, lapack_solver
268-
use array_utils, only: concatenate, eye, norm
294+
use array_utils, only: concatenate, generate_preconditioner, norm
269295
use davidson_dense, only: generalized_eigensolver_dense
270296
implicit none
271297

@@ -343,7 +369,7 @@ end function fun_stx_gemv
343369

344370
! ! Basis of subspace of approximants
345371
real(dp), dimension(size(ritz_vectors, 1),1) :: guess, rs
346-
real(dp), dimension(size(ritz_vectors, 1) ) :: diag_mtx, diag_stx
372+
real(dp), dimension(size(ritz_vectors, 1) ) :: diag_mtx, diag_stx, copy_d
347373
real(dp), dimension(lowest):: errors
348374

349375
! ! Working arrays
@@ -368,7 +394,10 @@ end function fun_stx_gemv
368394
diag_stx = extract_diagonal_free(fun_stx_gemv,dim_mtx)
369395

370396
! 1. Variables initialization
371-
V = eye(dim_mtx, dim_sub) ! Initial orthonormal basis
397+
! Select the initial ortogonal subspace based on lowest elements
398+
! of the diagonal of the matrix
399+
copy_d = diag_mtx
400+
V = generate_preconditioner(copy_d, dim_sub) ! Initial orthonormal basis
372401

373402
! 2. Generate subspace matrix problem by projecting into V
374403
mtx_proj = lapack_matmul('T', 'N', V, fun_mtx_gemv(V))
@@ -757,7 +786,7 @@ function compute_DPR_generalized_dense(mtx, V, eigenvalues, eigenvectors, stx) r
757786
real(dp), dimension(:, :), intent(in) :: mtx, V, eigenvectors
758787
real(dp), dimension(:, :), intent(in), optional :: stx
759788
real(dp), dimension(size(mtx, 1), size(V, 2)) :: correction
760-
789+
761790
! local variables
762791
integer :: ii,j, m
763792
real(dp), dimension(size(mtx, 1), size(mtx, 2)) :: diag, arr
@@ -780,12 +809,12 @@ function compute_DPR_generalized_dense(mtx, V, eigenvalues, eigenvectors, stx) r
780809
correction(:, j) = lapack_matrix_vector('N', arr, vec)
781810

782811
do ii=1,size(correction,1)
783-
if (gev) then
784-
correction(ii, j) = correction(ii, j) / (eigenvalues(j) * stx(ii,ii) - mtx(ii, ii))
785-
else
786-
correction(ii, j) = correction(ii, j) / (eigenvalues(j) - mtx(ii, ii))
787-
end if
788-
end do
812+
if (gev) then
813+
correction(ii, j) = correction(ii, j) / (eigenvalues(j) * stx(ii,ii) - mtx(ii, ii))
814+
else
815+
correction(ii, j) = correction(ii, j) / (eigenvalues(j) - mtx(ii, ii))
816+
endif
817+
end do
789818
end do
790819

791820
end function compute_DPR_generalized_dense

src/lapack_wrapper.f90

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module lapack_wrapper
77
private
88
!> \public
99
public :: lapack_generalized_eigensolver, lapack_matmul, lapack_matrix_vector, &
10-
lapack_qr, lapack_solver
10+
lapack_qr, lapack_solver, lapack_sort
1111

1212
contains
1313

@@ -280,6 +280,35 @@ function lapack_matrix_vector(transA, mtx, vector, alpha) result(rs)
280280

281281
end function lapack_matrix_vector
282282

283+
284+
function lapack_sort(id, vector) result(keys)
285+
!> \brief sort a vector of douboles
286+
!> \param vector Array to sort
287+
!> \param keys Indices of the sorted array
288+
!> \param id Sorted either `I` increasing or `D` decreasing
289+
real(dp), dimension(:), intent(inout) :: vector
290+
character(len=1), intent(in) :: id
291+
integer, dimension(size(vector)) :: keys
292+
293+
! local variables
294+
real(dp), dimension(size(vector)) :: xs
295+
integer :: i, j, info
296+
xs = vector
297+
298+
call DLASRT(id, size(vector), vector, info)
299+
call check_lapack_call(info, "DLASRT")
300+
301+
do i=1,size(vector)
302+
do j=1, size(vector)
303+
if (abs(vector(j) - xs(i)) < 1e-16) then
304+
keys(i) = j
305+
end if
306+
end do
307+
end do
308+
309+
end function lapack_sort
310+
311+
283312
subroutine check_lapack_call(info, name)
284313
!> Check if a subroutine finishes sucessfully
285314
!> \param info: Termination signal

src/main.f90

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ program main
3737

3838
implicit none
3939

40-
integer, parameter :: dim = 50
40+
integer, parameter :: dim = 100
4141
real(dp), dimension(3) :: eigenvalues_DPR, eigenvalues_GJD
4242
real(dp), dimension(dim, 3) :: eigenvectors_DPR, eigenvectors_GJD
4343
real(dp), dimension(dim, dim) :: mtx
@@ -46,27 +46,29 @@ program main
4646
real(dp), dimension(dim) :: xs
4747
integer :: iter_i, j
4848

49-
mtx = generate_diagonal_dominant(dim, 1d-4)
50-
stx = generate_diagonal_dominant(dim, 1d-4, 1d0)
51-
52-
call generalized_eigensolver(mtx, eigenvalues_GJD, eigenvectors_GJD, 3, "GJD", 1000, 1d-8, iter_i, 10, stx)
53-
call generalized_eigensolver(mtx, eigenvalues_DPR, eigenvectors_DPR, 3, "DPR", 1000, 1d-8, iter_i, 10, stx)
49+
mtx = generate_diagonal_dominant(dim, 1d-3)
50+
stx = generate_diagonal_dominant(dim, 1d-3, 1d0)
5451

52+
call generalized_eigensolver(mtx, eigenvalues_GJD, eigenvectors_GJD, 3, "GJD", 100, 1d-5, iter_i, 10, stx)
53+
print *, "GJD algorithm converged in: ", iter_i, " iterations!"
54+
call generalized_eigensolver(mtx, eigenvalues_DPR, eigenvectors_DPR, 3, "DPR", 100, 1d-5, iter_i, 10, stx)
55+
print *, "DPR algorithm converged in: ", iter_i, " iterations!"
56+
5557
print *, "Test 1"
5658
test_norm_eigenvalues = norm(eigenvalues_GJD - eigenvalues_DPR)
5759
print *, "Check that eigenvalues norm computed by different methods are the same: ", test_norm_eigenvalues < 1e-6
5860

5961
print *, "Test 2"
60-
print *, "Check that eigenvalue equation: H V = l S V "
62+
print *, "Check that eigenvalue equation: H V = l S V holds!"
6163
print *, "DPR method:"
6264
do j=1,3
6365
xs = matmul(mtx, eigenvectors_DPR(:, j)) - (eigenvalues_DPR(j) * matmul(stx, eigenvectors_DPR(:, j)))
64-
print *, "eigenvalue ", j, ": ", eigenvalues_DPR(j), " : ", norm(xs) , " : ", norm(xs)< 1.d-7
66+
print *, "eigenvalue ", j, ": ", eigenvalues_DPR(j), "||Error||: ", norm(xs)
6567
end do
6668
print *, "GJD method:"
6769
do j=1,3
6870
xs = matmul(mtx, eigenvectors_GJD(:, j)) - (eigenvalues_GJD(j) * matmul( stx, eigenvectors_GJD(:, j)))
69-
print *, "eigenvalue ", j, ": ",eigenvalues_GJD(j), " : ", norm(xs), " : ", norm(xs)< 1.d-7
71+
print *, "eigenvalue ", j, ": ",eigenvalues_GJD(j), "||Error||: ", norm(xs)
7072
end do
7173

7274
end program main

src/tests/CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ foreach(PROG ${SOURCE_DAVIDSON})
1717
endforeach(PROG)
1818
# set(NUMPY_TEST_SRC ${CMAKE_SOURCE_DIR}/src/davidson.f90 ${CMAKE_SOURCE_DIR}/src/lapack_wrapper.f90 ${CMAKE_SOURCE_DIR}/src/numeric_kinds.f90)
1919

20-
set(test_cases test_call_lapack test_dense_numpy test_free_numpy test_dense_properties test_free_properties)
20+
set(test_cases test_call_lapack test_dense_numpy test_free_numpy test_dense_properties
21+
test_free_properties test_reorder)
2122

2223
foreach(PROG ${test_cases})
2324
add_executable(${PROG}
@@ -50,6 +51,12 @@ add_test(
5051
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin
5152
)
5253

54+
add_test(
55+
NAME test_reorder
56+
COMMAND $<TARGET_FILE:test_reorder>
57+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin
58+
)
59+
5360
# Tests requiring python, numpy and scipy
5461
if(${_numpy_status} EQUAL 0)
5562
add_test(

0 commit comments

Comments
 (0)