hybrj Subroutine

public subroutine hybrj(fcn, n, x, fvec, fjac, ldfjac, xtol, maxfev, diag, mode, factor, nprint, info, nfev, njev, r, lr, qtf, wa1, wa2, wa3, wa4)

the purpose of hybrj is to find a zero of a system of n nonlinear functions in n variables by a modification of the powell hybrid method. the user must provide a subroutine which calculates the functions and the jacobian.

   fcn is the name of the user-supplied subroutine which
     calculates the functions and the jacobian. fcn must
     be declared in an external statement in the user
     calling program, and should be written as follows.

     subroutine fcn(n,x,fvec,fjac,ldfjac,iflag)
     integer n,ldfjac,iflag
     real(wp) x(n),fvec(n),fjac(ldfjac,n)
     ----------
     if iflag = 1 calculate the functions at x and
     return this vector in fvec. do not alter fjac.
     if iflag = 2 calculate the jacobian at x and
     return this matrix in fjac. do not alter fvec.
     ---------
     return
     end

     the value of iflag should not be changed by fcn unless
     the user wants to terminate execution of hybrj.
     in this case set iflag to a negative integer.

   n is a positive integer input variable set to the number
     of functions and variables.

   x is an array of length n. on input x must contain
     an initial estimate of the solution vector. on output x
     contains the final estimate of the solution vector.

   fvec is an output array of length n which contains
     the functions evaluated at the output x.

   fjac is an output n by n array which contains the
     orthogonal matrix q produced by the qr factorization
     of the final approximate jacobian.

   ldfjac is a positive integer input variable not less than n
     which specifies the leading dimension of the array fjac.

   xtol is a nonnegative input variable. termination
     occurs when the relative error between two consecutive
     iterates is at most xtol.

   maxfev is a positive integer input variable. termination
     occurs when the number of calls to fcn with iflag = 1
     has reached maxfev.

   diag is an array of length n. if mode = 1 (see
     below), diag is internally set. if mode = 2, diag
     must contain positive entries that serve as
     multiplicative scale factors for the variables.

   mode is an integer input variable. if mode = 1, the
     variables will be scaled internally. if mode = 2,
     the scaling is specified by the input diag. other
     values of mode are equivalent to mode = 1.

   factor is a positive input variable used in determining the
     initial step bound. this bound is set to the product of
     factor and the euclidean norm of diag*x if nonzero, or else
     to factor itself. in most cases factor should lie in the
     interval (.1,100.). 100. is a generally recommended value.

   nprint is an integer input variable that enables controlled
     printing of iterates if it is positive. in this case,
     fcn is called with iflag = 0 at the beginning of the first
     iteration and every nprint iterations thereafter and
     immediately prior to return, with x and fvec available
     for printing. fvec and fjac should not be altered.
     if nprint is not positive, no special calls of fcn
     with iflag = 0 are made.

   info is an integer output variable. if the user has
     terminated execution, info is set to the (negative)
     value of iflag. see description of fcn. otherwise,
     info is set as follows.

     info = 0   improper input parameters.

     info = 1   relative error between two consecutive iterates
                is at most xtol.

     info = 2   number of calls to fcn with iflag = 1 has
                reached maxfev.

     info = 3   xtol is too small. no further improvement in
                the approximate solution x is possible.

     info = 4   iteration is not making good progress, as
                measured by the improvement from the last
                five jacobian evaluations.

     info = 5   iteration is not making good progress, as
                measured by the improvement from the last
                ten iterations.

   nfev is an integer output variable set to the number of
     calls to fcn with iflag = 1.

   njev is an integer output variable set to the number of
     calls to fcn with iflag = 2.

   r is an output array of length lr which contains the
     upper triangular matrix produced by the qr factorization
     of the final approximate jacobian, stored rowwise.

   lr is a positive integer input variable not less than
     (n*(n+1))/2.

   qtf is an output array of length n which contains
     the vector (q transpose)*fvec.

   wa1, wa2, wa3, and wa4 are work arrays of length n.

Characteristics of the algorithm

HYBRJ is a modification of the Powell hybrid method. Two of its main characteristics involve the choice of the correction as a convex combination of the Newton and scaled gradient directions and the updating of the Jacobian by the rank-1 method of Broy- den. The choice of the correction guarantees (under reasonable conditions) global convergence for starting points far from the solution and a fast rate of convergence. The Jacobian is calcu- lated at the starting point, but it is not recalculated until the rank-1 method fails to produce satisfactory progress.

References.

  • M. J. D. Powell, A Hybrid Method for Nonlinear Equations. Numerical Methods for Nonlinear Algebraic Equations, P. Rabinowitz, editor. Gordon and Breach, 1970.

Arguments

Type IntentOptional Attributes Name
procedure(fcn_hybrj) :: fcn
integer :: n
real(kind=wp) :: x(n)
real(kind=wp) :: fvec(n)
real(kind=wp) :: fjac(ldfjac,n)
integer :: ldfjac
real(kind=wp) :: xtol
integer :: maxfev
real(kind=wp) :: diag(n)
integer :: mode
real(kind=wp) :: factor
integer :: nprint
integer :: info
integer :: nfev
integer :: njev
real(kind=wp) :: r(lr)
integer :: lr
real(kind=wp) :: qtf(n)
real(kind=wp) :: wa1(n)
real(kind=wp) :: wa2(n)
real(kind=wp) :: wa3(n)
real(kind=wp) :: wa4(n)

Calls

proc~~hybrj~~CallsGraph proc~hybrj minpack_module::hybrj proc~dogleg minpack_module::dogleg proc~hybrj->proc~dogleg proc~dpmpar minpack_module::dpmpar proc~hybrj->proc~dpmpar proc~enorm minpack_module::enorm proc~hybrj->proc~enorm proc~qform minpack_module::qform proc~hybrj->proc~qform proc~qrfac minpack_module::qrfac proc~hybrj->proc~qrfac proc~r1mpyq minpack_module::r1mpyq proc~hybrj->proc~r1mpyq proc~r1updt minpack_module::r1updt proc~hybrj->proc~r1updt proc~dogleg->proc~dpmpar proc~dogleg->proc~enorm proc~qrfac->proc~dpmpar proc~qrfac->proc~enorm proc~r1updt->proc~dpmpar

Called by

proc~~hybrj~~CalledByGraph proc~hybrj minpack_module::hybrj proc~hybrj1 minpack_module::hybrj1 proc~hybrj1->proc~hybrj

Source Code

    subroutine hybrj(fcn,n,x,fvec,fjac,ldfjac,xtol,maxfev,diag,mode,  &
                   & factor,nprint,info,nfev,njev,r,lr,qtf,wa1,wa2,   &
                   & wa3,wa4)

    implicit none

    procedure(fcn_hybrj) :: fcn
    integer n , ldfjac , maxfev , mode , nprint , info , nfev , njev , lr
    real(wp) xtol , factor
    real(wp) x(n) , fvec(n) , fjac(ldfjac,n) , diag(n) , &
             r(lr) , qtf(n) , wa1(n) , wa2(n) , wa3(n) , &
             wa4(n)

      integer i , iflag , iter , j , jm1 , l , ncfail , ncsuc , nslow1 , nslow2
      integer iwa(1)
      logical jeval , sing
      real(wp) actred , delta , epsmch , fnorm , fnorm1 , &
               pnorm , prered , ratio ,&
               sum , temp , xnorm

      real(wp),parameter :: p1    = 1.0e-1_wp
      real(wp),parameter :: p5    = 5.0e-1_wp
      real(wp),parameter :: p001  = 1.0e-3_wp
      real(wp),parameter :: p0001 = 1.0e-4_wp

      epsmch = dpmpar(1)  ! the machine precision
!
      info = 0
      iflag = 0
      nfev = 0
      njev = 0
!
!     check the input parameters for errors.
!
      if ( n<=0 .or. ldfjac<n .or. xtol<zero .or. maxfev<=0 .or. &
           factor<=zero .or. lr<(n*(n+1))/2 ) goto 300
      if ( mode==2 ) then
         do j = 1 , n
            if ( diag(j)<=zero ) goto 300
         enddo
      endif
!
!     evaluate the function at the starting point
!     and calculate its norm.
!
      iflag = 1
      call fcn(n,x,fvec,fjac,ldfjac,iflag)
      nfev = 1
      if ( iflag<0 ) goto 300
      fnorm = enorm(n,fvec)
!
!     initialize iteration counter and monitors.
!
      iter = 1
      ncsuc = 0
      ncfail = 0
      nslow1 = 0
      nslow2 = 0
!
!     beginning of the outer loop.
!
 100  jeval = .true.
!
!        calculate the jacobian matrix.
!
      iflag = 2
      call fcn(n,x,fvec,fjac,ldfjac,iflag)
      njev = njev + 1
      if ( iflag<0 ) goto 300
!
!        compute the qr factorization of the jacobian.
!
      call qrfac(n,n,fjac,ldfjac,.false.,iwa,1,wa1,wa2,wa3)
!
!        on the first iteration and if mode is 1, scale according
!        to the norms of the columns of the initial jacobian.
!
      if ( iter==1 ) then
         if ( mode/=2 ) then
            do j = 1 , n
               diag(j) = wa2(j)
               if ( wa2(j)==zero ) diag(j) = one
            enddo
         endif
!
!        on the first iteration, calculate the norm of the scaled x
!        and initialize the step bound delta.
!
         do j = 1 , n
            wa3(j) = diag(j)*x(j)
         enddo
         xnorm = enorm(n,wa3)
         delta = factor*xnorm
         if ( delta==zero ) delta = factor
      endif
!
!        form (q transpose)*fvec and store in qtf.
!
      do i = 1 , n
         qtf(i) = fvec(i)
      enddo
      do j = 1 , n
         if ( fjac(j,j)/=zero ) then
            sum = zero
            do i = j , n
               sum = sum + fjac(i,j)*qtf(i)
            enddo
            temp = -sum/fjac(j,j)
            do i = j , n
               qtf(i) = qtf(i) + fjac(i,j)*temp
            enddo
         endif
      enddo
!
!        copy the triangular factor of the qr factorization into r.
!
      sing = .false.
      do j = 1 , n
         l = j
         jm1 = j - 1
         if ( jm1>=1 ) then
            do i = 1 , jm1
               r(l) = fjac(i,j)
               l = l + n - i
            enddo
         endif
         r(l) = wa1(j)
         if ( wa1(j)==zero ) sing = .true.
      enddo
!
!        accumulate the orthogonal factor in fjac.
!
      call qform(n,n,fjac,ldfjac,wa1)
!
!        rescale if necessary.
!
      if ( mode/=2 ) then
         do j = 1 , n
            diag(j) = max(diag(j),wa2(j))
         enddo
      endif
!
!        beginning of the inner loop.
!
!
!           if requested, call fcn to enable printing of iterates.
!
 200  if ( nprint>0 ) then
         iflag = 0
         if ( mod(iter-1,nprint)==0 )                                   &
            & call fcn(n,x,fvec,fjac,ldfjac,iflag)
         if ( iflag<0 ) goto 300
      endif
!
!           determine the direction p.
!
      call dogleg(n,r,lr,diag,qtf,delta,wa1,wa2,wa3)
!
!           store the direction p and x + p. calculate the norm of p.
!
      do j = 1 , n
         wa1(j) = -wa1(j)
         wa2(j) = x(j) + wa1(j)
         wa3(j) = diag(j)*wa1(j)
      enddo
      pnorm = enorm(n,wa3)
!
!           on the first iteration, adjust the initial step bound.
!
      if ( iter==1 ) delta = min(delta,pnorm)
!
!           evaluate the function at x + p and calculate its norm.
!
      iflag = 1
      call fcn(n,wa2,wa4,fjac,ldfjac,iflag)
      nfev = nfev + 1
      if ( iflag>=0 ) then
         fnorm1 = enorm(n,wa4)
!
!           compute the scaled actual reduction.
!
         actred = -one
         if ( fnorm1<fnorm ) actred = one - (fnorm1/fnorm)**2
!
!           compute the scaled predicted reduction.
!
         l = 1
         do i = 1 , n
            sum = zero
            do j = i , n
               sum = sum + r(l)*wa1(j)
               l = l + 1
            enddo
            wa3(i) = qtf(i) + sum
         enddo
         temp = enorm(n,wa3)
         prered = zero
         if ( temp<fnorm ) prered = one - (temp/fnorm)**2
!
!           compute the ratio of the actual to the predicted
!           reduction.
!
         ratio = zero
         if ( prered>zero ) ratio = actred/prered
!
!           update the step bound.
!
         if ( ratio>=p1 ) then
            ncfail = 0
            ncsuc = ncsuc + 1
            if ( ratio>=p5 .or. ncsuc>1 ) delta = max(delta,pnorm/p5)
            if ( abs(ratio-one)<=p1 ) delta = pnorm/p5
         else
            ncsuc = 0
            ncfail = ncfail + 1
            delta = p5*delta
         endif
!
!           test for successful iteration.
!
         if ( ratio>=p0001 ) then
!
!           successful iteration. update x, fvec, and their norms.
!
            do j = 1 , n
               x(j) = wa2(j)
               wa2(j) = diag(j)*x(j)
               fvec(j) = wa4(j)
            enddo
            xnorm = enorm(n,wa2)
            fnorm = fnorm1
            iter = iter + 1
         endif
!
!           determine the progress of the iteration.
!
         nslow1 = nslow1 + 1
         if ( actred>=p001 ) nslow1 = 0
         if ( jeval ) nslow2 = nslow2 + 1
         if ( actred>=p1 ) nslow2 = 0
!
!           test for convergence.
!
         if ( delta<=xtol*xnorm .or. fnorm==zero ) info = 1
         if ( info==0 ) then
!
!           tests for termination and stringent tolerances.
!
            if ( nfev>=maxfev ) info = 2
            if ( p1*max(p1*delta,pnorm)<=epsmch*xnorm ) info = 3
            if ( nslow2==5 ) info = 4
            if ( nslow1==10 ) info = 5
            if ( info==0 ) then
!
!           criterion for recalculating jacobian.
!
               if ( ncfail==2 ) goto 100
!
!           calculate the rank one modification to the jacobian
!           and update qtf if necessary.
!
               do j = 1 , n
                  sum = zero
                  do i = 1 , n
                     sum = sum + fjac(i,j)*wa4(i)
                  enddo
                  wa2(j) = (sum-wa3(j))/pnorm
                  wa1(j) = diag(j)*((diag(j)*wa1(j))/pnorm)
                  if ( ratio>=p0001 ) qtf(j) = sum
               enddo
!
!           compute the qr factorization of the updated jacobian.
!
               call r1updt(n,n,r,lr,wa1,wa2,wa3,sing)
               call r1mpyq(n,n,fjac,ldfjac,wa2,wa3)
               call r1mpyq(1,n,qtf,1,wa2,wa3)
!
!           end of the inner loop.
!
               jeval = .false.
!
!        end of the outer loop.
!
               goto 200
            endif
         endif
      endif
!
!     termination, either normal or user imposed.
!
 300  if ( iflag<0 ) info = iflag
      iflag = 0
      if ( nprint>0 ) call fcn(n,x,fvec,fjac,ldfjac,iflag)

      end subroutine hybrj