Ejemplo n.º 1
0
def linear_response(filt, stim, nsamples_after=0):
    """
    Compute the response of a linear filter to a stimulus.

    Parameters
    ----------
    filt : array_like
        The linear filter whose response is to be computed. The array should
        have shape ``(t, ...)``, where ``t`` is the number of time points in
        the filter and the ellipsis indicates any remaining spatial dimenions.
        The number of dimensions and the sizes of the spatial dimensions
        must match that of ``stim``.

    stim : array_like
        The stimulus to which the predicted response is computed. The array
        should have shape ``(T,...)``, where ``T`` is the number of time points
        in the stimulus and the ellipsis indicates any remaining spatial
        dimensions. The number of dimensions and the sizes of the spatial
        dimenions must match that of ``filt``.

    nsamples_after : int, optional
        The number of acausal points in the filter. Defaults to 0.

    Returns
    -------
    pred : array_like
        The predicted linear response, of shape ``(t,)``.

    Raises
    ------
    ValueError : If the number of dimensions of ``stim`` and ``filt`` do not
        match, or if the spatial dimensions differ.

    Notes
    -----
    Note that the first parameter is a *linear filter*. The values returned by
    ``filtertools.sta`` and ``filtertools.revcorr`` are proportional to the
    time-reverse of the linear filter, so to use those values in this function,
    they must be flipped along the first dimension.

    Both ``filtertools.sta`` and ``filtertools.revcorr`` can estimate "acausal"
    components, such as points in the stimulus occuring *after* a spike. The
    value passed as parameter ``nsamples_after`` must match that value used
    when calling ``filtertools.sta`` or ``filtertools.revcorr``.

    """
    if (filt.ndim != stim.ndim) or (filt.shape[1:] != stim.shape[1:]):
        raise ValueError("The filter and stimulus must have the same "
                         "number of dimensions and match in size along "
                         "spatial dimensions")
    if (nsamples_after >= filt.shape[0]):
        raise ValueError("Cannot compute the response of a "
                "filter with no causal points.")
    padded = np.concatenate((
            np.zeros((filt.shape[0] - nsamples_after - 1,) + stim.shape[1:]),
            stim, np.zeros((nsamples_after,) + stim.shape[1:])), axis=0)
    slices = np.fliplr(slicestim(padded, 
            filt.shape[0] - nsamples_after, nsamples_after))
    return np.einsum('tx,x->t', flat2d(slices), filt.ravel())
Ejemplo n.º 2
0
def test_slicestim_1d():
    """Test slicing a 1D stimulus into overlapping segments."""
    np.random.seed(0)
    stim_size = 1000
    stim = np.random.randn(stim_size,)
    history = 10
    sliced_stim = stimulustools.slicestim(stim, history)

    for i in range(stim_size - history + 1):
        assert np.all(sliced_stim[i] == stim[i:i + history]), 'slicing failed'
Ejemplo n.º 3
0
def test_linear_prediction_1d():
    """Test method for computing linear prediction from a
    filter to a one-dimensional stimulus.
    """
    np.random.seed(0)
    filt = np.random.randn(100, )
    stim = np.random.randn(1000, )
    pred = flt.linear_prediction(filt, stim)

    sl = slicestim(stim, filt.shape[0])
    assert np.allclose(sl.dot(filt), pred)
Ejemplo n.º 4
0
def test_linear_response_acausal():
    """Test computing a linear response from a filter to a 1D stimulus,
    including acausal portions of the stimulus.
    """
    np.random.seed(0)
    filt = np.random.randn(100,)
    stim = np.random.randn(1000,)
    pred = flt.linear_response(filt, stim, 10)

    sl = slicestim(stim, filt.shape[0] - 10, 10)
    assert np.allclose(sl.dot(filt), pred)
Ejemplo n.º 5
0
def test_slicestim_acausal():
    """Test slicing a stimulus into overlapping segments with
    samples before and after a hypothetical center.
    """
    np.random.seed(0)
    stim_size = 1000
    stim = np.random.randn(stim_size,)
    nbefore, nafter = 7, 3
    sliced_stim = stimulustools.slicestim(stim, nbefore, nafter)

    for i in range(stim_size - nbefore - nafter + 1):
        assert np.all(sliced_stim[i] == stim[i:i + nbefore + nafter]), 'slicing failed'
Ejemplo n.º 6
0
def test_slicestim_3d():
    """Test slicing a 3D stimulus into overlapping segments."""
    np.random.seed(0)
    stim_size = (100, 5, 5)
    stim = np.random.randn(*stim_size)
    history = 10
    
    sliced_stim = stimulustools.slicestim(stim, history)
    assert sliced_stim.ndim == stim.ndim + 1
    assert sliced_stim.shape[0] == stim.shape[0] - history + 1

    for i in range(stim_size[0] - history + 1):
        assert np.all(sliced_stim[i] == stim[i:i + history, ...]), 'slicing failed'
Ejemplo n.º 7
0
def test_slicestim_raises():
    """Verify slicestim() raises correct exceptions"""
    with pytest.raises(ValueError):
        stimulustools.slicestim(np.zeros(10,), 0)
    with pytest.raises(ValueError):
        stimulustools.slicestim(np.zeros(10,), 11)
    with pytest.raises(ValueError):
        stimulustools.slicestim(np.zeros(10,), 1.5)
Ejemplo n.º 8
0
def linear_response(filt, stim, nsamples_after=0):
    """
    Compute the response of a linear filter to a stimulus.

    Parameters
    ----------
    filt : array_like
        The linear filter whose response is to be computed. The array should
        have shape ``(t, ...)``, where ``t`` is the number of time points in the
        filter and the ellipsis indicates any remaining spatial dimenions.
        The number of dimensions and the sizes of the spatial dimensions
        must match that of ``stim``.

    stim : array_like
        The stimulus to which the predicted response is computed. The array
        should have shape (T,...), where ``T`` is the number of time points
        in the stimulus and the ellipsis indicates any remaining spatial
        dimensions. The number of dimensions and the sizes of the spatial
        dimenions must match that of ``filt``.

    nsamples_after : int, optional
        The number of acausal points in the filter. Defaults to 0.

    Returns
    -------
    pred : array_like
        The predicted linear response. The shape is ``(T - t + 1,)`` where
        ``T`` is the number of time points in the stimulus, and ``t`` is
        the number of time points in the filter. This is the valid portion
        of the convolution between the stimulus and filter.

    Raises
    ------
    ValueError : If the number of dimensions of ``stim`` and ``filt`` do not
        match, or if the spatial dimensions differ.

    Notes
    -----
    Both ``filtertools.sta`` and ``filtertools.revcorr`` can estimate "acausal"
    components, such as points in the stimulus occuring *after* a spike. The
    value passed as parameter ``nsamples_after`` must match that value used
    when calling ``filtertools.sta`` or ``filtertools.revcorr``.

    """
    if (filt.ndim != stim.ndim) or (filt.shape[1:] != stim.shape[1:]):
        raise ValueError("The filter and stimulus must have the same " +
                         "number of dimensions and match in size along spatial dimensions")

    slices = slicestim(stim, filt.shape[0] - nsamples_after, nsamples_after)
    return np.einsum('tx,x->t', flat2d(slices), filt.ravel())
Ejemplo n.º 9
0
def revcorr(response, stimulus, filter_length):
    """
    Compute the reverse-correlation between a stimulus and a response.

    This returns the best-fitting linear filter which predicts the given
    response from the stimulus. It is analogous to the spike-triggered
    average for continuous variables. ``response`` is most often a membrane
    potential.

    Parameters
    ----------
    response : array_like
        A continuous output response correlated with the stimulus. Must
        be one-dimensional.

    stimulus : array_like
        A input stimulus correlated with the ``response``. Must be of shape
        ``(t, ...)``, where ``t`` is the time and ``...`` indicates any spatial dimensions.

    filter_length : int
        The length of the returned filter, in samples of the ``stimulus`` and
        ``response`` arrays.

    Returns
    -------
    filt : array_like
        An array of shape ``(filter_length, ...)`` containing the best-fitting
        linear filter which predicts the response from the stimulus. The ellipses
        indicates spatial dimensions of the filter.

    Raises
    ------
    ValueError : If the ``stimulus`` and ``response`` arrays are of different shapes.

    Notes
    -----
    The ``response`` and ``stimulus`` arrays must share the same sampling
    rate. As the stimulus often has a lower sampling rate, one can use
    ``stimulustools.upsamplestim`` to upsample it.
    """
    if response.ndim > 1:
        raise ValueError("The `response` must be 1-dimensional")
    if response.size != (stimulus.shape[0] - filter_length + 1):
        msg = "`stimulus` must have {:#d} time points (`response.size` + `filter_length`)"
        raise ValueError(msg.format(response.size + filter_length + 1))

    slices = slicestim(stimulus, filter_length)
    recovered = np.einsum('tx,t->x', flat2d(slices), response)
    return recovered.reshape(slices.shape[1:])
Ejemplo n.º 10
0
def test_linear_prediction_nd():
    """Test method for computing linear prediction from a
    filter to a multi-dimensional stimulus.
    """
    np.random.seed(0)
    for ndim in range(2, 4):
        filt = np.random.randn(100, *((10, ) * ndim))
        stim = np.random.randn(1000, *((10, ) * ndim))
        pred = flt.linear_prediction(filt, stim)

        sl = slicestim(stim, filt.shape[0])
        tmp = np.zeros(sl.shape[0])
        for i in range(tmp.size):
            tmp[i] = np.inner(filt.ravel(), sl[i].ravel())

        assert np.allclose(tmp, pred)
Ejemplo n.º 11
0
def linear_prediction(filt, stim):
    """
    Compute the predicted linear response of a receptive field to a stimulus.

    Parameters
    ----------
    filt : array_like
        The linear filter whose response is to be computed. The array should
        have shape ``(t, ...)``, where ``t`` is the number of time points in the
        filter and the ellipsis indicates any remaining spatial dimenions.
        The number of dimensions and the sizes of the spatial dimensions
        must match that of ``stim``.

    stim : array_like
        The stimulus to which the predicted response is computed. The array
        should have shape (T,...), where ``T`` is the number of time points
        in the stimulus and the ellipsis indicates any remaining spatial
        dimensions. The number of dimensions and the sizes of the spatial
        dimenions must match that of ``filt``.

    Returns
    -------
    pred : array_like
        The predicted linear response. The shape is ``(T - t + 1,)`` where
        ``T`` is the number of time points in the stimulus, and ``t`` is 
        the number of time points in the filter. This is the valid portion
        of the convolution between the stimulus and filter

    Raises
    ------
    ValueError : If the number of dimensions of ``stim`` and ``filt`` do not
        match, or if the spatial dimensions differ.
    """
    if (filt.ndim != stim.ndim) or (filt.shape[1:] != stim.shape[1:]):
        raise ValueError(
            "The filter and stimulus must have the same " +
            "number of dimensions and match in size along spatial dimensions")

    slices = slicestim(stim, filt.shape[0])
    return np.einsum('tx,x->t', flat2d(slices), filt.ravel())
Ejemplo n.º 12
0
def test_slicestim_shape():
    shape = (10, 3, 3)
    history = 2
    stim = np.zeros(shape)
    assert (stimulustools.slicestim(stim, history).shape ==
            (shape[0] - history + 1, history, shape[1], shape[2]))
Ejemplo n.º 13
0
def revcorr(stimulus, response, nsamples_before, nsamples_after=0):
    """
    Compute the reverse-correlation between a stimulus and a response.

    Parameters
    ----------
    stimulus : array_like
        A input stimulus correlated with the ``response``. Must be of shape
        ``(t, ...)``, where ``t`` is the time and ``...`` indicates any spatial
        dimensions.

    response : array_like
        A continuous output response correlated with ``stimulus``. Must
        be one-dimensional, of size ``t``, the same size as ``stimulus``
        along the first axis. Note that the first ``history`` points of
        the response are ignored, where ``history = nsamples_before +
        nsamples_after``, in order to only return the portion of the
        correlation during which the ``stimulus`` and ``response``
        completely overlap.

    nsamples_before : int
        The maximum negative lag for the correlation between stimulus and
        response, in samples.

    nsamples_after : int, optional
        The maximum positive lag for the correlation between stimulus and
        response, in samples. Defaults to 0.

    Returns
    -------
    rc : array_like
        An array of shape ``(nsamples_before + nsamples_after, ...)``
        containing the best-fitting linear filter which predicts the response
        from the stimulus. The ellipses indicates spatial dimensions of the
        filter.

    lags : array_like
        An array of shape ``(nsamples_before + nsamples_after,)``, which gives
        the lags, in samples, between ``stimulus`` and ``response`` for the
        correlation returned in ``rc``. This can be converted to an axis of time
        (like that returned from ``filtertools.sta``) by multiplying by the 
        sampling period.

    Raises
    ------
    ValueError : If the ``stimulus`` and ``response`` arrays do not match in
    size along the first dimension.

    Notes
    -----
    The ``response`` and ``stimulus`` arrays must share the same sampling
    rate. As the stimulus often has a lower sampling rate, one can use
    ``stimulustools.upsample`` to upsample it.

    Reverse correlation is a method analogous to spike-triggered averaging for
    continuous response variables, such as a membrane voltage recording. It
    estimates the stimulus feature that most strongly correlates with the
    response on average.

    It is the time-reverse of the standard cross-correlation function, and is defined
    as:

    .. math::
        c[-k] = \\sum_{n} s[n] r[n - k]

    The parameter ``k`` is the lag between the two signals in samples. The range
    of lags computed in this method are determined by ``nsamples_before`` and
    ``nsamples_after``.

    Note that, as with ``filtertools.sta``, the values (samples) in the ``lags``
    array increase with increasing array index. This means that time is moving
    forward with increasing array index.

    Also note that this method assumes an uncorrelated stimulus. If the
    stimulus is correlated, those will bias the estimated reverse correlation.

    """
    history = nsamples_before + nsamples_after
    if response.ndim > 1:
        raise ValueError("The `response` must be 1-dimensional")
    if response.size != stimulus.shape[0]:
        raise ValueError('`stimulus` and `response` must match in ' +
                'size along the first axis')
    slices = slicestim(stimulus, nsamples_before, nsamples_after)
    recovered = np.einsum('tx,t->x', flat2d(slices),
            response[history - 1:]).reshape(slices.shape[1:])
    lags = np.arange(-nsamples_before + 1, nsamples_after + 1)
    return recovered, lags