Skip to main content

microscope_memory/
structural_plasticity.rs

1//! Structural Neuroplasticity — physical network reorganization
2//! Dendritic growth, synaptic pruning, neurogenesis (new neuron-like blocks)
3
4use std::collections::HashMap;
5use std::fs;
6use std::path::Path;
7
8#[derive(Clone, Debug)]
9pub struct Dendrite {
10    pub block_id: u32,
11    pub branches: Vec<u32>, // connected blocks
12    pub branch_count: u32,
13    pub total_length: f32, // accumulated connection strength
14    pub growth_rate: f32,  // 0.0-1.0
15    pub pruning_state: u8, // 0=active, 1=marked_for_pruning, 2=pruned
16}
17
18#[derive(Clone, Debug)]
19pub struct NeuronLike {
20    pub id: u64,
21    pub blocks: Vec<u32>,
22    pub dendrite: Dendrite,
23    pub axon_terminals: Vec<u32>,
24    pub activation_history: Vec<f32>, // recent activations
25    pub specialization: String,
26}
27
28pub struct StructuralPlasticity {
29    pub neurons: HashMap<u64, NeuronLike>,
30    pub dendritic_growth_rate: f32,
31    pub pruning_threshold: f32,
32    pub neurogenesis_events: u32,
33}
34
35impl Default for StructuralPlasticity {
36    fn default() -> Self {
37        Self::new()
38    }
39}
40
41impl StructuralPlasticity {
42    pub fn new() -> Self {
43        Self {
44            neurons: HashMap::new(),
45            dendritic_growth_rate: 0.05,
46            pruning_threshold: 0.1,
47            neurogenesis_events: 0,
48        }
49    }
50
51    /// Grow dendrite: add new branches (dendritic growth)
52    pub fn grow_dendrite(&mut self, neuron_id: u64, new_block: u32) -> bool {
53        if let Some(neuron) = self.neurons.get_mut(&neuron_id) {
54            // Check if dendrite can grow
55            if neuron.dendrite.pruning_state == 0 {
56                neuron.dendrite.branches.push(new_block);
57                neuron.dendrite.branch_count += 1;
58                neuron.dendrite.total_length += self.dendritic_growth_rate;
59                return true;
60            }
61        }
62        false
63    }
64
65    /// Mark synapse for pruning (use it or lose it)
66    pub fn mark_for_pruning(&mut self, neuron_id: u64, block_to_remove: u32) {
67        if let Some(neuron) = self.neurons.get_mut(&neuron_id) {
68            if let Some(pos) = neuron
69                .dendrite
70                .branches
71                .iter()
72                .position(|&b| b == block_to_remove)
73            {
74                neuron.dendrite.branches.remove(pos);
75                neuron.dendrite.branch_count = neuron.dendrite.branch_count.saturating_sub(1);
76                neuron.dendrite.total_length -= 0.05;
77            }
78        }
79    }
80
81    /// Prune inactive branches
82    pub fn prune_inactive_branches(&mut self, neuron_id: u64) -> u32 {
83        let mut pruned = 0;
84
85        if let Some(neuron) = self.neurons.get_mut(&neuron_id) {
86            let avg_activation = if neuron.activation_history.is_empty() {
87                0.0
88            } else {
89                neuron.activation_history.iter().sum::<f32>()
90                    / neuron.activation_history.len() as f32
91            };
92
93            neuron
94                .dendrite
95                .branches
96                .retain(|_| avg_activation > self.pruning_threshold);
97            let new_count = neuron.dendrite.branches.len() as u32;
98            pruned = neuron.dendrite.branch_count.saturating_sub(new_count);
99            neuron.dendrite.branch_count = new_count;
100        }
101
102        pruned
103    }
104
105    /// Neurogenesis: create new neuron-like structure
106    pub fn neurogenesis(&mut self, seed_blocks: Vec<u32>, specialization: &str) -> u64 {
107        let neuron_id = Self::neuron_hash(&seed_blocks);
108
109        let dendrite = Dendrite {
110            block_id: seed_blocks[0],
111            branches: seed_blocks.clone(),
112            branch_count: seed_blocks.len() as u32,
113            total_length: seed_blocks.len() as f32 * 0.1,
114            growth_rate: 0.05,
115            pruning_state: 0,
116        };
117
118        let neuron = NeuronLike {
119            id: neuron_id,
120            blocks: seed_blocks,
121            dendrite,
122            axon_terminals: Vec::new(),
123            activation_history: Vec::new(),
124            specialization: specialization.to_string(),
125        };
126
127        self.neurons.insert(neuron_id, neuron);
128        self.neurogenesis_events += 1;
129        neuron_id
130    }
131
132    /// Record activation (for pruning decisions)
133    pub fn record_activation(&mut self, neuron_id: u64, strength: f32) {
134        if let Some(neuron) = self.neurons.get_mut(&neuron_id) {
135            neuron.activation_history.push(strength);
136            if neuron.activation_history.len() > 100 {
137                neuron.activation_history.remove(0); // sliding window
138            }
139        }
140    }
141
142    /// Calculate dendritic complexity
143    pub fn dendritic_complexity(&self, neuron_id: u64) -> f32 {
144        if let Some(neuron) = self.neurons.get(&neuron_id) {
145            (neuron.dendrite.branch_count as f32 * neuron.dendrite.total_length).min(1.0)
146        } else {
147            0.0
148        }
149    }
150
151    /// Get network statistics
152    pub fn stats(&self) -> (u32, u32, f32, u32) {
153        let total_neurons = self.neurons.len() as u32;
154        let total_branches: u32 = self.neurons.values().map(|n| n.dendrite.branch_count).sum();
155        let avg_dendrite_length = if self.neurons.is_empty() {
156            0.0
157        } else {
158            self.neurons
159                .values()
160                .map(|n| n.dendrite.total_length)
161                .sum::<f32>()
162                / self.neurons.len() as f32
163        };
164
165        (
166            total_neurons,
167            total_branches,
168            avg_dendrite_length,
169            self.neurogenesis_events,
170        )
171    }
172
173    /// Get most specialized neurons
174    pub fn specialized_neurons(&self) -> Vec<(u64, &str, u32)> {
175        let mut specialized: Vec<_> = self
176            .neurons
177            .iter()
178            .map(|(id, n)| (*id, n.specialization.as_str(), n.dendrite.branch_count))
179            .collect();
180        specialized.sort_by_key(|b| std::cmp::Reverse(b.2));
181        specialized
182    }
183
184    pub fn save(&self, dir: &Path) -> Result<(), String> {
185        let path = dir.join("structural_plasticity.bin");
186        let mut data = Vec::new();
187
188        data.extend_from_slice(b"STPLS");
189        data.push(1);
190
191        // Neurons
192        let neuron_count = self.neurons.len() as u32;
193        data.extend_from_slice(&neuron_count.to_le_bytes());
194        for neuron in self.neurons.values() {
195            data.extend_from_slice(&neuron.id.to_le_bytes());
196
197            let blocks_count = neuron.blocks.len() as u16;
198            data.extend_from_slice(&blocks_count.to_le_bytes());
199            for &block in &neuron.blocks {
200                data.extend_from_slice(&block.to_le_bytes());
201            }
202
203            // Dendrite
204            data.extend_from_slice(&neuron.dendrite.block_id.to_le_bytes());
205            data.extend_from_slice(&neuron.dendrite.growth_rate.to_le_bytes());
206            data.push(neuron.dendrite.pruning_state);
207
208            let branch_count = neuron.dendrite.branches.len() as u16;
209            data.extend_from_slice(&branch_count.to_le_bytes());
210            for &branch in &neuron.dendrite.branches {
211                data.extend_from_slice(&branch.to_le_bytes());
212            }
213
214            // Specialization
215            let spec_bytes = neuron.specialization.as_bytes();
216            data.push(spec_bytes.len() as u8);
217            data.extend_from_slice(spec_bytes);
218        }
219
220        data.extend_from_slice(&self.neurogenesis_events.to_le_bytes());
221
222        let tmp_path = dir.join("structural_plasticity.bin.tmp");
223        fs::write(&tmp_path, data).map_err(|e| e.to_string())?;
224        fs::rename(&tmp_path, &path).map_err(|e| e.to_string())
225    }
226
227    pub fn load(dir: &Path) -> Result<Self, String> {
228        let path = dir.join("structural_plasticity.bin");
229        if !path.exists() {
230            return Ok(Self::new());
231        }
232
233        let data = fs::read(&path).map_err(|e| e.to_string())?;
234        if data.len() < 5 || &data[0..5] != b"STPLS" {
235            return Ok(Self::new());
236        }
237
238        let mut idx = 6;
239        let mut neurons = HashMap::new();
240
241        // Read neurons
242        if idx + 4 <= data.len() {
243            let neuron_count =
244                u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]])
245                    as usize;
246            idx += 4;
247
248            for _ in 0..neuron_count {
249                if idx + 10 > data.len() {
250                    break;
251                }
252
253                let neuron_id = u64::from_le_bytes([
254                    data[idx],
255                    data[idx + 1],
256                    data[idx + 2],
257                    data[idx + 3],
258                    data[idx + 4],
259                    data[idx + 5],
260                    data[idx + 6],
261                    data[idx + 7],
262                ]);
263                idx += 8;
264
265                let blocks_count = u16::from_le_bytes([data[idx], data[idx + 1]]) as usize;
266                idx += 2;
267
268                let mut blocks = Vec::new();
269                for _ in 0..blocks_count {
270                    if idx + 4 > data.len() {
271                        break;
272                    }
273                    let block = u32::from_le_bytes([
274                        data[idx],
275                        data[idx + 1],
276                        data[idx + 2],
277                        data[idx + 3],
278                    ]);
279                    blocks.push(block);
280                    idx += 4;
281                }
282
283                if idx + 13 > data.len() {
284                    break;
285                }
286                let dendrite_block_id =
287                    u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
288                idx += 4;
289
290                let growth_rate =
291                    f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
292                idx += 4;
293
294                let pruning_state = data[idx];
295                idx += 1;
296
297                let branch_count = u16::from_le_bytes([data[idx], data[idx + 1]]) as usize;
298                idx += 2;
299
300                let mut branches = Vec::new();
301                for _ in 0..branch_count {
302                    if idx + 4 > data.len() {
303                        break;
304                    }
305                    let branch = u32::from_le_bytes([
306                        data[idx],
307                        data[idx + 1],
308                        data[idx + 2],
309                        data[idx + 3],
310                    ]);
311                    branches.push(branch);
312                    idx += 4;
313                }
314
315                if idx >= data.len() {
316                    break;
317                }
318                let spec_len = data[idx] as usize;
319                idx += 1;
320
321                if idx + spec_len > data.len() {
322                    break;
323                }
324                let specialization =
325                    String::from_utf8_lossy(&data[idx..idx + spec_len]).to_string();
326                idx += spec_len;
327
328                let dendrite = Dendrite {
329                    block_id: dendrite_block_id,
330                    branches,
331                    branch_count: branch_count as u32,
332                    total_length: branch_count as f32 * 0.1,
333                    growth_rate,
334                    pruning_state,
335                };
336
337                neurons.insert(
338                    neuron_id,
339                    NeuronLike {
340                        id: neuron_id,
341                        blocks,
342                        dendrite,
343                        axon_terminals: Vec::new(),
344                        activation_history: Vec::new(),
345                        specialization,
346                    },
347                );
348            }
349        }
350
351        let mut neurogenesis_events = 0;
352        if idx + 4 <= data.len() {
353            neurogenesis_events =
354                u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
355        }
356
357        Ok(Self {
358            neurons,
359            dendritic_growth_rate: 0.05,
360            pruning_threshold: 0.1,
361            neurogenesis_events,
362        })
363    }
364
365    pub fn load_or_init(dir: &Path) -> Self {
366        Self::load(dir).unwrap_or_else(|_| Self::new())
367    }
368
369    fn neuron_hash(blocks: &[u32]) -> u64 {
370        let mut hash: u64 = 0xcbf29ce484222325;
371        for &block in blocks.iter().take(10) {
372            hash = hash.wrapping_mul(0x100000001b3) ^ (block as u64);
373        }
374        hash
375    }
376}