/* ========================================================================= ============================ CG_DESCENT ================================= ========================================================================= ________________________________________________________________ | A conjugate gradient method with guaranteed descent | | C-code Version 1.1 (October 6, 2005) | | Version 1.2 (November 14, 2005) | | Version 2.0 (September 23, 2007) | | Version 3.0 (May 18, 2008) | | William W. Hager and Hongchao Zhang | | hager@math.ufl.edu hzhang@math.ufl.edu | | Department of Mathematics | | University of Florida | | Gainesville, Florida 32611 USA | | 352-392-0281 x 244 | | | | Copyright by William W. Hager | | | | http://www.math.ufl.edu/~hager/papers/CG | |________________________________________________________________| ________________________________________________________________ |This program is free software; you can redistribute it and/or | |modify it under the terms of the GNU General Public License as | |published by the Free Software Foundation; either version 2 of | |the License, or (at your option) any later version. | |This program is distributed in the hope that it will be useful, | |but WITHOUT ANY WARRANTY; without even the implied warranty of | |MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |GNU General Public License for more details. | | | |You should have received a copy of the GNU General Public | |License along with this program; if not, write to the Free | |Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | |MA 02110-1301 USA | |________________________________________________________________|*/ #include "cg_user.h" #include "cg_descent.h" int cg_descent /* return: -2 (function value became nan) -1 (starting function value is nan) 0 (convergence tolerance satisfied) 1 (change in func <= feps*|f|) 2 (total iterations exceeded maxit) 3 (slope always negative in line search) 4 (number secant iterations exceed nsecant) 5 (search direction not a descent direction) 6 (line search fails in initial interval) 7 (line search fails during bisection) 8 (line search fails during interval update) 9 (debugger is on and the function value increases) 10 (out of memory) */ ( double *x, /* input: starting guess, output: the solution */ INT n, /* problem dimension */ cg_stats *Stat, /* structure with statistics (can be NULL) */ cg_parameter *UParm, /* user parameters, NULL = use default parameters */ double grad_tol, /* StopRule = 1: |g|_infty <= max (grad_tol, StopFac*initial |g|_infty) [default] StopRule = 0: |g|_infty <= grad_tol(1+|f|) */ double (*value) (double *, INT), /* f = value (x, n) */ void (*grad) (double *, double *, INT), /* grad (g, x, n) */ double (*valgrad) (double *, double *, INT), /* f = valgrad (g, x, n), NULL = compute value & gradient using value & grad */ double *Work /* either size 4n work array or NULL */ ) { INT n5, iter, maxit, nrestart, i ; int status, StopRule ; double delta2, eta_sq, Qk, Ck, f, ftemp, gnorm, xnorm, gnorm2, dnorm2, denom, t, t1, t2, t3, t4, t5, dphi, dphi0, alpha, talpha, yk, ykyk, ykgk, dkyk, yk1, yk2, yk3, yk4, yk5, beta, tol, *d, *g, *xtemp, *gtemp, *work ; cg_parameter *Parm, ParmStruc ; cg_com Com ; /* initialize the parameters */ if ( UParm == NULL ) { Parm = &ParmStruc ; cg_default (Parm) ; } else Parm = UParm ; Com.Parm = Parm ; if ( Parm->PrintParms ) cg_printParms (Parm) ; /* allocate work arrays */ if ( Work == NULL ) work = malloc (4*n*sizeof (double)) ; else work = Work ; if ( work == NULL ) { printf ("Insufficient memory for specified problem dimension %e\n", (double) n) ; status = 10 ; return (status) ; } Com.x = x ; Com.d = d = work ; g = d+n ; Com.xtemp = xtemp = g+n ; Com.gtemp = gtemp = xtemp+n ; Com.n = n ; /* problem dimension */ Com.nf = (INT) 0 ; /* number of function evaluations */ Com.ng = (INT) 0 ; /* number of gradient evaluations */ Com.AWolfe = Parm->AWolfe ; /* do not touch user's AWolfe */ Com.cg_value = value ; Com.cg_grad = grad ; Com.cg_valgrad = valgrad ; StopRule = Parm->StopRule ; /* the conjugate gradient algorithm is restarted every nrestart iteration */ nrestart = (INT) (((double) n)*Parm->restart_fac) ; /* abort when number of iterations reaches maxit */ if ( Parm->maxit_fac == INF ) maxit = INT_INF ; else maxit = (INT) (((double) n)*Parm->maxit_fac) ; f = ZERO ; n5 = n % 5 ; Ck = ZERO ; Qk = ZERO ; /* initial function and gradient evaluations, initial direction */ f = cg_fg (g, x, &Com) ; Com.f0 = f + f ; xnorm = ZERO ; for (i = 0; i < n5; i++) if ( xnorm < fabs (x [i]) ) xnorm = fabs (x [i]) ; for (; i < n; i += 5) { if ( xnorm < fabs (x [i] ) ) xnorm = fabs (x [i] ) ; if ( xnorm < fabs (x [i+1]) ) xnorm = fabs (x [i+1]) ; if ( xnorm < fabs (x [i+2]) ) xnorm = fabs (x [i+2]) ; if ( xnorm < fabs (x [i+3]) ) xnorm = fabs (x [i+3]) ; if ( xnorm < fabs (x [i+4]) ) xnorm = fabs (x [i+4]) ; } gnorm = ZERO ; gnorm2 = ZERO ; for (i = 0; i < n5; i++) { t = g [i] ; d [i] = -t ; gnorm2 += t*t ; if ( gnorm < fabs (t) ) gnorm = fabs (t) ; } for (; i < n;) { t1 = g [i] ; d [i] = -t1 ; if ( gnorm < fabs (t1) ) gnorm = fabs (t1) ; i++ ; t2 = g [i] ; d [i] = -t2 ; if ( gnorm < fabs (t2) ) gnorm = fabs (t2) ; i++ ; t3 = g [i] ; d [i] = -t3 ; if ( gnorm < fabs (t3) ) gnorm = fabs (t3) ; i++ ; t4 = g [i] ; d [i] = -t4 ; if ( gnorm < fabs (t4) ) gnorm = fabs (t4) ; i++ ; t5 = g [i] ; d [i] = -t5 ; if ( gnorm < fabs (t5) ) gnorm = fabs (t5) ; i++ ; gnorm2 += t1*t1 + t2*t2 + t3*t3 + t4*t4 + t5*t5 ; } /* check that starting function value is nan */ if ( f != f ) { status = -1 ; goto Exit ; } if ( Parm->StopRule ) tol = MAX (gnorm*Parm->StopFac, grad_tol) ; else tol = grad_tol ; if ( Parm->PrintLevel >= 1 ) { printf ("iter: %5i f = %14.6e gnorm = %14.6e AWolfe = %2i\n", (int) 0, f, gnorm, Com.AWolfe) ; } if ( cg_tol (f, gnorm, StopRule, tol) ) { status = 0 ; goto Exit ; } dphi0 = -gnorm2 ; delta2 = 2*Parm->delta - ONE ; eta_sq = Parm->eta*Parm->eta ; alpha = Parm->step ; if ( alpha == 0. ) { alpha = Parm->psi0*xnorm/gnorm ; if ( xnorm == ZERO ) { if ( f != ZERO ) alpha = Parm->psi0*fabs (f)/gnorm2 ; else alpha = ONE ; } } /* start the conjugate gradient iteration alpha starts as old step, ends as final step for current iteration f is function value for alpha = 0 QuadOK = TRUE means that a quadratic step was taken */ for (iter = 1; iter <= maxit; iter++) { Com.QuadOK = FALSE ; alpha = Parm->psi2*alpha ; if ( Parm->QuadStep ) { if ( f != ZERO ) t = fabs ((f-Com.f0)/f) ; else t = ONE ; if ( t > Parm->QuadCutOff ) /* take provisional step talpha */ { talpha = Parm->psi1*alpha ; cg_step (xtemp, x, d, talpha, n) ; ftemp = cg_f (xtemp, &Com) ; /* provisional function value */ /* check if function value is nan */ if ( ftemp != ftemp ) /* reduce stepsize */ { for (i = 0; i < Parm->nexpand; i++) { talpha /= Parm->rho ; cg_step (xtemp, x, d, talpha, n) ; ftemp = cg_f (xtemp, &Com) ; if ( ftemp == ftemp ) break ; } if ( i == Parm->nexpand ) { status = -2 ; goto Exit ; } } if ( ftemp < f ) /* check if quadstep > 0 */ { denom = 2.*(((ftemp-f)/talpha)-dphi0) ; if ( denom > ZERO ) /* try a quadratic fit step */ { Com.QuadOK = TRUE ; alpha = -dphi0*talpha/denom ; } } } } Com.f0 = f ; /* f0 saved as prior value */ if ( Parm->PrintLevel >= 1 ) { printf ("QuadOK: %2i initial a: %14.6e f0: %14.6e dphi: %14.6e\n", Com.QuadOK, alpha, Com.f0, dphi0) ; } /* parameters in Wolfe and approximate Wolfe conditions, and in update */ Qk = Parm->Qdecay*Qk + ONE ; Ck = Ck + (fabs (f) - Ck)/Qk ; /* average cost magnitude */ if ( Parm->PertRule ) Com.fpert = f + Parm->eps*Ck ; else Com.fpert = f + Parm->eps ; Com.wolfe_hi = Parm->delta*dphi0 ; Com.wolfe_lo = Parm->sigma*dphi0 ; Com.awolfe_hi = delta2*dphi0 ; Com.alpha = alpha ; /* either prior step or quadratic fit step */ Com.f = f ; if ( Com.AWolfe ) status = cg_line (dphi0, &Com) ; /* approx. Wolfe */ else status = cg_lineW (dphi0, &Com) ;/* ordinary Wolfe */ if ( (status > 0) && !Com.AWolfe )/*try approximate Wolfe line search*/ { if ( Parm->PrintLevel >= 1 ) { printf ("\nWOLFE LINE SEARCH FAILS\n") ; } Com.AWolfe = TRUE ; status = cg_line (dphi0, &Com) ; } alpha = Com.alpha ; f = Com.f ; dphi = Com.df ; if ( status ) goto Exit ; /*Test for convergence to within machine epsilon [set feps to zero to remove this test] */ if ( -alpha*dphi0 <= Parm->feps*fabs (f) ) { status = 1 ; goto Exit ; } /* compute beta, yk2, gnorm, gnorm2, dnorm2, update x and g */ if ( iter % nrestart != 0 ) { cg_copy (x, xtemp, n) ; dnorm2 = ZERO ; for (i = 0; i < n5; i++) dnorm2 = dnorm2 + d [i]*d [i] ; for (; i < n; i += 5) { dnorm2 = dnorm2 + d [i]*d [i] + d [i+1]*d [i+1] + d [i+2]*d [i+2] + d [i+3]*d [i+3] + d [i+4]*d [i+4] ; } gnorm = ZERO ; ykyk = ZERO ; ykgk = ZERO ; for (i = 0; i < n5; i++) { t = gtemp [i] ; if ( gnorm < fabs (t) ) gnorm = fabs (t) ; yk = t - g [i] ; g [i] = t ; ykgk += yk*t ; ykyk += yk*yk ; } for (; i < n; ) { t1 = gtemp [i] ; yk1 = t1 - g [i] ; g [i] = t1 ; if ( gnorm < fabs (t1) ) gnorm = fabs (t1) ; i++ ; t2 = gtemp [i] ; yk2 = t2 - g [i] ; g [i] = t2 ; if ( gnorm < fabs (t2) ) gnorm = fabs (t2) ; i++ ; t3 = gtemp [i] ; yk3 = t3 - g [i] ; g [i] = t3 ; if ( gnorm < fabs (t3) ) gnorm = fabs (t3) ; i++ ; t4 = gtemp [i] ; yk4 = t4 - g [i] ; g [i] = t4 ; if ( gnorm < fabs (t4) ) gnorm = fabs (t4) ; i++ ; t5 = gtemp [i] ; yk5 = t5 - g [i] ; g [i] = t5 ; if ( gnorm < fabs (t5) ) gnorm = fabs (t5) ; i++ ; ykyk += yk1*yk1 + yk2*yk2 + yk3*yk3 + yk4*yk4 + yk5*yk5 ; ykgk += yk1*t1 + yk2*t2 + yk3*t3 + yk4*t4 + yk5*t5 ; } if ( cg_tol (f, gnorm, StopRule, tol) ) { status = 0 ; goto Exit ; } dkyk = dphi - dphi0 ; beta = (ykgk - 2.*dphi*ykyk/dkyk)/dkyk ; /* faster: initialize dnorm2 = gnorm2 at start, then dnorm2 = gnorm2 + beta**2*dnorm2 - 2.*beta*dphi gnorm2 = ||g_{k+1}||^2 dnorm2 = ||d_{k+1}||^2 dpi = g_{k+1}' d_k */ t = -ONE/sqrt (dnorm2*MIN (eta_sq, gnorm2)) ; beta = MAX (beta, t) ; /* update search direction d = -g + beta*dold */ gnorm2 = ZERO ; for (i = 0; i < n5; i++) { t = g [i] ; d [i] = -t + beta*d [i] ; gnorm2 += t*t ; } for (; i < n; ) { t1 = g [i] ; d [i] = -t1 + beta*d [i] ; i++ ; t2 = g [i] ; d [i] = -t2 + beta*d [i] ; i++ ; t3 = g [i] ; d [i] = -t3 + beta*d [i] ; i++ ; t4 = g [i] ; d [i] = -t4 + beta*d [i] ; i++ ; t5 = g [i] ; d [i] = -t5 + beta*d [i] ; i++ ; gnorm2 += t1*t1 + t2*t2 + t3*t3 + t4*t4 + t5*t5 ; } dphi0 = -gnorm2 + beta*dphi ; if ( Parm->debug ) /* Check the dphi0 = d'g */ { t = ZERO ; for (i = 0; i < n; i++) t = t + d [i]*g [i] ; if ( fabs(t-dphi0) > Parm->debugtol*fabs(dphi0) ) { printf("Warning, dphi0 != d'g!\n"); printf("dphi0:%14.6e, d'g:%14.6e\n",dphi0, t) ; } } } else { /* search direction d = -g */ if ( Parm->PrintLevel >= 1 ) printf ("RESTART CG\n") ; gnorm = ZERO ; gnorm2 = ZERO ; cg_copy (x, xtemp, n) ; for (i = 0; i < n5; i++) { t = gtemp [i] ; g [i] = t ; d [i] = -t ; if ( gnorm < fabs (t) ) gnorm = fabs (t) ; gnorm2 += t*t ; } for (; i < n; ) { t1 = gtemp [i] ; g [i] = t1 ; d [i] = -t1 ; if ( gnorm < fabs (t1) ) gnorm = fabs (t1) ; i++ ; t2 = gtemp [i] ; g [i] = t2 ; d [i] = -t2 ; if ( gnorm < fabs (t2) ) gnorm = fabs (t2) ; i++ ; t3 = gtemp [i] ; g [i] = t3 ; d [i] = -t3 ; if ( gnorm < fabs (t3) ) gnorm = fabs (t3) ; i++ ; t4 = gtemp [i] ; g [i] = t4 ; d [i] = -t4 ; if ( gnorm < fabs (t4) ) gnorm = fabs (t4) ; i++ ; t5 = gtemp [i] ; g [i] = t5 ; d [i] = -t5 ; if ( gnorm < fabs (t5) ) gnorm = fabs (t5) ; i++ ; gnorm2 += t1*t1 + t2*t2 + t3*t3 + t4*t4 + t5*t5 ; } if ( cg_tol (f, gnorm, StopRule, tol) ) { status = 0 ; goto Exit ; } dphi0 = -gnorm2 ; } if ( !Com.AWolfe ) { if ( fabs (f-Com.f0) < Parm->AWolfeFac*Ck ) Com.AWolfe = TRUE ; } if ( Parm->PrintLevel >= 1 ) { printf ("\niter: %5i f = %14.6e gnorm = %14.6e AWolfe = %2i\n", (int) iter, f, gnorm, Com.AWolfe) ; } if ( Parm->debug ) { if ( f > Com.f0 + Parm->debugtol*Ck ) { status = 9 ; goto Exit ; } } if ( dphi0 > ZERO ) { status = 5 ; goto Exit ; } } status = 2 ; Exit: if ( Stat != NULL ) { Stat->f = f ; Stat->gnorm = gnorm ; Stat->nfunc = Com.nf ; Stat->ngrad = Com.ng ; Stat->iter = iter ; } if ( status > 2 ) { gnorm = ZERO ; for (i = 0; i < n; i++) { x [i] = xtemp [i] ; g [i] = gtemp [i] ; t = fabs (g [i]) ; gnorm = MAX (gnorm, t) ; } if ( Stat != NULL ) Stat->gnorm = gnorm ; } if ( Parm->PrintFinal || Parm->PrintLevel >= 1 ) { const char mess1 [] = "Possible causes of this error message:" ; const char mess2 [] = " - your tolerance may be too strict: " "grad_tol = " ; const char mess3 [] = "Line search fails" ; const char mess4 [] = " - your gradient routine has an error" ; const char mess5 [] = " - the parameter epsilon in cg_descent_c.parm " "is too small" ; printf ("\nTermination status: %i\n", status) ; if ( status == -2 ) { printf ("At iteration %10.0f function value became nan\n", (double) iter) ; } else if ( status == -1 ) { printf ("Objective function value is nan at starting point\n") ; } else if ( status == 0 ) { printf ("Convergence tolerance for gradient satisfied\n") ; } else if ( status == 1 ) { printf ("Terminating since change in function value " "<= feps*|f|\n") ; } else if ( status == 2 ) { printf ("Number of iterations exceed specified limit\n") ; printf ("Iterations: %10.0f maxit: %10.0f\n", (double) iter, (double) maxit) ; printf ("%s\n", mess1) ; printf ("%s %e\n", mess2, grad_tol) ; } else if ( status == 3 ) { printf ("Slope always negative in line search\n") ; printf ("%s\n", mess1) ; printf (" - your cost function has an error\n") ; printf ("%s\n", mess4) ; } else if ( status == 4 ) { printf ("Line search fails, too many secant steps\n") ; printf ("%s\n", mess1) ; printf ("%s %e\n", mess2, grad_tol) ; } else if ( status == 5 ) { printf ("Search direction not a descent direction\n") ; } else if ( status == 6 ) /* line search fails */ { printf ("%s\n", mess3) ; printf ("%s\n", mess1) ; printf ("%s %e\n", mess2, grad_tol) ; printf ("%s\n", mess4) ; printf ("%s\n", mess5) ; } else if ( status == 7 ) /* line search fails */ { printf ("%s\n", mess3) ; printf ("%s\n", mess1) ; printf ("%s %e\n", mess2, grad_tol) ; } else if ( status == 8 ) /* line search fails */ { printf ("%s\n", mess3) ; printf ("%s\n", mess1) ; printf ("%s %e\n", mess2, grad_tol) ; printf ("%s\n", mess4) ; printf ("%s\n", mess5) ; } else if ( status == 9 ) { printf ("Debugger is on, function value does not improve\n") ; printf ("new value: %25.16e old value: %25.16e\n", f, Com.f0) ; } else if ( status == 10 ) { printf ("Insufficient memory\n") ; } printf ("maximum norm for gradient: %13.6e\n", gnorm) ; printf ("function value: %13.6e\n\n", f) ; printf ("cg iterations: %10.0f\n", (double) iter) ; printf ("function evaluations: %10.0f\n", (double) Com.nf) ; printf ("gradient evaluations: %10.0f\n", (double) Com.ng) ; printf ("===================================\n\n") ; } if ( Work == NULL ) free (work) ; return (status) ; } /* ========================================================================= === cg_default ========================================================== ========================================================================= Set default conjugate gradient parameter values. If the parameter argument of cg_descent is NULL, this routine is called by cg_descent automatically. If the user wishes to set parameter values, then the cg_parameter structure should be allocated in the main program. The user could call cg_default to initialize the structure, and then individual elements in the structure could be changed, before passing the structure to cg_descent. =========================================================================*/ void cg_default ( cg_parameter *Parm ) { /* T => print final function value F => no printout of final function value */ Parm->PrintFinal = TRUE ; /* Level 0 = no printing, ... , Level 3 = maximum printing */ Parm->PrintLevel = 0 ; /* T => print parameters values F => do not display parmeter values */ Parm->PrintParms = FALSE ; /* T => use approximate Wolfe line search F => use ordinary Wolfe line search, switch to approximate Wolfe when |f_k+1-f_k| < AWolfeFac*C_k, C_k = average size of cost */ Parm->AWolfe = FALSE ; Parm->AWolfeFac = 1.e-3 ; /* factor in [0, 1] used to compute average cost magnitude C_k as follows: Q_k = 1 + (Qdecay)Q_k-1, Q_0 = 0, C_k = C_k-1 + (|f_k| - C_k-1)/Q_k */ Parm->Qdecay = .7 ; /* Stop Rules: T => ||grad||_infty <= max(grad_tol, initial |grad|_infty*StopFact) F => ||grad||_infty <= grad_tol*(1 + |f_k|) */ Parm->StopRule = TRUE ; Parm->StopFac = 0.e-12 ; /* T => estimated error in function value is eps*Ck, F => estimated error in function value is eps */ Parm->PertRule = TRUE ; Parm->eps = 1.e-6 ; /* T => attempt quadratic interpolation in line search when |f_k+1 - f_k|/f_k <= QuadCutoff F => no quadratic interpolation step */ Parm->QuadStep = TRUE ; Parm->QuadCutOff = 1.e-12 ; /* T => check that f_k+1 - f_k <= debugtol*C_k F => no checking of function values */ Parm->debug = FALSE ; Parm->debugtol = 1.e-10 ; /* if step is nonzero, it is the initial step of the initial line search */ Parm->step = ZERO ; /* abort cg after maxit_fac*n iterations */ Parm->maxit_fac = INF ; /* maximum number of times the bracketing interval grows or shrinks in the line search is nexpand */ Parm->nexpand = (int) 50 ; /* maximum number of secant iterations in line search is nsecant */ Parm->nsecant = (int) 50 ; /* conjugate gradient method restarts after (n*restart_fac) iterations */ Parm->restart_fac = ONE ; /* stop when -alpha*dphi0 (estimated change in function value) <= feps*|f|*/ Parm->feps = ZERO ; /* after encountering nan, growth factor when searching for a bracketing interval */ Parm->nan_rho = 1.3 ; /* Wolfe line search parameter, range [0, .5] phi (a) - phi (0) <= delta phi'(0) */ Parm->delta = .1 ; /* Wolfe line search parameter, range [delta, 1] phi' (a) >= sigma phi' (0) */ Parm->sigma = .9 ; /* decay factor for bracket interval width in line search, range (0, 1) */ Parm->gamma = .66 ; /* growth factor in search for initial bracket interval */ Parm->rho = 5. ; /* conjugate gradient parameter beta_k must be >= eta*||d_k||_2 */ Parm->eta = .01 ; /* starting guess for line search = psi0 ||x_0||_infty over ||g_0||_infty if x_0 != 0 psi0 |f(x_0)|/||g_0||_2 otherwise */ Parm->psi0 = .01 ; /* factor used in starting guess for iteration 1 */ /* for a QuadStep, function evalutated at psi1*previous step */ Parm->psi1 = .1 ; /* when starting a new cg iteration, our initial guess for the line search stepsize is psi2*previous step */ Parm->psi2 = 2. ; } /* ========================================================================= ==== cg_Wolfe =========================================================== ========================================================================= Check whether the Wolfe or the approximate Wolfe conditions are satisfied ========================================================================= */ int cg_Wolfe ( double alpha, /* stepsize */ double f, /* function value associated with stepsize alpha */ double dphi, /* derivative value associated with stepsize alpha */ cg_com *Com /* cg com */ ) { if ( dphi >= Com->wolfe_lo ) { /* test original Wolfe conditions */ if ( f - Com->f0 <= alpha*Com->wolfe_hi ) { if ( Com->Parm->PrintLevel >= 2 ) { printf ("wolfe f: %14.6e f0: %14.6e dphi: %14.6e\n", f, Com->f0, dphi) ; } return (1) ; } /* test approximate Wolfe conditions */ else if ( Com->AWolfe ) { if ( (f <= Com->fpert) && (dphi <= Com->awolfe_hi) ) { if ( Com->Parm->PrintLevel >= 2 ) { printf ("f: %14.6e fpert: %14.6e dphi: %14.6e awolf_hi: " "%14.6e\n", f, Com->fpert, dphi, Com->awolfe_hi) ; } return (1) ; } } } return (0) ; } /* ========================================================================= ==== cg_f =============================================================== Evaluate the function =========================================================================*/ double cg_f ( double *x, cg_com *Com ) { double f ; f = Com->cg_value (x, Com->n) ; Com->nf++ ; return (f) ; } /* ========================================================================= ==== cg_g =============================================================== Evaluate the gradient =========================================================================*/ void cg_g ( double *g, double *x, cg_com *Com ) { Com->cg_grad (g, x, Com->n) ; Com->ng++ ; } /* ========================================================================= ==== cg_fg ============================================================== Evaluate the function and gradient =========================================================================*/ double cg_fg ( double *g, double *x, cg_com *Com ) { double f ; if ( Com->cg_valgrad != NULL ) f = Com->cg_valgrad (g, x, Com->n) ; else { Com->cg_grad (g, x, Com->n) ; f = Com->cg_value (x, Com->n) ; } Com->nf++ ; Com->ng++ ; return (f) ; } /* ========================================================================= ==== cg_tol ============================================================= ========================================================================= Check for convergence ========================================================================= */ int cg_tol ( double f, /* function value associated with stepsize */ double gnorm, /* gradient sup-norm */ int StopRule, /* T => |grad|_infty <=max (tol, |grad|_infty*StopFact) F => |grad|_infty <= tol*(1+|f|)) */ double tol /* tolerance */ ) { if ( StopRule ) { if ( gnorm <= tol ) return (1) ; } else if ( gnorm <= tol*(ONE + fabs (f)) ) return (1) ; return (0) ; } /* ========================================================================= ==== cg_dot ============================================================= ========================================================================= Compute dot product of x and y, vectors of length n ========================================================================= */ double cg_dot ( double *x, /* first vector */ double *y, /* second vector */ INT n /* length of vectors */ ) { INT i, n5 ; double t ; t = 0. ; n5 = n % 5 ; for (i = 0; i < n5; i++) t += x [i]*y [i] ; for (; i < n; i += 5) { t += x [i]*y[i] + x [i+1]*y [i+1] + x [i+2]*y [i+2] + x [i+3]*y [i+3] + x [i+4]*y [i+4] ; } return (t) ; } /* ========================================================================= === cg_copy ============================================================= ========================================================================= Copy vector x into vector y ========================================================================= */ void cg_copy ( double *y, /* output of copy */ double *x, /* input of copy */ int n /* length of vectors */ ) { int j, n10 ; n10 = n % 10 ; for (j = 0; j < n10; j++) y [j] = x [j] ; for (; j < n; j += 10) { y [j] = x [j] ; y [j+1] = x [j+1] ; y [j+2] = x [j+2] ; y [j+3] = x [j+3] ; y [j+4] = x [j+4] ; y [j+5] = x [j+5] ; y [j+6] = x [j+6] ; y [j+7] = x [j+7] ; y [j+8] = x [j+8] ; y [j+9] = x [j+9] ; } } /* ========================================================================= ==== cg_step ============================================================ ========================================================================= Compute xtemp = x + alpha d ========================================================================= */ void cg_step ( double *xtemp, /*output vector */ double *x, /* initial vector */ double *d, /* search direction */ double alpha, /* stepsize */ INT n /* length of the vectors */ ) { INT n5, i ; n5 = n % 5 ; for (i = 0; i < n5; i++) xtemp [i] = x[i] + alpha*d[i] ; for (; i < n; i += 5) { xtemp [i] = x [i] + alpha*d [i] ; xtemp [i+1] = x [i+1] + alpha*d [i+1] ; xtemp [i+2] = x [i+2] + alpha*d [i+2] ; xtemp [i+3] = x [i+3] + alpha*d [i+3] ; xtemp [i+4] = x [i+4] + alpha*d [i+4] ; } } /* ========================================================================= ==== cg_line ============================================================ ========================================================================= Approximate Wolfe line search routine ========================================================================= */ int cg_line ( double dphi0, /* function derivative at starting point (alpha = 0) */ cg_com *Com /* cg com structure */ ) { INT n, iter ; int i, nsecant, nshrink, ngrow, status ; double a, dphia, b, dphib, c, alpha, phi, dphi, a0, da0, b0, db0, width, fquad, rho, *x, *xtemp, *d, *gtemp ; cg_parameter *Parm ; Parm = Com->Parm ; if ( Parm->PrintLevel >= 1 ) printf ("Approximate Wolfe line search\n") ; alpha = Com->alpha ; phi = Com->f ; n = Com->n ; x = Com->x ; /* current iterate */ xtemp = Com->xtemp ; /* x + alpha*d */ d = Com->d ; /* current search direction */ gtemp = Com->gtemp ; /* gradient at x + alpha*d */ rho = Parm->rho ; cg_step (xtemp, x, d, alpha, n) ; cg_g (gtemp, xtemp, Com) ; dphi = cg_dot (gtemp, d, n) ; /* check if gradient is nan; if so, reduce stepsize */ if ( dphi != dphi ) { for (i = 0; i < Parm->nexpand; i++) { alpha /= rho ; cg_step (xtemp, x, d, alpha, n) ; cg_g (gtemp, xtemp, Com) ; dphi = cg_dot (gtemp, d, n) ; if ( dphi == dphi ) break ; } if ( i == Parm->nexpand ) { status = -2 ; goto Exit ; } rho = Parm->nan_rho ; } /*Find initial interval [a,b] such that dphia < 0, dphib >= 0, and phia <= phi0 + feps*fabs (phi0) */ a = ZERO ; dphia = dphi0 ; ngrow = 0 ; nshrink = 0 ; while ( dphi < ZERO ) { phi = cg_f (xtemp, Com) ; /* if quadstep in effect and quadratic conditions hold, check wolfe condition*/ if ( Com->QuadOK ) { if ( ngrow == 0 ) fquad = MIN (phi, Com->f0) ; if ( phi <= fquad ) { if ( Parm->PrintLevel >= 2 ) { printf ("alpha: %14.6e phi: %14.6e fquad: %14.6e\n", alpha, phi, fquad) ; } if ( cg_Wolfe (alpha, phi, dphi, Com) ) { status = 0 ; goto Exit ; } } } if ( phi > Com->fpert ) { /* contraction phase, only break at termination or Secant step */ b = alpha ; while ( TRUE ) { alpha = .5*(a+b) ; nshrink++ ; if ( nshrink > Parm->nexpand ) { status = 6 ; goto Exit ; } cg_step (xtemp, x, d, alpha, n) ; cg_g (gtemp, xtemp, Com) ; dphi = cg_dot (gtemp, d, n) ; if ( dphi >= ZERO ) goto Secant ; phi = cg_f (xtemp, Com) ; if ( Parm->PrintLevel >= 2 ) { printf ("contract, a: %14.6e b: %14.6e alpha: %14.6e phi: " "%14.6e dphi: %14.6e\n", a, b, alpha, phi, dphi) ; } if ( Com->QuadOK && (phi <= fquad) ) { if ( cg_Wolfe (alpha, phi, dphi, Com) ) { status = 0 ; goto Exit ; } } if ( phi <= Com->fpert ) { a = alpha ; dphia = dphi ; } else { b = alpha ; } } } /* expansion phase */ a = alpha ; dphia = dphi ; ngrow++ ; if ( ngrow > Parm->nexpand ) { status = 3 ; goto Exit ; } alpha = rho*alpha ; cg_step (xtemp, x, d, alpha, n) ; cg_g (gtemp, xtemp, Com) ; dphi = cg_dot (gtemp, d, n) ; if ( Parm->PrintLevel >= 2 ) { printf ("expand, a: %14.6e alpha: %14.6e phi: " "%14.6e dphi: %14.6e\n", a, alpha, phi, dphi) ; } } Secant: b = alpha ; dphib = dphi ; if ( Com->QuadOK ) { phi = cg_f (xtemp, Com) ; if ( ngrow + nshrink == 0 ) fquad = MIN (phi, Com->f0) ; if ( phi <= fquad ) { if ( cg_Wolfe (alpha, phi, dphi, Com) ) { status = 0 ; goto Exit ; } } } nsecant = Parm->nsecant ; for (iter = 1; iter <= nsecant; iter++) { if ( Parm->PrintLevel >= 2 ) { printf ("secant, a: %14.6e b: %14.6e da: %14.6e db: %14.6e\n", a, b, dphia, dphib) ; } width = Parm->gamma*(b - a) ; if ( -dphia <= dphib ) alpha = a - (a-b)*(dphia/(dphia-dphib)) ; else alpha = b - (a-b)*(dphib/(dphia-dphib)) ; c = alpha ; a0 = a ; b0 = b ; da0 = dphia ; db0 = dphib ; status = cg_update (&a, &dphia, &b, &dphib, &alpha, &phi, &dphi, Com) ; if ( status >= 0 ) goto Exit ; else if ( status == -2 ) { if ( c == a ) { if ( dphi > da0 ) alpha = c - (c-a0)*(dphi/(dphi-da0)) ; else alpha = a ; } else { if ( dphi < db0 ) alpha = c - (c-b0)*(dphi/(dphi-db0)) ; else alpha = b ; } if ( (alpha > a) && (alpha < b) ) { if ( Parm->PrintLevel >= 2 ) printf ("2nd secant\n") ; status = cg_update (&a, &dphia, &b, &dphib, &alpha, &phi, &dphi, Com) ; if ( status >= 0 ) goto Exit ; } } /* bisection iteration */ if ( b-a >= width ) { alpha = .5*(b+a) ; if ( Parm->PrintLevel >= 2 ) printf ("bisection\n") ; status = cg_update (&a, &dphia, &b, &dphib, &alpha, &phi, &dphi, Com) ; if ( status >= 0 ) goto Exit ; } else if ( b <= a ) { status = 7 ; goto Exit ; } } status = 4 ; Exit: Com->alpha = alpha ; Com->f = phi ; Com->df = dphi ; return (status) ; } /* ========================================================================= ==== cg_lineW =========================================================== ========================================================================= Ordinary Wolfe line search routine. This routine is identical to cg_line except that the function psi [a] = phi [a] - phi [0] - a*delta*dphi [0] is minimized instead of the function phi ========================================================================= */ int cg_lineW ( double dphi0, /* function derivative at starting point (alpha = 0) */ cg_com *Com /* cg com structure */ ) { INT n, iter ; int i, nsecant, nshrink, ngrow, status ; double a, dpsia, b, dpsib, c, alpha, phi, dphi, a0, da0, b0, db0, width, fquad, rho, psi, dpsi, *x, *xtemp, *d, *gtemp ; cg_parameter *Parm ; Parm = Com->Parm ; if ( Parm->PrintLevel >= 1 ) printf ("Wolfe line search\n") ; alpha = Com->alpha ; phi = Com->f ; dphi = Com->df ; n = Com->n ; x = Com->x ; /* current iterate */ xtemp = Com->xtemp ; /* x + alpha*d */ d = Com->d ; /* current search direction */ gtemp = Com->gtemp ; /* gradient at x + alpha*d */ rho = Parm->rho ; cg_step (xtemp, x, d, alpha, n) ; cg_g (gtemp, xtemp, Com) ; dphi = cg_dot (gtemp, d, n) ; /* check if gradient is nan; if so, reduce stepsize */ if ( dphi != dphi ) { for (i = 0; i < Parm->nexpand; i++) { alpha /= rho ; cg_step (xtemp, x, d, alpha, n) ; cg_g (gtemp, xtemp, Com) ; dphi = cg_dot (gtemp, d, n) ; if ( dphi == dphi ) break ; } if ( i == Parm->nexpand ) { status = -2 ; goto Exit ; } rho = Parm->nan_rho ; } dpsi = dphi - Com->wolfe_hi ; /*Find initial interval [a,b] such that dphia < 0, dphib >= 0, and phia <= phi0 + feps*fabs (phi0) */ a = ZERO ; dpsia = dphi0 - Com->wolfe_hi ; ngrow = 0 ; nshrink = 0 ; while ( dpsi < ZERO ) { phi = cg_f (xtemp, Com) ; psi = phi - alpha*Com->wolfe_hi ; /* if quadstep in effect and quadratic conditions hold, check Wolfe condition*/ if ( Com->QuadOK ) { if ( ngrow == 0 ) fquad = MIN (phi, Com->f0) ; if ( phi <= fquad ) { if ( Parm->PrintLevel >= 2 ) { printf ("alpha: %14.6e phi: %14.6e fquad: %14.6e\n", alpha, phi, fquad) ; } if ( cg_Wolfe (alpha, phi, dphi, Com) ) { status = 0 ; goto Exit ; } } } if ( psi <= Com->fpert ) { a = alpha ; dpsia = dphi ; } else { /* contraction phase, only break at termination or Secant step */ b = alpha ; while ( TRUE ) { alpha = .5*(a+b) ; nshrink++ ; if ( nshrink > Parm->nexpand ) { status = 6 ; goto Exit ; } cg_step (xtemp, x, d, alpha, n) ; cg_g (gtemp, xtemp, Com) ; dphi = cg_dot (gtemp, d, n) ; dpsi = dphi - Com->wolfe_hi ; if ( dpsi >= ZERO ) goto Secant ; phi = cg_f (xtemp, Com) ; psi = phi - alpha*Com->wolfe_hi ; if ( Parm->PrintLevel >= 2 ) { printf ("contract, a: %14.6e b: %14.6e alpha: %14.6e phi: " "%14.6e dphi: %14.6e\n", a, b, alpha, phi, dphi) ; } if ( Com->QuadOK && (phi <= fquad) ) { if ( cg_Wolfe (alpha, phi, dphi, Com) ) { status = 0 ; goto Exit ; } } if ( psi <= Com->fpert ) { a = alpha ; dpsia = dpsi ; } else { b = alpha ; } } } /* expansion phase */ ngrow++ ; if ( ngrow > Parm->nexpand ) { status = 3 ; goto Exit ; } alpha *= rho ; cg_step (xtemp, x, d, alpha, n) ; cg_g (gtemp, xtemp, Com) ; dphi = cg_dot (gtemp, d, n) ; dpsi = dphi - Com->wolfe_hi ; if ( Parm->PrintLevel >= 2 ) { printf ("expand, a: %14.6e alpha: %14.6e phi: " "%14.6e dphi: %14.6e\n", a, alpha, phi, dphi) ; } } Secant: b = alpha ; dpsib = dpsi ; if ( Com->QuadOK ) { phi = cg_f (xtemp, Com) ; if ( ngrow + nshrink == 0 ) fquad = MIN (phi, Com->f0) ; if ( phi <= fquad ) { if ( cg_Wolfe (alpha, phi, dphi, Com) ) { status = 0 ; goto Exit ; } } } nsecant = Parm->nsecant ; for (iter = 1; iter <= nsecant; iter++) { if ( Parm->PrintLevel >= 2 ) { printf ("secant, a: %14.6e b: %14.6e da: %14.6e db: %14.6e\n", a, b, dpsia, dpsib) ; } width = Parm->gamma*(b - a) ; if ( -dpsia <= dpsib ) alpha = a - (a-b)*(dpsia/(dpsia-dpsib)) ; else alpha = b - (a-b)*(dpsib/(dpsia-dpsib)) ; c = alpha ; a0 = a ; b0 = b ; da0 = dpsia ; db0 = dpsib ; status = cg_updateW (&a, &dpsia, &b, &dpsib, &alpha, &phi, &dphi, &dpsi, Com) ; if ( status >= 0 ) goto Exit ; else if ( status == -2 ) { if ( c == a ) { if ( dpsi > da0 ) alpha = c - (c-a0)*(dpsi/(dpsi-da0)) ; else alpha = a ; } else { if ( dpsi < db0 ) alpha = c - (c-b0)*(dpsi/(dpsi-db0)) ; else alpha = b ; } if ( (alpha > a) && (alpha < b) ) { if ( Parm->PrintLevel >= 2 ) printf ("2nd secant\n") ; status = cg_updateW (&a, &dpsia, &b, &dpsib, &alpha, &phi, &dphi, &dpsi, Com) ; if ( status >= 0 ) goto Exit ; } } /* bisection iteration */ if ( b-a >= width ) { alpha = .5*(b+a) ; if ( Parm->PrintLevel >= 2 ) printf ("bisection\n") ; status = cg_updateW (&a, &dpsia, &b, &dpsib, &alpha, &phi, &dphi, &dpsi, Com) ; if ( status >= 0 ) goto Exit ; } else if ( b <= a ) { status = 7 ; goto Exit ; } } status = 4 ; Exit: Com->alpha = alpha ; Com->f = phi ; Com->df = dphi ; return (status) ; } /* ========================================================================= ==== cg_update ========================================================== ========================================================================= update returns: 8 if too many iterations 0 if Wolfe condition is satisfied -1 if interval is updated and a search is done -2 if the interval updated successfully ========================================================================= */ int cg_update ( double *a , /* left side of bracketing interval */ double *dphia , /* derivative at a */ double *b , /* right side of bracketing interval */ double *dphib , /* derivative at b */ double *alpha , /* trial step (between a and b) */ double *phi , /* function value at alpha (returned) */ double *dphi , /* function derivative at alpha (returned) */ cg_com *Com /* cg com structure */ ) { INT n ; int nshrink, status ; double *x, *xtemp, *d, *gtemp ; cg_parameter *Parm ; Parm = Com->Parm ; n = Com->n ; x = Com->x ; /* current iterate */ xtemp = Com->xtemp ; /* x + alpha*d */ d = Com->d ; /* current search direction */ gtemp = Com->gtemp ; /* gradient at x + alpha*d */ cg_step (xtemp, x, d, *alpha, n) ; *phi = cg_fg (gtemp, xtemp, Com) ; *dphi = cg_dot (gtemp, d, n) ; if ( Parm->PrintLevel >= 2 ) { printf ("update alpha: %14.6e phi: %14.6e dphi: %14.6e\n", *alpha, *phi, *dphi) ; } if ( cg_Wolfe (*alpha, *phi, *dphi, Com) ) { status = 0 ; goto Exit2 ; } status = -2 ; if ( *dphi >= ZERO ) { *b = *alpha ; *dphib = *dphi ; goto Exit2 ; } else { if ( *phi <= Com->fpert ) { *a = *alpha ; *dphia = *dphi ; goto Exit2 ; } } nshrink = 0 ; *b = *alpha ; while ( TRUE ) { *alpha = .5*(*a + *b) ; nshrink++ ; if ( nshrink > Parm->nexpand ) { status = 8 ; goto Exit2 ; } cg_step (xtemp, x, d, *alpha, n) ; *phi = cg_fg (gtemp, xtemp, Com) ; *dphi = cg_dot (gtemp, d, n) ; if ( Parm->PrintLevel >= 2 ) { printf ("contract, a: %14.6e alpha: %14.6e " "phi: %14.6e dphi: %14.6e\n", *a, *alpha, *phi, *dphi) ; } if ( cg_Wolfe (*alpha, *phi, *dphi, Com) ) { status = 0 ; goto Exit2 ; } if ( *dphi >= ZERO ) { *b = *alpha ; *dphib = *dphi ; goto Exit1 ; } if ( *phi <= Com->fpert ) { if ( Parm->PrintLevel >= 2 ) { printf ("update a: %14.6e dphia: %14.6e\n", *alpha, *dphi) ; } *a = *alpha ; *dphia = *dphi ; } else *b = *alpha ; } Exit1: status = -1 ; Exit2: if ( Parm->PrintLevel >= 2 ) { printf ("UP a: %14.6e b: %14.6e da: %14.6e db: %14.6e status: %i\n", *a, *b, *dphia, *dphib, status) ; } return (status) ; } /* ========================================================================= ==== cg_updateW ========================================================= ========================================================================= This routine is identical to cg_update except that the function psi [a] = phi [a] - phi [0] - a*delta*dphi [0] is minimized instead of the function phi. The return int has the following meaning: 8 if too many iterations 0 if Wolfe condition is satisfied -1 if interval is updated and a search is done -2 if the interval updated successfully ========================================================================= */ int cg_updateW ( double *a , /* left side of bracketing interval */ double *dpsia , /* derivative at a */ double *b , /* right side of bracketing interval */ double *dpsib , /* derivative at b */ double *alpha , /* trial step (between a and b) */ double *phi , /* function value at alpha (returned) */ double *dphi , /* derivative of phi at alpha (returned) */ double *dpsi , /* derivative of psi at alpha (returned) */ cg_com *Com /* cg com structure */ ) { INT n ; int nshrink, status ; double psi, *x, *xtemp, *d, *gtemp ; cg_parameter *Parm ; Parm = Com->Parm ; n = Com->n ; x = Com->x ; /* current iterate */ xtemp = Com->xtemp ; /* x + alpha*d */ d = Com->d ; /* current search direction */ gtemp = Com->gtemp ; /* gradient at x + alpha*d */ cg_step (xtemp, x, d, *alpha, n) ; *phi = cg_fg (gtemp, xtemp, Com) ; psi = *phi - *alpha*Com->wolfe_hi ; *dphi = cg_dot (gtemp, d, n) ; *dpsi = *dphi - Com->wolfe_hi ; if ( Parm->PrintLevel >= 2 ) { printf ("update alpha: %14.6e psi: %14.6e dpsi: %14.6e\n", *alpha, psi, *dpsi) ; } if ( cg_Wolfe (*alpha, *phi, *dphi, Com) ) { status = 0 ; goto Exit2 ; } status = -2 ; if ( *dpsi >= ZERO ) { *b = *alpha ; *dpsib = *dpsi ; goto Exit2 ; } else { if ( psi <= Com->fpert ) { *a = *alpha ; *dpsia = *dpsi ; goto Exit2 ; } } nshrink = 0 ; *b = *alpha ; while ( TRUE ) { *alpha = .5*(*a + *b) ; nshrink++ ; if ( nshrink > Parm->nexpand ) { status = 8 ; goto Exit2 ; } cg_step (xtemp, x, d, *alpha, n) ; *phi = cg_fg (gtemp, xtemp, Com) ; *dphi = cg_dot (gtemp, d, n) ; *dpsi = *dphi - Com->wolfe_hi ; psi = *phi - *alpha*Com->wolfe_hi ; if ( Parm->PrintLevel >= 2 ) { printf ("contract, a: %14.6e alpha: %14.6e " "phi: %14.6e dphi: %14.6e\n", *a, *alpha, *phi, *dphi) ; } if ( cg_Wolfe (*alpha, *phi, *dphi, Com) ) { status = 0 ; goto Exit2 ; } if ( *dpsi >= ZERO ) { *b = *alpha ; *dpsib = *dpsi ; goto Exit1 ; } if ( psi <= Com->fpert ) { if ( Parm->PrintLevel >= 2 ) { printf ("update a: %14.6e dpsia: %14.6e\n", *alpha, *dpsi) ; } *a = *alpha ; *dpsia = *dpsi ; } else *b = *alpha ; } Exit1: status = -1 ; Exit2: if ( Parm->PrintLevel >= 2 ) { printf ("UP a: %14.6e b: %14.6e da: %14.6e db: %14.6e status: %i\n", *a, *b, *dpsia, *dpsib, status) ; } return (status) ; } /* ========================================================================= ==== cg_printParms ====================================================== ========================================================================= Print the contents of the cg_parameter structure ========================================================================= */ void cg_printParms ( cg_parameter *Parm ) { printf ("PARAMETERS:\n") ; printf ("\n") ; printf ("Wolfe line search parameter ..................... delta: %e\n", Parm->delta) ; printf ("Wolfe line search parameter ..................... sigma: %e\n", Parm->sigma) ; printf ("decay factor for bracketing interval ............ gamma: %e\n", Parm->gamma) ; printf ("growth factor for bracket interval ................ rho: %e\n", Parm->rho) ; printf ("growth factor for bracket interval after nan .. nan_rho: %e\n", Parm->nan_rho) ; printf ("truncation factor for cg beta ..................... eta: %e\n", Parm->eta) ; printf ("perturbation parameter for function value ......... eps: %e\n", Parm->eps) ; printf ("factor for computing average cost .............. Qdecay: %e\n", Parm->Qdecay) ; printf ("relative change in cost to stop quadstep ... QuadCufOff: %e\n", Parm->QuadCutOff) ; printf ("factor multiplying gradient in stop condition . StopFac: %e\n", Parm->StopFac) ; printf ("cost change factor, approx Wolfe transition . AWolfeFac: %e\n", Parm->AWolfeFac) ; printf ("restart cg every restart_fac*n iterations . restart_fac: %e\n", Parm->restart_fac) ; printf ("stop when cost change <= feps*|f| ................. eps: %e\n", Parm->eps) ; printf ("starting guess parameter in first iteration ...... psi0: %e\n", Parm->psi0) ; printf ("starting step in first iteration if nonzero ...... step: %e\n", Parm->step) ; printf ("factor multiply starting guess in quad step ...... psi1: %e\n", Parm->psi1) ; printf ("initial guess factor for general iteration ....... psi2: %e\n", Parm->psi2) ; printf ("max iterations is n*maxit_fac ............... maxit_fac: %e\n", Parm->maxit_fac) ; printf ("max expansions in line search ................. nexpand: %i\n", Parm->nexpand) ; printf ("max secant iterations in line search .......... nsecant: %i\n", Parm->nsecant) ; printf ("print level (0 = none, 2 = maximum) ........ PrintLevel: %i\n", Parm->PrintLevel) ; printf ("Logical parameters:\n") ; if ( Parm->PertRule ) printf (" Error estimate for function value is eps\n") ; else printf (" Error estimate for function value is eps*Ck\n") ; if ( Parm->QuadStep ) printf (" Use quadratic interpolation step\n") ; else printf (" No quadratic interpolation step\n") ; if ( Parm->PrintFinal ) printf (" Print final cost and statistics\n") ; else printf (" Do not print final cost and statistics\n") ; if ( Parm->PrintParms ) printf (" Print the parameter structure\n") ; else printf (" Do not print parameter structure\n") ; if ( Parm->AWolfe) printf (" Approximate Wolfe line search\n") ; else printf (" Wolfe line search") ; if ( Parm->AWolfeFac > 0. ) printf (" ... switching to approximate Wolfe\n") ; else printf ("\n") ; if ( Parm->StopRule ) printf (" Stopping condition uses initial grad tolerance\n") ; else printf (" Stopping condition weighted by absolute cost\n") ; if ( Parm->debug) printf (" Check for decay of cost, debugger is on\n") ; else printf (" Do not check for decay of cost, debugger is off\n") ; } /* Version 1.2 Change: The variable dpsi needs to be included in the argument list for subroutine cg_updateW (update of a Wolfe line search) Version 2.0 Changes: The user interface was redesigned. The parameters no longer need to be read from a file. For compatibility with earlier versions of the code, we include the routine cg_readParms to read parameters. In the simplest case, the user can use NULL for the parameter argument of cg_descent, and the code sets the default parameter values. If the user wishes to modify the parameters, call cg_default in the main program to initialize a cg_parameter structure. Individual elements of the structure could be modified. The header file cg_user.h contains the structures and prototypes that the user may need to reference or modify, while cg_descent.h contains header elements that only cg_descent will access. Note that the arguments of cg_descent have changed. Version 3.0 Changes: Major overhaul */