Changeset 1006
- Timestamp:
- 11/10/11 13:17:57 (6 months ago)
- Location:
- branches/neo_output
- Files:
-
- 11 modified
-
examples/VAbenchmarks.py (modified) (4 diffs)
-
src/brian/recording.py (modified) (1 diff)
-
src/common/populations.py (modified) (7 diffs)
-
src/core.py (modified) (2 diffs)
-
src/moose/recording.py (modified) (1 diff)
-
src/nemo/recording.py (modified) (1 diff)
-
src/nest/recording.py (modified) (1 diff)
-
src/neuron/recording.py (modified) (5 diffs)
-
src/neuron/simulator.py (modified) (4 diffs)
-
src/pcsim/recording.py (modified) (1 diff)
-
src/recording/__init__.py (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/neo_output/examples/VAbenchmarks.py
r915 r1006 27 27 exec("from pyNN.%s import *" % simulator_name) 28 28 from pyNN.random import NumpyRNG, RandomDistribution 29 from neo.io import PyNNTextIO 29 30 30 31 timer = Timer() … … 43 44 44 45 dt = 0.1 # (ms) simulation timestep 45 tstop = 1000 # (ms) simulaton duration46 tstop = 200 #1000 # (ms) simulaton duration 46 47 delay = 0.2 47 48 … … 148 149 # === Setup recording ========================================================== 149 150 print "%s Setting up recording..." % node_id 150 exc_cells.record( )151 inh_cells.record( )152 exc_cells[[0, 1]].record _v()151 exc_cells.record('spikes') 152 inh_cells.record('spikes') 153 exc_cells[[0, 1]].record('v') 153 154 154 155 buildCPUTime = timer.diff() … … 177 178 os.mkdir('Results') 178 179 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))180 exc_cells.write_data(PyNNTextIO("Results/VAbenchmark_%s_exc_%s_np%d.ras" % (benchmark, simulator_name, np)), 'spikes') 181 inh_cells.write_data(PyNNTextIO("Results/VAbenchmark_%s_inh_%s_np%d.ras" % (benchmark, simulator_name, np)), 'spikes') 182 exc_cells[[0, 1]].write_data(PyNNTextIO("Results/VAbenchmark_%s_exc_%s_np%d.v" % (benchmark, simulator_name, np)), 'v') 182 183 writeCPUTime = timer.diff() 183 184 -
branches/neo_output/src/brian/recording.py
r1000 r1006 120 120 N[id + padding] = len(self._devices[0].spiketimes[id]) 121 121 return N 122 123 124 simulator.Recorder = Recorder -
branches/neo_output/src/common/populations.py
r1001 r1006 386 386 raise NotImplementedError() 387 387 388 #@deprecated("initialize('v', rand_distr)")388 @deprecated("initialize('v', rand_distr)") 389 389 def randomInit(self, rand_distr): 390 390 """ … … 392 392 random values. 393 393 """ 394 warn("The randomInit() method is deprecated, and will be removed in a future release. Use initialize('v', rand_distr) instead.")395 394 self.initialize('v', rand_distr) 396 395 … … 426 425 return (variable in self.celltype.recordable) 427 426 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) 457 453 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')") 468 459 def record_v(self, to_file=True): 469 460 """ 470 461 Record the membrane potential for all cells in the Population. 471 462 """ 472 self._record('v', to_file) 473 463 self.record('v', to_file) 464 465 @deprecated("record(['gsyn_exc', 'gsyn_inh'])") 474 466 def record_gsyn(self, to_file=True): 475 467 """ 476 468 Record synaptic conductances for all cells in the Population. 477 469 """ 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')") 480 506 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')") 505 510 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')") 516 514 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')") 539 518 def get_v(self, gather=True, compatible_output=True): 540 """541 Return a 2-column numpy array containing cell ids and Vm for542 recorded cells.543 """544 519 return self.recorders['v'].get(gather, compatible_output, self.record_filter) 545 520 521 @deprecated("write_data(file, ['gsyn_exc', 'gsyn_inh'])") 546 522 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'])") 564 526 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 570 528 571 529 def get_spike_counts(self, gather=True): … … 573 531 Returns the number of spikes for each neuron. 574 532 """ 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()") 577 536 def meanSpikeCount(self, gather=True): 537 return self.mean_spike_count(gather) 538 539 def mean_spike_count(self, gather=True): 578 540 """ 579 541 Returns the mean number of spikes per neuron. 580 542 """ 581 spike_counts = self. recorders['spikes'].count(gather, self.record_filter)543 spike_counts = self.get_spike_counts(gather) 582 544 total_spikes = sum(spike_counts.values()) 583 545 if self._simulator.state.mpi_rank == 0 or not gather: # should maybe use allgather, and get the numbers on all nodes … … 597 559 current_source.inject_into(self) 598 560 561 # name should be consistent with saving/writing data, i.e. save_data() and save_positions() or write_data() and write_positions() 599 562 def save_positions(self, file): 600 563 """ … … 668 631 for variable, value in self.celltype.default_initial_values.items(): 669 632 self.initialize(variable, value) 670 self.recorder s = {}633 self.recorder = self.recorder_class(self) 671 634 Population.nPop += 1 672 635 … … 831 794 self.first_id = numpy.min(self.all_cells) # only works if we assume all_cells is sorted, otherwise could use min() 832 795 self.last_id = numpy.max(self.all_cells) 833 self.recorder s = self.parent.recorders796 self.recorder = self.parent.recorder 834 797 self.record_filter= self.all_cells 835 798 -
branches/neo_output/src/core.py
r999 r1006 283 283 function is called and suggests a replacement. 284 284 """ 285 # can we also control what is returned by dir(obj), so that deprecated methods do not appear? 285 286 286 287 def __init__(self, replacement=''): … … 295 296 return func(*args, **kwargs) 296 297 new_func.__name__ = func.__name__ 297 new_func.__doc__ = func.__doc__ 298 new_func.__doc__ = func.__doc__ # should modify docstring to explain the deprecation 298 299 new_func.__dict__.update(func.__dict__) 299 300 return new_func -
branches/neo_output/src/moose/recording.py
r957 r1006 75 75 N[int(id)] = len(id._cell.spike_table) 76 76 return N 77 78 simulator.Recorder = Recorder -
branches/neo_output/src/nemo/recording.py
r1000 r1006 75 75 N[id] = len(self.data[id]) 76 76 return N 77 78 79 simulator.Recorder = Recorder -
branches/neo_output/src/nest/recording.py
r1000 r1006 318 318 N[id] = r-l 319 319 return N 320 321 simulator.Recorder = Recorder # very inelegant. Need to rethink the module structure -
branches/neo_output/src/neuron/recording.py
r1000 r1006 6 6 7 7 import numpy 8 from datetime import datetime 8 9 from pyNN import recording 9 10 from pyNN.neuron import simulator 10 11 import re 11 12 from neuron import h 13 import neo 14 import quantities as pq 15 from copy import copy 12 16 13 17 recordable_pattern = re.compile(r'((?P<section>\w+)(\((?P<location>[-+]?[0-9]*\.?[0-9]+)\))?\.)?(?P<var>\w+)') … … 15 19 # --- For implementation of record_X()/get_X()/print_X() ----------------------- 16 20 21 def 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 37 class 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 17 50 class Recorder(recording.Recorder): 18 51 """Encapsulates data and functions related to recording model variables.""" 19 52 _simulator = simulator 20 53 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): 22 60 """Add the cells in `new_ids` to the set of recorded cells.""" 23 if self.variable == 'spikes':61 if variable == 'spikes': 24 62 for id in new_ids: 25 63 id._cell.record(1) 26 elif self.variable == 'v':64 elif variable == 'v': 27 65 for id in new_ids: 28 66 id._cell.record_v(1) 29 elif self.variable == 'gsyn':67 elif variable == 'gsyn_exc': 30 68 for id in new_ids: 31 69 id._cell.record_gsyn("excitatory", 1) 32 id._cell.record_gsyn("inhibitory", 1)33 70 if id._cell.excitatory_TM is not None: 34 71 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: 35 76 id._cell.record_gsyn("inhibitory_TM", 1) 36 77 else: 37 78 for id in new_ids: 38 self._native_record( id)79 self._native_record(variable, id) 39 80 40 81 def _reset(self): … … 46 87 id._cell.record_gsyn(syn_name, active=False) 47 88 48 def _native_record(self, id):89 def _native_record(self, variable, id): 49 90 match = recordable_pattern.match(self.variable) 50 91 if match: … … 58 99 else: 59 100 segment = id._cell.source 60 id._cell.traces[ self.variable] = vec = h.Vector()101 id._cell.traces[variable] = vec = h.Vector() 61 102 vec.record(getattr(segment, "_ref_%s" % parts['var'])) 62 103 if not id._cell.recording_time: … … 65 106 id._cell.recording_time += 1 66 107 else: 67 raise Exception("Recording of %s not implemented." % self.variable)108 raise Exception("Recording of %s not implemented." % variable) 68 109 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)) 119 115 if gather and simulator.state.num_processes > 1: 120 116 data = recording.gather(data) 121 117 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 122 159 123 def _local_count(self, filter=None):160 def _local_count(self, variable, filter_ids=None): 124 161 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") 127 167 return N 128 129 simulator.Recorder = Recorder -
branches/neo_output/src/neuron/simulator.py
r998 r1006 13 13 Classes: 14 14 ID 15 Recorder16 15 Connection 17 16 … … 152 151 self.clear() 153 152 self.default_maxstep=10.0 153 self.t_start = 0.0 154 154 155 155 t = h_property('t') … … 176 176 state.t = 0 177 177 state.tstop = 0 178 state.t_start = 0 178 179 h.finitialize() 179 180 … … 190 191 assert local_minimum_delay >= state.min_delay, \ 191 192 "There are connections with delays (%g) shorter than the minimum delay (%g)" % (local_minimum_delay, state.min_delay) 193 state.t_start = state.t 192 194 state.tstop += simtime 193 195 logger.info("Running the simulation for %g ms" % simtime) -
branches/neo_output/src/pcsim/recording.py
r1000 r1006 108 108 N = recording.gather_dict(N) 109 109 return N 110 111 simulator.Recorder = Recorder -
branches/neo_output/src/recording/__init__.py
r1000 r1006 17 17 import numpy 18 18 import os 19 from collections import defaultdict 20 from pyNN import errors 19 21 from pyNN.recording import files 20 22 try: … … 35 37 mpi_comm = MPI.COMM_WORLD 36 38 MPI_ROOT = 0 39 40 UNITS_MAP = { 41 'spikes': 'ms', 42 'v': 'mV', 43 'gsyn_exc': 'uS', 44 'gsyn_inh': 'uS' 45 } 37 46 38 47 def rename_existing(filename): … … 79 88 80 89 90 def 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 81 98 class Recorder(object): 82 99 """Encapsulates data and functions related to recording model variables.""" 83 100 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): 91 102 """ 92 103 Create a recorder. 93 104 94 `variable` -- "spikes", "v" or "gsyn"95 105 `population` -- the Population instance which is being recorded by the 96 recorder (optional)106 recorder 97 107 `file` -- one of: 98 108 - a file-name, … … 100 110 - `False` (write to memory). 101 111 """ 102 self.variable = variable103 112 self.file = file 104 113 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) 108 115 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 """ 111 120 logger.debug('Recorder.record(<%d cells>)' % len(ids)) 112 121 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) 117 128 118 129 def reset(self): 119 130 """Reset the list of things to be recorded.""" 120 131 self._reset() 121 self.recorded = set([])132 self.recorded = defaultdict(set) 122 133 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]) 126 137 else: 127 return self.recorded 138 return self.recorded[variable] 128 139 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 139 149 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): 141 151 """Write recorded data to file.""" 142 152 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) 157 158 if self._simulator.state.mpi_rank == 0 or gather == False: 158 if compatible_output:159 data = self._make_compatible(data)160 159 # Open the output file, if necessary and write the data 161 160 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) 166 162 167 163 @property 168 164 def metadata(self): 169 165 metadata = {} 170 metadata['variable'] = self.variable171 166 if self.population is not None: 172 167 metadata.update({ … … 179 174 }) 180 175 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_size184 176 return metadata 185 177 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): 223 179 """ 224 180 Return the number of data points for each cell, as a dict. This is mainly 225 181 useful for spike counts or for variable-time-step integration methods. 226 182 """ 227 if self.variable == 'spikes':228 N = self._local_count( filter)183 if variable == 'spikes': 184 N = self._local_count(variable, filter_ids) 229 185 else: 230 186 raise Exception("Only implemented for spikes.")
