Skip to contents

bmm (development version)

Documentation

  • bmm now follows a written lifecycle policy. A deprecated function or argument keeps working, with a warning that names its replacement and the earliest release that may remove it, for at least one minor release: something deprecated in 1.5.0 works in every 1.5.x release and is removed in 1.6.0 at the earliest. Every deprecation and removal is listed under “Deprecated functions and arguments” in this file, and breaking changes are announced in a GitHub issue before the release that contains them. See the contributing guide (#441).
  • For developers: the header of the file use_model_template() writes named an outdated place for a new model’s test cases. It now points to model_cases_<name>() in tests/testthat/helper-model-<name>.R, which needs a stored_frame, sd_default, init and links case, and it lists tests/testthat/test-model_<name>.R for the model’s own tests (#580).

bmm 1.4.1

This is the 1.4.0 release resubmitted: 1.4.0 was sent to CRAN on 2026-10-15 and returned by the incoming checks because its check took too long on Windows. 1.4.1 runs fewer tests on CRAN and is otherwise the same package.

New models

  • Add Yes/No Signal Detection Theory (sdt_yn) for detection and old/new recognition tasks with aggregated response counts. It estimates sensitivity (d), the decision criterion and optionally the unequal-variance ratio (sdratio). The criterion is estimated as criterion, measured from the noise mean, or as bias, measured from the midpoint between the means: write the one your design holds constant, e.g. criterion for weak and strong targets tested against shared lures (#543). Also adds dsdt_yn(), rsdt_yn(), sdt_d(), sdt_criterion() and sdt_bias(). See ?sdt_yn for the parameters, links, default priors and the designs that identify sdratio. Thanks to @GidonFrischkorn
  • Add m-Alternative Forced Choice Signal Detection Theory (sdt_mafc) for accuracy-only m-AFC tasks, where one of m alternatives carries the signal (DeCarlo, 2012). It estimates sensitivity (d) from response counts and has no bias parameter. m may be a constant or the name of a data column, so trials with different numbers of alternatives can be fit jointly. Also adds dsdt_mafc() and rsdt_mafc(). See ?sdt_mafc for the parameters, the noise distributions, the default priors and how d relates to the sensitivity sdt_yn() reports. Thanks to @GidonFrischkorn
  • Add Ranking Signal Detection Theory (sdt_ranking) for tasks where observers rank m items, one of them a studied target, by how strongly they recognise them (Meyer-Grant et al., 2026). It estimates sensitivity (d) from the target’s rank counts, with Gumbel ("gumbel_min") or Gaussian ("normal") noise. m may be a constant or the name of a data column, so trials with different set sizes can be fit jointly. Also adds dsdt_ranking() and rsdt_ranking(). See ?sdt_ranking for the parameters, the response format and the default priors. Thanks to @GidonFrischkorn
  • Add Confidence Rating Signal Detection Theory (sdt_rating) for old/new recognition and detection tasks with ordered confidence ratings, aggregated to counts per rating category. It estimates sensitivity (d), the criterion (as criterion or bias, as in sdt_yn()) and the confidence thresholds, and optionally the unequal-variance ratio (sdratio); threshold_type chooses how the thresholds are parameterized. Also adds dsdt_rating() and rsdt_rating(). See ?sdt_rating for the parameters, the threshold types, the default priors and how d relates to the sensitivity sdt_yn() reports. Thanks to @GidonFrischkorn
  • Add two versions of sdt_rating(), selected with version: "dpsdt", the dual-process model (Yonelinas, 1994) with recollection of old items (Ro) and recall-to-reject of new items (Rn; as in Yonelinas, 2024), and "metad", the meta-d’ model, which estimates metacognitive efficiency as the log M-ratio logmratio (Maniscalco & Lau, 2012). Both reduce to the standard model while their extra parameters are off. Also adds mratio(), dsdt_dpsdt(), rsdt_dpsdt(), dsdt_metad() and rsdt_metad(). See ?sdt_rating. Thanks to @GidonFrischkorn
  • Add Continuous Dual-Process Signal Detection Theory (sdt_cdp) for old/new recognition with Remember/Know judgments (Wixted & Mickes, 2010). It estimates familiarity (dfam) and recollection (drec) sensitivity, the old/new criterion, the confidence thresholds and the Remember criterion (rcrit) from aggregated response counts; the recollection SD (sigmar), the familiarity-recollection correlation (rho) and a Know/Guess criterion (kcrit) can be freed through the formula. Also adds aggregate_sdt_cdp_data(), dsdt_cdp() and rsdt_cdp(). See ?sdt_cdp for the parameters, the response format and how drec relates to the sensitivity sdt_yn() reports. Thanks to @GidonFrischkorn
  • Add Multinomial Processing Tree models (mpt) for categorical responses from discrete processing states, such as the two-high-threshold model of recognition. Write each tree’s branches with mpt_tree(), as formulas or as a named list of strings, name a shared parameter alike in every tree, and fit the model to response counts, one row per participant and tree, with tree_id naming each row’s tree. links sets a logit or probit link for all or single parameters; parameters can be fixed in the formula (bmf(g = 0.5)) or given non-linear formulas. Also adds dmpt() and rmpt(). See ?mpt (#255). Thanks to @GidonFrischkorn
  • mpt() takes restrictions that equate parameters or fix them to constants, in MPTinR and TreeBUGS syntax (c("Dn = Do", "g = 0.5"), or readLines() of such a restrictions file), and simplex for parameters that sum to 1, such as guessing rates. A simplex group’s default uniform prior gives each of K members prior mean 1/K whatever their order; ?mpt shows how to get the per-branch uniform priors of TreeBUGS, which give the first member prior mean 0.5. Printing the model lists the restrictions, the simplex groups with their prior and an identifiability check (#255). Thanks to @GidonFrischkorn
  • Branch expressions can use data columns declared in covariates, for probabilities the design fixes, such as guessing rates that depend on the number of response options; bmm() stops when a row’s values make the branches of a tree not sum to 1. Response categories a tree cannot produce, such as distractor responses on trials without distractors, are declared with impossible in mpt_tree() and in the importers. dmpt() and rmpt() take covariate values too. See ?mpt_tree (#255). Thanks to @GidonFrischkorn
  • New functions mpt_from_string() and mpt_from_eqn() build an mpt() model from MPTinR-style model text (a string or readLines() output) or from an EQN file (multiTree, MPTinR, TreeBUGS); both take restrictions as their second argument. mpt_from_eqn() renames and reports names mpt() cannot use, such as D_o; it takes restrictions in the file’s names, maps per-tree category labels onto shared categories with categories, and numeric tree labels with tree_names. Unlike MPTinR and TreeBUGS, bmm reads an EQN file’s first line unless it is the equation count, so start a title line with # or delete it. See ?mpt_from_eqn (#255). Thanks to @GidonFrischkorn
  • New function mpt_data_reshape() reshapes the wide frequency tables of MPTinR and TreeBUGS, one row per participant and one column per category, into the one-row-per-participant-and-tree layout bmm() expects, with 0 for the categories a tree cannot produce. Columns that are no counts, such as a group factor, are carried to every row; columns = "mptinr" reads the unnamed, sorted columns of MPTinR data. See ?mpt_data_reshape. Thanks to @GidonFrischkorn
  • plot() draws the processing-tree diagram of an mpt_tree(), or of every tree of an mpt() model, from the branch expressions, and marks restricted parameters on their edges. See ?plot.mpt_tree (#255). Thanks to @GidonFrischkorn
  • New article Multinomial Processing Tree (MPT) models walks through specifying, fitting and interpreting mpt() models, constraining parameters, design-fixed covariates, a fit to real data, non-linear parameters, importing model files and migrating from MPTinR or TreeBUGS (#255). Thanks to @GidonFrischkorn

New datasets

  • Add broeder_schuetz_2009_e3, binary old/new recognition data from Broeder & Schuetz (2009, Exp. 3), with five base-rate conditions from 40 subjects. See ?broeder_schuetz_2009_e3.
  • Add meyer_grant_jakob_2025, ranking data from Meyer-Grant & Jakob (2025): 60 subjects ranking 3, 4 or 5 faces, aggregated to the target’s rank counts per subject and set size. See ?meyer_grant_jakob_2025.

New features

  • New function ppp() computes the goodness-of-fit statistics T1 (category frequencies) and T2 (their covariances across participants) of Klauer (2010) with their posterior predictive p-values, the checks TreeBUGS’ PPP() reports, for mpt(), m3() and the SDT count models. level = "participant" gives one T1 per participant. See ?ppp (#497).
  • m3() accepts choice_rule = "gaussian", a Thurstonian rule: every candidate gets independent normal noise and the strongest one is chosen ("softmax" is the same with Gumbel noise). Activation estimates are on a smaller scale than under "softmax", and conclusions about c against a, or about conditions that change the number of candidates such as set size, can differ between the rules. Fits take much longer than with "softmax". The Gaussian rule requires whole numbers of options. See choice_rule in ?m3. Thanks to @GidonFrischkorn
  • New function bmm_setup() checks whether your machine can fit models: the C++ toolchain, cmdstanr with CmdStan, rstan, and the backend bmm() will use. It then compiles and samples a small test model and gives one fix for every check that failed. When bmm() fails because the compiler or the Stan backend is missing or broken, the error now points to bmm_setup() (#431).
  • New post-processing functions for the signal detection models: roc_sdt(), roc_observed() and auc_sdt() return the model-implied and observed ROC and the area under it for sdt_yn and sdt_rating; latent_sdt() the latent evidence distributions of all four models; sdt_thresholds() the confidence thresholds of sdt_rating; and sdt_sensitivity() the sensitivity on the noise, signal or root-mean-square SD scale. All but roc_observed() print a summary; plot() draws the ROC with the observed points, the AUC and the latent distributions; summary() says when d is d_a. The new article Signal Detection Theory models walks through all four models and these functions (#370). Thanks to @GidonFrischkorn
  • bmm(file_refit = "on_change") now works instead of warning and falling back to "never". The cached fit is returned only while the Stan code, the Stan data, the factor levels of the model variables and the algorithm are unchanged; any change refits. As in brms, sampler settings are not compared: rerunning with a higher adapt_delta, more iter, another seed or save_pars(all = TRUE) returns the cached fit unchanged. Use file_refit = "always" in those cases (#411).
  • bmm_options(file_refit = ) accepts the same values as bmm(). It previously required a logical, so the string forms could be set only through options(bmm.file_refit = ) (#411).
  • update() gained the file and file_compress arguments. file writes the updated fit in the same order bmm() does, i.e. after the bmm postprocessing (#411).
  • New function prior_info() reports, for each parameter of a fitted model, its link function, the prior actually used on the sampling scale, and whether it was a bmm default, a brms default, or user-specified; parameters left with improper flat priors are flagged. format = "text" produces methods-section-ready sentences. Parameters that exist only because the family machinery requires them are omitted, so the report lists the model’s own vocabulary: the mu brms forces on the response-time custom families, the mu2/kappa2 second component of a brms::mixture() family, and the theta2 mixture-weight reference the sampler holds at zero (#391).
  • New function model_citation() returns the references of a model or a fitted model, one per element, for your reference list. Fits saved with an older version of bmm get the current references. The model references now include their DOIs, and several were corrected, e.g. the year of Miller et al. for cswald() is 2018, not 2017. If you copied a reference from a help page, check it again. The help pages of ezdm() and sdt_mafc() list their two references as separate items (#476).
  • New function native_parameters() returns posterior draws of the model parameters on their native scale, evaluated over a grid of predictor values. Because draws are returned rather than summaries, contrasts between conditions are ordinary arithmetic on the draws. The inverse link transformation is applied to the draws before any summary is computed, so quantiles and credible intervals are exact. Mixture weights are transformed jointly through their softmax (#388).
  • New developer generic native_transform() defines how a model maps its parameters from the sampling scale to the native scale. The default method covers every transformation that can be expressed through an elementwise links declaration in a .model_*() constructor, or through a single softmax group that is active on every row of the data, so most new models are supported without writing a method. native_transform.non_targets() ships as the worked example of a design-dependent method (#388).
  • New function bmm_data_check() prints a report on your data before fitting, without compiling the model. It runs the same checks as bmm(), collects all errors, warnings and messages, and summarizes the response variables with the coding the model expects, the data columns each parameter formula uses, the number of observations per design cell, and common data mistakes, such as circular responses in degrees or misplaced NA values in nt_features. Developers can add model-specific checks with data_check_findings() methods and data_check_finding() (#389).
  • Random-effects standard deviations now get default priors suited to each parameter instead of the student_t(3, 0, 2.5) default of brms, which on a log link lets individual parameters vary by a factor of 12 around the group value. The priors are exponential() on the link scale, with rates from 1 to 4 depending on the parameter; prior_info() shows them for a fit. Estimates from models with random effects can differ from earlier versions. To change a prior, address the parameter with dpar or nlpar, e.g. set_prior("exponential(2)", class = "sd", nlpar = "kappa") (#342).
  • Correlations among random effects now get an lkj(2) default prior instead of the uniform lkj(1) of brms. For two correlated effects this lowers the prior probability of a correlation beyond ±.9 from 10% to 1.45%, so estimated correlations shrink slightly towards zero. The default applies only to models that estimate a correlation matrix, not to (1 | ID) or (x || ID). To return to the previous behaviour, pass prior = set_prior("lkj(1)", class = "cor"). prior_info() shows the prior as lkj(2), class cor, as written in set_prior() (#417).
  • bmm() now starts every model from tight initial values for its random effects, and mixture2p, mixture3p and imm start their population-level parameters near the centre of their default priors; these models and m3 previously used init = 1. This reduces the lkj_corr_cholesky_lpdf: Random variable[k] is 0, von_mises_lpdf: Scale parameter is inf and Rejecting initial value messages at the start of warmup. Posteriors are unaffected. A user-supplied init still replaces the bmm default.
  • bmm() and update() now start Stan’s step-size search at 0.01 instead of 1, which further reduces the lkj_corr_cholesky_lpdf: Random variable[k] is 0 and von_mises_lpdf: Scale parameter is inf messages at the start of warmup. The adapted step size and the posterior do not change. A step_size (or stepsize) in your own control list wins, and bmm_options(step_size = FALSE) restores Stan’s default for new fits; update() keeps the step size a fit was run with.
  • pp_check() can now check every observable of a model’s likelihood, not just the primary response. brms’s pp_check() only plots the brms Y variable, so the ddm and cswald responses and the ezdm RT variance and accuracy went unchecked. The new resp_var argument selects the observable (e.g. pp_check(fit, resp_var = "response")), including derived ones ("signed_rt" for the RT models; "mean_pc", the proportion of upper-boundary responses, for ezdm), and resp_var = "all" returns a panel of all checks drawn from one shared joint simulation. pp_check_vars(fit) lists the available checks, the plot type each uses by default, and the brms standata slots it reads; model authors declare observables via the pp_observables()/pp_simulate() S3 generics. The ezdm checks show how each statistic is distributed across design cells: density overlays for the RT statistics and a histogram for the proportion of upper responses (the new type = "bars_binned"); type = "intervals" compares each cell with its own predictive interval instead. Where an observable is undefined for some cells (an ezdm boundary reached by fewer than two responses), observations are dropped only when the observed value is undefined; undefined simulated values are absorbed by dropping those posterior draws, so the number of observations checked does not depend on ndraws (#401).

Bug fixes

  • bmm() says that a function passed as data is not coercible to a data frame also while the plyr namespace is loaded (a pp_check() loads it); it used to say the data has no observations.
  • brms::kfold() and reloo() now run on m3, sdt_rating, sdt_ranking, sdt_cdp, sdt_mafc (with m a column), mixture3p and imm fits, and so does update(fit, newdata = fit$data[rows, ]). They stopped before the first refit with Response columns ... missing in the data, The response variable(s) ... missing in the data or a complaint about set_size, because update() checked the fit’s stored data as if you had passed them. No refit and no change to your calls is needed.
  • A prior set on a whole non-linear parameter, e.g. set_prior("normal(0, 2)", class = "b", nlpar = "kappa"), did not reach its intercept in mixture2p, mixture3p, imm and m3: the intercept kept the bmm default, and brms warned that your prior would not be used. Your prior now applies to the intercept too. A prior with coef = "Intercept" still sets the intercept alone. Refit models that used such a prior (#526).
  • update() with a formula. that adds predictors to a parameter fitted with an intercept only gave the new coefficients a flat prior instead of the bmm default, in models such as mixture2p, mixture3p, imm and m3. With 0 + predictor, the whole parameter was left without a prior. They now get the priors bmm() gives them. Rerun such updates.
  • A saved fit, whether through file = in bmm() or update() or with saveRDS(), held extra copies of the data and of the fitted model. The file could be several times larger than needed, and more so for a data frame with many columns the model does not use. New fits are saved at their own size. A fit saved by an earlier version keeps its size until you refit it with bmm() or with update(recompile = TRUE); update() alone may carry the copies over (#518).
  • conditional_effects() no longer fails with Number of trials must be non-negative integers on m3 fits whose rows differ in their number of trials (#510).
  • conditional_effects() on m3 fits ignored conditions, int_conditions, surface, spaghetti, draw_ids and re_formula = NULL without a warning, and returned a wrong interval for probs. They now apply, as they do for sdt_rating, sdt_cdp and sdt_ranking. The estimate is the posterior median and se__ its MAD, as in brms; m3 used to report the mean and standard deviation, and robust = FALSE returns them (#512).
  • conditional_effects(spaghetti = TRUE) drew its lines on a different scale than the curve for some parameters: on the sampling scale at the default scale = "native" (e.g. kappa and thetat of mixture3p) and on the native scale at scale = "sampling" (e.g. kappa and c of sdm). The lines now follow scale. Redraw plots made with spaghetti = TRUE (#512).
  • On m3, sdt_rating, sdt_cdp and sdt_ranking fits, conditional_effects() now stops with a message naming the argument if you pass categorical, ordinal, select_points, transform or method = "posterior_predict", which do not apply to their latent parameters. m3 used to ignore them. plot(points = TRUE) on m3 failed with Column resp__ not found and now works (#512).
  • conditional_effects() on the mixture3p parameters thetat and thetant at the default scale = "native" returned probs with lower above upper, ignored draw_ids and re_formula = NULL, and with ndraws combined draws that did not belong together. All four now work. Default output is unchanged (#512).
  • summary() of m3, sdt_rating, sdt_ranking and sdt_cdp fits left the name of the data blank (Data: (Number of observations: 120)). It now names the data passed to bmm(). Refit to get the name into the summary; fits saved earlier keep the blank.
  • native_parameters() failed with Cannot build a prediction grid: variable(s) ... are not columns of the model data whenever a parameter had a non-linear formula, e.g. bmf(c ~ exp(nlc), nlc ~ 1 + set_size). It now returns the grid over the predictors of every formula involved, as it does for linear formulas.
  • native_parameters() failed with Cannot build a prediction grid: variable(s) 'p' are not columns of the model data for a fit whose formula links random effects across parameters, e.g. (1 |p| id), and for a nested grouping such as (1 | g1/g2). For m3 fits, conditional_effects() also listed the variables of a nested grouping as effects. bmm_data_check() reported the ID p as a missing data column. All three work now.
  • bmm() and default_prior() no longer warn that flat priors will be used when a parameter that has no bmm default prior gets a non-linear formula. The warning now appears only when a formula replaces a default prior. The parameters inside such a formula still get flat priors unless you set them: in m3 with version = "ss", b ~ exp(nlb) leaves nlb flat.
  • posterior_epred() failed with object 'posterior_epred_ddm' not found (or the name of another model) on ddm, cswald, ezdm, sdt_yn, sdt_mafc and sdm fits, and printed the same message inside performance::model_performance() and report::report(). It now returns the expected response of these models, also for fits saved by earlier versions; ?posterior_epred.bmmfit says what that is for each model. For sdm, mixture2p, mixture3p and imm it and fitted() stop with an error pointing to native_parameters(), because the mean of a circular response error is not useful (#475).
  • A name in a model’s links argument that names no parameter of that model is now an error instead of being added: sdm(resp_error = "y", links = list(kapa = "identity")) reported a parameter kapa while kappa kept its default link. A name one edit from a parameter is read as that parameter, with a warning; a link allowing values the default excludes warns once. A link the model cannot apply is refused too, naming those it can: links = list(bound = "loglog") used to fail with argument is of length zero. sdm, mixture2p, mixture3p and imm take none (#420).
  • sdm no longer fails before sampling when mu is predicted without an intercept (Initial values for vectors are only specified for b-coefficients, sd and z parameters). sdm, ddm, ezdm and cswald no longer fail with the rstan backend when a parameter has exactly one slope (no more scalars to read), nor when their random effects use gr(..., by = ).
  • prior_info() no longer fails for a fit whose formula needs data2, e.g. (1 | gr(ID, cov = A)) (Object 'A' was not found in 'data2').
  • prior_info() now names every kind of correlation prior the way set_prior() documents it, not only the one on group-level effects. A fit with two or more me() terms was reported as class Lme with lkj_corr_cholesky(1), brms’s internal spelling, instead of corme with lkj(1). The same applied to rescor, lncor and cortime.
  • update() with newdata or a new formula. no longer fails before sampling for a model that starts from bmm’s initial values (no more scalars to read on rstan, Fitting failed on cmdstanr). The initial values are now built for the data and formula the update fits; an init passed to update() still wins (#415).
  • Formulas with mo() or s() terms no longer start with a partial init list, which made cmdstanr print Init values were only set for a subset of parameters. extract_parameter_dimensions() now reads a declaration sized by an element of a data array, such as simplex[Jmo_c[1]], and simplex parameters get an initial value.
  • file_refit no longer accepts a logical that is not a single TRUE/FALSE. NA, logical(0) and multi-element logicals were coerced to "never", so file_refit = cfg$refit with a missing or NA config key silently returned the cached fit instead of erroring as it did before "on_change" was added (#411).
  • Under file_refit = "on_change", a cached fit that restructure() cannot bring forward to the current bmm version is now refitted rather than aborting bmm() with “Unable to restructure the object… Please refit” — that is the one thing "on_change" exists to do. A cached fit with no algorithm field also no longer aborts on brms’s bare stopifnot(); the comparison falls back to the Stan code, Stan data and factor levels (#411).
  • Fix file_refit silently falling back to "never" for any capitalisation other than all-lowercase. "Always" passed validation, which lowercases, but the coercion that followed it did not, so bmm(..., file = , file_refit = "Always") returned the cached fit instead of refitting (#411).
  • update(fit, file = ) now saves a bmmfit. It used to save a plain brmsfit that bmm() refused to read back (Object loaded via 'file' is not of class 'bmmfit'). When the file already existed, update(fit, newdata = , file = ), as in a script that caches its fits, silently returned the old fit instead of the updated one. Refit anything produced this way (#411).
  • Fix options(bmm.default_priors = FALSE) making bmm() fail for every model instead of fitting with flat priors. set_default_prior() returned NULL, which combine_prior() indexed unconditionally (second argument must be a list); set_default_prior() now returns an empty prior, and combine_prior() passes a NULL argument through (#391).
  • update() now configures the likelihood for the threading spec that will actually be used. brms::update.brmsfit falls back to the original fit’s threads when the argument is not passed, but bmm only inspected the new request, so updating a threaded fit without repeating threads emitted the serial likelihood chunk into threaded Stan code — for the sdm model a compile error (Identifier 'COSN' not in scope), and for any loop = FALSE custom family a silently mis-sliced likelihood. The fallback follows brms in distinguishing an absent threads argument from an explicit threads = NULL, which turns threading off.
  • update() no longer lets a global options(brms.threads = ) reach the likelihood configuration. Updating an unthreaded fit under a session-wide threading option emitted the sliced likelihood chunk while brms generated serial Stan code (Identifier 'start' not in scope); the spec of the fit being updated now always wins, as it does in brms.
  • update() now re-resolves every parameter whose constant the new formula changes. update() never called check_model(), so the constant was never resolved again and the original fit’s prior overrode the freshly configured one: update(fit, bmf(..., mu ~ 1)) returned a model in which mu was still pinned, update(fit, bmf(..., kappa = 5)) one that reported kappa = 5 while Stan estimated kappa freely, and update(fit, bmf(..., mu = 0.5)) one that reported mu = 0.5 while Stan kept mu at the original value — all three with no error or warning.
  • ezdm(version = "4par") no longer drops cells in which a boundary was reached fewer than twice or has no RT summaries. brms excluded the whole row, response counts included, so the cells with the most extreme accuracy were missing. bmm() now keeps them and warns how many there are; refit such models. newdata for log_lik() or predict() now needs the columns rt_used_upper and rt_used_lower, as in fit$data. Where some cells have no usable RT summaries at the upper boundary, pp_check() without resp_var leaves those cells out and no longer accepts newdata or the loo_* types (#430).
  • dezdm(version = "4par") now returns the density without a boundary’s response-time terms where that boundary’s summaries are NA, instead of NA (#430).
  • ezdm_summary_stats(method = "mixture") now returns n_upper and n_trials for the responses its mean_rt/var_rt are based on. It previously returned the raw counts beside cleaned moments, so every cell told ezdm it rested on more responses than it had and its posterior came out too narrow. Summary statistics change and fits are not reproduced; recompute them and refit. Use guess_rate to set the accuracy expected of a contaminant response (version = "3par" only). A cell whose accuracy guess_rate cannot explain is corrected without it, and warns. adjust_ezdm_accuracy() is deprecated — applying it now removes the same contaminants twice (#423).
  • The ezdm likelihood no longer assumes that reaction times are normally distributed. Because response times are right-skewed, it treated each cell as more informative than it is, and posteriors, especially for bound, came out too narrow. Existing ezdm fits are not reproduced: in our simulations, credible intervals for subject-level bound estimates widen by 20% to 75%, and fitting takes longer (see ?ezdm_dist). A fit cached with bmm(file = ) is reused unless file_refit = "on_change". rezdm() no longer truncates mean_rt at ndt, so with very few trials it can return mean_rt <= 0, which bmm() rejects (#407).
  • Fix numerical failures in ezdm. With large drift rates, the 4-parameter model returned NaN in dezdm(), rezdm(), log_lik() and pp_check(), and both models could return a log-likelihood of -Inf when the predicted accuracy was very close to 1 but a cell contained errors. Near zero drift, the likelihood jumped where the code switched between formulas, and the response counts said nothing about the direction of drift. dezdm() now rejects counts that are not whole numbers (#407).
  • The mixing weights thetat (mixture2p) and thetat/thetant (mixture3p) had no effects prior, so any regression coefficient on them was flat. They now get normal(0, 2.5) on the logit/softmax scale (#305, #466).
  • The circular bias parameters mu (sdm) and mu1 (mixture2p, mixture3p, imm) had a student_t(1, 0, 1) intercept prior, uniform over the circle under the tan_half link, and no effects prior, which gave non-reference levels a bimodal prior on the native scale. They now get normal(0, 0.5) on the intercept (95% of the prior bias within 89 degrees of the target), normal(0, 0.25) on effects (a condition difference with SD 24 degrees) and exponential(4) on random-effects SDs. mu/mu1 stay fixed at 0 by default, so this only affects models that free them (#306).
  • The cswald model no longer rejects extreme response times at high drift rates. Its likelihood returned NaN there, which the sampler reported as “Log probability evaluates to log(0)” or as divergent transitions, and could bias the posterior for data with long tails. Fits of such data change; refit to benefit (#387).
  • Passing threads = NULL while options(brms.threads = ) is set no longer produces Stan code that fails to compile (Identifier 'start' not in scope) for the sdm and cswald models. threads = NULL now turns parallelization off, as it does in brms.
  • rsdm() and rmixture2p() now draw each value from its own parameter values when a parameter has one value per draw. Before, every value came from the mix of all parameter values, so posterior_predict() and pp_check() for sdm fits were too wide. Fits are unaffected; rerun posterior_predict() or pp_check(). rsdm() no longer fails for large c and kappa, and rmixture2p() accepts vectors of kappa and p_mem. rejection_sampling() takes arguments of length n in ... per draw and requires a whole-number n. The same seed now gives different draws from rsdm(), rmixture2p(), rmixture3p() and rimm() (#445).
  • update() without newdata no longer fails for m3 fits (The response variable(s) corr, other, dist, npl missing in the data) or for mixture3p and imm fits whose formulas do not use set_size (The set_size variable ‘set_size’ must be either a variable in your data or a single numeric value), and no longer warns for sdt_yn fits that the reserved column dist_type will be overwritten (#429).
  • update() without newdata no longer fails for mixture3p and imm fits in which every response at the largest set size is NA (The number of columns for non-target values in the argument ‘nt_features’ should equal max(set_size)-1). Fits saved before this fix can be updated as they are; the workaround of passing the original data as newdata is no longer needed (#459).
  • m3 no longer ignores num_options when its numbers are named after the response categories, as in num_options = c(corr = 1, other = 4, dist = 5, npl = 5). Each category’s activation was combined with itself instead of its number of options, with no error or warning. Such names are now matched to the categories, in any order; names already used by a data column or a parameter are an error. If fit$formula$formula shows a category twice, as in log(corr * corr), refit the model, e.g. with update(fit) (#449).
  • m3 no longer pairs column names in num_options with the wrong response categories when they are named after the categories in another order than resp_cats, as in num_options = c(other = "n_other", corr = "n_corr", dist = "n_dist", npl = "n_npl"). Each category silently got another category’s column. Such names are now matched by name; m3() refuses partly or repeatedly named column names and NA among numeric counts. If fit$formula$formula shows the wrong column, e.g. log(other * n_corr), refit with bmm() on your original data; update(fit) without newdata stops when it cannot rebuild such a fit (#457).
  • dm3() and rm3() no longer mix up the response categories when the activation formulas are not listed in the order of resp_cats. Each number of options was applied to whichever activation stood at its position, so densities and simulated counts were wrong; rerun such calls. Fitting with bmm() was not affected. The columns of rm3() and the order of x in dm3() now follow resp_cats.
  • m3 with version = "custom" now stops when a parameter in an activation formula has no formula of its own, such as c in corr ~ b + a + c without c ~ 1. Before, such a parameter was fitted with only an intercept, announced only by the message “No formula for parameter c provided”. Scripts that relied on this now fail; add the missing formula, e.g. c ~ 1. Numeric num_options can be used by name, n_opt_<category> unless you named them, and nTrials and Idx_<category> work in non-linear activations. Earlier fits are unaffected (#495).
  • m3 now warns when a response count is missing (NA) in a row where its category has options, and says how many were counted as 0. Missing counts where the category has no options, as for dist in oberauer_lewandowsky_2019_e1, are still read as 0 without a warning. Data columns named Y, nTrials or Idx_<category> are now refused, not silently replaced; rename them. Response categories may still be called Y or nTrials. Missing values in num_options columns now give an error naming those columns instead of “missing value where TRUE/FALSE needed”; enter 0 where there were no options (#496).
  • log_lik() for cswald fits no longer returns NaN for a posterior draw whose ndt is at or above an observation’s RT, which happens with newdata such as held-out folds. Such draws now give -Inf, or 0 for errors in the "simple" version, matching the likelihood the model was fitted with. kfold() and loo::elpd() no longer break on the common case of a few such draws; an observation whose RT is below every posterior draw of ndt can still return a non-finite estimate, which reports a real mismatch with the data, not this bug. Rerun affected computations (#453).
  • ddm, ezdm and cswald no longer fail before sampling when a parameter gets a link that excludes values its default link allows, such as a log or softplus link on drift (Variable 'Intercept_drift' has NA values) (#460).
  • imm(), sdm() and ezdm() now refuse an unknown version when the model is created, with an error that lists the valid ones. Before, imm() accepted it and bmm() then failed with no applicable method for 'configure_model', ezdm() failed only inside bmm(), and sdm() silently fitted the "simple" version, so such sdm() fits need no rerun. These three and m3() also accept an unambiguous abbreviation, such as version = "f" for "full" (#467).
  • brms::combine_models() now merges a fit with a refit made by update(), e.g. update(fit, seed = 2) to rerun chains that failed. Before, it stopped with “Models 1 and 2 have different formulas”. Both fits must come from the same bmm version, so rerun update() refits made with an earlier version (#464).
  • Loading bmm no longer breaks printing a message condition, such as one caught with tryCatch(message("hi"), message = identity). Printing it failed with argument 1 (type ‘list’) cannot be handled by ‘cat’ (#481).
  • adjust_ezdm_accuracy() adjusts one cell at a time. Passing vectors failed with an unrelated R error; it now stops with a message that points to ezdm_summary_stats(), which handles grouped data. The function remains deprecated (#409).
  • Fix pp_check() for the RT models silently producing a misleading plot with negative_rt = TRUE: brms forwarded the argument to posterior_predict() (signed predicted RTs) while the observed response times stayed unsigned, so the plot looked like severe misfit. pp_check() now checks the "signed_rt" observable (with a message) so both halves are signed, and errors for models without signed RTs (#401).
  • Remove the unreachable dv argument of the internal ezdm posterior_predict functions and its documentation. posterior_predict(fit, dv = "var_rt") silently returned mean_rt: brms forwards posterior_predict() dots to prepare_predictions() only, never to the family’s prediction function, so dv was dropped without a warning and has never worked. Use pp_check(fit, resp_var = ...) instead (#401).
  • Fix rezdm(version = "4par") crashing (recycling errors) when some but not all simulated cells produced fewer than 2 responses at the upper boundary — the upper-boundary branch did not subset its moments and non-decision time by the affected cells, while the lower-boundary branch did. The bug was invisible whenever every cell shared the same parameters (the unindexed vectors are then constant), and is reachable through rezdm() directly; fixing it is a prerequisite for the multi-observable pp_check(), which calls rezdm() with per-draw posterior parameters (#401).
  • Fix the grouped plot type auto-selected by pp_check(group = ) (e.g. dens_overlay_grouped) being silently dropped when type was not supplied, so that brms fell back to the ungrouped type and bayesplot warned about an unrecognized group argument.

Deprecated functions and arguments

  • parameters() is deprecated and will be removed in bmm 1.6.0. Use parameter_info() instead: it takes the same arguments and returns the same table. Until then parameters() keeps working and warns. The parameters package exports a function of the same name, so with both packages attached, parameters() calls the function from whichever package is attached last (#474).
  • supported_models() is deprecated and will be removed in bmm 1.6.0. Use bmm_models() instead. It takes no arguments and returns the model names, which print as the grouped list. supported_models(print_call = FALSE) becomes bmm_models(). Until then supported_models() keeps working and warns. The insight and parameters packages export a function of the same name, so with bmm and either of them attached, supported_models() calls the one attached last (#481).

Documentation

  • ?conditional_effects.bmmfit now says that se__ is the spread of the draws on the scale brms or bmm summarises them on, so with a link other than identity it can sit on a different scale than estimate__. Use lower__ and upper__ for uncertainty on the scale you asked for. A new section covers models with a multinomial family (#512).
  • New online article on testing hypotheses and comparing models: testing effects on a parameter with hypothesis(), comparing a model with and without a predictor, and comparing different models fitted to the same data, with leave-one-out cross-validation and Bayes factors (#308).
  • ?ddm, ?ezdm and ?cswald now give the scale of zr: ddm() and cswald(version = "crisk") fix it at 0 on its logit scale, an unbiased 0.5, and ?ddm explains why response-coded data need zr estimated. ?cswald says that drift has a log link in "simple" and an identity link in "crisk", and that the "simple" bound is half the boundary separation. ?ezdm_summary_stats says that method = "robust" gives larger drift and smaller boundary estimates than "mixture" (#540).

Other changes

  • ezdm_summary_stats() and flag_contaminant_rts() now run up to 500 EM iterations (maxit) instead of 100. Cells with almost no contaminants can need more than 100; they fell back to robust moments in ezdm_summary_stats() and to NA probabilities in flag_contaminant_rts(), which a filter such as contam_prob <= 0.5 drops entirely. Such cells now get mixture-based values, so summary statistics and flags from earlier runs can differ for them. The warning now says when the EM stopped at maxit. Pass maxit = 100 to reproduce earlier results (#540).
  • bmm_models() (formerly supported_models()) now lists the models under four task groups (continuous reproduction; categorical recall and n-AFC decisions; detection, recognition and confidence judgments; choices and response times), one line per model with its constructor and full name. The argument lists are gone from the printout; ?modelname has them (#440).
  • The home page of the website and the README now start from your task and list the models for each one, with links to the matching articles. The lists are generated from the package, so they stay current as models are added (#440).
  • New Get started page on the website: install and check your machine with bmm_setup(), find the model for your task, including a table of the signal detection models by response format and noise distribution, and fit a first model to broeder_schuetz_2009_e3 step by step (#440).
  • The model lists on the Get started page are now tables with one row per model version: the data columns each version needs, by argument, and the parameters it estimates (#467).
  • For developers: the model file written by use_model_template() now follows the package’s current conventions. document() runs on it without warnings, the new model passes the website reference check, its default priors include sd, and with custom_family = TRUE posterior_epred() works once the generated stubs are filled in. The file header lists the registries and test fixtures a new model must also update (#490).
  • The cswald likelihood now evaluates all observations in one call instead of one at a time. This makes fitting faster, improves the accuracy of the gradients the sampler uses, and adds support for within-chain parallelization: bmm(..., threads = 2) now works for cswald as it does for sdm. The posterior is unchanged (#387).

bmm 1.3.2

CRAN release: 2026-09-16

Changes to default priors

  • Recalibrated the default priors for the m3 activation parameters (a, c) so that the simple and softmax choice rules imply a comparable, broad prior-predictive range of average performance, and so that the softmax defaults place equal prior means on general (a) and context (c) activation — centering the implied c - a prior at zero for fair comparisons under cell-means coding. The mis-scaled normal(0, 2) effect prior on c is replaced by the shared normal(0, 0.5). Because the simple rule requires c > a to predict accurate recall, its defaults remain asymmetric; direct comparisons of context and general activation should use the softmax choice rule (#364).

New features

  • The sdm model now supports within-chain parallelization via the threads argument (e.g. bmm(..., threads = 2)), reducing fitting time by up to ~45% in benchmarks (#374).
  • Add softplus as an opt-in link function for positively-bounded parameters, as an alternative to the default log link. softplus(x) = log(1 + exp(x)) keeps parameters positive while growing linearly for large values, avoiding the numerical blow-up of exp() and giving predictor effects an additive (rather than multiplicative) interpretation on the natural scale. Enable it per parameter via the model’s links list, e.g. m3(...)$links <- list(c = "softplus", a = "softplus") or ddm(rt, response, links = list(bound = "softplus")) (#363).

Bug fixes

  • Fix initial values being set in two places, where the init returned by configure_model() was silently overwritten by create_initfun(). This caused the m3 model’s intended init = 0 (needed for stable sampling with the simple choice rule and an identity link) to be lost, and left dead init code in the sdm model. create_initfun() is now the single source of truth for initial values, with model-specific behaviour expressed through S3 methods (#375).
  • Fix .pwald() returning NaN/-Inf in the upper tail of the shifted-Wald survival function, which propagated to dcswald() (and therefore log_lik/posterior_predict) for the cswald model at extreme reaction times. The R-side survival now uses the stable log_diff_exp form already used by the Stan likelihood (swald_lccdf) (#376).
  • Fix print() for model summaries selecting regression-coefficient rows by an unanchored substring match, so a parameter such as a could pull in rows of another parameter like kappa (e.g. in the imm model). Rows are now matched on the exact parameter prefix. This also fixes a crash when only a single coefficient row is shown (#379, #369).
  • create_initfun() now matches Stan parameters to model parameters with a word-boundary regex ((^|_)param(_|$)) instead of a substring match, preventing collisions in models with short parameter names (e.g. s, c, a) that are substrings of longer ones (sim, correct, activation); the longest (most specific) match is selected when several apply (#354, #355).
  • create_initfun() now resolves initialization terms from nlpars when a model parameter is not a distributional parameter, so models built as non-linear brms formulas (e.g. native-multinomial models whose parameters live in bterms$nlpars) no longer error with no applicable method for 'has_intercept' applied to an object of class "NULL" (#362).
  • The sdm model now emits its serial likelihood under threading(n, force = TRUE), matching the unsliced Stan code brms generates in that case. Previously the threaded chunk was emitted and the model failed to compile.
  • bmm() now warns when a predictor in the formula shares its name with both a predicted parameter and a column in the data. Such a predictor was silently treated as a non-linear term (emitted via nlf() instead of lf()), changing the likelihood without any error. Short parameter names (c, a, s, b) collide naturally with condition codes or columns like accuracy/stimulus (#378).

Other changes

  • Added an internal consistency check in configure_prior(): if a model’s fixed_parameters includes a parameter that its configure_model() never wires into the formula (neither a dpar nor an nlpar), bmm now fails with a clear model-definition error instead of letting a malformed b_Intercept ~ constant() prior reach brm(). This is a safety net for model development; it cannot be reached through the normal bmm() interface, where an unrecognized parameter is already caught earlier by check_formula() (#377).
  • print.bmmodel() now also displays the required response variables (one per line, annotated with the expected coding — e.g. radians in [-pi, pi] for the circular models, seconds and 0/1 responses for the trial-wise RT models) and the default parameter links, answering “what does my data frame need to look like?” directly at the console (#392).
  • The package maintainer contact is now Gidon T. Frischkorn, and the repository URLs point to https://github.com/popov-lab/bmm.

Developer-facing changes

  • use_model_template() now scaffolds the current model-specification patterns. It generates a flat .{model}_defaults block for unversioned models (like ddm) or, with the new versions argument, a .{model}_version_table block for versioned models (like cswald). The generated constructor spells out every field of the model object inline (referencing the defaults/version table for parameters, links, fixed_parameters, default_priors, and init_ranges), and versioned aliases validate version with match.arg() (#350).
  • Removed the unused void_mu field from all model definitions and the template. It was assigned but never read anywhere — response-mean suppression is already handled via fixed_parameters and the family’s dpars (#350).

bmm 1.3.1

CRAN release: 2026-06-05

Bug fixes

  • Fix swald_lccdf() returning incorrect log-survival probability when response time equals non-decision time in the cswald model. Previously returned -Inf instead of 0 (log of survival = 1) (#348).

Other changes

  • The ddm model supports both cmdstanr and rstan backends. Previously, cmdstanr was required.

bmm 1.3.0

CRAN release: 2026-03-30

New models

  • Add the Diffusion Decision Model (ddm) for speeded decision-making tasks with trial-level RT and response data. The model estimates drift rate, boundary separation, non-decision time, and (optionally) relative starting point. Includes distribution functions dddm() and rddm() (#280).
  • Add the EZ-Diffusion Model (ezdm) for speeded decision-making tasks. The model estimates drift rate, boundary separation, and non-decision time from aggregated summary statistics (mean RT, variance of RT, accuracy) using the closed-form equations derived by Wagenmakers et al. (2007). Supports both 3-parameter (symmetric starting point) and 4-parameter (asymmetric starting point) versions based on Srivastava et al. (2016). Implements Bayesian hierarchical estimation following Chavez & Vandekerckhove (2025). Includes distribution functions dezdm() and rezdm() (#281).
  • Add the Censored Shifted Wald Model (cswald) for choice reaction time tasks with two response boundaries. The model estimates drift rate, boundary separation, and non-decision time from trial-level RT and response data. Implements two versions: simple (treats errors as censored correct responses, appropriate for high-accuracy tasks) and crisk (competing risks version with separate accumulators for each response, suitable for balanced accuracy). Includes distribution functions dcswald(), pcswald(), qcswald(), and rcswald(). Thanks to @GidonFrischkorn

New features

  • New S3 method conditional_effects() for bmmfit objects. Provides an intuitive interface for visualizing predictor effects on model parameters, with automatic routing between distributional and non-linear parameters, inverse link transformations to show parameters on their natural scale (scale = "native"), softmax handling for mixture3p weight parameters, and filtering of internal model variables (#203).
  • New S3 methods for emmeans support on bmmfit objects. Users can now call emmeans(fit, ~ condition, dpar = "kappa") for any bmmodel (#323).
  • New pp_check() method for multinomial models (e.g., m3). Since brms::pp_check() does not support the multinomial family, bmm now provides a custom method that compares observed and predicted response proportions in the ppc_bars style from bayesplot. The method supports faceting by experimental conditions via group, configurable credible intervals via probs, and population-level predictions via re_formula = NA. For non-multinomial models, pp_check() delegates to brms::pp_check() and auto-selects the grouped plot variant when group is specified (#324).
  • New function parameters() lists all parameters of a bmmodel or bmmfit object with descriptions, link functions, and fixed values (#329).
  • New function extract_stan_blocks() extracts individual program blocks (functions, data, parameters, etc.) from compiled Stan code (#286).
  • New function extract_parameter_dimensions() extracts parameter names, dimensions, and types from a Stan parameters block.
  • New functions ezdm_summary_stats() and adjust_ezdm_accuracy() to compute and pre-process summary statistics from trial-level RT data for the EZ-Diffusion Model (#291).
  • New functions flag_contaminant_rts() and validate_fast_guesses() for trial-level contamination detection in RT data. Identifies fast guesses and attention lapses using mixture modeling and provides Bayesian validation of fast guess assumptions (#307).
  • New function create_initfun() creates initialization functions for models that benefit from or require initial values for MCMC sampling (#285).

Documentation

  • New online article to accompany the ddm model
  • New online article to accompany the ezdm model
  • New online article to accompany the cswald model
  • New online article on pre-processing and contamination detection for reaction time data

Other changes

  • Improved rm3() random generation function for the M3 model (#279).
  • Removed magrittr dependency; replaced %>% with the native pipe |> (#341).
  • Minimum R version is now 4.1.0.

bmm 1.2.0

CRAN release: 2025-07-24

New models

  • Add the Memory Measurement Model (Oberauer & Lewandowsky, 2019) and its generalization as the Multinomial Measurement Model for categorical decision tasks as new model class m3 with three versions: simple span (ss), complex span (cs), and custom. For details, see the article on the bmm website (#237). Thanks to @GidonFrischkorn and @chenyu-psy

New features

  • Updates to the bmf2bf S3 methods for more flexible translation of bmmformulas into brmsformulas (#227).
  • New function apply_links adds link functions to all non-linear formulas in a bmmformula object.
  • New example data set oberauer_lewandowsky_2019_e1 for exploring the m3 model.
  • The file_refit argument of the bmm function now accepts character strings like brms. A warning is given when “on_change” is specified, as this is not currently implemented for bmmodels (#228). (The warning was removed and “on_change” implemented in the development version, see #411.)
  • New function rejection_sampling

Bug fixes

  • Fix conflict in setting default priors when model parameters were transformed in a non-linear formula (#232).
  • Allow a NULL formula (formula(NULL)) to be added to a bmmformula for consistentcy with brms (#264)
  • Improve error messages when attempting to construct bmmformulas without a left-hand-side variable

Documentation

  • Add documentation to the continuous reproduction task article for pre-processing half-circular stimulus spaces when using bmmodels of the circular model class (#229, #233).
  • New online article to accompany the m3 model

Other changes

  • vectorize k2sd() function for improved performance
  • various internal refactorings (#246, #242)
  • dplyr, magrittr and tidyr dependencies are now optional (#240)
  • new contributor - Chenyun Li (chenyu-psy) for his work on the m3 model

bmm 1.0.0

First version of the package on published on CRAN!

New features

  • you can now specify to save the bmmfit object generated by bmm() to a file with the file argument, similarly to brms::brm() (#190)
  • the parameterization of the imm was adapted to accurately reflect the model as implemented by Oberauer et al. (2017)
  • prepare package for CRAN submission

Bug fixes

  • fix incorrect specification of default priors when only an interaction is specified (#201)
  • the random generation function for the mixture3p and imm returned incorrect samples for some rare parameter combinations, this has now been fixed, so that the functions now return correct samples for all parameter combinations.

Deprecated functions and arguments

  • BREAKING CHANGE: the arguments for the distribution functions of the mixture2p and mixture3p model have been change to match the snake_case coding scheme. Instead of pMem and pNT these are now p_mem and p_nt. The old names are deprecated and are no longer supported

bmm 0.5.1

Bug fixes

  • fix the display of the model call in the summary method for bmm models

bmm 0.5.0

New features

  • add a summary() method for bmmfit objects (#144)
  • add a global option bmm.summary_backend to control the backend used for the summary() method (choices are “bmm” and “brms”)
  • function restructure() now allows to apply methods introduced in newer bmm versions to bmmfit objects created by older bmm versions
  • you can now specify any model parameter to be a constant by using an equal sign in the bmmformula (#142)
  • you can now choose to estimate parameters that are fixed to a constant by default for all models (#145)
  • default priors for all models are now specified via the configure_prior() S3 method (#145)
  • cmdstanr will be used as the default backend for brms if the user has it installed (#145)
  • various updates to the documentation and data sets

Documentation

Bug fixes

  • fix a bug preventing the sort_data check from being executed (#72)
  • fix bugs with the summary() function not displaying implicit parameters (#152) and not working properly with some hierarchical designs (#173)
  • fix a bug in which the sort_data check occurred in cases where it shouldn’t (#158)

Deprecated functions and arguments

  • BREAKING CHANGE: remove get_model_prior(), get_stancode() and get_standata(). Due to recent changes in brms version 2.21.0, you can now use the brms functions default_prior, stancode and standata directly with bmm models.
  • the function fit_model() is deprecated in favor of bmm() and will be removed in a future version (#163)
  • the argument setsize for the mixture3p and IMM models is now called set_size for consistency. The old argument name is deprecated and will be removed in a future version (#163)
  • the distributions functions for the imm model are renamed from dIMM, pIMM, rIMM and qIMM to dimm, pimm, rimm and qimm (#163)
  • the argument parallel for the bmm() function is deprecated and will be removed in a future version. Use cores instead, as for brms::brm() (#163)
  • the models IMMfull(), IMMabc() and IMMbsc() are now called via imm(), imm(version = “abc”) or imm(version = “bsc”). The old names are deprecated and will be removed in a future version (#163)
  • the sdmSimple() model is now called sdm(). The old name is deprecated and will be removed in a future version (#163)

Other changes

  • bmm now requires the at least version 2.21.0 of brms.

bmm 0.4.0

New features

  • add a check for the sdmSimple model if the data is sorted by predictors. This leads to much faster sampling. The user can control the default behavior with the sort_data argument (#72)
  • the mixture3p and IMM models now require that the intercept must be suppressed when set size is used as a predictor (#96).
  • add postprocessing methods for sdmSimple to allow the use of pp_check(), conditional_effects and bridgesampling with the model (#30)
  • add informed default priors for all models. You can always use the get_model_prior() function to see the default priors for a model
  • add a new function set_default_prior for developers, which allows them to more easily set default priors on new models regardless of the user-specified formula
  • you can now specify variables for models via regular expressions rather than character vectors (#102)
  • you can now view and set all bmm global options via bmm_options(). See ?bmm_options for more information
  • add a start-up message upon loading the package

Bug fixes

  • fix a bug in the mixture3p and IMM models which caused an error when intercept was not suppressed and set size was used as predictor
  • update() now works properly with bmmfit objects (#95)
  • fix a bug in the sort_data check which caused an error when using grouped covariance structure in random effects across different parameters

Other changes

  • brms is now loaded automatically when loading bmm with library(bmm)

bmm 0.3.0

New features

  • BREAKING CHANGE: The fit_model function now requires a bmmformula to be passed. The syntax of the bmmformula or its short form bmf is equal to specifying a brmsformula. However, as of this version the bmmformula only specifies how parameters of a bmmodel change across experimental conditions or continuous predictors. The response variables that the model is fit to now have to be specified when the model is defined using model = bmmodel(). (#79)
  • BREAKING CHANGE: The non_target and spaPos variables for the mixture3p and IMM models were relabeled to nt_features and nt_distances for consistency. This is also to communicate that distance is not limited to spatial distance but distances on any feature dimensions of the retrieval cues. Currently, still only a single generalization gradient for the cue features is possible.
  • This release includes reference fits for all implemented models to ensure that future changes to the package do not compromise the included models and change the results that their implementations produce.
  • The check_formula methods have been adapted to match the new bmmformula syntax. It now evaluates if formulas have been specified using the bmmformula function, if formulas for all parameters of a bmmodel have been specified and warns the user that only a fixed intercept will be estimated if no formula for one of the parameters was provided. Additionally, check_formula throws an error should formulas be provided that do not match a parameter of the called bmmodel unless they are part of a non-linear transformation.
  • You can now specify formulas for internally fixed parameters such as mu in all visual working memory models. This allows you to predict if there is response bias in the data. If a formula is not provided for mu, the model will assume that the mean of the response distribution is fixed to zero.
  • there is now an option bmm.silent that allows to suppress messages
  • the baseline activation b was removed from the IMM models, as this is internally fixed to zero for scaling and as of now cannot be predicted by independent variables because the model would be unidentifiable.
  • the arguments used to fit the bmmodel are now accessible in the bmmfit object via the fit$bmm$fit_args list.
  • add class(‘bmmfit’) to the object returned from fit_model() allowing for more flexible postprocessing of the underlying brmsfit object. The object is now of class(‘bmmfit’, ‘brmsfit’)
  • changes to column names of datasets zhang_luck_2008 and oberauer_lin_2017 to make them more consistent

Bug Fixes

  • an error with the treatment of distances in the IMMfull and the IMMbsc has been corrected. This versions ensures that only positive distances can be passed to any of the two models.
  • removed a warning regarding the scaling of the distances in the IMMfull and the IMMbsc that was specific only for circular distances.

Documentation

  • All articles have been update to the new bmmformula syntax.

bmm 0.2.2

Bug Fixes

  • fixed a bug where passing a character vector or negative values to set_size argument of visual working memory models caused an error or incorrect behavior (#97)

bmm 0.2.1

Bug Fixes

  • Minor change to sdmSimple Stan helper functions to avoid a harmless warning message in the Stan output

bmm 0.2.0

New features

  • New model available - The Signal Discrimination Model by Oberauer (2023) for visual working memory continuous reproduction tasks. See ?sdmSimple. The current version does not take into account non-target activation
  • Add ability to extract information about the default priors in bmm models with get_model_prior() (#53)
  • Add ability to generate stan code and stan data for each model with get_model_stancode() and get_model_standata() (#81)
  • BREAKING CHANGE: Add distribution functions for likelihood (e.g. dIMM()) and random variate generation rIMM()) for all models in the package. Remove deprecated gen_3p_data() and gen_imm_data() functions (#69)
  • Two new data sets are available: zhang_luck_2008 and oberauer_lin_2017 (#22)

Documentation

Other changes

  • Save bmm package version in the brmsfit object for reproducibility - e.g. fit$version$bmm (#88)

bmm 0.1.1

New features

  • BREAKING CHANGE: Improve user interface to fit_model() ensures package stability and future development. Model specific arguments are now passed to the model functions as named arguments (e.g. mixture3p(non_targets, setsize)). This allows for a more flexible and intuitive way to specify model arguments. Passing model specific arguments directly to the fit_model() function is now deprecated (#43).
  • Add information about each model such as domain, task, name, version, citation, requirements and parameters (#42)
  • Add ability to generate a template file for adding new models to the package with use_model_template() (for developers) (#39)

Other changes

  • Improve documentation of model functions. You can now get help on each model by typing ?model_name into your console. For example, calling the information on the full version of the Interference Measurement Model would look like this: ?IMMfull

bmm 0.1.0

A major restructuring of the package to support stable and generalizable development of future models (#41).

New Features

  • Refactor the fit_model() function to be generic and independent of the model being fit (#20)
  • Transform models to be S3 objects. (#41).
  • View currently supported models with new function supported_models(). Currently supported models are: mixture2p(), mixture3p(), IMMabc(), IMMbsc(), IMMfull()
  • Add S3 methods for checking the data, formula, model and priors (#41)
  • Add distribution functions for the Signal Discrimination Model. See ?SDM for usage (#27)
  • Add softmax and softmaxinv functions

Bug Fixes

  • Change default prior on log(kappa) to Normal(2,1) for the mixture3p() model (#15)

Other changes

  • BREAKING CHANGE: deprecate model_type argument in fit_model(). Models must now be specified with S3 functions passed to argument model rather than model names as strings passed to argument model_type (#41)
  • Add extensive unit testing

bmm 0.0.1

  • Initial release version