Repository navigation
v0.26.0 makes cpm much faster and adds new models and data. The built-in applications are refactored to improve runtime. With the new optional numba backend (pip install "cpm-toolbox[numba]") they run 90–220 times faster, and 11–23 times faster without it, with the same results. EmpiricalBayes and VariationalBayes have new options (hessian="finite_differences", start="prior_mean", number_of_starts=1) that bring them to about cbm's speed. The release also adds the hybrid model-based/model-free model of the two-step task, a SARSA rule with an eligibility trace, cpm.brainexplorer, SessionWrapper for writing fast custom models, and new datasets and examples. The documentation has been rebuilt with Sphinx, and the release fixes a long list of bugs. Thanks to @lucakosina, @aslansd and @chotong for their contributions to this release.
Note
Good to know when upgrading. A few fixes mean some fits now give different, more correct numbers than in 0.25.x:
EmpiricalBayesandVariationalBayesnow keep the estimated population priors throughout each fit.- Parallel fits with
prior=Truenow use priors updated withupdate_prior, so they match serial fits. - With three or more starts,
Fmin,FminBound,MinimizeandBadsnow always keep each participant's best fit. Value(prior="uniform")now spans[lower, upper].
The new speed options are opt-in, so the defaults give the same results as before for the same seed. cpm now requires Python 3.11 or later.
Added
- Added test units for
cpm.hierarchical.EmpiricalBayesandcpm.hierarchical.VariationalBayes - Added
number_of_startsandinitial_guess_suppliedattributes to all optimisers, recording how many starts a fit used and whether the initial guesses were supplied (#83) - Added test units for the new optimiser attributes, and a smoke test that runs a real
cpm.optimisation.Badsfit - Added
log_likelihoodandlog_priorto the output of all optimisers fitted withprior=True(#28) - Added a
metricsargument to all optimisers, for goodness-of-fit metrics such asPenalisedLikelihoods.BICevaluated at the optimum (#28) - Added
cpm.applications.reinforcement_learning.HybridMBMF, the hybrid model-based / model-free model of the two-step task (Kool et al., 2016; Smid et al., 2022) - Added
cpm.models.learning.SARSATrace, a SARSA learning rule with an eligibility trace - Added test units for
HybridMBMFandSARSATrace - Added a two-step task example replicating Smid et al. (2022), with its data in
cpm.datasets.load_two_step_data - Added
cpm.brainexplorer, which computes descriptive statistics and applies the exclusion criteria for the BrainExplorer games Space Observer, Scavenger, Treasure Hunt, and Milky Way and Pirate Market, with a page in the API reference - Added test units for
cpm.brainexplorer - Added
cpm.datasets.load_model_based_model_free, two-step task data from the BrainExplorer game Goblin Heist - Added an example that fits causal ratings in a blocking experiment, recreating Figures 1 and 2 of Spicer et al. (2021), with its data in
cpm.datasets.load_blocking_data - Added
cpm.generators.SessionWrapper, aWrapperfor models that compute all trials of a participant at once - Added
cpm.models.kernels, the formulas of thecpm.modelsclasses as functions that numba can compile, used by the classes and the built-in applications - Added numba as an optional dependency (
pip install "cpm-toolbox[numba]") that compiles the built-in applications; without it, they give the same results as plain Python - Added the numba install option to the installation guide, a how-to guide on numba, and troubleshooting entries
- Added a how-to guide on speeding up your own model with
SessionWrapper, with a test that runs its example - Added test units for
SessionWrapper,cpm.models.kernels, the fast priors, and the built-in applications with and without numba - Added a benchmark suite (
benchmarks/run.pyfor one evaluation,benchmarks/hierarchical.pyfor the hierarchical tutorials) andscripts/local_tests.py, which runs the tests with and without numba - Added a GitHub Actions workflow that runs the tests with and without numba, on Python 3.11 to 3.14
- Added
benchmarks/hierarchical_profile.py, which shows where the time of aVariationalBayesfit goes and counts the objective evaluations per participant and iteration; against cbm (RLRW, 100 participants, 120 trials), cpm makes about as many evaluations but each one costs more, at 17.3 against 5.0 ms per participant and iteration - Added a
hessianargument tocpm.optimisation.FminBound,Fmin,MinimizeandBads;hessian="finite_differences"computes the Hessian at the optimum with bounded finite differences (cpm.core.optimisers.finite_difference_hessian), cutting Hessian evaluations from 22 to 7 per participant and iteration in the speed comparison. The default,"numdifftools", keeps the previous Hessian - Added a
startargument tocpm.hierarchical.EmpiricalBayesandcpm.hierarchical.VariationalBayes;start="prior_mean"starts the first optimisation of every participant from the current prior's location, clipped into the bounds, as cbm does, so fits stop sooner. The default,"random", keeps the previous starts - Added a
positive_definitecolumn to thefitofcpm.hierarchical.EmpiricalBayesandcpm.hierarchical.VariationalBayes, recording whether each participant's Hessian was positive definite, and anot_positive_definitecolumn to theirhyperparameterswith the count; an estimate on a bound can have a non-positive-definite Hessian and is kept, as the bounds require - Added the results of the speed comparison with cbm (100 participants, 120 trials) in
benchmarks/results/hierarchical-comparison-*.csv: combininghessian="finite_differences",start="prior_mean"andnumber_of_starts=1bringsEmpiricalBayes/VariationalBayesfrom 20.8/20.1 to 6.8/7.0 ms per participant and iteration, against cbm's 4.4 ms, with RMSE against 0.26.0.dev1 staying within bootstrap 95% intervals throughout
Changed
- Rebuilt the documentation with Sphinx and the PyData theme, with a separate API reference, tutorials, examples and how-to guides; pages of the old site redirect
- Converted all docstrings to reStructuredText (NumPy style), and fixed incorrect examples and a
SyntaxWarningfrom LaTeX in docstrings - Restricted package discovery to
cpm, and addeddocsandnotebooksoptional dependencies - Sped up long and multi-chain runs of
cpm.hierarchical.EmpiricalBayesandcpm.hierarchical.VariationalBayes - Made every model cheaper to evaluate: copies of a
cpm.generators.Valueshare their prior, and the built-in priors are evaluated without scipy overhead, with identical results cpm.generators.Value.update_prior()now replaces the prior instead of changing it in place; changingvalue.prior.kwdsdirectly now affects every copy of theValuecpm.generators.Wrapper.reset()now keeps the current priors and bounds of the parameters, instead of restoring the ones the model was created with- Made the built-in applications (
RLRW,HybridMBMF,PTSM,PTSM1992,PTSM2025) compute all trials at once: 90-220 times faster with numba, 11-23 times without, with the same interface and results - Made per-trial
cpm.generators.Wrappermodels about twice as fast, by reading trials withoutDataFrame.iloc - Made the classes in
cpm.modelsfaster, up to 17 times for large inputs, with identical results - Made
LogLikelihood.bernoulliandLogLikelihood.continuous2.5-3 times faster, with identical results - Made
cpm.applications.signal_detection.EstimatorMetaDabout 10 times faster, with identical results cpm.models.decision.Softmaxno longer overflows: it returns the correct policy where it returned NaN and warnedcpm.models.activation.ProspectUtility.weightsand.utilitiesare float arrays instead of object arrays, and thesimulationrecords ofHybridMBMFhold NumPy scalars instead of Python numbersPTSM2025always uses its power utility: replacingparameters.utility_curvatureon a model no longer changes it- Required Python 3.11 or later (previously
>3.11.0, which excluded 3.11.0), removed the PyPy classifier, and tagged the wheel for Python 3 only cpm.optimisation.FminBoundwithapprox_grad=Truenow computes the finite-difference gradient itself, together with the objective, exactly as SciPy does, with identical results; withLogLikelihood.bernoulli, this makes the hierarchical fits of the speed comparison 19% faster- The defaults of the new arguments,
hessian="numdifftools"andstart="random", give the same results as before for the same seed; the faster options have to be chosen
Fixed
- Fixed the optimisers and
cpm.applications.signal_detection.EstimatorMetaDraisingUnboundLocalErrorfor a pandas DataFrame withoutppt_identifier; they now raise aValueErrorthat explains what to pass, or aKeyErrorthat lists the columns ifppt_identifieris not one of them, and takeppt_identifierfrom the grouping of data grouped by a single column, such asdata.groupby("ppt"), so that the fits record the participants - Fixed
cpm.hierarchical.EmpiricalBayesandcpm.hierarchical.VariationalBayesdiscarding the estimated population priors after the first evaluation of each fit; hierarchical results change - Fixed the random starting priors of later chains of
EmpiricalBayesandVariationalBayesbeing length-one arrays - Fixed
numpy.random.seednot reproducing the later chains ofEmpiricalBayesandVariationalBayes, whose starting priors now also respect the lower bound and work with infinite bounds - Fixed
cpm.optimisation.Badsemitting aDeprecationWarningon every GP fit (#88) - Fixed
cpm.optimisation.Badsfailing under NumPy 2, by requiringgpyreg>=1.2.1 - Fixed installs with pandas, matplotlib or pybads releases that do not work with NumPy 2, by requiring
pandas>=2.2.2,matplotlib>=3.8.4andpybads>=1.0.5 - Fixed
cpm.optimisation.FminBoundraisingTypeErroron SciPy 1.18.0 and later - Fixed
cpm.hierarchical.VariationalBayes.ttestraisingNameErrorwhennullis apandas.DataFrame - Fixed
cpm.hierarchical.VariationalBayes.lmesrepeating the final list of model evidences for every iteration - Removed a dead, always-zero
mean_errorbarcolumn fromcpm.hierarchical.VariationalBayes.hyperparameters - Fixed
cpm.generators.Valuewithprior="uniform"spanning[lower, lower + upper]instead of[lower, upper] - Fixed
cpm.generators.Parameters.sample()raisingAttributeErrorfor a parameter that isNone - Fixed callables passed to
cpm.generators.Parametersleaking onto every otherParametersinstance - Fixed
cpm.models.learning.SeparableRule.errornever being filled, which madenoisy_learning_rule()a no-op, and theerrorofDeltaRuleandSeparableRulehaving the wrong shape for 1D weights - Fixed
cpm.generators.Wrapper.reset()misassigning an array of parameter values when a state is declared before a free parameter - Fixed
numpy.asarray(value, dtype=...)raisingTypeErrorfor acpm.generators.Value - Fixed
cpm.models.activation.ProspectUtilitywithweighting="prelec"failing for options with several outcomes - Fixed
cpm.applications.decision_making.PTSM2025stopping a fit withValueErrorwhen its exponentials overflowed - Fixed
cpm.models.learning.HumbleTeacherraisingIndexErrorfor 1D weights - Fixed
cpm.applications.reinforcement_learning.RLRWnot returning the choices it simulates withgenerate=True; it now outputsresponse(#100) - Fixed
cpm.generators.Simulatornot raising its intendedTypeErrorfor an ungroupedpandas.DataFrame - Fixed
cpm.generators.SimulatorraisingAttributeErrorfor parameters in apandas.DataFramewith grouped data - Fixed
cpm.generators.SimulatorraisingValueErrorfor a single parameter set given asParameters,dictorpandas.Series - Fixed
diagnostics()ofEmpiricalBayesandVariationalBayesfailing for three or more free parameters;convergence_diagnostics_plotshas a newboundsargument - Fixed a clean install raising
ModuleNotFoundErrorindiagnostics(), by addingmatplotlibto the dependencies - Fixed
cpm.utils.dataandcpm.utils.metadnot being reachable afterimport cpm - Fixed
cpm.core.diagnostics.gelman_rubinandcpm.core.diagnostics.psrffailing on every call, andgelman_rubincomputing the statistic incorrectly - Fixed fits with
parallel=Trueandprior=Truegoing back to the priors the model was created with after the first evaluation in every worker, so that priors updated withupdate_prior, such as the population priors ofcpm.hierarchical.EmpiricalBayesandcpm.hierarchical.VariationalBayes, were ignored; parallel fits now equal serial ones, and parallel results with updated priors change.cpm.generatorsno longer deletes its submodules, which made dill copy its classes into the workers instead of referencing them - Fixed
Fmin,FminBound,MinimizeandBadscomparing every later start with the first instead of the best so far, which with three or more starts could replace a participant's best fit with a worse one; results with three or more starts can change - Fixed the
cpm.hierarchical.VariationalBayesdocstring giving 1000 as the default ofiteration, which is 50
Full changelog: 0.25.6...0.26.0