sc_neurocore_engine/neurons/trivial/
integer_qif.rs1#[derive(Clone, Debug)]
11pub struct IntegerQIFNeuron {
12 pub v: i64,
13 pub v_rest: i64,
14 pub v_threshold: i64,
15 pub v_reset: i64,
16 pub a: i64,
17 pub b: i64,
18 pub v_max: i64,
19 pub v_min: i64,
20}
21
22impl IntegerQIFNeuron {
23 #[allow(clippy::too_many_arguments)]
24 pub fn with_parameters(
25 v: i32,
26 v_rest: i32,
27 v_threshold: i32,
28 v_reset: i32,
29 a: i32,
30 b: i32,
31 v_max: i32,
32 v_min: i32,
33 ) -> Result<Self, &'static str> {
34 let neuron = Self {
35 v: i64::from(v),
36 v_rest: i64::from(v_rest),
37 v_threshold: i64::from(v_threshold),
38 v_reset: i64::from(v_reset),
39 a: i64::from(a),
40 b: i64::from(b),
41 v_max: i64::from(v_max),
42 v_min: i64::from(v_min),
43 };
44 if neuron.valid() {
45 Ok(neuron)
46 } else {
47 Err("invalid IQIF state or parameter ordering")
48 }
49 }
50
51 pub fn valid(&self) -> bool {
52 let i32_range = |value: i64| i64::from(i32::MIN) <= value && value <= i64::from(i32::MAX);
53 [
54 self.v,
55 self.v_rest,
56 self.v_threshold,
57 self.v_reset,
58 self.a,
59 self.b,
60 self.v_max,
61 self.v_min,
62 ]
63 .into_iter()
64 .all(i32_range)
65 && self.a >= 0
66 && self.b >= 0
67 && self.a + self.b > 0
68 && self.v_min < self.v_rest
69 && self.v_rest < self.v_threshold
70 && self.v_threshold < self.v_max
71 && (self.v_min..=self.v_max).contains(&self.v_reset)
72 && (self.v_min..=self.v_max).contains(&self.v)
73 }
74
75 pub fn branch_point(&self) -> i64 {
76 let numerator = self.b * self.v_threshold + self.a * self.v_rest;
77 let denominator = self.a + self.b;
78 if numerator >= 0 {
79 numerator / denominator
80 } else {
81 -((-numerator) / denominator)
82 }
83 }
84
85 pub fn try_step(&mut self, current: i32) -> Result<i32, &'static str> {
86 if !self.valid() {
87 return Err("invalid IQIF runtime state or parameter ordering");
88 }
89 let force = if self.v < self.branch_point() {
90 self.a
91 .checked_mul(self.v_rest - self.v)
92 .ok_or("IQIF restoring force overflowed")?
93 } else {
94 self.b
95 .checked_mul(self.v - self.v_threshold)
96 .ok_or("IQIF restoring force overflowed")?
97 };
98 let candidate = self
99 .v
100 .checked_add(force >> 3)
101 .and_then(|value| value.checked_add(i64::from(current)))
102 .ok_or("IQIF candidate overflowed")?;
103 if candidate > self.v_max {
104 self.v = self.v_reset;
105 Ok(1)
106 } else {
107 self.v = candidate.max(self.v_min);
108 Ok(0)
109 }
110 }
111
112 pub fn step(&mut self, current: i32) -> i32 {
113 self.try_step(current)
114 .expect("IQIF step requires a validated signed-int32 contract")
115 }
116
117 pub fn reset(&mut self) {
118 self.v = self.v_rest;
119 }
120}
121
122impl Default for IntegerQIFNeuron {
123 fn default() -> Self {
124 Self {
125 v: 128,
126 v_rest: 128,
127 v_threshold: 200,
128 v_reset: 128,
129 a: 1,
130 b: 1,
131 v_max: 255,
132 v_min: 0,
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn iqif_fires() {
143 let mut n = IntegerQIFNeuron::default();
144 let total: i32 = (0..400).map(|_| n.step(10)).sum();
145 assert_eq!(total, 26);
146 }
147 #[test]
148 fn iqif_silent_without_input() {
149 let mut n = IntegerQIFNeuron::default();
150 let t: i32 = (0..200).map(|_| n.step(0)).sum();
151 assert_eq!(t, 0);
152 }
153 #[test]
154 fn iqif_reset_clears_state() {
155 let mut n = IntegerQIFNeuron::default();
156 for _ in 0..100 {
157 n.step(10);
158 }
159 n.reset();
160 assert_eq!(n.v, 128);
161 }
162 #[test]
163 fn iqif_bounded() {
164 let mut n = IntegerQIFNeuron::default();
165 for _ in 0..1000 {
166 n.step(10000);
167 }
168 }
170 #[test]
171 fn iqif_negative_no_crash() {
172 let mut n = IntegerQIFNeuron::default();
173 for _ in 0..500 {
174 n.step(-50);
175 }
176 assert_eq!(n.v, 0);
177 }
178 #[test]
179 fn iqif_matches_source_tutorial_prefix_and_period() {
180 let mut n = IntegerQIFNeuron::default();
181 let mut trace = Vec::new();
182 let mut spike_steps = Vec::new();
183 for index in 0..400 {
184 if n.step(10) == 1 {
185 spike_steps.push(index);
186 }
187 trace.push(n.v);
188 }
189 assert_eq!(
190 &trace[..15],
191 &[138, 146, 153, 159, 165, 170, 176, 183, 190, 198, 207, 217, 229, 242, 128]
192 );
193 assert_eq!(spike_steps, (14..400).step_by(15).collect::<Vec<_>>());
194 assert_eq!(trace.last(), Some(&198));
195 }
196}