Example #1
0
def convolve_with_3d_basis(stim, basis, shape=['first', 'central', 'central']):
    """ Project stimulus onto a basis.
    :param stim   T x Dx x Dy array of inputs.
                  T is the number of time bins
                  Dx is the stimulus x dimension.
                  Dy is the stimulus y dimension.
    :param basis  Tb x Dbx x Dby basis matrix
                  Tb is the length of the impulse response
                  Dbx is the basis x dimension
                  Dby is the basis y dimension

    :rtype Tx1 vector of stimuli convolved with the 2D basis
    """
    assert stim.ndim == basis.ndim == 3
    (T, Dx, Dy) = stim.shape
    (Tb, Dbx, Dby) = basis.shape

    # First, by convention, the impulse responses are apply to times
    # (t-R:t-1). That means we need to prepend a row of zeros to make
    # sure the basis remains causal
    basis = np.concatenate((np.zeros((1, Dbx, Dby)), basis), axis=0)

    # Flip the spatial dimension for convolution
    # We are convolving the stimulus with the filter, so the temporal part does
    # NOT need to be flipped
    basis = basis[:, ::-1, ::-1]

    # Compute convolution using FFT
    if Dx == Dbx and Dy == Dby and shape[1] == 'valid':
        raise Warning("Use low rank convolution when D==Db!")

    # Look for fft_stim in _fft_cache
    fft_stim = None
    for (cache_stim, cache_fft_stim) in _fft_cache:
        if np.allclose(stim[-128:],cache_stim[-128:]) and \
           np.allclose(stim[:128],cache_stim[:128]):
            fft_stim = cache_fft_stim
            break

    if not fft_stim is None:
        fstim, _ = fftconv.fftconvolve(stim, basis, 'full', fft_in1=fft_stim)
    else:
        fstim, fft_stim, _ = fftconv.fftconvolve(stim, basis, 'full')
        _fft_cache.append((stim, fft_stim))

    # Slice the result
    assert len(shape) == 3
    if shape[0] == 'first':
        fstim = fstim[:T, :, :]
    else:
        raise Exception(
            'Only supporting \'first\' slicing for dimension 0 (time)')

    if shape[1] == 'full':
        pass
    elif shape[1] == 'central':
        sz = Dx + Dbx - 1
        start = (sz - Dx) / 2
        stop = start + Dx
        fstim = fstim[:, start:stop, :]
    else:
        raise NotImplementedError(
            'Only supporting full and central slicing for spatial dims')

    if shape[2] == 'full':
        pass
    elif shape[2] == 'central':
        sz = Dy + Dby - 1
        start = (sz - Dy) / 2
        stop = start + Dy
        fstim = fstim[:, :, start:stop]
    else:
        raise NotImplementedError(
            'Only supporting full and central slicing for spatial dims')

    return fstim
Example #2
0
def convolve_with_3d_basis(stim, basis, shape=['first', 'central', 'central']):
    """ Project stimulus onto a basis.
    :param stim   T x Dx x Dy array of inputs.
                  T is the number of time bins
                  Dx is the stimulus x dimension.
                  Dy is the stimulus y dimension.
    :param basis  Tb x Dbx x Dby basis matrix
                  Tb is the length of the impulse response
                  Dbx is the basis x dimension
                  Dby is the basis y dimension

    :rtype Tx1 vector of stimuli convolved with the 2D basis
    """
    assert stim.ndim == basis.ndim == 3
    (T,Dx,Dy) = stim.shape
    (Tb,Dbx,Dby) = basis.shape

    # First, by convention, the impulse responses are apply to times
    # (t-R:t-1). That means we need to prepend a row of zeros to make
    # sure the basis remains causal
    basis = np.concatenate((np.zeros((1,Dbx,Dby)),basis), axis=0)

    # Flip the spatial dimension for convolution
    # We are convolving the stimulus with the filter, so the temporal part does
    # NOT need to be flipped
    basis = basis[:,::-1, ::-1]

    # Compute convolution using FFT
    if Dx==Dbx and Dy==Dby and shape[1] == 'valid':
        raise Warning("Use low rank convolution when D==Db!")

    # Look for fft_stim in _fft_cache
    fft_stim = None
    for (cache_stim, cache_fft_stim) in _fft_cache:
        if np.allclose(stim[-128:],cache_stim[-128:]) and \
           np.allclose(stim[:128],cache_stim[:128]):
            fft_stim = cache_fft_stim
            break

    if not fft_stim is None:
        fstim,_ = fftconv.fftconvolve(stim, basis, 'full',
                                      fft_in1=fft_stim)
    else:
        fstim,fft_stim,_ = fftconv.fftconvolve(stim, basis, 'full')
        _fft_cache.append((stim,fft_stim))

    # Slice the result
    assert len(shape) == 3
    if shape[0] == 'first':
        fstim = fstim[:T,:,:]
    else:
        raise Exception('Only supporting \'first\' slicing for dimension 0 (time)')

    if shape[1] == 'full':
        pass
    elif shape[1] == 'central':
        sz = Dx + Dbx - 1
        start = (sz - Dx)/2
        stop = start + Dx
        fstim = fstim[:,start:stop, :]
    else:
        raise NotImplementedError('Only supporting full and central slicing for spatial dims')

    if shape[2] == 'full':
        pass
    elif shape[2] == 'central':
        sz = Dy + Dby - 1
        start = (sz - Dy)/2
        stop = start + Dy
        fstim = fstim[:,:,start:stop]
    else:
        raise NotImplementedError('Only supporting full and central slicing for spatial dims')

    return fstim
Example #3
0
def convolve_with_2d_basis(stim, basis, shape=['first', 'valid']):
    """ Project stimulus onto a basis.
    :param stim   TxD matrix of inputs.
                  T is the number of time bins
                  D is the number of stimulus dimensions.
    :param basis  TbxDb basis matrix
                  Tb is the length of the impulse response
                  Db is the number of basis dimensions.

    :rtype Tx1 vector of stimuli convolved with the 2D basis
    """
    (T, D) = stim.shape
    (Tb, Db) = basis.shape
    # assert D==Db, "Spatial dimension of basis must match spatial dimension of stimulus."

    #    import scipy.signal as sig

    # First, by convention, the impulse responses are apply to times
    # (t-R:t-1). That means we need to prepend a row of zeros to make
    # sure the basis remains causal
    basis = np.vstack((np.zeros((1, Db)), basis))

    # Flip the spatial dimension for convolution
    # We are convolving the stimulus with the filter, so the temporal part does
    # NOT need to be flipped
    basis = basis[:, ::-1]

    # Compute convolution using FFT
    if D == Db and shape[1] == 'valid':
        raise Warning("Use low rank convolution when D==Db!")

    # Look for fft_stim in _fft_cache
    fft_stim = None
    for (cache_stim, cache_fft_stim) in _fft_cache:
        if np.allclose(stim[-128:],cache_stim[-128:]) and \
           np.allclose(stim[:128],cache_stim[:128]):
            fft_stim = cache_fft_stim
            break

    if not fft_stim is None:
        fstim, _ = fftconv.fftconvolve(stim, basis, 'full', fft_in1=fft_stim)
    else:
        fstim, fft_stim, _ = fftconv.fftconvolve(stim, basis, 'full')
        _fft_cache.append((stim, fft_stim))

    # Slice the result
    assert len(shape) == 2
    if shape[0] == 'first':
        fstim = fstim[:T, :]
    else:
        raise Exception(
            'Only supporting \'first\' slicing for dimension 0 (time)')

    if shape[1] == 'valid':
        assert Db == D, 'Dimension of basis must match that of stimuli for valid'
    elif shape[1] == 'central':
        sz = D + Db - 1
        start = (sz - D) / 2
        stop = start + D
        fstim = fstim[:, start:stop]

    return fstim
Example #4
0
def convolve_with_2d_basis(stim, basis, shape=['first', 'valid']):
    """ Project stimulus onto a basis.
    :param stim   TxD matrix of inputs.
                  T is the number of time bins
                  D is the number of stimulus dimensions.
    :param basis  TbxDb basis matrix
                  Tb is the length of the impulse response
                  Db is the number of basis dimensions.

    :rtype Tx1 vector of stimuli convolved with the 2D basis
    """
    (T,D) = stim.shape
    (Tb,Db) = basis.shape
    # assert D==Db, "Spatial dimension of basis must match spatial dimension of stimulus."

#    import scipy.signal as sig

    # First, by convention, the impulse responses are apply to times
    # (t-R:t-1). That means we need to prepend a row of zeros to make
    # sure the basis remains causal
    basis = np.vstack((np.zeros((1,Db)),basis))

    # Flip the spatial dimension for convolution
    # We are convolving the stimulus with the filter, so the temporal part does
    # NOT need to be flipped
    basis = basis[:,::-1]

    # Compute convolution using FFT
    if D==Db and shape[1] == 'valid':
        raise Warning("Use low rank convolution when D==Db!")

    # Look for fft_stim in _fft_cache
    fft_stim = None
    for (cache_stim, cache_fft_stim) in _fft_cache:
        if np.allclose(stim[-128:],cache_stim[-128:]) and \
           np.allclose(stim[:128],cache_stim[:128]):
            fft_stim = cache_fft_stim
            break

    if not fft_stim is None:
        fstim,_ = fftconv.fftconvolve(stim, basis, 'full',
                                      fft_in1=fft_stim)
    else:
        fstim,fft_stim,_ = fftconv.fftconvolve(stim, basis, 'full')
        _fft_cache.append((stim,fft_stim))

    # Slice the result
    assert len(shape) == 2
    if shape[0] == 'first':
        fstim = fstim[:T,:]
    else:
        raise Exception('Only supporting \'first\' slicing for dimension 0 (time)')

    if shape[1] == 'valid':
        assert Db == D, 'Dimension of basis must match that of stimuli for valid'
    elif shape[1] == 'central':
        sz = D + Db - 1
        start = (sz - D)/2
        stop = start + D
        fstim = fstim[:,start:stop]

    return fstim