GeNN  4.9.0
GPU enhanced Neuronal Networks (GeNN)
spikeRecorder.h
Go to the documentation of this file.
1 #pragma once
2 
3 // Standard C++ includes
4 #include <algorithm>
5 #include <fstream>
6 #include <iterator>
7 #include <list>
8 #include <string>
9 #include <tuple>
10 #include <vector>
11 
12 // Standard C includes
13 #include <cassert>
14 #include <cstdint>
15 
16 #ifdef _WIN32
17 #include <intrin.h>
18 #endif
19 //----------------------------------------------------------------------------
20 // SpikeWriterText
21 //----------------------------------------------------------------------------
24 {
25 public:
26  SpikeWriterText(const std::string &filename, const std::string &delimiter = " ", bool header = false)
27  : m_Stream(filename), m_Delimiter(delimiter)
28  {
29  // Set precision
30  m_Stream.precision(16);
31 
32  if(header) {
33  m_Stream << "Time [ms], Neuron ID" << std::endl;
34  }
35  }
36 
37 // GCC 4.x does not provide a move constructor for ofstream
38 #if !defined(__GNUC__) || __clang__ || __GNUC__ > 4
40  : m_Stream(std::move(other.m_Stream)),
41  m_Delimiter(std::move(other.m_Delimiter))
42  {
43  }
44 #endif
45 
46 protected:
47  //----------------------------------------------------------------------------
48  // Protected API
49  //----------------------------------------------------------------------------
50  void recordSpikes(double t, unsigned int spikeCount, const unsigned int *currentSpikes)
51  {
52  for(unsigned int i = 0; i < spikeCount; i++) {
53  m_Stream << t << m_Delimiter << currentSpikes[i] << std::endl;
54  }
55  }
56 
57 private:
58  //----------------------------------------------------------------------------
59  // Members
60  //----------------------------------------------------------------------------
61  std::ofstream m_Stream;
62  const std::string m_Delimiter;
63 };
64 
65 //----------------------------------------------------------------------------
66 // SpikeWriterTextCached
67 //----------------------------------------------------------------------------
70 {
71 public:
72  SpikeWriterTextCached(const std::string &filename, const std::string &delimiter = " ", bool header = false)
73  : m_Stream(filename), m_Delimiter(delimiter)
74  {
75  // Set precision
76  m_Stream.precision(16);
77 
78  if(header) {
79  m_Stream << "Time [ms], Neuron ID" << std::endl;
80  }
81  }
82 
83 // GCC 4.x does not provide a move constructor for ofstream
84 #if !defined(__GNUC__) || __clang__ || __GNUC__ > 4
86  : m_Stream(std::move(other.m_Stream)),
87  m_Delimiter(std::move(other.m_Delimiter)),
88  m_Cache(std::move(other.m_Cache))
89  {
90  }
91 #endif
92 
94  {
95  writeCache();
96  }
97 
98  //----------------------------------------------------------------------------
99  // Public API
100  //----------------------------------------------------------------------------
101  void writeCache()
102  {
103  // Loop through timesteps
104  for(const auto &timestep : m_Cache) {
105  // Loop through spikes
106  for(unsigned int spike : timestep.second) {
107  // Write CSV
108  m_Stream << timestep.first << m_Delimiter << spike << std::endl;
109  }
110  }
111 
112  // Clear cache
113  m_Cache.clear();
114  }
115 
116 protected:
117  //----------------------------------------------------------------------------
118  // Protected API
119  //----------------------------------------------------------------------------
120  void recordSpikes(double t, unsigned int spikeCount, const unsigned int *currentSpikes)
121  {
122  // Add a new entry to the cache
123  m_Cache.emplace_back();
124 
125  // Fill in time
126  m_Cache.back().first = t;
127 
128  // Reserve vector to hold spikes
129  m_Cache.back().second.reserve(spikeCount);
130 
131  // Copy spikes into vector
132  std::copy_n(currentSpikes, spikeCount, std::back_inserter(m_Cache.back().second));
133  }
134 
135 private:
136  //----------------------------------------------------------------------------
137  // Members
138  //----------------------------------------------------------------------------
139  std::ofstream m_Stream;
140  const std::string m_Delimiter;
141 
142  std::list<std::pair<double, std::vector<unsigned int>>> m_Cache;
143 };
144 
145 //----------------------------------------------------------------------------
146 // SpikeRecorderBase
147 //----------------------------------------------------------------------------
149 template<typename Writer = SpikeWriterText>
150 class SpikeRecorder : public Writer
151 {
152 public:
153  typedef unsigned int& (*GetCurrentSpikeCountFunc)(unsigned int);
154  typedef unsigned int* (*GetCurrentSpikesFunc)(unsigned int);
155 
156  template<typename... WriterArgs>
157  SpikeRecorder(unsigned int batch, GetCurrentSpikesFunc getCurrentSpikes,
158  GetCurrentSpikeCountFunc getCurrentSpikeCount, WriterArgs &&... writerArgs)
159  : Writer(std::forward<WriterArgs>(writerArgs)...), m_Batch(batch),
160  m_GetCurrentSpikes(getCurrentSpikes), m_GetCurrentSpikeCount(getCurrentSpikeCount), m_Sum(0)
161  {
162  }
163 
164  template<typename... WriterArgs>
165  SpikeRecorder(GetCurrentSpikesFunc getCurrentSpikes, GetCurrentSpikeCountFunc getCurrentSpikeCount,
166  WriterArgs &&... writerArgs)
167  : SpikeRecorder(0, getCurrentSpikes, getCurrentSpikeCount, std::forward<WriterArgs>(writerArgs)...)
168  {
169  }
170 
171  SpikeRecorder(SpikeRecorder<Writer>&& other) = default;
172 
173  //----------------------------------------------------------------------------
174  // Public API
175  //----------------------------------------------------------------------------
176  void record(double t)
177  {
178  const unsigned int spikeCount = m_GetCurrentSpikeCount(m_Batch);
179  m_Sum += spikeCount;
180  this->recordSpikes(t, spikeCount, m_GetCurrentSpikes(m_Batch));
181  }
182 
183  unsigned int getSum() const{ return m_Sum; }
184 
185 private:
186  //----------------------------------------------------------------------------
187  // Members
188  //----------------------------------------------------------------------------
189  const unsigned int m_Batch;
190  GetCurrentSpikesFunc m_GetCurrentSpikes;
191  GetCurrentSpikeCountFunc m_GetCurrentSpikeCount;
192  unsigned int m_Sum;
193 };
194 
196 
201 inline void writeBinarySpikeRecording(const std::string &filename, const uint32_t *spkRecord,
202  unsigned int popSize, unsigned int numTimesteps, bool append = false)
203 {
204  // Calculate recording size
205  const unsigned int numWords = ((popSize + 31) / 32) * numTimesteps;
206 
207  // Write spikes to binary file
208  std::ofstream spikes(filename, append ? std::ofstream::app | std::ofstream::binary : std::ofstream::binary);
209  spikes.write(reinterpret_cast<const char*>(spkRecord), sizeof(uint32_t) * numWords);
210 }
211 
212 
213 inline int _clz(unsigned int value)
214 {
215 #ifdef _WIN32
216  unsigned long leadingZero = 0;
217  if(_BitScanReverse(&leadingZero, value)) {
218  return 31 - leadingZero;
219  }
220  else {
221  return 32;
222  }
223 #else
224  return __builtin_clz(value);
225 #endif
226 }
227 
230 
239 inline void writeTextSpikeRecording(const std::string &filename, const uint32_t *spkRecord,
240  unsigned int popSize, unsigned int numTimesteps, double dt = 1.0,
241  const std::string &delimiter = " ", bool header = false, bool append = false,
242  double startTime = 0.0)
243 {
244  // Calculate number of words per-timestep
245  const unsigned int timestepWords = (popSize + 31) / 32;
246 
247  // Create stream and set precision
248  std::ofstream stream(filename, append ? std::ofstream::app : std::ofstream::out);
249  stream.precision(16);
250 
251  // Write header if required
252  if(header) {
253  stream << "Time [ms], Neuron ID" << std::endl;
254  }
255 
256  // Loop through timesteps
257  for(unsigned int t = 0; t < numTimesteps; t++) {
258  // Convert timestep to time
259  const double time = startTime + (t * dt);
260 
261  // Loop through words representing timestep
262  for(unsigned int w = 0; w < timestepWords; w++) {
263  // Get word
264  uint32_t spikeWord = spkRecord[(t * timestepWords) + w];
265 
266  // Calculate neuron id of highest bit of this word
267  unsigned int neuronID = (w * 32) + 31;
268 
269  // While bits remain
270  while(spikeWord != 0) {
271  // Calculate leading zeros
272  const int numLZ = _clz(spikeWord);
273 
274  // If all bits have now been processed, zero spike word
275  // Otherwise shift past the spike we have found
276  spikeWord = (numLZ == 31) ? 0 : (spikeWord << (numLZ + 1));
277 
278  // Subtract number of leading zeros from neuron ID
279  neuronID -= numLZ;
280 
281  // Write out CSV line
282  stream << time << delimiter << neuronID << std::endl;
283 
284  // New neuron id of the highest bit of this word
285  neuronID--;
286  }
287  }
288  }
289 }
void writeTextSpikeRecording(const std::string &filename, const uint32_t *spkRecord, unsigned int popSize, unsigned int numTimesteps, double dt=1.0, const std::string &delimiter=" ", bool header=false, bool append=false, double startTime=0.0)
Definition: spikeRecorder.h:239
Class to write spikes to text file.
Definition: spikeRecorder.h:23
void writeBinarySpikeRecording(const std::string &filename, const uint32_t *spkRecord, unsigned int popSize, unsigned int numTimesteps, bool append=false)
Writes spikes recorded using GeNN&#39;s spike timing sytem directly to binary file.
Definition: spikeRecorder.h:201
void recordSpikes(double t, unsigned int spikeCount, const unsigned int *currentSpikes)
Definition: spikeRecorder.h:120
SpikeRecorder(unsigned int batch, GetCurrentSpikesFunc getCurrentSpikes, GetCurrentSpikeCountFunc getCurrentSpikeCount, WriterArgs &&... writerArgs)
Definition: spikeRecorder.h:157
Class to read spikes from neuron groups.
Definition: spikeRecorder.h:150
STL namespace.
void writeCache()
Definition: spikeRecorder.h:101
SpikeWriterTextCached(const std::string &filename, const std::string &delimiter=" ", bool header=false)
Definition: spikeRecorder.h:72
void record(double t)
Definition: spikeRecorder.h:176
void recordSpikes(double t, unsigned int spikeCount, const unsigned int *currentSpikes)
Definition: spikeRecorder.h:50
SpikeRecorder(GetCurrentSpikesFunc getCurrentSpikes, GetCurrentSpikeCountFunc getCurrentSpikeCount, WriterArgs &&... writerArgs)
Definition: spikeRecorder.h:165
SpikeWriterText(const std::string &filename, const std::string &delimiter=" ", bool header=false)
Definition: spikeRecorder.h:26
Class to write spikes to text file, caching in memory before writing.
Definition: spikeRecorder.h:69
int _clz(unsigned int value)
Definition: spikeRecorder.h:213
SpikeWriterText(SpikeWriterText &&other)
Definition: spikeRecorder.h:39
~SpikeWriterTextCached()
Definition: spikeRecorder.h:93
SpikeWriterTextCached(SpikeWriterTextCached &&other)
Definition: spikeRecorder.h:85
unsigned int getSum() const
Definition: spikeRecorder.h:183