Skip to main content

sc_neurocore_engine/neurons/hardware/
brainscales_adex.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Commercial license available
3// © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
4// © Code 2020–2026 Miroslav Šotek. All rights reserved.
5// ORCID: 0009-0009-3560-0851
6// Contact: www.anulum.li | protoscience@anulum.li
7// SC-NeuroCore — BrainScaleS AdEx Neuron Emulator
8
9/// BrainScaleS AdEx — Heidelberg analog wafer-scale. Schemmel et al. 2010.
10#[derive(Clone, Debug)]
11pub struct BrainScaleSAdExNeuron {
12    pub v: f64,
13    pub w: f64,
14    pub v_rest: f64,
15    pub v_reset: f64,
16    pub v_threshold: f64,
17    pub delta_t: f64,
18    pub v_rh: f64,
19    pub tau: f64,
20    pub tau_w: f64,
21    pub a: f64,
22    pub b: f64,
23    pub hw_speedup: f64,
24    pub dt: f64,
25}
26
27impl BrainScaleSAdExNeuron {
28    pub fn new() -> Self {
29        Self {
30            v: -65.0,
31            w: 0.0,
32            v_rest: -65.0,
33            v_reset: -68.0,
34            v_threshold: -50.0,
35            delta_t: 2.0,
36            v_rh: -55.0,
37            tau: 20.0,
38            tau_w: 100.0,
39            a: 0.5,
40            b: 7.0,
41            hw_speedup: 1000.0,
42            dt: 0.1,
43        }
44    }
45    pub fn step(&mut self, current: f64) -> i32 {
46        let exp_arg = ((self.v - self.v_rh) / self.delta_t).clamp(-20.0, 20.0);
47        let exp_term = self.delta_t * exp_arg.exp();
48        let dv = (-(self.v - self.v_rest) + exp_term - self.w + current) / self.tau * self.dt;
49        let dw = (self.a * (self.v - self.v_rest) - self.w) / self.tau_w * self.dt;
50        self.v += dv;
51        self.w += dw;
52        if self.v >= self.v_threshold {
53            self.v = self.v_reset;
54            self.w += self.b;
55            1
56        } else {
57            0
58        }
59    }
60    pub fn reset(&mut self) {
61        self.v = self.v_rest;
62        self.w = 0.0;
63    }
64}
65impl Default for BrainScaleSAdExNeuron {
66    fn default() -> Self {
67        Self::new()
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn brainscales_fires() {
77        let mut n = BrainScaleSAdExNeuron::new();
78        let t: i32 = (0..2000).map(|_| n.step(500.0)).sum();
79        assert!(t > 0);
80    }
81    #[test]
82    fn brainscales_silent() {
83        let mut n = BrainScaleSAdExNeuron::new();
84        let t: i32 = (0..200).map(|_| n.step(0.0)).sum();
85        assert_eq!(t, 0);
86    }
87    #[test]
88    fn brainscales_reset() {
89        let mut n = BrainScaleSAdExNeuron::new();
90        for _ in 0..100 {
91            n.step(500.0);
92        }
93        n.reset();
94        assert!((n.v - n.v_rest).abs() < 1e-10);
95    }
96    #[test]
97    fn brainscales_bounded() {
98        let mut n = BrainScaleSAdExNeuron::new();
99        for _ in 0..2000 {
100            n.step(1e4);
101        }
102        assert!(n.v.is_finite());
103    }
104    #[test]
105    fn brainscales_nan_no_panic() {
106        BrainScaleSAdExNeuron::new().step(f64::NAN);
107    }
108}