Skip to content

Commit

Permalink
-Monitors can now be used as a sequencer/stimulator (see github issue #2
Browse files Browse the repository at this point in the history
)

-exportAER now accepts object of type Monitor as the first argument
  • Loading branch information
isnl committed Oct 6, 2014
1 parent d07d4c3 commit 59e3331
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 13 deletions.
58 changes: 47 additions & 11 deletions src/pyNCS/monitors.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from __future__ import absolute_import
import numpy as np
import pylab
from .pyST.STsl import mapSpikeListAddresses, composite_plot, SpikeList, SpikeTrain
from collections import defaultdict
from .pyST.STsl import mapSpikeListAddresses, composite_plot, SpikeList, SpikeTrain, merge_spikelists
import copy

def create_SpikeMonitor_from_SpikeList(st):
Expand Down Expand Up @@ -43,6 +44,25 @@ def __iter__(self):
def __len__(self):
return len(self.monitors)

def __getitem__(self, item_id):
return self.monitors[item_id]

def __setitem__(self, item_id, item):
self.monitors[item_id] = item

def append(self, spikemonitor):
if not isinstance(spikemonitor, SpikeMonitor):
raise TypeError('Can only append a object of type SpikeMonitor')

def to_chstlist(self):
'''
Returns a dictionary with channels as keys and merged SpikeLists as values.
'''
chstlist = defaultdict(SpikeList)
for spk_mon in self:
chstlist[spk_mon.channel] = merge_spikelists(spk_mon.sl,chstlist[spk_mon.channel])
return chstlist

@property
def channels(self):
'''
Expand All @@ -67,7 +87,7 @@ def normalize_tstart_tstop(self, t_stop=None):
if not m.sl.t_stop > t_stop:
m.sl.t_stop = t_stop

def populate_monitors(self, chstlist):
def populate(self, chstlist):
"""
Populates SpikeMonitors in the list of monitors
chstlist is the dictionary returned by NeuroSetup.stimulate.
Expand All @@ -77,6 +97,12 @@ def populate_monitors(self, chstlist):
for mon, st in self.iterchst(chstlist):
mon.populate(st)

def populate_monitors(self, chstlist):
'''
backward compatibility
'''
return self.populate(chstlist)

def iterchst(self, chstlist):
"""
iterate over channels in monitors and try to find spikelists with matching channels.
Expand Down Expand Up @@ -189,7 +215,7 @@ def get_t_stop(self):
return t_stop


class MonitorPlotBase(object):
class PlotBase(object):
"""
Base Class for plotting SpikeMonitors.
Virtual class, use RasterPlot or MeanRatePlot instead.
Expand Down Expand Up @@ -285,7 +311,7 @@ def __call__(self, *args, **kwargs):
return mticker.MaxNLocator.__call__(self, *args, **kwargs)


class RasterPlot(MonitorPlotBase):
class RasterPlot(PlotBase):
"""
A Raster Plot Class for plotting several Spike Monitors at once.
The figure is automatically plotted, unless it is constructed with plot=False.
Expand All @@ -297,7 +323,7 @@ class RasterPlot(MonitorPlotBase):
"""

def __init__(self, monitors, flat=False, even_distance = False, plot_kwargs={}, *args, **kwargs):
MonitorPlotBase.__init__(self, monitors)
PlotBase.__init__(self, monitors)
self.flat = flat
self.even = even_distance
self.cs = [] #centers of raster plots
Expand Down Expand Up @@ -352,7 +378,7 @@ def draw(self, plot_kwargs={}, *args, **kwargs):
id=i + 0.5, kwargs=plot_kwargs_mon, *args, **kwargs)


class MeanRatePlot(MonitorPlotBase):
class MeanRatePlot(PlotBase):
"""
A Mean Rate Plot with plots the mean rates of the provided SpikeMonitors.
The figure is automatically plotted
Expand All @@ -364,7 +390,7 @@ def __init__(self, monitors, time_bin=30, mean=True, *args, **kwargs):
*time_bin*: time bin which will be used to compute the firing rates
*args* and *kwargs* will be passed to pylab.plot
"""
MonitorPlotBase.__init__(self, monitors)
PlotBase.__init__(self, monitors)

self.time_bin = int(time_bin)
#If this is set to float, then y scale gets screwed. No idea why!
Expand Down Expand Up @@ -411,15 +437,15 @@ def draw(self, *args, **plot_args):
class SpikeMonitor(object):
"""
A class for monitoring spiking activity during experimentation.
API is the one of AddrGroup.
Interface is similar to the one of AddrGroup.
>>> pop_mon = SpikeMonitor(pop.soma, plot_args={'color':'r', 'linewidth':3})
>>> nsetup.monitors.import_monitors([pop_mon])
>>> nsetup.stimulate(stStim)
>>> nsetup.monitors.raster_plot()
"""
def __init__(self, addr_group=[], plot_args = None):
def __init__(self, addr_group=None, plot_args = None):
# By definition of populations, SpikeMonitor is associated to at most
# one channel
self.addr_group = addr_group
Expand All @@ -433,6 +459,15 @@ def __init__(self, addr_group=[], plot_args = None):
self.name = self.addr_group.name
self.channel = self.addr_group.channel

def create_spiketrains(self, name, *args, **kwargs):
'''
Create spike trains from addr_group.
Inputs:
*name*: specifies spiketrains type. Function calls 'addr_group.spiketrains_'+name
**args* and ***kwargs* passed to spiketrains_+name function
'''
self._sl = self.to_monitorSpikeList(getattr(self.addr_group,'spiketrains_'+name)(*args,**kwargs)[self.channel])

@property
def sl(self):
"""
Expand All @@ -448,6 +483,7 @@ def __len__(self):
return self.addr_group.__len__()

def __getslice__(self, i, j):
#TODO: consider returning slices monitor instead
return self.addr_group.__getslice__(i, j)

def copy(self):
Expand All @@ -474,7 +510,7 @@ def populate(self, st):
def _do_populate(self):
assert hasattr(self, '_data'), "SpikeMonitor must be populated first"
self._populated = True
self._sl = self.toSpikeListMonitor(self._data)
self._sl = self.to_monitorSpikeList(self._data)
self._sl.complete(self.addr_group.laddr)
del self._data

Expand Down Expand Up @@ -527,7 +563,7 @@ def get_plotargs(self, kwargs={}):
plot_kwargs_mon.setdefault(k, v)
return plot_kwargs_mon

def toSpikeListMonitor(self, st):
def to_monitorSpikeList(self, st):
"""
Transform SpikeList *st* into a monitorSpikeList object
"""
Expand Down
12 changes: 10 additions & 2 deletions src/pyNCS/pyST/STas.py
Original file line number Diff line number Diff line change
Expand Up @@ -1012,15 +1012,23 @@ def rawoutput_from_chevents(self, ch_events, func=None, normalize=True, filter_d

def exportAER(self, spikeLists, filename=None, format='a', isi=True, sep='\t', addr_format='%u', time_format='%u', *args, **kwargs):
'''
spikeLists can be a spikeList, a list of spikelists of dimension nChannels, a dictionary i.e.{channel: spikeList}. If a SpikeList is given, it will be equivalent to {0: spikeLists}.
spikeLists can be of the follwing type:
- Monitors
- SpikeList
- list of SpikeLists of dimension nChannels
- dictionary with channels as keys and SpikeLists as values
- SpikeList is given, it will be interpreted as {0: spikeLists}.
format specifies whether timestamps (format='t') or addresses (format='a') should be on the first column.
*addr_format* and *time_format* format to be used by np.savetxt
'''

out = []
assert format in ['t', 'a'], 'Format must be "a" or "t"'

if isinstance(spikeLists, list):
if hasattr(spikeLists, 'to_chstlist'):
#Assuming it is of type Monitors
spikeLists = spikeLists.to_chstlist()
elif isinstance(spikeLists, list):
assert len(spikeLists) == self.nChannels, "spikeLists must have dimension %d" % self.nChannels
for i in range(len(spikeLists)):
if not isinstance(spikeLists[i], SpikeList):
Expand Down

0 comments on commit 59e3331

Please sign in to comment.