Likelihood kinds in HSSM¶
Every HSSM model needs a function that scores the observed response and reaction time under a set of model parameters. HSSM calls that function a likelihood and supports three kinds. The distinction matters because it determines which samplers are available, where the numerical implementation comes from, and how a custom model must be configured.
Analytical likelihoods¶
An analytical likelihood is a differentiable numerical implementation owned
by HSSM. It may use PyTensor operations directly or provide a JAX callable that
HSSM wraps for use inside a PyTensor graph. Gradient-based PyMC samplers can use
either backend. The label describes the implementation route; some functions
use accurate numerical approximations rather than a single closed-form
expression.
HSSM uses the analytical route by default whenever a built-in model provides one. This includes the DDM and DDM-SDV, the LBA models, racing diffusion, Poisson race, and the softmax choice models. See the built-in model and likelihood matrix for the current list.
Approximate differentiable likelihoods¶
An approx_differentiable likelihood is a learned or otherwise approximate
function that HSSM can differentiate. The usual artifact is a single-trial ONNX
network translated to JAX, although a compatible JAX callable can also provide
the likelihood. HSSM vectorizes the single-trial function across observations.
This route makes models without an analytical likelihood available to gradient-based samplers. Its validity depends on the training domain and on simulation-based validation: differentiability does not guarantee that a network is accurate outside the parameter region it learned.
ONNX artifacts must satisfy the exact ONNX likelihood contract. To choose an external training route, use Bring your own likelihood.
Black-box likelihoods¶
A blackbox likelihood is an ordinary Python, PyTensor, or ONNX-backed function
for which HSSM cannot provide gradients. It is the most flexible route, but it
requires a sampler that does not depend on likelihood gradients. The black-box
ONNX walkthrough deliberately permits a batched dynamic graph; that is a
different execution path from the concrete single-trial graph required by the
approximate differentiable route.
Use Custom models from ONNX files
for the black-box procedure. Do not apply its dynamic-axis rewrite to an
approx_differentiable artifact.
Defaults and overrides¶
When loglik_kind is omitted for a built-in hssm.HSSM model, HSSM selects the
first available kind in this order:
analytical;approx_differentiable; andblackbox.
Passing loglik_kind requests a specific configured route. Passing loglik
overrides the corresponding default function or artifact. A custom model also
needs the response columns, parameter order, choices, and likelihood metadata
described by hssm.ModelConfig or
hssm.register_model.
The built-in model and likelihood matrix
is the canonical catalog. Exact constructor rules live in the
hssm.HSSM API reference.
Declaring where the response-time support starts¶
HSSM floors the log-likelihood of every response time at or below the non-decision time t, because a sequential sampling model assigns no density there. Some likelihoods admit responses below t: the hddm_wfpt likelihood behind full_ddm reads st as the full width of the non-decision-time distribution, so its support starts at t - st / 2. Such a likelihood declares where its support starts with an ndt_edge_shift entry, {"param": "<name>", "scale": s} (the hssm._types.NDTEdgeShift TypedDict), which moves the floor to t - s * <name>. The declaration can be given in three places:
- in the likelihood dict of a model registered with
hssm.register_model, so it ships with the likelihood; - as an
"ndt_edge_shift"field ofmodel_configwhen constructinghssm.HSSM(...), which overrides the registered declaration; - as the
ndt_edge_shift=keyword ofmake_distributionwhen building apm.Distributiondirectly (see Use the low-level API with PyMC).
full_ddm declares {"param": "st", "scale": 0.5}. A likelihood without the key keeps the floor at t, and a model without t in its list_params has no floor at all. For a network trained on ssm-simulators output, the declaration must match the simulator's: the network does not learn the edge, its training labels merely smooth across it, and the floor is what restores the model's zero density below the edge. For example, a registered model whose non-decision time varies uniformly between t - st and t + st declares the edge at t - st:
hssm.register_model(
name="my_ddm_st",
response=["rt", "response"],
list_params=["v", "a", "z", "t", "st"],
choices=[-1, 1],
description="A DDM whose non-decision time is Uniform(t - st, t + st)",
likelihoods={
"blackbox": {
"loglik": my_ddm_st_logp,
"backend": None,
"default_priors": {},
"bounds": {
"v": (-3.0, 3.0),
"a": (0.3, 3.0),
"z": (0.1, 0.9),
"t": (0.001, 2.0),
"st": (0.0, 0.5),
},
"extra_fields": None,
# Responses are admissible from t - st onwards, not from t.
"ndt_edge_shift": {"param": "st", "scale": 1.0},
}
},
)