Skip to content

Commit

Permalink
Merge pull request #21 from mariomulansky/unified_interface
Browse files Browse the repository at this point in the history
Unified interface
  • Loading branch information
mariomulansky committed Mar 9, 2016
2 parents f7b9061 + ee0e980 commit d2c74dd
Show file tree
Hide file tree
Showing 24 changed files with 785 additions and 271 deletions.
7 changes: 7 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@ python:
env:
- CYTHON_INSTALL="pip install -q cython"
- CYTHON_INSTALL=""
before_install:
- sudo apt-get update
- sudo apt-get install libblas-dev
- sudo apt-get install liblapack-dev
- sudo apt-get install gfortran
install:
- pip install scipy
- $CYTHON_INSTALL

script:
- python setup.py build_ext --inplace
- nosetests
- nosetests test/numeric
16 changes: 9 additions & 7 deletions Readme.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ PySpike
:target: https://travis-ci.org/mariomulansky/PySpike

PySpike is a Python library for the numerical analysis of spike train similarity.
Its core functionality is the implementation of the bivariate ISI_ and SPIKE_ distance [#]_ [#]_ as well as SPIKE-Synchronization_ [#]_.
Additionally, it provides functions to compute multivariate profiles, distance matrices, as well as averaging and general spike train processing.
Its core functionality is the implementation of the ISI_ and SPIKE_ distance [#]_ [#]_ as well as SPIKE-Synchronization_ [#]_.
It provides functions to compute multivariate profiles, distance matrices, as well as averaging and general spike train processing.
All computation intensive parts are implemented in C via cython_ to reach a competitive performance (factor 100-200 over plain Python).

PySpike provides the same fundamental functionality as the SPIKY_ framework for Matlab, which additionally contains spike-train generators, more spike train distance measures and many visualization routines.
Expand All @@ -24,6 +24,8 @@ All source codes are available on `Github <https://github.com/mariomulansky/PySp
Important Changelog
-----------------------------

With version 0.5.0, the interfaces have been unified and the specific functions for multivariate computations have become deprecated.

With version 0.2.0, the :code:`SpikeTrain` class has been introduced to represent spike trains.
This is a breaking change in the function interfaces.
Hence, programs written for older versions of PySpike (0.1.x) will not run with newer versions.
Expand Down Expand Up @@ -76,7 +78,7 @@ Therefore, add your :code:`/path/to/PySpike` to the :code:`$PYTHONPATH` environm
Examples
-----------------------------

The following code loads some exemplary spike trains, computes the dissimilarity profile of the ISI-distance of the first two :code:`SpikeTrain` s, and plots it with matplotlib:
The following code loads some exemplary spike trains, computes the dissimilarity profile of the ISI-distance of the first two :code:`SpikeTrain` objects, and plots it with matplotlib:

.. code:: python
Expand All @@ -92,15 +94,15 @@ The following code loads some exemplary spike trains, computes the dissimilarity
plt.show()
The following example computes the multivariate ISI-, SPIKE- and SPIKE-Sync-profile for a list of spike trains using the :code:`isi_profile_multi`, :code:`spike_profile_multi`, :code:`spike_sync_profile_multi` functions:
The following example computes the multivariate ISI-, SPIKE- and SPIKE-Sync-profile for a list of spike trains loaded from a text file:

.. code:: python
spike_trains = spk.load_spike_trains_from_txt("PySpike_testdata.txt",
edges=(0, 4000))
avrg_isi_profile = spk.isi_profile_multi(spike_trains)
avrg_spike_profile = spk.spike_profile_multi(spike_trains)
avrg_spike_sync_profile = spk.spike_sync_profile_multi(spike_trains)
avrg_isi_profile = spk.isi_profile(spike_trains)
avrg_spike_profile = spk.spike_profile(spike_trains)
avrg_spike_sync_profile = spk.spike_sync_profile(spike_trains)
More examples with detailed descriptions can be found in the `tutorial section <http://mariomulansky.github.io/PySpike/#tutorial>`_.

Expand Down
54 changes: 37 additions & 17 deletions doc/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,9 @@ If you are only interested in the scalar ISI-distance and not the profile, you c

.. code:: python
isi_dist = spk.isi_distance(spike_trains[0], spike_trains[1], interval)
where :code:`interval` is optional, as above, and if omitted the ISI-distance is computed for the complete spike trains.
isi_dist = spk.isi_distance(spike_trains[0], spike_trains[1], interval=(0, 1000))
where :code:`interval` is optional, as above, and if omitted the ISI-distance is computed for the complete spike train.

SPIKE-distance
..............
Expand All @@ -113,19 +112,20 @@ But the general approach is very similar:
plt.show()
This short example computes and plots the SPIKE-profile of the first two spike trains in the file :code:`PySpike_testdata.txt`.

In contrast to the ISI-profile, a SPIKE-profile is a piece-wise *linear* function and is therefore represented by a :class:`.PieceWiseLinFunc` object.
Just like the :class:`.PieceWiseConstFunc` for the ISI-profile, the :class:`.PieceWiseLinFunc` provides a :meth:`.PieceWiseLinFunc.get_plottable_data` member function that returns arrays that can be used directly to plot the function.
Furthermore, the :meth:`.PieceWiseLinFunc.avrg` member function returns the average of the profile defined as the overall SPIKE distance.
As above, you can provide an interval as a pair of floats as well as a sequence of such pairs to :code:`avrg` to specify the averaging interval if required.

Again, you can use
Again, you can use:

.. code:: python
spike_dist = spk.spike_distance(spike_trains[0], spike_trains[1], interval)
spike_dist = spk.spike_distance(spike_trains[0], spike_trains[1], interval=ival)
to compute the SPIKE distance directly, if you are not interested in the profile at all.
The parameter :code:`interval` is optional and if neglected the whole spike train is used.
The parameter :code:`interval` is optional and if neglected the whole time interval is used.


SPIKE synchronization
Expand Down Expand Up @@ -164,26 +164,47 @@ For the direct computation of the overall spike synchronization value within som

.. code:: python
spike_sync = spk.spike_sync(spike_trains[0], spike_trains[1], interval)
spike_sync = spk.spike_sync(spike_trains[0], spike_trains[1], interval=ival)
Computing multivariate profiles and distances
----------------------------------------------

To compute the multivariate ISI-profile, SPIKE-profile or SPIKE-Synchronization profile f a set of spike trains, PySpike provides multi-variate version of the profile function.
The following example computes the multivariate ISI-, SPIKE- and SPIKE-Sync-profile for a list of spike trains using the :func:`.isi_profile_multi`, :func:`.spike_profile_multi`, :func:`.spike_sync_profile_multi` functions:
To compute the multivariate ISI-profile, SPIKE-profile or SPIKE-Synchronization profile for a set of spike trains, simply provide a list of spike trains to the profile or distance functions.
The following example computes the multivariate ISI-, SPIKE- and SPIKE-Sync-profile for a list of spike trains:

.. code:: python
spike_trains = spk.load_spike_trains_from_txt("PySpike_testdata.txt",
edges=(0, 4000))
avrg_isi_profile = spk.isi_profile_multi(spike_trains)
avrg_spike_profile = spk.spike_profile_multi(spike_trains)
avrg_spike_sync_profile = spk.spike_sync_profile_multi(spike_trains)
avrg_isi_profile = spk.isi_profile(spike_trains)
avrg_spike_profile = spk.spike_profile(spike_trains)
avrg_spike_sync_profile = spk.spike_sync_profile(spike_trains)
All functions also take an optional parameter :code:`indices`, a list of indices that allows to define the spike trains that should be used for the multivariate profile.
As before, if you are only interested in the distance values, and not in the profile, you can call the functions: :func:`.isi_distance`, :func:`.spike_distance` and :func:`.spike_sync` with a list of spike trains.
They return the scalar overall multivariate ISI-, SPIKE-distance or the SPIKE-Synchronization value.

The following code is equivalent to the bivariate example above, computing the ISI-Distance between the first two spike trains in the given interval using the :code:`indices` parameter:

.. code:: python
isi_dist = spk.isi_distance(spike_trains, indices=[0, 1], interval=(0, 1000))
As you can see, the distance functions also accept an :code:`interval` parameter that can be used to specify the begin and end of the averaging interval as a pair of floats, if neglected the complete interval is used.

**Note:**

------------------------------

Instead of providing lists of spike trains to the profile or distance functions, you can also call those functions with many spike trains as (unnamed) parameters, e.g.:

.. code:: python
# st1, st2, st3, st4 are spike trains
spike_prof = spk.spike_profile(st1, st2, st3, st4)
------------------------------

All functions take an optional parameter :code:`indices`, a list of indices that allows to define the spike trains that should be used for the multivariate profile.
As before, if you are only interested in the distance values, and not in the profile, PySpike offers the functions: :func:`.isi_distance_multi`, :func:`.spike_distance_multi` and :func:`.spike_sync_multi`, that return the scalar overall multivariate ISI- and SPIKE-distance as well as the SPIKE-Synchronization value.
Those functions also accept an :code:`interval` parameter that can be used to specify the begin and end of the averaging interval as a pair of floats, if neglected the complete interval is used.

Another option to characterize large sets of spike trains are distance matrices.
Each entry in the distance matrix represents a bivariate distance (similarity for SPIKE-Synchronization) of two spike trains.
Expand All @@ -210,4 +231,3 @@ The following example computes and plots the ISI- and SPIKE-distance matrix as w
plt.title("SPIKE-Sync")
plt.show()
2 changes: 1 addition & 1 deletion examples/averages.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import pyspike as spk

spike_trains = spk.load_spike_trains_from_txt("PySpike_testdata.txt",
time_interval=(0, 4000))
edges=(0, 4000))

f = spk.isi_profile(spike_trains[0], spike_trains[1])

Expand Down
6 changes: 3 additions & 3 deletions examples/merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@

print(merged_spike_train.spikes)

plt.plot(spike_trains[0].spikes, np.ones_like(spike_trains[0].spikes), 'o')
plt.plot(spike_trains[1].spikes, np.ones_like(spike_trains[1].spikes), 'x')
plt.plot(spike_trains[0], np.ones_like(spike_trains[0]), 'o')
plt.plot(spike_trains[1], np.ones_like(spike_trains[1]), 'x')
plt.plot(merged_spike_train.spikes,
2*np.ones_like(merged_spike_train.spikes), 'o')
2*np.ones_like(merged_spike_train), 'o')

plt.show()
4 changes: 2 additions & 2 deletions examples/multivariate.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def time_diff_in_ms(start, end):
print("Number of spikes: %d" % num_of_spikes)

# calculate the multivariate spike distance
f = spk.spike_profile_multi(spike_trains)
f = spk.spike_profile(spike_trains)

t_spike = time.clock()

Expand All @@ -39,7 +39,7 @@ def time_diff_in_ms(start, end):
t_avrg = time.clock()

# compute average distance directly, should give the same result as above
spike_dist = spk.spike_distance_multi(spike_trains)
spike_dist = spk.spike_distance(spike_trains)
print("Spike distance directly: %.8f" % spike_dist)

t_dist = time.clock()
Expand Down
27 changes: 15 additions & 12 deletions examples/performance.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,38 +31,41 @@
t_end = datetime.now()
runtime = (t_end-t_start).total_seconds()

sort_by = 'tottime'
# sort_by = 'cumtime'

print("Spike generation runtime: %.3fs" % runtime)
print()

print("================ ISI COMPUTATIONS ================")
print(" MULTIVARIATE DISTANCE")
cProfile.run('spk.isi_distance_multi(spike_trains)', 'performance.stat')
cProfile.run('spk.isi_distance(spike_trains)', 'performance.stat')
p = pstats.Stats('performance.stat')
p.strip_dirs().sort_stats('tottime').print_stats(5)
p.strip_dirs().sort_stats(sort_by).print_stats(5)

print(" MULTIVARIATE PROFILE")
cProfile.run('spk.isi_profile_multi(spike_trains)', 'performance.stat')
cProfile.run('spk.isi_profile(spike_trains)', 'performance.stat')
p = pstats.Stats('performance.stat')
p.strip_dirs().sort_stats('tottime').print_stats(5)
p.strip_dirs().sort_stats(sort_by).print_stats(5)

print("================ SPIKE COMPUTATIONS ================")
print(" MULTIVARIATE DISTANCE")
cProfile.run('spk.spike_distance_multi(spike_trains)', 'performance.stat')
cProfile.run('spk.spike_distance(spike_trains)', 'performance.stat')
p = pstats.Stats('performance.stat')
p.strip_dirs().sort_stats('tottime').print_stats(5)
p.strip_dirs().sort_stats(sort_by).print_stats(5)

print(" MULTIVARIATE PROFILE")
cProfile.run('spk.spike_profile_multi(spike_trains)', 'performance.stat')
cProfile.run('spk.spike_profile(spike_trains)', 'performance.stat')
p = pstats.Stats('performance.stat')
p.strip_dirs().sort_stats('tottime').print_stats(5)
p.strip_dirs().sort_stats(sort_by).print_stats(5)

print("================ SPIKE-SYNC COMPUTATIONS ================")
print(" MULTIVARIATE DISTANCE")
cProfile.run('spk.spike_sync_multi(spike_trains)', 'performance.stat')
cProfile.run('spk.spike_sync(spike_trains)', 'performance.stat')
p = pstats.Stats('performance.stat')
p.strip_dirs().sort_stats('tottime').print_stats(5)
p.strip_dirs().sort_stats(sort_by).print_stats(5)

print(" MULTIVARIATE PROFILE")
cProfile.run('spk.spike_sync_profile_multi(spike_trains)', 'performance.stat')
cProfile.run('spk.spike_sync_profile(spike_trains)', 'performance.stat')
p = pstats.Stats('performance.stat')
p.strip_dirs().sort_stats('tottime').print_stats(5)
p.strip_dirs().sort_stats(sort_by).print_stats(5)
5 changes: 3 additions & 2 deletions examples/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@
for (i, spike_train) in enumerate(spike_trains):
plt.scatter(spike_train, i*np.ones_like(spike_train), marker='|')

f = spk.isi_profile(spike_trains[0], spike_trains[1])
# profile of the first two spike trains
f = spk.isi_profile(spike_trains, indices=[0, 1])
x, y = f.get_plottable_data()

plt.figure()
plt.plot(x, np.abs(y), '--k', label="ISI-profile")

print("ISI-distance: %.8f" % f.avrg())

f = spk.spike_profile(spike_trains[0], spike_trains[1])
f = spk.spike_profile(spike_trains, indices=[0, 1])
x, y = f.get_plottable_data()

plt.plot(x, y, '-b', label="SPIKE-profile")
Expand Down
4 changes: 2 additions & 2 deletions examples/profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
print()

# compute the multivariate ISI profile
f = spk.isi_profile_multi(spike_trains)
f = spk.isi_profile(spike_trains)

t = 1200
print("Multivariate ISI value at t =", t, ":", f(t))
Expand All @@ -56,7 +56,7 @@
print()

# compute the multivariate SPIKE profile
f = spk.spike_profile_multi(spike_trains)
f = spk.spike_profile(spike_trains)

# SPIKE values at certain points
t = 1200
Expand Down
2 changes: 1 addition & 1 deletion examples/spike_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

plt.subplot(211)

f = spk.spike_sync_profile_multi(spike_trains)
f = spk.spike_sync_profile(spike_trains)
x, y = f.get_plottable_data()
plt.plot(x, y, '-b', alpha=0.7, label="SPIKE-Sync profile")

Expand Down
2 changes: 1 addition & 1 deletion pyspike/SpikeTrain.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def get_spikes_non_empty(self):
"""Returns the spikes of this spike train with auxiliary spikes in case
of empty spike trains.
"""
if len(self.spikes) < 2:
if len(self.spikes) < 1:
return np.unique(np.insert([self.t_start, self.t_end], 1,
self.spikes))
else:
Expand Down
Loading

0 comments on commit d2c74dd

Please sign in to comment.