Changeset 1006

Show
Ignore:
Timestamp:
11/10/11 13:17:57 (6 months ago)
Author:
apdavison
Message:

New recording system based on Neo now integrated into pyNN.neuron for a single Segment (without reset() support)

Location:
branches/neo_output
Files:
11 modified

Legend:

Unmodified
Added
Removed
  • branches/neo_output/examples/VAbenchmarks.py

    r915 r1006  
    2727exec("from pyNN.%s import *" % simulator_name) 
    2828from pyNN.random import NumpyRNG, RandomDistribution 
     29from neo.io import PyNNTextIO 
    2930 
    3031timer = Timer() 
     
    4344 
    4445dt       = 0.1   # (ms) simulation timestep 
    45 tstop    = 1000  # (ms) simulaton duration 
     46tstop    = 200 #1000  # (ms) simulaton duration 
    4647delay    = 0.2 
    4748 
     
    148149# === Setup recording ========================================================== 
    149150print "%s Setting up recording..." % node_id 
    150 exc_cells.record() 
    151 inh_cells.record() 
    152 exc_cells[[0, 1]].record_v() 
     151exc_cells.record('spikes') 
     152inh_cells.record('spikes') 
     153exc_cells[[0, 1]].record('v') 
    153154 
    154155buildCPUTime = timer.diff() 
     
    177178    os.mkdir('Results') 
    178179 
    179 exc_cells.printSpikes("Results/VAbenchmark_%s_exc_%s_np%d.ras" % (benchmark, simulator_name, np)) 
    180 inh_cells.printSpikes("Results/VAbenchmark_%s_inh_%s_np%d.ras" % (benchmark, simulator_name, np)) 
    181 exc_cells[[0, 1]].print_v("Results/VAbenchmark_%s_exc_%s_np%d.v" % (benchmark, simulator_name, np)) 
     180exc_cells.write_data(PyNNTextIO("Results/VAbenchmark_%s_exc_%s_np%d.ras" % (benchmark, simulator_name, np)), 'spikes') 
     181inh_cells.write_data(PyNNTextIO("Results/VAbenchmark_%s_inh_%s_np%d.ras" % (benchmark, simulator_name, np)), 'spikes') 
     182exc_cells[[0, 1]].write_data(PyNNTextIO("Results/VAbenchmark_%s_exc_%s_np%d.v" % (benchmark, simulator_name, np)), 'v') 
    182183writeCPUTime = timer.diff() 
    183184 
  • branches/neo_output/src/brian/recording.py

    r1000 r1006  
    120120            N[id + padding] = len(self._devices[0].spiketimes[id]) 
    121121        return N 
    122          
    123  
    124 simulator.Recorder = Recorder 
  • branches/neo_output/src/common/populations.py

    r1001 r1006  
    386386        raise NotImplementedError() 
    387387 
    388     #@deprecated("initialize('v', rand_distr)") 
     388    @deprecated("initialize('v', rand_distr)") 
    389389    def randomInit(self, rand_distr): 
    390390        """ 
     
    392392        random values. 
    393393        """ 
    394         warn("The randomInit() method is deprecated, and will be removed in a future release. Use initialize('v', rand_distr) instead.") 
    395394        self.initialize('v', rand_distr) 
    396395 
     
    426425        return (variable in self.celltype.recordable) 
    427426 
    428     def _add_recorder(self, variable, to_file): 
    429         """Create a new Recorder for the supplied variable.""" 
    430         assert variable not in self.recorders 
    431         if hasattr(self, "parent"): 
    432             population = self.grandparent 
    433         else: 
    434             population = self 
    435         logger.debug("Adding recorder for %s to %s" % (variable, self.label)) 
    436         population.recorders[variable] = population.recorder_class(variable, 
    437                                                                    population=population, file=to_file) 
    438  
    439     def _record(self, variable, to_file=True): 
    440         """ 
    441         Private method called by record() and record_v(). 
    442         """ 
    443         if variable is None: # reset the list of things to record 
    444                              # note that if _record(None) is called on a view of a population 
    445                              # recording will be reset for the entire population, not just the view 
    446             for recorder in self.recorders.values(): 
    447                 recorder.reset() 
    448             self.recorders = {}     
    449         else: 
    450             if not self.can_record(variable): 
    451                 raise errors.RecordingError(variable, self.celltype)         
    452             logger.debug("%s.record('%s')", self.label, variable) 
    453             if variable not in self.recorders: 
    454                 self._add_recorder(variable, to_file) 
    455             if self.record_filter is not None: 
    456                 self.recorders[variable].record(self.record_filter) 
     427    def record(self, variables=None, to_file=None): 
     428        """ 
     429        Record the specified variable or variables for all cells in the 
     430        Population or view. 
     431         
     432        `variables` may be either a single variable name or a list of variable 
     433        names. For a given celltype class, `celltype.recordable` contains a list of 
     434        variables that can be recorded for that celltype. 
     435         
     436        If specified, `to_file` should be a Neo IO instance and `write_data()` 
     437        will be automatically called when `end()` is called. 
     438        """ 
     439        if variables is None: # reset the list of things to record 
     440                              # note that if record(None) is called on a view of a population 
     441                              # recording will be reset for the entire population, not just the view 
     442            self.recorder.reset() 
     443        elif variables in (True, False, None): # catch use of previous API 
     444            msg = "Use of the record() method has changed. Use record('spikes') instead." 
     445            warnings.warn(msg, category=DeprecationWarning) 
     446            if variables is not None: 
     447                to_file = variables 
     448            self.record('spikes', to_file) 
     449        else:         
     450            logger.debug("%s.record('%s')", self.label, variables) 
     451            if self.record_filter is None: 
     452                self.recorder.record(variables, self.all_cells) 
    457453            else: 
    458                 self.recorders[variable].record(self.all_cells) 
    459             #if isinstance(to_file, basestring): 
    460             #    self.recorders[variable].file = to_file 
    461  
    462     def record(self, to_file=True): 
    463         """ 
    464         Record spikes from all cells in the Population. 
    465         """ 
    466         self._record('spikes', to_file) 
    467  
     454                self.recorder.record(variables, self.record_filter)   
     455        if isinstance(to_file, basestring): 
     456            self.recorder.file = to_file 
     457 
     458    @deprecated("record('v')") 
    468459    def record_v(self, to_file=True): 
    469460        """ 
    470461        Record the membrane potential for all cells in the Population. 
    471462        """ 
    472         self._record('v', to_file) 
    473  
     463        self.record('v', to_file) 
     464 
     465    @deprecated("record(['gsyn_exc', 'gsyn_inh'])") 
    474466    def record_gsyn(self, to_file=True): 
    475467        """ 
    476468        Record synaptic conductances for all cells in the Population. 
    477469        """ 
    478         self._record('gsyn', to_file) 
    479  
     470        self.record(['gsyn_exc', 'gsyn_inh'], to_file) 
     471 
     472    def write_data(self, io, variables='all', gather=True): 
     473        """ 
     474        Write recorded data to file, using one of the file formats supported by 
     475        Neo. 
     476         
     477        `ìo` - a Neo IO instance 
     478        `variables` - either a single variable name or a list of variable names 
     479                      Variables must have been previously recorded, otherwise an 
     480                      Exception will be raised. 
     481                       
     482        For parallel simulators, if `gather` is True, all data will be gathered 
     483        to the master node and a single output file created there. Otherwise, a 
     484        file will be written on each node, containing only data from the cells 
     485        simulated on that node. 
     486        """ 
     487        self.recorder.write(variables, io, gather, self.record_filter) 
     488 
     489    def get_data(self, variables='all', gather=True): 
     490        """ 
     491        Return a Neo `Block` containing the data (spikes, state variables) 
     492        recorded from the Population. 
     493         
     494        `variables` - either a single variable name or a list of variable names 
     495                      Variables must have been previously recorded, otherwise an 
     496                      Exception will be raised. 
     497         
     498        For parallel simulators, if `gather` is True, all data will be gathered 
     499        to all nodes and the Neo `Block` will contain data from all nodes. 
     500        Otherwise, the Neo `Block` will contain only data from the cells 
     501        simulated on the local node. 
     502        """ 
     503        self.recorder.get(variables, gather, self.record_filter) 
     504 
     505    @deprecated("write_data(file, 'spikes')") 
    480506    def printSpikes(self, file, gather=True, compatible_output=True): 
    481         """ 
    482         Write spike times to file. 
    483  
    484         file should be either a filename or a PyNN File object. 
    485  
    486         If compatible_output is True, the format is "spiketime cell_id", 
    487         where cell_id is the index of the cell counting along rows and down 
    488         columns (and the extension of that for 3-D). 
    489         This allows easy plotting of a `raster' plot of spiketimes, with one 
    490         line for each cell. 
    491         The timestep, first id, last id, and number of data points per cell are 
    492         written in a header, indicated by a '#' at the beginning of the line. 
    493  
    494         If compatible_output is False, the raw format produced by the simulator 
    495         is used. This may be faster, since it avoids any post-processing of the 
    496         spike files. 
    497  
    498         For parallel simulators, if gather is True, all data will be gathered 
    499         to the master node and a single output file created there. Otherwise, a 
    500         file will be written on each node, containing only the cells simulated 
    501         on that node. 
    502         """ 
    503         self.recorders['spikes'].write(file, gather, compatible_output, self.record_filter) 
    504  
     507        self.write_data(file, 'spikes', gather) 
     508 
     509    @deprecated("get_data('spikes')") 
    505510    def getSpikes(self, gather=True, compatible_output=True): 
    506         """ 
    507         Return a 2-column numpy array containing cell ids and spike times for 
    508         recorded cells. 
    509  
    510         Useful for small populations, for example for single neuron Monte-Carlo. 
    511         """ 
    512         return self.recorders['spikes'].get(gather, compatible_output, self.record_filter) 
    513         # if we haven't called record(), this will give a KeyError. A more 
    514         # informative error message would be nice. 
    515  
     511        return self.get_data('spikes', gather) 
     512 
     513    @deprecated("write_data(file, 'v')") 
    516514    def print_v(self, file, gather=True, compatible_output=True): 
    517         """ 
    518         Write membrane potential traces to file. 
    519  
    520         file should be either a filename or a PyNN File object. 
    521  
    522         If compatible_output is True, the format is "v cell_id", 
    523         where cell_id is the index of the cell counting along rows and down 
    524         columns (and the extension of that for 3-D). 
    525         The timestep, first id, last id, and number of data points per cell are 
    526         written in a header, indicated by a '#' at the beginning of the line. 
    527  
    528         If compatible_output is False, the raw format produced by the simulator 
    529         is used. This may be faster, since it avoids any post-processing of the 
    530         voltage files. 
    531  
    532         For parallel simulators, if gather is True, all data will be gathered 
    533         to the master node and a single output file created there. Otherwise, a 
    534         file will be written on each node, containing only the cells simulated 
    535         on that node. 
    536         """ 
    537         self.recorders['v'].write(file, gather, compatible_output, self.record_filter) 
    538  
     515        self.write_data(file, 'v', gather) 
     516 
     517    @deprecated("get_data('v')") 
    539518    def get_v(self, gather=True, compatible_output=True): 
    540         """ 
    541         Return a 2-column numpy array containing cell ids and Vm for 
    542         recorded cells. 
    543         """ 
    544519        return self.recorders['v'].get(gather, compatible_output, self.record_filter) 
    545520 
     521    @deprecated("write_data(file, ['gsyn_exc', 'gsyn_inh'])") 
    546522    def print_gsyn(self, file, gather=True, compatible_output=True): 
    547         """ 
    548         Write synaptic conductance traces to file. 
    549  
    550         file should be either a filename or a PyNN File object. 
    551  
    552         If compatible_output is True, the format is "t g cell_id", 
    553         where cell_id is the index of the cell counting along rows and down 
    554         columns (and the extension of that for 3-D). 
    555         The timestep, first id, last id, and number of data points per cell are 
    556         written in a header, indicated by a '#' at the beginning of the line. 
    557  
    558         If compatible_output is False, the raw format produced by the simulator 
    559         is used. This may be faster, since it avoids any post-processing of the 
    560         voltage files. 
    561         """ 
    562         self.recorders['gsyn'].write(file, gather, compatible_output, self.record_filter) 
    563  
     523        self.write_data(file, ['gsyn_exc', 'gsyn_inh'], gather) 
     524 
     525    @deprecated("get_data(['gsyn_exc', 'gsyn_inh'])") 
    564526    def get_gsyn(self, gather=True, compatible_output=True): 
    565         """ 
    566         Return a 3-column numpy array containing cell ids and synaptic 
    567         conductances for recorded cells. 
    568         """ 
    569         return self.recorders['gsyn'].get(gather, compatible_output, self.record_filter) 
     527        return self.get_data(['gsyn_exc', 'gsyn_inh']), gather 
    570528 
    571529    def get_spike_counts(self, gather=True): 
     
    573531        Returns the number of spikes for each neuron. 
    574532        """ 
    575         return self.recorders['spikes'].count(gather, self.record_filter) 
    576  
     533        return self.recorder.count('spikes', gather, self.record_filter) 
     534 
     535    @deprecated("mean_spike_count()") 
    577536    def meanSpikeCount(self, gather=True): 
     537        return self.mean_spike_count(gather) 
     538 
     539    def mean_spike_count(self, gather=True): 
    578540        """ 
    579541        Returns the mean number of spikes per neuron. 
    580542        """ 
    581         spike_counts = self.recorders['spikes'].count(gather, self.record_filter) 
     543        spike_counts = self.get_spike_counts(gather) 
    582544        total_spikes = sum(spike_counts.values()) 
    583545        if self._simulator.state.mpi_rank == 0 or not gather:  # should maybe use allgather, and get the numbers on all nodes 
     
    597559        current_source.inject_into(self) 
    598560 
     561    # name should be consistent with saving/writing data, i.e. save_data() and save_positions() or write_data() and write_positions() 
    599562    def save_positions(self, file): 
    600563        """ 
     
    668631        for variable, value in self.celltype.default_initial_values.items(): 
    669632            self.initialize(variable, value) 
    670         self.recorders = {} 
     633        self.recorder = self.recorder_class(self) 
    671634        Population.nPop += 1 
    672635 
     
    831794        self.first_id     = numpy.min(self.all_cells) # only works if we assume all_cells is sorted, otherwise could use min() 
    832795        self.last_id      = numpy.max(self.all_cells) 
    833         self.recorders    = self.parent.recorders 
     796        self.recorder    = self.parent.recorder 
    834797        self.record_filter= self.all_cells 
    835798 
  • branches/neo_output/src/core.py

    r999 r1006  
    283283    function is called and suggests a replacement. 
    284284    """ 
     285    # can we also control what is returned by dir(obj), so that deprecated methods do not appear? 
    285286     
    286287    def __init__(self, replacement=''): 
     
    295296            return func(*args, **kwargs) 
    296297        new_func.__name__ = func.__name__ 
    297         new_func.__doc__ = func.__doc__ 
     298        new_func.__doc__ = func.__doc__ # should modify docstring to explain the deprecation 
    298299        new_func.__dict__.update(func.__dict__) 
    299300        return new_func 
  • branches/neo_output/src/moose/recording.py

    r957 r1006  
    7575            N[int(id)] = len(id._cell.spike_table) 
    7676        return N 
    77  
    78 simulator.Recorder = Recorder 
  • branches/neo_output/src/nemo/recording.py

    r1000 r1006  
    7575            N[id] = len(self.data[id]) 
    7676        return N 
    77          
    78  
    79 simulator.Recorder = Recorder 
  • branches/neo_output/src/nest/recording.py

    r1000 r1006  
    318318                N[id] = r-l 
    319319        return N 
    320      
    321 simulator.Recorder = Recorder # very inelegant. Need to rethink the module structure 
  • branches/neo_output/src/neuron/recording.py

    r1000 r1006  
    66 
    77import numpy 
     8from datetime import datetime 
    89from pyNN import recording 
    910from pyNN.neuron import simulator 
    1011import re 
    1112from neuron import h 
     13import neo 
     14import quantities as pq 
     15from copy import copy 
    1216 
    1317recordable_pattern = re.compile(r'((?P<section>\w+)(\((?P<location>[-+]?[0-9]*\.?[0-9]+)\))?\.)?(?P<var>\w+)') 
     
    1519# --- For implementation of record_X()/get_X()/print_X() ----------------------- 
    1620 
     21def filter_variables(segment, variables): 
     22    """ 
     23    Return a new `Segment` containing only recordings of the variables given in 
     24    the list `variables` 
     25    """ 
     26    if variables == 'all': 
     27        return segment 
     28    else: 
     29        new_segment = copy(segment) # shallow copy 
     30        if 'spikes' not in variables: 
     31            new_segment.spiketrains = [] 
     32        new_segment.analogsignals = [sig for sig in segment.analogsignals if sig.name in variables] 
     33        # also need to handle Units, RecordingChannels 
     34        return new_segment 
     35 
     36 
     37class DataCache(object): 
     38    # primitive implementation for now, storing in memory - later can consider caching to disk 
     39    def __init__(self): 
     40        self._data = [] 
     41 
     42    def __iter__(self): 
     43        return iter(self._data) 
     44     
     45    def store(self, obj): 
     46        self._data.append(obj) 
     47         
     48 
     49 
    1750class Recorder(recording.Recorder): 
    1851    """Encapsulates data and functions related to recording model variables.""" 
    1952    _simulator = simulator 
    2053     
    21     def _record(self, new_ids): 
     54    def __init__(self, population, file=None): 
     55        __doc__ = super(Recorder, self).__init__.__doc__ 
     56        super(Recorder, self).__init__(population, file) 
     57        self.cache = DataCache() 
     58     
     59    def _record(self, variable, new_ids): 
    2260        """Add the cells in `new_ids` to the set of recorded cells.""" 
    23         if self.variable == 'spikes': 
     61        if variable == 'spikes': 
    2462            for id in new_ids: 
    2563                id._cell.record(1) 
    26         elif self.variable == 'v': 
     64        elif variable == 'v': 
    2765            for id in new_ids: 
    2866                id._cell.record_v(1) 
    29         elif self.variable == 'gsyn': 
     67        elif variable == 'gsyn_exc': 
    3068            for id in new_ids: 
    3169                id._cell.record_gsyn("excitatory", 1) 
    32                 id._cell.record_gsyn("inhibitory", 1) 
    3370                if id._cell.excitatory_TM is not None: 
    3471                    id._cell.record_gsyn("excitatory_TM", 1) 
     72        elif variable == 'gsyn_inh': 
     73             for id in new_ids: 
     74                id._cell.record_gsyn("inhibitory", 1) 
     75                if id._cell.inhibitory_TM is not None: 
    3576                    id._cell.record_gsyn("inhibitory_TM", 1) 
    3677        else: 
    3778            for id in new_ids: 
    38                self._native_record(id) 
     79               self._native_record(variable, id) 
    3980     
    4081    def _reset(self): 
     
    4687                id._cell.record_gsyn(syn_name, active=False) 
    4788     
    48     def _native_record(self, id): 
     89    def _native_record(self, variable, id): 
    4990        match = recordable_pattern.match(self.variable) 
    5091        if match: 
     
    5899            else: 
    59100                segment = id._cell.source 
    60             id._cell.traces[self.variable] = vec = h.Vector() 
     101            id._cell.traces[variable] = vec = h.Vector() 
    61102            vec.record(getattr(segment, "_ref_%s" % parts['var'])) 
    62103            if not id._cell.recording_time: 
     
    65106                id._cell.recording_time += 1 
    66107        else: 
    67             raise Exception("Recording of %s not implemented." % self.variable) 
     108            raise Exception("Recording of %s not implemented." % variable) 
    68109     
    69     def _get(self, gather=False, compatible_output=True, filter=None): 
    70         """Return the recorded data as a Numpy array.""" 
    71         # compatible_output is not used, but is needed for compatibility with the nest module. 
    72         # Does nest really need it? 
    73         if self.variable == 'spikes': 
    74             data = numpy.empty((0,2)) 
    75             for id in self.filter_recorded(filter): 
    76                 spikes = numpy.array(id._cell.spike_times) 
    77                 spikes = spikes[spikes<=simulator.state.t+1e-9] 
    78                 if len(spikes) > 0:     
    79                     new_data = numpy.array([numpy.ones(spikes.shape)*id, spikes]).T 
    80                     data = numpy.concatenate((data, new_data)) 
    81         elif self.variable == 'v': 
    82             data = numpy.empty((0,3)) 
    83             for id in self.filter_recorded(filter): 
    84                 v = numpy.array(id._cell.vtrace)   
    85                 t = numpy.array(id._cell.record_times)                
    86                 new_data = numpy.array([numpy.ones(v.shape)*id, t, v]).T 
    87                 data = numpy.concatenate((data, new_data)) 
    88         elif self.variable == 'gsyn': 
    89             data = numpy.empty((0,4)) 
    90             for id in self.filter_recorded(filter): 
    91                 ge = numpy.array(id._cell.gsyn_trace['excitatory']) 
    92                 gi = numpy.array(id._cell.gsyn_trace['inhibitory']) 
    93                 if 'excitatory_TM' in id._cell.gsyn_trace: 
    94                     ge_TM = numpy.array(id._cell.gsyn_trace['excitatory_TM']) 
    95                     gi_TM = numpy.array(id._cell.gsyn_trace['inhibitory_TM']) 
    96                     if ge.size == 0: 
    97                         ge = ge_TM 
    98                     elif ge.size == ge_TM.size: 
    99                         ge = ge + ge_TM 
    100                     else: 
    101                         raise Exception("Inconsistent conductance array sizes: ge.size=%d, ge_TM.size=%d", (ge.size, ge_TM.size)) 
    102                     if gi.size == 0: 
    103                         gi = gi_TM 
    104                     elif gi.size == gi_TM.size: 
    105                         gi = gi + gi_TM 
    106                     else: 
    107                         raise Exception() 
    108                 t = numpy.array(id._cell.record_times)              
    109                 new_data = numpy.array([numpy.ones(ge.shape)*id, t, ge, gi]).T 
    110                 data = numpy.concatenate((data, new_data)) 
    111         else: 
    112             data = numpy.empty((0,3)) 
    113             for id in self.filter_recorded(filter): 
    114                 var = numpy.array(id._cell.traces[self.variable])   
    115                 t = numpy.array(id._cell.record_times)                
    116                 new_data = numpy.array([numpy.ones(var.shape)*id, t, var]).T 
    117                 data = numpy.concatenate((data, new_data))     
    118             #raise Exception("Recording of %s not implemented." % self.variable) 
     110    def _get(self, variables, gather=False, filter_ids=None): 
     111        """Return the recorded data as a Neo `Block`.""" 
     112        data = neo.Block() 
     113        data.segments = [filter_variables(segment, variables) for segment in self.cache] 
     114        data.segments.append(self._get_current_segment(filter_ids=filter_ids, variables=variables)) 
    119115        if gather and simulator.state.num_processes > 1: 
    120116            data = recording.gather(data) 
    121117        return data 
     118     
     119    def _get_current_segment(self, filter_ids=None, variables='all'): 
     120        segment = neo.Segment(name=self.population.label, 
     121                              description=self.population.describe(), 
     122                              rec_datetime=datetime.now()) # would be nice to get the time at the start of the recording, not the end 
     123        variables_to_include = set(self.recorded.keys()) 
     124        if variables is not 'all': 
     125            variables_to_include = variables_to_include.intersection(set(variables)) 
     126        def trim_spikes(spikes): 
     127            return spikes[spikes<=simulator.state.t+1e-9] 
     128        for variable in variables_to_include: 
     129            if variable == 'spikes': 
     130                segment.spiketrains = [ 
     131                    neo.SpikeTrain(trim_spikes(numpy.array(id._cell.spike_times)), 
     132                                   t_stop=simulator.state.t*pq.ms, 
     133                                   units='ms', 
     134                                   source_population=self.population.label, 
     135                                   source_id=int(id)) # index? 
     136                    for id in self.filter_recorded('spikes', filter_ids)] 
     137            else: 
     138                if variable == 'v': 
     139                    get_signal = lambda id: id._cell.vtrace 
     140                elif variable == 'gsyn_exc': 
     141                    get_signal = lambda id: id._cell.gsyn_trace['excitatory'] 
     142                elif variable == 'gsyn_inh': 
     143                    get_signal = lambda id: id._cell.gsyn_trace['inhibitory'] 
     144                else: 
     145                    signal = lambda id: id._cell.traces[variable] 
     146                segment.analogsignals = [ 
     147                    neo.AnalogSignal(get_signal(id), # assuming not using cvode, otherwise need to use IrregularlySampledAnalogSignal 
     148                                     units=recording.UNITS_MAP.get(variable, 'dimensionless'), 
     149                                     t_start=simulator.state.t_start*pq.ms, 
     150                                     sampling_period=simulator.state.dt*pq.ms, 
     151                                     name=variable, 
     152                                     source_population=self.population.label, 
     153                                     source_id=int(id)) 
     154                    for id in self.filter_recorded(variable, filter_ids) 
     155                ] 
     156                assert segment.analogsignals[0].t_stop - simulator.state.t*pq.ms < simulator.state.dt*pq.ms 
     157                # need to add `Unit` and `RecordingChannel` objects 
     158        return segment 
    122159         
    123     def _local_count(self, filter=None): 
     160    def _local_count(self, variable, filter_ids=None): 
    124161        N = {} 
    125         for id in self.filter_recorded(filter): 
    126             N[int(id)] = id._cell.spike_times.size() 
     162        if variable == 'spikes': 
     163            for id in self.filter_recorded(variable, filter_ids): 
     164                N[int(id)] = id._cell.spike_times.size() 
     165        else: 
     166            raise Exception("Only implemented for spikes") 
    127167        return N 
    128  
    129 simulator.Recorder = Recorder 
  • branches/neo_output/src/neuron/simulator.py

    r998 r1006  
    1313Classes: 
    1414    ID 
    15     Recorder 
    1615    Connection 
    1716     
     
    152151        self.clear() 
    153152        self.default_maxstep=10.0 
     153        self.t_start = 0.0 
    154154     
    155155    t = h_property('t') 
     
    176176    state.t = 0 
    177177    state.tstop = 0 
     178    state.t_start = 0 
    178179    h.finitialize() 
    179180 
     
    190191            assert local_minimum_delay >= state.min_delay, \ 
    191192                   "There are connections with delays (%g) shorter than the minimum delay (%g)" % (local_minimum_delay, state.min_delay) 
     193    state.t_start = state.t 
    192194    state.tstop += simtime 
    193195    logger.info("Running the simulation for %g ms" % simtime) 
  • branches/neo_output/src/pcsim/recording.py

    r1000 r1006  
    108108            N = recording.gather_dict(N) 
    109109        return N 
    110      
    111 simulator.Recorder = Recorder 
  • branches/neo_output/src/recording/__init__.py

    r1000 r1006  
    1717import numpy 
    1818import os 
     19from collections import defaultdict 
     20from pyNN import errors 
    1921from pyNN.recording import files 
    2022try: 
     
    3537    mpi_comm = MPI.COMM_WORLD 
    3638MPI_ROOT = 0 
     39 
     40UNITS_MAP = { 
     41    'spikes': 'ms', 
     42    'v': 'mV', 
     43    'gsyn_exc': 'uS', 
     44    'gsyn_inh': 'uS' 
     45} 
    3746 
    3847def rename_existing(filename): 
     
    7988 
    8089 
     90def normalize_variables_arg(variables): 
     91    """If variables is a single string, encapsulate it in a list.""" 
     92    if isinstance(variables, basestring): 
     93        return [variables] 
     94    else: 
     95        return variables 
     96 
     97 
    8198class Recorder(object): 
    8299    """Encapsulates data and functions related to recording model variables.""" 
    83100     
    84  
    85     formats = {'spikes': 'id t', 
    86                'v': 'id t v', 
    87                'gsyn': 'id t ge gi', 
    88                'generic': 'id t variable'} 
    89      
    90     def __init__(self, variable, population=None, file=None): 
     101    def __init__(self, population, file=None): 
    91102        """ 
    92103        Create a recorder. 
    93104         
    94         `variable` -- "spikes", "v" or "gsyn" 
    95105        `population` -- the Population instance which is being recorded by the 
    96                         recorder (optional) 
     106                        recorder 
    97107        `file` -- one of: 
    98108            - a file-name, 
     
    100110            - `False` (write to memory). 
    101111        """ 
    102         self.variable = variable 
    103112        self.file = file 
    104113        self.population = population # needed for writing header information 
    105         if population: 
    106             assert population.can_record(variable) 
    107         self.recorded = set([]) 
     114        self.recorded = defaultdict(set) 
    108115         
    109     def record(self, ids): 
    110         """Add the cells in `ids` to the set of recorded cells.""" 
     116    def record(self, variables, ids): 
     117        """ 
     118        Add the cells in `ids` to the sets of recorded cells for the given variables. 
     119        """ 
    111120        logger.debug('Recorder.record(<%d cells>)' % len(ids)) 
    112121        ids = set([id for id in ids if id.local]) 
    113         new_ids = ids.difference(self.recorded) 
    114         self.recorded = self.recorded.union(ids) 
    115         logger.debug('Recorder.recorded contains %d ids' % len(self.recorded)) 
    116         self._record(new_ids) 
     122        for variable in normalize_variables_arg(variables): 
     123            if not self.population.can_record(variable): 
     124                raise errors.RecordingError(variable, self.population.celltype)      
     125            new_ids = ids.difference(self.recorded[variable]) 
     126            self.recorded[variable] = self.recorded[variable].union(ids) 
     127            self._record(variable, new_ids) 
    117128     
    118129    def reset(self): 
    119130        """Reset the list of things to be recorded.""" 
    120131        self._reset() 
    121         self.recorded = set([]) 
     132        self.recorded = defaultdict(set) 
    122133     
    123     def filter_recorded(self, filter): 
    124         if filter is not None: 
    125             return set(filter).intersection(self.recorded) 
     134    def filter_recorded(self, variable, filter_ids): 
     135        if filter_ids is not None: 
     136            return set(filter_ids).intersection(self.recorded[variable]) 
    126137        else: 
    127             return self.recorded 
     138            return self.recorded[variable] 
    128139     
    129     def get(self, gather=False, compatible_output=True, filter=None): 
    130         """Return the recorded data as a Numpy array.""" 
    131         data_array = self._get(gather, compatible_output, filter) 
    132         if self.population is not None: 
    133             try: 
    134                 data_array[:,0] = self.population.id_to_index(data_array[:, 0]) # id is always first column             
    135             except Exception: 
    136                 pass 
    137         self._data_size = data_array.shape[0] 
    138         return data_array 
     140    def get(self, variables, gather=False, filter_ids=None): 
     141        """Return the recorded data as a Neo `Block`.""" 
     142        variables = normalize_variables_arg(variables) 
     143        data = self._get(variables, gather, filter_ids) 
     144        data.name = self.population.label 
     145        data.description = self.population.describe() 
     146        data.rec_datetime = data.segments[0].rec_datetime 
     147        data.annotate(**self.metadata) 
     148        return data 
    139149     
    140     def write(self, file=None, gather=False, compatible_output=True, filter=None): 
     150    def write(self, variables, file=None, gather=False, filter_ids=None): 
    141151        """Write recorded data to file.""" 
    142152        file = file or self.file 
    143         if isinstance(file, basestring): 
    144             filename = file 
    145             #rename_existing(filename) 
    146             if gather==False and self._simulator.state.num_processes > 1: 
    147                 filename += '.%d' % self._simulator.state.mpi_rank 
    148         else: 
    149             filename = file.name 
    150         logger.debug("Recorder is writing '%s' to file '%s' with gather=%s and compatible_output=%s" % (self.variable, 
    151                                                                                                         filename, 
    152                                                                                                         gather, 
    153                                                                                                         compatible_output)) 
    154         data = self.get(gather, compatible_output, filter) 
    155         metadata = self.metadata 
    156         logger.debug("data has size %s" % str(data.size)) 
     153        if gather==False and self._simulator.state.num_processes > 1: 
     154            file.name += '.%d' % self._simulator.state.mpi_rank 
     155        logger.debug("Recorder is writing '%s' to file '%s' with gather=%s" % ( 
     156                                               variables, file.name, gather)) 
     157        data = self.get(variables, gather, filter_ids) 
    157158        if self._simulator.state.mpi_rank == 0 or gather == False: 
    158             if compatible_output: 
    159                 data = self._make_compatible(data) 
    160159            # Open the output file, if necessary and write the data 
    161160            logger.debug("Writing data to file %s" % file) 
    162             if isinstance(file, basestring): 
    163                 file = files.StandardTextFile(filename, mode='w') 
    164             file.write(data, metadata) 
    165             file.close() 
     161            file.write(data) 
    166162     
    167163    @property 
    168164    def metadata(self): 
    169165        metadata = {} 
    170         metadata['variable'] = self.variable 
    171166        if self.population is not None: 
    172167            metadata.update({ 
     
    179174            }) 
    180175        metadata['dt'] = self._simulator.state.dt # note that this has to run on all nodes (at least for NEST) 
    181         if not hasattr(self, '_data_size'): 
    182             self.get() 
    183         metadata['n'] = self._data_size 
    184176        return metadata 
    185177     
    186     def _make_compatible(self, data_source): 
    187         """ 
    188         Rewrite simulation data in a standard format: 
    189             spiketime (in ms) cell_id-min(cell_id) 
    190         """ 
    191         assert isinstance(data_source, numpy.ndarray) 
    192         logger.debug("Converting data from memory into compatible format") 
    193         N = len(data_source) 
    194          
    195         logger.debug("Number of data elements = %d" % N) 
    196         if N > 0: 
    197             # Shuffle columns if necessary 
    198             input_format = self.formats.get(self.variable, 
    199                                             self.formats["generic"]).split() 
    200             time_column = input_format.index('t') 
    201             id_column = input_format.index('id') 
    202              
    203             if self.variable == 'gsyn': 
    204                 ge_column = input_format.index('ge') 
    205                 gi_column = input_format.index('gi') 
    206                 column_map = [ge_column, gi_column, id_column] 
    207             elif self.variable == 'v': # voltage files 
    208                 v_column = input_format.index('v') 
    209                 column_map = [v_column, id_column] 
    210             elif self.variable == 'spikes': # spike files 
    211                 column_map = [time_column, id_column] 
    212             else: 
    213                 variable_column = input_format.index('variable') 
    214                 column_map = [variable_column, id_column] 
    215              
    216             data_array = data_source[:, column_map] 
    217         else: 
    218             logger.warning("%s is empty or does not exist" % data_source) 
    219             data_array = numpy.array([]) 
    220         return data_array 
    221      
    222     def count(self, gather=True, filter=None): 
     178    def count(self, variable, gather=True, filter_ids=None): 
    223179        """ 
    224180        Return the number of data points for each cell, as a dict. This is mainly 
    225181        useful for spike counts or for variable-time-step integration methods. 
    226182        """ 
    227         if self.variable == 'spikes': 
    228             N = self._local_count(filter) 
     183        if variable == 'spikes': 
     184            N = self._local_count(variable, filter_ids) 
    229185        else: 
    230186            raise Exception("Only implemented for spikes.")