sc_neurocore_engine/neurons/
ibarz_tanaka_map.rs1#[derive(Clone, Debug)]
13pub struct IbarzTanakaMapNeuron {
14 pub v: f64,
15 pub u: f64,
16 pub alpha: f64,
17 pub mu: f64,
18 pub sigma: f64,
19}
20
21impl IbarzTanakaMapNeuron {
22 pub fn new() -> Self {
23 Self {
24 v: -1.0,
25 u: -0.1,
26 alpha: 1.0,
27 mu: 0.001,
28 sigma: 0.1,
29 }
30 }
31
32 fn parameters_are_valid(&self) -> bool {
33 self.alpha.is_finite()
34 && self.mu.is_finite()
35 && self.sigma.is_finite()
36 && self.alpha > 0.0
37 && self.mu > 0.0
38 }
39
40 fn candidate(&self, current: f64) -> Result<(f64, f64, i32), &'static str> {
41 let lower = -1.0 - self.alpha / 2.0;
42 let upper = 1.0 + current + self.u;
43 let (v_next, event) = if self.v < lower {
44 (
45 -(self.alpha * self.alpha) / 4.0 - self.alpha + current + self.u,
46 0,
47 )
48 } else if self.v <= 0.0 {
49 (
50 self.alpha * self.v + (self.v + 1.0) * (self.v + 1.0) + current + self.u,
51 0,
52 )
53 } else if self.v < upper {
54 (upper, 0)
55 } else {
56 (-1.0, 1)
57 };
58 let u_next = self.u - self.mu * (self.v + 1.0 - self.sigma);
59 if !v_next.is_finite() || !u_next.is_finite() {
60 return Err("invalid Ibarz-Tanaka map candidate");
61 }
62 Ok((v_next, u_next, event))
63 }
64
65 pub fn try_step(&mut self, current: f64) -> Result<i32, &'static str> {
67 if !self.v.is_finite() || !self.u.is_finite() || !self.parameters_are_valid() {
68 return Err("invalid Ibarz-Tanaka runtime state");
69 }
70 if !current.is_finite() {
71 return Err("invalid Ibarz-Tanaka current");
72 }
73 let (v_next, u_next, event) = self.candidate(current)?;
74 self.v = v_next;
75 self.u = u_next;
76 Ok(event)
77 }
78
79 pub fn step(&mut self, current: f64) -> i32 {
81 self.try_step(current).unwrap_or(0)
82 }
83
84 pub fn simulate(
86 &mut self,
87 n_steps: usize,
88 current: f64,
89 ) -> Result<(Vec<f64>, i64), &'static str> {
90 let mut candidate = self.clone();
91 let mut trace = Vec::with_capacity(n_steps);
92 let mut events = 0_i64;
93 for _ in 0..n_steps {
94 events += i64::from(candidate.try_step(current)?);
95 trace.push(candidate.v);
96 }
97 self.v = candidate.v;
98 self.u = candidate.u;
99 Ok((trace, events))
100 }
101
102 pub fn reset(&mut self) {
103 self.v = -1.0;
104 self.u = -0.1;
105 }
106}
107impl Default for IbarzTanakaMapNeuron {
108 fn default() -> Self {
109 Self::new()
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn ibarz_fires() {
119 let mut n = IbarzTanakaMapNeuron::new();
120 let t: i32 = (0..2000).map(|_| n.step(2.0)).sum();
121 assert!(t > 0);
122 }
123
124 #[test]
125 fn checked_batch_is_complete_and_failure_atomic() {
126 let mut neuron = IbarzTanakaMapNeuron::new();
127 let (trace, events) = neuron.simulate(1_000, 0.2).unwrap();
128 assert_eq!(trace.len(), 1_000);
129 assert_eq!(events, 33);
130 assert_eq!(trace.last().copied(), Some(neuron.v));
131
132 let before = (neuron.v, neuron.u);
133 assert!(neuron.simulate(4, f64::NAN).is_err());
134 assert_eq!((neuron.v, neuron.u), before);
135 }
136
137 #[test]
138 fn earlier_branch_precedence_prevents_false_reset_events() {
139 let mut neuron = IbarzTanakaMapNeuron::new();
140 assert!(neuron.v >= 1.0 - 5.0 + neuron.u);
141 assert_eq!(neuron.try_step(-5.0), Ok(0));
142 assert_ne!(neuron.v, -1.0);
143 }
144}