1use super::{
24 neuron::{LIFNeuron, NeuronConfig, NeuronPopulation},
25 network::{SpikingNetwork, NetworkConfig, LayerConfig},
26 SimTime, Spike,
27};
28use crate::graph::{DynamicGraph, VertexId};
29use std::collections::HashMap;
30
31#[derive(Debug, Clone)]
33pub struct MorphConfig {
34 pub grid_size: usize,
36 pub growth_threshold: f64,
38 pub prune_threshold: f64,
40 pub prune_weight: f64,
42 pub target_connectivity: f64,
44 pub stability_epsilon: f64,
46 pub diffusion_sigma: f64,
48 pub max_steps: usize,
50 pub dt: f64,
52}
53
54impl Default for MorphConfig {
55 fn default() -> Self {
56 Self {
57 grid_size: 10,
58 growth_threshold: 0.6,
59 prune_threshold: 0.2,
60 prune_weight: 0.1,
61 target_connectivity: 0.5,
62 stability_epsilon: 0.01,
63 diffusion_sigma: 2.0,
64 max_steps: 1000,
65 dt: 1.0,
66 }
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq)]
72pub enum TuringPattern {
73 Spots,
75 Stripes,
77 Labyrinth,
79 Uniform,
81}
82
83#[derive(Debug, Clone)]
85pub struct GrowthRules {
86 pub growth_threshold: f64,
88 pub prune_threshold: f64,
90 pub prune_weight: f64,
92 pub target_connectivity: f64,
94 pub pattern: TuringPattern,
96}
97
98impl Default for GrowthRules {
99 fn default() -> Self {
100 Self {
101 growth_threshold: 0.6,
102 prune_threshold: 0.2,
103 prune_weight: 0.1,
104 target_connectivity: 0.5,
105 pattern: TuringPattern::Spots,
106 }
107 }
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
112pub struct GridPosition {
113 pub x: usize,
115 pub y: usize,
117}
118
119impl GridPosition {
120 pub fn new(x: usize, y: usize) -> Self {
122 Self { x, y }
123 }
124
125 pub fn to_index(&self, grid_size: usize) -> usize {
127 self.y * grid_size + self.x
128 }
129
130 pub fn from_index(idx: usize, grid_size: usize) -> Self {
132 Self {
133 x: idx % grid_size,
134 y: idx / grid_size,
135 }
136 }
137
138 pub fn distance(&self, other: &GridPosition) -> f64 {
140 let dx = self.x as f64 - other.x as f64;
141 let dy = self.y as f64 - other.y as f64;
142 (dx * dx + dy * dy).sqrt()
143 }
144}
145
146#[derive(Debug, Clone)]
148pub struct DiffusionKernel {
149 pub sigma: f64,
151 pub excite_range: f64,
153 pub inhibit_range: f64,
155}
156
157impl DiffusionKernel {
158 pub fn new(sigma: f64) -> Self {
160 Self {
161 sigma,
162 excite_range: sigma,
163 inhibit_range: sigma * 3.0,
164 }
165 }
166
167 pub fn weight(&self, pos1: &GridPosition, pos2: &GridPosition) -> (f64, f64) {
169 let d = pos1.distance(pos2);
170
171 let excite = (-d * d / (2.0 * self.excite_range * self.excite_range)).exp();
173 let inhibit = (-d * d / (2.0 * self.inhibit_range * self.inhibit_range)).exp() * 0.5;
174
175 (excite, inhibit)
176 }
177}
178
179#[derive(Debug, Clone)]
181pub struct MorphNeuronPair {
182 pub excitatory: LIFNeuron,
184 pub inhibitory: LIFNeuron,
186 pub position: GridPosition,
188}
189
190impl MorphNeuronPair {
191 pub fn new(id: usize, position: GridPosition) -> Self {
193 let e_config = NeuronConfig {
194 tau_membrane: 15.0,
195 threshold: 0.8,
196 ..NeuronConfig::default()
197 };
198
199 let i_config = NeuronConfig {
200 tau_membrane: 25.0,
201 threshold: 0.6,
202 ..NeuronConfig::default()
203 };
204
205 Self {
206 excitatory: LIFNeuron::with_config(id * 2, e_config),
207 inhibitory: LIFNeuron::with_config(id * 2 + 1, i_config),
208 position,
209 }
210 }
211
212 pub fn net_activation(&self) -> f64 {
214 self.excitatory.membrane_potential() - self.inhibitory.membrane_potential()
215 }
216}
217
218pub struct MorphogeneticSNN {
220 neurons: Vec<MorphNeuronPair>,
222 grid_size: usize,
224 diffusion_kernel: DiffusionKernel,
226 graph: DynamicGraph,
228 growth_rules: GrowthRules,
230 config: MorphConfig,
232 time: SimTime,
234 mincut_history: Vec<f64>,
236 is_mature: bool,
238}
239
240const MAX_GRID_SIZE: usize = 256;
242
243impl MorphogeneticSNN {
244 pub fn new(config: MorphConfig) -> Self {
249 let grid_size = config.grid_size.min(MAX_GRID_SIZE);
251 let n = grid_size * grid_size;
252
253 let neurons: Vec<_> = (0..n)
255 .map(|i| {
256 let pos = GridPosition::from_index(i, grid_size);
257 MorphNeuronPair::new(i, pos)
258 })
259 .collect();
260
261 let graph = DynamicGraph::new();
263 for i in 0..n {
264 graph.add_vertex(i as u64);
265 }
266
267 for y in 0..grid_size {
269 for x in 0..grid_size {
270 let i = y * grid_size + x;
271
272 if x + 1 < grid_size {
273 let j = y * grid_size + (x + 1);
274 let _ = graph.insert_edge(i as u64, j as u64, 0.5);
275 }
276
277 if y + 1 < grid_size {
278 let j = (y + 1) * grid_size + x;
279 let _ = graph.insert_edge(i as u64, j as u64, 0.5);
280 }
281 }
282 }
283
284 let growth_rules = GrowthRules {
285 growth_threshold: config.growth_threshold,
286 prune_threshold: config.prune_threshold,
287 prune_weight: config.prune_weight,
288 target_connectivity: config.target_connectivity,
289 ..GrowthRules::default()
290 };
291
292 Self {
293 neurons,
294 grid_size,
295 diffusion_kernel: DiffusionKernel::new(config.diffusion_sigma),
296 graph,
297 growth_rules,
298 config,
299 time: 0.0,
300 mincut_history: Vec::new(),
301 is_mature: false,
302 }
303 }
304
305 pub fn develop_step(&mut self) {
307 let dt = self.config.dt;
308 self.time += dt;
309
310 let n = self.neurons.len();
311
312 let mut activities = vec![0.0; n];
314
315 for i in 0..n {
316 let pos_i = self.neurons[i].position;
318 let mut e_input = 0.0;
319 let mut i_input = 0.0;
320
321 for j in 0..n {
322 if i == j {
323 continue;
324 }
325
326 let pos_j = &self.neurons[j].position;
327 let (e_weight, i_weight) = self.diffusion_kernel.weight(&pos_i, pos_j);
328
329 e_input += e_weight * self.neurons[j].excitatory.membrane_potential();
330 i_input += i_weight * self.neurons[j].inhibitory.membrane_potential();
331 }
332
333 let e_current = e_input - self.neurons[i].inhibitory.membrane_potential();
335 let i_current = i_input + self.neurons[i].excitatory.membrane_potential();
336
337 self.neurons[i].excitatory.step(e_current, dt, self.time);
338 self.neurons[i].inhibitory.step(i_current, dt, self.time);
339
340 activities[i] = self.neurons[i].net_activation();
341 }
342
343 self.apply_growth_rules(&activities);
345
346 let mincut = self.estimate_mincut();
348 self.mincut_history.push(mincut);
349
350 if self.mincut_history.len() > 100 {
351 self.mincut_history.remove(0);
352 }
353
354 self.is_mature = self.check_maturity(mincut);
356 }
357
358 fn apply_growth_rules(&mut self, activities: &[f64]) {
360 let n = self.neurons.len();
361
362 let mut to_add: Vec<(VertexId, VertexId)> = Vec::new();
364 let mut to_remove: Vec<(VertexId, VertexId)> = Vec::new();
365
366 for i in 0..n {
367 let pos_i = &self.neurons[i].position;
368
369 if activities[i] > self.growth_rules.growth_threshold {
371 for j in 0..n {
373 if i == j {
374 continue;
375 }
376
377 let pos_j = &self.neurons[j].position;
378 let dist = pos_i.distance(pos_j);
379
380 if dist <= self.diffusion_kernel.excite_range * 2.0 {
382 let u = i as u64;
383 let v = j as u64;
384
385 if !self.graph.has_edge(u, v) {
386 to_add.push((u, v));
387 }
388 }
389 }
390 }
391
392 if activities[i] < self.growth_rules.prune_threshold {
394 let u = i as u64;
395
396 for (v, _) in self.graph.neighbors(u) {
397 if let Some(edge) = self.graph.get_edge(u, v) {
398 if edge.weight < self.growth_rules.prune_weight {
399 to_remove.push((u, v));
400 }
401 }
402 }
403 }
404 }
405
406 for (u, v) in to_add {
408 if !self.graph.has_edge(u, v) {
409 let _ = self.graph.insert_edge(u, v, 0.5);
410 }
411 }
412
413 for (u, v) in to_remove {
414 let _ = self.graph.delete_edge(u, v);
415 }
416 }
417
418 fn estimate_mincut(&self) -> f64 {
420 if self.graph.num_vertices() == 0 {
421 return 0.0;
422 }
423
424 self.graph.vertices()
426 .iter()
427 .map(|&v| self.graph.degree(v) as f64)
428 .fold(f64::INFINITY, f64::min)
429 }
430
431 fn check_maturity(&self, current_mincut: f64) -> bool {
433 let connectivity = self.graph.num_edges() as f64 /
435 (self.graph.num_vertices() * (self.graph.num_vertices() - 1) / 2).max(1) as f64;
436
437 if connectivity < self.growth_rules.target_connectivity {
438 return false;
439 }
440
441 if self.mincut_history.len() < 20 {
443 return false;
444 }
445
446 let recent: Vec<_> = self.mincut_history.iter().rev().take(20).cloned().collect();
447 let mean = recent.iter().sum::<f64>() / recent.len() as f64;
448 let variance = recent.iter().map(|&x| (x - mean).powi(2)).sum::<f64>() / recent.len() as f64;
449
450 variance.sqrt() < self.config.stability_epsilon
451 }
452
453 pub fn develop(&mut self) -> usize {
455 let mut steps = 0;
456
457 while steps < self.config.max_steps && !self.is_mature {
458 self.develop_step();
459 steps += 1;
460 }
461
462 steps
463 }
464
465 pub fn graph(&self) -> &DynamicGraph {
467 &self.graph
468 }
469
470 pub fn grid_size(&self) -> usize {
472 self.grid_size
473 }
474
475 pub fn is_mature(&self) -> bool {
477 self.is_mature
478 }
479
480 pub fn activation_pattern(&self) -> Vec<f64> {
482 self.neurons.iter().map(|n| n.net_activation()).collect()
483 }
484
485 pub fn detect_pattern(&self) -> TuringPattern {
487 let activations = self.activation_pattern();
488
489 let mean = activations.iter().sum::<f64>() / activations.len() as f64;
491
492 let mut local_corr = 0.0;
493 let mut global_corr = 0.0;
494 let mut count = 0;
495
496 for i in 0..self.neurons.len() {
497 for j in (i + 1)..self.neurons.len() {
498 let dist = self.neurons[i].position.distance(&self.neurons[j].position);
499 let prod = (activations[i] - mean) * (activations[j] - mean);
500
501 if dist <= 2.0 {
502 local_corr += prod;
503 }
504 global_corr += prod / dist.max(0.1);
505 count += 1;
506 }
507 }
508
509 local_corr /= count.max(1) as f64;
510 global_corr /= count.max(1) as f64;
511
512 if local_corr > 0.3 && global_corr < 0.1 {
514 TuringPattern::Spots
515 } else if local_corr > 0.2 && global_corr > 0.2 {
516 TuringPattern::Stripes
517 } else if local_corr > 0.1 {
518 TuringPattern::Labyrinth
519 } else {
520 TuringPattern::Uniform
521 }
522 }
523
524 pub fn reset(&mut self) {
526 for pair in &mut self.neurons {
527 pair.excitatory.reset();
528 pair.inhibitory.reset();
529 }
530
531 self.graph.clear();
532 for i in 0..self.neurons.len() {
533 self.graph.add_vertex(i as u64);
534 }
535
536 for y in 0..self.grid_size {
538 for x in 0..self.grid_size {
539 let i = y * self.grid_size + x;
540
541 if x + 1 < self.grid_size {
542 let j = y * self.grid_size + (x + 1);
543 let _ = self.graph.insert_edge(i as u64, j as u64, 0.5);
544 }
545
546 if y + 1 < self.grid_size {
547 let j = (y + 1) * self.grid_size + x;
548 let _ = self.graph.insert_edge(i as u64, j as u64, 0.5);
549 }
550 }
551 }
552
553 self.time = 0.0;
554 self.mincut_history.clear();
555 self.is_mature = false;
556 }
557}
558
559#[cfg(test)]
560mod tests {
561 use super::*;
562
563 #[test]
564 fn test_grid_position() {
565 let pos1 = GridPosition::new(0, 0);
566 let pos2 = GridPosition::new(3, 4);
567
568 assert_eq!(pos1.distance(&pos2), 5.0);
569 assert_eq!(pos1.to_index(10), 0);
570 assert_eq!(pos2.to_index(10), 43);
571 }
572
573 #[test]
574 fn test_diffusion_kernel() {
575 let kernel = DiffusionKernel::new(2.0);
576
577 let pos1 = GridPosition::new(0, 0);
578 let pos2 = GridPosition::new(1, 0);
579 let pos3 = GridPosition::new(5, 0);
580
581 let (e1, i1) = kernel.weight(&pos1, &pos2);
582 let (e2, i2) = kernel.weight(&pos1, &pos3);
583
584 assert!(e1 > e2);
586 }
587
588 #[test]
589 fn test_morphogenetic_snn_creation() {
590 let mut config = MorphConfig::default();
591 config.grid_size = 5;
592
593 let snn = MorphogeneticSNN::new(config);
594
595 assert_eq!(snn.neurons.len(), 25);
596 assert!(!snn.is_mature());
597 }
598
599 #[test]
600 fn test_morphogenetic_development() {
601 let mut config = MorphConfig::default();
602 config.grid_size = 5;
603 config.max_steps = 100;
604
605 let mut snn = MorphogeneticSNN::new(config);
606
607 let steps = snn.develop();
608 assert!(steps <= 100);
609
610 assert!(snn.graph().num_edges() > 0);
612 }
613
614 #[test]
615 fn test_pattern_detection() {
616 let mut config = MorphConfig::default();
617 config.grid_size = 5;
618
619 let snn = MorphogeneticSNN::new(config);
620 let pattern = snn.detect_pattern();
621
622 assert!(pattern == TuringPattern::Uniform ||
624 pattern == TuringPattern::Spots ||
625 pattern == TuringPattern::Stripes ||
626 pattern == TuringPattern::Labyrinth);
627 }
628}