scirs2_spatial/neuromorphic/core/
events.rs1#[derive(Debug, Clone)]
33pub struct SpikeEvent {
34 pub neuron_id: usize,
36 pub timestamp: f64,
38 pub amplitude: f64,
40 pub spatial_coords: Vec<f64>,
42}
43
44impl SpikeEvent {
45 pub fn new(neuron_id: usize, timestamp: f64, amplitude: f64, spatial_coords: Vec<f64>) -> Self {
56 Self {
57 neuron_id,
58 timestamp,
59 amplitude,
60 spatial_coords,
61 }
62 }
63
64 pub fn neuron_id(&self) -> usize {
66 self.neuron_id
67 }
68
69 pub fn timestamp(&self) -> f64 {
71 self.timestamp
72 }
73
74 pub fn amplitude(&self) -> f64 {
76 self.amplitude
77 }
78
79 pub fn spatial_coords(&self) -> &[f64] {
81 &self.spatial_coords
82 }
83
84 pub fn spatial_dims(&self) -> usize {
86 self.spatial_coords.len()
87 }
88
89 pub fn temporal_distance(&self, other: &SpikeEvent) -> f64 {
97 (self.timestamp - other.timestamp).abs()
98 }
99
100 pub fn spatial_distance(&self, other: &SpikeEvent) -> Option<f64> {
108 if self.spatial_coords.len() != other.spatial_coords.len() {
109 return None;
110 }
111
112 let distance = self
113 .spatial_coords
114 .iter()
115 .zip(other.spatial_coords.iter())
116 .map(|(a, b)| (a - b).powi(2))
117 .sum::<f64>()
118 .sqrt();
119
120 Some(distance)
121 }
122
123 pub fn is_before(&self, other: &SpikeEvent) -> bool {
131 self.timestamp < other.timestamp
132 }
133
134 pub fn is_after(&self, other: &SpikeEvent) -> bool {
142 self.timestamp > other.timestamp
143 }
144
145 pub fn same_neuron(&self, other: &SpikeEvent) -> bool {
153 self.neuron_id == other.neuron_id
154 }
155}
156
157impl PartialEq for SpikeEvent {
158 fn eq(&self, other: &Self) -> bool {
159 self.neuron_id == other.neuron_id
160 && (self.timestamp - other.timestamp).abs() < 1e-9
161 && (self.amplitude - other.amplitude).abs() < 1e-9
162 && self.spatial_coords == other.spatial_coords
163 }
164}
165
166impl PartialOrd for SpikeEvent {
167 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
168 self.timestamp.partial_cmp(&other.timestamp)
169 }
170}
171
172#[derive(Debug, Clone)]
174pub struct SpikeSequence {
175 events: Vec<SpikeEvent>,
177}
178
179impl SpikeSequence {
180 pub fn new() -> Self {
182 Self { events: Vec::new() }
183 }
184
185 pub fn from_events(events: Vec<SpikeEvent>) -> Self {
193 Self { events }
194 }
195
196 pub fn add_event(&mut self, event: SpikeEvent) {
201 self.events.push(event);
202 }
203
204 pub fn events(&self) -> &[SpikeEvent] {
206 &self.events
207 }
208
209 pub fn len(&self) -> usize {
211 self.events.len()
212 }
213
214 pub fn is_empty(&self) -> bool {
216 self.events.is_empty()
217 }
218
219 pub fn sort_by_time(&mut self) {
221 self.events
222 .sort_by(|a, b| a.timestamp.partial_cmp(&b.timestamp).unwrap());
223 }
224
225 pub fn events_in_window(&self, start_time: f64, end_time: f64) -> Vec<&SpikeEvent> {
234 self.events
235 .iter()
236 .filter(|event| event.timestamp >= start_time && event.timestamp <= end_time)
237 .collect()
238 }
239
240 pub fn events_from_neuron(&self, neuron_id: usize) -> Vec<&SpikeEvent> {
248 self.events
249 .iter()
250 .filter(|event| event.neuron_id == neuron_id)
251 .collect()
252 }
253
254 pub fn firing_rate(&self, neuron_id: usize, time_window: f64) -> f64 {
263 let neuron_events = self.events_from_neuron(neuron_id);
264 if neuron_events.is_empty() || time_window <= 0.0 {
265 return 0.0;
266 }
267
268 neuron_events.len() as f64 / time_window
269 }
270
271 pub fn time_span(&self) -> Option<(f64, f64)> {
276 if self.events.is_empty() {
277 return None;
278 }
279
280 let times: Vec<f64> = self.events.iter().map(|event| event.timestamp).collect();
281 let min_time = times.iter().fold(f64::INFINITY, |a, &b| a.min(b));
282 let max_time = times.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
283
284 Some((min_time, max_time))
285 }
286}
287
288impl Default for SpikeSequence {
289 fn default() -> Self {
290 Self::new()
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297
298 #[test]
299 fn test_spike_event_creation() {
300 let spike = SpikeEvent::new(42, 1000.5, 1.0, vec![0.5, 0.3, 0.8]);
301
302 assert_eq!(spike.neuron_id(), 42);
303 assert_eq!(spike.timestamp(), 1000.5);
304 assert_eq!(spike.amplitude(), 1.0);
305 assert_eq!(spike.spatial_coords(), &[0.5, 0.3, 0.8]);
306 assert_eq!(spike.spatial_dims(), 3);
307 }
308
309 #[test]
310 fn test_temporal_distance() {
311 let spike1 = SpikeEvent::new(1, 100.0, 1.0, vec![0.0]);
312 let spike2 = SpikeEvent::new(2, 105.0, 1.0, vec![1.0]);
313
314 assert_eq!(spike1.temporal_distance(&spike2), 5.0);
315 assert_eq!(spike2.temporal_distance(&spike1), 5.0);
316 }
317
318 #[test]
319 fn test_spatial_distance() {
320 let spike1 = SpikeEvent::new(1, 100.0, 1.0, vec![0.0, 0.0]);
321 let spike2 = SpikeEvent::new(2, 105.0, 1.0, vec![3.0, 4.0]);
322
323 assert_eq!(spike1.spatial_distance(&spike2), Some(5.0));
324
325 let spike3 = SpikeEvent::new(3, 110.0, 1.0, vec![1.0]);
327 assert_eq!(spike1.spatial_distance(&spike3), None);
328 }
329
330 #[test]
331 fn test_spike_ordering() {
332 let spike1 = SpikeEvent::new(1, 100.0, 1.0, vec![0.0]);
333 let spike2 = SpikeEvent::new(2, 105.0, 1.0, vec![1.0]);
334
335 assert!(spike1.is_before(&spike2));
336 assert!(spike2.is_after(&spike1));
337 assert!(!spike1.is_after(&spike2));
338 assert!(!spike2.is_before(&spike1));
339 }
340
341 #[test]
342 fn test_spike_sequence() {
343 let mut sequence = SpikeSequence::new();
344 assert!(sequence.is_empty());
345
346 let spike1 = SpikeEvent::new(1, 100.0, 1.0, vec![0.0]);
347 let spike2 = SpikeEvent::new(2, 105.0, 1.0, vec![1.0]);
348
349 sequence.add_event(spike1);
350 sequence.add_event(spike2);
351
352 assert_eq!(sequence.len(), 2);
353 assert!(!sequence.is_empty());
354
355 let time_span = sequence.time_span().unwrap();
356 assert_eq!(time_span.0, 100.0);
357 assert_eq!(time_span.1, 105.0);
358 }
359
360 #[test]
361 fn test_events_in_window() {
362 let mut sequence = SpikeSequence::new();
363 sequence.add_event(SpikeEvent::new(1, 95.0, 1.0, vec![0.0]));
364 sequence.add_event(SpikeEvent::new(2, 100.0, 1.0, vec![1.0]));
365 sequence.add_event(SpikeEvent::new(3, 105.0, 1.0, vec![2.0]));
366 sequence.add_event(SpikeEvent::new(4, 110.0, 1.0, vec![3.0]));
367
368 let windowed_events = sequence.events_in_window(100.0, 105.0);
369 assert_eq!(windowed_events.len(), 2);
370 assert_eq!(windowed_events[0].neuron_id, 2);
371 assert_eq!(windowed_events[1].neuron_id, 3);
372 }
373
374 #[test]
375 fn test_firing_rate() {
376 let mut sequence = SpikeSequence::new();
377 sequence.add_event(SpikeEvent::new(1, 100.0, 1.0, vec![0.0]));
378 sequence.add_event(SpikeEvent::new(1, 105.0, 1.0, vec![0.0]));
379 sequence.add_event(SpikeEvent::new(1, 110.0, 1.0, vec![0.0]));
380 sequence.add_event(SpikeEvent::new(2, 107.0, 1.0, vec![1.0]));
381
382 assert_eq!(sequence.firing_rate(1, 10.0), 0.3);
384
385 assert_eq!(sequence.firing_rate(2, 10.0), 0.1);
387
388 assert_eq!(sequence.firing_rate(99, 10.0), 0.0);
390 }
391}