Skip to content

Support utilities API

The ssms.support_utils namespace exports the kde_class and utils modules. For the task-oriented likelihood workflow and its input prerequisites, see Estimate a likelihood with the KDE class.

ssms.support_utils

Modules:

  • kde_class –
  • utils –

    This module provides utility functions for handling parameter dependencies and sampling

ssms.support_utils.kde_class

Classes:

  • LogKDE –

    Class for generating kdes from (rt, choice) data. Works for any number of choices.

Functions:

  • bandwidth_silverman –

    Computes silverman bandwidth for an array of samples (rts in our context, but general).

LogKDE

LogKDE(simulator_data: dict, bandwidth_type: str = 'silverman', auto_bandwidth: bool = True, displace_t: bool = False)

Class for generating kdes from (rt, choice) data. Works for any number of choices.

Only samples usable as log-RT KDE input are retained per choice: omission sentinels, non-positive RTs and non-finite log-RTs are dropped from data['rts'] and data['log_rts']. Non-positive RTs are legitimate simulator output for models whose non-decision-time kernel has unbounded support (a Normal(t, st) kernel, say). data['choice_proportions'] is computed before this filter, so it still counts the dropped samples. That is intentional and consistent with omissions, which also count towards their choice's proportion while contributing no RT to its density.

Known limitation: a choice left with exactly one retained sample falls through to bandwidth_silverman's std_n_1 default, yielding a bandwidth of about 10.59 that passes the bandwidth > 0 check. That choice's density estimate is not meaningful.

Methods:

  • compute_bandwidths –

    Computes bandwidths for each choice from rt data. _generate_base_kdes(auto_bandwidth=True, bandwidth_type='silverman') Generates kdes from rt data. kde_eval(data=([], []), log_eval=True) Evaluates kde log likelihood at chosen points. kde_sample(n_samples=2000, use_empirical_choice_p=True, alternate_choice_p=0) Samples from a given kde. _attach_data_from_simulator(simulator_data={'rts':[0, 2, 4], 'choices':[-1, 1, -1], 'metadata':{}})) Helper function to transform ddm simulator output to dataset suitable for the kde function class.

  • Returns: –

    type: description

Arguments:
simulator_data: Dictionary containing simulation data with keys 'rts', 'choices', and 'metadata'.
    Follows the format returned by simulator functions in this package.
bandwidth_type: Type of bandwidth to use for KDE. Currently only 'silverman' is supported.
    Defaults to 'silverman'.
auto_bandwidth: Whether to automatically compute bandwidths based on the data.
    If False, bandwidths must be set manually. Defaults to True.
displace_t: Whether to shift RTs by the t parameter from metadata.
    Only works if all trials have the same t value and the model's
    response times cannot fall below t (see ``KDE_NO_DISPLACE_T``
    and the ``ndt_edge_shift`` config key). Defaults to False.
Raises:
ValueError: If displace_t is True but metadata contains multiple t
    values, or names a model whose response times start below t.
compute_bandwidths
compute_bandwidths(bandwidth_type: str = 'silverman') -> list[float | str]

Computes bandwidths for each choice from rt data.

Arguments:

bandwidth_type: str Type of bandwidth to use, default is 'silverman' which follows silverman rule. return_result: bool Whether to return the result. Defaults to False.

Returns:

bandwidths: list[float] | None List of bandwidths for each choice if return_result is True, otherwise None.

kde_eval
kde_eval(data: dict, log_eval: bool = True, lb: float = -66.774, eps: float = 0.0001, filter_rts: float = OMISSION_SENTINEL) -> np.ndarray

Evaluates kde log likelihood at chosen points.

Arguments:

data: dict Dictionary with keys 'rts', and/or 'log_rts' and 'choices' to evaluate the kde at. If 'rts' is provided, 'log_rts' is ignored. log_eval: boolean Whether to return log likelihood or likelihood, default is True. lb: float Lower bound for log likelihoods, default is -66.774. (This is the log of 1e-29) eps: float Epsilon value to use for lower bounds on rts. filter_rts: float Value to filter rts by, default is OMISSION_SENTINEL (-999). This is the sentinel value returned by simulators when a trial exceeds max_t or deadline (i.e., an omission).

Returns:

log_kde_eval: array Array of log likelihoods for each (rt, choice) pair.

kde_sample
kde_sample(n_samples: int = 2000, use_empirical_choice_p: bool = True, alternate_choice_p: ndarray | float = 0.0, random_state: int | None = None) -> dict[str, np.ndarray | dict]

Samples from a given kde.

Arguments:

n_samples: int Number of samples to draw. use_empirical_choice_p: bool Whether to use empirical choice proportions, default is True. (Note 'empirical' here, refers to the originally attached datasets that served as the basis to generate the choice-wise kdes) alternate_choice_p: np.ndarray | float Array of choice proportions to use, default is 0. (Note 'alternate' here refers to 'alternative' to the 'empirical' choice proportions) random_state: int | None Random seed for reproducibility. If None, uses non-reproducible random behavior.

Returns:

dict[str, np.ndarray | dict] Dictionary containing: - 'rts': np.ndarray - Response times - 'log_rts': np.ndarray - Log of response times - 'choices': np.ndarray - Choices made - 'metadata': dict - Simulator information

bandwidth_silverman

bandwidth_silverman(sample: Iterable[float] = (0, 0, 0), std_cutoff: float = 0.001, std_proc: str = 'restrict', std_n_1: float = 10.0) -> np.float64

Computes silverman bandwidth for an array of samples (rts in our context, but general).

Arguments:

sample: np.ndarray Array of samples to compute bandwidth for. std_cutoff: float Cutoff for std, default is 1e-3. (If sample-std is smaller than this, we either kill it or restrict it to this value) std_proc: str How to deal with small stds, default is 'restrict'. (Options: 'kill', 'restrict') std_n_1: float Value to use if n = 1, default is 10.0. (Not clear if the default is sensible here)

Returns:

float Silverman bandwidth for the given sample. This is applied as the bandwidth parameter when generating gaussian-based kdes in the LogKDE class.

ssms.support_utils.utils

This module provides utility functions for handling parameter dependencies and sampling parameters within specified constraints.

Functions:

  • parse_bounds –

    Parse the bounds of a parameter and extract any dependencies.

  • build_dependency_graph –

    Build a dependency graph based on parameter bounds.

  • topological_sort_util –

    visited: Set[str], stack: List[str], graph: Dict[str, Set[str]], temp_marks: Set[str]) -> None Helper function for performing a depth-first search in the topological sort.

  • topological_sort –

    Perform a topological sort on the dependency graph to determine the sampling order.

  • sample_parameters_from_constraints –

    sample_size: int) -> Dict[str, np.ndarray] Sample parameters uniformly within specified bounds, respecting any dependencies.

build_dependency_graph

build_dependency_graph(param_dict: dict[str, tuple[Any, Any]]) -> Dict[str, Set[str]]

Build a dependency graph based on parameter bounds.

The graph is structured as parent -> children, where children depend on parent. For example, if 'st' has upper bound 't', then graph['t'] contains 'st'.

Args: param_dict: Dictionary mapping parameter names to (lower, upper) bounds.

Returns: Dictionary where keys are parameters and values are sets of parameters that depend on them.

Raises: ValueError: If a dependency references an undefined parameter.

Example: >>> param_dict = {"a": (0, 1), "b": ("a", 2)} >>> build_dependency_graph(param_dict) {'a': {'b'}, 'b': set()}

parse_bounds

parse_bounds(bounds: tuple[Any, Any]) -> Set[str]

Parse the bounds of a parameter and extract any dependencies.

Args: bounds: A tuple of (lower, upper) bounds. Can be numeric or strings (for dependencies).

Returns: Set of parameter names that this parameter depends on.

Example: >>> parse_bounds((0.0, 1.0)) set() >>> parse_bounds(("a", 1.0)) {'a'} >>> parse_bounds((0.0, "b")) {'b'} >>> parse_bounds(("c", "d"))

sample_parameters_from_constraints

sample_parameters_from_constraints(param_dict: dict[str, tuple[Any, Any]], sample_size: int, random_state: int | None = None) -> Dict[str, np.ndarray]

Sample parameters uniformly within specified bounds, respecting any dependencies.

This is a backward-compatible wrapper around UniformParameterSampler.

Args: param_dict: Dictionary mapping parameter names to (lower, upper) bounds. Bounds can be numeric or strings (for dependencies). sample_size: Number of parameter sets to sample. random_state: Optional random seed for reproducibility.

Returns: Dictionary mapping parameter names to arrays of sampled values.

Raises: ValueError: If bounds are invalid (lower >= upper) or circular dependencies exist.

Example: >>> param_dict = {"v": (-1.0, 1.0), "a": (0.5, 2.0)} >>> samples = sample_parameters_from_constraints(param_dict, sample_size=10) >>> samples['v'].shape (10,)

topological_sort

topological_sort(graph: dict[str, set[str]]) -> List[str]

Perform a topological sort on the dependency graph to determine the sampling order.

Args: graph: Dependency graph where keys are parameters and values are sets of parameters that depend on them.

Returns: List of parameter names in sampling order (dependencies first).

Raises: ValueError: If circular dependencies are detected.

Example: >>> graph = {"a": {"b"}, "b": set()} >>> topological_sort(graph) ['a', 'b']

topological_sort_util

topological_sort_util(node: str, visited: set[str], stack: list[str], graph: dict[str, set[str]], temp_marks: set[str]) -> None

Helper function for performing a depth-first search in the topological sort.

Args: node: Current node to visit visited: Set of permanently visited nodes stack: List to accumulate the topological order (prepended in post-order) graph: Dependency graph temp_marks: Set of temporarily marked nodes (for cycle detection)

Raises: ValueError: If a circular dependency is detected.

LogKDE

ssms.support_utils.kde_class.LogKDE

LogKDE(simulator_data: dict, bandwidth_type: str = 'silverman', auto_bandwidth: bool = True, displace_t: bool = False)

Class for generating kdes from (rt, choice) data. Works for any number of choices.

Only samples usable as log-RT KDE input are retained per choice: omission sentinels, non-positive RTs and non-finite log-RTs are dropped from data['rts'] and data['log_rts']. Non-positive RTs are legitimate simulator output for models whose non-decision-time kernel has unbounded support (a Normal(t, st) kernel, say). data['choice_proportions'] is computed before this filter, so it still counts the dropped samples. That is intentional and consistent with omissions, which also count towards their choice's proportion while contributing no RT to its density.

Known limitation: a choice left with exactly one retained sample falls through to bandwidth_silverman's std_n_1 default, yielding a bandwidth of about 10.59 that passes the bandwidth > 0 check. That choice's density estimate is not meaningful.

Methods:

  • compute_bandwidths –

    Computes bandwidths for each choice from rt data. _generate_base_kdes(auto_bandwidth=True, bandwidth_type='silverman') Generates kdes from rt data. kde_eval(data=([], []), log_eval=True) Evaluates kde log likelihood at chosen points. kde_sample(n_samples=2000, use_empirical_choice_p=True, alternate_choice_p=0) Samples from a given kde. _attach_data_from_simulator(simulator_data={'rts':[0, 2, 4], 'choices':[-1, 1, -1], 'metadata':{}})) Helper function to transform ddm simulator output to dataset suitable for the kde function class.

  • Returns: –

    type: description

Arguments:
simulator_data: Dictionary containing simulation data with keys 'rts', 'choices', and 'metadata'.
    Follows the format returned by simulator functions in this package.
bandwidth_type: Type of bandwidth to use for KDE. Currently only 'silverman' is supported.
    Defaults to 'silverman'.
auto_bandwidth: Whether to automatically compute bandwidths based on the data.
    If False, bandwidths must be set manually. Defaults to True.
displace_t: Whether to shift RTs by the t parameter from metadata.
    Only works if all trials have the same t value and the model's
    response times cannot fall below t (see ``KDE_NO_DISPLACE_T``
    and the ``ndt_edge_shift`` config key). Defaults to False.
Raises:
ValueError: If displace_t is True but metadata contains multiple t
    values, or names a model whose response times start below t.

ssms.support_utils.kde_class.LogKDE.compute_bandwidths

compute_bandwidths(bandwidth_type: str = 'silverman') -> list[float | str]

Computes bandwidths for each choice from rt data.

Arguments:

bandwidth_type: str Type of bandwidth to use, default is 'silverman' which follows silverman rule. return_result: bool Whether to return the result. Defaults to False.

Returns:

bandwidths: list[float] | None List of bandwidths for each choice if return_result is True, otherwise None.

ssms.support_utils.kde_class.LogKDE.kde_eval

kde_eval(data: dict, log_eval: bool = True, lb: float = -66.774, eps: float = 0.0001, filter_rts: float = OMISSION_SENTINEL) -> np.ndarray

Evaluates kde log likelihood at chosen points.

Arguments:

data: dict Dictionary with keys 'rts', and/or 'log_rts' and 'choices' to evaluate the kde at. If 'rts' is provided, 'log_rts' is ignored. log_eval: boolean Whether to return log likelihood or likelihood, default is True. lb: float Lower bound for log likelihoods, default is -66.774. (This is the log of 1e-29) eps: float Epsilon value to use for lower bounds on rts. filter_rts: float Value to filter rts by, default is OMISSION_SENTINEL (-999). This is the sentinel value returned by simulators when a trial exceeds max_t or deadline (i.e., an omission).

Returns:

log_kde_eval: array Array of log likelihoods for each (rt, choice) pair.

ssms.support_utils.kde_class.LogKDE.kde_sample

kde_sample(n_samples: int = 2000, use_empirical_choice_p: bool = True, alternate_choice_p: ndarray | float = 0.0, random_state: int | None = None) -> dict[str, np.ndarray | dict]

Samples from a given kde.

Arguments:

n_samples: int Number of samples to draw. use_empirical_choice_p: bool Whether to use empirical choice proportions, default is True. (Note 'empirical' here, refers to the originally attached datasets that served as the basis to generate the choice-wise kdes) alternate_choice_p: np.ndarray | float Array of choice proportions to use, default is 0. (Note 'alternate' here refers to 'alternative' to the 'empirical' choice proportions) random_state: int | None Random seed for reproducibility. If None, uses non-reproducible random behavior.

Returns:

dict[str, np.ndarray | dict] Dictionary containing: - 'rts': np.ndarray - Response times - 'log_rts': np.ndarray - Log of response times - 'choices': np.ndarray - Choices made - 'metadata': dict - Simulator information

ssms.support_utils.kde_class.bandwidth_silverman

bandwidth_silverman(sample: Iterable[float] = (0, 0, 0), std_cutoff: float = 0.001, std_proc: str = 'restrict', std_n_1: float = 10.0) -> np.float64

Computes silverman bandwidth for an array of samples (rts in our context, but general).

Arguments:

sample: np.ndarray Array of samples to compute bandwidth for. std_cutoff: float Cutoff for std, default is 1e-3. (If sample-std is smaller than this, we either kill it or restrict it to this value) std_proc: str How to deal with small stds, default is 'restrict'. (Options: 'kill', 'restrict') std_n_1: float Value to use if n = 1, default is 10.0. (Not clear if the default is sensible here)

Returns:

float Silverman bandwidth for the given sample. This is applied as the bandwidth parameter when generating gaussian-based kdes in the LogKDE class.