subroutine lmqn (ifail, n, x, f, g, w, lw, sfun, * msglvl, maxit, maxfun, eta, stepmx, accrcy, xtol) implicit double precision (a-h,o-z) integer msglvl, n, maxfun, ifail, lw double precision x(n), g(n), w(lw), eta, xtol, stepmx, f, accrcy c c this routine solves the optimization problem c c minimize f(x) c x c c where x is a vector of n real variables. the method used is c a truncated-newton algorithm (see "newton-type minimization via c the lanczos method" by s.g. nash (siam j. numer. anal. 21 (1984), c pp. 770-778). this algorithm finds a local minimum of f(x). it does c not assume that the function f is convex (and so cannot guarantee a c global solution), but does assume that the function is bounded below. c it can solve problems having any number of variables, but it is c especially useful when the number of variables (n) is large. c c subroutine parameters: c c ierror - (integer) error code c ( 0 => normal return) c ( 2 => more than maxfun evaluations) c ( 3 => line search failed to find c ( lower point (may not be serious) c (-1 => error in input parameters) c n - (integer) number of variables c x - (real*8) vector of length at least n; on input, an initial c estimate of the solution; on output, the computed solution. c g - (real*8) vector of length at least n; on output, the final c value of the gradient c f - (real*8) on input, a rough estimate of the value of the c objective function at the solution; on output, the value c of the objective function at the solution c w - (real*8) work vector of length at least 14*n c lw - (integer) the declared dimension of w c sfun - a user-specified subroutine that computes the function c and gradient of the objective function. it must have c the calling sequence c subroutine sfun (n, x, f, g) c integer n c double precision x(n), g(n), f c c eta - severity of the linesearch c maxfun - maximum allowable number of function evaluations c xtol - desired accuracy for the solution x* c stepmx - maximum allowable step in the linesearch c accrcy - accuracy of computed function values c msglvl - determines quantity of printed output c 0 = none, 1 = one line per major iteration. c maxit - maximum number of inner iterations per step c c this routine is a truncated-newton method. c the truncated-newton method is preconditioned by a limited-memory c quasi-newton method (this preconditioning strategy is developed c in this routine) with a further diagonal scaling (see routine ndia3). c for further details on the parameters, see routine tn. c integer i, icycle, ioldg, ipk, iyk, loldg, lpk, lsr, * lwtest, lyk, lyr, nftotl, niter, nm1, numf, nwhy double precision abstol, alpha, difnew, difold, epsmch, * epsred, fkeep, fm, fnew, fold, fstop, ftest, gnorm, gsk, * gtg, gtpnew, oldf, oldgtp, one, pe, peps, pnorm, reltol, * rteps, rtleps, rtol, rtolsq, small, spe, tiny, * tnytol, toleps, xnorm, yksk, yrsr, zero logical lreset, upd1 c c the following imsl and standard functions are used c double precision dabs, ddot, dsqrt, step1, dnrm2 external sfun common /subscr/ lgv,lz1,lzk,lv,lsk,lyk,ldiagb,lsr,lyr, * loldg,lhg,lhyk,lpk,lemat,lwtest c c initialize parameters and constants c if (msglvl .ge. 1) write(*,800) call setpar(n) upd1 = .true. ireset = 0 nfeval = 0 nmodif = 0 nlincg = 0 fstop = f zero = 0.d0 one = 1.d0 nm1 = n - 1 c c within this routine the array w(loldg) is shared by w(lhyr) c lhyr = loldg c c check parameters and set constants c call chkucp(lwtest,maxfun,nwhy,n,alpha,epsmch, * eta,peps,rteps,rtol,rtolsq,stepmx,ftest, * xtol,xnorm,x,lw,small,tiny,accrcy) if (nwhy .lt. 0) go to 120 call setucr(small,nftotl,niter,n,f,fnew, * fm,gtg,oldf,sfun,g,x) fold = fnew if (msglvl .ge. 1) write(*,810) niter,nftotl,nlincg,fnew,gtg c c check for small gradient at the starting point. c ftest = one + dabs(fnew) if (gtg .lt. 1.d-4*epsmch*ftest*ftest) go to 90 c c set initial values to other parameters c icycle = nm1 toleps = rtol + rteps rtleps = rtolsq + epsmch gnorm = dsqrt(gtg) difnew = zero epsred = 5.0d-2 fkeep = fnew c c set the diagonal of the approximate hessian to unity. c idiagb = ldiagb do 10 i = 1,n w(idiagb) = one idiagb = idiagb + 1 10 continue c c ..................start of main iterative loop.......... c c compute the new search direction c modet = msglvl - 3 call modlnp(modet,w(lpk),w(lgv),w(lz1),w(lv), * w(ldiagb),w(lemat),x,g,w(lzk), * n,w,lw,niter,maxit,nfeval,nmodif, * nlincg,upd1,yksk,gsk,yrsr,lreset,sfun,.false.,ipivot, * accrcy,gtpnew,gnorm,xnorm) 20 continue call dcopy(n,g,1,w(loldg),1) pnorm = dnrm2(n,w(lpk),1) oldf = fnew oldgtp = gtpnew c c prepare to compute the step length c pe = pnorm + epsmch c c compute the absolute and relative tolerances for the linear search c reltol = rteps*(xnorm + one)/pe abstol = - epsmch*ftest/(oldgtp - epsmch) c c compute the smallest allowable spacing between points in c the linear search c tnytol = epsmch*(xnorm + one)/pe spe = stepmx/pe c c set the initial step length. c alpha = step1(fnew,fm,oldgtp,spe) c c perform the linear search c call linder(n,sfun,small,epsmch,reltol,abstol,tnytol, * eta,zero,spe,w(lpk),oldgtp,x,fnew,alpha,g,numf, * nwhy,w,lw) c fold = fnew niter = niter + 1 nftotl = nftotl + numf gtg = ddot(n,g,1,g,1) if (msglvl .ge. 1) write(*,810) niter,nftotl,nlincg,fnew,gtg if (nwhy .lt. 0) go to 120 if (nwhy .eq. 0 .or. nwhy .eq. 2) go to 30 c c the linear search has failed to find a lower point c nwhy = 3 go to 100 30 if (nwhy .le. 1) go to 40 call sfun(n,x,fnew,g) nftotl = nftotl + 1 c c terminate if more than maxfun evalutations have been made c 40 nwhy = 2 if (nftotl .gt. maxfun) go to 110 nwhy = 0 c c set up parameters used in convergence and resetting tests c difold = difnew difnew = oldf - fnew c c if this is the first iteration of a new cycle, compute the c percentage reduction factor for the resetting test. c if (icycle .ne. 1) go to 50 if (difnew .gt. 2.0d0 *difold) epsred = epsred + epsred if (difnew .lt. 5.0d-1*difold) epsred = 5.0d-1*epsred 50 continue gnorm = dsqrt(gtg) ftest = one + dabs(fnew) xnorm = dnrm2(n,x,1) c c test for convergence c if ((alpha*pnorm .lt. toleps*(one + xnorm) * .and. dabs(difnew) .lt. rtleps*ftest * .and. gtg .lt. peps*ftest*ftest) * .or. gtg .lt. 1.d-4*accrcy*ftest*ftest) go to 90 c c compute the change in the iterates and the corresponding change c in the gradients c isk = lsk ipk = lpk iyk = lyk ioldg = loldg do 60 i = 1,n w(iyk) = g(i) - w(ioldg) w(isk) = alpha*w(ipk) ipk = ipk + 1 isk = isk + 1 iyk = iyk + 1 ioldg = ioldg + 1 60 continue c c set up parameters used in updating the direction of search. c yksk = ddot(n,w(lyk),1,w(lsk),1) lreset = .false. if (icycle .eq. nm1 .or. difnew .lt. * epsred*(fkeep-fnew)) lreset = .true. if (lreset) go to 70 yrsr = ddot(n,w(lyr),1,w(lsr),1) if (yrsr .le. zero) lreset = .true. 70 continue upd1 = .false. c c compute the new search direction c modet = msglvl - 3 call modlnp(modet,w(lpk),w(lgv),w(lz1),w(lv), * w(ldiagb),w(lemat),x,g,w(lzk), * n,w,lw,niter,maxit,nfeval,nmodif, * nlincg,upd1,yksk,gsk,yrsr,lreset,sfun,.false.,ipivot, * accrcy,gtpnew,gnorm,xnorm) if (lreset) go to 80 c c store the accumulated change in the point and gradient as an c "average" direction for preconditioning. c call dxpy(n,w(lsk),1,w(lsr),1) call dxpy(n,w(lyk),1,w(lyr),1) icycle = icycle + 1 goto 20 c c reset c 80 ireset = ireset + 1 c c initialize the sum of all the changes in x. c call dcopy(n,w(lsk),1,w(lsr),1) call dcopy(n,w(lyk),1,w(lyr),1) fkeep = fnew icycle = 1 go to 20 c c ...............end of main iteration....................... c 90 ifail = 0 f = fnew go to 130 100 oldf = fnew c c local search here could be installed here c 110 f = oldf c c set ifail c 120 ifail = nwhy 130 if (msglvl .ge. 1 .and. ifail .lt. 0) then if (ifail .ne. 0) write(*,820) ifail return endif if (msglvl .ge. 1) then if (ifail .ne. 0) write(*,820) ifail write(*,830) f write(*,840) nmax = 10 if (n .lt. nmax) nmax = n write(*,850) (i,x(i),i=1,nmax) endif return 800 format(//' nit nf cg', 9x, 'f', 21x, 'gtg',//) 810 format(' ',i3,1x,i4,1x,i4,1x,1pd22.15,2x,1pd15.8) 820 format(//,' error code =', i3) 830 format(//,' optimal function value = ', 1pd22.15) 840 format(10x, 'current solution is (at most 10 components)', /, * 14x, 'i', 11x, 'x(i)') 850 format(10x, i5, 2x, 1pd22.15) end c c subroutine lmqnbc (ifail, n, x, f, g, w, lw, sfun, low, up, * ipivot, msglvl, maxit, maxfun, eta, stepmx, accrcy, xtol) implicit double precision (a-h,o-z) integer msglvl,n,maxfun,ifail,lw integer ipivot(n) double precision eta,xtol,stepmx,f,accrcy double precision x(n),g(n),w(lw),low(n),up(n) c this routine solves the optimization problem c c minimize f(x) c x c subject to low <= x <= up c c where x is a vector of n real variables. the method used is c a truncated-newton algorithm (see "newton-type minimization via c the lanczos algorithm" by s.g. nash (technical report 378, math. c the lanczos method" by s.g. nash (siam j. numer. anal. 21 (1984), c pp. 770-778). this algorithm finds a local minimum of f(x). it does c not assume that the function f is convex (and so cannot guarantee a c global solution), but does assume that the function is bounded below. c it can solve problems having any number of variables, but it is c especially useful when the number of variables (n) is large. c c subroutine parameters: c c ierror - (integer) error code c ( 0 => normal return c ( 2 => more than maxfun evaluations c ( 3 => line search failed to find lower c ( point (may not be serious) c (-1 => error in input parameters c n - (integer) number of variables c x - (real*8) vector of length at least n; on input, an initial c estimate of the solution; on output, the computed solution. c g - (real*8) vector of length at least n; on output, the final c value of the gradient c f - (real*8) on input, a rough estimate of the value of the c objective function at the solution; on output, the value c of the objective function at the solution c w - (real*8) work vector of length at least 14*n c lw - (integer) the declared dimension of w c sfun - a user-specified subroutine that computes the function c and gradient of the objective function. it must have c the calling sequence c subroutine sfun (n, x, f, g) c integer n c double precision x(n), g(n), f c low, up - (real*8) vectors of length at least n containing c the lower and upper bounds on the variables. if c there are no bounds on a particular variable, set c the bounds to -1.d38 and 1.d38, respectively. c ipivot - (integer) work vector of length at least n, used c to record which variables are at their bounds. c c eta - severity of the linesearch c maxfun - maximum allowable number of function evaluations c xtol - desired accuracy for the solution x* c stepmx - maximum allowable step in the linesearch c accrcy - accuracy of computed function values c msglvl - controls quantity of printed output c 0 = none, 1 = one line per major iteration. c maxit - maximum number of inner iterations per step c c this routine is a bounds-constrained truncated-newton method. c the truncated-newton method is preconditioned by a limited-memory c quasi-newton method (this preconditioning strategy is developed c in this routine) with a further diagonal scaling (see routine ndia3). c for further details on the parameters, see routine tnbc. c integer i, icycle, ioldg, ipk, iyk, loldg, lpk, lsr, * lwtest, lyk, lyr, nftotl, niter, nm1, numf, nwhy double precision abstol, alpha, difnew, difold, epsmch, epsred, * fkeep, flast, fm, fnew, fold, fstop, ftest, gnorm, gsk, * gtg, gtpnew, oldf, oldgtp, one, pe, peps, pnorm, reltol, * rteps, rtleps, rtol, rtolsq, small, spe, tiny, * tnytol, toleps, xnorm, yksk, yrsr, zero logical conv, lreset, upd1, newcon c c the following standard functions and system functions are used c double precision dabs, ddot, dnrm2, dsqrt, step1 external sfun common/subscr/ lgv, lz1, lzk, lv, lsk, lyk, ldiagb, lsr, lyr, * loldg, lhg, lhyk, lpk, lemat, lwtest c c check that initial x is feasible and that the bounds are consistent c call crash(n,x,ipivot,low,up,ier) if (ier .ne. 0) write(*,800) if (ier .ne. 0) return if (msglvl .ge. 1) write(*,810) c c initialize variables c call setpar(n) upd1 = .true. ireset = 0 nfeval = 0 nmodif = 0 nlincg = 0 fstop = f conv = .false. zero = 0.d0 one = 1.d0 nm1 = n - 1 c c within this routine the array w(loldg) is shared by w(lhyr) c lhyr = loldg c c check parameters and set constants c call chkucp(lwtest,maxfun,nwhy,n,alpha,epsmch, * eta,peps,rteps,rtol,rtolsq,stepmx,ftest, * xtol,xnorm,x,lw,small,tiny,accrcy) if (nwhy .lt. 0) go to 160 call setucr(small,nftotl,niter,n,f,fnew, * fm,gtg,oldf,sfun,g,x) fold = fnew flast = fnew c c test the lagrange multipliers to see if they are non-negative. c because the constraints are only lower bounds, the components c of the gradient corresponding to the active constraints are the c lagrange multipliers. afterwords, the projected gradient is formed. c do 10 i = 1,n if (ipivot(i) .eq. 2) go to 10 if (-ipivot(i)*g(i) .ge. 0.d0) go to 10 ipivot(i) = 0 10 continue call ztime(n,g,ipivot) gtg = ddot(n,g,1,g,1) if (msglvl .ge. 1) * call monit(n,x,fnew,g,niter,nftotl,nfeval,lreset,ipivot) c c check if the initial point is a local minimum. c ftest = one + dabs(fnew) if (gtg .lt. 1.d-4*epsmch*ftest*ftest) go to 130 c c set initial values to other parameters c icycle = nm1 toleps = rtol + rteps rtleps = rtolsq + epsmch gnorm = dsqrt(gtg) difnew = zero epsred = 5.0d-2 fkeep = fnew c c set the diagonal of the approximate hessian to unity. c idiagb = ldiagb do 15 i = 1,n w(idiagb) = one idiagb = idiagb + 1 15 continue c c ..................start of main iterative loop.......... c c compute the new search direction c modet = msglvl - 3 call modlnp(modet,w(lpk),w(lgv),w(lz1),w(lv), * w(ldiagb),w(lemat),x,g,w(lzk), * n,w,lw,niter,maxit,nfeval,nmodif, * nlincg,upd1,yksk,gsk,yrsr,lreset,sfun,.true.,ipivot, * accrcy,gtpnew,gnorm,xnorm) 20 continue call dcopy(n,g,1,w(loldg),1) pnorm = dnrm2(n,w(lpk),1) oldf = fnew oldgtp = gtpnew c c prepare to compute the step length c pe = pnorm + epsmch c c compute the absolute and relative tolerances for the linear search c reltol = rteps*(xnorm + one)/pe abstol = - epsmch*ftest/(oldgtp - epsmch) c c compute the smallest allowable spacing between points in c the linear search c tnytol = epsmch*(xnorm + one)/pe call stpmax(stepmx,pe,spe,n,x,w(lpk),ipivot,low,up) c c set the initial step length. c alpha = step1(fnew,fm,oldgtp,spe) c c perform the linear search c call linder(n,sfun,small,epsmch,reltol,abstol,tnytol, * eta,zero,spe,w(lpk),oldgtp,x,fnew,alpha,g,numf, * nwhy,w,lw) newcon = .false. if (dabs(alpha-spe) .gt. 1.d1*epsmch) go to 30 newcon = .true. nwhy = 0 call modz(n,x,w(lpk),ipivot,epsmch,low,up,flast,fnew) flast = fnew c 30 if (msglvl .ge. 2) write(*,820) alpha,pnorm fold = fnew niter = niter + 1 nftotl = nftotl + numf c c if required, print the details of this iteration c if (msglvl .ge. 1) * call monit(n,x,fnew,g,niter,nftotl,nfeval,lreset,ipivot) if (nwhy .lt. 0) go to 160 if (nwhy .eq. 0 .or. nwhy .eq. 2) go to 40 c c the linear search has failed to find a lower point c nwhy = 3 go to 140 40 if (nwhy .le. 1) go to 50 call sfun(n,x,fnew,g) nftotl = nftotl + 1 c c terminate if more than maxfun evaluations have been made c 50 nwhy = 2 if (nftotl .gt. maxfun) go to 150 nwhy = 0 c c set up parameters used in convergence and resetting tests c difold = difnew difnew = oldf - fnew c c if this is the first iteration of a new cycle, compute the c percentage reduction factor for the resetting test. c if (icycle .ne. 1) go to 60 if (difnew .gt. 2.d0*difold) epsred = epsred + epsred if (difnew .lt. 5.0d-1*difold) epsred = 5.0d-1*epsred 60 call dcopy(n,g,1,w(lgv),1) call ztime(n,w(lgv),ipivot) gtg = ddot(n,w(lgv),1,w(lgv),1) gnorm = dsqrt(gtg) ftest = one + dabs(fnew) xnorm = dnrm2(n,x,1) c c test for convergence c call cnvtst(conv,alpha,pnorm,toleps,xnorm,difnew,rtleps, * ftest,gtg,peps,epsmch,gtpnew,fnew,flast,g,ipivot,n,accrcy) if (conv) go to 130 call ztime(n,g,ipivot) c c compute the change in the iterates and the corresponding change c in the gradients c if (newcon) go to 90 isk = lsk ipk = lpk iyk = lyk ioldg = loldg do 70 i = 1,n w(iyk) = g(i) - w(ioldg) w(isk) = alpha*w(ipk) ipk = ipk + 1 isk = isk + 1 iyk = iyk + 1 ioldg = ioldg + 1 70 continue c c set up parameters used in updating the preconditioning strategy. c yksk = ddot(n,w(lyk),1,w(lsk),1) lreset = .false. if (icycle .eq. nm1 .or. difnew .lt. * epsred*(fkeep-fnew)) lreset = .true. if (lreset) go to 80 yrsr = ddot(n,w(lyr),1,w(lsr),1) if (yrsr .le. zero) lreset = .true. 80 continue upd1 = .false. c c compute the new search direction c 90 if (upd1 .and. msglvl .ge. 2) write(*,830) if (newcon .and. msglvl .ge. 2) write(*,840) modet = msglvl - 3 call modlnp(modet,w(lpk),w(lgv),w(lz1),w(lv), * w(ldiagb),w(lemat),x,g,w(lzk), * n,w,lw,niter,maxit,nfeval,nmodif, * nlincg,upd1,yksk,gsk,yrsr,lreset,sfun,.true.,ipivot, * accrcy,gtpnew,gnorm,xnorm) if (newcon) go to 20 if (lreset) go to 110 c c compute the accumulated step and its corresponding c gradient difference. c call dxpy(n,w(lsk),1,w(lsr),1) call dxpy(n,w(lyk),1,w(lyr),1) icycle = icycle + 1 goto 20 c c reset c 110 ireset = ireset + 1 c c initialize the sum of all the changes in x. c call dcopy(n,w(lsk),1,w(lsr),1) call dcopy(n,w(lyk),1,w(lyr),1) fkeep = fnew icycle = 1 go to 20 c c ...............end of main iteration....................... c 130 ifail = 0 f = fnew goto 170 140 oldf = fnew c c local search could be installed here c 150 f = oldf if (msglvl .ge. 1) call monit(n,x, * f,g,niter,nftotl,nfeval,ireset,ipivot) c c set ifail c 160 ifail = nwhy 170 if (msglvl .ge. 1 .and. ifail .lt. 0) then if (ifail .ne. 0) write(*,850) ifail return endif if (msglvl .ge. 1) then if (ifail .ne. 0) write(*,850) ifail write(*,860) f write(*,870) nmax = 10 if (n .lt. nmax) nmax = n write(*,880) (i,x(i),i=1,nmax) endif return 800 format(' there is no feasible point; terminating algorithm') 810 format(//' nit nf cg', 9x, 'f', 21x, 'gtg',//) 820 format(' linesearch results: alpha,pnorm',2(1pd12.4)) 830 format(' upd1 is true - trivial preconditioning') 840 format(' newcon is true - constraint added in linesearch') 850 format(//,' error code =', i3) 860 format(//,' optimal function value = ', 1pd22.15) 870 format(10x, 'current solution is (at most 10 components)', /, * 14x, 'i', 11x, 'x(i)') 880 format(10x, i5, 2x, 1pd22.15) end c c subroutine monit(n,x,f,g,niter,nftotl,nfeval,ireset,ipivot) c c print results of current iteration c implicit double precision (a-h,o-z) double precision x(n),f,g(n),gtg integer ipivot(n) c gtg = 0.d0 do 10 i = 1,n if (ipivot(i) .ne. 0) go to 10 gtg = gtg + g(i)*g(i) 10 continue write(*,800) niter,nftotl,nfeval,f,gtg return 800 format(' ',i4,1x,i4,1x,i4,1x,1pd22.15,2x,1pd15.8) end c c subroutine ztime(n,x,ipivot) implicit double precision (a-h,o-z) double precision x(n) integer ipivot(n) c c this routine multiplies the vector x by the constraint matrix z c do 10 i = 1,n if (ipivot(i) .ne. 0) x(i) = 0.d0 10 continue return end c c subroutine stpmax(stepmx,pe,spe,n,x,p,ipivot,low,up) implicit double precision (a-h,o-z) double precision low(n),up(n),x(n),p(n),stepmx,pe,spe,t integer ipivot(n) c c compute the maximum allowable step length c spe = stepmx / pe c spe is the standard (unconstrained) max step do 10 i = 1,n if (ipivot(i) .ne. 0) go to 10 if (p(i) .eq. 0.d0) go to 10 if (p(i) .gt. 0.d0) go to 5 t = low(i) - x(i) if (t .gt. spe*p(i)) spe = t / p(i) go to 10 5 t = up(i) - x(i) if (t .lt. spe*p(i)) spe = t / p(i) 10 continue return end c c subroutine modz(n,x,p,ipivot,epsmch,low,up,flast,fnew) implicit double precision (a-h,o-z) double precision x(n), p(n), epsmch, dabs, tol, low(n), up(n), * flast, fnew integer ipivot(n) c c update the constraint matrix if a new constraint is encountered c do 10 i = 1,n if (ipivot(i) .ne. 0) go to 10 if (p(i) .eq. 0.d0) go to 10 if (p(i) .gt. 0.d0) go to 5 tol = 1.d1 * epsmch * (dabs(low(i)) + 1.d0) if (x(i)-low(i) .gt. tol) go to 10 flast = fnew ipivot(i) = -1 x(i) = low(i) go to 10 5 tol = 1.d1 * epsmch * (dabs(up(i)) + 1.d0) if (up(i)-x(i) .gt. tol) go to 10 flast = fnew ipivot(i) = 1 x(i) = up(i) 10 continue return end c c subroutine cnvtst(conv,alpha,pnorm,toleps,xnorm,difnew,rtleps, * ftest,gtg,peps,epsmch,gtpnew,fnew,flast,g,ipivot,n,accrcy) implicit double precision (a-h,o-z) logical conv,ltest integer ipivot(n) double precision g(n), alpha, pnorm, toleps, xnorm, difnew, * rtleps, ftest, gtg, peps, epsmch, gtpnew, fnew, flast, one, * cmax, t, accrcy c c test for convergence c imax = 0 cmax = 0.d0 ltest = flast - fnew .le. -5.d-1*gtpnew do 10 i = 1,n if (ipivot(i) .eq. 0 .or. ipivot(i) .eq. 2) go to 10 t = -ipivot(i)*g(i) if (t .ge. 0.d0) go to 10 conv = .false. if (ltest) go to 10 if (cmax .le. t) go to 10 cmax = t imax = i 10 continue if (imax .eq. 0) go to 15 ipivot(imax) = 0 flast = fnew return 15 continue conv = .false. one = 1.d0 if ((alpha*pnorm .ge. toleps*(one + xnorm) * .or. dabs(difnew) .ge. rtleps*ftest * .or. gtg .ge. peps*ftest*ftest) * .and. gtg .ge. 1.d-4*accrcy*ftest*ftest) return conv = .true. c c for details, see gill, murray, and wright (1981, p. 308) and c fletcher (1981, p. 116). the multiplier tests (here, testing c the sign of the components of the gradient) may still need to c modified to incorporate tolerances for zero. c return end c c subroutine crash(n,x,ipivot,low,up,ier) implicit double precision (a-h,o-z) double precision x(n),low(n),up(n) integer ipivot(n) c c this initializes the constraint information, and ensures that the c initial point satisfies low <= x <= up. c the constraints are checked for consistency. c ier = 0 do 30 i = 1,n if (x(i) .lt. low(i)) x(i) = low(i) if (x(i) .gt. up(i)) x(i) = up(i) ipivot(i) = 0 if (x(i) .eq. low(i)) ipivot(i) = -1 if (x(i) .eq. up(i)) ipivot(i) = 1 if (up(i) .eq. low(i)) ipivot(i) = 2 if (low(i) .gt. up(i)) ier = -i 30 continue return end c c the vectors sk and yk, although not in the call, c are used (via their position in w) by the routine msolve. c subroutine modlnp(modet,zsol,gv,r,v,diagb,emat, * x,g,zk,n,w,lw,niter,maxit,nfeval,nmodif,nlincg, * upd1,yksk,gsk,yrsr,lreset,sfun,bounds,ipivot,accrcy, * gtp,gnorm,xnorm) implicit double precision (a-h,o-z) integer modet,n,niter,ipivot(1) double precision zsol(n),g(n),gv(n),r(n),v(n),diagb(n),w(lw) double precision emat(n),zk(n),x(n),accrcy double precision alpha,beta,delta,gsk,gtp,pr, * qold,qnew,qtest,rhsnrm,rnorm,rz,rzold,tol,vgv,yksk,yrsr double precision gnorm,xnorm double precision ddot,dnrm2 logical first,upd1,lreset,bounds external sfun c c this routine performs a preconditioned conjugate-gradient c iteration in order to solve the newton equations for a search c direction for a truncated-newton algorithm. when the value of the c quadratic model is sufficiently reduced, c the iteration is terminated. c c parameters c c modet - integer which controls amount of output c zsol - computed search direction c g - current gradient c gv,gz1,v - scratch vectors c r - residual c diagb,emat - diagonal preconditoning matrix c niter - nonlinear iteration # c feval - value of quadratic function c c ************************************************************* c initialization c ************************************************************* c c general initialization c if (modet .gt. 0) write(*,800) if (maxit .eq. 0) return first = .true. rhsnrm = gnorm tol = 1.d-12 qold = 0.d0 c c initialization for preconditioned conjugate-gradient algorithm c call initpc(diagb,emat,n,w,lw,modet, * upd1,yksk,gsk,yrsr,lreset) do 10 i = 1,n r(i) = -g(i) v(i) = 0.d0 zsol(i) = 0.d0 10 continue c c ************************************************************ c main iteration c ************************************************************ c do 30 k = 1,maxit nlincg = nlincg + 1 if (modet .gt. 1) write(*,810) k c c cg iteration to solve system of equations c if (bounds) call ztime(n,r,ipivot) call msolve(r,zk,n,w,lw,upd1,yksk,gsk, * yrsr,lreset,first) if (bounds) call ztime(n,zk,ipivot) rz = ddot(n,r,1,zk,1) if (rz/rhsnrm .lt. tol) go to 80 if (k .eq. 1) beta = 0.d0 if (k .gt. 1) beta = rz/rzold do 20 i = 1,n v(i) = zk(i) + beta*v(i) 20 continue if (bounds) call ztime(n,v,ipivot) call gtims(v,gv,n,x,g,w,lw,sfun,first,delta,accrcy,xnorm) if (bounds) call ztime(n,gv,ipivot) nfeval = nfeval + 1 vgv = ddot(n,v,1,gv,1) if (vgv/rhsnrm .lt. tol) go to 50 call ndia3(n,emat,v,gv,r,vgv,modet) c c compute linear step length c alpha = rz / vgv if (modet .ge. 1) write(*,820) alpha c c compute current solution and related vectors c call daxpy(n,alpha,v,1,zsol,1) call daxpy(n,-alpha,gv,1,r,1) c c test for convergence c gtp = ddot(n,zsol,1,g,1) pr = ddot(n,r,1,zsol,1) qnew = 5.d-1 * (gtp + pr) qtest = k * (1.d0 - qold/qnew) if (qtest .lt. 0.d0) go to 70 qold = qnew if (qtest .le. 5.d-1) go to 70 c c perform cautionary test c if (gtp .gt. 0) go to 40 rzold = rz 30 continue c c terminate algorithm c k = k-1 go to 70 c c truncate algorithm in case of an emergency c 40 if (modet .ge. -1) write(*,830) k call daxpy(n,-alpha,v,1,zsol,1) gtp = ddot(n,zsol,1,g,1) go to 90 50 continue if (modet .gt. -2) write(*,840) 60 if (k .gt. 1) go to 70 call msolve(g,zsol,n,w,lw,upd1,yksk,gsk,yrsr,lreset,first) call negvec(n,zsol) if (bounds) call ztime(n,zsol,ipivot) gtp = ddot(n,zsol,1,g,1) 70 continue if (modet .ge. -1) write(*,850) k,rnorm go to 90 80 continue if (modet .ge. -1) write(*,860) if (k .gt. 1) go to 70 call dcopy(n,g,1,zsol,1) call negvec(n,zsol) if (bounds) call ztime(n,zsol,ipivot) gtp = ddot(n,zsol,1,g,1) go to 70 c c store (or restore) diagonal preconditioning c 90 continue call dcopy(n,emat,1,diagb,1) return 800 format(' ',//,' entering modlnp') 810 format(' ',//,' ### iteration ',i2,' ###') 820 format(' alpha',1pd16.8) 830 format(' g(t)z positive at iteration ',i2, * ' - truncating method',/) 840 format(' ',10x,'hessian not positive-definite') 850 format(' ',/,8x,'modlan truncated after ',i3,' iterations', * ' rnorm = ',1pd14.6) 860 format(' preconditioning not positive-definite') end c c subroutine ndia3(n,e,v,gv,r,vgv,modet) implicit double precision (a-h,o-z) double precision e(n),v(n),gv(n),r(n),vgv,vr,ddot c c update the preconditioing matrix based on a diagonal version c of the bfgs quasi-newton update. c vr = ddot(n,v,1,r,1) do 10 i = 1,n e(i) = e(i) - r(i)*r(i)/vr + gv(i)*gv(i)/vgv if (e(i) .gt. 1.d-6) go to 10 if (modet .gt. -2) write(*,800) e(i) e(i) = 1.d0 10 continue return 800 format(' *** emat negative: ',1pd16.8) end c c service routines for optimization c subroutine negvec(n,v) implicit double precision (a-h,o-z) integer n double precision v(n) c c negative of the vector v c integer i do 10 i = 1,n v(i) = -v(i) 10 continue return end c c subroutine lsout(iloc,itest,xmin,fmin,gmin,xw,fw,gw,u,a, * b,tol,eps,scxbd,xlamda) implicit double precision (a-h,o-z) double precision xmin,fmin,gmin,xw,fw,gw,u,a,b, * tol,eps,scxbd,xlamda c c error printouts for getptc c double precision ya,yb,ybnd,yw,yu yu = xmin + u ya = a + xmin yb = b + xmin yw = xw + xmin ybnd = scxbd + xmin write(*,800) write(*,810) tol,eps write(*,820) ya,yb write(*,830) ybnd write(*,840) yw,fw,gw write(*,850) xmin,fmin,gmin write(*,860) yu write(*,870) iloc,itest return 800 format(///' output from linear search') 810 format(' tol and eps'/2d25.14) 820 format(' current upper and lower bounds'/2d25.14) 830 format(' strict upper bound'/d25.14) 840 format(' xw, fw, gw'/3d25.14) 850 format(' xmin, fmin, gmin'/3d25.14) 860 format(' new estimate'/2d25.14) 870 format(' iloc and itest'/2i3) end c c double precision function step1(fnew,fm,gtp,smax) implicit double precision (a-h,o-z) double precision fnew,fm,gtp,smax c c ******************************************************** c step1 returns the length of the initial step to be taken along the c vector p in the next linear search. c ******************************************************** c double precision alpha,d,epsmch double precision dabs,mchpr1 epsmch = mchpr1() d = dabs(fnew-fm) alpha = 1.d0 if (2.d0*d .le. (-gtp) .and. d .ge. epsmch) * alpha = -2.d0*d/gtp if (alpha .ge. smax) alpha = smax step1 = alpha return end c c double precision function mchpr1() implicit double precision (a-h,o-z) double precision x c c returns the value of epsmch, where epsmch is the smallest possible c real number such that 1.0 + epsmch .gt. 1.0 c c for vax c c mchpr1 = 1.d-17 c c for sun c c mchpr1 = 1.0842021724855d-19 c win32 mchpr1 = 2.2204460492503131d-16 return end c c subroutine chkucp(lwtest,maxfun,nwhy,n,alpha,epsmch, * eta,peps,rteps,rtol,rtolsq,stepmx,test, * xtol,xnorm,x,lw,small,tiny,accrcy) implicit double precision (a-h,o-z) integer lw,lwtest,maxfun,nwhy,n double precision accrcy,alpha,epsmch,eta,peps,rteps,rtol, * rtolsq,stepmx,test,xtol,xnorm,small,tiny double precision x(n) c c checks parameters and sets constants which are common to both c derivative and non-derivative algorithms c double precision dabs,dsqrt,mchpr1 epsmch = mchpr1() small = epsmch*epsmch tiny = small nwhy = -1 rteps = dsqrt(epsmch) rtol = xtol if (dabs(rtol) .lt. accrcy) rtol = 1.d1*rteps c c check for errors in the input parameters c if (lw .lt. lwtest * .or. n .lt. 1 .or. rtol .lt. 0.d0 .or. eta .ge. 1.d0 .or. * eta .lt. 0.d0 .or. stepmx .lt. rtol .or. * maxfun .lt. 1) return nwhy = 0 c c set constants for later c rtolsq = rtol*rtol peps = accrcy**0.6666d0 xnorm = dnrm2(n,x,1) alpha = 0.d0 test = 0.d0 return end c c subroutine setucr(small,nftotl,niter,n,f,fnew, * fm,gtg,oldf,sfun,g,x) implicit double precision (a-h,o-z) integer nftotl,niter,n double precision f,fnew,fm,gtg,oldf,small double precision g(n),x(n) external sfun c c check input parameters, compute the initial function value, set c constants for the subsequent minimization c fm = f c c compute the initial function value c call sfun(n,x,fnew,g) nftotl = 1 c c set constants for later c niter = 0 oldf = fnew gtg = ddot(n,g,1,g,1) return end c c subroutine gtims(v,gv,n,x,g,w,lw,sfun,first,delta,accrcy,xnorm) implicit double precision (a-h,o-z) double precision v(n),gv(n),dinv,delta,g(n) double precision f,x(n),w(lw),accrcy,dsqrt,xnorm logical first external sfun common/subscr/ lgv,lz1,lzk,lv,lsk,lyk,ldiagb,lsr,lyr, * lhyr,lhg,lhyk,lpk,lemat,lwtest c c this routine computes the product of the matrix g times the vector c v and stores the result in the vector gv (finite-difference version) c if (.not. first) go to 20 delta = dsqrt(accrcy)*(1.d0+xnorm) first = .false. 20 continue dinv = 1.d0/delta ihg = lhg do 30 i = 1,n w(ihg) = x(i) + delta*v(i) ihg = ihg + 1 30 continue call sfun(n,w(lhg),f,gv) do 40 i = 1,n gv(i) = (gv(i) - g(i))*dinv 40 continue return end c c subroutine msolve(g,y,n,w,lw,upd1,yksk,gsk, * yrsr,lreset,first) implicit double precision (a-h,o-z) double precision g(n),y(n),w(lw),yksk,gsk,yrsr logical upd1,lreset,first c c this routine sets upt the arrays for mslv c common/subscr/ lgv,lz1,lzk,lv,lsk,lyk,ldiagb,lsr,lyr, * lhyr,lhg,lhyk,lpk,lemat,lwtest call mslv(g,y,n,w(lsk),w(lyk),w(ldiagb),w(lsr),w(lyr),w(lhyr), * w(lhg),w(lhyk),upd1,yksk,gsk,yrsr,lreset,first) return end subroutine mslv(g,y,n,sk,yk,diagb,sr,yr,hyr,hg,hyk, * upd1,yksk,gsk,yrsr,lreset,first) implicit double precision (a-h,o-z) double precision g(n),y(n) c c this routine acts as a preconditioning step for the c linear conjugate-gradient routine. it is also the c method of computing the search direction from the c gradient for the non-linear conjugate-gradient code. c it represents a two-step self-scaled bfgs formula. c double precision ddot,yksk,gsk,yrsr,rdiagb,ykhyk,ghyk, * yksr,ykhyr,yrhyr,gsr,ghyr double precision sk(n),yk(n),diagb(n),sr(n),yr(n),hyr(n),hg(n), * hyk(n),one logical lreset,upd1,first if (upd1) go to 100 one = 1.d0 gsk = ddot(n,g,1,sk,1) if (lreset) go to 60 c c compute hg and hy where h is the inverse of the diagonals c do 57 i = 1,n rdiagb = 1.0d0/diagb(i) hg(i) = g(i)*rdiagb if (first) hyk(i) = yk(i)*rdiagb if (first) hyr(i) = yr(i)*rdiagb 57 continue if (first) yksr = ddot(n,yk,1,sr,1) if (first) ykhyr = ddot(n,yk,1,hyr,1) gsr = ddot(n,g,1,sr,1) ghyr = ddot(n,g,1,hyr,1) if (first) yrhyr = ddot(n,yr,1,hyr,1) call ssbfgs(n,one,sr,yr,hg,hyr,yrsr, * yrhyr,gsr,ghyr,hg) if (first) call ssbfgs(n,one,sr,yr,hyk,hyr,yrsr, * yrhyr,yksr,ykhyr,hyk) ykhyk = ddot(n,hyk,1,yk,1) ghyk = ddot(n,hyk,1,g,1) call ssbfgs(n,one,sk,yk,hg,hyk,yksk, * ykhyk,gsk,ghyk,y) return 60 continue c c compute gh and hy where h is the inverse of the diagonals c do 65 i = 1,n rdiagb = 1.d0/diagb(i) hg(i) = g(i)*rdiagb if (first) hyk(i) = yk(i)*rdiagb 65 continue if (first) ykhyk = ddot(n,yk,1,hyk,1) ghyk = ddot(n,g,1,hyk,1) call ssbfgs(n,one,sk,yk,hg,hyk,yksk, * ykhyk,gsk,ghyk,y) return 100 continue do 110 i = 1,n 110 y(i) = g(i) / diagb(i) return end c c subroutine ssbfgs(n,gamma,sj,yj,hjv,hjyj,yjsj,yjhyj, * vsj,vhyj,hjp1v) implicit double precision (a-h,o-z) integer n double precision gamma,yjsj,yjhyj,vsj,vhyj double precision sj(n),yj(n),hjv(n),hjyj(n),hjp1v(n) c c self-scaled bfgs c integer i double precision beta,delta delta = (1.d0 + gamma*yjhyj/yjsj)*vsj/yjsj * - gamma*vhyj/yjsj beta = -gamma*vsj/yjsj do 10 i = 1,n hjp1v(i) = gamma*hjv(i) + delta*sj(i) + beta*hjyj(i) 10 continue return end c c routines to initialize preconditioner c subroutine initpc(diagb,emat,n,w,lw,modet, * upd1,yksk,gsk,yrsr,lreset) implicit double precision (a-h,o-z) double precision diagb(n),emat(n),w(lw) double precision yksk,gsk,yrsr logical lreset,upd1 common/subscr/ lgv,lz1,lzk,lv,lsk,lyk,ldiagb,lsr,lyr, * lhyr,lhg,lhyk,lpk,lemat,lwtest call initp3(diagb,emat,n,lreset,yksk,yrsr,w(lhyk), * w(lsk),w(lyk),w(lsr),w(lyr),modet,upd1) return end subroutine initp3(diagb,emat,n,lreset,yksk,yrsr,bsk, * sk,yk,sr,yr,modet,upd1) implicit double precision (a-h,o-z) double precision diagb(n),emat(n),yksk,yrsr,bsk(n),sk(n), * yk(n),cond,sr(n),yr(n),ddot,sds,srds,yrsk,td,d1,dn logical lreset,upd1 if (upd1) go to 90 if (lreset) go to 60 do 10 i = 1,n bsk(i) = diagb(i)*sr(i) 10 continue sds = ddot(n,sr,1,bsk,1) srds = ddot(n,sk,1,bsk,1) yrsk = ddot(n,yr,1,sk,1) do 20 i = 1,n td = diagb(i) bsk(i) = td*sk(i) - bsk(i)*srds/sds+yr(i)*yrsk/yrsr emat(i) = td-td*td*sr(i)*sr(i)/sds+yr(i)*yr(i)/yrsr 20 continue sds = ddot(n,sk,1,bsk,1) do 30 i = 1,n emat(i) = emat(i) - bsk(i)*bsk(i)/sds+yk(i)*yk(i)/yksk 30 continue go to 110 60 continue do 70 i = 1,n bsk(i) = diagb(i)*sk(i) 70 continue sds = ddot(n,sk,1,bsk,1) do 80 i = 1,n td = diagb(i) emat(i) = td - td*td*sk(i)*sk(i)/sds + yk(i)*yk(i)/yksk 80 continue go to 110 90 continue call dcopy(n,diagb,1,emat,1) 110 continue if (modet .lt. 1) return d1 = emat(1) dn = emat(1) do 120 i = 1,n if (emat(i) .lt. d1) d1 = emat(i) if (emat(i) .gt. dn) dn = emat(i) 120 continue cond = dn/d1 write(*,800) d1,dn,cond 800 format(' ',//8x,'dmin =',1pd12.4,' dmax =',1pd12.4, * ' cond =',1pd12.4,/) return end c c subroutine setpar(n) implicit double precision (a-h,o-z) integer lsub(14) common/subscr/ lsub,lwtest c c set up parameters for the optimization routine c do 10 i = 1,14 lsub(i) = (i-1)*n + 1 10 continue lwtest = lsub(14) + n - 1 return end c c line search algorithms of gill and murray c subroutine linder(n,sfun,small,epsmch,reltol,abstol, * tnytol,eta,sftbnd,xbnd,p,gtp,x,f,alpha,g,nftotl, * iflag,w,lw) implicit double precision (a-h,o-z) integer n,nftotl,iflag,lw double precision small,epsmch,reltol,abstol,tnytol,eta, * sftbnd,xbnd,gtp,f,alpha double precision p(n),x(n),g(n),w(lw) c c integer i,ientry,itest,l,lg,lx,numf,itcnt double precision a,b,b1,big,e,factor,fmin,fpresn,fu, * fw,gmin,gtest1,gtest2,gu,gw,oldf,scxbnd,step, * tol,u,xmin,xw,rmu,rtsmll,ualpha logical braktd c c the following standard functions and system functions are c called within linder c double precision ddot,dsqrt external sfun c c allocate the addresses for local workspace c lx = 1 lg = lx + n lsprnt = 0 nprnt = 10000 rtsmll = dsqrt(small) big = 1.d0/small itcnt = 0 c c set the estimated relative precision in f(x). c fpresn = 10.d0*epsmch numf = 0 u = alpha fu = f fmin = f gu = gtp rmu = 1.0d-4 c c first entry sets up the initial interval of uncertainty. c ientry = 1 10 continue c c test for too many iterations c itcnt = itcnt + 1 iflag = 1 if (itcnt .gt. 20) go to 50 iflag = 0 call getptc(big,small,rtsmll,reltol,abstol,tnytol, * fpresn,eta,rmu,xbnd,u,fu,gu,xmin,fmin,gmin, * xw,fw,gw,a,b,oldf,b1,scxbnd,e,step,factor, * braktd,gtest1,gtest2,tol,ientry,itest) clsout if (lsprnt .ge. nprnt) call lsout(ientry,itest,xmin,fmin,gmin, * xw,fw,gw,u,a,b,tol,reltol,scxbnd,xbnd) c c if itest=1, the algorithm requires the function value to be c calculated. c if (itest .ne. 1) go to 30 ualpha = xmin + u l = lx do 20 i = 1,n w(l) = x(i) + ualpha*p(i) l = l + 1 20 continue call sfun(n,w(lx),fu,w(lg)) numf = numf + 1 gu = ddot(n,w(lg),1,p,1) c c the gradient vector corresponding to the best point is c overwritten if fu is less than fmin and fu is sufficiently c lower than f at the origin. c if (fu .le. fmin .and. fu .le. oldf-ualpha*gtest1) * call dcopy(n,w(lg),1,g,1) goto 10 c c if itest=2 or 3 a lower point could not be found c 30 continue nftotl = numf iflag = 1 if (itest .ne. 0) go to 50 c c if itest=0 a successful search has been made c iflag = 0 f = fmin alpha = xmin do 40 i = 1,n x(i) = x(i) + alpha*p(i) 40 continue 50 return end c c subroutine getptc(big,small,rtsmll,reltol,abstol,tnytol, * fpresn,eta,rmu,xbnd,u,fu,gu,xmin,fmin,gmin, * xw,fw,gw,a,b,oldf,b1,scxbnd,e,step,factor, * braktd,gtest1,gtest2,tol,ientry,itest) implicit double precision (a-h,o-z) logical braktd integer ientry,itest double precision big,small,rtsmll,reltol,abstol,tnytol, * fpresn,eta,rmu,xbnd,u,fu,gu,xmin,fmin,gmin, * xw,fw,gw,a,b,oldf,b1,scxbnd,e,step,factor, * gtest1,gtest2,tol,denom c c ************************************************************ c getptc, an algorithm for finding a steplength, called repeatedly by c routines which require a step length to be computed using cubic c interpolation. the parameters contain information about the interval c in which a lower point is to be found and from this getptc computes a c point at which the function can be evaluated by the calling program. c the value of the integer parameters ientry determines the path taken c through the code. c ************************************************************ c logical convrg double precision abgmin,abgw,absr,a1,chordm,chordu, * d1,d2,p,q,r,s,scale,sumsq,twotol,xmidpt double precision zero, point1,half,one,three,five,eleven c c the following standard functions and system functions are called c within getptc c double precision dabs, dsqrt c zero = 0.d0 point1 = 1.d-1 half = 5.d-1 one = 1.d0 three = 3.d0 five = 5.d0 eleven = 11.d0 c c branch to appropriate section of code depending on the c value of ientry. c goto (10,20), ientry c c ientry=1 c check input parameters c 10 itest = 2 if (u .le. zero .or. xbnd .le. tnytol .or. gu .gt. zero) * return itest = 1 if (xbnd .lt. abstol) abstol = xbnd tol = abstol twotol = tol + tol c c a and b define the interval of uncertainty, x and xw are points c with lowest and second lowest function values so far obtained. c initialize a,smin,xw at origin and corresponding values of c function and projection of the gradient along direction of search c at values for latest estimate at minimum. c a = zero xw = zero xmin = zero oldf = fu fmin = fu fw = fu gw = gu gmin = gu step = u factor = five c c the minimum has not yet been bracketed. c braktd = .false. c c set up xbnd as a bound on the step to be taken. (xbnd is not computed c explicitly but scxbnd is its scaled value.) set the upper bound c on the interval of uncertainty initially to xbnd + tol(xbnd). c scxbnd = xbnd b = scxbnd + reltol*dabs(scxbnd) + abstol e = b + b b1 = b c c compute the constants required for the two convergence criteria. c gtest1 = -rmu*gu gtest2 = -eta*gu c c set ientry to indicate that this is the first iteration c ientry = 2 go to 210 c c ientry = 2 c c update a,b,xw, and xmin c 20 if (fu .gt. fmin) go to 60 c c if function value not increased, new point becomes next c origin and other points are scaled accordingly. c chordu = oldf - (xmin + u)*gtest1 if (fu .le. chordu) go to 30 c c the new function value does not satisfy the sufficient decrease c criterion. prepare to move the upper bound to this point and c force the interpolation scheme to either bisect the interval of c uncertainty or take the linear interpolation step which estimates c the root of f(alpha)=chord(alpha). c chordm = oldf - xmin*gtest1 gu = -gmin denom = chordm-fmin if (dabs(denom) .ge. 1.d-15) go to 25 denom = 1.d-15 if (chordm-fmin .lt. 0.d0) denom = -denom 25 continue if (xmin .ne. zero) gu = gmin*(chordu-fu)/denom fu = half*u*(gmin+gu) + fmin if (fu .lt. fmin) fu = fmin go to 60 30 fw = fmin fmin = fu gw = gmin gmin = gu xmin = xmin + u a = a-u b = b-u xw = -u scxbnd = scxbnd - u if (gu .le. zero) go to 40 b = zero braktd = .true. go to 50 40 a = zero 50 tol = dabs(xmin)*reltol + abstol go to 90 c c if function value increased, origin remains unchanged c but new point may now qualify as w. c 60 if (u .lt. zero) go to 70 b = u braktd = .true. go to 80 70 a = u 80 xw = u fw = fu gw = gu 90 twotol = tol + tol xmidpt = half*(a + b) c c check termination criteria c convrg = dabs(xmidpt) .le. twotol - half*(b-a) .or. * dabs(gmin) .le. gtest2 .and. fmin .lt. oldf .and. * (dabs(xmin - xbnd) .gt. tol .or. .not. braktd) if (.not. convrg) go to 100 itest = 0 if (xmin .ne. zero) return c c if the function has not been reduced, check to see that the relative c change in f(x) is consistent with the estimate of the delta- c unimodality constant, tol. if the change in f(x) is larger than c expected, reduce the value of tol. c itest = 3 if (dabs(oldf-fw) .le. fpresn*(one + dabs(oldf))) return tol = point1*tol if (tol .lt. tnytol) return reltol = point1*reltol abstol = point1*abstol twotol = point1*twotol c c continue with the computation of a trial step length c 100 r = zero q = zero s = zero if (dabs(e) .le. tol) go to 150 c c fit cubic through xmin and xw c r = three*(fmin-fw)/xw + gmin + gw absr = dabs(r) q = absr if (gw .eq. zero .or. gmin .eq. zero) go to 140 c c compute the square root of (r*r - gmin*gw) in a way c which avoids underflow and overflow. c abgw = dabs(gw) abgmin = dabs(gmin) s = dsqrt(abgmin)*dsqrt(abgw) if ((gw/abgw)*gmin .gt. zero) go to 130 c c compute the square root of r*r + s*s. c sumsq = one p = zero if (absr .ge. s) go to 110 c c there is a possibility of overflow. c if (s .gt. rtsmll) p = s*rtsmll if (absr .ge. p) sumsq = one +(absr/s)**2 scale = s go to 120 c c there is a possibility of underflow. c 110 if (absr .gt. rtsmll) p = absr*rtsmll if (s .ge. p) sumsq = one + (s/absr)**2 scale = absr 120 sumsq = dsqrt(sumsq) q = big if (scale .lt. big/sumsq) q = scale*sumsq go to 140 c c compute the square root of r*r - s*s c 130 q = dsqrt(dabs(r+s))*dsqrt(dabs(r-s)) if (r .ge. s .or. r .le. (-s)) go to 140 r = zero q = zero go to 150 c c compute the minimum of fitted cubic c 140 if (xw .lt. zero) q = -q s = xw*(gmin - r - q) q = gw - gmin + q + q if (q .gt. zero) s = -s if (q .le. zero) q = -q r = e if (b1 .ne. step .or. braktd) e = step c c construct an artificial bound on the estimated steplength c 150 a1 = a b1 = b step = xmidpt if (braktd) go to 160 step = -factor*xw if (step .gt. scxbnd) step = scxbnd if (step .ne. scxbnd) factor = five*factor go to 170 c c if the minimum is bracketed by 0 and xw the step must lie c within (a,b). c 160 if ((a .ne. zero .or. xw .ge. zero) .and. (b .ne. zero .or. * xw .le. zero)) go to 180 c c if the minimum is not bracketed by 0 and xw the step must lie c within (a1,b1). c d1 = xw d2 = a if (a .eq. zero) d2 = b c this line might be c if (a .eq. zero) d2 = e u = - d1/d2 step = five*d2*(point1 + one/u)/eleven if (u .lt. one) step = half*d2*dsqrt(u) 170 if (step .le. zero) a1 = step if (step .gt. zero) b1 = step c c reject the step obtained by interpolation if it lies outside the c required interval or it is greater than half the step obtained c during the last-but-one iteration. c 180 if (dabs(s) .le. dabs(half*q*r) .or. * s .le. q*a1 .or. s .ge. q*b1) go to 200 c c a cubic interpolation step c step = s/q c c the function must not be evalutated too close to a or b. c if (step - a .ge. twotol .and. b - step .ge. twotol) go to 210 if (xmidpt .gt. zero) go to 190 step = -tol go to 210 190 step = tol go to 210 200 e = b-a c c if the step is too large, replace by the scaled bound (so as to c compute the new point on the boundary). c 210 if (step .lt. scxbnd) go to 220 step = scxbnd c c move sxbd to the left so that sbnd + tol(xbnd) = xbnd. c scxbnd = scxbnd - (reltol*dabs(xbnd)+abstol)/(one + reltol) 220 u = step if (dabs(step) .lt. tol .and. step .lt. zero) u = -tol if (dabs(step) .lt. tol .and. step .ge. zero) u = tol itest = 1 return end