sc_neurocore_engine/
rall_dendrite.rs1pub struct RallDendriteRust {
13 pub n_branches: usize,
14 pub branch_length: usize,
15 #[allow(dead_code)]
16 tau: f64,
17 coupling: f64,
18 #[allow(dead_code)]
19 dt: f64,
20 decay: f64,
21 dt_over_tau: f64,
22 attenuation: Vec<f64>,
23 v: Vec<Vec<f64>>,
25 pub soma_v: f64,
26}
27
28impl RallDendriteRust {
29 pub fn new(n_branches: usize, branch_length: usize, tau: f64, coupling: f64, dt: f64) -> Self {
30 let decay = (-dt / tau).exp();
31 let dt_over_tau = dt / tau;
32 let parent_d = (n_branches as f64).powf(2.0 / 3.0);
34 let attenuation: Vec<f64> = (0..n_branches)
35 .map(|_| (1.0 / parent_d).powf(1.5))
36 .collect();
37
38 Self {
39 n_branches,
40 branch_length,
41 tau,
42 coupling,
43 dt,
44 decay,
45 dt_over_tau,
46 attenuation,
47 v: vec![vec![0.0; branch_length]; n_branches],
48 soma_v: 0.0,
49 }
50 }
51
52 pub fn step(&mut self, branch_inputs: &[f64]) -> f64 {
54 let nb = self.n_branches;
55 let bl = self.branch_length;
56
57 for b in 0..nb {
59 for k in 0..bl {
60 self.v[b][k] *= self.decay;
61 }
62 }
63
64 for b in 0..nb.min(branch_inputs.len()) {
66 self.v[b][bl - 1] += branch_inputs[b] * self.dt_over_tau;
67 }
68
69 for k in (1..bl).rev() {
71 for b in 0..nb {
72 let flow = self.coupling * (self.v[b][k] - self.v[b][k - 1]);
73 self.v[b][k] -= flow;
74 self.v[b][k - 1] += flow;
75 }
76 }
77
78 let mut soma_input = 0.0;
80 for b in 0..nb {
81 soma_input += self.v[b][0] * self.attenuation[b];
82 }
83 self.soma_v = self.decay * self.soma_v + soma_input * self.dt_over_tau;
84 self.soma_v
85 }
86
87 pub fn reset(&mut self) {
88 for b in &mut self.v {
89 b.fill(0.0);
90 }
91 self.soma_v = 0.0;
92 }
93
94 pub fn branch_voltages(&self) -> Vec<Vec<f64>> {
95 self.v.clone()
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_initial_zero() {
105 let d = RallDendriteRust::new(4, 3, 10.0, 0.5, 1.0);
106 assert_eq!(d.soma_v, 0.0);
107 assert!(d.v.iter().all(|b| b.iter().all(|&v| v == 0.0)));
108 }
109
110 #[test]
111 fn test_input_reaches_soma() {
112 let mut d = RallDendriteRust::new(2, 3, 10.0, 0.5, 1.0);
113 for _ in 0..20 {
114 d.step(&[1.0, 0.0]);
115 }
116 assert!(d.soma_v > 0.0);
117 }
118
119 #[test]
120 fn test_more_branches_more_input() {
121 let mut d1 = RallDendriteRust::new(4, 2, 10.0, 0.5, 1.0);
122 let mut d2 = RallDendriteRust::new(4, 2, 10.0, 0.5, 1.0);
123 for _ in 0..20 {
124 d1.step(&[1.0, 0.0, 0.0, 0.0]);
125 d2.step(&[1.0, 1.0, 1.0, 1.0]);
126 }
127 assert!(d2.soma_v > d1.soma_v);
128 }
129
130 #[test]
131 fn test_reset() {
132 let mut d = RallDendriteRust::new(2, 2, 10.0, 0.5, 1.0);
133 d.step(&[5.0, 5.0]);
134 d.reset();
135 assert_eq!(d.soma_v, 0.0);
136 assert!(d.v.iter().all(|b| b.iter().all(|&v| v == 0.0)));
137 }
138
139 #[test]
140 fn test_distal_higher_than_proximal() {
141 let mut d = RallDendriteRust::new(1, 5, 20.0, 0.3, 1.0);
142 for _ in 0..10 {
143 d.step(&[2.0]);
144 }
145 let bv = &d.v[0];
146 assert!(bv[4] > bv[0], "Distal should be higher than proximal");
147 }
148}