given an m by n lower trapezoidal matrix s, an m-vector u, and an n-vector v, the problem is to determine an orthogonal matrix q such that
t
(s + u*v )*q
is again lower trapezoidal.
this subroutine determines q as the product of 2*(n - 1) transformations
gv(n-1)*...*gv(1)*gw(1)*...*gw(n-1)
where gv(i), gw(i) are givens rotations in the (i,n) plane which eliminate elements in the i-th and n-th planes, respectively. q itself is not accumulated, rather the information to recover the gv, gw rotations is returned.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
integer, | intent(in) | :: | m | a positive integer input variable set to the number of rows of s. |
||
integer, | intent(in) | :: | n | a positive integer input variable set to the number of columns of s. n must not exceed m. |
||
real(kind=wp), | intent(inout) | :: | s(Ls) | an array of length ls. on input s must contain the lower trapezoidal matrix s stored by columns. on output s contains the lower trapezoidal matrix produced as described above. |
||
integer, | intent(in) | :: | Ls | a positive integer input variable not less than (n(2m-n+1))/2. |
||
real(kind=wp), | intent(in) | :: | u(m) | an input array of length m which must contain the vector u. |
||
real(kind=wp), | intent(inout) | :: | v(n) | an array of length n. on input v must contain the vector v. on output v(i) contains the information necessary to recover the givens rotation gv(i) described above. |
||
real(kind=wp), | intent(out) | :: | w(m) | an output array of length m. w(i) contains information necessary to recover the givens rotation gw(i) described above. |
||
logical, | intent(out) | :: | Sing | a logical output variable. sing is set true if any of the diagonal elements of the output s are zero. otherwise sing is set false. |
subroutine r1updt(m, n, s, Ls, u, v, w, Sing)
implicit none
integer, intent(in) :: m !! a positive integer input variable set to the number
!! of rows of s.
integer, intent(in) :: n !! a positive integer input variable set to the number
!! of columns of s. n must not exceed m.
integer, intent(in) :: Ls !! a positive integer input variable not less than
!! (n*(2*m-n+1))/2.
logical, intent(out) :: Sing !! a logical output variable. sing is set true if any
!! of the diagonal elements of the output s are zero. otherwise
!! sing is set false.
real(wp), intent(inout) :: s(Ls) !! an array of length ls. on input s must contain the lower
!! trapezoidal matrix s stored by columns. on output s contains
!! the lower trapezoidal matrix produced as described above.
real(wp), intent(in) :: u(m) !! an input array of length m which must contain the
!! vector u.
real(wp), intent(inout) :: v(n) !! an array of length n. on input v must contain the vector
!! v. on output v(i) contains the information necessary to
!! recover the givens rotation gv(i) described above.
real(wp), intent(out) :: w(m) !! an output array of length m. w(i) contains information
!! necessary to recover the givens rotation gw(i) described
!! above.
integer :: i, j, jj, l, nmj, nm1
real(wp) :: cos, cotan, sin, tan, tau, temp
real(wp), parameter :: p5 = 5.0e-1_wp
real(wp), parameter :: p25 = 2.5e-1_wp
real(wp), parameter :: giant = dpmpar(3) !! the largest magnitude.
! initialize the diagonal element pointer.
jj = (n*(2*m - n + 1))/2 - (m - n)
! move the nontrivial part of the last column of s into w.
l = jj
do i = n, m
w(i) = s(l)
l = l + 1
end do
! rotate the vector v into a multiple of the n-th unit vector
! in such a way that a spike is introduced into w.
nm1 = n - 1
if (nm1 >= 1) then
do nmj = 1, nm1
j = n - nmj
jj = jj - (m - j + 1)
w(j) = zero
if (v(j) /= zero) then
! determine a givens rotation which eliminates the
! j-th element of v.
if (abs(v(n)) >= abs(v(j))) then
tan = v(j)/v(n)
cos = p5/sqrt(p25 + p25*tan**2)
sin = cos*tan
tau = sin
else
cotan = v(n)/v(j)
sin = p5/sqrt(p25 + p25*cotan**2)
cos = sin*cotan
tau = one
if (abs(cos)*giant > one) tau = one/cos
end if
! apply the transformation to v and store the information
! necessary to recover the givens rotation.
v(n) = sin*v(j) + cos*v(n)
v(j) = tau
! apply the transformation to s and extend the spike in w.
l = jj
do i = j, m
temp = cos*s(l) - sin*w(i)
w(i) = sin*s(l) + cos*w(i)
s(l) = temp
l = l + 1
end do
end if
end do
end if
! add the spike from the rank 1 update to w.
do i = 1, m
w(i) = w(i) + v(n)*u(i)
end do
! eliminate the spike.
Sing = .false.
if (nm1 >= 1) then
do j = 1, nm1
if (w(j) /= zero) then
! determine a givens rotation which eliminates the
! j-th element of the spike.
if (abs(s(jj)) >= abs(w(j))) then
tan = w(j)/s(jj)
cos = p5/sqrt(p25 + p25*tan**2)
sin = cos*tan
tau = sin
else
cotan = s(jj)/w(j)
sin = p5/sqrt(p25 + p25*cotan**2)
cos = sin*cotan
tau = one
if (abs(cos)*giant > one) tau = one/cos
end if
! apply the transformation to s and reduce the spike in w.
l = jj
do i = j, m
temp = cos*s(l) + sin*w(i)
w(i) = -sin*s(l) + cos*w(i)
s(l) = temp
l = l + 1
end do
! store the information necessary to recover the
! givens rotation.
w(j) = tau
end if
! test for zero diagonal elements in the output s.
if (s(jj) == zero) Sing = .true.
jj = jj + (m - j + 1)
end do
end if
! move w back into the last column of the output s.
l = jj
do i = n, m
s(l) = w(i)
l = l + 1
end do
if (s(jj) == zero) Sing = .true.
end subroutine r1updt