1use crate::connectome_manager::ConnectomeManager;
22use crate::models::{CorticalArea, CorticalID};
23use crate::types::{BduError, BduResult};
24use feagi_evolutionary::RuntimeGenome;
25use feagi_npu_neural::types::{Precision, QuantizationSpec};
26use parking_lot::RwLock;
27use std::sync::Arc;
28use tracing::{debug, error, info, trace, warn};
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum DevelopmentStage {
33 Initialization,
35 Corticogenesis,
37 Voxelogenesis,
39 Neurogenesis,
41 Synaptogenesis,
43 Completed,
45 Failed,
47}
48
49#[derive(Debug, Clone)]
51pub struct DevelopmentProgress {
52 pub stage: DevelopmentStage,
54 pub progress: u8,
56 pub cortical_areas_created: usize,
58 pub neurons_created: usize,
60 pub synapses_created: usize,
62 pub duration_ms: u64,
64}
65
66impl Default for DevelopmentProgress {
67 fn default() -> Self {
68 Self {
69 stage: DevelopmentStage::Initialization,
70 progress: 0,
71 cortical_areas_created: 0,
72 neurons_created: 0,
73 synapses_created: 0,
74 duration_ms: 0,
75 }
76 }
77}
78
79pub struct Neuroembryogenesis {
87 connectome_manager: Arc<RwLock<ConnectomeManager>>,
89
90 progress: Arc<RwLock<DevelopmentProgress>>,
92
93 start_time: std::time::Instant,
95}
96
97impl Neuroembryogenesis {
98 pub fn new(connectome_manager: Arc<RwLock<ConnectomeManager>>) -> Self {
100 Self {
101 connectome_manager,
102 progress: Arc::new(RwLock::new(DevelopmentProgress::default())),
103 start_time: std::time::Instant::now(),
104 }
105 }
106
107 pub fn get_progress(&self) -> DevelopmentProgress {
109 self.progress.read().clone()
110 }
111
112 fn sync_core_neuron_params(&self, cortical_idx: u32, area: &CorticalArea) -> BduResult<()> {
116 use crate::models::CorticalAreaExt;
117
118 let npu_arc = {
119 let manager = self.connectome_manager.read();
120 manager
121 .get_npu()
122 .cloned()
123 .ok_or_else(|| BduError::Internal("NPU not connected".to_string()))?
124 };
125
126 let mut npu_lock = npu_arc
127 .lock()
128 .map_err(|e| BduError::Internal(format!("Failed to lock NPU: {}", e)))?;
129
130 npu_lock.update_cortical_area_threshold_with_gradient(
131 cortical_idx,
132 area.firing_threshold(),
133 area.firing_threshold_increment_x(),
134 area.firing_threshold_increment_y(),
135 area.firing_threshold_increment_z(),
136 );
137 npu_lock.update_cortical_area_threshold_limit(cortical_idx, area.firing_threshold_limit());
138 npu_lock.update_cortical_area_leak(cortical_idx, area.leak_coefficient());
139 npu_lock.update_cortical_area_excitability(cortical_idx, area.neuron_excitability());
140 npu_lock.update_cortical_area_refractory_period(cortical_idx, area.refractory_period());
141 npu_lock.update_cortical_area_consecutive_fire_limit(
142 cortical_idx,
143 area.consecutive_fire_count() as u16,
144 );
145 npu_lock.update_cortical_area_snooze_period(cortical_idx, area.snooze_period());
146 npu_lock.update_cortical_area_mp_charge_accumulation(
147 cortical_idx,
148 area.mp_charge_accumulation(),
149 );
150
151 Ok(())
152 }
153
154 pub fn add_cortical_areas(
166 &mut self,
167 areas: Vec<CorticalArea>,
168 genome: &RuntimeGenome,
169 ) -> BduResult<(usize, usize)> {
170 info!(target: "feagi-bdu", "🧬 Incrementally adding {} cortical areas", areas.len());
171
172 let mut total_neurons = 0;
173 let mut total_synapses = 0;
174
175 for area in &areas {
177 let mut manager = self.connectome_manager.write();
178 manager.add_cortical_area(area.clone())?;
179 info!(target: "feagi-bdu", " ✓ Added cortical area structure: {}", area.cortical_id.as_base_64());
180 }
181
182 use feagi_structures::genomic::cortical_area::CoreCorticalType;
185 let death_id = CoreCorticalType::Death.to_cortical_id();
186 let power_id = CoreCorticalType::Power.to_cortical_id();
187 let fatigue_id = CoreCorticalType::Fatigue.to_cortical_id();
188
189 let mut core_areas = Vec::new();
190 let mut other_areas = Vec::new();
191
192 for area in &areas {
194 if area.cortical_id == death_id {
195 core_areas.push((0, area)); } else if area.cortical_id == power_id {
197 core_areas.push((1, area)); } else if area.cortical_id == fatigue_id {
199 core_areas.push((2, area)); } else {
201 other_areas.push(area);
202 }
203 }
204
205 core_areas.sort_by_key(|(idx, _)| *idx);
207
208 if !core_areas.is_empty() {
210 info!(target: "feagi-bdu", " 🎯 Creating core area neurons FIRST ({} areas) for deterministic IDs", core_areas.len());
211 for (core_idx, area) in &core_areas {
212 let existing_core_neurons = {
213 let manager = self.connectome_manager.read();
214 let npu = manager.get_npu();
215 match npu {
216 Some(npu_arc) => {
217 let npu_lock = npu_arc.lock();
218 match npu_lock {
219 Ok(npu_guard) => {
220 npu_guard.get_neurons_in_cortical_area(*core_idx).len()
221 }
222 Err(_) => 0,
223 }
224 }
225 None => 0,
226 }
227 };
228
229 if existing_core_neurons > 0 {
230 self.sync_core_neuron_params(*core_idx, area)?;
231 let refreshed = {
232 let manager = self.connectome_manager.read();
233 manager.refresh_neuron_count_for_area(&area.cortical_id)
234 };
235 let count = refreshed.unwrap_or(existing_core_neurons);
236 total_neurons += count;
237 info!(
238 target: "feagi-bdu",
239 " ↪ Skipping core neuron creation for {} (existing={}, idx={})",
240 area.cortical_id.as_base_64(),
241 count,
242 core_idx
243 );
244 continue;
245 }
246 let neurons_created = {
247 let mut manager = self.connectome_manager.write();
248 manager.create_neurons_for_area(&area.cortical_id)
249 };
250
251 match neurons_created {
252 Ok(count) => {
253 total_neurons += count as usize;
254 info!(target: "feagi-bdu", " ✅ Created {} neurons for core area {} (deterministic ID: neuron {})",
255 count, area.cortical_id.as_base_64(), core_idx);
256 }
257 Err(e) => {
258 error!(target: "feagi-bdu", " ❌ FATAL: Failed to create neurons for core area {}: {}", area.cortical_id.as_base_64(), e);
259 return Err(e);
260 }
261 }
262 }
263 }
264
265 for area in &other_areas {
267 let neurons_created = {
268 let mut manager = self.connectome_manager.write();
269 manager.create_neurons_for_area(&area.cortical_id)
270 };
271
272 match neurons_created {
273 Ok(count) => {
274 total_neurons += count as usize;
275 trace!(
276 target: "feagi-bdu",
277 "Created {} neurons for area {}",
278 count,
279 area.cortical_id.as_base_64()
280 );
281 }
282 Err(e) => {
283 error!(target: "feagi-bdu", " ❌ FATAL: Failed to create neurons for {}: {}", area.cortical_id.as_base_64(), e);
284 return Err(e);
286 }
287 }
288 }
289
290 for area in &areas {
292 let has_dstmap = area
294 .properties
295 .get("cortical_mapping_dst")
296 .and_then(|v| v.as_object())
297 .map(|m| !m.is_empty())
298 .unwrap_or(false);
299
300 if !has_dstmap {
301 debug!(target: "feagi-bdu", " No mappings for area {}", area.cortical_id.as_base_64());
302 continue;
303 }
304
305 let synapses_created = {
306 let mut manager = self.connectome_manager.write();
307 manager.apply_cortical_mapping(&area.cortical_id)
308 };
309
310 match synapses_created {
311 Ok(count) => {
312 total_synapses += count as usize;
313 trace!(
314 target: "feagi-bdu",
315 "Created {} synapses for area {}",
316 count,
317 area.cortical_id
318 );
319 }
320 Err(e) => {
321 warn!(target: "feagi-bdu", " ⚠️ Failed to create synapses for {}: {}", area.cortical_id, e);
322 let estimated = estimate_synapses_for_area(area, genome);
323 total_synapses += estimated;
324 }
325 }
326 }
327
328 info!(target: "feagi-bdu", "✅ Incremental add complete: {} areas, {} neurons, {} synapses",
329 areas.len(), total_neurons, total_synapses);
330
331 Ok((total_neurons, total_synapses))
332 }
333
334 pub fn develop_from_genome(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
338 info!(target: "feagi-bdu","🧬 Starting neuroembryogenesis for genome: {}", genome.metadata.genome_id);
339
340 let _quantization_precision = &genome.physiology.quantization_precision;
342 let quant_spec = QuantizationSpec::default();
344
345 info!(target: "feagi-bdu",
346 " Quantization precision: {:?} (range: [{}, {}] for membrane potential)",
347 quant_spec.precision,
348 quant_spec.membrane_potential_min,
349 quant_spec.membrane_potential_max
350 );
351
352 match quant_spec.precision {
356 Precision::FP32 => {
357 info!(target: "feagi-bdu", " ✓ Using FP32 (32-bit floating-point) - highest precision");
358 info!(target: "feagi-bdu", " Memory usage: Baseline (4 bytes/neuron for membrane potential)");
359 }
360 Precision::INT8 => {
361 info!(target: "feagi-bdu", " ✓ Using INT8 (8-bit integer) - memory efficient");
362 info!(target: "feagi-bdu", " Memory reduction: 42% (1 byte/neuron for membrane potential)");
363 info!(target: "feagi-bdu", " Quantization range: [{}, {}]",
364 quant_spec.membrane_potential_min,
365 quant_spec.membrane_potential_max);
366 }
369 Precision::FP16 => {
370 warn!(target: "feagi-bdu", " FP16 quantization requested but not yet implemented.");
371 warn!(target: "feagi-bdu", " FP16 support planned for future GPU optimization.");
372 }
374 }
375
376 info!(target: "feagi-bdu", " ✓ Quantization handled by DynamicNPU (dispatches at runtime)");
379
380 self.update_stage(DevelopmentStage::Initialization, 0);
382
383 self.corticogenesis(genome)?;
385
386 self.voxelogenesis(genome)?;
388
389 self.neurogenesis(genome)?;
391
392 self.synaptogenesis(genome)?;
394
395 self.update_stage(DevelopmentStage::Completed, 100);
397
398 let progress = self.progress.read();
399 info!(target: "feagi-bdu",
400 "✅ Neuroembryogenesis completed in {}ms: {} cortical areas, {} neurons, {} synapses",
401 progress.duration_ms,
402 progress.cortical_areas_created,
403 progress.neurons_created,
404 progress.synapses_created
405 );
406
407 Ok(())
408 }
409
410 fn corticogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
412 self.update_stage(DevelopmentStage::Corticogenesis, 0);
413 info!(target: "feagi-bdu","🧠 Stage 1: Corticogenesis - Creating {} cortical areas", genome.cortical_areas.len());
414 info!(target: "feagi-bdu","🔍 Genome brain_regions check: is_empty={}, count={}",
415 genome.brain_regions.is_empty(), genome.brain_regions.len());
416 if !genome.brain_regions.is_empty() {
417 info!(target: "feagi-bdu"," Existing regions: {:?}", genome.brain_regions.keys().collect::<Vec<_>>());
418 }
419
420 let total_areas = genome.cortical_areas.len();
421
422 for (idx, (cortical_id, area)) in genome.cortical_areas.iter().enumerate() {
424 {
426 let mut manager = self.connectome_manager.write();
427 manager.add_cortical_area(area.clone())?;
428 } let progress_pct = ((idx + 1) * 100 / total_areas.max(1)) as u8;
432 self.update_progress(|p| {
433 p.cortical_areas_created = idx + 1;
434 p.progress = progress_pct;
435 });
436
437 trace!(target: "feagi-bdu", "Created cortical area: {} ({})", cortical_id, area.name);
438 }
439
440 info!(target: "feagi-bdu","🔍 BRAIN REGION AUTO-GEN CHECK: genome.brain_regions.is_empty() = {}", genome.brain_regions.is_empty());
443 let (brain_regions_to_add, region_parent_map) = if genome.brain_regions.is_empty() {
444 info!(target: "feagi-bdu"," ✅ TRIGGERING AUTO-GENERATION: No brain_regions in genome - auto-generating default root region");
445 info!(target: "feagi-bdu"," 📊 Genome has {} cortical areas to process", genome.cortical_areas.len());
446
447 let all_cortical_ids = genome.cortical_areas.keys().cloned().collect::<Vec<_>>();
449 info!(target: "feagi-bdu"," 📊 Collected {} cortical area IDs: {:?}", all_cortical_ids.len(),
450 if all_cortical_ids.len() <= 5 {
451 format!("{:?}", all_cortical_ids.iter().map(|id| id.to_string()).collect::<Vec<_>>())
452 } else {
453 format!("{:?}...", all_cortical_ids[0..5].iter().map(|id| id.to_string()).collect::<Vec<_>>())
454 });
455
456 let mut auto_inputs = Vec::new();
458 let mut auto_outputs = Vec::new();
459
460 let mut ipu_areas = Vec::new(); let mut opu_areas = Vec::new(); let mut core_areas = Vec::new(); let mut custom_memory_areas = Vec::new(); for (area_id, area) in genome.cortical_areas.iter() {
467 let area_id_str = area_id.to_string();
474 let category = if area_id_str.starts_with("___") {
476 "CORE"
477 } else if let Ok(cortical_type) = area.cortical_id.as_cortical_type() {
478 use feagi_structures::genomic::cortical_area::CorticalAreaType;
480 match cortical_type {
481 CorticalAreaType::Core(_) => "CORE",
482 CorticalAreaType::BrainInput(_) => "IPU",
483 CorticalAreaType::BrainOutput(_) => "OPU",
484 CorticalAreaType::Memory(_) => "MEMORY",
485 CorticalAreaType::Custom(_) => "CUSTOM",
486 }
487 } else {
488 let cortical_group = area
490 .properties
491 .get("cortical_group")
492 .and_then(|v| v.as_str())
493 .map(|s| s.to_uppercase());
494
495 match cortical_group.as_deref() {
496 Some("IPU") => "IPU",
497 Some("OPU") => "OPU",
498 Some("CORE") => "CORE",
499 Some("MEMORY") => "MEMORY",
500 Some("CUSTOM") => "CUSTOM",
501 _ => "CUSTOM", }
503 };
504
505 if ipu_areas.len() + opu_areas.len() + core_areas.len() + custom_memory_areas.len()
507 < 5
508 {
509 let source = if area.cortical_id.as_cortical_type().is_ok() {
510 "cortical_id_type"
511 } else if area.properties.contains_key("cortical_group") {
512 "cortical_group"
513 } else {
514 "default_fallback"
515 };
516
517 if area.cortical_id.as_cortical_type().is_ok() {
519 let type_desc = crate::cortical_type_utils::describe_cortical_type(area);
520 let frame_handling =
521 if crate::cortical_type_utils::uses_absolute_frames(area) {
522 "absolute"
523 } else if crate::cortical_type_utils::uses_incremental_frames(area) {
524 "incremental"
525 } else {
526 "n/a"
527 };
528 info!(target: "feagi-bdu"," 🔍 {}, frames={}, source={}",
529 type_desc, frame_handling, source);
530 } else {
531 info!(target: "feagi-bdu"," 🔍 Area {}: category={}, source={}",
532 area_id_str, category, source);
533 }
534 }
535
536 match category {
538 "IPU" => {
539 ipu_areas.push(*area_id);
540 auto_inputs.push(*area_id);
541 }
542 "OPU" => {
543 opu_areas.push(*area_id);
544 auto_outputs.push(*area_id);
545 }
546 "CORE" => {
547 core_areas.push(*area_id);
548 }
549 "MEMORY" | "CUSTOM" => {
550 custom_memory_areas.push(*area_id);
551 }
552 _ => {}
553 }
554 }
555
556 info!(target: "feagi-bdu"," 📊 Classification complete: IPU={}, OPU={}, CORE={}, CUSTOM/MEMORY={}",
557 ipu_areas.len(), opu_areas.len(), core_areas.len(), custom_memory_areas.len());
558
559 use feagi_structures::genomic::brain_regions::{BrainRegion, RegionID, RegionType};
561 let mut regions_map = std::collections::HashMap::new();
562
563 let mut root_area_ids = Vec::new();
565 root_area_ids.extend(ipu_areas.iter().cloned());
566 root_area_ids.extend(opu_areas.iter().cloned());
567 root_area_ids.extend(core_areas.iter().cloned());
568
569 let (root_inputs, root_outputs) =
571 Self::analyze_region_io(&root_area_ids, &genome.cortical_areas);
572
573 let root_region_id = RegionID::new();
576 let root_region_id_str = root_region_id.to_string();
577
578 let mut root_region = BrainRegion::new(
579 root_region_id,
580 "Root Brain Region".to_string(),
581 RegionType::Undefined,
582 )
583 .expect("Failed to create root region")
584 .with_areas(root_area_ids.iter().cloned());
585
586 if !root_inputs.is_empty() {
588 root_region
589 .add_property("inputs".to_string(), serde_json::json!(root_inputs.clone()));
590 }
591 if !root_outputs.is_empty() {
592 root_region.add_property(
593 "outputs".to_string(),
594 serde_json::json!(root_outputs.clone()),
595 );
596 }
597
598 info!(target: "feagi-bdu"," ✅ Created root region with {} areas (IPU={}, OPU={}, CORE={}) - analyzed: {} inputs, {} outputs",
599 root_area_ids.len(), ipu_areas.len(), opu_areas.len(), core_areas.len(),
600 root_inputs.len(), root_outputs.len());
601
602 let mut subregion_id = None;
604 if !custom_memory_areas.is_empty() {
605 let mut custom_memory_strs: Vec<String> = custom_memory_areas
607 .iter()
608 .map(|id| id.as_base_64())
609 .collect();
610 custom_memory_strs.sort(); let combined = custom_memory_strs.join("|");
612
613 use std::collections::hash_map::DefaultHasher;
615 use std::hash::{Hash, Hasher};
616 let mut hasher = DefaultHasher::new();
617 combined.hash(&mut hasher);
618 let hash = hasher.finish();
619 let hash_hex = format!("{:08x}", hash as u32);
620 let region_id = format!("region_autogen_{}", hash_hex);
621
622 let (subregion_inputs, subregion_outputs) =
624 Self::analyze_region_io(&custom_memory_areas, &genome.cortical_areas);
625
626 let autogen_position =
628 Self::calculate_autogen_region_position(&root_area_ids, genome);
629
630 let mut subregion = BrainRegion::new(
632 RegionID::new(), "Autogen Region".to_string(),
634 RegionType::Undefined, )
636 .expect("Failed to create subregion")
637 .with_areas(custom_memory_areas.iter().cloned());
638
639 subregion.add_property(
641 "coordinate_3d".to_string(),
642 serde_json::json!(autogen_position),
643 );
644 subregion.add_property("coordinate_2d".to_string(), serde_json::json!([0, 0]));
645
646 if !subregion_inputs.is_empty() {
648 subregion.add_property(
649 "inputs".to_string(),
650 serde_json::json!(subregion_inputs.clone()),
651 );
652 }
653 if !subregion_outputs.is_empty() {
654 subregion.add_property(
655 "outputs".to_string(),
656 serde_json::json!(subregion_outputs.clone()),
657 );
658 }
659
660 let subregion_id_str = subregion.region_id.to_string();
661
662 info!(target: "feagi-bdu"," ✅ Created subregion '{}' with {} CUSTOM/MEMORY areas ({} inputs, {} outputs)",
663 region_id, custom_memory_areas.len(), subregion_inputs.len(), subregion_outputs.len());
664
665 regions_map.insert(subregion_id_str.clone(), subregion);
666 subregion_id = Some(subregion_id_str);
667 }
668
669 regions_map.insert(root_region_id_str.clone(), root_region);
670
671 let total_inputs = root_inputs.len()
673 + if let Some(ref sid) = subregion_id {
674 regions_map
675 .get(sid)
676 .and_then(|r| r.properties.get("inputs"))
677 .and_then(|v| v.as_array())
678 .map(|a| a.len())
679 .unwrap_or(0)
680 } else {
681 0
682 };
683
684 let total_outputs = root_outputs.len()
685 + if let Some(ref sid) = subregion_id {
686 regions_map
687 .get(sid)
688 .and_then(|r| r.properties.get("outputs"))
689 .and_then(|v| v.as_array())
690 .map(|a| a.len())
691 .unwrap_or(0)
692 } else {
693 0
694 };
695
696 info!(target: "feagi-bdu"," ✅ Auto-generated {} brain region(s) with {} total cortical areas ({} total inputs, {} total outputs)",
697 regions_map.len(), all_cortical_ids.len(), total_inputs, total_outputs);
698
699 let mut parent_map = std::collections::HashMap::new();
701 if let Some(ref sub_id) = subregion_id {
702 parent_map.insert(sub_id.clone(), root_region_id_str.clone());
703 info!(target: "feagi-bdu"," 🔗 Parent relationship: {} -> {}", sub_id, root_region_id_str);
704 }
705
706 (regions_map, parent_map)
707 } else {
708 info!(target: "feagi-bdu"," 📋 Genome already has {} brain regions - using existing structure", genome.brain_regions.len());
709 (
712 genome.brain_regions.clone(),
713 std::collections::HashMap::new(),
714 )
715 };
716
717 {
719 let mut manager = self.connectome_manager.write();
720 let brain_region_count = brain_regions_to_add.len();
721 info!(target: "feagi-bdu"," Adding {} brain regions from genome", brain_region_count);
722
723 let root_entry = brain_regions_to_add
725 .iter()
726 .find(|(_, region)| region.name == "Root Brain Region");
727 if let Some((root_id, root_region)) = root_entry {
728 manager.add_brain_region(root_region.clone(), None)?;
729 debug!(target: "feagi-bdu"," ✓ Added brain region: {} (Root Brain Region) [parent=None]", root_id);
730 }
731
732 for (region_id, region) in brain_regions_to_add.iter() {
734 if region.name == "Root Brain Region" {
735 continue; }
737
738 let parent_id = region_parent_map.get(region_id).cloned();
739 manager.add_brain_region(region.clone(), parent_id.clone())?;
740 debug!(target: "feagi-bdu"," ✓ Added brain region: {} ({}) [parent={:?}]",
741 region_id, region.name, parent_id);
742 }
743
744 info!(target: "feagi-bdu"," Total brain regions in ConnectomeManager: {}", manager.get_brain_region_ids().len());
745 } self.update_stage(DevelopmentStage::Corticogenesis, 100);
748 info!(target: "feagi-bdu"," ✅ Corticogenesis complete: {} cortical areas created", total_areas);
749
750 Ok(())
751 }
752
753 fn voxelogenesis(&mut self, _genome: &RuntimeGenome) -> BduResult<()> {
755 self.update_stage(DevelopmentStage::Voxelogenesis, 0);
756 info!(target: "feagi-bdu","📐 Stage 2: Voxelogenesis - Establishing spatial framework");
757
758 self.update_stage(DevelopmentStage::Voxelogenesis, 100);
762 info!(target: "feagi-bdu"," ✅ Voxelogenesis complete: Spatial framework established");
763
764 Ok(())
765 }
766
767 fn neurogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
776 self.update_stage(DevelopmentStage::Neurogenesis, 0);
777 info!(target: "feagi-bdu","🔬 Stage 3: Neurogenesis - Generating neurons (SIMD-optimized batches)");
778
779 let expected_neurons = genome.stats.innate_neuron_count;
780 info!(target: "feagi-bdu"," Expected innate neurons from genome: {}", expected_neurons);
781
782 use feagi_structures::genomic::cortical_area::CoreCorticalType;
784 let death_id = CoreCorticalType::Death.to_cortical_id();
785 let power_id = CoreCorticalType::Power.to_cortical_id();
786 let fatigue_id = CoreCorticalType::Fatigue.to_cortical_id();
787
788 let mut core_areas = Vec::new();
789 let mut other_areas = Vec::new();
790
791 for (cortical_id, area) in genome.cortical_areas.iter() {
793 if *cortical_id == death_id {
794 core_areas.push((0, *cortical_id, area)); } else if *cortical_id == power_id {
796 core_areas.push((1, *cortical_id, area)); } else if *cortical_id == fatigue_id {
798 core_areas.push((2, *cortical_id, area)); } else {
800 other_areas.push((*cortical_id, area));
801 }
802 }
803
804 core_areas.sort_by_key(|(idx, _, _)| *idx);
806
807 info!(target: "feagi-bdu"," 🎯 Creating core area neurons FIRST ({} areas) for deterministic IDs", core_areas.len());
808
809 let mut total_neurons_created = 0;
810 let mut processed_count = 0;
811 let total_areas = genome.cortical_areas.len();
812
813 for (core_idx, cortical_id, area) in &core_areas {
815 let existing_core_neurons = {
816 let manager = self.connectome_manager.read();
817 let npu = manager.get_npu();
818 match npu {
819 Some(npu_arc) => {
820 let npu_lock = npu_arc.lock();
821 match npu_lock {
822 Ok(npu_guard) => {
823 npu_guard.get_neurons_in_cortical_area(*core_idx).len()
824 }
825 Err(_) => 0,
826 }
827 }
828 None => 0,
829 }
830 };
831
832 if existing_core_neurons > 0 {
833 self.sync_core_neuron_params(*core_idx, area)?;
834 let refreshed = {
835 let manager = self.connectome_manager.read();
836 manager.refresh_neuron_count_for_area(cortical_id)
837 };
838 let count = refreshed.unwrap_or(existing_core_neurons);
839 total_neurons_created += count;
840 info!(
841 target: "feagi-bdu",
842 " ↪ Skipping core neuron creation for {} (existing={}, idx={})",
843 cortical_id.as_base_64(),
844 count,
845 core_idx
846 );
847 processed_count += 1;
848 let progress_pct = (processed_count * 100 / total_areas.max(1)) as u8;
849 self.update_progress(|p| {
850 p.neurons_created = total_neurons_created;
851 p.progress = progress_pct;
852 });
853 continue;
854 }
855 let per_voxel_count = area
856 .properties
857 .get("neurons_per_voxel")
858 .and_then(|v| v.as_u64())
859 .unwrap_or(1) as i64;
860
861 let cortical_id_str = cortical_id.to_string();
862 info!(target: "feagi-bdu"," 🔋 [CORE-AREA {}] {} - dimensions: {:?}, per_voxel: {}",
863 core_idx, cortical_id_str, area.dimensions, per_voxel_count);
864
865 if per_voxel_count == 0 {
866 warn!(target: "feagi-bdu"," ⚠️ Skipping core area {} - per_voxel_neuron_cnt is 0", cortical_id_str);
867 continue;
868 }
869
870 let neurons_created = {
872 let manager_arc = self.connectome_manager.clone();
873 let mut manager = manager_arc.write();
874 manager.create_neurons_for_area(cortical_id)
875 };
876
877 match neurons_created {
878 Ok(count) => {
879 total_neurons_created += count as usize;
880 info!(target: "feagi-bdu"," ✅ Created {} neurons for core area {} (deterministic ID: neuron {})",
881 count, cortical_id_str, core_idx);
882 }
883 Err(e) => {
884 error!(target: "feagi-bdu"," ❌ FATAL: Failed to create neurons for core area {}: {}", cortical_id_str, e);
885 return Err(e);
886 }
887 }
888
889 processed_count += 1;
890 let progress_pct = (processed_count * 100 / total_areas.max(1)) as u8;
891 self.update_progress(|p| {
892 p.neurons_created = total_neurons_created;
893 p.progress = progress_pct;
894 });
895 }
896
897 info!(target: "feagi-bdu"," 📦 Creating neurons for {} other areas", other_areas.len());
899 for (cortical_id, area) in &other_areas {
900 let _per_voxel_count = area
902 .properties
903 .get("neurons_per_voxel")
904 .and_then(|v| v.as_u64())
905 .unwrap_or(1) as i64;
906
907 let per_voxel_count = area
908 .properties
909 .get("neurons_per_voxel")
910 .and_then(|v| v.as_u64())
911 .unwrap_or(1) as i64;
912
913 let cortical_id_str = cortical_id.to_string();
914
915 if per_voxel_count == 0 {
916 warn!(target: "feagi-bdu"," ⚠️ Skipping area {} - per_voxel_neuron_cnt is 0 (will have NO neurons!)", cortical_id_str);
917 continue;
918 }
919
920 let neurons_created = {
923 let manager_arc = self.connectome_manager.clone();
924 let mut manager = manager_arc.write();
925 manager.create_neurons_for_area(cortical_id)
926 }; match neurons_created {
929 Ok(count) => {
930 total_neurons_created += count as usize;
931 trace!(
932 target: "feagi-bdu",
933 "Created {} neurons for area {}",
934 count,
935 cortical_id_str
936 );
937 }
938 Err(e) => {
939 warn!(target: "feagi-bdu"," Failed to create neurons for {}: {} (NPU may not be connected)",
941 cortical_id_str, e);
942 let total_voxels = area.dimensions.width as usize
943 * area.dimensions.height as usize
944 * area.dimensions.depth as usize;
945 let expected = total_voxels * per_voxel_count as usize;
946 total_neurons_created += expected;
947 }
948 }
949
950 processed_count += 1;
951 let progress_pct = (processed_count * 100 / total_areas.max(1)) as u8;
953 self.update_progress(|p| {
954 p.neurons_created = total_neurons_created;
955 p.progress = progress_pct;
956 });
957 }
958
959 if expected_neurons > 0 && total_neurons_created != expected_neurons {
961 trace!(target: "feagi-bdu",
962 created_neurons = total_neurons_created,
963 genome_stats_innate = expected_neurons,
964 "Neuron creation complete (genome stats may only count innate neurons)"
965 );
966 }
967
968 self.update_stage(DevelopmentStage::Neurogenesis, 100);
969 info!(target: "feagi-bdu"," ✅ Neurogenesis complete: {} neurons created", total_neurons_created);
970
971 Ok(())
972 }
973
974 fn synaptogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
980 self.update_stage(DevelopmentStage::Synaptogenesis, 0);
981 info!(target: "feagi-bdu","🔗 Stage 4: Synaptogenesis - Forming synaptic connections (SIMD-optimized batches)");
982
983 let expected_synapses = genome.stats.innate_synapse_count;
984 info!(target: "feagi-bdu"," Expected innate synapses from genome: {}", expected_synapses);
985
986 self.rebuild_memory_twin_mappings_from_genome(genome)?;
987
988 let mut total_synapses_created = 0;
989 let total_areas = genome.cortical_areas.len();
990
991 for (idx, (_src_cortical_id, src_area)) in genome.cortical_areas.iter().enumerate() {
994 let has_dstmap = src_area
996 .properties
997 .get("cortical_mapping_dst")
998 .and_then(|v| v.as_object())
999 .map(|m| !m.is_empty())
1000 .unwrap_or(false);
1001
1002 if !has_dstmap {
1003 trace!(target: "feagi-bdu", "No dstmap for area {}", &src_area.cortical_id);
1004 continue;
1005 }
1006
1007 let src_cortical_id = &src_area.cortical_id;
1011 let src_cortical_id_str = src_cortical_id.to_string(); let synapses_created = {
1013 let manager_arc = self.connectome_manager.clone();
1014 let mut manager = manager_arc.write();
1015 if let Some(dstmap) = src_area.properties.get("cortical_mapping_dst") {
1016 if let Some(area) = manager.get_cortical_area_mut(src_cortical_id) {
1017 area.properties
1018 .insert("cortical_mapping_dst".to_string(), dstmap.clone());
1019 }
1020 }
1021 manager.apply_cortical_mapping(src_cortical_id)
1022 }; match synapses_created {
1025 Ok(count) => {
1026 total_synapses_created += count as usize;
1027 trace!(
1028 target: "feagi-bdu",
1029 "Created {} synapses for area {}",
1030 count,
1031 src_cortical_id_str
1032 );
1033 }
1034 Err(e) => {
1035 warn!(target: "feagi-bdu"," Failed to create synapses for {}: {} (NPU may not be connected)",
1037 src_cortical_id_str, e);
1038 let estimated = estimate_synapses_for_area(src_area, genome);
1039 total_synapses_created += estimated;
1040 }
1041 }
1042
1043 let progress_pct = ((idx + 1) * 100 / total_areas.max(1)) as u8;
1045 self.update_progress(|p| {
1046 p.synapses_created = total_synapses_created;
1047 p.progress = progress_pct;
1048 });
1049 }
1050
1051 let npu_arc = {
1056 let manager = self.connectome_manager.read();
1057 manager.get_npu().cloned()
1058 };
1059 if let Some(npu_arc) = npu_arc {
1060 let mut npu_lock = npu_arc
1061 .lock()
1062 .map_err(|e| BduError::Internal(format!("Failed to lock NPU: {}", e)))?;
1063 npu_lock.rebuild_synapse_index();
1064
1065 let manager = self.connectome_manager.read();
1067 manager.update_cached_synapse_count();
1068 }
1069
1070 #[cfg(feature = "plasticity")]
1073 {
1074 use feagi_evolutionary::extract_memory_properties;
1075 use feagi_npu_plasticity::{MemoryNeuronLifecycleConfig, PlasticityExecutor};
1076
1077 let manager = self.connectome_manager.read();
1078 if let Some(executor) = manager.get_plasticity_executor() {
1079 let mut registered_count = 0;
1080
1081 for area_id in manager.get_cortical_area_ids() {
1083 if let Some(area) = manager.get_cortical_area(area_id) {
1084 if let Some(mem_props) = extract_memory_properties(&area.properties) {
1085 let upstream_areas = manager.get_upstream_cortical_areas(area_id);
1086
1087 if let Some(npu_arc) = manager.get_npu() {
1090 if let Ok(mut npu) = npu_arc.lock() {
1091 let existing_configs = npu.get_all_fire_ledger_configs();
1092 for &upstream_idx in &upstream_areas {
1093 let existing = existing_configs
1094 .iter()
1095 .find(|(idx, _)| *idx == upstream_idx)
1096 .map(|(_, w)| *w)
1097 .unwrap_or(0);
1098
1099 let desired = mem_props.temporal_depth as usize;
1100 let resolved = existing.max(desired);
1101 if resolved != existing {
1102 if let Err(e) = npu.configure_fire_ledger_window(
1103 upstream_idx,
1104 resolved,
1105 ) {
1106 warn!(
1107 target: "feagi-bdu",
1108 "Failed to configure FireLedger window for upstream area idx={} (requested={}): {}",
1109 upstream_idx,
1110 resolved,
1111 e
1112 );
1113 }
1114 }
1115 }
1116 } else {
1117 warn!(target: "feagi-bdu", "Failed to lock NPU for FireLedger configuration");
1118 }
1119 }
1120
1121 if let Ok(exec) = executor.lock() {
1122 let upstream_non_memory =
1123 manager.filter_non_memory_upstream_areas(&upstream_areas);
1124 let lifecycle_config = MemoryNeuronLifecycleConfig {
1125 initial_lifespan: mem_props.init_lifespan,
1126 lifespan_growth_rate: mem_props.lifespan_growth_rate,
1127 longterm_threshold: mem_props.longterm_threshold,
1128 max_reactivations: 1000,
1129 };
1130
1131 exec.register_memory_area(
1132 area.cortical_idx,
1133 area_id.as_base_64(),
1134 mem_props.temporal_depth,
1135 upstream_non_memory,
1136 Some(lifecycle_config),
1137 );
1138
1139 registered_count += 1;
1140 }
1141 }
1142 }
1143 }
1144 let _ = registered_count; }
1146 }
1147
1148 if expected_synapses > 0 {
1150 let diff = (total_synapses_created as i64 - expected_synapses as i64).abs();
1151 let diff_pct = (diff as f64 / expected_synapses.max(1) as f64) * 100.0;
1152
1153 if diff_pct > 10.0 {
1154 warn!(target: "feagi-bdu",
1155 "Synapse count variance: created {} but genome stats expected {} ({:.1}% difference)",
1156 total_synapses_created, expected_synapses, diff_pct
1157 );
1158 } else {
1159 info!(target: "feagi-bdu",
1160 "Synapse count matches genome stats within {:.1}% ({} vs {})",
1161 diff_pct, total_synapses_created, expected_synapses
1162 );
1163 }
1164 }
1165
1166 self.update_stage(DevelopmentStage::Synaptogenesis, 100);
1167 info!(target: "feagi-bdu"," ✅ Synaptogenesis complete: {} synapses created", total_synapses_created);
1168
1169 Ok(())
1170 }
1171
1172 fn rebuild_memory_twin_mappings_from_genome(
1173 &mut self,
1174 genome: &RuntimeGenome,
1175 ) -> BduResult<()> {
1176 use feagi_structures::genomic::cortical_area::CorticalAreaType;
1177 let mut repaired = 0usize;
1178
1179 for (memory_id, memory_area) in genome.cortical_areas.iter() {
1180 let is_memory = matches!(
1181 memory_area.cortical_id.as_cortical_type(),
1182 Ok(CorticalAreaType::Memory(_))
1183 ) || memory_area
1184 .properties
1185 .get("is_mem_type")
1186 .and_then(|v| v.as_bool())
1187 .unwrap_or(false)
1188 || memory_area
1189 .properties
1190 .get("cortical_group")
1191 .and_then(|v| v.as_str())
1192 .is_some_and(|v| v.eq_ignore_ascii_case("MEMORY"));
1193 if !is_memory {
1194 continue;
1195 }
1196
1197 let Some(dstmap) = memory_area
1198 .properties
1199 .get("cortical_mapping_dst")
1200 .and_then(|v| v.as_object())
1201 else {
1202 continue;
1203 };
1204
1205 for (dst_id_str, rules) in dstmap {
1206 let Some(rule_array) = rules.as_array() else {
1207 continue;
1208 };
1209 let has_replay = rule_array.iter().any(|rule| {
1210 rule.get("morphology_id")
1211 .and_then(|v| v.as_str())
1212 .is_some_and(|id| id == "memory_replay")
1213 });
1214 if !has_replay {
1215 continue;
1216 }
1217
1218 let dst_id = match CorticalID::try_from_base_64(dst_id_str) {
1219 Ok(id) => id,
1220 Err(_) => {
1221 warn!(
1222 target: "feagi-bdu",
1223 "Invalid twin cortical ID in memory_replay dstmap: {}",
1224 dst_id_str
1225 );
1226 continue;
1227 }
1228 };
1229
1230 let Some(twin_area) = genome.cortical_areas.get(&dst_id) else {
1231 continue;
1232 };
1233 let Some(upstream_id_str) = twin_area
1234 .properties
1235 .get("memory_twin_of")
1236 .and_then(|v| v.as_str())
1237 else {
1238 continue;
1239 };
1240 let upstream_id = match CorticalID::try_from_base_64(upstream_id_str) {
1241 Ok(id) => id,
1242 Err(_) => {
1243 warn!(
1244 target: "feagi-bdu",
1245 "Invalid memory_twin_of value on twin area {}: {}",
1246 dst_id.as_base_64(),
1247 upstream_id_str
1248 );
1249 continue;
1250 }
1251 };
1252
1253 let mut manager = self.connectome_manager.write();
1254 if let Err(e) = manager.ensure_memory_twin_area(memory_id, &upstream_id) {
1255 warn!(
1256 target: "feagi-bdu",
1257 "Failed to rebuild memory twin mapping for memory {} upstream {}: {}",
1258 memory_id.as_base_64(),
1259 upstream_id.as_base_64(),
1260 e
1261 );
1262 continue;
1263 }
1264 repaired += 1;
1265 }
1266 }
1267
1268 info!(
1269 target: "feagi-bdu",
1270 "Rebuilt {} memory twin mapping(s) from genome",
1271 repaired
1272 );
1273 Ok(())
1274 }
1275}
1276
1277fn estimate_synapses_for_area(
1281 src_area: &CorticalArea,
1282 genome: &feagi_evolutionary::RuntimeGenome,
1283) -> usize {
1284 let dstmap = match src_area.properties.get("cortical_mapping_dst") {
1285 Some(serde_json::Value::Object(map)) => map,
1286 _ => return 0,
1287 };
1288
1289 let mut total = 0;
1290
1291 for (dst_id, rules) in dstmap {
1292 let dst_cortical_id = match feagi_evolutionary::string_to_cortical_id(dst_id) {
1294 Ok(id) => id,
1295 Err(_) => continue,
1296 };
1297 let dst_area = match genome.cortical_areas.get(&dst_cortical_id) {
1298 Some(area) => area,
1299 None => continue,
1300 };
1301
1302 let rules_array = match rules.as_array() {
1303 Some(arr) => arr,
1304 None => continue,
1305 };
1306
1307 for rule in rules_array {
1308 let morphology_id = rule
1309 .get("morphology_id")
1310 .and_then(|v| v.as_str())
1311 .unwrap_or("unknown");
1312 let scalar = rule
1313 .get("morphology_scalar")
1314 .and_then(|v| v.as_i64())
1315 .unwrap_or(1) as usize;
1316
1317 let src_per_voxel = src_area
1319 .properties
1320 .get("neurons_per_voxel")
1321 .and_then(|v| v.as_u64())
1322 .unwrap_or(1) as usize;
1323 let dst_per_voxel = dst_area
1324 .properties
1325 .get("neurons_per_voxel")
1326 .and_then(|v| v.as_u64())
1327 .unwrap_or(1) as usize;
1328
1329 let src_voxels =
1330 src_area.dimensions.width * src_area.dimensions.height * src_area.dimensions.depth;
1331 let dst_voxels =
1332 dst_area.dimensions.width * dst_area.dimensions.height * dst_area.dimensions.depth;
1333
1334 let src_neurons = src_voxels as usize * src_per_voxel;
1335 let dst_neurons = dst_voxels as usize * dst_per_voxel as usize;
1336
1337 let count = match morphology_id {
1339 "block_to_block" => src_neurons * dst_per_voxel * scalar,
1340 "projector" => src_neurons * dst_neurons * scalar,
1341 _ if morphology_id.contains("lateral") => src_neurons * scalar,
1342 _ => (src_neurons * scalar).min(src_neurons * dst_neurons / 10),
1343 };
1344
1345 total += count;
1346 }
1347 }
1348
1349 total
1350}
1351
1352impl Neuroembryogenesis {
1353 fn calculate_autogen_region_position(
1355 root_area_ids: &[CorticalID],
1356 genome: &feagi_evolutionary::RuntimeGenome,
1357 ) -> [i32; 3] {
1358 if root_area_ids.is_empty() {
1359 return [100, 0, 0];
1360 }
1361
1362 let mut min_x = i32::MAX;
1363 let mut max_x = i32::MIN;
1364 let mut min_y = i32::MAX;
1365 let mut max_y = i32::MIN;
1366 let mut min_z = i32::MAX;
1367 let mut max_z = i32::MIN;
1368
1369 for cortical_id in root_area_ids {
1370 if let Some(area) = genome.cortical_areas.get(cortical_id) {
1371 let pos: (i32, i32, i32) = area.position.into();
1372 let dims = (
1373 area.dimensions.width as i32,
1374 area.dimensions.height as i32,
1375 area.dimensions.depth as i32,
1376 );
1377
1378 min_x = min_x.min(pos.0);
1379 max_x = max_x.max(pos.0 + dims.0);
1380 min_y = min_y.min(pos.1);
1381 max_y = max_y.max(pos.1 + dims.1);
1382 min_z = min_z.min(pos.2);
1383 max_z = max_z.max(pos.2 + dims.2);
1384 }
1385 }
1386
1387 let bbox_width = (max_x - min_x).max(1);
1388 let padding = (bbox_width / 5).max(50);
1389 let autogen_x = max_x + padding;
1390 let autogen_y = (min_y + max_y) / 2;
1391 let autogen_z = (min_z + max_z) / 2;
1392
1393 info!(target: "feagi-bdu",
1394 " 📐 Autogen position: ({}, {}, {}) [padding: {}]",
1395 autogen_x, autogen_y, autogen_z, padding);
1396
1397 [autogen_x, autogen_y, autogen_z]
1398 }
1399
1400 fn analyze_region_io(
1406 region_area_ids: &[feagi_structures::genomic::cortical_area::CorticalID],
1407 all_cortical_areas: &std::collections::HashMap<CorticalID, CorticalArea>,
1408 ) -> (Vec<String>, Vec<String>) {
1409 let area_set: std::collections::HashSet<_> = region_area_ids.iter().cloned().collect();
1410 let mut inputs = Vec::new();
1411 let mut outputs = Vec::new();
1412
1413 let extract_destinations = |area: &CorticalArea| -> Vec<String> {
1415 area.properties
1416 .get("cortical_mapping_dst")
1417 .and_then(|v| v.as_object())
1418 .map(|obj| obj.keys().cloned().collect())
1419 .unwrap_or_default()
1420 };
1421
1422 for area_id in region_area_ids {
1424 if let Some(area) = all_cortical_areas.get(area_id) {
1425 let destinations = extract_destinations(area);
1426 let external_destinations: Vec<_> = destinations
1428 .iter()
1429 .filter_map(|dest| feagi_evolutionary::string_to_cortical_id(dest).ok())
1430 .filter(|dest_id| !area_set.contains(dest_id))
1431 .collect();
1432
1433 if !external_destinations.is_empty() {
1434 outputs.push(area_id.as_base_64());
1435 }
1436 }
1437 }
1438
1439 for (source_area_id, source_area) in all_cortical_areas.iter() {
1441 if area_set.contains(source_area_id) {
1443 continue;
1444 }
1445
1446 let destinations = extract_destinations(source_area);
1447 for dest_str in destinations {
1448 if let Ok(dest_id) = feagi_evolutionary::string_to_cortical_id(&dest_str) {
1449 if area_set.contains(&dest_id) {
1450 let dest_string = dest_id.as_base_64();
1451 if !inputs.contains(&dest_string) {
1452 inputs.push(dest_string);
1453 }
1454 }
1455 }
1456 }
1457 }
1458
1459 (inputs, outputs)
1460 }
1461
1462 fn update_stage(&self, stage: DevelopmentStage, progress: u8) {
1464 let mut p = self.progress.write();
1465 p.stage = stage;
1466 p.progress = progress;
1467 p.duration_ms = self.start_time.elapsed().as_millis() as u64;
1468 }
1469
1470 fn update_progress<F>(&self, f: F)
1472 where
1473 F: FnOnce(&mut DevelopmentProgress),
1474 {
1475 let mut p = self.progress.write();
1476 f(&mut p);
1477 p.duration_ms = self.start_time.elapsed().as_millis() as u64;
1478 }
1479}
1480
1481#[cfg(test)]
1482mod tests {
1483 use super::*;
1484 use feagi_evolutionary::create_genome_with_core_morphologies;
1485 use feagi_structures::genomic::cortical_area::CorticalAreaDimensions;
1486
1487 #[test]
1488 fn test_neuroembryogenesis_creation() {
1489 let manager = ConnectomeManager::instance();
1490 let neuro = Neuroembryogenesis::new(manager);
1491
1492 let progress = neuro.get_progress();
1493 assert_eq!(progress.stage, DevelopmentStage::Initialization);
1494 assert_eq!(progress.progress, 0);
1495 }
1496
1497 #[test]
1498 fn test_development_from_minimal_genome() {
1499 ConnectomeManager::reset_for_testing(); let manager = ConnectomeManager::instance();
1501 let mut neuro = Neuroembryogenesis::new(manager.clone());
1502
1503 let mut genome = create_genome_with_core_morphologies(
1505 "test_genome".to_string(),
1506 "Test Genome".to_string(),
1507 );
1508
1509 let cortical_id = CorticalID::try_from_bytes(b"cst_neur").unwrap(); let cortical_type = cortical_id
1511 .as_cortical_type()
1512 .expect("Failed to get cortical type");
1513 let area = CorticalArea::new(
1514 cortical_id,
1515 0,
1516 "Test Area".to_string(),
1517 CorticalAreaDimensions::new(10, 10, 10).unwrap(),
1518 (0, 0, 0).into(),
1519 cortical_type,
1520 )
1521 .expect("Failed to create cortical area");
1522 genome.cortical_areas.insert(cortical_id, area);
1523
1524 let result = neuro.develop_from_genome(&genome);
1526 assert!(result.is_ok(), "Development failed: {:?}", result);
1527
1528 let progress = neuro.get_progress();
1530 assert_eq!(progress.stage, DevelopmentStage::Completed);
1531 assert_eq!(progress.progress, 100);
1532 assert_eq!(progress.cortical_areas_created, 1);
1533
1534 let mgr = manager.read();
1536 assert!(
1538 mgr.has_cortical_area(&cortical_id),
1539 "Cortical area should have been added to connectome"
1540 );
1541
1542 println!("✅ Development completed in {}ms", progress.duration_ms);
1543 }
1544}