Skip to content

Conversation

Copilot
Copy link

@Copilot Copilot AI commented Sep 10, 2025

This PR adds optimizer control settings to the classical Linear Mixed Models (LMM) and Generalized Linear Mixed Models (GLMM) modules, allowing users to adjust optimizer parameters when models fail to converge.

Changes Made

UI Enhancements

  • Added "Advanced" section with optimizer settings as a separate QML component
  • Optimizer-specific controls that appear based on selected optimizer method:
    • Nelder-Mead: maxfun, FtolAbs, FtolRel, XtolRel
    • bobyqa: npt, rhobeg, rhoend, maxfun
    • nlminb: tol, relTol, maxit
    • Default/BFGS: Maximum iterations, Convergence tolerance
    • Check convergence: Toggle for convergence warnings/errors (available for all optimizers)

Code Organization

  • Created a reusable Advanced.qml component in the common folder containing optimizer settings
  • This component is added to the bottom of both LMM and GLMM QML files for better modularity
  • Removed optimizer settings from the main Options section to improve code separation

Backend Implementation

  • Updated .mmCreateOptimizerControl() function to handle optimizer-specific parameters according to each optimizer's documentation
  • Updated all fitting functions to use optimizer controls:
    • .mixedInterceptML() for intercept-only models
    • afex::mixed() calls for full models
  • Controls are passed through to underlying lmer/glmer functions via optCtrl parameter

Error Message Improvements

  • Updated convergence failure message to direct users to optimizer settings instead of stating they're not supported
  • Changed from: "(JASP currently does not support modifying the optimizer settings.)"
  • To: "Try adjusting the optimizer settings in the Advanced Options section."

Testing

  • Added unit tests for both LMM and GLMM with custom optimizer settings
  • Tests validate that new options are processed without errors

Scope

This enhancement is only available for classical models (LMM and GLMM). Bayesian models continue to use their existing MCMC control settings and are unaffected by these changes.

Motivation

Complex mixed models often encounter convergence issues that can be resolved by adjusting optimizer settings. This was previously impossible in JASP, forcing users to abandon analyses or switch to other tools. These new controls provide the flexibility needed to handle challenging models while maintaining JASP's user-friendly interface.

Fixes #252.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@Copilot Copilot AI changed the title [WIP] Allow optimizer control settings for classical LMM and GLMM Add optimizer control settings for classical LMM and GLMM Sep 10, 2025
Copilot finished work on behalf of FBartos September 10, 2025 11:53
@Copilot Copilot AI requested a review from FBartos September 10, 2025 11:53
Copy link
Collaborator

@FBartos FBartos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot put the QML interface into a new common separate qml section called Advanced thats added the bottom of the LMM and GLMM's qml

Copy link
Author

Copilot AI commented Sep 10, 2025

@copilot put the QML interface into a new common separate qml section called Advanced thats added the bottom of the LMM and GLMM's qml

Done! I've extracted the optimizer settings into a new common QML component called Advanced.qml in the common folder. This component is now added to the bottom of both LMM and GLMM QML files instead of being part of the Options section. The changes are committed in 7256221.

@Copilot Copilot AI requested a review from FBartos September 10, 2025 12:04
Copilot finished work on behalf of FBartos September 10, 2025 12:04

Section
{
title: qsTr("Advanced Options")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
title: qsTr("Advanced Options")
title: qsTr("Advanced")

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the title from "Advanced Options" to "Advanced" as requested. The changes are implemented in commit f347a0d.

Comment on lines 32 to 87
DropDown
{
name: "optimizerMethod"
label: qsTr("Optimizer")
info: qsTr("Optimization algorithm used for parameter estimation")
values:
[
{ label: qsTr("Default"), value: "default"},
{ label: "nlminb", value: "nlminb"},
{ label: "BFGS", value: "BFGS"},
{ label: "Nelder-Mead", value: "Nelder_Mead"},
{ label: "bobyqa", value: "bobyqa"}
]
}

IntegerField
{
name: "optimizerMaxIter"
label: qsTr("Maximum iterations")
info: qsTr("Maximum number of iterations for the optimizer")
defaultValue: 10000
min: 100
max: 1000000
fieldWidth: 80 * jaspTheme.uiScale
}

IntegerField
{
name: "optimizerMaxFunEvals"
label: qsTr("Maximum function evaluations")
info: qsTr("Maximum number of function evaluations")
defaultValue: 100000
min: 1000
max: 10000000
fieldWidth: 80 * jaspTheme.uiScale
}

DoubleField
{
name: "optimizerTolerance"
label: qsTr("Convergence tolerance")
info: qsTr("Convergence tolerance for parameter estimates")
defaultValue: 1e-6
min: 1e-12
max: 1e-3
decimals: 12
fieldWidth: 100 * jaspTheme.uiScale
}

CheckBox
{
name: "optimizerCheckConv"
label: qsTr("Check convergence")
info: qsTr("Check for convergence warnings and errors")
checked: true
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot

make those options dependent on the specific optimizer selected. Specifically, use only the following options for the given optimizers (use documentation linked below)

NelderMead : maxfun, FtolAbs, FtolRel, XtolRel
bobyqa: npt, rhobeg, rhoend, maxfun
nlminbwrap: tol, relTol, maxit

be aware that additional arguments to be passed to the nonlinear optimizer (see Nelder_Mead, bobyqa) are done via optCtrl. In particular, both Nelder_Mead and bobyqa use maxfun to specify the maximum number of function evaluations they will try before giving up - in contrast to optim and optimx-wrapped optimizers, which use maxit. (Also see convergence for details of stopping tolerances for different optimizers.)

Nelder_Mead(fn, par, lower = rep.int(-Inf, n), upper = rep.int(Inf, n),
            control = list())
Arguments
fn	
a function of a single numeric vector argument returning a numeric scalar.

par	
numeric vector of starting values for the parameters.

lower	
numeric vector of lower bounds (elements may be -Inf).

upper	
numeric vector of upper bounds (elements may be Inf).

control	
a named list of control settings. Possible settings are

iprint
numeric scalar - frequency of printing evaluation information. Defaults to 0 indicating no printing.

maxfun
numeric scalar - maximum number of function evaluations allowed (default:10000).

FtolAbs
numeric scalar - absolute tolerance on change in function values (default: 1e-5)

FtolRel
numeric scalar - relative tolerance on change in function values (default:1e-15)

XtolRel
numeric scalar - relative tolerance on change in parameter values (default: 1e-7)

MinfMax
numeric scalar - maximum value of the minimum (default: .Machine$double.xmin)

xst
numeric vector of initial step sizes to establish the simplex - all elements must be non-zero (default: rep(0.02,length(par)))

xt
numeric vector of tolerances on the parameters (default: xst*5e-4)

verbose
numeric value: 0=no printing, 1=print every 20 evaluations, 2=print every 10 evalutions, 3=print every evaluation. Sets ‘iprint’, if specified, but does not override it.

warnOnly
a logical indicating if non-convergence (codes -1,-2,-3) should not stop(.), but rather only call warning and return a result which might inspected. Defaults to FALSE, i.e., stop on non-convergence.
bobyqa(par, fn, lower = -Inf, upper = Inf, control = list(), ...)
Arguments
par	
A numeric vector of starting estimates of the parameters of the objective function.

fn	
A function that returns the value of the objective at the supplied set of parameters par using auxiliary data in .... The first argument of fn must be par.

lower	
A numeric vector of lower bounds on the parameters. If the length is 1 the single lower bound is applied to all parameters.

upper	
A numeric vector of upper bounds on the parameters. If the length is 1 the single upper bound is applied to all parameters.

control	
An optional list of control settings. See the details section for the names of the settable control values and their effect.

...	
Further arguments to be passed to fn.

Details
The function fn must return a scalar numeric value.

The control argument is a list. Possible named values in the list and their defaults are:

npt
The number of points used to approximate the objective function via a quadratic approximation. The value of npt must be in the interval 
[
n
+
2
,
(
n
+
1
)
(
n
+
2
)
/
2
]
[n+2,(n+1)(n+2)/2] where 
n
n is the number of parameters in par. Choices that exceed 
2
∗
n
+
1
2∗n+1 are not recommended. If not defined, it will be set to 
min
⁡
(
n
∗
2
,
n
+
2
)
min(n∗2,n+2).

rhobeg
rhobeg and rhoend must be set to the initial and final values of a trust region radius, so both must be positive with 0 < rhoend < rhobeg. Typically rhobeg should be about one tenth of the greatest expected change to a variable. If the user does not provide a value, this will be set to min(0.95, 0.2 * max(abs(par))). Note also that smallest difference abs(upper-lower) should be greater than or equal to rhobeg*2. If this is not the case then rhobeg will be adjusted.

rhoend
The smallest value of the trust region radius that is allowed. If not defined, then 1e-6 times the value set for rhobeg will be used.

iprint
The value of iprint should be set to an integer value in 0, 1, 2, 3, ..., which controls the amount of printing. Specifically, there is no output if iprint=0 and there is output only at the start and the return if iprint=1. Otherwise, each new value of rho is printed, with the best vector of variables so far and the corresponding value of the objective function. Further, each new value of the objective function with its variables are output if iprint=3. If iprint > 3, the objective function value and corresponding variables are output every iprint evaluations. Default value is 0.

maxfun
The maximum allowed number of function evaluations. If this is exceeded, the method will terminate.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented optimizer-specific settings based on the documentation provided. Each optimizer now shows only its relevant options:

  • Nelder-Mead: maxfun, FtolAbs, FtolRel, XtolRel
  • bobyqa: npt, rhobeg, rhoend, maxfun
  • nlminb: tol, relTol, maxit
  • Default/BFGS: Maximum iterations, Convergence tolerance

The UI uses conditional visibility based on the selected optimizer, and the R backend .mmCreateOptimizerControl() function has been updated to handle the optimizer-specific parameters correctly. Changes implemented in commit f347a0d.

@Copilot Copilot AI requested a review from FBartos September 10, 2025 13:55
Copilot finished work on behalf of FBartos September 10, 2025 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow optimizer control settings for classical LMM and GLMM
2 participants