Exemplo n.º 1
0
    def test_triangular(self):
        def evaluate_old(t):
            t_units = t.units
            t_abs = np.abs(t.magnitude)
            tau = math.sqrt(6) * kernel.sigma.rescale(t_units).magnitude
            kernel_pdf = (t_abs < tau) * 1 / tau * (1 - t_abs / tau)
            kernel_pdf = pq.Quantity(kernel_pdf, units=1 / t_units)
            return kernel_pdf

        for invert in (False, True):
            kernel = kernels.TriangularKernel(self.sigma, invert=invert)
            assert_array_almost_equal(kernel(self.time_input),
                                      evaluate_old(self.time_input))
Exemplo n.º 2
0
    def setUp(self):
        # create a poisson spike train:
        self.st_tr = (0, 20.0)  # seconds
        self.st_dur = self.st_tr[1] - self.st_tr[0]  # seconds
        self.st_margin = 5.0  # seconds
        self.st_rate = 10.0  # Hertz

        st_num_spikes = np.random.poisson(self.st_rate*(self.st_dur-2*self.st_margin))
        spike_train = np.random.rand(st_num_spikes) * (self.st_dur-2*self.st_margin) + self.st_margin
        spike_train.sort()

        # convert spike train into neo objects
        self.spike_train = neo.SpikeTrain(spike_train*pq.s,
                                          t_start=self.st_tr[0]*pq.s,
                                          t_stop=self.st_tr[1]*pq.s)

        # generation of a multiply used specific kernel
        self.kernel = kernels.TriangularKernel(sigma = 0.03*pq.s)
Exemplo n.º 3
0
    def test_wrong_input(self):
        self.assertRaises(TypeError, stds.victor_purpura_distance,
                          [self.array1, self.array2], self.q3)
        self.assertRaises(TypeError, stds.victor_purpura_distance,
                          [self.qarray1, self.qarray2], self.q3)
        self.assertRaises(TypeError, stds.victor_purpura_distance,
                          [self.qarray1, self.qarray2], 5.0 * ms)

        self.assertRaises(TypeError,
                          stds.victor_purpura_distance,
                          [self.array1, self.array2],
                          self.q3,
                          algorithm='intuitive')
        self.assertRaises(TypeError,
                          stds.victor_purpura_distance,
                          [self.qarray1, self.qarray2],
                          self.q3,
                          algorithm='intuitive')
        self.assertRaises(TypeError,
                          stds.victor_purpura_distance,
                          [self.qarray1, self.qarray2],
                          5.0 * ms,
                          algorithm='intuitive')

        self.assertRaises(TypeError, stds.van_rossum_distance,
                          [self.array1, self.array2], self.tau3)
        self.assertRaises(TypeError, stds.van_rossum_distance,
                          [self.qarray1, self.qarray2], self.tau3)
        self.assertRaises(TypeError, stds.van_rossum_distance,
                          [self.qarray1, self.qarray2], 5.0 * Hz)

        self.assertRaises(TypeError, stds.victor_purpura_distance,
                          [self.st11, self.st13], self.tau2)
        self.assertRaises(TypeError, stds.victor_purpura_distance,
                          [self.st11, self.st13], 5.0)
        self.assertRaises(TypeError,
                          stds.victor_purpura_distance, [self.st11, self.st13],
                          self.tau2,
                          algorithm='intuitive')
        self.assertRaises(TypeError,
                          stds.victor_purpura_distance, [self.st11, self.st13],
                          5.0,
                          algorithm='intuitive')
        self.assertRaises(TypeError, stds.van_rossum_distance,
                          [self.st11, self.st13], self.q4)
        self.assertRaises(TypeError, stds.van_rossum_distance,
                          [self.st11, self.st13], 5.0)

        self.assertRaises(NotImplementedError,
                          stds.victor_purpura_distance, [self.st01, self.st02],
                          self.q3,
                          kernel=kernels.Kernel(2.0 / self.q3))
        self.assertRaises(NotImplementedError,
                          stds.victor_purpura_distance, [self.st01, self.st02],
                          self.q3,
                          kernel=kernels.SymmetricKernel(2.0 / self.q3))
        self.assertEqual(
            stds.victor_purpura_distance(
                [self.st01, self.st02],
                self.q1,
                kernel=kernels.TriangularKernel(
                    2.0 / (np.sqrt(6.0) * self.q2)))[0, 1],
            stds.victor_purpura_distance(
                [self.st01, self.st02],
                self.q3,
                kernel=kernels.TriangularKernel(
                    2.0 / (np.sqrt(6.0) * self.q2)))[0, 1])
        self.assertEqual(
            stds.victor_purpura_distance(
                [self.st01, self.st02],
                kernel=kernels.TriangularKernel(
                    2.0 / (np.sqrt(6.0) * self.q2)))[0, 1], 1.0)
        self.assertNotEqual(
            stds.victor_purpura_distance(
                [self.st01, self.st02],
                kernel=kernels.AlphaKernel(2.0 / (np.sqrt(6.0) * self.q2)))[0,
                                                                            1],
            1.0)

        self.assertRaises(NameError,
                          stds.victor_purpura_distance, [self.st11, self.st13],
                          self.q2,
                          algorithm='slow')
Exemplo n.º 4
0
def victor_purpura_distance(spiketrains,
                            cost_factor=1.0 * pq.Hz,
                            kernel=None,
                            sort=True,
                            algorithm='fast'):
    """
    Calculates the Victor-Purpura's (VP) distance. It is often denoted as
    :math:`D^{\\text{spike}}[q]`.

    It is defined as the minimal cost of transforming spike train `a` into
    spike train `b` by using the following operations:

        * Inserting or deleting a spike (cost 1.0).
        * Shifting a spike from :math:`t` to :math:`t'` (cost :math:`q
          \\cdot |t - t'|`).

    A detailed description can be found in
    *Victor, J. D., & Purpura, K. P. (1996). Nature and precision of
    temporal coding in visual cortex: a metric-space analysis. Journal of
    Neurophysiology.*

    Given the average number of spikes :math:`n` in a spike train and
    :math:`N` spike trains the run-time complexity of this function is
    :math:`O(N^2 n^2)` and :math:`O(N^2 + n^2)` memory will be needed.

    Parameters
    ----------
    spiketrains : list of neo.SpikeTrain
        Spike trains to calculate pairwise distance.
    cost_factor : pq.Quantity, optional
        A cost factor :math:`q` for spike shifts as inverse time scalar.
        Extreme values :math:`q=0` meaning no cost for any shift of
        spikes, or :math: `q=np.inf` meaning infinite cost for any
        spike shift and hence exclusion of spike shifts, are explicitly
        allowed. If `kernel` is not `None`, :math:`q` will be ignored.
        Default: 1.0 * pq.Hz
    kernel : elephant.kernels.Kernel or None, optional
        Kernel to use in the calculation of the distance. If `kernel` is
        `None`, an unnormalized triangular kernel with standard deviation
        of :math:'2.0/(q * sqrt(6.0))' corresponding to a half width of
        :math:`2.0/q` will be used. Usage of the default value calculates
        the Victor-Purpura distance correctly with a triangular kernel of
        the suitable width. The choice of another kernel is enabled, but
        this leaves the framework of Victor-Purpura distances.
        Default: None
    sort : bool, optional
        Spike trains with sorted spike times will be needed for the
        calculation. You can set `sort` to `False` if you know that your
        spike trains are already sorted to decrease calculation time.
        Default: True
    algorithm : str, optional
        Allowed values are 'fast' or 'intuitive', each selecting an
        algorithm with which to calculate the pairwise Victor-Purpura distance.
        Typically 'fast' should be used, because while giving always the
        same result as 'intuitive', within the temporary structure of
        Python and add-on modules as numpy it is faster.
        Default: 'fast'

    Returns
    -------
    np.ndarray
        2-D Matrix containing the VP distance of all pairs of spike trains.

    Examples
    --------
    >>> import quantities as pq
    >>> from elephant.spike_train_dissimilarity import victor_purpura_distance
    >>> q = 1.0 / (10.0 * pq.ms)
    >>> st_a = SpikeTrain([10, 20, 30], units='ms', t_stop= 1000.0)
    >>> st_b = SpikeTrain([12, 24, 30], units='ms', t_stop= 1000.0)
    >>> vp_f = victor_purpura_distance([st_a, st_b], q)[0, 1]
    >>> vp_i = victor_purpura_distance([st_a, st_b], q,
    ...        algorithm='intuitive')[0, 1]
    """
    for train in spiketrains:
        if not (isinstance(train, (pq.quantity.Quantity, SpikeTrain))
                and train.dimensionality.simplified == pq.Quantity(
                    1, "s").dimensionality.simplified):
            raise TypeError("Spike trains must have a time unit.")

    if not (isinstance(cost_factor, pq.quantity.Quantity)
            and cost_factor.dimensionality.simplified == pq.Quantity(
                1, "Hz").dimensionality.simplified):
        raise TypeError("cost_factor must be a rate quantity.")

    if kernel is None:
        if cost_factor == 0.0:
            num_spikes = np.atleast_2d([st.size for st in spiketrains])
            return np.absolute(num_spikes.T - num_spikes)
        if cost_factor == np.inf:
            num_spikes = np.atleast_2d([st.size for st in spiketrains])
            return num_spikes.T + num_spikes
        kernel = kernels.TriangularKernel(sigma=2.0 /
                                          (np.sqrt(6.0) * cost_factor))

    if sort:
        spiketrains = [
            np.sort(st.view(type=pq.Quantity)) for st in spiketrains
        ]

    def compute(i, j):
        if i == j:
            return 0.0
        if algorithm == 'fast':
            return _victor_purpura_dist_for_st_pair_fast(
                spiketrains[i], spiketrains[j], kernel)
        if algorithm == 'intuitive':
            return _victor_purpura_dist_for_st_pair_intuitive(
                spiketrains[i], spiketrains[j], cost_factor)
        raise NameError("The algorithm must be either 'fast' or 'intuitive'.")

    return _create_matrix_from_indexed_function(
        (len(spiketrains), len(spiketrains)), compute, kernel.is_symmetric())