sc_neurocore_engine/neuron/
lapicque.rs1#[derive(Clone, Debug)]
11pub struct LapicqueNeuron {
12 pub v: f64,
13 pub v_rest: f64,
14 pub v_reset: f64,
15 pub v_threshold: f64,
16 pub tau: f64,
17 pub resistance: f64,
18 pub dt: f64,
19 pub capacitance: f64,
20 pub series_resistance: f64,
21 pub polarization_resistance: f64,
22 pub excited: bool,
23 pub source_profile: bool,
24}
25
26pub type LapicqueCompleteTrace = (Vec<f64>, Vec<u8>, f64, bool);
28
29impl LapicqueNeuron {
30 pub fn new(tau: f64, resistance: f64, threshold: f64, dt: f64) -> Self {
32 Self {
33 v: 0.0,
34 v_rest: 0.0,
35 v_reset: 0.0,
36 v_threshold: threshold,
37 tau,
38 resistance,
39 dt,
40 capacitance: 1.1,
41 series_resistance: 10.0,
42 polarization_resistance: 1.0,
43 excited: false,
44 source_profile: false,
45 }
46 }
47
48 pub fn lapicque_1907() -> Self {
50 let mut state = Self::new(20.0, 1.0, 1.0, 0.01);
51 state.source_profile = true;
52 state
53 }
54
55 pub fn valid(&self) -> bool {
57 if !self.v.is_finite()
58 || !self.v_threshold.is_finite()
59 || self.v_threshold <= 0.0
60 || !self.dt.is_finite()
61 || self.dt <= 0.0
62 {
63 return false;
64 }
65 if self.source_profile {
66 return (self.excited || self.v < self.v_threshold)
67 && self.capacitance.is_finite()
68 && self.capacitance > 0.0
69 && self.series_resistance.is_finite()
70 && self.series_resistance > 0.0
71 && self.polarization_resistance.is_finite()
72 && self.polarization_resistance > 0.0;
73 }
74 !self.excited
75 && self.v_rest.is_finite()
76 && self.v_reset.is_finite()
77 && self.v_threshold > self.v_rest
78 && self.v_threshold > self.v_reset
79 && self.v < self.v_threshold
80 && self.tau.is_finite()
81 && self.tau > 0.0
82 && self.resistance.is_finite()
83 && self.resistance > 0.0
84 }
85
86 pub fn try_step(&mut self, drive: f64) -> Result<i32, &'static str> {
88 if !drive.is_finite() {
89 return Err("Lapicque drive must be finite");
90 }
91 if !self.valid() {
92 return Err("Lapicque state violates its profile contract");
93 }
94
95 let (v_inf, decay) = if self.source_profile {
96 let total_resistance = self.series_resistance + self.polarization_resistance;
97 let beta = self.capacitance * self.series_resistance * self.polarization_resistance
98 / total_resistance;
99 (
100 drive * self.polarization_resistance / total_resistance,
101 (-self.dt / beta).exp(),
102 )
103 } else {
104 (
105 self.v_rest + self.resistance * drive,
106 (-self.dt / self.tau).exp(),
107 )
108 };
109 let next_v = v_inf + (self.v - v_inf) * decay;
110 if !v_inf.is_finite() || !decay.is_finite() || !next_v.is_finite() {
111 return Err("Lapicque candidate must remain finite");
112 }
113
114 if self.source_profile {
115 let event = !self.excited && next_v >= self.v_threshold;
116 self.v = next_v;
117 if event {
118 self.excited = true;
119 return Ok(1);
120 }
121 return Ok(0);
122 }
123
124 if next_v >= self.v_threshold {
125 self.v = self.v_reset;
126 Ok(1)
127 } else {
128 self.v = next_v;
129 Ok(0)
130 }
131 }
132
133 pub fn step(&mut self, drive: f64) -> i32 {
135 self.try_step(drive).unwrap_or(0)
136 }
137
138 pub fn simulate_complete(
140 &self,
141 n_steps: usize,
142 drive: f64,
143 ) -> Result<LapicqueCompleteTrace, &'static str> {
144 if !drive.is_finite() || !self.valid() {
145 return Err("invalid Lapicque batch contract");
146 }
147 let mut candidate = self.clone();
148 let mut voltage = Vec::with_capacity(n_steps);
149 let mut events = Vec::with_capacity(n_steps);
150 for _ in 0..n_steps {
151 let event = candidate.try_step(drive)?;
152 voltage.push(candidate.v);
153 events.push(event as u8);
154 }
155 Ok((voltage, events, candidate.v, candidate.excited))
156 }
157
158 pub fn reset(&mut self) {
160 self.v = if self.source_profile {
161 0.0
162 } else {
163 self.v_rest
164 };
165 self.excited = false;
166 }
167}
168
169#[cfg(test)]
170#[path = "lapicque_tests.rs"]
171mod tests;