CARLsim  5.0.0
CARLsim: a GPU-accelerated SNN simulator
spike_monitor_core.cpp
Go to the documentation of this file.
1 /* * Copyright (c) 2016 Regents of the University of California. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions
5 * are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * 3. The names of its contributors may not be used to endorse or promote
15 * products derived from this software without specific prior written
16 * permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * *********************************************************************************************** *
31 * CARLsim
32 * created by: (MDR) Micah Richert, (JN) Jayram M. Nageswaran
33 * maintained by:
34 * (MA) Mike Avery <averym@uci.edu>
35 * (MB) Michael Beyeler <mbeyeler@uci.edu>,
36 * (KDC) Kristofor Carlson <kdcarlso@uci.edu>
37 * (TSC) Ting-Shuo Chou <tingshuc@uci.edu>
38 * (HK) Hirak J Kashyap <kashyaph@uci.edu>
39 *
40 * CARLsim v1.0: JM, MDR
41 * CARLsim v2.0/v2.1/v2.2: JM, MDR, MA, MB, KDC
42 * CARLsim3: MB, KDC, TSC
43 * CARLsim4: TSC, HK
44 * CARLsim5: HK, JX, KC
45 *
46 * CARLsim available from http://socsci.uci.edu/~jkrichma/CARLsim/
47 * Ver 12/31/2016
48 */
49 #include <spike_monitor_core.h>
50 
51 #include <snn.h> // CARLsim private implementation
52 #include <snn_definitions.h> // KERNEL_ERROR, KERNEL_INFO, ...
53 
54 #include <algorithm> // std::sort
55 
56 
57 
58 // we aren't using namespace std so pay attention!
59 SpikeMonitorCore::SpikeMonitorCore(SNN* snn, int monitorId, int grpId) {
60  snn_ = snn;
61  grpId_= grpId;
62  monitorId_ = monitorId;
63  nNeurons_ = -1;
64  spikeFileId_ = NULL;
65  recordSet_ = false;
66  spkMonLastUpdated_ = 0;
67 
68  mode_ = AER;
69  persistentData_ = false;
70  userHasBeenWarned_ = false;
71  needToWriteFileHeader_ = true;
72  spikeFileSignature_ = 206661989;
73  spikeFileVersion_ = 0.2f;
74 
75  // defer all unsafe operations to init function
76  init();
77 }
78 
79 void SpikeMonitorCore::init() {
80  nNeurons_ = snn_->getGroupNumNeurons(grpId_);
81  assert(nNeurons_>0);
82 
83  // spike vector will be a 2D structure that holds a list of spike times for each neuron in the group
84  // so the first dimension is neuron ID, and each spkVector_[i] holds the list of spike times of that neuron
85  // fix the first dimension of spike vector
86  spkVector_.resize(nNeurons_);
87 
88  clear();
89 
90  // use KERNEL_{ERROR|WARNING|etc} typesetting (const FILE*)
91  fpInf_ = snn_->getLogFpInf();
92  fpErr_ = snn_->getLogFpErr();
93  fpDeb_ = snn_->getLogFpDeb();
94  fpLog_ = snn_->getLogFpLog();
95 }
96 
98  if (spikeFileId_!=NULL) {
99  fclose(spikeFileId_);
100  spikeFileId_ = NULL;
101  }
102 }
103 
104 // +++++ PUBLIC METHODS: +++++++++++++++++++++++++++++++++++++++++++++++//
105 
107  assert(!isRecording());
108  recordSet_ = false;
109  userHasBeenWarned_ = false;
110  startTime_ = -1;
111  startTimeLast_ = -1;
112  stopTime_ = -1;
113  accumTime_ = 0;
114  totalTime_ = -1;
115 
116  for (int i=0; i<nNeurons_; i++)
117  spkVector_[i].clear();
118 
119  needToCalculateFiringRates_ = true;
120  needToSortFiringRates_ = true;
121  firingRates_.clear();
122  firingRatesSorted_.clear();
123  firingRates_.assign(nNeurons_,0);
124  firingRatesSorted_.assign(nNeurons_,0);
125 }
126 
128  assert(!isRecording());
129 
130  if (totalTime_==0)
131  return 0.0f;
132 
133  return getPopNumSpikes()*1000.0/(getRecordingTotalTime()*nNeurons_);
134 }
135 
137  assert(!isRecording());
138 
139  if (totalTime_==0)
140  return 0.0f;
141 
142  float meanRate = getPopMeanFiringRate();
143  std::vector<float> rates = getAllFiringRates();
144  float std = 0.0f;
145  if (nNeurons_>1) {
146  for (int i=0; i<nNeurons_; i++)
147  std += (rates[i]-meanRate)*(rates[i]-meanRate);
148  std = sqrt(std/(nNeurons_-1));
149  }
150 
151  return std;
152 }
153 
155  assert(!isRecording());
156 
157  int nSpk = 0;
158  for (int i=0; i<nNeurons_; i++)
159  nSpk += getNeuronNumSpikes(i);
160 
161  return nSpk;
162 }
163 
165  assert(!isRecording());
166 
167  // if necessary, get data structures up-to-date
168  calculateFiringRates();
169 
170  return firingRates_;
171 }
172 
174  assert(!isRecording());
175 
176  std::vector<float> rates = getAllFiringRatesSorted();
177 
178  return rates.back();
179 }
180 
182  assert(!isRecording());
183 
184  std::vector<float> rates = getAllFiringRatesSorted();
185 
186  return rates.front();
187 }
188 
190  assert(!isRecording());
191  assert(neurId>=0 && neurId<nNeurons_);
192 
193  return getNeuronNumSpikes(neurId)*1000.0/getRecordingTotalTime();
194 }
195 
197  assert(!isRecording());
198  assert(neurId>=0 && neurId<nNeurons_);
199  assert(getMode()==AER);
200 
201  return spkVector_[neurId].size();
202 }
203 
205  assert(!isRecording());
206 
207  // if necessary, get data structures up-to-date
208  sortFiringRates();
209 
210  return firingRatesSorted_;
211 }
212 
214  assert(!isRecording());
215  assert(min>=0.0f && max>=0.0f);
216  assert(max>=min);
217 
218  // if necessary, get data structures up-to-date
219  sortFiringRates();
220 
221  int counter = 0;
222  std::vector<float>::const_iterator it_begin = firingRatesSorted_.begin();
223  std::vector<float>::const_iterator it_end = firingRatesSorted_.end();
224  for(std::vector<float>::const_iterator it=it_begin; it!=it_end; it++){
225  if((*it) >= min && (*it) <= max)
226  counter++;
227  }
228 
229  return counter;
230 }
231 
233  assert(!isRecording());
234 
235  return getNumNeuronsWithFiringRate(0.0f, 0.0f);
236 }
237 
238 // \TODO need to do error check on interface
240  assert(!isRecording());
241 
242  return getNumNeuronsWithFiringRate(min,max)*100.0/nNeurons_;
243 }
244 
246  assert(!isRecording());
247 
248  return getNumNeuronsWithFiringRate(0,0)*100.0/nNeurons_;
249 }
250 
251 std::vector<std::vector<int> > SpikeMonitorCore::getSpikeVector2D(){
252  assert(!isRecording());
253  assert(mode_==AER);
254 
255  return spkVector_;
256 }
257 
258 void SpikeMonitorCore::print(bool printSpikeTimes) {
259  assert(!isRecording());
260 
261  // how many spike times to display per row
262  int dispSpkTimPerRow = 7;
263 
264  KERNEL_INFO("(t=%.3fs) SpikeMonitor for group %s(%d) has %d spikes in %ld ms (%.2f +/- %.2f Hz)",
265  (float)(snn_->getSimTime()/1000.0),
266  snn_->getGroupName(grpId_).c_str(),
267  grpId_,
268  getPopNumSpikes(),
272 
273  if (printSpikeTimes && mode_==AER) {
274  // spike times only available in AER mode
275  KERNEL_INFO("| Neur ID | Rate (Hz) | Spike Times (ms)");
276  KERNEL_INFO("|- - - - -|- - - - - -|- - - - - - - - - - - - - - - - -- - - - - - - - - - - - -")
277 
278  for (int i=0; i<nNeurons_; i++) {
279  char buffer[200];
280 #if defined(WIN32) || defined(WIN64)
281  _snprintf(buffer, 200, "| %7d | % 9.2f | ", i, getNeuronMeanFiringRate(i));
282 #else
283  snprintf(buffer, 200, "| %7d | % 9.2f | ", i, getNeuronMeanFiringRate(i));
284 #endif
285  int nSpk = spkVector_[i].size();
286  for (int j=0; j<nSpk; j++) {
287  char times[10];
288 #if defined(WIN32) || defined(WIN64)
289  _snprintf(times, 10, "%8d", spkVector_[i][j]);
290 #else
291  snprintf(times, 10, "%8d", spkVector_[i][j]);
292 #endif
293  strcat(buffer, times);
294  if (j%dispSpkTimPerRow == dispSpkTimPerRow-1 && j<nSpk-1) {
295  KERNEL_INFO("%s",buffer);
296  strcpy(buffer,"| | | ");
297  }
298  }
299  KERNEL_INFO("%s",buffer);
300  }
301  }
302 }
303 
304 void SpikeMonitorCore::pushAER(int time, int neurId) {
305  assert(isRecording());
306  assert(getMode()==AER);
307 
308  spkVector_[neurId].push_back(time);
309 }
310 
312  assert(!isRecording());
313 
314  if (!persistentData_) {
315  // if persistent mode is off (default behavior), automatically call clear() here
316  clear();
317  }
318 
319  // call updateSpikeMonitor to make sure spike file and spike vector are up-to-date
320  // Caution: must be called before recordSet_ is set to true!
321  snn_->updateSpikeMonitor(grpId_);
322 
323  needToCalculateFiringRates_ = true;
324  needToSortFiringRates_ = true;
325  recordSet_ = true;
326  long int currentTime = snn_->getSimTimeSec()*1000+snn_->getSimTimeMs();
327 
328  if (persistentData_) {
329  // persistent mode on: accumulate all times
330  // change start time only if this is the first time running it
331  startTime_ = (startTime_<0) ? currentTime : startTime_;
332  startTimeLast_ = currentTime;
333  accumTime_ = (totalTime_>0) ? totalTime_ : 0;
334  }
335  else {
336  // persistent mode off: we only care about the last probe
337  startTime_ = currentTime;
338  startTimeLast_ = currentTime;
339  accumTime_ = 0;
340  }
341 }
342 
344  assert(isRecording());
345  assert(startTime_>-1 && startTimeLast_>-1 && accumTime_>-1);
346 
347  // call updateSpikeMonitor to make sure spike file and spike vector are up-to-date
348  // Caution: must be called before recordSet_ is set to false!
349  snn_->updateSpikeMonitor(grpId_);
350 
351  recordSet_ = false;
352  userHasBeenWarned_ = false;
353  stopTime_ = snn_->getSimTimeSec()*1000+snn_->getSimTimeMs();
354 
355  // total time is the amount of time of the last probe plus all accumulated time from previous probes
356  totalTime_ = stopTime_-startTimeLast_ + accumTime_;
357  assert(totalTime_>=0);
358 }
359 
360 void SpikeMonitorCore::setSpikeFileId(FILE* spikeFileId) {
361  assert(!isRecording());
362 
363  // close previous file pointer if exists
364  if (spikeFileId_!=NULL) {
365  fclose(spikeFileId_);
366  spikeFileId_ = NULL;
367  }
368 
369  // set it to new file id
370  spikeFileId_=spikeFileId;
371 
372  if (spikeFileId_==NULL)
373  needToWriteFileHeader_ = false;
374  else {
375  // file pointer has changed, so we need to write header (again)
376  needToWriteFileHeader_ = true;
377  writeSpikeFileHeader();
378  }
379 }
380 
381 // calculate average firing rate for every neuron if we haven't done so already
382 void SpikeMonitorCore::calculateFiringRates() {
383  // only update if we have to
384  if (!needToCalculateFiringRates_)
385  return;
386 
387  assert(getMode()==AER);
388 
389  // clear, so we get the same answer every time.
390  firingRates_.assign(nNeurons_,0);
391  firingRatesSorted_.assign(nNeurons_,0);
392 
393  // this really shouldn't happen at this stage, but if recording time is zero, return all zeros
394  if (totalTime_==0) {
395  KERNEL_WARN("SpikeMonitorCore: calculateFiringRates has 0 totalTime");
396  return;
397  }
398 
399  // compute firing rate
400  assert(totalTime_>0); // avoid division by zero
401  for(int i=0;i<nNeurons_;i++) {
402  firingRates_[i]=spkVector_[i].size()*1000.0/totalTime_;
403  }
404 
405  needToCalculateFiringRates_ = false;
406 }
407 
408 // sort firing rates if we haven't done so already
409 void SpikeMonitorCore::sortFiringRates() {
410  // only sort if we have to
411  if (!needToSortFiringRates_)
412  return;
413 
414  // first make sure firing rate vector is up-to-date
415  calculateFiringRates();
416 
417  firingRatesSorted_=firingRates_;
418  std::sort(firingRatesSorted_.begin(),firingRatesSorted_.end());
419 
420  needToSortFiringRates_ = false;
421 }
422 
423 // write the header section of the spike file
424 // this should be done once per file, and should be the very first entries in the file
425 void SpikeMonitorCore::writeSpikeFileHeader() {
426  if (!needToWriteFileHeader_)
427  return;
428 
429  // write file signature
430  if (!fwrite(&spikeFileSignature_,sizeof(int),1,spikeFileId_))
431  KERNEL_ERROR("SpikeMonitorCore: writeSpikeFileHeader has fwrite error");
432 
433  // write version number
434  if (!fwrite(&spikeFileVersion_,sizeof(float),1,spikeFileId_))
435  KERNEL_ERROR("SpikeMonitorCore: writeSpikeFileHeader has fwrite error");
436 
437  // write grid dimensions
438  Grid3D grid = snn_->getGroupGrid3D(grpId_);
439  int tmpInt = grid.numX;
440  if (!fwrite(&tmpInt,sizeof(int),1,spikeFileId_))
441  KERNEL_ERROR("SpikeMonitorCore: writeSpikeFileHeader has fwrite error");
442 
443  tmpInt = grid.numY;
444  if (!fwrite(&tmpInt,sizeof(int),1,spikeFileId_))
445  KERNEL_ERROR("SpikeMonitorCore: writeSpikeFileHeader has fwrite error");
446 
447  tmpInt = grid.numZ;
448  if (!fwrite(&tmpInt,sizeof(int),1,spikeFileId_))
449  KERNEL_ERROR("SpikeMonitorCore: writeSpikeFileHeader has fwrite error");
450 
451 
452  needToWriteFileHeader_ = false;
453 }
454 
455 // Iterate through 2D spike vector and approximate size in memory.
456 // This is not exact, we are not counting the buffer overhead, only
457 // the size each subvector is memory.
459  long int bufferSize=0; // in bytes
460  for(int i=0; i<spkVector_.size();i++){
461  bufferSize+=spkVector_[i].size()*sizeof(int);
462  }
463  return bufferSize;
464 }
465 
466 // check if the spike vector is getting large. If it is, return true once until
467 // stopRecording is called.
469  if(userHasBeenWarned_)
470  return false;
471  else {
472  //check if buffer is too big
474  userHasBeenWarned_=true;
475  return true;
476  }
477  else {
478  return false;
479  }
480  }
481 }
482 
483 // returns the total accumulated time.
485  return accumTime_;
486 }
487 
SNN::getSimTimeSec
int getSimTimeSec()
Definition: snn.h:582
SNN::getGroupNumNeurons
int getGroupNumNeurons(int gGrpId)
Definition: snn.h:559
SpikeMonitorCore::getAccumTime
long int getAccumTime()
returns the total accumulated time
Definition: spike_monitor_core.cpp:484
AER
@ AER
mode in which spike information is collected in AER format
Definition: carlsim_datastructures.h:203
SpikeMonitorCore::getMinFiringRate
float getMinFiringRate()
returns the smallest recorded firing rate
Definition: spike_monitor_core.cpp:181
SpikeMonitorCore::~SpikeMonitorCore
~SpikeMonitorCore()
destructor, cleans up all the memory upon object deletion
Definition: spike_monitor_core.cpp:97
Grid3D::numX
int numX
Definition: carlsim_datastructures.h:548
SpikeMonitorCore::pushAER
void pushAER(int time, int neurId)
inserts a (time,neurId) tupel into the 2D spike vector
Definition: spike_monitor_core.cpp:304
SpikeMonitorCore::print
void print(bool printSpikeTimes)
prints the AER vector in human-readable format
Definition: spike_monitor_core.cpp:258
SpikeMonitorCore::isRecording
bool isRecording()
returns recording status
Definition: spike_monitor_core.h:152
SpikeMonitorCore::getPopNumSpikes
int getPopNumSpikes()
returns the total number of recorded spikes in the group
Definition: spike_monitor_core.cpp:154
KERNEL_WARN
#define KERNEL_WARN(formatc,...)
Definition: snn_definitions.h:112
SpikeMonitorCore::getPopStdFiringRate
float getPopStdFiringRate()
computes the standard deviation of firing rates in the group
Definition: spike_monitor_core.cpp:136
SpikeMonitorCore::getBufferSize
long int getBufferSize()
returns the approximate size of the spike vector in bytes
Definition: spike_monitor_core.cpp:458
SpikeMonitorCore::getAllFiringRates
std::vector< float > getAllFiringRates()
returns a list of firing rates for all neurons in the group (sorted by neuron ID ascending)
Definition: spike_monitor_core.cpp:164
SpikeMonitorCore::getPopMeanFiringRate
float getPopMeanFiringRate()
returns the recorded mean firing rate of the group
Definition: spike_monitor_core.cpp:127
SpikeMonitorCore::stopRecording
void stopRecording()
stops recording AER data
Definition: spike_monitor_core.cpp:343
SpikeMonitorCore::getRecordingTotalTime
long int getRecordingTotalTime()
returns the total recorded time in ms
Definition: spike_monitor_core.h:137
SpikeMonitorCore::SpikeMonitorCore
SpikeMonitorCore(SNN *snn, int monitorId, int grpId)
constructor (called by CARLsim::setSpikeMonitor)
Definition: spike_monitor_core.cpp:59
snn_definitions.h
KERNEL_ERROR
#define KERNEL_ERROR(formatc,...)
Definition: snn_definitions.h:110
SpikeMonitorCore::getPercentSilentNeurons
float getPercentSilentNeurons()
returns percentage of neurons that didn't spike during recording
Definition: spike_monitor_core.cpp:245
KERNEL_INFO
#define KERNEL_INFO(formatc,...)
Definition: snn_definitions.h:114
Grid3D::numY
int numY
Definition: carlsim_datastructures.h:548
SpikeMonitorCore::getNumNeuronsWithFiringRate
int getNumNeuronsWithFiringRate(float min, float max)
returns number of neurons whose firing rate was in [min,max] during recording
Definition: spike_monitor_core.cpp:213
SpikeMonitorCore::getAllFiringRatesSorted
std::vector< float > getAllFiringRatesSorted()
returns a list of firing rates for all neurons in the group (sorted by firing rate ascending)
Definition: spike_monitor_core.cpp:204
SNN::getLogFpErr
const FILE * getLogFpErr()
returns file pointer to error log
Definition: snn.h:516
SpikeMonitorCore::getNumSilentNeurons
int getNumSilentNeurons()
returns number of neurons that didn't spike while recording
Definition: spike_monitor_core.cpp:232
SNN::getSimTimeMs
int getSimTimeMs()
Definition: snn.h:583
SpikeMonitorCore::isBufferBig
bool isBufferBig()
returns true if spike buffer is close to maxAllowedBufferSize
Definition: spike_monitor_core.cpp:468
SNN::getLogFpInf
const FILE * getLogFpInf()
function writes population weights from gIDpre to gIDpost to file fname in binary.
Definition: snn.h:514
Grid3D
A struct to arrange neurons on a 3D grid (a primitive cubic Bravais lattice with cubic side length 1)
Definition: carlsim_datastructures.h:490
SpikeMonitorCore::clear
void clear()
deletes data from the 2D spike vector
Definition: spike_monitor_core.cpp:106
SpikeMonitorCore::getMode
SpikeMonMode getMode()
returns recording mode
Definition: spike_monitor_core.h:101
SNN::getSimTime
int getSimTime()
Definition: snn.h:581
SNN::getLogFpDeb
const FILE * getLogFpDeb()
returns file pointer to debug log
Definition: snn.h:518
SNN::getGroupName
std::string getGroupName(int grpId)
Definition: snn_manager.cpp:1865
SpikeMonitorCore::getNeuronNumSpikes
int getNeuronNumSpikes(int neurId)
returns the number of recorded spikes of a specific neuron
Definition: spike_monitor_core.cpp:196
snn.h
SpikeMonitorCore::setSpikeFileId
void setSpikeFileId(FILE *spikeFileId)
sets pointer to spike file
Definition: spike_monitor_core.cpp:360
SNN
Contains all of CARLsim's core functionality.
Definition: snn.h:115
SpikeMonitorCore::getNeuronMeanFiringRate
float getNeuronMeanFiringRate(int neurId)
returns the recorded mean firing rate for a specific neuron
Definition: spike_monitor_core.cpp:189
MAX_SPIKE_MON_BUFFER_SIZE
#define MAX_SPIKE_MON_BUFFER_SIZE
Definition: snn_definitions.h:159
SpikeMonitorCore::getSpikeVector2D
std::vector< std::vector< int > > getSpikeVector2D()
returns the 2D AER vector
Definition: spike_monitor_core.cpp:251
Grid3D::numZ
int numZ
Definition: carlsim_datastructures.h:548
SpikeMonitorCore::getMaxFiringRate
float getMaxFiringRate()
returns the largest recorded firing rate
Definition: spike_monitor_core.cpp:173
spike_monitor_core.h
SNN::getGroupGrid3D
Grid3D getGroupGrid3D(int grpId)
Definition: snn_manager.cpp:1846
SpikeMonitorCore::getPercentNeuronsWithFiringRate
float getPercentNeuronsWithFiringRate(float min, float max)
returns percentage of neurons whose firing rate was in [min,max] during recording
Definition: spike_monitor_core.cpp:239
SNN::updateSpikeMonitor
void updateSpikeMonitor(int grpId=ALL)
copy required spikes from firing buffer to spike buffer
Definition: snn_manager.cpp:6479
SpikeMonitorCore::startRecording
void startRecording()
starts recording AER data
Definition: spike_monitor_core.cpp:311
SNN::getLogFpLog
const FILE * getLogFpLog()
returns file pointer to log file
Definition: snn.h:520