CARLsim  4.1.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 *
45 * CARLsim available from http://socsci.uci.edu/~jkrichma/CARLsim/
46 * Ver 12/31/2016
47 */
48 #include <spike_monitor_core.h>
49 
50 #include <snn.h> // CARLsim private implementation
51 #include <snn_definitions.h> // KERNEL_ERROR, KERNEL_INFO, ...
52 
53 #include <algorithm> // std::sort
54 
55 
56 
57 // we aren't using namespace std so pay attention!
58 SpikeMonitorCore::SpikeMonitorCore(SNN* snn, int monitorId, int grpId) {
59  snn_ = snn;
60  grpId_= grpId;
61  monitorId_ = monitorId;
62  nNeurons_ = -1;
63  spikeFileId_ = NULL;
64  recordSet_ = false;
65  spkMonLastUpdated_ = 0;
66 
67  mode_ = AER;
68  persistentData_ = false;
69  userHasBeenWarned_ = false;
70  needToWriteFileHeader_ = true;
71  spikeFileSignature_ = 206661989;
72  spikeFileVersion_ = 0.2f;
73 
74  // defer all unsafe operations to init function
75  init();
76 }
77 
78 void SpikeMonitorCore::init() {
79  nNeurons_ = snn_->getGroupNumNeurons(grpId_);
80  assert(nNeurons_>0);
81 
82  // spike vector will be a 2D structure that holds a list of spike times for each neuron in the group
83  // so the first dimension is neuron ID, and each spkVector_[i] holds the list of spike times of that neuron
84  // fix the first dimension of spike vector
85  spkVector_.resize(nNeurons_);
86 
87  clear();
88 
89  // use KERNEL_{ERROR|WARNING|etc} typesetting (const FILE*)
90  fpInf_ = snn_->getLogFpInf();
91  fpErr_ = snn_->getLogFpErr();
92  fpDeb_ = snn_->getLogFpDeb();
93  fpLog_ = snn_->getLogFpLog();
94 }
95 
97  if (spikeFileId_!=NULL) {
98  fclose(spikeFileId_);
99  spikeFileId_ = NULL;
100  }
101 }
102 
103 // +++++ PUBLIC METHODS: +++++++++++++++++++++++++++++++++++++++++++++++//
104 
106  assert(!isRecording());
107  recordSet_ = false;
108  userHasBeenWarned_ = false;
109  startTime_ = -1;
110  startTimeLast_ = -1;
111  stopTime_ = -1;
112  accumTime_ = 0;
113  totalTime_ = -1;
114 
115  for (int i=0; i<nNeurons_; i++)
116  spkVector_[i].clear();
117 
118  needToCalculateFiringRates_ = true;
119  needToSortFiringRates_ = true;
120  firingRates_.clear();
121  firingRatesSorted_.clear();
122  firingRates_.assign(nNeurons_,0);
123  firingRatesSorted_.assign(nNeurons_,0);
124 }
125 
127  assert(!isRecording());
128 
129  if (totalTime_==0)
130  return 0.0f;
131 
132  return getPopNumSpikes()*1000.0/(getRecordingTotalTime()*nNeurons_);
133 }
134 
136  assert(!isRecording());
137 
138  if (totalTime_==0)
139  return 0.0f;
140 
141  float meanRate = getPopMeanFiringRate();
142  std::vector<float> rates = getAllFiringRates();
143  float std = 0.0f;
144  if (nNeurons_>1) {
145  for (int i=0; i<nNeurons_; i++)
146  std += (rates[i]-meanRate)*(rates[i]-meanRate);
147  std = sqrt(std/(nNeurons_-1));
148  }
149 
150  return std;
151 }
152 
154  assert(!isRecording());
155 
156  int nSpk = 0;
157  for (int i=0; i<nNeurons_; i++)
158  nSpk += getNeuronNumSpikes(i);
159 
160  return nSpk;
161 }
162 
164  assert(!isRecording());
165 
166  // if necessary, get data structures up-to-date
167  calculateFiringRates();
168 
169  return firingRates_;
170 }
171 
173  assert(!isRecording());
174 
175  std::vector<float> rates = getAllFiringRatesSorted();
176 
177  return rates.back();
178 }
179 
181  assert(!isRecording());
182 
183  std::vector<float> rates = getAllFiringRatesSorted();
184 
185  return rates.front();
186 }
187 
189  assert(!isRecording());
190  assert(neurId>=0 && neurId<nNeurons_);
191 
192  return getNeuronNumSpikes(neurId)*1000.0/getRecordingTotalTime();
193 }
194 
196  assert(!isRecording());
197  assert(neurId>=0 && neurId<nNeurons_);
198  assert(getMode()==AER);
199 
200  return spkVector_[neurId].size();
201 }
202 
204  assert(!isRecording());
205 
206  // if necessary, get data structures up-to-date
207  sortFiringRates();
208 
209  return firingRatesSorted_;
210 }
211 
213  assert(!isRecording());
214  assert(min>=0.0f && max>=0.0f);
215  assert(max>=min);
216 
217  // if necessary, get data structures up-to-date
218  sortFiringRates();
219 
220  int counter = 0;
221  std::vector<float>::const_iterator it_begin = firingRatesSorted_.begin();
222  std::vector<float>::const_iterator it_end = firingRatesSorted_.end();
223  for(std::vector<float>::const_iterator it=it_begin; it!=it_end; it++){
224  if((*it) >= min && (*it) <= max)
225  counter++;
226  }
227 
228  return counter;
229 }
230 
232  assert(!isRecording());
233 
234  return getNumNeuronsWithFiringRate(0.0f, 0.0f);
235 }
236 
237 // \TODO need to do error check on interface
239  assert(!isRecording());
240 
241  return getNumNeuronsWithFiringRate(min,max)*100.0/nNeurons_;
242 }
243 
245  assert(!isRecording());
246 
247  return getNumNeuronsWithFiringRate(0,0)*100.0/nNeurons_;
248 }
249 
250 std::vector<std::vector<int> > SpikeMonitorCore::getSpikeVector2D(){
251  assert(!isRecording());
252  assert(mode_==AER);
253 
254  return spkVector_;
255 }
256 
257 void SpikeMonitorCore::print(bool printSpikeTimes) {
258  assert(!isRecording());
259 
260  // how many spike times to display per row
261  int dispSpkTimPerRow = 7;
262 
263  KERNEL_INFO("(t=%.3fs) SpikeMonitor for group %s(%d) has %d spikes in %ld ms (%.2f +/- %.2f Hz)",
264  (float)(snn_->getSimTime()/1000.0),
265  snn_->getGroupName(grpId_).c_str(),
266  grpId_,
267  getPopNumSpikes(),
271 
272  if (printSpikeTimes && mode_==AER) {
273  // spike times only available in AER mode
274  KERNEL_INFO("| Neur ID | Rate (Hz) | Spike Times (ms)");
275  KERNEL_INFO("|- - - - -|- - - - - -|- - - - - - - - - - - - - - - - -- - - - - - - - - - - - -")
276 
277  for (int i=0; i<nNeurons_; i++) {
278  char buffer[200];
279 #if defined(WIN32) || defined(WIN64)
280  _snprintf(buffer, 200, "| %7d | % 9.2f | ", i, getNeuronMeanFiringRate(i));
281 #else
282  snprintf(buffer, 200, "| %7d | % 9.2f | ", i, getNeuronMeanFiringRate(i));
283 #endif
284  int nSpk = spkVector_[i].size();
285  for (int j=0; j<nSpk; j++) {
286  char times[10];
287 #if defined(WIN32) || defined(WIN64)
288  _snprintf(times, 10, "%8d", spkVector_[i][j]);
289 #else
290  snprintf(times, 10, "%8d", spkVector_[i][j]);
291 #endif
292  strcat(buffer, times);
293  if (j%dispSpkTimPerRow == dispSpkTimPerRow-1 && j<nSpk-1) {
294  KERNEL_INFO("%s",buffer);
295  strcpy(buffer,"| | | ");
296  }
297  }
298  KERNEL_INFO("%s",buffer);
299  }
300  }
301 }
302 
303 void SpikeMonitorCore::pushAER(int time, int neurId) {
304  assert(isRecording());
305  assert(getMode()==AER);
306 
307  spkVector_[neurId].push_back(time);
308 }
309 
311  assert(!isRecording());
312 
313  if (!persistentData_) {
314  // if persistent mode is off (default behavior), automatically call clear() here
315  clear();
316  }
317 
318  // call updateSpikeMonitor to make sure spike file and spike vector are up-to-date
319  // Caution: must be called before recordSet_ is set to true!
320  snn_->updateSpikeMonitor(grpId_);
321 
322  needToCalculateFiringRates_ = true;
323  needToSortFiringRates_ = true;
324  recordSet_ = true;
325  long int currentTime = snn_->getSimTimeSec()*1000+snn_->getSimTimeMs();
326 
327  if (persistentData_) {
328  // persistent mode on: accumulate all times
329  // change start time only if this is the first time running it
330  startTime_ = (startTime_<0) ? currentTime : startTime_;
331  startTimeLast_ = currentTime;
332  accumTime_ = (totalTime_>0) ? totalTime_ : 0;
333  }
334  else {
335  // persistent mode off: we only care about the last probe
336  startTime_ = currentTime;
337  startTimeLast_ = currentTime;
338  accumTime_ = 0;
339  }
340 }
341 
343  assert(isRecording());
344  assert(startTime_>-1 && startTimeLast_>-1 && accumTime_>-1);
345 
346  // call updateSpikeMonitor to make sure spike file and spike vector are up-to-date
347  // Caution: must be called before recordSet_ is set to false!
348  snn_->updateSpikeMonitor(grpId_);
349 
350  recordSet_ = false;
351  userHasBeenWarned_ = false;
352  stopTime_ = snn_->getSimTimeSec()*1000+snn_->getSimTimeMs();
353 
354  // total time is the amount of time of the last probe plus all accumulated time from previous probes
355  totalTime_ = stopTime_-startTimeLast_ + accumTime_;
356  assert(totalTime_>=0);
357 }
358 
359 void SpikeMonitorCore::setSpikeFileId(FILE* spikeFileId) {
360  assert(!isRecording());
361 
362  // close previous file pointer if exists
363  if (spikeFileId_!=NULL) {
364  fclose(spikeFileId_);
365  spikeFileId_ = NULL;
366  }
367 
368  // set it to new file id
369  spikeFileId_=spikeFileId;
370 
371  if (spikeFileId_==NULL)
372  needToWriteFileHeader_ = false;
373  else {
374  // file pointer has changed, so we need to write header (again)
375  needToWriteFileHeader_ = true;
376  writeSpikeFileHeader();
377  }
378 }
379 
380 // calculate average firing rate for every neuron if we haven't done so already
381 void SpikeMonitorCore::calculateFiringRates() {
382  // only update if we have to
383  if (!needToCalculateFiringRates_)
384  return;
385 
386  assert(getMode()==AER);
387 
388  // clear, so we get the same answer every time.
389  firingRates_.assign(nNeurons_,0);
390  firingRatesSorted_.assign(nNeurons_,0);
391 
392  // this really shouldn't happen at this stage, but if recording time is zero, return all zeros
393  if (totalTime_==0) {
394  KERNEL_WARN("SpikeMonitorCore: calculateFiringRates has 0 totalTime");
395  return;
396  }
397 
398  // compute firing rate
399  assert(totalTime_>0); // avoid division by zero
400  for(int i=0;i<nNeurons_;i++) {
401  firingRates_[i]=spkVector_[i].size()*1000.0/totalTime_;
402  }
403 
404  needToCalculateFiringRates_ = false;
405 }
406 
407 // sort firing rates if we haven't done so already
408 void SpikeMonitorCore::sortFiringRates() {
409  // only sort if we have to
410  if (!needToSortFiringRates_)
411  return;
412 
413  // first make sure firing rate vector is up-to-date
414  calculateFiringRates();
415 
416  firingRatesSorted_=firingRates_;
417  std::sort(firingRatesSorted_.begin(),firingRatesSorted_.end());
418 
419  needToSortFiringRates_ = false;
420 }
421 
422 // write the header section of the spike file
423 // this should be done once per file, and should be the very first entries in the file
424 void SpikeMonitorCore::writeSpikeFileHeader() {
425  if (!needToWriteFileHeader_)
426  return;
427 
428  // write file signature
429  if (!fwrite(&spikeFileSignature_,sizeof(int),1,spikeFileId_))
430  KERNEL_ERROR("SpikeMonitorCore: writeSpikeFileHeader has fwrite error");
431 
432  // write version number
433  if (!fwrite(&spikeFileVersion_,sizeof(float),1,spikeFileId_))
434  KERNEL_ERROR("SpikeMonitorCore: writeSpikeFileHeader has fwrite error");
435 
436  // write grid dimensions
437  Grid3D grid = snn_->getGroupGrid3D(grpId_);
438  int tmpInt = grid.numX;
439  if (!fwrite(&tmpInt,sizeof(int),1,spikeFileId_))
440  KERNEL_ERROR("SpikeMonitorCore: writeSpikeFileHeader has fwrite error");
441 
442  tmpInt = grid.numY;
443  if (!fwrite(&tmpInt,sizeof(int),1,spikeFileId_))
444  KERNEL_ERROR("SpikeMonitorCore: writeSpikeFileHeader has fwrite error");
445 
446  tmpInt = grid.numZ;
447  if (!fwrite(&tmpInt,sizeof(int),1,spikeFileId_))
448  KERNEL_ERROR("SpikeMonitorCore: writeSpikeFileHeader has fwrite error");
449 
450 
451  needToWriteFileHeader_ = false;
452 }
453 
454 // Iterate through 2D spike vector and approximate size in memory.
455 // This is not exact, we are not counting the buffer overhead, only
456 // the size each subvector is memory.
458  long int bufferSize=0; // in bytes
459  for(int i=0; i<spkVector_.size();i++){
460  bufferSize+=spkVector_[i].size()*sizeof(int);
461  }
462  return bufferSize;
463 }
464 
465 // check if the spike vector is getting large. If it is, return true once until
466 // stopRecording is called.
468  if(userHasBeenWarned_)
469  return false;
470  else {
471  //check if buffer is too big
473  userHasBeenWarned_=true;
474  return true;
475  }
476  else {
477  return false;
478  }
479  }
480 }
481 
482 // returns the total accumulated time.
484  return accumTime_;
485 }
486 
SNN::getSimTimeSec
int getSimTimeSec()
Definition: snn.h:581
SNN::getGroupNumNeurons
int getGroupNumNeurons(int gGrpId)
Definition: snn.h:558
SpikeMonitorCore::getAccumTime
long int getAccumTime()
returns the total accumulated time
Definition: spike_monitor_core.cpp:483
AER
@ AER
mode in which spike information is collected in AER format
Definition: carlsim_datastructures.h:202
SpikeMonitorCore::getMinFiringRate
float getMinFiringRate()
returns the smallest recorded firing rate
Definition: spike_monitor_core.cpp:180
SpikeMonitorCore::~SpikeMonitorCore
~SpikeMonitorCore()
destructor, cleans up all the memory upon object deletion
Definition: spike_monitor_core.cpp:96
Grid3D::numX
int numX
Definition: carlsim_datastructures.h:547
SpikeMonitorCore::pushAER
void pushAER(int time, int neurId)
inserts a (time,neurId) tupel into the 2D spike vector
Definition: spike_monitor_core.cpp:303
SpikeMonitorCore::print
void print(bool printSpikeTimes)
prints the AER vector in human-readable format
Definition: spike_monitor_core.cpp:257
SpikeMonitorCore::isRecording
bool isRecording()
returns recording status
Definition: spike_monitor_core.h:151
SpikeMonitorCore::getPopNumSpikes
int getPopNumSpikes()
returns the total number of recorded spikes in the group
Definition: spike_monitor_core.cpp:153
KERNEL_WARN
#define KERNEL_WARN(formatc,...)
Definition: snn_definitions.h:111
SpikeMonitorCore::getPopStdFiringRate
float getPopStdFiringRate()
computes the standard deviation of firing rates in the group
Definition: spike_monitor_core.cpp:135
SpikeMonitorCore::getBufferSize
long int getBufferSize()
returns the approximate size of the spike vector in bytes
Definition: spike_monitor_core.cpp:457
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:163
SpikeMonitorCore::getPopMeanFiringRate
float getPopMeanFiringRate()
returns the recorded mean firing rate of the group
Definition: spike_monitor_core.cpp:126
SpikeMonitorCore::stopRecording
void stopRecording()
stops recording AER data
Definition: spike_monitor_core.cpp:342
SpikeMonitorCore::getRecordingTotalTime
long int getRecordingTotalTime()
returns the total recorded time in ms
Definition: spike_monitor_core.h:136
SpikeMonitorCore::SpikeMonitorCore
SpikeMonitorCore(SNN *snn, int monitorId, int grpId)
constructor (called by CARLsim::setSpikeMonitor)
Definition: spike_monitor_core.cpp:58
snn_definitions.h
KERNEL_ERROR
#define KERNEL_ERROR(formatc,...)
Definition: snn_definitions.h:109
SpikeMonitorCore::getPercentSilentNeurons
float getPercentSilentNeurons()
returns percentage of neurons that didn't spike during recording
Definition: spike_monitor_core.cpp:244
KERNEL_INFO
#define KERNEL_INFO(formatc,...)
Definition: snn_definitions.h:113
Grid3D::numY
int numY
Definition: carlsim_datastructures.h:547
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:212
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:203
SNN::getLogFpErr
const FILE * getLogFpErr()
returns file pointer to error log
Definition: snn.h:515
SpikeMonitorCore::getNumSilentNeurons
int getNumSilentNeurons()
returns number of neurons that didn't spike while recording
Definition: spike_monitor_core.cpp:231
SNN::getSimTimeMs
int getSimTimeMs()
Definition: snn.h:582
SpikeMonitorCore::isBufferBig
bool isBufferBig()
returns true if spike buffer is close to maxAllowedBufferSize
Definition: spike_monitor_core.cpp:467
SNN::getLogFpInf
const FILE * getLogFpInf()
function writes population weights from gIDpre to gIDpost to file fname in binary.
Definition: snn.h:513
Grid3D
A struct to arrange neurons on a 3D grid (a primitive cubic Bravais lattice with cubic side length 1)
Definition: carlsim_datastructures.h:489
SpikeMonitorCore::clear
void clear()
deletes data from the 2D spike vector
Definition: spike_monitor_core.cpp:105
SpikeMonitorCore::getMode
SpikeMonMode getMode()
returns recording mode
Definition: spike_monitor_core.h:100
SNN::getSimTime
int getSimTime()
Definition: snn.h:580
SNN::getLogFpDeb
const FILE * getLogFpDeb()
returns file pointer to debug log
Definition: snn.h:517
SNN::getGroupName
std::string getGroupName(int grpId)
Definition: snn_manager.cpp:1864
SpikeMonitorCore::getNeuronNumSpikes
int getNeuronNumSpikes(int neurId)
returns the number of recorded spikes of a specific neuron
Definition: spike_monitor_core.cpp:195
snn.h
SpikeMonitorCore::setSpikeFileId
void setSpikeFileId(FILE *spikeFileId)
sets pointer to spike file
Definition: spike_monitor_core.cpp:359
SNN
Contains all of CARLsim's core functionality.
Definition: snn.h:114
SpikeMonitorCore::getNeuronMeanFiringRate
float getNeuronMeanFiringRate(int neurId)
returns the recorded mean firing rate for a specific neuron
Definition: spike_monitor_core.cpp:188
MAX_SPIKE_MON_BUFFER_SIZE
#define MAX_SPIKE_MON_BUFFER_SIZE
Definition: snn_definitions.h:158
SpikeMonitorCore::getSpikeVector2D
std::vector< std::vector< int > > getSpikeVector2D()
returns the 2D AER vector
Definition: spike_monitor_core.cpp:250
Grid3D::numZ
int numZ
Definition: carlsim_datastructures.h:547
SpikeMonitorCore::getMaxFiringRate
float getMaxFiringRate()
returns the largest recorded firing rate
Definition: spike_monitor_core.cpp:172
spike_monitor_core.h
SNN::getGroupGrid3D
Grid3D getGroupGrid3D(int grpId)
Definition: snn_manager.cpp:1845
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:238
SNN::updateSpikeMonitor
void updateSpikeMonitor(int grpId=ALL)
copy required spikes from firing buffer to spike buffer
Definition: snn_manager.cpp:6478
SpikeMonitorCore::startRecording
void startRecording()
starts recording AER data
Definition: spike_monitor_core.cpp:310
SNN::getLogFpLog
const FILE * getLogFpLog()
returns file pointer to log file
Definition: snn.h:519