sc_neurocore_engine/neurons/
hardware.rs1#[derive(Clone, Debug)]
12pub struct LoihiCUBANeuron {
13 pub v: i32,
14 pub u: i32,
15 pub tau_v: i32,
16 pub tau_u: i32,
17 pub v_threshold: i32,
18 pub v_reset: i32,
19}
20
21impl LoihiCUBANeuron {
22 pub fn new() -> Self {
23 Self {
24 v: 0,
25 u: 0,
26 tau_v: 10,
27 tau_u: 5,
28 v_threshold: 1000,
29 v_reset: 0,
30 }
31 }
32 pub fn step(&mut self, weighted_input: i32) -> i32 {
33 self.u = self.u - self.u / self.tau_u + weighted_input;
34 self.v = self.v - self.v / self.tau_v + self.u;
35 if self.v >= self.v_threshold {
36 self.v = self.v_reset;
37 1
38 } else {
39 0
40 }
41 }
42 pub fn reset(&mut self) {
43 self.v = 0;
44 self.u = 0;
45 }
46}
47impl Default for LoihiCUBANeuron {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53#[derive(Clone, Debug)]
55pub struct Loihi2Neuron {
56 pub s1: i32,
57 pub s2: i32,
58 pub s3: i32,
59 pub tau1: i32,
60 pub tau2: i32,
61 pub tau3: i32,
62 pub w12: i32,
63 pub w13: i32,
64 pub w23: i32,
65 pub s1_threshold: i32,
66 pub s1_reset: i32,
67 pub s3_incr: i32,
68}
69
70impl Loihi2Neuron {
71 pub fn new() -> Self {
72 Self {
73 s1: 0,
74 s2: 0,
75 s3: 0,
76 tau1: 10,
77 tau2: 5,
78 tau3: 50,
79 w12: 1,
80 w13: 0,
81 w23: 0,
82 s1_threshold: 1000,
83 s1_reset: 0,
84 s3_incr: 10,
85 }
86 }
87 pub fn step(&mut self, weighted_input: i32) -> i32 {
88 self.s2 = self.s2 - (self.s2 >> self.tau2.max(1)) + self.w12 * self.s1;
89 self.s3 = self.s3 - (self.s3 >> self.tau3.max(1)) + self.w13 * self.s1 + self.w23 * self.s2;
90 self.s1 = self.s1 - (self.s1 >> self.tau1.max(1)) + self.s2 + weighted_input;
91 if self.s1 >= self.s1_threshold {
92 self.s1 = self.s1_reset;
93 self.s3 += self.s3_incr;
94 1
95 } else {
96 0
97 }
98 }
99 pub fn reset(&mut self) {
100 self.s1 = 0;
101 self.s2 = 0;
102 self.s3 = 0;
103 }
104}
105impl Default for Loihi2Neuron {
106 fn default() -> Self {
107 Self::new()
108 }
109}
110
111#[derive(Clone, Debug)]
113pub struct TrueNorthNeuron {
114 pub v: i32,
115 pub leak: i32,
116 pub threshold: i32,
117 pub v_reset: i32,
118}
119
120impl TrueNorthNeuron {
121 pub fn new(threshold: i32) -> Self {
122 Self {
123 v: 0,
124 leak: 0,
125 threshold,
126 v_reset: 0,
127 }
128 }
129 pub fn step(&mut self, weighted_input: i32) -> i32 {
130 self.v += weighted_input - self.leak;
131 if self.v >= self.threshold {
132 self.v = self.v_reset;
133 1
134 } else {
135 0
136 }
137 }
138 pub fn reset(&mut self) {
139 self.v = 0;
140 }
141}
142impl Default for TrueNorthNeuron {
143 fn default() -> Self {
144 Self::new(100)
145 }
146}
147
148#[derive(Clone, Debug)]
150pub struct BrainScaleSAdExNeuron {
151 pub v: f64,
152 pub w: f64,
153 pub v_rest: f64,
154 pub v_reset: f64,
155 pub v_threshold: f64,
156 pub delta_t: f64,
157 pub v_rh: f64,
158 pub tau: f64,
159 pub tau_w: f64,
160 pub a: f64,
161 pub b: f64,
162 pub hw_speedup: f64,
163 pub dt: f64,
164}
165
166impl BrainScaleSAdExNeuron {
167 pub fn new() -> Self {
168 Self {
169 v: -65.0,
170 w: 0.0,
171 v_rest: -65.0,
172 v_reset: -68.0,
173 v_threshold: -50.0,
174 delta_t: 2.0,
175 v_rh: -55.0,
176 tau: 20.0,
177 tau_w: 100.0,
178 a: 0.5,
179 b: 7.0,
180 hw_speedup: 1000.0,
181 dt: 0.1,
182 }
183 }
184 pub fn step(&mut self, current: f64) -> i32 {
185 let exp_arg = ((self.v - self.v_rh) / self.delta_t).clamp(-20.0, 20.0);
186 let exp_term = self.delta_t * exp_arg.exp();
187 let dv = (-(self.v - self.v_rest) + exp_term - self.w + current) / self.tau * self.dt;
188 let dw = (self.a * (self.v - self.v_rest) - self.w) / self.tau_w * self.dt;
189 self.v += dv;
190 self.w += dw;
191 if self.v >= self.v_threshold {
192 self.v = self.v_reset;
193 self.w += self.b;
194 1
195 } else {
196 0
197 }
198 }
199 pub fn reset(&mut self) {
200 self.v = self.v_rest;
201 self.w = 0.0;
202 }
203}
204impl Default for BrainScaleSAdExNeuron {
205 fn default() -> Self {
206 Self::new()
207 }
208}
209
210#[derive(Clone, Debug)]
212pub struct SpiNNakerLIFNeuron {
213 pub v: f64,
214 pub v_rest: f64,
215 pub v_reset: f64,
216 pub v_threshold: f64,
217 pub tau_m: f64,
218 pub i_offset: f64,
219 pub tau_refrac: f64,
220 pub refrac_count: f64,
221 pub dt: f64,
222}
223
224impl SpiNNakerLIFNeuron {
225 pub fn new() -> Self {
226 Self {
227 v: -70.0,
228 v_rest: -70.0,
229 v_reset: -70.0,
230 v_threshold: -50.0,
231 tau_m: 20.0,
232 i_offset: 0.0,
233 tau_refrac: 2.0,
234 refrac_count: 0.0,
235 dt: 1.0,
236 }
237 }
238 pub fn step(&mut self, current: f64) -> i32 {
239 if self.refrac_count > 0.0 {
240 self.refrac_count -= self.dt;
241 return 0;
242 }
243 self.v += (-(self.v - self.v_rest) + current + self.i_offset) / self.tau_m * self.dt;
244 if self.v >= self.v_threshold {
245 self.v = self.v_reset;
246 self.refrac_count = self.tau_refrac;
247 1
248 } else {
249 0
250 }
251 }
252 pub fn reset(&mut self) {
253 self.v = self.v_rest;
254 self.refrac_count = 0.0;
255 }
256}
257impl Default for SpiNNakerLIFNeuron {
258 fn default() -> Self {
259 Self::new()
260 }
261}
262
263#[derive(Clone, Debug)]
265pub struct SpiNNaker2Neuron {
266 pub v: i32,
267 pub v_rest: i32,
268 pub v_reset: i32,
269 pub v_threshold: i32,
270 pub decay_mult: i32,
271 pub decay_shift: i32,
272 pub refrac_steps: i32,
273 pub refrac_count: i32,
274}
275
276impl SpiNNaker2Neuron {
277 pub fn new() -> Self {
278 Self {
279 v: 0,
280 v_rest: 0,
281 v_reset: 0,
282 v_threshold: 1024,
283 decay_mult: 243,
284 decay_shift: 8,
285 refrac_steps: 2,
286 refrac_count: 0,
287 }
288 }
289 pub fn step(&mut self, current: i32) -> i32 {
290 if self.refrac_count > 0 {
291 self.refrac_count -= 1;
292 return 0;
293 }
294 self.v = ((self.v - self.v_rest).wrapping_mul(self.decay_mult) >> self.decay_shift)
295 + self.v_rest
296 + current;
297 if self.v >= self.v_threshold {
298 self.v = self.v_reset;
299 self.refrac_count = self.refrac_steps;
300 1
301 } else {
302 0
303 }
304 }
305 pub fn reset(&mut self) {
306 self.v = self.v_rest;
307 self.refrac_count = 0;
308 }
309}
310impl Default for SpiNNaker2Neuron {
311 fn default() -> Self {
312 Self::new()
313 }
314}
315
316#[derive(Clone, Debug)]
318pub struct DPINeuron {
319 pub i_mem: f64,
320 pub i_threshold: f64,
321 pub i_reset: f64,
322 pub i_leak: f64,
323 pub tau: f64,
324 pub gain: f64,
325 pub dt: f64,
326}
327
328impl DPINeuron {
329 pub fn new() -> Self {
330 Self {
331 i_mem: 0.0,
332 i_threshold: 1.0,
333 i_reset: 0.0,
334 i_leak: 0.01,
335 tau: 20.0,
336 gain: 1.0,
337 dt: 1.0,
338 }
339 }
340 pub fn step(&mut self, i_syn: f64) -> i32 {
341 self.i_mem += (-self.i_mem + self.gain * i_syn + self.i_leak) / self.tau * self.dt;
342 if self.i_mem >= self.i_threshold {
343 self.i_mem = self.i_reset;
344 1
345 } else {
346 0
347 }
348 }
349 pub fn reset(&mut self) {
350 self.i_mem = 0.0;
351 }
352}
353impl Default for DPINeuron {
354 fn default() -> Self {
355 Self::new()
356 }
357}
358
359#[derive(Clone, Debug)]
361pub struct AkidaNeuron {
362 pub v: i32,
363 pub threshold: i32,
364 pub modulation: f64,
365 pub rank: i32,
366 pub spiked: bool,
367}
368
369impl AkidaNeuron {
370 pub fn new(threshold: i32) -> Self {
371 Self {
372 v: 0,
373 threshold,
374 modulation: 0.75,
375 rank: 0,
376 spiked: false,
377 }
378 }
379 pub fn step(&mut self, weight: i32) -> i32 {
380 if self.spiked {
381 return 0;
382 }
383 self.v += (weight as f64 * self.modulation.powi(self.rank)) as i32;
384 self.rank += 1;
385 if self.v >= self.threshold {
386 self.spiked = true;
387 1
388 } else {
389 0
390 }
391 }
392 pub fn reset(&mut self) {
393 self.v = 0;
394 self.rank = 0;
395 self.spiked = false;
396 }
397}
398impl Default for AkidaNeuron {
399 fn default() -> Self {
400 Self::new(100)
401 }
402}
403
404#[derive(Clone, Debug)]
406pub struct NeuroGridNeuron {
407 pub v_s: f64,
408 pub v_d: f64,
409 pub tau_s: f64,
410 pub tau_d: f64,
411 pub g_c: f64,
412 pub delta_t: f64,
413 pub v_rest: f64,
414 pub v_threshold: f64,
415 pub v_peak: f64,
416 pub v_reset: f64,
417 pub dt: f64,
418}
419
420impl NeuroGridNeuron {
421 pub fn new() -> Self {
422 Self {
423 v_s: -65.0,
424 v_d: -65.0,
425 tau_s: 20.0,
426 tau_d: 50.0,
427 g_c: 0.5,
428 delta_t: 2.0,
429 v_rest: -65.0,
430 v_threshold: -50.0,
431 v_peak: 20.0,
432 v_reset: -65.0,
433 dt: 0.1,
434 }
435 }
436 pub fn step(&mut self, current: f64) -> i32 {
437 let dv_d =
438 (-(self.v_d - self.v_rest) + current - self.g_c * (self.v_d - self.v_s)) / self.tau_d;
439 self.v_d += dv_d * self.dt;
440 let exp_arg = ((self.v_s - self.v_threshold) / self.delta_t).min(20.0);
441 let exp_term = self.delta_t * exp_arg.exp();
442 let dv_s =
443 (-(self.v_s - self.v_rest) + exp_term + self.g_c * (self.v_d - self.v_s)) / self.tau_s;
444 self.v_s += dv_s * self.dt;
445 if self.v_s >= self.v_peak {
446 self.v_s = self.v_reset;
447 1
448 } else {
449 0
450 }
451 }
452 pub fn reset(&mut self) {
453 self.v_s = -65.0;
454 self.v_d = -65.0;
455 }
456}
457impl Default for NeuroGridNeuron {
458 fn default() -> Self {
459 Self::new()
460 }
461}
462
463#[cfg(test)]
464mod tests {
465 use super::*;
466
467 #[test]
468 fn loihi_cuba_fires() {
469 let mut n = LoihiCUBANeuron::new();
470 let t: i32 = (0..200).map(|_| n.step(100)).sum();
471 assert!(t > 0);
472 }
473 #[test]
474 fn loihi2_fires() {
475 let mut n = Loihi2Neuron {
476 tau3: 8,
477 ..Loihi2Neuron::new()
478 };
479 let t: i32 = (0..500).map(|_| n.step(200)).sum();
480 assert!(t > 0);
481 }
482 #[test]
483 fn truenorth_fires() {
484 let mut n = TrueNorthNeuron::default();
485 let t: i32 = (0..10).map(|_| n.step(50)).sum();
486 assert!(t > 0);
487 }
488 #[test]
489 fn brainscales_fires() {
490 let mut n = BrainScaleSAdExNeuron::new();
491 let t: i32 = (0..2000).map(|_| n.step(500.0)).sum();
492 assert!(t > 0);
493 }
494 #[test]
495 fn spinnaker_fires() {
496 let mut n = SpiNNakerLIFNeuron::new();
497 let t: i32 = (0..200).map(|_| n.step(30.0)).sum();
498 assert!(t > 0);
499 }
500 #[test]
501 fn spinnaker2_fires() {
502 let mut n = SpiNNaker2Neuron::new();
503 let t: i32 = (0..200).map(|_| n.step(100)).sum();
504 assert!(t > 0);
505 }
506 #[test]
507 fn dpi_fires() {
508 let mut n = DPINeuron::new();
509 let t: i32 = (0..100).map(|_| n.step(1.0)).sum();
510 assert!(t > 0);
511 }
512 #[test]
513 fn akida_fires() {
514 let mut n = AkidaNeuron::default();
515 let t: i32 = (0..10).map(|_| n.step(50)).sum();
516 assert!(t > 0);
517 }
518 #[test]
519 fn neurogrid_fires() {
520 let mut n = NeuroGridNeuron::new();
521 let t: i32 = (0..2000).map(|_| n.step(500.0)).sum();
522 assert!(t > 0);
523 }
524}