User guide#
The interface#
PSP(model, discretize, control, save=False, path=".", extension=".csv", quiet=False,
*, batched=True, device=None, dtype=None, generator=None) -> PSPResult
Argument |
Meaning |
|---|---|
|
Maps parameter sets to the model’s continuous outputs: a |
|
Maps model outputs to ordinal patterns: |
|
A mapping with the keys of the R package, below, or a |
|
Write every evaluated parameter set to |
|
Do not print the iteration number. |
|
|
|
Where the search runs. Defaults to the device and dtype of |
|
A |
|
Meaning |
|---|---|
|
Starting parameter sets, one per row. |
|
Bounds of each parameter; proposals that cross them are moved onto them. |
|
Largest step in any one parameter, a number or one per parameter. |
|
Parameter sets each pattern needs before the search stops proposing from it; |
|
Cap on the number of iterations; |
|
Column names of the saved files (optional). |
|
Size of the pattern matrix and length of the model output, checked on every evaluation (optional). |
The result, a psp.PSPResult, has ordinal_patterns of shape
(patterns, dimensionality, dimensionality), ordinal_counts of shape (patterns,) and
iterations, as attributes or as keys: result["ordinal_counts"] works like the R list.
Many independent runs at once#
psp.PSPLockstep() runs one independent search per control (for example one per
participant, each on its own trial order) and advances them in lockstep: every iteration, the
proposals of all runs go to model in one call, with the index of the run each row belongs to.
Each run keeps its own pattern store, files and result. This is how hundreds of small searches
fill a GPU.
results = PSPLockstep(lambda par, run: evaluate(par, training, sequence=run), discretize, controls)
Models written for one parameter set#
Any function that takes one parameter vector and returns one output vector, as model does in
R, works with batched=False or through psp.rowwise(), which can also hand the rows over
as NumPy arrays:
from psp import PSP, rowwise
def model(par): # par: one parameter set, a 1-D tensor
return par.cumsum(0)
result = PSP(model, discretize, control, batched=False)
result = PSP(rowwise(model, numpy=True), discretize, control) # par is a numpy array
Using the GPU#
Pass device="cuda" (and, when the model allows it, dtype=torch.float32). The proposals,
bounds and centres then live on the GPU and model receives CUDA tensors.
The GPU pays off only when one call of model does real tensor work for many parameter sets at
once, and a PSP run only produces large batches while many patterns are still filling. For a
model that runs in microseconds, or a search whose iterations propose a handful of parameter
sets, the CPU is faster, because each iteration costs a few kernel launches whatever the batch
size; and on consumer cards float64 is slow. The benchmarks
measure where the line falls for three models, and
benchmarks/devices.py
is a template for measuring it for yours. The architecture page shows what
runs where.
Models shipped with the package#
psp.models.prospect: prospect theory on four bets of Kahneman and Tversky (1979), the model of the tutorial.psp.models.alcove: ALCOVE (Kruschke, 1992) on the six category structures of Shepard, Hovland and Jenkins (1961): a batched port of theslpALCOVE()of catlearn (Wills et al., 2025) that reproduces catlearn’s trial-by-trial output to machine precision, with catlearn’s training orders and thenosof94data of Nosofsky et al. (1994) shipped as package data. The model of the ALCOVE recipe.psp.models.oscar: OSCAR (Dome & Wills, 2026; arXiv:2608.06149) on the inverse base-rate effect data of 354 participants (dome2025ibre, shipped as package data), batched over parameter sets and participants, validated against the C++ implementation.psp.models.voronoi: a cheap toy model whose regions are Voronoi cells, used by the tests and benchmarks.
Comparing models with human data#
psp.clobe is a set of tools supporting the analyses of Dome and Wills (2025), for comparing
the heterogeneity of a model with that of people: inequality matrices (imac(),
patterns()), g-distance (gdistance()), distances between
patterns (pattern_distance()), and the universal set of possible patterns,
counted (semiorders(), fubini(), stirling2())
or enumerated (orderings(), brutes()).
imac() is batched, so it can be the discretize of a run, and
gdistance() takes the patterns a run found:
from functools import partial
from psp import PSP, clobe
thresholds = (0.1, -0.1)
result = PSP(model, partial(clobe.imac, thresholds=thresholds), control)
human = clobe.patterns(data, thresholds) # (participants, stimuli) -> unique patterns
clobe.gdistance(human.inequality_matrices, result.ordinal_patterns,
universal=clobe.semiorders(data.shape[1]), weight=0.5,
frequencies=human.frequencies)
The clobe vignettes work through each of these.
Differences from the R package#
modelanddiscretizeare batched by default (see above).ordinal_patternsis indexed[k], not[, , k], and the saved pattern ids are 0-based.The saved csv header has no trailing comma; values are written in their shortest round-trip form.
Random numbers come from torch, so a run is not bit-identical to an R run with the same seed. The sampler is otherwise the same: one hypersphere proposal per underpopulated pattern per iteration, centred on the last parameter set that produced the pattern, with the same clamping and the same pattern identity rules.
radiusmay be set per parameter, andresponses,dimensionalityand the names are optional.The deprecated
psp_global()andpsp_control()are not ported.In
psp.clobe, matrices are stacked as(n, k, k)rather than(k, k, n), indices are 0-based, counts are exact Python integers,brutes()runs on any device instead of a thread pool, andgdistance()always returnsalpha,betaandaccommodation(R’sxtdo = TRUE).
References#
Dome, L., & Wills, A. J. (2025). g-Distance: On the comparison of model and human heterogeneity. Psychological Review, 132(3), 632–655. https://doi.org/10.1037/rev0000550
Dome, L., & Wills, A. J. (2026). Errorless Irrationality: A unified computational account of the inverse base-rate effect across predictive, observational, and unsupervised procedures (arXiv:2608.06149). arXiv. https://doi.org/10.48550/arXiv.2608.06149
Kahneman, D., & Tversky, A. (1979). Prospect Theory: An Analysis of Decision under Risk. Econometrica, 47(2), 263–292. https://doi.org/10.2307/1914185
Kruschke, J. K. (1992). ALCOVE: An exemplar-based connectionist model of category learning. Psychological Review, 99(1), 22–44. https://doi.org/10.1037/0033-295X.99.1.22
Nosofsky, R. M., Gluck, M. A., Palmeri, T. J., McKinley, S. C., & Glauthier, P. (1994). Comparing models of rule-based classification learning: A replication and extension of Shepard, Hovland, and Jenkins (1961). Memory & Cognition, 22(3), 352–369. https://doi.org/10.3758/BF03200862
Shepard, R. N., Hovland, C. I., & Jenkins, H. M. (1961). Learning and memorization of classifications. Psychological Monographs: General and Applied, 75(13), 1–42. https://doi.org/10.1037/h0093825
Wills, A. J., Dome, L., Edmunds, C., Honke, G., Inkster, A., Schlegelmilch, R., & Spicer, S. (2025). catlearn: Formal Psychological Models of Categorization and Learning. R package version 1.1. https://CRAN.R-project.org/package=catlearn