Skip to content

basic simulators

ssms.basic_simulators.Simulator

Simulator(
    model=None,
    boundary=None,
    drift=None,
    simulator_function=None,
    parameter_adapter=None,
    parameter_adaptations=None,
    **config_overrides
)

Class-based interface for Sequential Sampling Model simulations.

This class provides a flexible, extensible way to configure and run SSM simulations. It supports: - Pre-defined models (via string names like "ddm", "angle", etc.) - Custom boundary and drift functions with existing simulators - Fully custom simulator functions - Configuration overrides for any model parameter

Examples:

Basic usage with pre-defined model:

>>> sim = Simulator("ddm")
>>> results = sim.simulate(theta={'v': 0.5, 'a': 1.0, 'z': 0.5, 't': 0.3})

Custom boundary function:

>>> def my_boundary(t, theta, scale):
...     return scale * np.sin(theta * t)
>>> sim = Simulator("ddm", boundary=my_boundary, boundary_params=["theta", "scale"])
>>> results = sim.simulate(theta={'v': 0.5, 'a': 1.0, 'z': 0.5, 't': 0.3,
...                               'theta': 0.5, 'scale': 1.0})

Fully custom simulator:

>>> def my_sim(v, a, z, t, max_t=20, n_samples=1000, **kwargs):
...     rts = np.random.exponential(1/abs(v), n_samples) + t
...     choices = np.where(np.random.random(n_samples) < z, 1, -1)
...     return {'rts': rts, 'choices': choices,
...             'metadata': {'model': 'custom', 'n_samples': n_samples}}
>>> sim = Simulator(simulator_function=my_sim, params=["v", "a", "z", "t"],
...                 nchoices=2)
>>> results = sim.simulate(theta={'v': 0.5, 'a': 1.0, 'z': 0.5, 't': 0.3})

Attributes:

  • config (dict) –

    The full model configuration dictionary

Parameters:

  • model (str, dict, or None, default: None ) –

    Either a model name (e.g., "ddm", "angle"), a full configuration dictionary, or None if providing a custom simulator_function.

  • boundary (str, Callable, or None, default: None ) –

    Boundary function. Can be: - A string name from the boundary registry (e.g., "angle", "weibull_cdf") - A callable function with signature: func(t, **params) -> np.ndarray - None to use the model's default boundary

  • drift (str, Callable, or None, default: None ) –

    Drift function. Can be: - A string name from the drift registry (e.g., "constant", "gamma_drift") - A callable function with signature: func(t, **params) -> np.ndarray - None to use the model's default drift (if applicable)

  • simulator_function (Callable or None, default: None ) –

    Custom simulator function. If provided, this overrides the model's default simulator. Must follow the simulator interface contract.

  • parameter_adapter (ParameterSimulatorAdapterProtocol or None, default: None ) –

    Custom parameter adapter for preparing parameters for simulators. If None, uses ModularParameterSimulatorAdapter with default adaptations for the model.

  • parameter_adaptations (list[ParameterAdaptation] or None, default: None ) –

    Additional parameter adaptations to apply AFTER the model's default adaptations. Useful for adding custom parameter preparation without replacing the entire adapter. Only used if parameter_adapter is None.

  • **config_overrides

    Additional configuration parameters to override. Common options: - params : list[str] - Parameter names (required if simulator_function provided) - param_bounds : list - [[lower bounds], [upper bounds]] - nchoices : int - Number of choices (required if simulator_function provided) - choices : list - Possible choice values - boundary_params : list[str] - Parameters for custom boundary function - drift_params : list[str] - Parameters for custom drift function - name : str - Model name (for custom simulators)

Raises:

  • ValueError

    If configuration is invalid or missing required parameters

Examples:

Use default modular adapter:

>>> sim = Simulator("lba2")
>>> results = sim.simulate(theta={'v0': 0.5, 'v1': 0.6, 'A': 0.5, 'b': 1.0})

Add custom adaptations:

>>> from ssms.basic_simulators.parameter_adapters import SetDefaultValue
>>> sim = Simulator("ddm", parameter_adaptations=[SetDefaultValue("custom_param", 42)])

Use a custom parameter adapter:

>>> custom_adapter = ModularParameterSimulatorAdapter()
>>> sim = Simulator("ddm", parameter_adapter=custom_adapter)

config property

config

Get the full model configuration.

Returns:

  • dict

    Configuration dictionary

parameter_adapter property

parameter_adapter

Get the configured parameter adapter.

Returns:

simulate

simulate(
    theta,
    n_samples=1000,
    delta_t=0.001,
    max_t=20,
    no_noise=False,
    sigma_noise=None,
    smooth_unif=True,
    random_state=None,
    return_option="full",
    n_threads=1,
    extra_fields=None,
)

Run simulation with given parameters.

Parameters:

  • theta (list, np.ndarray, dict, or pd.DataFrame) –

    Model parameters. If dict or DataFrame, keys/columns should match parameter names in config. If array, order should match config['params'].

  • extra_fields (dict or None, default: None ) –

    Optional per-trial covariates forwarded to the model function (e.g. the aDDM fixations r1, r2, flag, sacc_array, d, sigma), so a covariate-driven model can condition on observed inputs rather than self-sampling them. Empty/None is a no-op for models that take no covariates. Arrays are per-trial (aligned with theta's trial axis).

  • n_samples (int, default: 1000 ) –

    Number of simulation samples per parameter set

  • delta_t (float, default: 0.001 ) –

    Time step size for simulation

  • max_t (float, default: 20 ) –

    Maximum simulation time

  • no_noise (bool, default: False ) –

    If True, disable noise in simulation

  • sigma_noise (float or None, default: None ) –

    Standard deviation of noise. If None, uses model defaults.

  • smooth_unif (bool, default: True ) –

    Whether to add uniform smoothing to RTs

  • random_state (int or None, default: None ) –

    Integer seed for the C-level RNG. Must lie in [-2**31, 2**31 - 1] (fits a 32-bit signed C long, required on Windows). None picks a seed in [0, 2**31 - 1] automatically.

  • return_option (str, default: "full" ) –

    Output format: "full" or "minimal"

  • n_threads (int, default: 1 ) –

    Number of threads for parallel execution. If > 1 and OpenMP is available, uses multi-threaded simulation. Note: trajectory recording is only available with n_threads=1.

Returns:

  • dict

    Simulation results with keys: - 'rts' : np.ndarray of reaction times - 'choices' : np.ndarray of choices - 'metadata' : dict with simulation metadata - Additional keys depending on model and return_option

Raises:

  • ValueError

    If parameters are invalid

validate_params

validate_params(theta)

Validate parameter values.

Parameters:

  • theta (dict) –

    Parameter dictionary to validate

Raises:

  • ValueError

    If parameters are invalid

ssms.basic_simulators.boundary_functions

Define a collection of boundary functions for the simulators in the package.

addm_collapse

addm_collapse(t=0, a=1.0, b=0.5)

Linearly collapsing boundary for the attentional DDM: a - b * t.

The aDDM simulator uses this for the upper boundary and mirrors it (-(a - b * t)) for the lower one. b >= 0 collapses the bounds over time; b = 0 recovers a constant boundary. This documents the boundary the cssm.addm engine computes internally from its own a/b params.

Arguments
t (float or np.ndarray, optional): Time point(s). Defaults to 0.
a (float, optional): Boundary intercept at t=0. Defaults to 1.0.
b (float, optional): Collapse slope (>= 0). Defaults to 0.5.

Returns:

  • float or np.ndarray: ``a - b * t``.

angle module-attribute

angle = angle

Linear collapsing boundary at angle theta.

Arguments
t (float or np.ndarray, optional): Time point(s). Defaults to 1.
a (float, optional): Threshold parameter (starting height). Defaults to 1.0.
theta (float, optional): Collapse angle in radians. Defaults to 1.0.

Returns:

  • np.ndarray or float: Boundary value = a + t * tan(theta)

conflict_gamma module-attribute

conflict_gamma = conflict_gamma

Conflict boundary with gamma bump and linear collapse.

Arguments
t: (float, np.ndarray)
    Time points (with arbitrary measure, but in HDDM it is used as seconds),
    at which to evaluate the bound. Defaults to np.arange(0, 20, 0.1).
a: float
    Threshold parameter (starting height). Defaults to 1.0.
theta: float
    Collapse angle. Defaults to 0.5.
scale: float
    Scaling the gamma distribution of the boundary
    (since bound does not have to integrate to one). Defaults to 1.0.
alphaGamma: float
    alpha parameter for a gamma in scale shape parameterization. Defaults to 1.01.
scaleGamma: float
    scale parameter for a gamma in scale shape parameterization. Defaults to 0.3.

Returns:

  • np.ndarray: Boundary value = a + gamma_bump(t) + t*tan(theta)

constant module-attribute

constant = constant

Constant boundary function.

Arguments
t (float or np.ndarray, optional): Time point(s). Defaults to 0.
a (float, optional): Threshold parameter. Defaults to 1.0.

Returns:

  • float or np.ndarray: Constant boundary value = a (scalar if t is scalar, array if t is array)

generalized_logistic module-attribute

generalized_logistic = generalized_logistic

Generalized logistic boundary function.

Arguments
t (float or np.ndarray, optional): Time point(s). Defaults to 1.
a (float, optional): Threshold parameter. Defaults to 1.0.
B (float, optional): Growth rate. Defaults to 2.0.
M (float, optional): Time of maximum growth. Defaults to 3.0.
v (float, optional): Affects near which asymptote maximum growth occurs.
Defaults to 0.5.

Returns:

  • np.ndarray or float: Boundary value = a + logistic_decay(t)

weibull_cdf module-attribute

weibull_cdf = weibull_cdf

Weibull decay boundary function.

Arguments
t (float or np.ndarray, optional): Time point(s). Defaults to 1.
a (float, optional): Threshold parameter (starting height). Defaults to 1.0.
alpha (float, optional): Shape parameter. Defaults to 1.0.
beta (float, optional): Scale parameter. Defaults to 1.0.

Returns:

  • np.ndarray or float: Boundary value = a * exp(-(t/β)^α)

ssms.basic_simulators.drift_functions

Define a collection of drift functions for the simulators in the package.

All drift functions accept v as a parameter and return the FINAL drift value, consistent with boundary functions that accept a and return the final boundary value.

attend_drift module-attribute

attend_drift = attend_drift

Shrink spotlight model, which involves a time varying function dependent on a linearly decreasing standard deviation of attention.

The final drift is v + (attention-weighted drift component).

Arguments
t: np.ndarray
    Timepoints at which to evaluate the drift.
    Usually np.arange() of some sort.
v: float
    Base drift rate (typically 0 for shrink_spot models). Defaults to 0.0.
pouter: float
    perceptual input for outer flankers
pinner: float
    perceptual input for inner flankers
ptarget: float
    perceptual input for target flanker
r: float
    rate parameter for sda decrease
sda: float
    width of attentional spotlight
Return

np.ndarray Final drift evaluated at timepoints t

attend_drift_simple

attend_drift_simple(
    t=arange(0, 20, 0.1),
    v=0.0,
    ptarget=-0.3,
    pouter=-0.3,
    r=0.5,
    sda=2,
)

Drift function for shrinking spotlight model, which involves a time varying function dependent on a linearly decreasing standard deviation of attention.

The final drift is v + (attention-weighted drift component).

Arguments
t: np.ndarray
    Timepoints at which to evaluate the drift.
    Usually np.arange() of some sort.
v: float
    Base drift rate (typically 0 for shrink_spot models). Defaults to 0.0.
pouter: float
    perceptual input for outer flankers
ptarget: float
    perceptual input for target flanker
r: float
    rate parameter for sda decrease
sda: float
    width of attentional spotlight
Return

np.ndarray Final drift evaluated at timepoints t

conflict_ds_drift module-attribute

conflict_ds_drift = conflict_ds_drift

This drift is inspired by a conflict task which involves a target and a distractor stimuli both presented simultaneously.

Two drift timecourses are linearly combined weighted by the coherence in the respective target and distractor stimuli. Each timecourse follows a dynamical system as described in the ds_support_analytic() function.

The final drift is v + (combined drift timecourse).

Arguments
t: np.ndarray
    Timepoints at which to evaluate the drift.
    Usually np.arange() of some sort.
v: float
    Base drift rate (typically 0 for conflict models). Defaults to 0.0.
tinit: float
    Initial condition of target drift timecourse
dinit: float
    Initial condition of distractor drift timecourse
tslope: float
    Slope parameter for target drift timecourse
dslope: float
    Slope parameter for distractor drift timecourse
tfixedp: float
    Fixed point for target drift timecourse
tcoh: float
    Coefficient for the target drift timecourse
dcoh: float
    Coefficient for the distractor drift timecourse
Return

np.ndarray The full drift timecourse evaluated at the supplied timepoints t.

conflict_dsstimflex_drift module-attribute

conflict_dsstimflex_drift = conflict_dsstimflex_drift

Drift function for conflict task with stimuli with potentially variable onset.

The final drift is v + (combined drift timecourse).

Arguments:
t: np.ndarray
    Timepoints at which to evaluate the drift.
    Usually np.arange() of some sort.
v: float
    Base drift rate (typically 0 for conflict models). Defaults to 0.0.
tcoh: float
    Coherence of the target stimulus when 'on'.
dcoh: float
    Coherence of the distractor stimulus when 'on'.
tinit: float
    Initial condition of target drift timecourse.
dinit: float
    Initial condition of distractor drift timecourse.
tslope: float
    Slope parameter for target drift timecourse.
dslope: float
    Slope parameter for distractor drift timecourse.
tfixedp: float
    Fixed point for target drift timecourse.
tonset: float
    Onset time of the target stimulus coherence.
donset: float
    Onset time of the distractor stimulus coherence.
rel_first: bool
    If True, the first stimulus to appear (target or distractor)
    is treated as appearing at time 0, and the other stimulus
    is adjusted accordingly. If False, the onsets are treated
    as absolute times.

conflict_stimflex_drift module-attribute

conflict_stimflex_drift = conflict_stimflex_drift

Drift function for conflict task with stimuli with potentially variable onset and duration.

The final drift is v + (combined drift timecourse) when sum_drifts=True. When sum_drifts=False, returns 2D array for dual-drift models (v not added).

Arguments:
t: np.ndarray
    Timepoints at which to evaluate the drift.
    Usually np.arange() of some sort.
v: float
    Base drift rate (typically 0 for conflict models). Defaults to 0.0.
    Only used when sum_drifts=True.
tcoh: float
    Coherence of the target stimulus when 'on'.
dcoh: float
    Coherence of the distractor stimulus when 'on'.
vt: float
    Static drift-rate of target stimulus, when 'on'.
vd: float
    Static drift-rate of distractor stimulus, when 'on'.
tonset: float
    Onset time of the target stimulus coherence.
donset: float
    Onset time of the distractor stimulus coherence.
toffset, doffset: float or None
    Duration of the stimulus coherence pulse. If None, the pulse
    lasts until the end of the trial.
rel_first: bool
    If True, the first stimulus to appear (target or distractor)
    is treated as appearing at time 0, and the other stimulus
    is adjusted accordingly. If False, the onsets are treated
    as absolute times.
sum_drifts: bool
    If True, the drift contributions from target and distractor
    are summed to produce a single drift timecourse. If False,
    a 2D array is returned with separate columns for target
    and distractor drift timecourses (for dual-drift models).

Returns:

  • np.ndarray: Array of drift values, same length as t. If sum_drifts

    is False, the array has shape (len(t), 2)

constant module-attribute

constant = constant

Constant drift function.

Arguments
t: np.ndarray, optional
    Timepoints at which to evaluate the drift. Defaults to
    np.arange(0, 20, 0.1).
v: float, optional
    Drift rate. Defaults to 0.0.

Returns:

  • np.ndarray: Array of constant drift values = v, same length as t

ds_support_analytic module-attribute

ds_support_analytic = ds_support_analytic

Solve DE.

DE is of the form: x' = slope*(fix_point - x), with initial condition init_p. The solution takes the form: (init_p - fix_point) * exp(-slope * t) + fix_point

Arguments
t: np.ndarray
    Timepoints at which to evaluate the drift. Usually np.arange() of some sort.
init_p: float
    Initial condition of dynamical system
fix_point: float
    Fixed point of dynamical system
slope: float
    Coefficient in exponent of the solution.
Return

np.ndarray The gamma drift evaluated at the supplied timepoints t.

gamma_drift module-attribute

gamma_drift = gamma_drift

Drift function that follows a scaled gamma distribution.

The final drift is v + (scaled gamma component).

Arguments
t: np.ndarray
    Timepoints at which to evaluate the drift.
    Usually np.arange() of some sort.
v: float
    Base drift rate. Defaults to 0.0.
shape: float
    Shape parameter of the gamma distribution
scale: float
    Scale parameter of the gamma distribution
c: float
    Scalar parameter that scales the peak of
    the gamma distribution.
    (Note this function follows a gamma distribution
    but does not integrate to 1)
Return
np.ndarray
    The final drift (v + gamma component) evaluated at the supplied timepoints t.

stimflex_support

stimflex_support(t, onset, offset, coh)

Construct a rectangular coherence timecourse, with discrete and potentially variable onsets and offsets of stimulus evidence.

Arguments
t: np.ndarray
    Timepoints within trial.
onset: float
    Onset time of the coherence pulse.
offset: float
    Offset time of the coherence pulse.
coh: float
    Coherence of the stimulus when 'on'.

Returns:

  • np.ndarray: Array of coherence values, same length as t.

ssms.basic_simulators.fixation_continuation

Pluggable fixation-continuation strategies + a positive-distribution factory for the aDDM.

The aDDM posterior-predictive path conditions on observed fixations (Mode 2). When a re-simulated particle has not decided by the last observed fixation, the drift "freezes" at the last gaze. This module makes that tail behaviour pluggable, and shares a scipy.stats positive-distribution factory with the aDDM's Mode-1 self-sampling.

Strategies (name -> callable), each mapping (node_r, d_r, flag_r, T, params, rng) -> (node_r, d_r, flag_r, max_d):

  • prolong_last_fixation -- no new fixations; the engine holds the last gaze to T (today's behaviour; the default).
  • sample_continuation -- keep the observed fixations; draw the continuation (tail) durations from a chosen positive distribution and keep alternating past the last observed onset. (Appending an onset after the last observed onset resamples the censored last fixation's duration; further onsets are new alternating fixations.)
  • resample_all_fixations -- ignore the observed fixation schedule; keep the observed stimulus (r1/r2 untouched upstream), re-sample the first-gaze flag and a fresh schedule. The drift still conditions on the stimulus, so the PPC stays comparable to data.

All durations come from :data:POSITIVE_DISTRIBUTIONS (scipy.stats), so any positive-support distribution is available by name with scipy-native parameters. Mirrors the attention_process registry idiom (name -> callable + a resolve_* helper).

draw_durations

draw_durations(dist_name, dist_params, size, rng)

Draw an array of size positive durations from dist_name (scipy-native params).

generate_schedule

generate_schedule(n, T, dist_name, dist_params, rng)

Self-sample a padded (n, max_d) fixation-onset schedule + per-row stage count d.

Durations are drawn from dist_name and cumulatively summed; column 0 is anchored at 0.0 (trial start). d = number of onsets strictly before T (>=1). The pre-draw budget is sized from the distribution's analytic mean, then over-allocated columns (onsets >= T) are trimmed to the realised max_d.

prolong_last_fixation

prolong_last_fixation(node_r, d_r, flag_r, T, params, rng)

No continuation: the engine holds the last observed gaze to T (today's behaviour).

resample_all_fixations

resample_all_fixations(node_r, d_r, flag_r, T, params, rng)

Ignore observed fixations; re-sample first-gaze + a fresh schedule (keep stimulus r1/r2).

resolve_continuation_mode

resolve_continuation_mode(mode)

Resolve a registry name or a callable to a concrete continuation strategy.

resolve_distribution

resolve_distribution(name)

Return the scipy.stats distribution registered under name.

sample_continuation

sample_continuation(node_r, d_r, flag_r, T, params, rng)

Keep observed fixations; draw the continuation (tail) durations from params['dist'].

ssms.basic_simulators.inv_temp_softmax

Choice-only inverse-temperature softmax simulators.

inv_temp_softmax

inv_temp_softmax(
    *,
    beta,
    q0,
    q1,
    q2=None,
    q3=None,
    n_samples=1000,
    n_trials=1,
    max_t=20.0,
    random_state=None,
    **kwargs
)

Sample choices from softmax(beta * Q) with placeholder RTs.

ssms.basic_simulators.modular_parameter_simulator_adapter

Modular parameter simulator adapter using adaptation pipelines.

This module provides the ModularParameterSimulatorAdapter class which applies parameter adaptations defined in model_config['parameter_transforms']['simulation'].

ModularParameterSimulatorAdapter

Modular parameter simulator adapter using config-defined transforms.

This adapter applies a sequence of parameter adaptations defined in the model configuration's parameter_transforms.simulation field.

All built-in models define their simulation transforms directly in their model config, making this adapter simple and transparent.

Examples:

>>> adapter = ModularParameterSimulatorAdapter()
>>> theta = adapter.adapt_parameters(theta, model_config, n_trials)

The model_config should have transforms defined like:

>>> model_config = {
...     "name": "lba_angle_3",
...     "parameter_transforms": {
...         "sampling": [...],
...         "simulation": [
...             ColumnStackParameters(["v0", "v1", "v2"], "v"),
...             ExpandDimension(["a", "z", "theta"]),
...             SetZeroArray("t"),
...         ],
...     },
... }

adapt_parameters

adapt_parameters(theta, model_config, n_trials)

Process theta by applying config-defined transformations.

Parameters:

  • theta (dict[str, Any]) –

    Dictionary of theta parameters

  • model_config (dict[str, Any]) –

    Model configuration dictionary containing parameter_transforms

  • n_trials (int) –

    Number of trials

Returns:

  • dict[str, Any]

    Processed theta parameters

ParameterSimulatorAdapterProtocol

Bases: Protocol

Protocol defining the interface for parameter adapters.

Any class that implements an adapt_parameters method with this signature can be used as a parameter adapter.

adapt_parameters

adapt_parameters(theta, model_config, n_trials)

Adapt theta parameters for simulator consumption.

Parameters:

  • theta (dict[str, Any]) –

    Dictionary of theta parameters

  • model_config (dict[str, Any]) –

    Model configuration dictionary

  • n_trials (int) –

    Number of trials

Returns:

  • dict[str, Any]

    Processed theta parameters

ssms.basic_simulators.parameter_adapters

Parameter adaptation system for modular parameter processing.

This module provides a composable system for adapting parameters before they are passed to simulators. Adaptations are small, focused classes that can be combined to handle complex parameter preparation logic.

Main Components: - ParameterAdaptation: Alias for ParameterTransform (backward compatibility) - Common adaptations: SetDefaultValue, ExpandDimension, etc. - ParameterAdapterRegistry: Maps models to adaptation pipelines - ModularParameterSimulatorAdapter: Applies adaptations in sequence

Example: >>> from ssms.basic_simulators.parameter_adapters import ( ... ExpandDimension, ColumnStackParameters ... ) >>> >>> adaptations = [ ... ColumnStackParameters(["v0", "v1", "v2"], "v"), ... ExpandDimension(["a", "t"]) ... ] >>> >>> for adaptation in adaptations: ... theta = adaptation.apply(theta, model_config, n_trials)

Note: New code should import directly from ssms.transforms.simulation for the common adaptation classes. This module re-exports them for backward compatibility.

ApplyMapping

ApplyMapping(
    source_param,
    target_param,
    mapping_key,
    additional_sources=None,
    delete_source=False,
)

Bases: ParameterTransform

Apply a mapping function to transform a parameter.

Uses a mapping from model_config to transform a parameter value. Commonly used for random variable distributions.

Parameters:

  • source_param (str) –

    Parameter to read from

  • target_param (str) –

    Parameter to write to

  • mapping_key (str) –

    Key in model_config["simulator_param_mappings"]

  • additional_sources (list[str], default: None ) –

    Additional parameters to pass to the mapping function

  • delete_source (bool, default: False ) –

    Whether to delete the source parameter after mapping

apply

apply(theta, model_config=None, n_trials=None)

Apply mapping function to transform parameter.

ColumnStackParameters

ColumnStackParameters(
    source_params, target_param, delete_sources=True
)

Bases: ParameterTransform

Stack multiple parameters into a single multi-column array.

Takes individual parameters (e.g., v0, v1, v2) and stacks them column-wise into a single array (e.g., v with shape (n_trials, 3)).

Parameters:

  • source_params (list[str]) –

    Names of parameters to stack

  • target_param (str) –

    Name of the resulting stacked parameter

  • delete_sources (bool, default: True ) –

    Whether to delete the source parameters after stacking

Examples:

>>> transform = ColumnStackParameters(["v0", "v1", "v2"], "v")
>>> theta = {"v0": [0.5], "v1": [0.6], "v2": [0.7]}
>>> theta = transform.apply(theta)
>>> theta["v"]  # array([[0.5, 0.6, 0.7]])

apply

apply(theta, model_config=None, n_trials=None)

Stack parameters column-wise.

ConditionalAdaptation

ConditionalAdaptation(condition, transformation)

Bases: ParameterTransform

Apply a transform only if a condition is met.

Parameters:

  • condition (Callable) –

    Function that takes (theta, model_config, n_trials) and returns bool

  • transformation (ParameterTransform) –

    Transform to apply if condition is True

Examples:

>>> transform = ConditionalAdaptation(
...     condition=lambda theta, cfg, n: "a" in theta,
...     transformation=ExpandDimension(["a"])
... )

apply

apply(theta, model_config=None, n_trials=None)

Apply transform if condition is met.

DeleteParameters

DeleteParameters(param_names)

Bases: ParameterTransform

Delete specified parameters from theta.

Parameters:

  • param_names (list[str]) –

    Names of parameters to delete

Examples:

>>> transform = DeleteParameters(["A", "b"])
>>> theta = {"v": [0.5], "A": [0.5], "b": [1.0]}
>>> theta = transform.apply(theta)
>>> "A" in theta  # False

apply

apply(theta, model_config=None, n_trials=None)

Delete specified parameters.

ExpandDimension

ExpandDimension(param_names)

Bases: ParameterTransform

Expand dimensions of specified parameters.

Converts 1D arrays of shape (n_trials,) to shape (n_trials, 1) by adding a dimension. This is commonly needed for multi-particle models.

Parameters:

  • param_names (list[str]) –

    Names of parameters to expand

Examples:

>>> transform = ExpandDimension(["a", "t"])
>>> theta = {"a": np.array([1.0, 2.0]), "t": np.array([0.3, 0.3])}
>>> theta = transform.apply(theta)
>>> theta["a"].shape  # (2, 1)

apply

apply(theta, model_config=None, n_trials=None)

Expand dimensions of specified parameters.

LambdaAdaptation

LambdaAdaptation(func, name=None)

Bases: ParameterTransform

Wrap a lambda or callable function as a transform.

Parameters:

  • func (Callable) –

    Function with signature: func(theta, model_config, n_trials) → theta

  • name (str, default: None ) –

    Optional name for debugging

Examples:

>>> transform = LambdaAdaptation(
...     lambda theta, cfg, n: theta.update({"nact": 3}) or theta,
...     name="set_nact_to_3"
... )

apply

apply(theta, model_config=None, n_trials=None)

Apply the wrapped function.

ParameterAdapterRegistry

ParameterAdapterRegistry()

Registry mapping models to parameter adaptation pipelines.

The registry supports two types of lookups: 1. Exact model match: Direct lookup by model name 2. Family match: Pattern-based matching using lambda functions

Exact matches take precedence over family matches, allowing specific models to override family defaults.

Examples:

>>> registry = ParameterAdapterRegistry()
>>>
>>> # Register exact model
>>> registry.register_model("lba2", [
...     SetDefaultValue("nact", 2),
...     ColumnStackParameters(["v0", "v1"], "v")
... ])
>>>
>>> # Register model family
>>> registry.register_family(
...     "race_2",
...     lambda m: m.startswith("race_") and m.endswith("_2"),
...     [ColumnStackParameters(["v0", "v1"], "v")]
... )
>>>
>>> # Lookup
>>> adaptations = registry.get_processor("lba2")  # Exact match
>>> adaptations = registry.get_processor("race_no_bias_2")  # Family match

__repr__

__repr__()

String representation of registry.

describe

describe(model_name)

Get human-readable description of adaptations for a model.

Parameters:

  • model_name (str) –

    Name of the model

Returns:

  • str

    Description of the adaptation pipeline

Examples:

>>> print(registry.describe("lba3"))
Model: lba3
Adaptations:
  1. SetDefaultValue(param_name='nact', default_value=3)
  2. ColumnStackParameters(source_params=['v0', 'v1', 'v2'], ...)

get_processor

get_processor(model_name)

Get adaptation pipeline for a model.

Lookup priority: 1. Exact model name match 2. First matching family 3. Empty list (no adaptations)

Parameters:

  • model_name (str) –

    Name of the model to look up

Returns:

  • list[ParameterAdaptation]

    List of adaptations to apply (empty list if no match)

Examples:

>>> adaptations = registry.get_processor("lba2")
>>> # Returns registered adaptations for "lba2"
>>>
>>> adaptations = registry.get_processor("race_no_bias_2")
>>> # Returns adaptations for "race_2" family (if registered)
>>>
>>> adaptations = registry.get_processor("unknown_model")
>>> # Returns [] (empty list)

has_processor

has_processor(model_name)

Check if a processor is registered for a model.

This checks if the model has a registration (exact or family match), even if the transformation list is empty.

Parameters:

  • model_name (str) –

    Name of the model to check

Returns:

  • bool

    True if model has a registration (explicit or via family)

list_registered_families

list_registered_families()

Get list of registered family names.

Returns:

  • list[str]

    List of family names

list_registered_models

list_registered_models()

Get list of explicitly registered model names.

Returns:

  • list[str]

    List of model names with exact registrations

register_family

register_family(family_name, matcher, adaptations)

Register adaptations for a family of models.

The matcher function determines which models belong to this family. It receives a model name and should return True if the model matches.

Parameters:

  • family_name (str) –

    Name for this model family (used as key)

  • matcher (Callable[[str], bool]) –

    Function that takes a model name and returns True if it matches

  • adaptations (list[ParameterAdaptation]) –

    List of adaptations to apply for matching models

Examples:

>>> registry = ParameterAdapterRegistry()
>>>
>>> # Match all race models with 2 choices
>>> registry.register_family(
...     "race_2",
...     lambda m: m.startswith("race_") and m.endswith("_2"),
...     [ColumnStackParameters(["v0", "v1"], "v")]
... )
>>>
>>> # Match all models with "no_bias" in the name
>>> registry.register_family(
...     "no_bias_models",
...     lambda m: "no_bias" in m,
...     [SetDefaultValue("z", 0.5)]
... )

register_model

register_model(model_name, adaptations)

Register adaptations for a specific model.

Parameters:

  • model_name (str) –

    Exact model name (e.g., "lba2", "ddm", "race_3")

  • adaptations (list[ParameterAdaptation]) –

    List of adaptations to apply for this model

Examples:

>>> registry = ParameterAdapterRegistry()
>>> registry.register_model("lba3", [
...     SetDefaultValue("nact", 3),
...     ColumnStackParameters(["v0", "v1", "v2"], "v")
... ])

RenameParameter

RenameParameter(old_name, new_name, transform_fn=None)

Bases: ParameterTransform

Rename a parameter and optionally transform its value.

Parameters:

  • old_name (str) –

    Current parameter name

  • new_name (str) –

    New parameter name

  • transform_fn (Callable or None, default: None ) –

    Optional function to apply to the value during renaming

Examples:

>>> transform = RenameParameter("A", "z", lambda x: np.expand_dims(x, axis=1))
>>> theta = {"A": np.array([0.5])}
>>> theta = transform.apply(theta)
>>> "z" in theta  # True

apply

apply(theta, model_config=None, n_trials=None)

Rename parameter and optionally transform its value.

SetDefaultValue

SetDefaultValue(param_name, default_value, dtype=float32)

Bases: ParameterTransform

Set a parameter to a default value if not present.

This transform adds a parameter with a default value if it doesn't already exist in theta. The value is automatically tiled to match n_trials.

Parameters:

  • param_name (str) –

    Name of the parameter to set

  • default_value (float or int or ndarray) –

    Default value to use. If scalar, will be tiled to (n_trials,).

  • dtype (dtype, default: float32 ) –

    Data type for the parameter. Defaults to np.float32.

Examples:

>>> transform = SetDefaultValue("nact", 3)
>>> theta = {}
>>> theta = transform.apply(theta, {}, n_trials=5)
>>> theta["nact"]  # array([3., 3., 3., 3., 3.], dtype=float32)

apply

apply(theta, model_config=None, n_trials=None)

Add parameter with default value if not present.

SetZeroArray

SetZeroArray(param_name, shape=None, dtype=float32)

Bases: ParameterTransform

Set a parameter to an array of zeros.

Parameters:

  • param_name (str) –

    Name of the parameter to set

  • shape (tuple or None, default: None ) –

    Shape of the array. Use None for n_trials dimension.

  • dtype (dtype, default: float32 ) –

    Data type for the array. Defaults to np.float32.

Examples:

>>> transform = SetZeroArray("v")
>>> theta = transform.apply({}, {}, n_trials=5)
>>> theta["v"].shape  # (5,)

apply

apply(theta, model_config=None, n_trials=None)

Set parameter to zero array.

TileArray

TileArray(param_name, value, dtype=float32)

Bases: ParameterTransform

Tile a constant value across trials.

Parameters:

  • param_name (str) –

    Name of the parameter to create

  • value (float or array - like) –

    Value(s) to tile

  • dtype (dtype, default: float32 ) –

    Data type for the array. Defaults to np.float32.

Examples:

>>> transform = TileArray("z", 0.5)
>>> theta = transform.apply({}, {}, n_trials=3)
>>> theta["z"]  # array([0.5, 0.5, 0.5], dtype=float32)

apply

apply(theta, model_config=None, n_trials=None)

Tile value across trials.

base

Base classes for parameter adaptations.

This module provides backward compatibility by re-exporting ParameterTransform as ParameterAdaptation.

Note: New code should use ParameterTransform from ssms.transforms.base directly.

ParameterTransform

Bases: ABC

Abstract base class for all parameter transforms.

A parameter transform is a single, focused operation that modifies theta parameters. Transforms can be composed into pipelines and are used at two phases:

  1. Sampling phase: Applied after parameter sampling to enforce constraints (e.g., ensure a > z by swapping values)

  2. Simulation phase: Applied before calling the simulator to prepare parameters for the expected format (e.g., stack v0, v1 → v)

Subclasses must implement the apply method. The method signature includes optional model_config and n_trials arguments to support both simple constraints (which only need theta) and complex adapters (which may need additional context).

Examples:

Create a custom transform:

>>> class ScaleParameter(ParameterTransform):
...     def __init__(self, param_name: str, scale: float):
...         self.param_name = param_name
...         self.scale = scale
...
...     def apply(self, theta, model_config=None, n_trials=None):
...         if self.param_name in theta:
...             theta[self.param_name] = theta[self.param_name] * self.scale
...         return theta

Use in model config:

>>> model_config = {
...     "name": "my_model",
...     "parameter_transforms": {
...         "sampling": [SwapIfLessConstraint("a", "z")],
...         "simulation": [ColumnStackParameters(["v0", "v1"], "v")],
...     }
... }
__repr__
__repr__()

String representation for debugging.

Returns:

  • str

    A string describing this transform.

__str__
__str__()

Human-readable string representation.

apply abstractmethod
apply(theta, model_config=None, n_trials=None)

Apply transform to theta parameters.

This method should modify the theta dictionary in place and return it. It may add new parameters, modify existing parameters, or remove parameters as needed.

Parameters:

  • theta (dict[str, Any]) –

    Dictionary of model parameters. Values are typically numpy arrays.

  • model_config (dict[str, Any] or None, default: None ) –

    Model configuration dictionary. Available for transforms that need additional context (e.g., looking up simulator_fixed_params). Sampling-time transforms typically ignore this.

  • n_trials (int or None, default: None ) –

    Number of trials. Available for transforms that need to create arrays of a specific size. Sampling-time transforms typically ignore this.

Returns:

  • dict[str, Any]

    The modified theta dictionary (usually the same object passed in).

Notes
  • Transforms should be pure when possible (no side effects)
  • If creating new arrays, use dtype=np.float32 for consistency
  • Document any parameters that are added, modified, or removed

get_adapter_registry

get_adapter_registry()

Get the global parameter adapter registry.

Use this to access registry methods like list_registered_models() or get_processor() directly.

Returns:

  • ParameterAdapterRegistry

    The global parameter adapter registry instance, pre-populated with all built-in model adaptations

Examples:

>>> from ssms.basic_simulators.parameter_adapters import get_adapter_registry
>>>
>>> # List all models with registered adaptations
>>> registry = get_adapter_registry()
>>> print(registry.list_registered_models())
['lba2', 'lba3', 'lca_3', ...]
>>>
>>> # Check if a model has adaptations
>>> if registry.has_processor("my_model"):
...     adaptations = registry.get_processor("my_model")
>>>
>>> # Describe adaptations for a model
>>> print(registry.describe("lba3"))

register_adapter_to_model

register_adapter_to_model(model_name, adaptations)

Register parameter adaptations to a specific model globally.

This associates existing parameter adaptation classes with a model. Once registered, the model will use these adaptations automatically when simulated with ModularParameterSimulatorAdapter.

Note: This registers which adaptations a MODEL uses, not a new adapter type. To create custom adapter types, subclass ParameterAdaptation directly.

Parameters:

  • model_name (str) –

    Unique model name

  • adaptations (list[ParameterAdaptation]) –

    List of parameter adaptation instances to apply for this model

Examples:

Register adaptations for a custom model:

>>> from ssms.basic_simulators.parameter_adapters import register_adapter_to_model
>>> from ssms.basic_simulators.parameter_adapters import (
...     SetDefaultValue, ExpandDimension, ColumnStackParameters
... )
>>>
>>> register_adapter_to_model("my_model", [
...     SetDefaultValue("z", 0.5),
...     ExpandDimension(["a", "t"]),
...     ColumnStackParameters(["v0", "v1"], "v"),
... ])
>>>
>>> # Now when simulating with this model, adaptations are applied automatically
>>> sim = Simulator(model="my_model")

Override built-in model adaptations:

>>> # Register custom adaptations for an existing model
>>> register_adapter_to_model("lba2", [
...     ColumnStackParameters(["v0", "v1"], "v"),
...     # Custom adaptations here
... ])

register_adapter_to_model_family

register_adapter_to_model_family(
    family_name, matcher, adaptations
)

Register parameter adaptations to a family of models globally.

This associates existing parameter adaptation classes with multiple models matching a pattern, without having to register each model individually.

Parameters:

  • family_name (str) –

    Name for this model family (used as identifier)

  • matcher (Callable[[str], bool]) –

    Function that returns True if a model name matches this family

  • adaptations (list[ParameterAdaptation]) –

    List of parameter adaptation instances to apply for matching models

Examples:

Register adaptations for all race models with 2 alternatives:

>>> from ssms.basic_simulators.parameter_adapters import register_adapter_to_model_family
>>> from ssms.basic_simulators.parameter_adapters import ColumnStackParameters
>>>
>>> register_adapter_to_model_family(
...     "race_2",
...     lambda m: m.startswith("race_") and m.endswith("_2"),
...     [ColumnStackParameters(["v0", "v1"], "v")]
... )
>>>
>>> # Now all models matching the pattern use these adaptations
>>> # e.g., "race_no_bias_2", "race_no_z_2", etc.

Register for models with specific naming pattern:

>>> register_adapter_to_model_family(
...     "no_bias_models",
...     lambda m: "no_bias" in m,
...     [SetDefaultValue("z", 0.5)]
... )

registry

Registry system for mapping models to parameter adaptations.

The registry provides a centralized way to associate model names with their required parameter adaptations. It supports both exact model matching and pattern-based family matching.

ParameterAdapterRegistry

ParameterAdapterRegistry()

Registry mapping models to parameter adaptation pipelines.

The registry supports two types of lookups: 1. Exact model match: Direct lookup by model name 2. Family match: Pattern-based matching using lambda functions

Exact matches take precedence over family matches, allowing specific models to override family defaults.

Examples:

>>> registry = ParameterAdapterRegistry()
>>>
>>> # Register exact model
>>> registry.register_model("lba2", [
...     SetDefaultValue("nact", 2),
...     ColumnStackParameters(["v0", "v1"], "v")
... ])
>>>
>>> # Register model family
>>> registry.register_family(
...     "race_2",
...     lambda m: m.startswith("race_") and m.endswith("_2"),
...     [ColumnStackParameters(["v0", "v1"], "v")]
... )
>>>
>>> # Lookup
>>> adaptations = registry.get_processor("lba2")  # Exact match
>>> adaptations = registry.get_processor("race_no_bias_2")  # Family match
__repr__
__repr__()

String representation of registry.

describe
describe(model_name)

Get human-readable description of adaptations for a model.

Parameters:

  • model_name (str) –

    Name of the model

Returns:

  • str

    Description of the adaptation pipeline

Examples:

>>> print(registry.describe("lba3"))
Model: lba3
Adaptations:
  1. SetDefaultValue(param_name='nact', default_value=3)
  2. ColumnStackParameters(source_params=['v0', 'v1', 'v2'], ...)
get_processor
get_processor(model_name)

Get adaptation pipeline for a model.

Lookup priority: 1. Exact model name match 2. First matching family 3. Empty list (no adaptations)

Parameters:

  • model_name (str) –

    Name of the model to look up

Returns:

  • list[ParameterAdaptation]

    List of adaptations to apply (empty list if no match)

Examples:

>>> adaptations = registry.get_processor("lba2")
>>> # Returns registered adaptations for "lba2"
>>>
>>> adaptations = registry.get_processor("race_no_bias_2")
>>> # Returns adaptations for "race_2" family (if registered)
>>>
>>> adaptations = registry.get_processor("unknown_model")
>>> # Returns [] (empty list)
has_processor
has_processor(model_name)

Check if a processor is registered for a model.

This checks if the model has a registration (exact or family match), even if the transformation list is empty.

Parameters:

  • model_name (str) –

    Name of the model to check

Returns:

  • bool

    True if model has a registration (explicit or via family)

list_registered_families
list_registered_families()

Get list of registered family names.

Returns:

  • list[str]

    List of family names

list_registered_models
list_registered_models()

Get list of explicitly registered model names.

Returns:

  • list[str]

    List of model names with exact registrations

register_family
register_family(family_name, matcher, adaptations)

Register adaptations for a family of models.

The matcher function determines which models belong to this family. It receives a model name and should return True if the model matches.

Parameters:

  • family_name (str) –

    Name for this model family (used as key)

  • matcher (Callable[[str], bool]) –

    Function that takes a model name and returns True if it matches

  • adaptations (list[ParameterAdaptation]) –

    List of adaptations to apply for matching models

Examples:

>>> registry = ParameterAdapterRegistry()
>>>
>>> # Match all race models with 2 choices
>>> registry.register_family(
...     "race_2",
...     lambda m: m.startswith("race_") and m.endswith("_2"),
...     [ColumnStackParameters(["v0", "v1"], "v")]
... )
>>>
>>> # Match all models with "no_bias" in the name
>>> registry.register_family(
...     "no_bias_models",
...     lambda m: "no_bias" in m,
...     [SetDefaultValue("z", 0.5)]
... )
register_model
register_model(model_name, adaptations)

Register adaptations for a specific model.

Parameters:

  • model_name (str) –

    Exact model name (e.g., "lba2", "ddm", "race_3")

  • adaptations (list[ParameterAdaptation]) –

    List of adaptations to apply for this model

Examples:

>>> registry = ParameterAdapterRegistry()
>>> registry.register_model("lba3", [
...     SetDefaultValue("nact", 3),
...     ColumnStackParameters(["v0", "v1", "v2"], "v")
... ])

get_adapter_registry

get_adapter_registry()

Get the global parameter adapter registry.

Use this to access registry methods like list_registered_models() or get_processor() directly.

Returns:

  • ParameterAdapterRegistry

    The global parameter adapter registry instance, pre-populated with all built-in model adaptations

Examples:

>>> from ssms.basic_simulators.parameter_adapters import get_adapter_registry
>>>
>>> # List all models with registered adaptations
>>> registry = get_adapter_registry()
>>> print(registry.list_registered_models())
['lba2', 'lba3', 'lca_3', ...]
>>>
>>> # Check if a model has adaptations
>>> if registry.has_processor("my_model"):
...     adaptations = registry.get_processor("my_model")
>>>
>>> # Describe adaptations for a model
>>> print(registry.describe("lba3"))

register_adapter_to_model

register_adapter_to_model(model_name, adaptations)

Register parameter adaptations to a specific model globally.

This associates existing parameter adaptation classes with a model. Once registered, the model will use these adaptations automatically when simulated with ModularParameterSimulatorAdapter.

Note: This registers which adaptations a MODEL uses, not a new adapter type. To create custom adapter types, subclass ParameterAdaptation directly.

Parameters:

  • model_name (str) –

    Unique model name

  • adaptations (list[ParameterAdaptation]) –

    List of parameter adaptation instances to apply for this model

Examples:

Register adaptations for a custom model:

>>> from ssms.basic_simulators.parameter_adapters import register_adapter_to_model
>>> from ssms.basic_simulators.parameter_adapters import (
...     SetDefaultValue, ExpandDimension, ColumnStackParameters
... )
>>>
>>> register_adapter_to_model("my_model", [
...     SetDefaultValue("z", 0.5),
...     ExpandDimension(["a", "t"]),
...     ColumnStackParameters(["v0", "v1"], "v"),
... ])
>>>
>>> # Now when simulating with this model, adaptations are applied automatically
>>> sim = Simulator(model="my_model")

Override built-in model adaptations:

>>> # Register custom adaptations for an existing model
>>> register_adapter_to_model("lba2", [
...     ColumnStackParameters(["v0", "v1"], "v"),
...     # Custom adaptations here
... ])

register_adapter_to_model_family

register_adapter_to_model_family(
    family_name, matcher, adaptations
)

Register parameter adaptations to a family of models globally.

This associates existing parameter adaptation classes with multiple models matching a pattern, without having to register each model individually.

Parameters:

  • family_name (str) –

    Name for this model family (used as identifier)

  • matcher (Callable[[str], bool]) –

    Function that returns True if a model name matches this family

  • adaptations (list[ParameterAdaptation]) –

    List of parameter adaptation instances to apply for matching models

Examples:

Register adaptations for all race models with 2 alternatives:

>>> from ssms.basic_simulators.parameter_adapters import register_adapter_to_model_family
>>> from ssms.basic_simulators.parameter_adapters import ColumnStackParameters
>>>
>>> register_adapter_to_model_family(
...     "race_2",
...     lambda m: m.startswith("race_") and m.endswith("_2"),
...     [ColumnStackParameters(["v0", "v1"], "v")]
... )
>>>
>>> # Now all models matching the pattern use these adaptations
>>> # e.g., "race_no_bias_2", "race_no_z_2", etc.

Register for models with specific naming pattern:

>>> register_adapter_to_model_family(
...     "no_bias_models",
...     lambda m: "no_bias" in m,
...     [SetDefaultValue("z", 0.5)]
... )

ssms.basic_simulators.simulator

This module defines the basic simulator function which is the main workshorse of the package. In addition some utility functions are provided that help with preprocessing the output of the simulator function.

bin_simulator_output

bin_simulator_output(
    out=None, bin_dt=0.04, nbins=0, max_t=-1, freq_cnt=False
)

Turns RT part of simulator output into bin-identifier by trial

Arguments
out : dict
    Output of the 'simulator' function
bin_dt : float
    If nbins is 0, this determines the desired
    bin size which in turn automatically
    determines the resulting number of bins.
nbins : int
    Number of bins to bin reaction time data into.
    If supplied as 0, bin_dt instead determines the number of
    bins automatically.
max_t : float <default=-1>
    Override the 'max_t' metadata as part of the simulator output.
    Sometimes useful, but usually default will do the job.
freq_cnt : bool <default=False>
    Decide whether to return proportions (default) or counts in bins.

Returns:

  • A histogram of counts or proportions.

bin_simulator_output_pointwise

bin_simulator_output_pointwise(
    out=None, bin_dt=0.04, nbins=0
)

Turns RT part of simulator output into bin-identifier by trial

Arguments
out: dict
    Output of the 'simulator' function
bin_dt: float
    If nbins is 0, this determines the desired
    bin size which in turn automatically
    determines the resulting number of bins.
nbins: int
    Number of bins to bin reaction time data into.
    If supplied as 0, bin_dt instead determines the
    number of bins automatically.

Returns:

  • 2d array. The first columns collects bin-identifiers

    by trial, the second column lists the corresponding choices.

make_boundary_dict

make_boundary_dict(config, theta)

Create a dictionary containing boundary-related parameters and functions.

This function extracts boundary-related parameters from the input theta dictionary, based on the boundary configuration specified in the config. It also retrieves the appropriate boundary function from the boundary registry.

Args: config (dict): A dictionary containing model configuration, including the boundary name. theta (dict): A dictionary of parameter values, potentially including boundary-related parameters.

Returns: dict: A dictionary containing: - boundary_params (dict): Extracted boundary-related parameters (including 'a'). - boundary_fun (callable): The boundary function corresponding to the specified boundary name.

make_drift_dict

make_drift_dict(config, theta)

Create a dictionary containing drift-related parameters and functions.

This function extracts drift-related parameters from the input theta dictionary, based on the drift configuration specified in the config. It also retrieves the appropriate drift function from the drift registry.

Args: config (dict): A dictionary containing model configuration, including the drift name. theta (dict): A dictionary of parameter values, potentially including drift-related parameters.

Returns: dict: A dictionary containing: - drift_fun (callable): The drift function corresponding to the specified drift name. - drift_params (dict): Extracted drift-related parameters. If no drift name is specified in config, returns an empty dictionary.

make_noise_vec

make_noise_vec(sigma_noise, n_trials, n_particles)

Create a noise vector for simulation.

This function generates a noise vector based on the provided sigma_noise parameter, replicating it across trials and particles as needed.

Arguments
sigma_noise (float | np.ndarray): Standard deviation of the noise. Can be a single float value
    or a numpy array.
n_trials (int): Number of trials to simulate.
n_particles (int): Number of particles per trial.

Returns:

  • np.ndarray: A noise vector with appropriate shape for the simulation,

    containing the replicated sigma_noise values.

simulator

simulator(
    theta,
    model="angle",
    n_samples=1000,
    delta_t=0.001,
    max_t=20,
    no_noise=False,
    sigma_noise=None,
    smooth_unif=True,
    random_state=None,
    return_option="full",
    n_threads=1,
    extra_fields=None,
)

Basic data simulator for the models included in HDDM.

Arguments
theta : list, numpy.array, dict or pd.DataFrame
    Parameters of the simulator. If 2d array, each row is treated as a 'trial'
    and the function runs n_sample * n_trials simulations.
deadline : numpy.array <default=None>
    If supplied, the simulator will run a deadline model. RTs will be returned
model: str <default='angle'>
    Determines the model that will be simulated.
n_samples: int <default=1000>
    Number of simulation runs for each row in the theta argument.
delta_t: float
    Size fo timesteps in simulator (conceptually measured in seconds)
max_t: float
    Maximum reaction the simulator can reach
no_noise: bool <default=False>
    Turn noise of (useful for plotting purposes mostly)
sigma_noise: float | None <default=None>
    Standard deviation of noise in the diffusion process. If None, defaults to 1.0 for most models
    and 0.1 for LBA models. If no_noise is True, sigma_noise will be set to 0.0.
    If 'sd' or 's' is passed via theta dictionary, sigma_noise must be None.
smooth_unif: bool <default=True>
    Whether to add uniform random noise to RTs to smooth the distributions.
random_state: int | None <default=None>
    Integer passed to the C-level RNG seeding.  Must lie in
    ``[-2**31, 2**31 - 1]`` so it fits in a 32-bit signed C ``long`` (required
    on Windows).  ``None`` draws a seed from that range automatically.
    Non-integer RNG objects may be supported on specific code paths.
return_option: str <default='full'>
    Determines what the function returns. Can be either
    'full' or 'minimal'. If 'full' the function returns
    a dictionary with keys 'rts', 'responses' and 'metadata', and
    metadata contains the model parameters and some additional
    information. 'metadata' is a simpler dictionary with less information
    if 'minimal' is chosen.
n_threads: int <default=1>
    Number of threads for parallel execution. If > 1 and OpenMP is available,
    uses multi-threaded simulation. Note: trajectory recording is only
    available with n_threads=1. For models that don't support n_threads yet,
    this parameter is silently ignored.

    **Reproducibility note:** the sequential path (n_threads=1) uses NumPy's
    RNG, while the parallel path (n_threads>1) uses a C-level GSL/Ziggurat
    RNG. Results with a fixed ``random_state`` are reproducible *within* a
    given ``n_threads`` value, but will differ *across* n_threads values.
    Both paths produce statistically equivalent distributions.
Return

dictionary where keys can be (rts, responses, metadata) or (rt-response histogram, metadata) or (rts binned pointwise, responses, metadata)

validate_ssm_parameters

validate_ssm_parameters(model, theta)

Validate the parameters for Sequential Sampling Models (SSM).

This function checks the validity of parameters for different SSM models. It performs specific checks based on the model type.

Args: model (str): The name of the SSM model. theta (dict): A dictionary containing the model parameters.

Raises: ValueError: If any of the parameter validations fail.

ssms.basic_simulators.simulator_class

Class-based interface for Sequential Sampling Model simulations.

This module provides a modern, object-oriented interface for SSM simulations that supports custom boundary functions, drift functions, and even fully custom simulator implementations.

Simulator

Simulator(
    model=None,
    boundary=None,
    drift=None,
    simulator_function=None,
    parameter_adapter=None,
    parameter_adaptations=None,
    **config_overrides
)

Class-based interface for Sequential Sampling Model simulations.

This class provides a flexible, extensible way to configure and run SSM simulations. It supports: - Pre-defined models (via string names like "ddm", "angle", etc.) - Custom boundary and drift functions with existing simulators - Fully custom simulator functions - Configuration overrides for any model parameter

Examples:

Basic usage with pre-defined model:

>>> sim = Simulator("ddm")
>>> results = sim.simulate(theta={'v': 0.5, 'a': 1.0, 'z': 0.5, 't': 0.3})

Custom boundary function:

>>> def my_boundary(t, theta, scale):
...     return scale * np.sin(theta * t)
>>> sim = Simulator("ddm", boundary=my_boundary, boundary_params=["theta", "scale"])
>>> results = sim.simulate(theta={'v': 0.5, 'a': 1.0, 'z': 0.5, 't': 0.3,
...                               'theta': 0.5, 'scale': 1.0})

Fully custom simulator:

>>> def my_sim(v, a, z, t, max_t=20, n_samples=1000, **kwargs):
...     rts = np.random.exponential(1/abs(v), n_samples) + t
...     choices = np.where(np.random.random(n_samples) < z, 1, -1)
...     return {'rts': rts, 'choices': choices,
...             'metadata': {'model': 'custom', 'n_samples': n_samples}}
>>> sim = Simulator(simulator_function=my_sim, params=["v", "a", "z", "t"],
...                 nchoices=2)
>>> results = sim.simulate(theta={'v': 0.5, 'a': 1.0, 'z': 0.5, 't': 0.3})

Attributes:

  • config (dict) –

    The full model configuration dictionary

Parameters:

  • model (str, dict, or None, default: None ) –

    Either a model name (e.g., "ddm", "angle"), a full configuration dictionary, or None if providing a custom simulator_function.

  • boundary (str, Callable, or None, default: None ) –

    Boundary function. Can be: - A string name from the boundary registry (e.g., "angle", "weibull_cdf") - A callable function with signature: func(t, **params) -> np.ndarray - None to use the model's default boundary

  • drift (str, Callable, or None, default: None ) –

    Drift function. Can be: - A string name from the drift registry (e.g., "constant", "gamma_drift") - A callable function with signature: func(t, **params) -> np.ndarray - None to use the model's default drift (if applicable)

  • simulator_function (Callable or None, default: None ) –

    Custom simulator function. If provided, this overrides the model's default simulator. Must follow the simulator interface contract.

  • parameter_adapter (ParameterSimulatorAdapterProtocol or None, default: None ) –

    Custom parameter adapter for preparing parameters for simulators. If None, uses ModularParameterSimulatorAdapter with default adaptations for the model.

  • parameter_adaptations (list[ParameterAdaptation] or None, default: None ) –

    Additional parameter adaptations to apply AFTER the model's default adaptations. Useful for adding custom parameter preparation without replacing the entire adapter. Only used if parameter_adapter is None.

  • **config_overrides

    Additional configuration parameters to override. Common options: - params : list[str] - Parameter names (required if simulator_function provided) - param_bounds : list - [[lower bounds], [upper bounds]] - nchoices : int - Number of choices (required if simulator_function provided) - choices : list - Possible choice values - boundary_params : list[str] - Parameters for custom boundary function - drift_params : list[str] - Parameters for custom drift function - name : str - Model name (for custom simulators)

Raises:

  • ValueError

    If configuration is invalid or missing required parameters

Examples:

Use default modular adapter:

>>> sim = Simulator("lba2")
>>> results = sim.simulate(theta={'v0': 0.5, 'v1': 0.6, 'A': 0.5, 'b': 1.0})

Add custom adaptations:

>>> from ssms.basic_simulators.parameter_adapters import SetDefaultValue
>>> sim = Simulator("ddm", parameter_adaptations=[SetDefaultValue("custom_param", 42)])

Use a custom parameter adapter:

>>> custom_adapter = ModularParameterSimulatorAdapter()
>>> sim = Simulator("ddm", parameter_adapter=custom_adapter)

config property

config

Get the full model configuration.

Returns:

  • dict

    Configuration dictionary

parameter_adapter property

parameter_adapter

Get the configured parameter adapter.

Returns:

simulate

simulate(
    theta,
    n_samples=1000,
    delta_t=0.001,
    max_t=20,
    no_noise=False,
    sigma_noise=None,
    smooth_unif=True,
    random_state=None,
    return_option="full",
    n_threads=1,
    extra_fields=None,
)

Run simulation with given parameters.

Parameters:

  • theta (list, np.ndarray, dict, or pd.DataFrame) –

    Model parameters. If dict or DataFrame, keys/columns should match parameter names in config. If array, order should match config['params'].

  • extra_fields (dict or None, default: None ) –

    Optional per-trial covariates forwarded to the model function (e.g. the aDDM fixations r1, r2, flag, sacc_array, d, sigma), so a covariate-driven model can condition on observed inputs rather than self-sampling them. Empty/None is a no-op for models that take no covariates. Arrays are per-trial (aligned with theta's trial axis).

  • n_samples (int, default: 1000 ) –

    Number of simulation samples per parameter set

  • delta_t (float, default: 0.001 ) –

    Time step size for simulation

  • max_t (float, default: 20 ) –

    Maximum simulation time

  • no_noise (bool, default: False ) –

    If True, disable noise in simulation

  • sigma_noise (float or None, default: None ) –

    Standard deviation of noise. If None, uses model defaults.

  • smooth_unif (bool, default: True ) –

    Whether to add uniform smoothing to RTs

  • random_state (int or None, default: None ) –

    Integer seed for the C-level RNG. Must lie in [-2**31, 2**31 - 1] (fits a 32-bit signed C long, required on Windows). None picks a seed in [0, 2**31 - 1] automatically.

  • return_option (str, default: "full" ) –

    Output format: "full" or "minimal"

  • n_threads (int, default: 1 ) –

    Number of threads for parallel execution. If > 1 and OpenMP is available, uses multi-threaded simulation. Note: trajectory recording is only available with n_threads=1.

Returns:

  • dict

    Simulation results with keys: - 'rts' : np.ndarray of reaction times - 'choices' : np.ndarray of choices - 'metadata' : dict with simulation metadata - Additional keys depending on model and return_option

Raises:

  • ValueError

    If parameters are invalid

validate_params

validate_params(theta)

Validate parameter values.

Parameters:

  • theta (dict) –

    Parameter dictionary to validate

Raises:

  • ValueError

    If parameters are invalid