diff --git a/CHANGELOG.md b/CHANGELOG.md index ab2c9ffb..870098a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.26.0.dev3] - 2026-10-14 + +### Added + +- Added `benchmarks/hierarchical_profile.py`, which shows where the time of a `VariationalBayes` fit 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 `hessian` argument to `cpm.optimisation.FminBound`, `Fmin`, `Minimize` and `Bads`; `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 `start` argument to `cpm.hierarchical.EmpiricalBayes` and `cpm.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_definite` column to the `fit` of `cpm.hierarchical.EmpiricalBayes` and `cpm.hierarchical.VariationalBayes`, recording whether each participant's Hessian was positive definite, and a `not_positive_definite` column to their `hyperparameters` with 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`: combining `hessian="finite_differences"`, `start="prior_mean"` and `number_of_starts=1` brings `EmpiricalBayes`/`VariationalBayes` from 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 + +- `cpm.optimisation.FminBound` with `approx_grad=True` now computes the finite-difference gradient itself, together with the objective, exactly as SciPy does, with identical results; with `LogLikelihood.bernoulli`, this makes the hierarchical fits of the speed comparison 19% faster +- The defaults of the new arguments, `hessian="numdifftools"` and `start="random"`, give the same results as before for the same seed; the faster options have to be chosen + ## [0.26.0.dev2] - 2026-10-06 ### Added diff --git a/benchmarks/hierarchical_profile.py b/benchmarks/hierarchical_profile.py new file mode 100644 index 00000000..cc611445 --- /dev/null +++ b/benchmarks/hierarchical_profile.py @@ -0,0 +1,261 @@ +""" +Where does the time of a hierarchical fit go? + + python benchmarks/hierarchical_profile.py [--data DATASET.csv] [--iterations 5] [--label NAME] + +Fits one dataset of the four-armed bandit (100 participants, 120 trials) with +`RLRW` and `VariationalBayes` for a fixed number of iterations, one chain and +two optimiser starts per participant, under `cProfile`. Prints the share of +time in the optimiser, the Hessian, `evaluate_fit`, the population update and +the parts of `objective`, and the number of objective evaluations per +participant and iteration, separately for the optimiser, the Hessian and +`evaluate_fit`. Writes the table to benchmarks/results/