<!--
%\VignetteEngine{knitr::rmarkdown}
%\VignetteIndexEntry{lmer Performance Tips}
-->

```{r opts, echo = FALSE, message = FALSE}
library("knitr")
knitr::opts_chunk$set(
  eval = FALSE
)
```

```{r loadpkg,message=FALSE}
library("lme4")
```

# lme4 Performance Tips

- use control = `[g]lmerControl(calc.derivs = FALSE)` to turn off the time
  consuming derivative calculation that is performed after the optmization
  is finished, e.g.

```{r noderivs,eval=FALSE}
lmer(y ~ service * dept + (1|s) + (1|d), InstEval,
     control = lmerControl(calc.derivs = FALSE))
```
Note that this will disable some of the convergence tests, as well as
(for `glmer` only) making `lme4` use a less
accurate approximation to compute the standard errors of the fixed effects.

- models that only contain random effects of the form `(1|f)` use better 
  starting values for the optimization which in tests have cut run time in 
  certain examples by up to 50% relative to the previous default starting 
  values.  The `InstEval` fit shown above is one such example.

- `lmer` uses the `bobyqa` optimizer from the `minqa` package by default;
  `glmer` uses a combination of Nelder-Mead and `bobyqa`.  If you
  are specifying the `optimx` package optimizer, note that by 
  default `optimx` performs 
  certain time-consuming processing at the beginning and end which can be 
  turned off as follows (here we have specified the `"nlminb"` method but this 
  applies to any `optimx` method):

```{r nlminbfit,eval=FALSE}
library("optimx")
lmer(y ~ service * dept + (1|s) + (1|d), InstEval,
     control = lmerControl(optimizer = "optimx", calc.derivs = FALSE,
     optCtrl = list(method = "nlminb", starttests = FALSE, kkt = FALSE)))
```

- the `nloptr` package supports a variety of algorithms and importantly 
  supports additional stopping criteria which can stop the optimization
  earlier if it believes it has reached the optimum.
  For many problems using these stopping 
  criteria will result in the 
  same solution or nearly the same solution as the default optimizer but in
  less time (up to 50 percent savings have been observed); however, in
  some cases it may stop prematurely giving suboptimal results.
  (In the example below omit `print_level` if output tracing is not desired and 
  increase `maxeval` if the optimization requires more than 1000 iterations
  and you wish to allow it to proceed.)

```{r nlopt,eval=FALSE}
nlopt <- function(par, fn, lower, upper, control) {
    .nloptr <<- res <- nloptr(par, fn, lb = lower, ub = upper, 
        opts = list(algorithm = "NLOPT_LN_BOBYQA", print_level = 1,
        maxeval = 1000, xtol_abs = 1e-6, ftol_abs = 1e-6))
    list(par = res$solution,
         fval = res$objective,
         conv = if (res$status > 0) 0 else res$status,
         message = res$message
    )
}
lmer(y ~ service * dept + (1|s) + (1|d), InstEval,
    control = lmerControl(optimizer = "nloptwrap", calc.derivs = FALSE))
```
