CHANGELOG#
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.26.0] - 2026-10-16#
Added#
Added test units for
cpm.hierarchical.EmpiricalBayesandcpm.hierarchical.VariationalBayesAdded
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.BadsfitAdded
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 traceAdded test units for
HybridMBMFandSARSATraceAdded a two-step task example replicating Smid et al. (2022), with its data in
cpm.datasets.load_two_step_dataAdded
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 referenceAdded test units for
cpm.brainexplorerAdded
cpm.datasets.load_model_based_model_free, two-step task data from the BrainExplorer game Goblin HeistAdded 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; based on an earlier version by @chotongAdded
cpm.generators.SessionWrapper, aWrapperfor models that compute all trials of a participant at onceAdded
cpm.models.kernels, the formulas of thecpm.modelsclasses as functions that numba can compile, used by the classes and the built-in applicationsAdded 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 PythonAdded 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 exampleAdded test units for
SessionWrapper,cpm.models.kernels, the fast priors, and the built-in applications with and without numbaAdded 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 numbaAdded 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 iterationAdded 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 HessianAdded 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 startsAdded 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 requireAdded 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 docstringsRestricted package discovery to
cpm, and addeddocsandnotebooksoptional dependenciesSped up long and multi-chain runs of
cpm.hierarchical.EmpiricalBayesandcpm.hierarchical.VariationalBayesMade 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 resultscpm.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 withMade 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 resultsMade per-trial
cpm.generators.Wrappermodels about twice as fast, by reading trials withoutDataFrame.ilocMade the classes in
cpm.modelsfaster, up to 17 times for large inputs, with identical resultsMade
LogLikelihood.bernoulliandLogLikelihood.continuous2.5-3 times faster, with identical resultsMade
cpm.applications.signal_detection.EstimatorMetaDabout 10 times faster, with identical resultscpm.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 itRequired 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 onlycpm.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% fasterThe 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 participantsFixed
cpm.hierarchical.EmpiricalBayesandcpm.hierarchical.VariationalBayesdiscarding the estimated population priors after the first evaluation of each fit; hierarchical results changeFixed the random starting priors of later chains of
EmpiricalBayesandVariationalBayesbeing length-one arraysFixed
numpy.random.seednot reproducing the later chains ofEmpiricalBayesandVariationalBayes, whose starting priors now also respect the lower bound and work with infinite boundsFixed
cpm.optimisation.Badsemitting aDeprecationWarningon every GP fit (#88)Fixed
cpm.optimisation.Badsfailing under NumPy 2, by requiringgpyreg>=1.2.1Fixed 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.5Fixed
cpm.optimisation.FminBoundraisingTypeErroron SciPy 1.18.0 and laterFixed
cpm.hierarchical.VariationalBayes.ttestraisingNameErrorwhennullis apandas.DataFrameFixed
cpm.hierarchical.VariationalBayes.lmesrepeating the final list of model evidences for every iterationRemoved a dead, always-zero
mean_errorbarcolumn fromcpm.hierarchical.VariationalBayes.hyperparametersFixed
cpm.generators.Valuewithprior="uniform"spanning[lower, lower + upper]instead of[lower, upper]Fixed
cpm.generators.Parameters.sample()raisingAttributeErrorfor a parameter that isNoneFixed callables passed to
cpm.generators.Parametersleaking onto every otherParametersinstanceFixed
cpm.models.learning.SeparableRule.errornever being filled, which madenoisy_learning_rule()a no-op, and theerrorofDeltaRuleandSeparableRulehaving the wrong shape for 1D weightsFixed
cpm.generators.Wrapper.reset()misassigning an array of parameter values when a state is declared before a free parameterFixed
numpy.asarray(value, dtype=...)raisingTypeErrorfor acpm.generators.ValueFixed
cpm.models.activation.ProspectUtilitywithweighting="prelec"failing for options with several outcomesFixed
cpm.applications.decision_making.PTSM2025stopping a fit withValueErrorwhen its exponentials overflowedFixed
cpm.models.learning.HumbleTeacherraisingIndexErrorfor 1D weightsFixed
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.DataFrameFixed
cpm.generators.SimulatorraisingAttributeErrorfor parameters in apandas.DataFramewith grouped dataFixed
cpm.generators.SimulatorraisingValueErrorfor a single parameter set given asParameters,dictorpandas.SeriesFixed
diagnostics()ofEmpiricalBayesandVariationalBayesfailing for three or more free parameters;convergence_diagnostics_plotshas a newboundsargumentFixed a clean install raising
ModuleNotFoundErrorindiagnostics(), by addingmatplotlibto the dependenciesFixed
cpm.utils.dataandcpm.utils.metadnot being reachable afterimport cpmFixed
cpm.core.diagnostics.gelman_rubinandcpm.core.diagnostics.psrffailing on every call, andgelman_rubincomputing the statistic incorrectlyFixed 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 themFixed
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 changeFixed the
cpm.hierarchical.VariationalBayesdocstring giving 1000 as the default ofiteration, which is 50
[0.25.6] - 2026-04-15#
Added#
Introduce a third-party connector for the
cpm.generators.Wrapperclass to facilitate integration with external optimisation proceduresAdd validation for ‘observed’ column in Wrapper class to ensure it exists before running model or computing loss
Add warnings to inform users if ‘observed’ column is missing in the data provided to Wrapper class
Added detailed guidance on fitting the models (PTSM, PTSM1992, and PTSM2025) to data, including recommendations for setting the temperature parameter, handling overflow warnings, and normalizing utility values to avoid numerical instability.
Added documentations for the datasets included in
cpm.datasets, detailing the experimental procedure, data structure and usage (#69) @FloorJBurghoornAdded
cpm.utils.data.convert_to_RLRWdata conversion utility to create dataframes compatible withcpm.applications.reinforcement_learning.RLRWAdded
cpm.utils.data.convert_to_PTSMdata conversion utility to create dataframes compatible withcpm.applications.decision_makin.RLRW
Changed#
cpm.core.parallel.execute_parallelnow suppresses ipyparallel cluster status messages by setting log level to ERRORcpm.core.parallel.execute_parallellibrariesparameter default is no longer a mutable listUpdate
cpm.applications.reinforcement_learning.RLRWclass to usenumpy.asarrayfor thevaluesparameter, ensuring compatibility with numpy=>2.0Increased bandit task dataset size
Update test units for
cpm.applications.reinforcement_learning.RLRWto include handling of new changes, such as usingnumpy.asarrayforvaluesand adding an ‘observed’ column in the test datasetImproved the error messages in check_nan_and_bounds_in_input to provide more actionable feedback when encountering NaN or Inf values in predicted or observed data, including likely causes and suggested remedies.
Display option now blocks all prints in optimization module (#71) @tzukpolinsky
Fixed#
Fix
cpm.core.parallel.in_ipynbto correctly distinguish Jupyter notebooks from IPython terminal sessions by checking forZMQInteractiveShellFix
cpm.core.parallel.execute_parallelipyparallel cluster not being shut down after execution, causing engine process leaksFix wrong probabilities for generating data in
cpm.applications.decision_making.PTSM2025model #67 @FloorJBurghoornFix matplotlib>=3.10 dependency mismatch errors upon loading
cpmby removing unused imports incpm.utils.metadFix the usage of ppt_identifier in Minimize class (#72) @tzukpolinsky
Fix usage of NaN in
cpm.models.decision.SoftMaxFix the import for
cpm.models.learning
Removed#
Removed unused imports in
cpm.utils.metadto prevent dependency issues with matplotlib>=3.10Removed
cpm.models.learning.KernelUpdatedue to inconsistencies between equations reported in paper and code available in GitHub
[0.23.18] - 2025-09-03#
Added#
Add input validation and error handling in all
cpm.optimisation.minimisemethodsAdd test units for
cpm.optimisation.minimiseAdded three models based on Prospect Theory:
cpm.applications.decision_making.PTSM,cpm.applications.decision_making.PTSM1992, andcpm.applications.decision_making.PTSM2025@BenJonathanWagnerThe
cpm.generators.Parametersclass now supports None-type parameters, allowing for more flexible model configurationsThe
cpm.generators.Parametersclass now supports the use of user-defined functions as attributes in addition to freely-varying parametersAdd
cpm.datasets.load_risky_choicesfunction to load built-in risky choices dataset @BenJonathanWagner @tuhauserExpanded
cpm.models.activation.ProspectUtilityclass to include additional parameters for more flexible modeling of decision-making under risk, more closely approximating Tversky & Kahneman’s (1992) version of Prospect Theory
Fixed#
Fix
simulation_exportfunction to handle DataFrame output correctlyFix
detailed_pandas_compilerfunction to support new numpy versionsFix probability adjustements in
cpm.optimisation.minimise.LogLikelihoodmethod to ensure correct parameter estimatesFix NaN handling in
cpm.models.decision.Softmax, andcpm.models.decision.Sigmoiddue to infinities in the exponential function for out-of-bounds parameters
Changed#
Resolved a bug in the
detailed_pandas_compilerfunction to handle various data types and ensure proper DataFrame formatting incpm/core/data.py.
[0.23.7] - 2025-07-02#
Added#
Detect parallel method to use given environment (support for parallelisation on Jupyter Notebooks)
Provide a complete n-dimensional and k-arm reinforcement learning model for multi-armed bandit tasks in applications
Add support for ‘>’ and ‘<’ operator in Value type
Add validation to export method and improve data handling in Simulator class
Update export tests to validate DataFrame output and adjust simulation assertions
Implement ProspectSoftmaxModel for decision-making under risk
Add meta-d to applications
Provide utility functions for data preprocessing with meta-d type models
Introduce new likelihoods (
multinomialandproduct)
Fixed#
Fix multi-outcome log-likelihood calculation in
cpm.optimisation.minimise.LogLikehood.categoricalmethodFix pandas groupby method for parallelization when in Jupyter Notebook
Fix magnitude is not taking effect in Nominal
Fix choice kernel choice should check whether computations still need to carry out
Fix column assignment logic in simulation_export function
Fix tests by updating ProspectUtility parameters in tests to reflect changes in constructor
Fix wrong probability adjustments in
cpm.optimisation.minimise.LogLikelihoodmethod causing strange parameter estimatesFix Issue #55: AttributeError: np.float_ was removed in NumPy 2.0 during export
Changed#
Update model description in RLRW class to include reference to Sutton & Barto (2021)
Fix column names in
cpm.applications.signal_detection.EstimatordMetaDclassFix
detailed_pandas_compilerbug to handle various data types and ensure proper DataFrame formattingFix column name issues in
cpm.applications.signal_detection.EstimatordMetaDclass
Changed#
Improved error handling in
cpm.applications.signal_detection.EstimatordMetaDImproved error handling and added input validation in several methods, such as the
detailed_pandas_compilerfunction and parameter bounds handling incpm/generators/parameters.pyAllow for larger variations in the estimation of the Hessian matrix in test units
Changed Softmax and Sigmoid function input shape requirements to ensure they accept 1D arrays only, with a warning for 2D arrays
[Unreleased] <=0.18.4#
Added#
d42e0689: Fmin can incorporate priors into its log likelihood function
0a9281f6: Fmin now also returns the hessian matrix of the minimisation function
71937516: Parameters can now output parameter bounds if parameter has specified priors
df4cac2c: FminBound implements a bounded parameter search with L-BFGS-B
2fc82638: Parameters now output freely varying parameter names
abb837ff: Fmin can now reiterate the minimisation process for multiple starting points
abb837ff: Fmin can now add ppt identifier to the output of the minimisation process
2254dbd6: Regenerate initial guesses in Fmin-type optimisation when reset (can be turned off)
2254dbd6: EmpiricalBayes creates new starting points for each iteration of the optimisation
6780753c: Wrapper updates variables in parameters that are also present in model output
c8cd4c7c: Simulator.generate() method now expects users to specify what variable to generate
7b2571b1: Parameter Recovery now supports the generation of user-specified dependent variables
27d16f6b: add squared errors to minimise modules
b921be30: Added Bayesian Adaptive Direct Search (BADS) as an optimization method
42db58b6: DifferentialEvolution now supports parallelisation
6312ad99: more thorough computation of inverse Hessian matrix and log determinant of Hessian matrix
289cde73: made update_priors usable for both normal and truncated normal priors
ec2a181c: Implementing Piray’s Variational Bayes method
2d0c716d: Added convergence diagnostic plots for hierarchical methods
Changed#
2477a127: Optimisers now only store freely varying parameter names
b7ed8069: Refactored Bads to implement up-to-date changes (changed parallelisation, works with new methods in Parameters, implements priors)
b921393d: rewrote piecewise power function to compute utilities to avoid numpy warnings
634b0e87: corrected estimation of parameter variances and means
Removed#
6780753c: Wrapper summary output is removed due to redundancy
f47c684a: remove the redundant pool.join and pool.close
Fixed#
e334d6e8: fix parameter class prior function is not carried over by copy method
e195266f: fix Wrapper class parameter updates, where list or array inputs deleted Value class attributes of parameters
6780753c: Wrapper now correctly finds the number of trials in the model output
5f5432bd: -Inf in Loglikelihood is turned into np.finfo(np.float64).min to avoid NaN in the likelihoods
a84ae319: Parameters now ignores attributes without prior when calculating the PDF
32520016: Simulator generated returns an empty array
7a276be6: Parameter Recovery quired the wrong dimension to establish what parameters to recover
cd6ef8cb: Fix naming clashes in parameter recovery
57c6a3c0: Fix parallel=False still spawns processes in Optimizations
42db58b6: Fixing the issue when likelihood is -inf (bounding to minimum calculable value causes error in sums)
42db58b6: Fixing nan and inf checks in the obejctive functions
3e830f64: fix bads value error when unpacking and compiling results from subject-level fits
ea5b2750: cpm.generators.Simulator can now handle cases where trial numbers differ between participants
2ae833f3: cpm.models.learning.DeltaRule.noisy_learning_rule() should not be scaled by learning rate
2d0c716d: cpm.hierarchical.EmpiricalBayes non-writable array and np.nanmean reference bug
62f92b16: fix #33:optimiser reset fails for parameters with any non-finite bounds
bfb167a8: updating params in LogParameters should only apply log transform when it is a freely varying parameter
b2a8ee35: fix LogParameters copy problem
5ecada13: fix the issue where updating parameters in LogParameters would only accept non-log values
988b77a4: fix variational bayes data type error
67df33c3: fix empirical bayes assigning values to objects before creating them
62f92b16: fix initial guesses cannot generate starting guesses for parameter with non-finite or nan bounds
88d056ff: fix a bug where undeclared variables caused issue in Empirical Bayes
b094aca9: fix inverted SD in the variational bayes method - remove as it is unnecessary