! ************************************************************************
! Fájl: QRAlg.f08
! Leírás: QR felbontás és QR módszer mátrix sajátértékeinek meghatározására
! ************************************************************************
program QRAlg
implicit none
! Rövid főprogram amely teszt adatokon futtatja a QR módszer algoritmusát
double precision, dimension(4,4) :: A,eig
double precision :: eps = 0.00001 ! epsilon a megengedett hiba
! Az A 4x4-es mátrix, elemei oszlopfolytonosan megadva
A = reshape( [2.0, 2.0, 0.0, 0.0, 1.0, 3.0, 0.0, 0.0, -1.0, 1.0, 4.0, -1.0, 1.0, -2.0, 2.0, 1.0], shape(A) )
! eig egy 4x4-es felsőháromszögmátrix,
! a diagonálisában a számított sajátértékekkel
eig = qr_method(A, eps)
! Input és az eredmény kiíratása
call print_matrix(A)
print *, '--------------------------'
call print_matrix(eig)
! A lényegi munkát végző függvények
contains
! ********************************************************
! Egy adott mátrix QR felbontását készíti el
! Input: A = négyzetes valós elemű mátrix
! Output: Q,R = négyzetes valós mátrixok,
! A = Q*R, Q ortogonális, R felsőháromszög mátrix
! A megvalósítás Gram-Schmidt ortogonalizációval történik
! Segédfüggvények: normalize(), projection()
! ********************************************************
subroutine qr_decomposition(A,Q,R)
implicit none
double precision, intent(in), dimension(:,:) :: A
double precision, intent(out), dimension(:,:) :: Q,R
! segédváltozó, az A n x n-es esetén egy n hosszú vektor
double precision, dimension(size(A,1)) :: sum_proj
integer :: n,k,j
n = size(A,1) ! mátrix mérete
! Elég csupán a Q mátrixot elkészíteni, az R utána szorzással megkapható
! Az algoritmus inicializálása, az input mátrix első oszlopát normalizáljuk
! Megjegyzés: A(1:n,1) syntaxis: array slice, A első oszlopát adja
Q(1:n,1) = normalize(A(1:n,1))
! A ciklusban Q további oszlopait ortogonalizáljuk minden előzőre
do k=2,n
! Gram-Schmidt féle projekciók elkészítése és összegzése
sum_proj = 0.0
do j=1,k-1
! A k. lépésben az eredeti mátrix k. oszlopát vetítjük a már meglévőkre
sum_proj = sum_proj + projection(vector=A(1:n,k),project_to=Q(1:n,j))
end do
! A vetítések összegét levonva az eredeti vektorból,
! egy az összes a rendszerben őt megelőzőre merőleges vektort kapunk,
! majd az elkészült új oszlopot normalizáljuk
Q(1:n, k) = normalize(A(1:n, k) - sum_proj)
end do
! R = Q'*A, a két intrinsic a transzponálást és mátrixszorzást végzi
R = matmul(transpose(Q), A)
end subroutine qr_decomposition
! ********************************************************
! Merőleges vetület számítása
! Input: vector = n hosszú valós vektor, amit vetítünk
! project_to, ugyanakkora méretű vektor, amire vetítünk
! Result: vetület vektor
! ********************************************************
function projection(vector, project_to)
implicit none
double precision, intent(in), dimension(:) :: vector, project_to
double precision, dimension(size(vector)) :: projection
! proj a on b = / * a
! a vetület vektor tehát két skalár szorzat hányadosa * a vektor amire vetítünk
projection = dot_product(project_to, vector)/dot_product(project_to, project_to) * project_to
end function projection
! ********************************************************
! Vektor normalizálása (2-es normában)
! Input: vector, n hosszú valós vektor
! Result: a normalizált vektor
! ********************************************************
function normalize(vector)
implicit none
double precision, intent(in), dimension(:) :: vector
double precision, dimension(size(vector)) :: normalize
! A vektort elosztjuk a hosszával, vagyis sqrt() - vel
normalize = vector / sqrt(dot_product(vector, vector))
end function normalize
! ********************************************************
! Mátrix sajátértékeinek meghatározása QR módszerrel
! Input: matrix, négyzetes valós mátrix
! eps: numerikus hibakorlát
! Result: R felsőháromszög mátrix,
! ami a sajátértékeket a diagonálisában tartalmazza
! X(k+1) = R(k) * Q(k) k=1,2,... az iteráció
! segédfüggvény: qr_decomposition(),check_convergence()
! ********************************************************
function qr_method(matrix, eps)
implicit none
double precision, intent(in), dimension(:,:) :: matrix
double precision, dimension(size(matrix,1),size(matrix,2)) :: qr_method
double precision, intent(in) :: eps
double precision, dimension(size(matrix,1),size(matrix,2)) :: Q,R,eigenvalues
! inicializáljuk az algoritmust
eigenvalues = matrix
! A fő iterációs ciklus
do while ( check_convergence(eigenvalues, eps) .eqv. .false. )
! Előállítjuk a QR felbontást,
call qr_decomposition(eigenvalues, Q,R)
! majd fordított sorrendben szorozzuk őket össze
eigenvalues = matmul(R,Q)
end do
qr_method = eigenvalues
end function qr_method
! ********************************************************
! A QR módszer segéd függvénye a konvergencia ellenőrzésére
! Input: matrix: négyzetes valós mátrix
! eps: a numerikus hibakorlát
! ********************************************************
function check_convergence(matrix, eps)
implicit none
logical :: check_convergence
double precision, intent(in), dimension(:,:) :: matrix
double precision, intent(in) :: eps
double precision :: max_err
integer :: n,i,j
n = size(matrix,1)
! A mátrixnak felsőháromszög mátrixhoz kell tartania,
! vagyis az alsóháromszög beli elemeknek 0-hoz kell tartania
! Megkeressük az alsóháromszög rész abszolútértékben maximális elemét
max_err = 0.0
do i=2,n
do j=1,i-1
if ( abs(matrix(i,j)) > max_err ) then
max_err = abs(matrix(i,j))
end if
end do
end do
! Ha a maximális hiba kisebb mint epsilon akkor ok
if ( max_err < eps ) then
check_convergence = .true.
else
check_convergence = .false.
end if
end function check_convergence
! Egyszerű kiírató segédfüggvény, a mátrixot soronként írja ki
subroutine print_matrix(matrix)
implicit none
double precision, dimension(:,:) :: matrix
integer :: i,j,n,m
n = size(matrix,1)
m = size(matrix,2)
do i=1,n
print *, ( matrix(i,j), j=1,m )
end do
end subroutine print_matrix
end program QRAlg