Create stimuli spanning a parameter space#

One of the strengths of stimupy is that its fully parameterized stimulus functions make it easy to create stimulus parameter spaces — or, more briefly, stimspaces.

A stimspace is a systematic variation of a stimulus across one or more parameter dimensions.
This is useful for experiments, computational modeling, and visualizations where you want to explore how changing certain stimulus parameters affects a stimulus.

Create a stimspace in stimupy#

stimupy provides two main utility functions for building stimspaces:

To define a stimspace, supply the parameters you want to vary as lists.
A parameter with a single value should still be wrapped in a list for consistency.


Example: 1D stimspace for Gabors#

To generate a stimspace, we first need to know the relevant variables of the stimulus function we want to use. Here, let’s explore stimspaces for Gabors.

from stimupy.stimuli.gabors import gabor

help(gabor)
Help on function gabor in module stimupy.stimuli.gabors:

gabor(
    visual_size=None,
    ppd=None,
    shape=None,
    frequency=None,
    n_bars=None,
    bar_width=None,
    period='ignore',
    rotation=0.0,
    phase_shift=0,
    intensities=(0.0, 1.0),
    origin='center',
    round_phase_width=False,
    sigma=None
)
    Draw a Gabor: a sinewave grating in a Gaussian envelope

    Parameters
    ----------
    visual_size : Sequence[Number, Number], Number, or None (default)
        visual size [height, width] of image, in degrees
    ppd : Sequence[Number, Number], Number, or None (default)
        pixels per degree [vertical, horizontal]
    shape : Sequence[Number, Number], Number, or None (default)
        shape [height, width] of image, in pixels
    frequency : Number, or None (default)
        spatial frequency of grating, in cycles per degree visual angle
    n_bars : Number, or None (default)
        number of bars in the grating
    bar_width : Number, or None (default)
        width of a single bar, in degrees visual angle
    sigma : float or (float, float)
        sigma of Gaussian in degree visual angle (y, x)
    period : "even", "odd", "either" or "ignore" (default)
        ensure whether the grating has "even" number of phases, "odd"
        number of phases, either or whether not to round the number of
        phases ("ignore")
    rotation : float, optional
        rotation (in degrees), counterclockwise, by default 0.0 (horizontal)
    phase_shift : float
        phase shift of grating in degrees
    intensities : Sequence[float, ...]
        maximal intensity value for each bar, by default (0.0, 1.0).
    origin : "corner", "mean" or "center"
        if "corner": set origin to upper left corner
        if "mean": set origin to hypothetical image center (default)
        if "center": set origin to real center (closest existing value to mean)

    Returns
    -------
    dict[str, Any]
        dict with the stimulus (key: "img"),
        mask with integer index for each bar (key: "grating_mask"),
        and additional keys containing stimulus parameters

In this example, we vary the frequency parameter while keeping the other parameters constant.

from stimupy.utils import permutate_params

params = {
    "visual_size": [1.],      # Each parameter must be in a list []
    "ppd": [100],
    "sigma": [0.1],
    "frequency": [1, 2, 4],   # Varying frequency spans the stimspace
}

permuted_params = permutate_params(params=params)
permuted_params
[{'visual_size': 1.0, 'ppd': 100, 'sigma': 0.1, 'frequency': 1},
 {'visual_size': 1.0, 'ppd': 100, 'sigma': 0.1, 'frequency': 2},
 {'visual_size': 1.0, 'ppd': 100, 'sigma': 0.1, 'frequency': 4}]

Now we generate and plot the stimuli for this 1D stimspace:

from stimupy.utils import create_stimspace_stimuli, plot_stimuli

space_1d = create_stimspace_stimuli(
    stimulus_function=gabor,
    permutations_dicts=permuted_params,
)

plot_stimuli(space_1d, units="deg")
../_images/a62ebb61e3b27337bfac334dde877c75f50d49024cc45baee7e405e8bcda9179.png

Example: 2D stimspace for Gabors#

Before, we have only focused on a single stimulus parameter. However, you can vary as many stimulus parameters at once as you like. In the next example, we vary both the frequency and the sigma of the Gabor to create a 2D stimspace.

params["sigma"] = [0.05, 0.1, 0.15]  # Now vary sigma as well

permuted_params_2d = permutate_params(params=params)
permuted_params_2d
[{'visual_size': 1.0, 'ppd': 100, 'sigma': 0.05, 'frequency': 1},
 {'visual_size': 1.0, 'ppd': 100, 'sigma': 0.05, 'frequency': 2},
 {'visual_size': 1.0, 'ppd': 100, 'sigma': 0.05, 'frequency': 4},
 {'visual_size': 1.0, 'ppd': 100, 'sigma': 0.1, 'frequency': 1},
 {'visual_size': 1.0, 'ppd': 100, 'sigma': 0.1, 'frequency': 2},
 {'visual_size': 1.0, 'ppd': 100, 'sigma': 0.1, 'frequency': 4},
 {'visual_size': 1.0, 'ppd': 100, 'sigma': 0.15, 'frequency': 1},
 {'visual_size': 1.0, 'ppd': 100, 'sigma': 0.15, 'frequency': 2},
 {'visual_size': 1.0, 'ppd': 100, 'sigma': 0.15, 'frequency': 4}]
stimspace_2d = create_stimspace_stimuli(
    stimulus_function=gabor,
    permutations_dicts=permuted_params_2d,
)

plot_stimuli(stimspace_2d, units="deg")
../_images/94ec191055795900f13104b5f89c1f7f8891305f782378feffb57776831f6c15.png