dwnlsm Subroutine

private subroutine dwnlsm(w, mdw, mme, ma, n, l, prgopt, x, rnorm, mode, ipivot, itype, wd, h, scale, z, temp, d)

This is a companion subprogram to DWNNLS. The documentation for DWNNLS has complete usage instructions.

In addition to the parameters discussed in the prologue to subroutine DWNNLS, the following work arrays are used in subroutine DWNLSM (they are passed through the calling sequence from DWNNLS for purposes of variable dimensioning). Their contents will in general be of no interest to the user.

  IPIVOT(*)
     An array of length N.  Upon completion it contains the
  pivoting information for the cols of W(*,*).

  ITYPE(*)
     An array of length M which is used to keep track
  of the classification of the equations.  ITYPE(I)=0
  denotes equation I as an equality constraint.
  ITYPE(I)=1 denotes equation I as a least squares
  equation.

  WD(*)
     An array of length N.  Upon completion it contains the
  dual solution vector.

  H(*)
     An array of length N.  Upon completion it contains the
  pivot scalars of the Householder transformations performed
  in the case KRANK<L.

  SCALE(*)
     An array of length M which is used by the subroutine
  to store the diagonal matrix of weights.
  These are used to apply the modified Givens
  transformations.

  Z(*),TEMP(*)
     Working arrays of length N.

  D(*)
     An array of length N that contains the
  column scaling for the matrix (E).
                                (A)

Revision history

  • 790701 DATE WRITTEN. Hanson, R. J., (SNLA), Haskell, K. H., (SNLA)
  • 890531 Changed all specific intrinsics to generic. (WRB)
  • 890618 Completely restructured and revised. (WRB & RWC)
  • 891214 Prologue converted to Version 4.0 format. (BAB)
  • 900315 CALLs to XERROR changed to CALLs to XERMSG. (THJ)
  • 900328 Added TYPE section. (WRB)
  • 900510 Fixed an error message. (RWC)
  • 900604 DP version created from SP version. (RWC)
  • 900911 Restriction on value of ALAMDA included. (WRB)

Arguments

Type IntentOptional Attributes Name
real(kind=wp) :: w(mdw,*)
integer :: mdw
integer :: mme
integer :: ma
integer :: n
integer :: l
real(kind=wp) :: prgopt(*)
real(kind=wp) :: x(*)
real(kind=wp) :: rnorm
integer :: mode
integer :: ipivot(*)
integer :: itype(*)
real(kind=wp) :: wd(*)
real(kind=wp) :: h(*)
real(kind=wp) :: scale(*)
real(kind=wp) :: z(*)
real(kind=wp) :: temp(*)
real(kind=wp) :: d(*)

Calls

proc~~dwnlsm~~CallsGraph proc~dwnlsm bspline_defc_module::dwnlsm proc~dasum bspline_blas_module::dasum proc~dwnlsm->proc~dasum proc~daxpy bspline_blas_module::daxpy proc~dwnlsm->proc~daxpy proc~dcopy bspline_blas_module::dcopy proc~dwnlsm->proc~dcopy proc~dh12 bspline_defc_module::dh12 proc~dwnlsm->proc~dh12 proc~dnrm2 bspline_blas_module::dnrm2 proc~dwnlsm->proc~dnrm2 proc~drotm bspline_blas_module::drotm proc~dwnlsm->proc~drotm proc~drotmg bspline_blas_module::drotmg proc~dwnlsm->proc~drotmg proc~dscal bspline_blas_module::dscal proc~dwnlsm->proc~dscal proc~dswap bspline_blas_module::dswap proc~dwnlsm->proc~dswap proc~dwnlit bspline_defc_module::dwnlit proc~dwnlsm->proc~dwnlit proc~idamax bspline_blas_module::idamax proc~dwnlsm->proc~idamax proc~dh12->proc~daxpy proc~dh12->proc~dswap proc~ddot bspline_blas_module::ddot proc~dh12->proc~ddot proc~dwnlit->proc~dcopy proc~dwnlit->proc~dh12 proc~dwnlit->proc~drotm proc~dwnlit->proc~drotmg proc~dwnlit->proc~dscal proc~dwnlit->proc~dswap proc~dwnlit->proc~idamax proc~dwnlt1 bspline_defc_module::dwnlt1 proc~dwnlit->proc~dwnlt1 proc~dwnlt2 bspline_defc_module::dwnlt2 proc~dwnlit->proc~dwnlt2 proc~dwnlt3 bspline_defc_module::dwnlt3 proc~dwnlit->proc~dwnlt3 proc~dwnlt1->proc~idamax proc~dwnlt3->proc~dswap

Called by

proc~~dwnlsm~~CalledByGraph proc~dwnlsm bspline_defc_module::dwnlsm proc~dwnnls bspline_defc_module::dwnnls proc~dwnnls->proc~dwnlsm proc~dlpdp bspline_defc_module::dlpdp proc~dlpdp->proc~dwnnls proc~dlsi bspline_defc_module::dlsi proc~dlsi->proc~dlpdp proc~dlsei bspline_defc_module::dlsei proc~dlsei->proc~dlsi proc~dfcmn bspline_defc_module::dfcmn proc~dfcmn->proc~dlsei proc~dfc bspline_defc_module::dfc proc~dfc->proc~dfcmn

Source Code

   subroutine dwnlsm (w, mdw, mme, ma, n, l, prgopt, x, rnorm, mode, &
                      ipivot, itype, wd, h, scale, z, temp, d)

   integer :: ipivot(*), itype(*), l, ma, mdw, mme, mode, n
   real(wp) :: d(*), h(*), prgopt(*), rnorm, scale(*), temp(*), &
               w(mdw,*), wd(*), x(*), z(*)

   real(wp) :: alamda, alpha, alsq, amax, blowup, bnorm, &
               dope(3), eanorm, fac, sm, sparam(5), t, tau, wmax, z2, &
               zz
   integer :: i, idope(3), imax, isol, itemp, iter, itmax, iwmax, j, &
              jcon, jp, key, krank, l1, last, link, m, me, next, niv, nlink, &
              nopt, nsoln, ntimes
   logical :: done, feasbl, hitcon, pos

   ! Set the nominal tolerance used in the code.
   tau = sqrt(drelpr)

   m = ma + mme
   me = mme
   mode = 2

   ! To process option vector
   fac = 1.0e-4_wp

   ! Set the nominal blow up factor used in the code.
   blowup = tau

   ! The nominal column scaling used in the code is
   ! the identity scaling.
   call dcopy (n, [1.0_wp], 0, d, 1)

   ! Define bound for number of options to change.
   nopt = 1000

   ! Define bound for positive value of LINK.
   nlink = 100000
   ntimes = 0
   last = 1
   link = prgopt(1)
   if (link<=0 .or. link>nlink) then
      write(*,*) 'IN DWNNLS, THE OPTION VECTOR IS UNDEFINED'
      return
   endif

   do
      if (link<=1) exit
      ntimes = ntimes + 1
      if (ntimes>nopt) then
         write(*,*) 'IN DWNNLS, THE LINKS IN THE OPTION VECTOR ARE CYCLING.'
         return
      endif

      key = prgopt(last+1)
      if (key==6 .and. prgopt(last+2)/=0.0_wp) then
         do j = 1,n
            t = dnrm2(m,w(1,j),1)
            if (t/=0.0_wp) t = 1.0_wp/t
            d(j) = t
         end do
      endif

      if (key==7) call dcopy (n, prgopt(last+2), 1, d, 1)
      if (key==8) tau = max(drelpr,prgopt(last+2))
      if (key==9) blowup = max(drelpr,prgopt(last+2))

      next = prgopt(link)
      if (next<=0 .or. next>nlink) then
         write(*,*) 'IN DWNNLS, THE OPTION VECTOR IS UNDEFINED'
         return
      endif

      last = link
      link = next
   end do

   do j = 1,n
      call dscal (m, d(j), w(1,j), 1)
   end do

   ! Process option vector
   done = .false.
   iter = 0
   itmax = 3*(n-l)
   mode = 0
   nsoln = l
   l1 = min(m,l)

   ! Compute scale factor to apply to equality constraint equations.
   do j = 1,n
      wd(j) = dasum(m,w(1,j),1)
   end do

   imax = idamax(n,wd,1)
   eanorm = wd(imax)
   bnorm = dasum(m,w(1,n+1),1)
   alamda = eanorm/(drelpr*fac)

   ! On machines, such as the VAXes using D floating, with a very
   ! limited exponent range for double precision values, the previously
   ! computed value of ALAMDA may cause an overflow condition.
   ! Therefore, this code further limits the value of ALAMDA.
   alamda = min(alamda,sqrt(huge(1.0_wp)))

   ! Define scaling diagonal matrix for modified Givens usage and
   ! classify equation types.
   alsq = alamda**2
   do i = 1,m
      ! When equation I is heavily weighted ITYPE(I)=0,
      ! else ITYPE(I)=1.
      if (i<=me) then
         t = alsq
         itemp = 0
      else
         t = 1.0_wp
         itemp = 1
      endif
      scale(i) = t
      itype(i) = itemp
   end do

   ! Set the solution vector X(*) to zero and the column interchange
   ! matrix to the identity.
   call dcopy (n, [0.0_wp], 0, x, 1)
   do i = 1,n
      ipivot(i) = i
   end do

   ! Perform initial triangularization in the submatrix
   ! corresponding to the unconstrained variables.
   ! Set first L components of dual vector to zero because
   ! these correspond to the unconstrained variables.
   call dcopy (l, [0.0_wp], 0, wd, 1)

   ! The arrays IDOPE(*) and DOPE(*) are used to pass
   ! information to DWNLIT().  This was done to avoid
   ! a long calling sequence or the use of COMMON.
   idope(1) = me
   idope(2) = nsoln
   idope(3) = l1
   dope(1) = alsq
   dope(2) = eanorm
   dope(3) = tau
   call dwnlit (w, mdw, m, n, l, ipivot, itype, h, scale, rnorm, &
                idope, dope, done)
   me    = idope(1)
   krank = idope(2)
   niv   = idope(3)

   main : do

      ! Perform WNNLS algorithm using the following steps.
      !
      ! Until(DONE)
      !    compute search direction and feasible point
      !    when (HITCON) add constraints
      !    else perform multiplier test and drop a constraint
      !    fin
      ! Compute-Final-Solution
      !
      ! To compute search direction and feasible point,
      ! solve the triangular system of currently non-active
      ! variables and store the solution in Z(*).
      !
      ! To solve system
      ! Copy right hand side into TEMP vector to use overwriting method.

      if (done) exit main
      isol = l + 1
      if (nsoln>=isol) then
         call dcopy (niv, w(1,n+1), 1, temp, 1)
         do j = nsoln,isol,-1
            if (j>krank) then
               i = niv - nsoln + j
            else
               i = j
            endif
            if (j>krank .and. j<=l) then
               z(j) = 0.0_wp
            else
               z(j) = temp(i)/w(i,j)
               call daxpy (i-1, -z(j), w(1,j), 1, temp, 1)
            endif
         end do
      endif

      ! Increment iteration counter and check against maximum number
      ! of iterations.
      iter = iter + 1
      if (iter>itmax) then
         mode = 1
         done = .true.
      endif

      ! Check to see if any constraints have become active.
      ! If so, calculate an interpolation factor so that all
      ! active constraints are removed from the basis.
      alpha = 2.0_wp
      hitcon = .false.
      do j = l+1,nsoln
         zz = z(j)
         if (zz<=0.0_wp) then
            t = x(j)/(x(j)-zz)
            if (t<alpha) then
               alpha = t
               jcon = j
            endif
            hitcon = .true.
         endif
      end do

      ! Compute search direction and feasible point
      if (hitcon) then

         ! To add constraints, use computed ALPHA to interpolate between
         ! last feasible solution X(*) and current unconstrained (and
         ! infeasible) solution Z(*).
         do j = l+1,nsoln
            x(j) = x(j) + alpha*(z(j)-x(j))
         end do
         feasbl = .false.

         do
            ! Remove column JCON and shift columns JCON+1 through N to the
            ! left.  Swap column JCON into the N th position.  This achieves
            ! upper Hessenberg form for the nonactive constraints and
            ! leaves an upper Hessenberg matrix to retriangularize.
            do i = 1,m
               t = w(i,jcon)
               call dcopy (n-jcon, w(i, jcon+1), mdw, w(i, jcon), mdw)
               w(i,n) = t
            end do

            ! Update permuted index vector to reflect this shift and swap.
            itemp = ipivot(jcon)
            do i = jcon,n - 1
               ipivot(i) = ipivot(i+1)
            end do
            ipivot(n) = itemp

            ! Similarly permute X(*) vector.
            call dcopy (n-jcon, x(jcon+1), 1, x(jcon), 1)
            x(n) = 0.0_wp
            nsoln = nsoln - 1
            niv = niv - 1

            ! Retriangularize upper Hessenberg matrix after adding
            ! constraints.
            i = krank + jcon - l
            do j = jcon,nsoln
               if (itype(i)==0 .and. itype(i+1)==0) then
                  ! Zero IP1 to I in column J
                  if (w(i+1,j)/=0.0_wp) then
                     call drotmg (scale(i), scale(i+1), w(i,j), w(i+1,j), &
                                 sparam)
                     w(i+1,j) = 0.0_wp
                     call drotm (n+1-j, w(i,j+1), mdw, w(i+1,j+1), mdw, &
                                 sparam)
                  endif
               elseif (itype(i)==1 .and. itype(i+1)==1) then
                  ! Zero IP1 to I in column J
                  if (w(i+1,j)/=0.0_wp) then
                     call drotmg (scale(i), scale(i+1), w(i,j), w(i+1,j), &
                                 sparam)
                     w(i+1,j) = 0.0_wp
                     call drotm (n+1-j, w(i,j+1), mdw, w(i+1,j+1), mdw, &
                                 sparam)
                  endif
               elseif (itype(i)==1 .and. itype(i+1)==0) then
                  call dswap (n+1, w(i,1), mdw, w(i+1,1), mdw)
                  call dswap (1, scale(i), 1, scale(i+1), 1)
                  itemp = itype(i+1)
                  itype(i+1) = itype(i)
                  itype(i) = itemp
                  ! Swapped row was formerly a pivot element, so it will
                  ! be large enough to perform elimination.
                  ! Zero IP1 to I in column J.
                  if (w(i+1,j)/=0.0_wp) then
                     call drotmg (scale(i), scale(i+1), w(i,j), w(i+1,j), &
                                 sparam)
                     w(i+1,j) = 0.0_wp
                     call drotm (n+1-j, w(i,j+1), mdw, w(i+1,j+1), mdw, &
                                 sparam)
                  endif
               elseif (itype(i)==0 .and. itype(i+1)==1) then
                  if (scale(i)*w(i,j)**2/alsq>(tau*eanorm)**2) then
                     ! Zero IP1 to I in column J
                     if (w(i+1,j)/=0.0_wp) then
                        call drotmg (scale(i), scale(i+1), w(i,j), &
                                    w(i+1,j), sparam)
                        w(i+1,j) = 0.0_wp
                        call drotm (n+1-j, w(i,j+1), mdw, w(i+1,j+1), mdw, &
                                    sparam)
                     endif
                  else
                     call dswap (n+1, w(i,1), mdw, w(i+1,1), mdw)
                     call dswap (1, scale(i), 1, scale(i+1), 1)
                     itemp = itype(i+1)
                     itype(i+1) = itype(i)
                     itype(i) = itemp
                     w(i+1,j) = 0.0_wp
                  endif
               endif
               i = i + 1
            end do

            ! See if the remaining coefficients in the solution set are
            ! feasible.  They should be because of the way ALPHA was
            ! determined.  If any are infeasible, it is due to roundoff
            ! error.  Any that are non-positive will be set to zero and
            ! removed from the solution set.
            do jcon = l+1,nsoln
               if (x(jcon)<=0.0_wp) then
                  exit
               else
                  if (jcon==nsoln) feasbl = .true.
               end if
            end do
            if (feasbl) exit
         end do

      else

         ! To perform multiplier test and drop a constraint.
         call dcopy (nsoln, z, 1, x, 1)
         if (nsoln<n) call dcopy (n-nsoln, [0.0_wp], 0, x(nsoln+1), 1)

         ! Reclassify least squares equations as equalities as necessary.
         i = niv + 1
         do
            if (i>me) exit
            if (itype(i)==0) then
               i = i + 1
            else
               call dswap (n+1, w(i,1), mdw, w(me,1), mdw)
               call dswap (1, scale(i), 1, scale(me), 1)
               itemp = itype(i)
               itype(i) = itype(me)
               itype(me) = itemp
               me = me - 1
            endif
         end do

         ! Form inner product vector WD(*) of dual coefficients.
         do j = nsoln+1,n
            sm = 0.0_wp
            do i = nsoln+1,m
               sm = sm + scale(i)*w(i,j)*w(i,n+1)
            end do
            wd(j) = sm
         end do

         do
            ! Find J such that WD(J)=WMAX is maximum.  This determines
            ! that the incoming column J will reduce the residual vector
            ! and be positive.
            wmax = 0.0_wp
            iwmax = nsoln + 1
            do j = nsoln+1,n
               if (wd(j)>wmax) then
                  wmax = wd(j)
                  iwmax = j
               endif
            end do
            if (wmax<=0.0_wp) exit main

            ! Set dual coefficients to zero for incoming column.
            wd(iwmax) = 0.0_wp

            ! WMAX > 0.0_wp, so okay to move column IWMAX to solution set.
            ! Perform transformation to retriangularize, and test for near
            ! linear dependence.
            !
            ! Swap column IWMAX into NSOLN-th position to maintain upper
            ! Hessenberg form of adjacent columns, and add new column to
            ! triangular decomposition.
            nsoln = nsoln + 1
            niv = niv + 1
            if (nsoln/=iwmax) then
               call dswap (m, w(1,nsoln), 1, w(1,iwmax), 1)
               wd(iwmax) = wd(nsoln)
               wd(nsoln) = 0.0_wp
               itemp = ipivot(nsoln)
               ipivot(nsoln) = ipivot(iwmax)
               ipivot(iwmax) = itemp
            endif

            ! Reduce column NSOLN so that the matrix of nonactive constraints
            ! variables is triangular.
            do j = m,niv+1,-1
               jp = j - 1

               ! When operating near the ME line, test to see if the pivot
               ! element is near zero.  If so, use the largest element above
               ! it as the pivot.  This is to maintain the sharp interface
               ! between weighted and non-weighted rows in all cases.
               if (j==me+1) then
                  imax = me
                  amax = scale(me)*w(me,nsoln)**2
                  do jp = j - 1,niv,-1
                     t = scale(jp)*w(jp,nsoln)**2
                     if (t>amax) then
                        imax = jp
                        amax = t
                     endif
                  end do
                  jp = imax
               endif

               if (w(j,nsoln)/=0.0_wp) then
                  call drotmg (scale(jp), scale(j), w(jp,nsoln), w(j,nsoln), sparam)
                  w(j,nsoln) = 0.0_wp
                  call drotm (n+1-nsoln, w(jp,nsoln+1), mdw, w(j,nsoln+1), mdw, sparam)
               endif
            end do

            ! Solve for Z(NSOLN)=proposed new value for X(NSOLN).  Test if
            ! this is nonpositive or too large.  If this was true or if the
            ! pivot term was zero, reject the column as dependent.
            if (w(niv,nsoln)/=0.0_wp) then
               isol = niv
               z2 = w(isol,n+1)/w(isol,nsoln)
               z(nsoln) = z2
               pos = z2 > 0.0_wp
               if (z2*eanorm>=bnorm .and. pos) then
                  pos = .not. (blowup*z2*eanorm>=bnorm)
               endif

            elseif (niv<=me .and. w(me+1,nsoln)/=0.0_wp) then
               ! Try to add row ME+1 as an additional equality constraint.
               ! Check size of proposed new solution component.
               ! Reject it if it is too large.
               isol = me + 1
               if (pos) then
                  ! Swap rows ME+1 and NIV, and scale factors for these rows.
                  call dswap (n+1, w(me+1,1), mdw, w(niv,1), mdw)
                  call dswap (1, scale(me+1), 1, scale(niv), 1)
                  itemp = itype(me+1)
                  itype(me+1) = itype(niv)
                  itype(niv) = itemp
                  me = me + 1
               endif
            else
               pos = .false.
            endif

            if (.not.pos) then
               nsoln = nsoln - 1
               niv = niv - 1
            endif
            if (pos.or.done) exit
         end do

      endif

   end do main

   ! Else perform multiplier test and drop a constraint.  To compute
   ! final solution.  Solve system, store results in X(*).
   !
   ! Copy right hand side into TEMP vector to use overwriting method.
   isol = 1
   if (nsoln>=isol) then
      call dcopy (niv, w(1,n+1), 1, temp, 1)
      do j = nsoln,isol,-1
         if (j>krank) then
            i = niv - nsoln + j
         else
            i = j
         endif
         if (j>krank .and. j<=l) then
            z(j) = 0.0_wp
         else
            z(j) = temp(i)/w(i,j)
            call daxpy (i-1, -z(j), w(1,j), 1, temp, 1)
         endif
      end do
   endif

   ! Solve system.
   call dcopy (nsoln, z, 1, x, 1)

   ! Apply Householder transformations to X(*) if KRANK<L
   if (krank<l) then
      do i = 1,krank
         call dh12 (2, i, krank+1, l, w(i,1), mdw, h(i), x, 1, 1, 1)
      end do
   endif

   ! Fill in trailing zeroes for constrained variables not in solution.
   if (nsoln<n) call dcopy (n-nsoln, [0.0_wp], 0, x(nsoln+1), 1)

   ! Permute solution vector to natural order.
   do i = 1,n
      j = i
      do
         if (ipivot(j)==i) exit
         j = j + 1
      end do
      ipivot(j) = ipivot(i)
      ipivot(i) = j
      call dswap (1, x(j), 1, x(i), 1)
   end do

   ! Rescale the solution using the column scaling.
   do j = 1,n
      x(j) = x(j)*d(j)
   end do

   do i = nsoln+1,m
      t = w(i,n+1)
      if (i<=me) t = t/alamda
      t = (scale(i)*t)*t
      rnorm = rnorm + t
   end do

   rnorm = sqrt(rnorm)

   end subroutine dwnlsm