Getting started#
Start with PyIBS Example 1: Basic usage and calibration for a worked introduction. The summary below shows how to create an estimate and use it for model fitting.
Estimate a log-likelihood#
To estimate a model’s log-likelihood:
Write a simulator that draws one response for each requested trial.
Supply the observed responses and each trial’s design, such as its stimulus.
Create an
IBSobject and call it with a parameter vector. For fitting, wrap this call as the target of an optimizer or inference method.Check the estimates and the fit.
This complete example generates 600 trials from the included orientation-discrimination model and compares an IBS estimate with its exact negative log-likelihood:
import numpy as np
from pyibs import IBS
from pyibs.examples.psycho_model import psycho_generator, psycho_neg_logl
# The parameters are (log(sigma), bias, lapse).
rng = np.random.default_rng(20261009)
S = 3 * rng.standard_normal(600) # the orientation of each trial's stimulus
theta = np.array([0.0, 0.2, 0.03])
R = psycho_generator(theta, S, rng) # the responses, 1 or -1
ibs = IBS(psycho_generator, R, S, vectorized=True, random_seed=1)
neg_logl, sd = ibs(theta, num_reps=100, additional_output="std")
exact = psycho_neg_logl(theta, S, R)
print(f"IBS: {neg_logl:.1f} +/- {sd:.1f}; exact: {exact:.1f}")
To use your own model, pass these inputs to IBS:
sample_from_model: a simulator called assample_from_model(params, design_rows). If it has anrngparameter that accepts a keyword, PyIBS also passes its random number generator asrng=rng. Return an independent simulated response for each requested row; a trial may be requested several times in one call.response_matrix: the observed responses, one row per trial.design_matrix(optional): the design of each trial, one row per trial. The simulator receives the rows requested by IBS. When no design is supplied, it receives the trials’ 0-based indices instead.
ibs(params) returns the negative log-likelihood estimate, averaging 10
independent IBS repeats by default. Set num_reps to change this number.
With additional_output="std", the call also returns an estimated
standard deviation, which measures variation between IBS calls at the same
parameters; it is not uncertainty in the parameters. With "full", it
returns an EstimateResult containing
per-trial estimates and the cost of the call. The
IBS reference describes all settings, including the
likelihood threshold neg_logl_threshold.
Choose a sampling schedule#
The code above sets vectorized=True, which requests batches of
responses, because psycho_generator returns many responses cheaply in
one call. By default (vectorized=None), IBS times your simulator once
and, if simulating is slow, requests one response per trial and call, which
wastes no samples; see
Should I set vectorized?.
With PyBADS and PyVBMC#
The fitting snippets below are templates. Supply a starting parameter
vector x0, hard bounds lb and ub, and plausible bounds plb
and pub; PyVBMC also needs a prior.
Example 2
and Example 3 define
these inputs for the orientation-discrimination model.
For PyBADS, which minimizes its target, return the negative log-likelihood and its estimated standard deviation:
from pybads import BADS
def target(theta):
return ibs(theta, num_reps=100, additional_output="std")
bads = BADS(
target, x0, lb, ub, plb, pub,
options={"specify_target_noise": True, "random_seed": 2},
)
result = bads.optimize()
For PyVBMC, return the log-likelihood
(return_positive=True) and its estimated standard deviation. When you
supply prior, PyVBMC adds the log prior itself:
from pyvbmc import VBMC
def log_likelihood(theta):
return ibs(
theta, num_reps=100, additional_output="std", return_positive=True
)
vbmc = VBMC(
log_likelihood, x0, lb, ub, plb, pub,
prior=prior, options={"specify_target_noise": True}, seed=2,
)
vp, results = vbmc.optimize()
Choose num_reps for a standard deviation of about 1 near the optimum.
PyBADS works best with noise of 1 or less there; PyVBMC works best with
about 1, and not much more than 3, in the region containing most posterior
mass. For this model, 100 repeats give an SD of about 1.1 on 600 trials.
For reproducible runs, seed both PyIBS and the fitting method, and have the
simulator draw from the rng it receives. Set vectorized explicitly
and leave the timing-dependent settings max_time and
acceleration_threshold at their defaults. The examples above use
separate seeds for IBS and the fit; the FAQ explains
when a seed reproduces a run.
Examples and FAQ#
The example notebooks form a short tutorial: basic use and checks of the uncertainty estimates, maximum-likelihood fitting with PyBADS, and posterior and model-evidence estimation with PyVBMC.
The FAQ explains how to write a simulator, choose num_reps
and the likelihood threshold, and troubleshoot a failed call.