Octopus
lalg_adv_tests.F90
Go to the documentation of this file.
1!! Copyright (C) 2025 N. Tancogne-Dejean
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, USA.
17!!
18
19#include "global.h"
20
23 use debug_oct_m
24 use global_oct_m
25 use iso_c_binding
26 use, intrinsic :: iso_fortran_env
31
32 implicit none
33
34 private
35 public :: &
37
38contains
39
40 !---------------------------------------------------------------------------
43 subroutine test_exponential_matrix(namespace)
44 type(namespace_t), intent(in) :: namespace
45
46 ! Tolerance for floating-point comparisons
47 real(real64), parameter :: tol = 1.0e-12_real64
48 integer, parameter :: n = 2
49 real(real64) :: factor
50 real(real64), dimension(n, n) :: A, funA, expected
51 complex(real64), dimension(n, n) :: zA, zfunA, zexpected
52 logical :: hermitian
53 integer, parameter :: n3 = 3
54 complex(real64), dimension(n3, n3) :: A3, funA3, ident3, product
55 complex(real64) :: zfactor
56
58
59 !-----------------------------------------------------------------------
60 ! Test 1: Diagonal matrices
61 ! A = [ [1, 0], [0, 2] ]
62 ! so that exp(A) should be diag(exp(1), exp(2))
63 ! and exp(iA) should be [ [exp(i), 1], [1, exp(2i)] ]
64 !-----------------------------------------------------------------------
65 a = reshape([1.0_real64, 0.0_real64, 0.0_real64, 2.0_real64], shape(a))
66 factor = 1.0_real64
67 hermitian = .true.
68 call lalg_matrix_function(n, factor, a, funa, expfun, hermitian)
69
70 ! Build expected result: only diagonal entries nonzero
71 expected = 0.0_real64
72 expected(1, 1) = exp(1.0_real64)
73 expected(2, 2) = exp(2.0_real64)
74
75 message(1) = "Testing real diagonal matrix exponential..."
76 if ( maxval( abs(funa - expected) ) < tol ) then
77 message(2) = "Diagonal matrix test passed."
78 else
79 message(2) = "Diagonal matrix test failed."
80 endif
81 call messages_info(2, namespace=namespace)
82
83
84 za = reshape([m_z1, m_z0, m_z0, m_two*m_z1], shape(a))
85 zfactor = m_zi
86
87 ! Build expected result: only diagonal entries nonzero
88 zexpected = m_zero
89 zexpected(1, 1) = exp(m_zi)
90 zexpected(2, 2) = exp(m_two*m_zi)
91
92 ! Assuming general matrix first
93 hermitian = .false.
94 call lalg_matrix_function(n, zfactor, za, zfuna, zexpfun, hermitian)
95
96 message(1) = "Testing complex diagonal matrix exponential..."
97 if ( maxval( abs(zfuna - zexpected) ) < tol ) then
98 message(2) = "Diagonal matrix test passed."
99 else
100 message(2) = "Diagonal matrix test failed."
101 endif
102 call messages_info(2, namespace=namespace)
103
104 ! Going via the Hermitian patch
105 hermitian = .true.
106 call lalg_matrix_function(n, zfactor, za, zfuna, zexpfun, hermitian)
107
108 message(1) = "Testing complex diagonal Hermitian matrix exponential..."
109 if ( maxval( abs(zfuna - zexpected) ) < tol ) then
110 message(2) = "Diagonal matrix test passed."
111 else
112 message(2) = "Diagonal matrix test failed."
113 endif
114 call messages_info(2, namespace=namespace)
116 !-----------------------------------------------------------------------
117 ! Test 2: Nilpotent matrix
118 ! A = [ [0, 1+i], [0, 0] ] where A**2 = 0 and exp(A) = I + A
119 !-----------------------------------------------------------------------
120 za = m_z0
121 za(1,2) = m_one + m_zi
122 ! Use a factor of 1 (i.e. compute exp(1*A))
123 zfactor = m_one
124 hermitian = .false.
125 call lalg_matrix_function(n, zfactor, za, zfuna, zexpfun, hermitian)
126
127 ! Build expected result: I + A
128 zexpected = (0.0_real64, 0.0_real64)
129 zexpected(1,1) = (1.0_real64, 0.0_real64)
130 zexpected(2,2) = (1.0_real64, 0.0_real64)
131 zexpected(1,2) = (1.0_real64, 1.0_real64)
132
133 message(1) = "Testing nilpotent matrix exponential..."
134 if ( maxval( abs(zfuna - zexpected) ) < tol ) then
135 message(2) = "Nilpotent matrix test passed."
136 else
137 message(2) = "Nilpotent matrix test failed."
138 endif
139 call messages_info(2, namespace=namespace)
140
141 !-----------------------------------------------------------------------
142 ! Test 3: Tridiagonal matrix with complex factor: exp(iA)
143 ! We use a 3x3 symmetric tridiagonal matrix:
144 ! A = [ 2 1 0 ]
145 ! [ 1 2 1 ]
146 ! [ 0 1 2 ]
147 ! With factor = i, we compute exp(iA). For hermitian A, exp(iA) is unitary.
148 ! We verify unitarity by checking that M * M^dagger = I.
149 !-----------------------------------------------------------------------
150 hermitian = .true.
151 zfactor = m_zi
152
153 a3 = (0.0_real64, 0.0_real64)
154 ! Set the main diagonal to 2
155 a3(1,1) = (2.0_real64, 0.0_real64)
156 a3(2,2) = (2.0_real64, 0.0_real64)
157 a3(3,3) = (2.0_real64, 0.0_real64)
158 ! Set the off-diagonals to 1
159 a3(1,2) = (1.0_real64, 0.0_real64); a3(2,1) = (1.0_real64, 0.0_real64)
160 a3(2,3) = (1.0_real64, 0.0_real64); a3(3,2) = (1.0_real64, 0.0_real64)
161
162 call lalg_matrix_function(n3, zfactor, a3, funa3, zexpfun, hermitian, tridiagonal=.true.)
163
164 ! Build the 3x3 identity matrix
165 ident3 = (0.0_real64, 0.0_real64)
166 ident3(1,1) = (1.0_real64, 0.0_real64)
167 ident3(2,2) = (1.0_real64, 0.0_real64)
168 ident3(3,3) = (1.0_real64, 0.0_real64)
169
170 ! Check unitarity: funA3 * funA3^dagger should equal identity.
171 product = matmul(funa3, transpose(conjg(funa3)))
172
173 message(1) = "Testing tridiagonal matrix exp(iA) for unitarity..."
174 if ( maxval( abs(product - ident3) ) < tol ) then
175 message(2) = "Tridiagonal matrix exp(iA) test passed."
176 else
177 message(2) = "Tridiagonal matrix exp(iA) test failed."
178 endif
179 call messages_info(2, namespace=namespace)
180
182 end subroutine test_exponential_matrix
183
184 !-----------------------------------------------------------------------
186 real(real64) function expfun(z)
187 implicit none
188 real(real64), intent(in) :: z
189 expfun = exp(z)
190 end function expfun
191
192 !-----------------------------------------------------------------------
194 complex(real64) function zexpfun(z)
195 implicit none
196 complex(real64), intent(in) :: z
197 zexpfun = exp(z)
198 end function zexpfun
199
200end module lalg_adv_tests_oct_m
201
202!! Local Variables:
203!! mode: f90
204!! coding: utf-8
205!! End:
double exp(double __x) __attribute__((__nothrow__
real(real64), parameter, public m_two
Definition: global.F90:190
real(real64), parameter, public m_zero
Definition: global.F90:188
complex(real64), parameter, public m_z0
Definition: global.F90:198
complex(real64), parameter, public m_zi
Definition: global.F90:202
complex(real64), parameter, public m_z1
Definition: global.F90:199
real(real64), parameter, public m_one
Definition: global.F90:189
This modules takes care of testing some linear algebra routines.
real(real64) function expfun(z)
Scalar exponential function for real arguments.
complex(real64) function zexpfun(z)
Scalar exponential function for complex arguments.
subroutine, public test_exponential_matrix(namespace)
Unit tests for the exponential of a matrix.
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:160
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:616
int true(void)