house Subroutine

private subroutine house(n, Ncor, Picor, r, Kpivot, Nparm, Aa, Beta, d, Save, b, Vec, Ihouse)

Given ncor n dimensional vectors as columns of the n by ncor matrix picor and an n dimensional vector r, this subroutine uses householder transformations to find the best least squares solution vec to the linear system of equations picor*vec = r, where vec is an ncor dimensional vector. if the rank of picor is (computationally) 0, the subroutine will return with the failure warning ihouse=1, otherwise it will return with ihouse=0. if the rank is > 0 but < ncor, then (ncor - rank) of the components of vec will be set to 0.0. the arays picor and r will not be changed by this subroutine. the subroutine will attempt up to numref iterative refinements of the solution, where the user can set numref as any nonnegative integer, but to get the most out of the iterative refinement process, the computation of the residual summ near the end of this subroutine should be done in higher precision than the other computations in the subroutine.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: n
integer, intent(in) :: Ncor
real(kind=wp) :: Picor(Nparm+1,Nparm+1)
real(kind=wp) :: r(Nparm+1)
integer :: Kpivot(Nparm+1)
integer, intent(in) :: Nparm
real(kind=wp) :: Aa(Nparm+1,Nparm+1)
real(kind=wp) :: Beta(Nparm+1)
real(kind=wp) :: d(Nparm+1)
real(kind=wp) :: Save(Nparm+1)
real(kind=wp) :: b(Nparm+1)
real(kind=wp) :: Vec(Nparm+1)
integer, intent(out) :: Ihouse

Called by

proc~~house~~CalledByGraph proc~house house proc~conenr conenr proc~conenr->proc~house proc~wolfe wolfe proc~wolfe->proc~conenr proc~conmax conmax_solver%conmax proc~conmax->proc~wolfe proc~rkcon conmax_solver%rkcon proc~conmax->proc~rkcon proc~slpcon conmax_solver%slpcon proc~conmax->proc~slpcon proc~corrct conmax_solver%corrct proc~corrct->proc~wolfe proc~rkcon->proc~wolfe proc~rkcon->proc~corrct proc~rkpar conmax_solver%rkpar proc~rkcon->proc~rkpar proc~searsl conmax_solver%searsl proc~rkcon->proc~searsl proc~rkpar->proc~wolfe proc~rkpar->proc~corrct proc~searsl->proc~corrct proc~slpcon->proc~searsl

Source Code

    subroutine house(n, Ncor, Picor, r, Kpivot, Nparm, Aa, Beta, d, Save, b, Vec, Ihouse)

        implicit none

        integer, intent(in)  :: Nparm
        integer, intent(out) :: Ihouse
        integer, intent(in)  :: n
        integer, intent(in)  :: Ncor
        integer  :: Kpivot(Nparm + 1)
        real(wp) :: Aa(Nparm + 1, Nparm + 1)
        real(wp) :: b(Nparm + 1)
        real(wp) :: Beta(Nparm + 1)
        real(wp) :: d(Nparm + 1)
        real(wp) :: Picor(Nparm + 1, Nparm + 1)
        real(wp) :: r(Nparm + 1)
        real(wp) :: Save(Nparm + 1)
        real(wp) :: Vec(Nparm + 1)

        real(wp) :: aakk, amax, sqdk, store, sum, summ, test, testt
        integer :: i, ia, icount, ii, j, jj, k, kchnge, kk, &
                   kp, krank, kt

        real(wp), parameter :: tolsq = (ten*ten*spcmn)**2
        integer, parameter :: numref = 1 !! Set numref = the limit on the number of iterative refinement steps.
        integer, parameter :: nmref1 = numref + 1
        integer, parameter :: nmref2 = numref + 2

        ! COMPUTE MACHINE AND PRECISION DEPENDENT CONSTANTS.
        Ihouse = 0

        ! SET KRANK = MIN(N,NCOR).  THIS MAY BE REDUCED LATER.
        krank = Ncor
        if (n < Ncor) krank = n
        ! INITIALLY SET KPIVOT.  AFTER ALL COLUMN INTERCHANGES ARE DONE
        ! KPIVOT(J) WILL BE THE ORIGINAL POSITION OF THE COLUMN WHERE THE
        ! JTH PIVOT WAS DONE.  THIS COLUMN WILL BE MOVED TO COLUMN J.
        do j = 1, Ncor
            Kpivot(j) = j
        end do
        ! COPY R INTO B AND PICOR INTO AA, BUT IN THE PROCESS REPLACE ANY NUMBERS
        ! WITH ABSOLUTE VALUE LESS THAN SPCMN BY ZERO TO AVOID UNDERFLOWS.
        do i = 1, n
            if (abs(r(i)) < spcmn) then
                b(i) = zero
            else
                b(i) = r(i)
            end if
        end do
        do j = 1, Ncor
            do i = 1, n
                if (abs(Picor(i, j)) < spcmn) then
                    Aa(i, j) = zero
                else
                    Aa(i, j) = Picor(i, j)
                end if
            end do
        end do
        do k = 1, Ncor
            if (k > n) exit
            d(k) = zero
            kchnge = k
            do jj = k, Ncor
                sum = zero
                do ia = k, n
                    if (abs(Aa(ia, jj)) > spcmn) sum = sum + Aa(ia, jj)*Aa(ia, jj)
                end do
                if (d(k) < sum) then
                    kchnge = jj
                    d(k) = sum
                end if
            end do

            !  KCHNGE CONTAINS THE INDEX OF THE COLUMN OF GREATEST
            !  LENGTH BETWEEN K AND NCOR (FROM POSITION K TO THE BOTTOM).
            ! IF K=1 AND D(K) < TOLSQ WE RETURN WITH THE FAILURE WARNING
            ! IHOUSE=1.
            if (k <= 1) then
                if (d(k) < tolsq) then
                    Ihouse = 1
                    return
                end if
            end if

            if (kchnge /= k) then
                !  START COLUMN INTERCHANGE.
                do i = 1, n
                    store = Aa(i, kchnge)
                    Aa(i, kchnge) = Aa(i, k)
                    Aa(i, k) = store
                end do
                kk = Kpivot(k)
                Kpivot(k) = Kpivot(kchnge)
                Kpivot(kchnge) = kk
            end if
            if (k /= 1) then
                amax = abs(d(1))
                test = (real(n - k + 1, wp)*(ten*ten*spcmn)**2)*(amax*amax)
                if (abs(d(k)) <= test) then
                    ! HERE THE LENGTH OF THE BEST OF COLUMNS K THROUGH NCOR (FROM K DOWN)
                    ! WAS TOO SMALL, AND WE REDUCE KRANK TO K-1 AND LEAVE THIS LOOP.
                    d(k) = sqrt(d(k))
                    krank = k - 1
                    exit
                end if
            end if

            ! NOW COMPUTE THE SCALAR BETA(K) AND THE N-K+1 DIMENSIONAL VECTOR
            ! GNU(K) (TO BE PLACED IN AA(K,K),...,AA(N,K)) FOR I(K) - BETA(K)*
            ! GNU(K)*(GNU(K) TRANSPOSE), WHICH IS THE ACTIVE PART OF THE
            ! HOUSEHOLDER TRANSFORMATION PH(K) = DIAG(I(K-1), ACTIVE PART).  THIS
            ! IS A SYMMETRIC ORTHOGONAL MATRIX WHICH WHEN MULTIPLIED TIMES AA WILL
            ! ZERO OUT AA(K+1,K),...,AA(N,K) AND CHANGE AA(K,K) TO -SGN(OLD
            ! AA(K,K))*SQDK, WHERE SQDK = LENGTH OF OLD (AA(K,K),...,AA(N,K)) AND
            ! WE REDEFINE THE SGN FUNCTION TO HAVE VALUE 1.0 IF ITS ARGUMENT IS
            ! 0.0.  WE WILL HAVE BETA(K) = 1.0/(SQDK**2 + ABS(OLD AA(K,K))*SQDK)
            ! AND GNU(K) = (OLD AA(K,K) + SGN(OLD AA(K,K))*SQDK, OLD AA(K+1,K),...,
            ! OLD AA(N,K)).  WE WILL ALSO REPLACE D(K) BY THE NEW AA(K,K) (WHICH
            ! WILL NOT ACTUALLY BE WRITTEN INTO AA) FOR LATER USE.
            aakk = Aa(k, k)
            sqdk = sqrt(d(k))
            if (aakk < zero) then
                Beta(k) = one/(d(k) - aakk*sqdk)
                Aa(k, k) = -sqdk + aakk
                d(k) = sqdk
            else
                Beta(k) = one/(d(k) + aakk*sqdk)
                Aa(k, k) = sqdk + aakk
                d(k) = -sqdk
            end if
            kt = k + 1
            if (k /= Ncor) then
                ! HERE K < NCOR AND WE MULTIPLY COLUMNS K+1,...,NCOR OF AA BY THE
                ! HOUSEHOLDER TRANSFORMATION PH(K), WHICH WILL CHANGE ONLY POSITIONS
                ! K THROUGH THE BOTTOM OF THESE COLUMNS.  THIS IS DONE BY, FOR J =
                ! K+1,...,NCOR, REPLACING COLUMN J (FROM K DOWN) BY COLUMN J (FROM K DOWN)
                ! - GNU(K)*(GNU(K).COLUMN J (FROM K DOWN))*BETA(K).
                do j = kt, Ncor
                    Save(j) = zero
                    do ia = k, n
                        Save(j) = Save(j) + Aa(ia, k)*Aa(ia, j)
                    end do
                    do i = k, n
                        Aa(i, j) = Aa(i, j) - Aa(i, k)*Save(j)*Beta(k)
                    end do
                end do
            end if
        end do

        do i = 1, krank
            ! IF I <= MIN(KRANK,NCOR-1), DIVIDE ROW I OF AA FROM COLUMN I+1
            ! THROUGH COLUMN NCOR BY THE NEW AA(I,I) (WHICH IS NOT ACTUALLY
            ! WRITTEN INTO AA(I,I), BUT IS STORED IN D(I)).
            ii = i + 1
            if (i == Ncor) exit
            do j = ii, Ncor
                Aa(i, j) = Aa(i, j)/d(i)
            end do
        end do

        ! NOW ALL THE DIAGONAL ELEMENTS OF AA (ALTHOUGH NOT WRITTEN IN)
        ! ARE 1.0 AND ALL OFF DIAGONAL ELEMENTS OF AA ARE LESS THAN OR
        ! EQUAL TO 1.0.

        ! INITIALIZE THE ITERATIVE REFINEMENT COUNTER ICOUNT AND ZERO OUT VEC
        ! INITIALLY.  THE VEC VALUES NOT CORRESPONDING TO THE FIRST KRANK
        ! COLUMNS (MODULO EARLIER COLUMN INTERCHANGES) WILL REMAIN AT 0.0.
        icount = 1
        do i = 1, Ncor
            Vec(i) = zero
        end do

        iteration: do

            ! PREMULTIPLY B BY THE HOUSEHOLDER TRANSFORMATIONS PH(1),...,
            ! PH(KRANK).  RECALL THAT GNU(I) IS STILL IN AA(I,I),...,AA(N,I)
            ! FOR I=1,...,KRANK.
            do i = 1, krank
                sum = zero
                do ia = i, n
                    sum = sum + Aa(ia, i)*b(ia)
                end do
                sum = sum*Beta(i)
                do j = i, n
                    b(j) = b(j) - Aa(j, i)*sum
                end do
            end do

            ! NOW ONLY USE THE FIRST KRANK TERMS OF B, AS WE CANT DO ANYTHING ABOUT
            ! THE OTHERS, WHOSE SQUARE ROOT OF SUM OF SQUARES WILL GIVE THE LEAST
            ! SQUARES DISTANCE.
            ! DIVIDE B(I) BY D(I) FOR I=1,...,KRANK AS WE DID THIS TO ROW I OF AA.
            do i = 1, krank
                b(i) = b(i)/d(i)
            end do

            ! THE PROBLEM HAS NOW BEEN REDUCED TO SOLVING (UPPER LEFT KRANK BY
            ! KRANK PART OF AA)*(FIRST KRANK TERMS OF VEC, MODULO COLUMN
            ! INTERCHANGE UNSCRAMBLING) = (FIRST KRANK TERMS OF B).  ALTHOUGH THE
            ! DIAGONAL AND BELOW DIAGONAL TERMS OF THE COEFFICIENT MATRIX HAVE NOT
            ! BEEN WRITTEN IN, THE SYSTEM IS UPPER TRIANGULAR WITH DIAGONAL ELEMENTS
            ! ALL EQUAL TO 1.0, SO WE SOLVE BY BACK SUBSTITUTION.  WE FIRST PUT
            ! THE SOLUTION TO THIS SYSTEM IN B(1),...,B(KRANK) AND SORT IT OUT
            ! LATER.  IF ICOUNT > 1 THE SOLUTION IS AN ITERATIVE CORRECTION TO
            ! VEC RATHER THAN VEC ITSELF.
            do ii = 1, krank
                i = krank + 1 - ii
                kk = i - 1
                if (i /= 1) then
                    ! HERE WE ALREADY HAVE B(I) (WHERE I  > 1) AND WE SUBTRACT AA(J,I)*
                    ! B(I) FROM B(J) FOR J = 1,...,I-1.
                    do j = 1, kk
                        b(j) = b(j) - Aa(j, i)*b(i)
                    end do
                end if
            end do

            !  TEST FOR CONVERGENCE.
            !  FIRST TEST, TOO MANY ITERATIONS.
            !  SECOND TEST, SEE IF VEC IS DECREASING.

            ! COMPUTE THE LENGTH SQUARED OF THE FIRST TOP 1 THROUGH KRANK PART OF
            ! B, WHICH WILL BE THE RESIDUAL VECTOR IF ICOUNT > 1.
            sum = zero
            do i = 1, krank
                if (abs(b(i)) > spcmn) sum = sum + b(i)*b(i)
            end do
            if (icount == 1) then
                testt = sum
            else if (sum > test/two) then
                icount = nmref2
            end if
            test = sum

            ! COMPUTE THE VEC VALUES, WHICH WILL BE ACTUAL VEC VALUES IF ICOUNT=1
            ! AND CORRECTIONS TO VEC VALUES IF ICOUNT > 1.  WE GET THESE BY
            ! UNSCRAMBLING THE B VALUES AND ADDING THEM TO THE APPROPRIATE OLD VEC
            ! VALUES (WHICH WILL BE 0.0 IF ICOUNT=1).
            do i = 1, krank
                kp = Kpivot(i)
                Vec(kp) = b(i) + Vec(kp)
            end do

            ! CALCULATE THE RESIDUAL R - ACOEF*VEC.  RECALL THAT ACOEF AND R
            ! CONTAIN THE ORIGINAL COEFFICIENT AND RIGHT SIDE ARRAYS RESPECTIVELY.
            ! TO GET THE MOST OUT OF ITERATIVE REFINEMENT THIS COMPUTATION SHOULD
            ! PROBABLY BE DONE IN HIGHER PRECISION, IN WHICH CASE IT MAY BE
            ! FRUITFUL TO ALSO SET NUMREF LARGER AT THE BEGINNING OF THIS
            ! SUBROUTINE.
            do i = 1, n
                summ = zero
                do j = 1, Ncor
                    if (abs(Picor(i, j)) >= spcmn) summ = summ + Picor(i, j)*Vec(j)
                end do
                b(i) = r(i) - summ
            end do

            !  THIRD TEST, WAS THE CORRECTION SIGNIFICANT.
            if (test >= spcmn*testt) then
                if (icount /= nmref1) then
                    if (icount < nmref2) then
                        icount = icount + 1
                        cycle iteration
                    end if
                end if
            end if

            exit iteration  ! done

        end do iteration

    end subroutine house