R/optimx.R

Defines functions optimx

Documented in optimx

optimx <- function(par, fn, gr=NULL, hess=NULL, lower=-Inf, upper=Inf, bdmsk=NULL, 
            method=c("Nelder-Mead","BFGS"), itnmax=NULL, hessian=FALSE,
            control=list(), ...) {
##### OPEN ISSUES: (any date order)
# 120411 -- ?? change method of specifying masks -- also for nlmrt
# 120128 -- ?? No way to input function value at initial parameters -- should have this
# 111127 -- ?? ctrl$trace vs trace, ctrl$dowarn, ctrl$maximize
# 111124 -- ?? check that structure of all answers is consistent
# 111026 -- ?? change structure of returned information
# 090729 -- ?? simplify structure of answers -- avoid repetition and make easier to access
# 110628 -- ?? tests needed of hess with maximize
# 110628 -- ?? tests needed with bounds and masks active
# 110628 -- ?? return bdmsk[]. Where? In "details" or in main answer?
# 110625 -- ?? dealing with bounds and masks in numDeriv and simple numerical derivs
# 110615 -- ?? should we put more in ufn e.g., function eval count etc.
# 091220 -- ?? kkt1 tolerance. Do we want to let this be an input control?
# 090612 -- ?? (Similar to above) Better choices for the tolerances in tests of equality for gradient / kkt tests?
# 091018 -- ?? masks? generally in optimx -- can do this by hack in ufn, ugr, uhess
# 090929 -- ?? control structure could use simplification 
# 090531 -- ?? like SNewton in to assist other workers
# 090601 -- ?? Do we want hessevals to count Hessian evaluations? -- put it in uhess

##### IMPLEMENTED: (reverse date order)
# 111124 -- direct kfn, kgr, khess counters
# 110624 -- align starttests with funcheck package to avoid maintaining double code
# 111028 -- allow NM as substitute for Nelder-Mead, fix optansout
# 110703 -- check maximize works in examples
# 111102 -- check for npar==1. Note that optimx STOPs, whereas optim() currently calls Brent
# 111102 -- check fnscale is positive
# 111029 -- check how tfn is built for nlm() with ugr and uhess when gr or hess are NULL
# 111029 -- moved scalecheck into optfntools
# 111029 -- put ugHgenb into post processing
# 111029 -- dealing with bounds and masks with gHgen and kktc -- ugHgenb
# 111029 -- put in ugr and uhess like we have ufn
# 110701 -- combined fnuser has fnuser$fn, fnuser$gr, fnuser$hess, of which 
#           3rd or 2nd and 3rd may be NULL. Still do NOT have right sense of how
#           to put in appropriate numerical approximations to these. ??
# 110627 -- hess() check does not look at whether we maximize -- fixed
# 110624 -- put in gHgen and kktc from kktc package
# 110615 -- sorting answers when maximize=TRUE
# 110614 -- put in opxfn.R as ufn to watch for inadmissible / non-computable function
# 110212 -- & to && and | to || for controls
# 110212 -- Hessian changed from NULL to FALSE default (??explain)
# 100328 -- check if maximize works for Powell (minqa) routines -- 100415
# 100329 -- make sure spg fixed on CRAN -- 100415
# 100329 -- maximize tested for all but minqa, though spg needs fixup on CRAN. ??
# 100215 -- Add newuoa and uobyqa to test minqa
# 100212 -- setting scaletol in controls
# 091018 -- new control "starttest" so we can skip them DONE 091220
# 091218 -- scaling checks (before computing rhobeg) and warning
# 091215 -- mcontrol omission in nlm() call fixed
# 091026 -- Remove SANN because no real conv indicator
# 091018 - 090531 Use function code joint with funtest and funcheck in initial checks -- not fully possible
# 091018 -- decided not to Put optimx.dev into package to provide for local source packages etc.
# 090923 -- add bobyqa (from minqa)
# 090923 -- add Rcgmin for large scale problems
# 090729 -- put hessian argument back as an alternate for kkt
# 090729 -- should have kkt only carried out for n<50 (no gradient), n<500 (with gradient and Jacobian
#            of gradient to get Hessian)
# 090531 -- KKT stuff
# 090531 -- control for keeping failed runs (save.failures)
# 090531 Decided to omit 'trust' from package methods for the time being.
# 090527 -- added follow.on
# 090511 What should be default method(s)? 
#         090601: to provide compatibility with optim(), Nelder-Mead is put first. 
#                 The choice of BFGS second is open to discussion. JN
#  A wrapper function to integrate major optimization packages in R
#  For now, we only consider algorithms for unconstrained and box-constrained optimization
#  This function can implement "multiple" algorithms
#
# Input:
#  par = a single vector of starting values
#  fn = objective function (assumed to be sufficeintly differentiable)
#  gr = name of a function to compute the (analytic) gradient of the objective function
#  hess = name of a function to compute the (analytic) Hessian of the objective function
#         Editorial note: Capitalize Hessian after the name of Otto Hesse. JN
#  method = a character vector denoting all the algorithms to be executed (in the specified order)
#      Complete names are not needed. A partial matching is attempted.
#  hessian = logical variable that, if present, is equivalent to control$kkt. If TRUE, it causes
#      optimx to try to compute an approximation to the Hessian matrix at the final set of parameters.
#  control = list of control information, in particular
#      trace = an integer controlling output (note different methods may use logicals
#         trace = 0 gives no output, positive numbers give more output for greater values
#      follow.on = TRUE or FALSE. If TRUE, and there are multiple methods, then the last set of 
#         parameters from one method is used as the starting set for the next. 
#      save.failures = TRUE if we wish to keep "answers" from runs where the method does not 
#         return conv==0. FALSE otherwise (default). Forced TRUE for follow.on = TRUE.
#      maximize = TRUE if we want to maximize rather than minimize a function. (Default FALSE)
#         090601: Not yet implemented for nlm, nlminb, ucminf. However, there is a check to avoid
#                 usage of these codes when maximize is TRUE.
#      all.methods = TRUE if we want to use all available (and suitable) methods
#      sort.result=TRUE, that is, we sort the results in decreasing order of the final function value
#      kkt=TRUE to run Kuhn, Karush, Tucker tests of results unless problem large
#      kkttol=0.001 (was .Machine$double.eps^(1/4)) Default value to check for small gradient and negative
#               Hessian eigenvalues
#      kkt2tol=1E-6 (default WAS 10* default for kkttol) Tolerance for eigenvalue ratio in KKT test of 
#               positive definite Hessian
#      all.methods=FALSE By default we do NOT run all methods
#      starttests=TRUE  By default we run tests of the function and parameters: feasibility relative to
#               bounds, analytic vs numerical gradient, scaling tests) before we try optimization methods
#      dowarn=TRUE By default we leave warnings generated by optimx.
#      badval=(0.5)*.Machine$double.xmax The value to set for the function value when try(fn()) fails.
#      scaletol=3 To check parameters or their bounds we take logs of absolute values and find the range 
#               i.e., max - min. This should not exceed scaletol. A value of 3 gives magnitudes between 
#               1 and 20 approximately.  
#
# Output:
# ans = an object containing two sets of information:
# essential output = a data frame of converged parameters, function value at convergence, 
#    name of algorithm, convergence indicator, and function evaluation and 
#    gradient evaluation counts
# detailed output = this is a list with one component for each algorithm, and contains parameters, 
#    function values, convergence code, number of function and gradient evals, numerical gradient 
#    and hessian at convergence, eigenvalues of that hessian approximation, cpu time, and other 
#    information returned by algorithms, and name of the algorithm.
# detailed output can be accessed via the attribute called `details'
#
#  Authors:  Ravi Varadhan & John Nash
#  Date:  February 17, 2008
#  Changes: Ravi Varadhan - Date: May 29, 2009, John Nash - Latest: July 2, 2011
#
#################################################################
# Note package "optfntools" is used extensively in the code -- but don't reload here!!!
npar<-length(par) # number of parameters
nullgr<-is.null(gr) # save these as we redefine functions so NOT null later
numgrad<-FALSE
nullhess<-is.null(hess)
tgr<-gr # save object
if (nullgr) gr<-"grfwd" # The default numerical gradient
if (is.character(gr)) { # we are calling an approximation to the gradient
   numgrad<-TRUE # set flag  ??? this may be problematic -- need to check all info there eg ...
   tgr<-function(par=par, userfn=fn){
      do.call(gr, list(par, userfn))
   }
}
# Get real name of function to be minimized
   fname<-deparse(substitute(fn))
   ## cat("fname:",fname,"\n")
   if (is.null(control$trace)) control$trace<-0 # to ensure trace set
   if (control$trace>0) {
      cat("Objective fn is ",fname,"\n")
   }
   ## cat("check params\n")
## Code more or less common to funtest, funcheck and optimx <<<
# Check parameters are in right form
   if(!is.null(dim(par))) stop("Parameter should be a vector, not a matrix!")
   if (! is.vector(par) ) {
      stop("The parameters are NOT in a vector")
   }
   if (is.null(bdmsk) ) bdmsk<-rep(1,npar) # set masks for free parameters
# Set control defaults. Do we want to save.failures?
   ## cat("Develop control list\n")
   ctrl <- list(
   follow.on=FALSE, 
   save.failures=TRUE,
   eps=1.e-07,
   trace=0,
   sort.result=TRUE,
   kkt=TRUE,
   all.methods=FALSE,
   starttests=TRUE,
   maximize=FALSE,
   dowarn=TRUE, 
   kkttol=0.001,
   kkt2tol=1.0E-6,
   badval=(0.5)*.Machine$double.xmax,
   scaletol=3,
   fnscale=1.0,
   parscale=NULL,
   axsearch.tries=1 # will try axial search once. No restart.
   # Note if axsearch.tries=3, restart only 2 times!
   ) # for now turn off sorting until we can work out how to do it nicely
# Note that we do NOT want to check on the names, because we may introduce 
#    new names in the control lists of added methods. That is, we do NOT do
#    if (!all(namc %in% names(ctrl))) 
#        stop("unknown names in control: ", namc[!(namc %in% names(ctrl))])
# However, we do want to substitute the appropriate information. 
   ncontrol <- names(control)
   nctrl <- names(ctrl)
   for (onename in ncontrol) {
      if (!(onename %in% nctrl)) {
         warning("optimx: control ",onename," is not in default control set\n")
      }
      ctrl[onename]<-control[onename]
   }
# Force save.failures for follow.on  111101
   if (ctrl$follow.on) ctrl$save.failures<-TRUE
# Set ctrl$kkt using 'hessian' 110718
   if ((! is.null(hessian)) && (hessian==TRUE)) {
      control$kkt<-TRUE # make hessian override control$kkt
   } else {
      hessian<-FALSE
   }
   if (is.null(control$kkt)) { # turn off kkt for large matrices
      ctrl$kkt<-TRUE # default it to compute KKT tests
      if (is.null(gr)) { # no analytic gradient
         if (npar > 50) {
            ctrl$kkt=FALSE # too much work when large number of parameters
            if (ctrl$trace>0) cat("gr NULL, npar > 50, kkt set FALSE\n")
         }
      } else {
         if (npar > 500) {
            ctrl$kkt=FALSE # too much work when large number of parameters, even with analytic gradient
            if (ctrl$trace>0) cat("gr NULL, npar > 50, kkt set FALSE\n")
         }
      }
   } else { # kkt is set
      if (control$kkt) {
         if (nullgr) {
            if (npar > 50) {
               if ((ctrl$trace>0) && ctrl$dowarn) warning("Computing hessian for gr NULL, npar > 50, can be slow\n")
            }
         } else {
            if (npar > 500) {
               if ((ctrl$trace>0) && ctrl$dowarn) warning("Computing hessian with gr code, npar > 500, can be slow\n")
            }
         }
      }
   }
   # copy some controls to top level
   dowarn<-ctrl$dowarn
   eps<-ctrl$eps
   # end move ctrl elements to top level
   if (ctrl$maximize) {
      if (ctrl$trace>0) cat("MAXIMIZING\n")
   }
   ## cat("check bounds\n")
# Check for npar > 1
   if (npar < 2) {
      if (dowarn) {warning("npar should be >1 for optimx")}
   } # End check on number of parameters
# Do we have bounds? 
   if (any(is.finite(c(lower, upper)))) { have.bounds<-TRUE # set this for convenience
   } else { have.bounds <- FALSE }
   if (ctrl$starttests) { # Check parameters in bounds
      infeasible<-FALSE
      if (ctrl$trace > 0) cat("Function has ",npar," arguments\n")
      if (have.bounds) {
          mybm<-bmchk(par, lower, upper, bdmsk, max(ctrl$trace-1, 0))
          if ( ! mybm$admissible ) {
             # ?? need to return something so we don't crash
             stop("The bounds are inadmissible for this problem")
          }
          if ( mybm$parchanged ) {
             if (ctrl$trace>0) 
                cat("At least one parameter has been changed to conform with bounds")
             if (ctrl$dowarn)
                warning("At least one parameter has been changed to conform with bounds")
          }
          if ( mybm$maskadded ) {
             if (ctrl$trace>0) cat("At least one mask added (close bounds)")
             if (ctrl$dowarn) warning("At least one mask added (close bounds)")
          }
	  lower<-mybm$lower # these may have been changed or expanded
          upper<-mybm$upper
          bdmsk<-mybm$bdmsk          
      } # end have.bounds
   } # end if (starttests) ...
   nmask<-length(which(bdmsk==0)) # number of masks
   if (ctrl$trace>0) cat("Number of masked (fixed) parameters = ",nmask,"\n")
   if (npar-nmask < 2) if(dowarn) {warning("The number of free parameters is less than 2")}
   ## cat("check fnscale/parscale\n")
   if ( ctrl$fnscale < 0.0 ) {
      if (ctrl$dowarn || (ctrl$trace>0) )
         warning("ctrl$fnscale must be positive -- using abs(ctrl$fnscale)")
      ctrl$fnscale<-abs(ctrl$fnscale)
   }
   if (! is.null(ctrl$parscale)) {
      if (length(ctrl$parscale) != npar) stop("Wrong length for scaling vector")
      if (any(ctrl$parscale <= 0.0)) stop("Scalings must be positive")
   } else {
      ctrl$parscale<-rep(1,npar)
   }
######## Set up environment to hold working data ################
opxfn<-list2env(list(fn=fn, gr=gr, hess=hess, MAXIMIZE=FALSE, PARSCALE=rep(1,npar), FNSCALE=1,
       KFN=0, KGR=0, KHESS=0, dots=list(...)))
   # define the user function for Hessian
opxfn$PARSCALE<-ctrl$parscale
opxfn$FNSCALE<-ctrl$fnscale
opxfn$MAXIMIZE<-ctrl$maximize
opxfn$gr<-tgr # copy over the appropriate gradient function
if (length(opxfn$dots)<1) opxfn$dots<-NULL # ensure null
#################################################################
# Scaling check
   if (ctrl$starttests) {
      srat<-scalecheck(par, lower, upper, ctrl$dowarn)
      sratv<-c(srat$lpratio, srat$lbratio)
      if(max(sratv,na.rm=TRUE) > ctrl$scaletol) { 
         warnstr<-"Parameters or bounds appear to have different scalings. See optimx.Rd"
         if (ctrl$dowarn) warning(warnstr)
      }
      if(ctrl$trace>0) {
          cat("Scale check -- log parameter ratio=",srat$lpratio,
                          "  log bounds ratio=",srat$lbratio,"\n")
      } # end scaling check
   } # end if (starttests) ...
   # 120128 -- need initial function value -- this is inefficient in that it 
   # causes an extra evaluation, but needed to ensure we have a finite value
   # for comparisons later
   # Check if function can be computed  
   myfval<-fnchk(par, ufn, trace=max(ctrl$trace-1, 0), fnuser=opxfn)
   if (ctrl$trace>0) {
       cat("results of first function evaluation:")
       print(myfval)
       cat("at:")
       print(par)
   }
   if (myfval$infeasible) {
       # ?? should exit in a controlled way
       stop("Function is infeasible at initial parameter values")
   }
   # end computation of function
   if (ctrl$starttests) {
      # check gradient and possibly Hessian functions
      ## cat("check gradient\n")
      if(! numgrad){ # check gradient
         gname <- deparse(substitute(gr)) # Make sure gr is NOT ugr
         if (ctrl$trace>1) cat("Analytic gradient uses function ",gname,"\n")
         kgr<-0
         mygc<-grchk(par, ufn, ugr, trace=max(0,(ctrl$trace-1)), fnuser=opxfn)
         if (! mygc) {
            # ?? need to change so we exit gracefully
            cat("Gradient check\n")
            print(mygc)
            stop("Gradient function might be wrong - check it!")
         }
         # Now try hessian
         if (! nullhess) {
           khess<-0
           myhc<-hesschk(par, ufn, ugr, uhess, trace=max(0, (ctrl$trace-1)), 
              fnuser=opxfn) 
           if (! myhc ) {
             cat("Hessian check:\n")
             print(myhc)
             stop("Hessian function might be wrong -- check it!")
           } else {
             if (ctrl$trace>1) {
                cat("Hessian check result:\n")
                print(myhc)
             }
           }
         } # end if (! nullhess) 
      } # end if (! numgrad) 
   } # end if (starttests) ...
   # Check that we have the functions we need for gradient check
   if (! require(numDeriv, quietly=FALSE) ) stop("Install package `numDeriv'", call.=FALSE) # cannot avoid stop here.
# Set up the vector to return answers
   ans.ret <- vector(mode="list")
   # mode= is not strictly required. length defaults to 0. This sets up our answer vector.
# List of methods in base or stats, namely those in optim(), nlm(), nlminb()
   bmeth <- c("Nelder-Mead", "BFGS", "CG", "L-BFGS-B", "nlm", "nlminb")
# SANN has no termination for optimality, only a maxit count for
#    the maximum number of function evaluations; remove DEoptim for now -- not useful 
#    for smooth functions. Code left in as example for those who may need it.
# List of methods in packages. 
   ## cat("check methods present\n")
# uobyqa removed 110114 because of some crashes that did not seem resolvable. 
#    Replaced 111027 -- different from newuoa. But giving problems, so not in ALL methods,
#    but can be called explicitly.
# Now make sure methods loaded
   allmeth <- bmeth # start with base methods
   if (require(BB, quietly=FALSE) )  allmeth<-c(allmeth,"spg")
   else warning("Package `BB' Not installed", call.=FALSE)

   if (require(ucminf, quietly=FALSE) ) allmeth<-c(allmeth, "ucminf")
   else warning("Package `ucminf' Not installed", call.=FALSE)
   
   if (require(Rcgmin, quietly=FALSE) )  allmeth<-c(allmeth, "Rcgmin")
   else warning("Package `Rcgmin' Not installed", call.=FALSE)
   

   if (require(Rvmmin, quietly=FALSE) )  allmeth<-c(allmeth, "Rvmmin")
   else warning("Package `Rvmmin' Not installed", call.=FALSE)
   
   if (require(minqa, quietly=FALSE) ) allmeth<-c(allmeth,"uobyqa", "newuoa", "bobyqa")
   else  warning("Package `minqa' (for uobyqa, newuoa, and bobyqa) Not installed", call.=FALSE)
   
   if (require(dfoptim, quietly=FALSE) ) allmeth<-c(allmeth, "hjkb", "nmkb")
   else  warning("Package `dfoptim' (for nmkb) Not installed", call.=FALSE)

   # Restrict list of methods if we have bounds
   bdsmeth<-c("L-BFGS-B", "nlminb", "spg", "Rcgmin", "Rvmmin", "bobyqa", "hjkb", "nmkb")
   if (any(is.finite(c(lower, upper)))) allmeth <- allmeth[which(allmeth %in% bdsmeth)]
   if (method=="ALL") ctrl$all.methods<-TRUE # 120709
   if (ctrl$all.methods) { # Changes method vector!
      method<-allmeth
      idx<-which(method=="uobyqa")
      method<-method[-idx]
      if (ctrl$trace>0) {
         cat("all.methods is TRUE -- Using all available methods except uobyqa\n")
         print(method)
      }
   } 
   # convert NM to Nelder-Mead for decisions, other way on output
   method[which(method == "NM")]<-"Nelder-Mead" 
   method <- try(unique(match.arg(method, allmeth, several.ok=TRUE) ),silent=TRUE)
   if (class(method)=="try-error") {
      warning("optimx: No match to available methods")
      method<-NULL
      nmeth<-0
   } else {
      nmeth <- length(method) # number of methods requested
   } 
   # JN 2011-1-17 fix for default when there are bounds
   if ((nmeth==0) && have.bounds) {
      method="L-BFGS-B"
      warning("Default method when bounds specified is L-BFGS-B to match optim()")
      nmeth<-1
   }
   if (nmeth==0) stop("No suitable method")
   if (ctrl$trace>1) {
      cat("Methods to be used:")
      print(method)
   }
   ## Check that methods are indeed available and loaded
   for (i in 1:nmeth) {
      cmeth <- method[i]
      if (ctrl$trace > 1) cat("Looking for method = ",cmeth,"\n")
      if (! (cmeth %in% allmeth) ) {
         errmsg <- paste(cmeth," not found in any list of methods available")
         stop(errmsg, call.=FALSE)
      } # otherwise the method is available, and just needs to be loaded
   } # end check methods available
   # restrict methods if we have masks
   if (any( bdmsk == 0 ) ){
      mmeth<-c("Rcgmin", "Rvmmin")
      if (ctrl$trace > 0) cat("Masks in effect. Restrict methods")
      method <- try(unique(match.arg(method, mmeth, several.ok=TRUE) ),silent=TRUE)
      if (class(method)=="try-error") {
         warning("optimx: No match between requested and mask-capable methods")
         method<-mmeth # THIS MAY BE UNWANTED!
         nmeth <- length(method) 
      } else {
         nmeth <- length(method) # number of methods requested
      }
   }
# Run methods
   ## cat("set times to 0\n")
   times <- rep(0, nmeth)  # figure out how many methods and reserve that many times to record.
   names(times) <- method  # And label these times appropriately with the method.
   j <- 0  # j will be the index to the answers, one per each method, starting parameter pair
   par0<-par # save starting parameters -- note issue with follow.on
   for (i in 1:nmeth) { # loop over the methods
      if (! ctrl$follow.on) par<-par0
      loopsleft<-ctrl$axsearch.tries # Initialize to 1 more than restarts
      repeat { # Start of main loop per method
        loopsleft<-loopsleft-1 # reduce cycle
        meth <- method[i] # extract the method name
        if (ctrl$trace>0) cat("Method: ", meth, "\n") # display the method being used
      # Set the counters
      # kfn<-0
      # kgr<-0
      # khess<-0
      opxfn$KFN<-0
      opxfn$KGR<-0
      opxfn$KHESS<-0
      conv <- -1 # indicate that we have not yet converged
      # 20100608 - take care of polyalgorithms
      if (! is.null(itnmax) ) {
         if (length(itnmax) == 1) {ctrl$maxit <- itnmax} # Note we will execute this FIRST
         else {if (length(itnmax) != nmeth) { 
            stop("Length of itnmax =",length(itnmax)," but should be ",nmeth) }
            else { ctrl$maxit<-itnmax[i] }
         }
         if (ctrl$follow.on) cat("Do ",ctrl$maxit," steps of ",meth,"\n")
      }
      # 20100215: Note that maxit needs to be defined other than 0 e.g., for ucminf
      # create local control list for a single method -- this is one of the key issues for optimx
      mcontrol<-ctrl
      mcontrol$follow.on<-NULL # And make sure that controls NOT in method are deleted (nulled)
      mcontrol$save.failures<-NULL
      mcontrol$sort.result<-NULL
      mcontrol$kkt<-NULL
      mcontrol$starttests<-NULL
      mcontrol$all.methods<-NULL
      mcontrol$dowarn<-NULL
      mcontrol$kkttol<-NULL
      mcontrol$kkt2tol<-NULL
      mcontrol$maximize<-NULL # Even if method DOES have it
      mcontrol$badval<-NULL
      mcontrol$eps<-NULL
      mcontrol$axsearch.tries<-NULL # used only by optimx
      mcontrol$scaletol<-NULL # not used in any methods -- it is here 
                 # for the scale check of parameters and bounds above
# ---  UNDEFINED METHOD ---
      if (! (meth %in% allmeth )) {
         errmsg<-paste("UNDEFINED METHOD: ", meth, sep='')
         stop(errmsg, call.=FALSE)
      }
## --------------------------------------------
      # Methods from optim()
      # if (meth == "NM") meth<-"Nelder-Mead" # change so optim works
      if (meth=="Nelder-Mead" || meth == "BFGS" || meth == "L-BFGS-B" || meth == "CG") { # no SANN
         if (ctrl$trace>2) cat("Found an optim method\n")
         # Take care of methods   from optim(): Nelder-Mead, BFGS, L-BFGS-B, CG
         mcontrol$usenumDeriv<-NULL # not used in optim()
         if (is.null(mcontrol$type)) mcontrol$type=2 # use Polak Ribiere formula
         ## Shall we use the fnuser scaling?
         mcontrol$parscale<-NULL
         mcontrol$fnscale<-NULL
         time <- system.time(ans <- try(optim(par=par, fn=ufn, gr=ugr, 
                 lower=lower, upper=upper, method=meth, 
                 control=mcontrol, fnuser=opxfn), silent=TRUE))[1]
         # The time is the index=1 element of the system.time for the process, 
         # which is a 'try()' of the regular optim() function
         if (class(ans)[1] != "try-error") { 
            ans$conv <- ans$convergence
         #  convergence: An integer code. '0' indicates successful convergence.
            ans$value<-as.numeric(ans$value) # ensure not atomic etc.
            ans$fevals<-ans$counts[1] # save function and gradient count information
            ans$gevals<-ans$counts[2]
            ans$counts<-NULL # and erase the counts element now data is saved
            ans$niter<-NULL # not used, so delete
         } else { # bad result -- What to do?
            ans<-list(fevals=NA) # ans not yet defined, so set as list
            ans$conv<-9999 # failed in run
            if (ctrl$trace>0) cat("optim function evaluation failure\n")
            ans$value= ctrl$badval
            ans$par<-rep(NA,npar)
         }
      }   # end if using optim() methods
## --------------------------------------------
##      else 
      if (meth == "nlminb") {
         # Here we use portLib routine nlminb rather than optim as our minimizer
         mcontrol$iter.max<-mcontrol$maxit # different name for iteration limit in this routine
         mcontrol$maxit<-NULL
         mcontrol$usenumDeriv<-NULL
         mcontrol$parscale<-NULL
         mcontrol$fnscale<-NULL
    ##     mcontrol$abs.tol<-0 # To fix issues when minimum is less than 0. 20100711
         if( is.null(mcontrol$trace) || is.na(mcontrol$trace) || mcontrol$trace == 0) { 
            mcontrol$trace <- 0
         } else { 
            mcontrol$trace <- 1 # this is EVERY iteration. nlminb trace is freq of reporting.
         }
         if (nullgr) {
            time <- system.time(ans <- try(nlminb(start=par, objective=ufn, gradient=NULL, 
              lower=lower, upper=upper, control=mcontrol, fnuser=opxfn), silent=TRUE))[1]
         } else {
            time <- system.time(ans <- try(nlminb(start=par, objective=ufn, gradient=ugr, 
              lower=lower, upper=upper, control=mcontrol, fnuser=opxfn), silent=TRUE))[1]
         }
         if (class(ans)[1] != "try-error") {
            ans$conv <- ans$convergence
            # Translate output to common format and names
            ans$value<-as.numeric(ans$objective)
            ans$objective<-NULL
            ans$fevals<-ans$evaluations[1]
            ans$gevals<-ans$evaluations[2]
            ans$evaluations<-NULL # cleanup
            ans$niter<-ans$iterations
            ans$iterations<-NULL
         } else { # bad result -- What to do?
            ans<-list(fevals=NA) # ans not yet defined, so set as list
            ans$conv<-9999 # failed in run
            if (ctrl$trace>0) cat("nlminb function evaluation failure\n")
            ans$value= ctrl$badval
            ans$par<-rep(NA,npar)
         }
      }  ## end if using nlminb
## --------------------------------------------
#      else 
      if (meth == "nlm") { # Use stats package nlm routine
         tufn <- ufn
         if (nullgr) attr(tufn, "gradient")<-NULL else attr(tufn, "gradient") <- ugr
         if (nullhess) attr(tufn, "hessian")<-NULL else attr(tufn, "hessian") <- uhess
         ## 091215 added control for iteration limit
         if (! is.null(mcontrol$maxit)) { iterlim<-mcontrol$maxit } 
         else { 
            iterlim = 100 
            mcontrol$maxit<-NULL # and remove it for this method
         }
         if (! is.null(mcontrol$trace)) { plevel<-0 }
         else {
            plevel<-min(2,mcontrol$trace) # 110702 max is 2
            mcontrol$trace<-NULL
         }
         mcontrol$parscale<-NULL
         mcontrol$fnscale<-NULL
         nampar<-names(par) # save names 110508
         time <- system.time(ans <- try(nlm(f=tufn, p=par, iterlim=iterlim, 
                 print.level=plevel, fnuser=opxfn), silent=TRUE))[1]
         if (class(ans)[1] != "try-error") {
            ans$conv <- ans$code
            if (ans$conv == 1 || ans$conv == 2 || ans$conv == 3) ans$conv <- 0
            if (ans$conv == 4) ans$conv <- 1
            # Translate output to common format
            ans$value<-as.numeric(ans$minimum)
            ans$par<-ans$estimate
            ans$estimate<-NULL
            ans$minimum<-NULL
            ans$fevals<-NA
            ans$gevals<-NA # ?? need to fix this somehow in nlm code
            ans$niter<-ans$iterations
            ans$iterations<-NULL
            names(ans$par)<-nampar # restore names 110508
         } else {
            if (ctrl$trace > 0) cat("nlm failed for this problem\n")
            ans<-list(fevals=NA) # ans not yet defined, so set as list
            ans$value= ctrl$badval
            ans$par<-rep(NA,npar)
            ans$conv<-9999 # failed in run
            ans$gevals<-NA 
            ans$niter<-NA
         }
      } # end if using nlm
## --------------------------------------------
#      else 
      if (meth == "spg") { # Use BB package routine spg as minimizer
         mcontrol$usenumDeriv<-NULL
         mcontrol$parscale<-NULL
         mcontrol$fnscale<-NULL
         mcontrol$maximize<-NULL # Use external maximization approach
         mcontrol$checkGrad.tol<-10.0 # Want to suppress the gradient check
         time <- system.time(ans <- try(spg(par=par, fn=ufn, gr=ugr, lower=lower, 
            upper=upper, fnuser=opxfn, control=mcontrol), silent=TRUE))[1]
          if (class(ans)[1] != "try-error") { 
           ans$value<-as.numeric(ans$value)
           ans$conv <- ans$convergence
           ans$fevals<-ans$feval
           ans$feval<-NULL # to erase conflicting name
           ans$gevals<-NA # ??fixup needed
           ans$niter<-ans$iter
           ans$iter<-NULL
         } else { # spg failed
           if (ctrl$trace > 0) cat("spg failed for this problem\n")
           ans<-list(fevals=NA) # ans not yet defined, so set as list
           ans$value= ctrl$badval
           ans$par<-rep(NA,npar)
           ans$conv<-9999 # failed in run
           ans$gevals<-NA 
           ans$niter<-NA
         }
      }  # end if using spg
## --------------------------------------------
#      else 
      if (meth == "ucminf") {
         ## Use ucminf routine
         if (is.null(ctrl$maxit)) mcontrol$maxit<-500 # ensure there is a default value
         # Change 20100415 to avoid setting ctrl values when all.methods
         mcontrol$maxeval<-mcontrol$maxit # Note it is just function evals for ucminf
         mcontrol$maxit<-NULL
         mcontrol$usenumDeriv<-NULL
         mcontrol$parscale<-NULL
         mcontrol$fnscale<-NULL
         time <- system.time(ans <- try(ucminf(par=par, fn=ufn, gr=ugr, 
              fnuser=opxfn, control=mcontrol), silent=TRUE))[1]
         if (class(ans)[1] != "try-error") {
            ans$conv <- ans$convergence
            # From ucminf documentation:  
            # convergence = 1 Stopped by small gradient (grtol).
            #               2 Stopped by small step (xtol).
            #               3 Stopped by function evaluation limit (maxeval).
            #               4 Stopped by zero step from line search
            #              -2 Computation did not start: length(par) = 0.
            #              -4 Computation did not start: stepmax is too small.
            #              -5 Computation did not start: grtol or xtol <= 0.
            #              -6 Computation did not start: maxeval <= 0.
            #              -7 Computation did not start: given Hessian not pos. definite.
            #      message: String with reason of termination.
         if (ans$conv == 1 || ans$conv == 2 || ans$conv == 4) {
            ans$conv <- 0
         } else {
            ans$conv <- ans$convergence
         } # Termination criteria are tricky here!  How to determine successful convergence?
         ans$convergence<-NULL
         ans$value<-as.numeric(ans$value)
         ans$fevals<-ans$info[4]
         ans$gevals<-ans$info[4] # calls fn and gr together
         ans$info<-NULL # to erase conflicting name
         ans$niter<-NULL
         if (ctrl$trace > 0) cat("ucminf message:",ans$message,"\n")
         } else { # ucminf failed
            if (ctrl$trace > 0) cat("ucminf failed for this problem\n")
            ans<-list(fevals=NA) # ans not yet defined, so set as list
            ans$value= ctrl$badval
            ans$par<-rep(NA,npar)
            ans$conv<-9999 # failed in run
            ans$gevals<-NA 
            ans$niter<-NA
         }
      }  ## end if using ucminf
## --------------------------------------------
#      else 
      if (meth == "Rcgmin") { # Use Rcgmin routine (ignoring masks)
         mcontrol$trace<-NULL
         mcontrol$maximize<-NULL
         mcontrol$parscale<-NULL
         mcontrol$fnscale<-NULL
         if (ctrl$trace>0) mcontrol$trace<-1
         time <- system.time(ans <- try(Rcgmin(par=par, fn=ufn, gr=ugr, lower=lower, 
              upper=upper, bdmsk=bdmsk, control=mcontrol, fnuser=opxfn), silent=TRUE))[1]
         if (class(ans)[1] != "try-error") {
            ans$conv <- ans$convergence
            ans$fevals<-ans$counts[1]
            ans$gevals<-ans$counts[2]
            ans$value<-as.numeric(ans$value)
         } else {
            if (ctrl$trace>0) cat("Rcgmin failed for current problem \n")
            ans<-list(fevals=NA) # ans not yet defined, so set as list
            ans$value= ctrl$badval
            ans$par<-rep(NA,npar)
            ans$conv<-9999 # failed in run
            ans$gevals<-NA 
            ans$niter<-NA
         }
      }  ## end if using Rcgmin
## --------------------------------------------
#      else 
      if (meth == "Rvmmin") { # Use Rvmmin routine (ignoring masks)
         mcontrol$trace<-NULL
         mcontrol$maximize<-NULL
         mcontrol$parscale<-NULL
         mcontrol$fnscale<-NULL
         if (ctrl$trace>0) mcontrol$trace<-1 # ?? does Rvmmin not allow other values??
         mcontrol$maximize<-NULL # negation built into ufn
         time <- system.time(ans <- try(Rvmmin(par=par, fn=ufn, gr=ugr, lower=lower, 
             upper=upper, bdmsk=bdmsk, control=mcontrol, fnuser=opxfn), silent=TRUE))[1]
         if ((class(ans)[1] != "try-error") && (ans$convergence<2)) { 
            # Note that convergence=1 when too many fns or grs.120520
            ans$conv <- ans$convergence
            ans$value<-as.numeric(ans$value)
            ans$fevals<-ans$counts[1]
            ans$gevals<-ans$counts[2]
         } else {
            if (ctrl$trace>0) cat("Rvmmin failed for current problem \n")
            ans<-list(fevals=NA) # ans not yet defined, so set as list
	    ans$value= ctrl$badval  # RV 07/29/2011
            ans$par<-rep(NA,npar)
            ans$conv<-9999 # failed in run
            ans$gevals<-NA 
            ans$niter<-NA
         }
      }  ## end if using Rvmmin
## --------------------------------------------
#      else 
      if (meth == "bobyqa") {# Use bobyqa routine from minqa package
         if (! is.null(mcontrol$maxit)) { 
            mcontrol$maxfun<-mcontrol$maxit
         } else {
            mcontrol$maxfun<-5000*round(sqrt(npar+1)) # ?? default at 100215, but should it be changed?!!
         }
         mcontrol$iprint<-min(mcontrol$trace, 3)
         mcontrol$maxit<-NULL # and null out control that is NOT used
         mcontrol$trace<-NULL
         mcontrol$parscale<-NULL
         mcontrol$fnscale<-NULL
         mcontrol$usenumDeriv<-NULL
         nampar<-names(par) # save names 110508
         time <- system.time(ans <- try(bobyqa(par=par, fn=ufn, lower=lower, upper=upper, 
               control=mcontrol, fnuser=opxfn), silent=TRUE))[1]
         if (class(ans)[1] != "try-error") {
            ans$conv <- 0
            if (ans$feval > mcontrol$maxfun) {
               ans$conv <- 1 # too many evaluations
            }
            ans$fevals<-ans$feval
            ans$gevals<-NA
            ans$value<-as.numeric(ans$fval )
            names(ans$par)<-nampar # restore names 110508
         } else {
            if (ctrl$trace>0) cat("bobyqa failed for current problem \n")
            ans<-list(fevals=NA) # ans not yet defined, so set as list
            ans$value= ctrl$badval
            ans$par<-rep(NA,npar)
            ans$conv<-9999 # failed in run
            ans$gevals<-NA 
            ans$niter<-NA
         }
      }  ## end if using bobyqa
## --------------------------------------------
#      else 
      if (meth == "uobyqa") { # Use uobyqa routine from minqa package
         if (! is.null(mcontrol$maxit)) { 
            mcontrol$maxfun<-mcontrol$maxit
         } else {
            mcontrol$maxfun<-5000*round(sqrt(npar+1)) # ?? default at 100215, but should it be changed?!!
         }
         mcontrol$maxit<-NULL # and null out control that is NOT used
         mcontrol$iprint<-min(mcontrol$trace, 3)
         mcontrol$trace<-NULL
         mcontrol$usenumDeriv<-NULL
         mcontrol$parscale<-NULL
         mcontrol$fnscale<-NULL
         nampar<-names(par) # save names 110508
         time <- system.time(ans <- try(uobyqa(par=par, fn=ufn, 
              control=mcontrol, fnuser=opxfn), silent=TRUE))[1]
         if (class(ans)[1] != "try-error") {
            ans$conv <- 0
            if (ans$feval > mcontrol$maxfun) {
               ans$conv <- 1 # too many evaluations
            }
            ans$fevals<-ans$feval
            ans$gevals<-NA
            ans$value<-as.numeric(ans$fval)
            names(ans$par)<-nampar # restore names 110508
         } else {
            if (ctrl$trace>0) cat("uobyqa failed for current problem \n")
            ans<-list(fevals=NA) # ans not yet defined, so set as list
            ans$value= ctrl$badval
            ans$par<-rep(NA,npar)
            ans$conv<-9999 # failed in run
            ans$gevals<-NA 
            ans$niter<-NA
         }
      }  ## end if using uobyqa
## --------------------------------------------
#      else 
      if (meth == "newuoa") {# Use newuoa routine from minqa package
         if (! is.null(mcontrol$maxit)) { 
            mcontrol$maxfun<-mcontrol$maxit
         } else {
            mcontrol$maxfun<-5000*round(sqrt(npar+1)) # ?? default at 100215, but should it be changed?!!
         }
         mcontrol$maxit<-NULL # and null out control that is NOT used
         mcontrol$iprint<-min(mcontrol$trace, 3)
         mcontrol$trace<-NULL
         mcontrol$usenumDeriv<-NULL
         mcontrol$parscale<-NULL
         mcontrol$fnscale<-NULL
         nampar<-names(par) # save names 110508
         time <- system.time(ans <- try(newuoa(par=par, fn=ufn, 
              control=mcontrol, fnuser=opxfn), silent=TRUE))[1]
         if (class(ans)[1] != "try-error") {
            ans$conv <- 0
            if (ans$feval > mcontrol$maxfun) {
               ans$conv <- 1 # too many evaluations
            }
            ans$fevals<-ans$feval
            ans$gevals<-NA
            ans$value<-as.numeric(ans$fval)
            names(ans$par)<-nampar # restore names 110508
         } else {
            if (ctrl$trace>0) cat("newuoa failed for current problem \n")
            ans<-list(fevals=NA) # ans not yet defined, so set as list
            ans$value= ctrl$badval
            ans$par<-rep(NA,npar)
            ans$conv<-9999 # failed in run
            ans$gevals<-NA 
            ans$niter<-NA
         }
      }  ## end if using newuoa
## --------------------------------------------
#      else 
      if (meth == "nmkb") {# Use nmkb routine from dfoptim package
         mcontrol$maxit<-NULL # and null out control that is NOT used
         if (mcontrol$trace > 0) {
            mcontrol$trace<-NULL
            mcontrol$trace<-TRUE # logical needed, not integer         
         } else { mcontrol$trace<-FALSE }
         mcontrol$usenumDeriv<-NULL
         mcontrol$maximize<-NULL
         mcontrol$parscale<-NULL
         mcontrol$fnscale<-NULL
         nampar<-names(par) # save names 110508
         if (have.bounds) {
             time <- system.time(ans <- try(nmkb(par=par, fn=ufn, lower = lower, 
              upper = upper, control=mcontrol, fnuser=opxfn), silent=TRUE))[1]
         } else {
             time <- system.time(ans <- try(nmk(par=par, fn=ufn, 
                control=mcontrol, fnuser=opxfn), silent=TRUE))[1]
         }
         if (class(ans)[1] != "try-error") {
            ans$conv <- ans$convergence
            ans$convergence<-NULL
            ans$value<-as.numeric(ans$value)
            ans$fevals<-ans$feval
            ans$gevals<-NA
            names(ans$par)<-nampar # restore names 110508
           # What about 'restarts' and 'message'??
         } else {
            if (ctrl$trace>0) cat("nmkb failed for current problem \n")
            ans<-list(fevals=NA) # ans not yet defined, so set as list
            ans$value= ctrl$badval
            ans$par<-rep(NA,npar)
            ans$conv<-9999 # failed in run
            ans$gevals<-NA 
            ans$niter<-NA
         }
#         if (ctrl$maximize) {
#            ans$value= -ans$value
#         }
      }  ## end if using nmkb
## --------------------------------------------
#      else 
      if (meth == "hjkb") {# Use hjkb routine from dfoptim package
         if (mcontrol$trace > 0) {
            mcontrol$trace<-NULL
            mcontrol$info<-TRUE # logical needed, not integer         
         } else { 
            mcontrol$trace<-NULL
            mcontrol$info<-FALSE 
         }
         mcontrol$usenumDeriv<-NULL
         mcontrol$maximize<-NULL
         mcontrol$parscale<-NULL
         mcontrol$fnscale<-NULL
         nampar<-names(par) # save names 110508
         if( have.bounds) {
             time <- system.time(ans <- try(hjkb(par=par, fn=ufn, lower = lower, 
                upper = upper, control=mcontrol, fnuser=opxfn), silent=TRUE))[1]
         } else {
             time <- system.time(ans <- try(hjk(par=par, fn=ufn, 
                control=mcontrol, fnuser=opxfn), silent=TRUE))[1]
         }
         if (class(ans)[1] != "try-error") {
            ans$conv <- ans$convergence
            ans$convergence<-NULL
            ans$value<-as.numeric(ans$value)
            ans$fevals<-ans$feval
            ans$gevals<-NA
            names(ans$par)<-nampar # restore names 110508
         } else {
            if (ctrl$trace>0) cat("hjkb failed for current problem \n")
            ans<-list(fevals=NA) # ans not yet defined, so set as list
            ans$value= ctrl$badval
            ans$par<-rep(NA,npar)
            ans$conv<-9999 # failed in run
            ans$gevals<-NA 
            ans$niter<-NA
         }
      }  ## end if using hjkb
## ========================== END OF METHODS ==========================
      times[i] <- times[i] + time # Accumulate time for a single method (in case called multiple times)
      #?? timing of axsearch
      if ( any(is.na(ans$par)) ) {
         loopsleft<-0 # force end to looping
         if (ctrl$trace>0) { 
             cat("NA in returned parameters\n")
         }
         break
      } else {
         if (ctrl$axsearch.tries > 0) {
            asres<-axsearch(ans$par, fn=ufn, fmin=ans$value, lower = lower, 
               upper = upper, bdmsk=NULL, trace=(ctrl$trace-1), fnuser=opxfn)
            if (asres$bestfn<ans$value) {
               ans$par<-asres$par # reset parameters
               ans$conv<-3 # adjust convergence code for axial search failure
               par<-ans$par # for restart
               fmin<-asres$bestfn
               if (ctrl$trace>1) {
                  cat("Axial Search result with lower fn val\n")
                  print(asres$details)
               }
            }
         }
         if (loopsleft<=0) {
            if (ctrl$axsearch.tries>0) {
               if (asres$bestfn>=ans$value) {
                  if (ctrl$trace>0) {
                     if (asres$bestfn<ans$value) 
                       cat("Exiting with axial search best function value and parameters\n")
                     cat("END OF REPEAT CYCLE\n")
                     print(asres$details)
                  }
               }
            }
            break
         }
         } #?? until()
      } # end of repeat loop for method
## Post-processing -- Kuhn Karush Tucker conditions
#  Ref. pg 77, Gill, Murray and Wright (1981) Practical Optimization, Academic Press
      if (ctrl$trace>0) { cat("Post processing for method ",meth,"\n") }
      pars<-NULL # Unless length(pars)>0 we don't save anything later
      msg<-"Default -- method has not succeeded"
      gradOK<-FALSE
      hessOK<-FALSE # to ensure these are set when kkt FALSE
      ans$kkt1<-NA
      ans$kkt2<-NA
      ans$ngatend<-NA
      ans$nhatend<-NA
      ans$evnhatend<-NA
      ans$kfn<-opxfn$KFN # save counters -- NOTE: do not re-zero, includes axsearch
      ans$kgr<-opxfn$KGR
      ans$khess<-opxfn$KHESS
      ans$restarts<-(ctrl$axsearch.tries-1)-loopsleft # note that best fn has been saved
      ans$mtilt<-NA # in case no axsearch
      if (ans$conv!=9999) {
          if (ctrl$axsearch.tries>0) {
             ans$mtilt<-max(asres$details$tilt)
             if (asres$bestfn<ans$value) ans$restarts <- -ans$restarts # found better at end when set negative
          }
      }
      if ( ctrl$save.failures || (ans$conv < 1 ) ){  # Save the solution if converged or directed to save
         j <- j + 1 ## increment the counter for (successful) method/start case
         if (ctrl$trace && ans$conv==0) cat("Successful convergence! \n")  ## inform user we've succeeded
         # Testing final solution. Use numDeriv to get gradient and Hessian
         if (ans$conv != 9999) {
            gH<-NULL # initialize answer elements that MAY not get calculated  111023
            ans$kkt1<-NA
            ans$kkt2<-NA
            ans$ngatend<-NA
            ans$nhatend<-NA
            ans$evnhatend<-NA
            if (ctrl$kkt || hessian) {
               if (ctrl$trace>0) cat("Compute gradient and Hessian approximations at finish of ",method[i],"\n")
               #?? NOTE: we could do extra counts and timings here, and probably should
               gH<-ugHgenb(ans$par, fnuser=opxfn, bdmsk=bdmsk, lower=lower,
                  upper=upper, control=list(dowarn=FALSE, ktrace=(ctrl$trace-1)))
               gradOK<-gH$gradOK #?? These MAY not be OK after scaling
               hessOK<-gH$hessOK
               ngatend<-gH$gn*ctrl$parscale/ctrl$fnscale
               nhatend<-(diag(ctrl$parscale) %*% gH$Hn %*% diag(ctrl$parscale))/ctrl$fnscale
            } # end test if hessian computed; note gradient is also computed
            if (gradOK) { # have gradient
               if (hessOK) { # have hessian
                  akkt<- kktc(ans$par, ans$value, ngatend, nhatend, gH$nbm, 
			control=list(ktrace=ctrl$trace))
                  ans$kkt1<-akkt$kkt1
                  ans$kkt2<-akkt$kkt2
                  ans$ngatend<-ngatend
                  ans$nhatend<-nhatend
                  ans$evnhatend<-akkt$hev# computed Hessian eigenvalues in kktc
                  if (ctrl$trace>1) {
                     cat("KKT results: gmax=",akkt$gmax,"  evratio=",akkt$evratio,
                          "  KKT1 & 2: ",akkt$kkt1,akkt$kkt2,"\n")
                     print(akkt$hev)
                  }
               } else { # computing Hessian has failed
                  warnstr<-paste("Hessian not computable after method ",method[i],sep='')
                  if (ctrl$dowarn) warning(warnstr)
                  if (ctrl$trace>0) cat(warnstr,"\n") 
               }
            } else { # gradient failure
               if (ctrl$kkt || hessian) {
                  warnstr<-paste("Gradient not computable/computed after method ",method[i],sep='')
                  if (ctrl$dowarn) warning(warnstr)
                  if (ctrl$trace>0) cat(warnstr,"\n") 
               }
            }
         } # end NOT conv=9999
            ans$systime <- time
            # Do we want more information saved?
            if (ctrl$trace>1) { 
               cat("Save results from method ",meth,"\n") 
            }
            ans.ret[[j]] <- ans  ## save the answer. [[]] indexes the CONTENTS of the list
            ans.ret[[j]]$method <- method[i] # and we tag on the method with the $ linker
            if (ctrl$trace>2) { cat("Assemble the answers\n") }
            #    attr(ans.ret, "CPU times (s)") <- times ## save the accumulated times 
            #    if (ctrl$trace>0) { cat("Done CPU times\n") }
            meths <- lapply(ans.ret, function(x) x$method)
            pars <- lapply(ans.ret, function(x) x$par)
            vals <- lapply(ans.ret, function(x) x$value)
            fevals <- lapply(ans.ret, function(x) x$kfn)
            gevals <- lapply(ans.ret, function(x) x$kgr)
            hevals <-  lapply(ans.ret, function(x) x$khess)
            convcode <- lapply(ans.ret, function(x) x$conv)
            kkt1 <- lapply(ans.ret, function(x) x$kkt1)
            kkt2 <- lapply(ans.ret, function(x) x$kkt2)
            xtimes <- lapply(ans.ret, function(x) x$systime)
            restarts <- lapply(ans.ret, function(x) x$restarts)
            mtilt <- lapply(ans.ret, function(x) x$mtilt)
         }  ## end post-processing of successful solution
         if (ctrl$follow.on) {
            par <- ans$par # save parameters for next method
            if (i < nmeth && ctrl$dowarn && ctrl$trace>0) cat("FOLLOW ON!\n")
         }
      } ## end loop over method (index i)
      if (length(pars) > 0) { # cannot save if no answers
         meths[which(meths == "Nelder-Mead")]<-"NM" # tidy names for output
         meths[which(meths == "L-BFGS-B")]<-"LBFGSB"
         ansout <- data.frame(cbind(method=meths, par=pars, fvalues=vals, fns=fevals, grs=gevals, 
                   hes=hevals, rs=restarts, conv=convcode, KKT1=kkt1, KKT2=kkt2, 
                   mtilt=mtilt, xtimes=xtimes, meths=meths))
         attr(ansout, "details") <- ans.ret
         # sort by function value (DECREASING so best is last and 
         # follow.on gives natural ordering)
         if (ctrl$sort.result) { # sort by fvalues decreasing
#            if(ctrl$maximize) ord <- order(as.numeric(ansout$fvalues)) # ???!!!
#            else ord <- rev(order(as.numeric(ansout$fvalues)))
            ord <- rev(order(as.numeric(ansout$fvalues)))
            ansout <- ansout[ord, ]
         }
      } else {
      ansout<-NULL # no answer if no parameters
   }
   if (ctrl$maximize) {
       for (i in 1:j) { ansout$fvalues[[i]] <- (-1)*ansout$fvalues[[i]] }
   }
   if (ctrl$trace>2) cat("returning after fcount=",attr(opxfn,"fcount"),"\n")
   rm(opxfn) # remove the environment
   ansout # return(ansout)-- modified test version
} ## end of optimx 

Try the optplus package in your browser

Any scripts or data that you put into this service are public.

optplus documentation built on May 2, 2019, 6:48 p.m.