Skip to content

cogmod

0.3.3Package record

Cognitive Models for Subjective Scales and Decision Making Tasks

0packages depend
734downloads / year
83.0%test coverage
13/13flavors OK

Overview

About

Implements cognitive models for data from subjective (Likert or analog) scales and from decision making tasks with reaction times and choice data. Provides random generation, density functions, and custom response distributions for Bayesian estimation with 'brms', covering discrete beta, ordered beta and choice-confidence models for subjective ratings; reaction time distributions such as the ex-Gaussian and the shifted log-normal, Wald, gamma and Weibull; and sequential sampling models of choice and reaction time, including the drift diffusion model (DDM), the racing diffusion model (RDM), the lognormal race model (LNR) and the linear ballistic accumulator (LBA). The website provides examples and tutorials for using and interpreting the models. Methods are described in Ratcliff and McKoon (2008) doi:10.1162/neco.2008.12-06-420, Brown and Heathcote (2008) doi:10.1016/j.cogpsych.2007.12.002, Rouder et al. (2015) doi:10.1007/s11336-013-9396-3, Tillman et al. (2020) doi:10.3758/s13423-020-01719-6, Kubinec (2023) doi:10.1017/pan.2022.20, and Sciandra et al. (2024) doi:10.1007/s10651-023-00592-5.

Changes in 0.3.3

New features

  • **A distributional parameter pinned at a boundary is now checked against the response.** The three bounded-scale families are mixtures of a continuous part and one or more point masses, and every weight involved can be fixed in bf(). Fixing one at a boundary switches its component off, so any observation belonging to that component has zero probability and the log-likelihood is -Inf everywhere - which CmdStan reports only as Initialization failed after 100 attempts, naming nothing. cogmod_priors() now errors first, saying how many rows are involved and how to proceed. Eleven combinations are covered: for cogmod_choco(), pmid = 0 with a response exactly at the midpoint, pmid = 1 with one anywhere else, pex = 0 with an exact 0 or 1, bex = 0 with a 1 and bex = 1 with a 0; for cogmod_betagate() the same pex = 0 and bex cases plus pex = 1 with any interior response; and for cogmod_betadiscrete(), pzero = 0 with a 0 and pzero = 1 with any rating. The first and the last are the ones the documentation invites - ?rcogmod_betadiscrete offers pzero = 0 as the way to say a scale has no zero category, and ?cogmod_priors suggests fixing pmid or pzero at 0 to switch the parameter off.
Show all changes in 0.3.3Hide the rest of the changes in 0.3.3
  • **cogmod_priors() now covers cogmod_choco(), cogmod_betagate() and cogmod_betadiscrete().** Every distributional parameter of the three bounded-scale rating families previously arrived either flat - improper - or with a brms default aimed at a different parameterization. Two of those were worse than unhelpful. The point-mass probabilities pmid and pzero were flat on a logit link, which is improper in the posterior as well as the prior whenever the event is absent from the data: with no exact midpoints anywhere, the likelihood in pmid increases monotonically all the way to zero and nothing stops the logit running to minus infinity. That is poutlier's failure, and they now get poutlier's treatment, including an omitted form whose mode is at zero. Measured on 400 slider responses with no midpoints and no extremes: the flat defaults put pex at -1.1e14 and pmid at -3.4e13, Rhat 2.9, ESS 5, with 12% divergent transitions; these priors give -4.70 and -5.15, Rhat 1.00, ESS 3000-4000, no divergences. phi arrived with student_t(3, 0, 2.5), which brms supplies because it recognises the name from its own beta family - the same trap cogmod_exgaussian()'s sigma falls into; on cogmod_betadiscrete()'s log link that prior's 95% interval runs to phi = 2853, where the Beta has collapsed onto a single rating category, so it is overridden rather than filled. The Beta precisions are fenced away from both ends: below about 1 the underlying Beta is U-shaped and unbounded, and a large one narrows it sharply. mu is left to brms in all three, since on a logit link its student_t(3, 0, 2.5) is the standard weakly informative choice. See ?cogmod_priors for the full table and the reasoning behind each number.
  • **cogmod_inits() now covers cogmod_choco() and cogmod_betadiscrete().** Both are bounded-scale families for subjective ratings, and neither has a flat region of the kind that makes an init mandatory for the RT families - every start is a proper density and a chain begun at the default does move. What it has to move away from is a description of a rating scale that no data set matches. The logit origin puts pmid and pzero at 0.5, i.e. half of every response exactly on the midpoint or outside the scale altogether; they now start at 0.05. cogmod_choco()'s two Beta precisions are behind a softplus link, so softplus(0) = 0.69 puts both Beta shapes below 1 - a U-shaped rating distribution, unbounded at both ends of each half of the scale; they now start at 2, in the middle of a plateau where the fit is insensitive to the exact value. pex starts at 0.1 rather than 0.5. On a 2000-trial slider data set the old start sat about 1900 log-likelihood units from the new one, 983 of them pmid alone. For cogmod_betadiscrete(), phi = 1 with mu = 0.5 is the discrete Uniform, which is where the origin already is - it is named so that init = "random", which draws phi anywhere from 0.14 to 7.4, no longer decides it. So this buys warmup rather than a fit that would otherwise fail.
  • **cogmod_inits() lists cogmod_geg() among the families it supports.** It had targets for it but was not naming it, so the error message for an unsupported family said cogmod_geg() was one. The supported list is now derived from the targets rather than kept beside them.
  • **cogmod_inits() jitters the hierarchical blocks a fifth as much as the population-level ones, and starts smooths flat.** One jitter (default 0.25) now applies to intercepts and slopes; the standardized group-level effects z_*, the smooth coefficients zs_* and their scales sd_* and sds_* get a fifth of it (0.05), and sds_* starts at 0.05 rather than the generic 0.25. Two numbers set the two tiers directly. The reason is that a unit of noise on a group effect or a spline coefficient is not a unit on the linear predictor: it is multiplied by a scale and a design column - tensor basis values reach tens - once per participant or basis function. On a production model with a tensor smooth on five distributional parameters and a participant intercept on six, the old jitter moved linear predictors by one to two and a half link units at some rows, which started one participant's non-decision time above 98 of their 128 trials and a sigma at 0.07 s where 0.5 was intended, all of it then explained by the outlier component; at 0.05 on those blocks the same model started where the targets say. Chains still start apart where it matters for Rhat, on the intercepts and slopes. Warm starts (cogmod_warmstart(), warmstart =) use the same rule at their smaller default.
  • **The 7-parameter cogmod_ddm() is 1.7x cheaper per gradient when both sigmabias and sigmandt are estimated.** Stan's 7-parameter wiener_lpdf() integrates the start-point and non-decision-time ranges out by adaptive cubature - eight integrals per observation, one for the density and one per partial derivative - and takes a tolerance for them that the Stan code never passed, so it ran at Stan's default of 1e-4. It now passes 1e-3 (.DDM_WIENER_PRECISION). Measured per observation and gradient on CmdStan 2.38.0: with both ranges open, 274 us falls to 163; with one open the cost does not move (102 to 100 us), because a one-dimensional integral is already settled by cubature's first pass, and nothing much happens past 1e-3 either (153 us at 1e-2), which is why it stops there. On the intercept-only program benchmarks/gradient_cost.R times, 5000 trials with all seven parameters estimated, a gradient went from 3.25 s to 1.50 s (ratio 0.46, median of 21 alternating blocks). The price: the Stan density still agrees with the R one to 1e-5 over the test grid, but its gradient sits about 2e-5 relative from central differences at a typical point where it sat 2e-6 before, and 1.2e-4 with ndt pushed 2.5 log units above its start - a point no chain visits after warmup, and small next to the leapfrog error a sampler already carries, but a change of an order of magnitude, so it is recorded with the constant. Any value between 3e-4 and 1e-3 measures the same gradient error and 1e-3 is the cheaper. A paired fit settles it (benchmarks/ddm_precision/): the 7-parameter model on 100 trials, both tolerances from one start, metric and seed, end to end and again with adaptation held fixed - the same 24 leapfrog steps per iteration, the same step size, acceptance 0.90 against 0.92, fewer divergences (7 against 10 and 11), min bulk ESS 248 against 206 and 261 against 224, at half the cost per gradient (445 against 883 ms): 2.2 to 2.4 times the effective samples per CPU second. That fit also puts the standalone figures above in perspective: at its posterior a gradient cost 8.3 ms per observation at Stan's default tolerance, thirty times the 274 us of the benign benchmark point. The same cost measurements are now in ?cogmod_ddm, under the between-trial variability section: estimating sigmadrift costs about 2.8x the classic model per gradient, estimating one of sigmabias / sigmandt about 18x and both about 30x, and the fast path is a test for *exactly* zero, so a tight prior does not buy it back - only sigmandt = 0 in bf() does. Nothing user-facing said so before, and it is the difference between a day and weeks on a large data set.

Bug fixes

  • **cogmod_priors() now sets the wiggliness prior (sds) of a smooth on a distributional parameter.** The table in ?cogmod_priors has always listed sds alongside sd at exponential(1), but brms fills a smooth's blanket sds row itself and leaves the per-term rows empty, so a function that fills only what arrives empty never reached it: ndt ~ s(x) kept student_t(3, 0, 2.5) on its smooth's scale. On a log or logit link that is a half-t with median 1.9 in link units - loose enough for the smooth alone to move a sigmandt by a factor of seven or walk a sigmabias across its range, behind an intercept the family had deliberately fenced. The blanket row is now replaced, for every family and every dpar cogmod_priors() handles; the response's own smooth is left to brms, as its slopes are. Group-level sd rows were never affected.
  • **cogmod_rdm()'s gradient is now exact.** Every normal tail in the RDM's Stan code went through Stan's std_normal_lcdf(), whose value is right but whose partial derivatives are an approximation, and in a race those partials *are* the gradient of the drifts and the boundary. Measured against central differences of the log probability, the gradient sat 2e-4 relative from the truth at a typical start and as far as 7e-2 where the drift is small, while the log probability itself was smooth to 1e-7. HMC stays exact under an inexact gradient - the accept step corrects for it - but pays in step size and acceptance. All eleven calls now go through cogmod_log_Phi(), the function introduced below for the LNR, which brings the worst error over the same grid to 2e-7 at about 14% more per gradient evaluation. The function moved into a prelude of its own (.LOG_PHI_STAN_PRELUDE) so that any family can take it; the LogNormal's and the RDM's preludes both start with it.
  • **cogmod_invgaussian()'s gradient is now exact.** The same defect as the RDM's, in the family's own Stan code: every normal tail of the Wald - the two terms of its CDF and survival, the two integrated ones a non-decision range needs, and the truncation factors that make sigmadrift a *truncated* normal drift - went through std_normal_lcdf(). sigmadrift is differentiated almost entirely through those truncation factors, and its partial sat 1.2e-3 relative from central differences of the log probability, mu's 2.7e-4, against 1e-8 for the families already on cogmod_log_Phi(). Six of the eighteen points the gradient check walks failed on it. All of them now go through cogmod_log_Phi(), which brings the worst error over the same grid to 5e-8; the truncation factors lose a round trip through log1m_exp() in the bargain, since log1m_exp(std_normal_lcdf(-z)) is cogmod_log_Phi(z) written the long way. cogmod_exwald() builds on the same prelude and gets the same fix. Values are unchanged.
  • **cogmod_geg()'s CDF term is no longer Stan's exp_mod_normal_lcdf().** The alpha-power construction puts (shape - 1) * log F_EG inside the *density*, so the ex-Gaussian CDF is differentiated on every evaluation, not only when a response is censored. The built-in agrees with the R side to 1e-8 over the range the tests walk, but outside it - at sigma / tau around 9, where the two terms of F_EG cancel hardest - both its value and its partials go wrong: Stan's own central differences put d/d sigma at 28.5 where its autodiff said 77.5, and d/d tau at 113.6 against 53.0. The Stan side now subtracts the two terms in log space itself, through cogmod_log_Phi(), exactly as .lcdf_exgaussian() always has in R; over the same grid the worst gradient error is 2e-9, and the log CDF matches quadrature of the density to 5e-15. It costs about 10% per gradient evaluation (3.1 ms to 3.5 ms over 5000 trials, median of 21 alternating blocks). Two things kept it to that. The ex-Gaussian density is the CDF's second term over tau, so the GEG writes both out from one pair of normal tails rather than calling for each; and the pair is written inline rather than returned from a helper - a vector[2] return measured half again the cost of the inline form, through the autodiff-stack allocation it makes on every call.
  • **cogmod_exgaussian()'s Stan density, CDF and survival go through cogmod_log_Phi().** The density is the same expression as before - exp_mod_normal_lpdf() and dcogmod_exgaussian() in R evaluate it too, and log-probabilities are unchanged to every digit that matters - but it is now written out over cogmod_log_Phi(), so it stays finite about 38 standardized units into the left tail where the built-in's bare erfc underflows, and it shares its normal tail with the CDF that cogmod_geg() is built on. The cens() path gets the accurate CDF with it: cogmod_exgaussian_lcdf() was exp_mod_normal_lcdf() and had the same wrong partials as the GEG's, and cogmod_exgaussian_lccdf() was on std_normal_lcdf(). About 10% per gradient evaluation, the cost of cogmod_log_Phi() over the built-in.
  • **cogmod_lnr() and cogmod_lognormal() no longer hand Stan a non-finite gradient in the tails.** cogmod_lognormal_ldiff_Phi() formed log(Phi(y + c) - Phi(y)) from erfc as log(u1) + log1m(u2 / u1), and the A == 0 branch of cogmod_lognormal_acc_ltails() called lognormal_lcdf() and lognormal_lccdf(), which are erfc alone. erfc underflows near x = -38 - about 38 standardized log units from an accumulator's median finishing time - and past that the value is log(0) and the partials are inf or 0 / 0. The outlier mixture then hides it: log_mix() stays finite with one component at -inf, but reverse mode multiplies the zero adjoint into the stored partial and 0 * inf is NaN, so one response in a data set turned the gradient of the whole model to NaN. That reads as Gradient evaluated at the initial value is not finite at the start of a fit and as divergent transitions afterwards. Both now go through one function, cogmod_log_Phi(): erfc in the body of the distribution and, below x = -25, the asymptotic expansion of the tail, whose leading term is the exponent itself, so nothing underflows and the result stays finite and differentiable as far as x = -1e150; its six terms agree with R's pnorm(log.p = TRUE) to 4e-16 relative, so the two branches meet with no step in the density. cogmod_lognormal_ldiff_Phi() now takes the difference in logs with log1m_exp() rather than as a quotient of two minute numbers. Measured over a grid of decision times from 1 ms to 300 s and sigmas from 0.02 to 1.2, with and without a start-point range: 6 of 72 gradients were non-finite before and none are now, and the densities agree with the R kernels everywhere, with no value newly truncated to -inf. std_normal_lcdf() is not used for this, although it has the range - its value is exact against pnorm(log.p = TRUE) as far as x = -1e7. Its analytic partials are not: on 20000 responses they sat 1.7e-3 from central differences of the log probability where the erfc route sat 4e-6, and in a race those partials *are* the gradient of nu and sigma. Where the old code's gradient was finite it was not always right. On 20000 responses with a start-point range it sat 8.3e-4 from central differences and the new one sits 9.6e-7, which is the finite differences' own noise; without a start-point range both sit at 4

The NEWS section is longer than shown here.

From NEWS.md in the release

Install

Health

CRAN checks
13OK
Slowest check: 11.1 min · r-oldrel-windows-x86_64
Status changes
  • OK13 OKNow
  • OK6 OK2026-09-13
Advisories

none known

Documentation

In release 0.3.3
README· In the repository, left out of the releaseVignettesYes · 1 · dynamicNEWSYes · 100% structured · mentions 0.3.3Citation fileYes · computed when R runs itWebsiteYes · pkgdown

The repository also keeps web-only articles.

Help pages
28
Examples that run
89%25 run in a plain check · 3 only when donttest examples are enabled (R CMD check --as-cran)
Exported functions with examples
100%
Documented parameters
66%
Return-value docs
100%
Help pages citing references
61%
Vignettes (1)

Downloads

734
CRAN downloads in the past year
Rank #17,222 · ~2/day · ~61/mo
Daily download trend is not available in this view yet.
61830 days
73490 days
7341 year
Compare downloads with other packages →
Also on13 r2u11 autocran

Repository

25Stars
0Forks
1Open issues
1Open PRs
1Releases
197Commits
2Commit identities
analogbetabrmschoicecognitiveconfidenceddmdecision
197 commits · Last activity 2026-09-26 · +13.6% stars, 30d
Responsiveness
How these are sampled
PRs merged (all time)
100%
Median days to close, 50 most recently updated closed issues
252
Median days to close, 50 most recently updated closed PRs
8

Stars over time

2026-09-12 · 102026-10-19 · 25
PracticesBeta

11 development-tooling and community-health practices detected across 7 families in the upstream repository

Checks run against github.com/dominiquemakowski/cogmod on 2026-10-18.

Continuous integration (2)
GitHub ActionsR CMD check workflow

The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel

Governance and community (1)

The repository owner accepts GitHub Sponsors

Docs source (4)
README.qmdpkgdown

GitHub Pages site last deployed 2026-09-26 · The repository has a site deploy workflow

pkgdown site last built 2026-09-26

Reproducibility and dev environment (1)
data-raw/
Show all practicesHide 3 practices
CRAN release process (1)
cran-comments.md
Research and citation (1)
CITATION.cff
Lint, format, editor (1)
RStudio project
How this is detected·Detection ruleset v3 (2026-09-26)
AI coding toolsBeta

This repository shows signs of AI coding tools.

  • Claude Codefirst seen 2026-08-17
    Named in .Rbuildignore: .claude, CLAUDE.md
    Named in .gitignore: CLAUDE.md
    Commits: at least 5 crediting it in the message
  • GitHub Copilotfirst seen 2026-09-20
    Commits: 2 by its own account
Models named in its commits
  • Claude Fable 5.14 commits · Sep 2026
  • Claude Opus 5.51 commit · Sep 2026
Counts are commits, not lines of code. Commits by a tool's accounts count once across all of its accounts. Commits crediting it are the largest count among its credit lines, a minimum.How this is detected·Detection ruleset v4 (updated 2026-09-27)

Dependencies

Declared dependencies
22 external dependencies (excludes base and recommended)

Installing cogmod adds 73 packages (35 with compiled code, 76 MB of source tarballs). Heaviest: BH 14 MB, stringi 11 MB, ggplot2 6.0 MB.

Depends (1)
R >= 3.5.0
Imports (3)
LinkingTo (0)
none
Enhances (0)
none
Reverse dependencies
0direct
0indirect

Nothing depends on this yet.

Datasets

People & History

People (1)
Author, Maintainer since 0.3.0 (2026)
Releases (2)

Pick two releases to compare their code metrics.

  • 0.3.3Latest
    record
    2026-09-25 · current release · diff ↗ ·
    added 12 exports
    cogmod_inv_metric, cogmod_step_size, cogmod_warmstart, pcogmod_bisa, pcogmod_exgaussian, pcogmod_gamma, pcogmod_invgamma, pcogmod_invweibull, pcogmod_lognormal, pcogmod_logstudent, pcogmod_logweibull, pcogmod_weibull
  • 0.3.0
    record
    2026-09-12
  • R
    R 4.6.0 released · 2026-04-24

Package metadata

First published
2026-09-12
Total releases
2 / 1 yrs
License
MIT + file LICENSE OSI
Additional repositories
mc-stan.org
Minimum R
≥ 3.5.0
Bundled data
7.2 KB / 1 file
Download size
655 KB

Cite

Cite this package

Run in R for the authors' preferred citation:

citation("cogmod")

The package provides a citation file that builds its entry when R runs it. Run citation() to see it.

The repository also keeps a CITATION.cff.

Cite the R Observatory

For a number measured here: a download total, a coverage figure, an archival date.

APA

Balamuta, J. J. (2026). R Observatory: Metrics for cogmod version 0.3.3 [Data set]. HJJB, LLC. Data release v2026-10-20. https://doi.org/10.5281/zenodo.21843040

From data release v2026-10-20, which the citation names so these numbers can be found later. More on citing and the projects behind them.

Report a problem with this page →

Privacy choices

These apply to this browser and are stored on this device only. Nothing about your choice is sent to us.

Read the privacy policy