gjp_imtqlx.f90
Go to the documentation of this file.
1 ! BEGIN_HEADER
2 ! -----------------------------------------------------------------------------
3 ! Gauss-Jacobi Quadrature Implementation
4 ! Authors: Rohit Goswami <rgoswami[at]ieee.org>
5 ! Source: GaussJacobiQuad Library
6 ! License: MIT
7 ! GitHub Repository: https://github.com/HaoZeke/GaussJacobiQuad
8 ! Date: 2023-08-28
9 ! Commit: c442f77
10 ! -----------------------------------------------------------------------------
11 ! This code is part of the GaussJacobiQuad library, providing an efficient
12 ! implementation for Gauss-Jacobi quadrature nodes and weights computation.
13 ! -----------------------------------------------------------------------------
14 ! To cite this software:
15 ! Rohit Goswami (2023). HaoZeke/GaussJacobiQuad: v0.1.0.
16 ! Zenodo: https://doi.org/10.5281/ZENODO.8285112
17 ! ---------------------------------------------------------------------
18 ! END_HEADER
19 module gjp_imtqlx
20 use gjp_types, only: dp
21 implicit none
22 
23 contains
24 
43 subroutine imtqlx(mat_size, diag, off_diag, sol_vec)
44  implicit none
45 
46  ! Inputs
47  integer, intent(in) :: mat_size
48  real(dp), intent(inout) :: diag(mat_size)
49  real(dp), intent(inout) :: off_diag(mat_size - 1)
50  real(dp), intent(inout) :: sol_vec(mat_size)
51 
52  ! Local variables
53  real(dp) :: precision, pivot_val, g_val, rot_val, scale_val, f_val, b_val, cos_val
54  integer :: lower_bound, upper_bound, inner_i, i, iter_count
55  integer, parameter :: max_iter = 30
56 
57  ! Initialize machine precision
58  precision = epsilon(precision)
59 
60  ! Set the last off-diagonal element to zero
61  off_diag(mat_size - 1) = 0.0_dp
62 
63  ! Main loop over all diagonal elements (Eigenvalues)
64  do lower_bound = 1, mat_size
65  iter_count = 0 ! Initialize iteration counter for the inner loop
66 
67  ! Inner loop for convergence criteria (max iterations)
68  do while (iter_count < max_iter)
69  ! Loop to find upper bound for the current lower bound
70  do upper_bound = lower_bound, mat_size
71  if (upper_bound == mat_size) exit
72  if (abs(off_diag(upper_bound)) <= precision * (abs(diag(upper_bound)) + abs(diag(upper_bound + 1)))) exit
73  end do
74 
75  ! Check for convergence
76  pivot_val = diag(lower_bound)
77  if (upper_bound == lower_bound) exit ! Converged
78 
79  ! Check for iteration limit
80  if (iter_count > max_iter) then
81  print*," "
82  print*,"IMTQLX - Fatal error."
83  print*,"Iteration limit exceeded."
84  stop "Terminating due to iteration limit exceeded."
85  end if
86 
87  iter_count = iter_count + 1 ! Update iteration count
88 
89  ! Calculation of g_val based on pivot and off-diagonal elements
90  g_val = (diag(lower_bound + 1) - pivot_val) / (2.0_dp * off_diag(lower_bound))
91  rot_val = sqrt(g_val * g_val + 1.0_dp)
92  g_val = diag(upper_bound) - pivot_val + off_diag(lower_bound) / (g_val + sign(rot_val, g_val))
93 
94  ! Initialize rotation parameters
95  scale_val = 1.0_dp
96  cos_val = 1.0_dp
97  pivot_val = 0.0_dp
98 
99  ! Loop for the implicit QR factorization
100  do inner_i = 1, upper_bound - lower_bound
101  i = upper_bound - inner_i
102  f_val = scale_val * off_diag(i)
103  b_val = cos_val * off_diag(i)
104 
105  ! Update by Givens rotation
106  if (abs(g_val) <= abs(f_val)) then
107  cos_val = g_val / f_val
108  rot_val = sqrt(cos_val * cos_val + 1.0_dp)
109  off_diag(i + 1) = f_val * rot_val
110  scale_val = 1.0_dp / rot_val
111  cos_val = cos_val * scale_val
112  else
113  scale_val = f_val / g_val
114  rot_val = sqrt(scale_val * scale_val + 1.0_dp)
115  off_diag(i + 1) = g_val * rot_val
116  cos_val = 1.0_dp / rot_val
117  scale_val = scale_val * cos_val
118  end if
119 
120  ! Update diagonal and off-diagonal elements
121  g_val = diag(i + 1) - pivot_val
122  rot_val = (diag(i) - g_val) * scale_val + 2.0_dp * cos_val * b_val
123  pivot_val = scale_val * rot_val
124  diag(i + 1) = g_val + pivot_val
125  g_val = cos_val * rot_val - b_val
126 
127  ! Update solution vector
128  f_val = sol_vec(i + 1)
129  sol_vec(i + 1) = scale_val * sol_vec(i) + cos_val * f_val
130  sol_vec(i) = cos_val * sol_vec(i) - scale_val * f_val
131  end do
132 
133  ! Update diagonal and off-diagonal elements after QR step
134  diag(lower_bound) = diag(lower_bound) - pivot_val
135  off_diag(lower_bound) = g_val
136  off_diag(upper_bound) = 0.0_dp
137  end do
138  end do
139 
140  ! Sort the solution after convergence
141  call dsort2a(mat_size, diag, sol_vec)
142 end subroutine
143 
144 ! Sorts x and performs the same swaps on y
145 subroutine dsort2a(n, x, y)
146  integer, intent(in) :: n
147  real(dp), intent(inout) :: x(n), y(n)
148  integer :: i, j, min_idx
149  real(dp) :: temp
150 
151  do i = 1, n - 1
152  min_idx = i
153  do j = i + 1, n
154  if (x(j) < x(min_idx)) min_idx = j
155  end do
156  if (min_idx /= i) then
157  ! Swap x(i) and x(min_idx)
158  temp = x(i)
159  x(i) = x(min_idx)
160  x(min_idx) = temp
161 
162  ! Swap y(i) and y(min_idx)
163  temp = y(i)
164  y(i) = y(min_idx)
165  y(min_idx) = temp
166  end if
167  end do
168 end subroutine dsort2a
169 
170 end module gjp_imtqlx
subroutine dsort2a(n, x, y)
Definition: gjp_imtqlx.f90:146
subroutine imtqlx(mat_size, diag, off_diag, sol_vec)
Implicitly-shifted Modified QL factorization algorithm for symmetric tridiagonal matrices.
Definition: gjp_imtqlx.f90:44
Module for defining types and precision levels for Gauss-Jacobi Polynomial (GJP) calculations.
Definition: gjp_types.f90:33
integer, parameter, public dp
Define various kinds for real numbers.
Definition: gjp_types.f90:39