1use crate::connectome_manager::ConnectomeManager;
22use crate::models::{CorticalArea, CorticalID};
23use crate::types::{BduError, BduResult};
24use feagi_evolutionary::{
25 apply_genome_title_to_unique_top_circuit, wrap_parentless_regions_under_named_root,
26 RuntimeGenome,
27};
28use feagi_npu_neural::types::{Precision, QuantizationSpec};
29use feagi_structures::genomic::brain_regions::ROOT_BRAIN_REGION_NAME;
30use parking_lot::RwLock;
31use std::sync::Arc;
32use tracing::{debug, error, info, trace, warn};
33
34fn autogen_subregion_display_name(genome_title: &str) -> String {
38 let t = genome_title.trim();
39 if t.is_empty() || t.eq_ignore_ascii_case("untitled") {
40 "Autogen Circuit".to_string()
41 } else {
42 t.to_string()
43 }
44}
45
46fn order_regions_parent_before_child(
57 region_ids: &[String],
58 region_parent_map: &std::collections::HashMap<String, String>,
59) -> Vec<String> {
60 use std::collections::{HashMap, HashSet, VecDeque};
61
62 let id_set: HashSet<&str> = region_ids.iter().map(String::as_str).collect();
63 let mut children: HashMap<&str, Vec<&str>> = HashMap::new();
64 let mut in_degree: HashMap<&str, usize> =
65 region_ids.iter().map(|id| (id.as_str(), 0usize)).collect();
66
67 for id in region_ids {
68 if let Some(parent) = region_parent_map.get(id) {
69 if id_set.contains(parent.as_str()) {
70 if let Some(degree) = in_degree.get_mut(id.as_str()) {
71 *degree += 1;
72 }
73 children
74 .entry(parent.as_str())
75 .or_default()
76 .push(id.as_str());
77 }
78 }
79 }
80
81 let mut ready: Vec<&str> = in_degree
82 .iter()
83 .filter(|(_, degree)| **degree == 0)
84 .map(|(id, _)| *id)
85 .collect();
86 ready.sort_unstable();
87
88 let mut ordered = Vec::with_capacity(region_ids.len());
89 let mut queue: VecDeque<&str> = ready.into();
90
91 while let Some(id) = queue.pop_front() {
92 ordered.push(id.to_string());
93 if let Some(kids) = children.get_mut(id) {
94 kids.sort_unstable();
95 for child in kids.iter().copied() {
96 if let Some(degree) = in_degree.get_mut(child) {
97 *degree -= 1;
98 if *degree == 0 {
99 queue.push_back(child);
100 }
101 }
102 }
103 }
104 }
105
106 if ordered.len() < region_ids.len() {
107 let emitted: HashSet<&str> = ordered.iter().map(String::as_str).collect();
108 let mut leftovers: Vec<String> = region_ids
109 .iter()
110 .filter(|id| !emitted.contains(id.as_str()))
111 .cloned()
112 .collect();
113 leftovers.sort();
114 ordered.extend(leftovers);
115 }
116
117 ordered
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum DevelopmentStage {
123 Initialization,
125 Corticogenesis,
127 Voxelogenesis,
129 Neurogenesis,
131 Synaptogenesis,
133 Completed,
135 Failed,
137}
138
139#[derive(Debug, Clone)]
141pub struct DevelopmentProgress {
142 pub stage: DevelopmentStage,
144 pub progress: u8,
146 pub cortical_areas_created: usize,
148 pub neurons_created: usize,
150 pub synapses_created: usize,
152 pub duration_ms: u64,
154}
155
156impl Default for DevelopmentProgress {
157 fn default() -> Self {
158 Self {
159 stage: DevelopmentStage::Initialization,
160 progress: 0,
161 cortical_areas_created: 0,
162 neurons_created: 0,
163 synapses_created: 0,
164 duration_ms: 0,
165 }
166 }
167}
168
169pub struct Neuroembryogenesis {
177 connectome_manager: Arc<RwLock<ConnectomeManager>>,
179
180 progress: Arc<RwLock<DevelopmentProgress>>,
182
183 start_time: std::time::Instant,
185}
186
187impl Neuroembryogenesis {
188 pub fn new(connectome_manager: Arc<RwLock<ConnectomeManager>>) -> Self {
190 Self {
191 connectome_manager,
192 progress: Arc::new(RwLock::new(DevelopmentProgress::default())),
193 start_time: std::time::Instant::now(),
194 }
195 }
196
197 pub fn get_progress(&self) -> DevelopmentProgress {
199 self.progress.read().clone()
200 }
201
202 fn sync_core_neuron_params(&self, cortical_idx: u32, area: &CorticalArea) -> BduResult<()> {
206 use crate::models::CorticalAreaExt;
207
208 let npu_arc = {
209 let manager = self.connectome_manager.read();
210 manager
211 .get_npu()
212 .cloned()
213 .ok_or_else(|| BduError::Internal("NPU not connected".to_string()))?
214 };
215
216 let mut npu_lock = npu_arc
217 .lock()
218 .map_err(|e| BduError::Internal(format!("Failed to lock NPU: {}", e)))?;
219
220 npu_lock.update_cortical_area_threshold_with_gradient(
221 cortical_idx,
222 area.firing_threshold(),
223 area.firing_threshold_increment_x(),
224 area.firing_threshold_increment_y(),
225 area.firing_threshold_increment_z(),
226 );
227 npu_lock.update_cortical_area_threshold_limit(cortical_idx, area.firing_threshold_limit());
228 npu_lock.update_cortical_area_leak(cortical_idx, area.leak_coefficient());
229 npu_lock.update_cortical_area_excitability(cortical_idx, area.neuron_excitability());
230 npu_lock.update_cortical_area_refractory_period(cortical_idx, area.refractory_period());
231 npu_lock.update_cortical_area_consecutive_fire_limit(
232 cortical_idx,
233 area.consecutive_fire_count() as u16,
234 );
235 npu_lock.update_cortical_area_snooze_period(cortical_idx, area.snooze_period());
236 npu_lock.update_cortical_area_mp_charge_accumulation(
237 cortical_idx,
238 area.mp_charge_accumulation(),
239 );
240
241 Ok(())
242 }
243
244 pub fn add_cortical_areas(
256 &mut self,
257 areas: Vec<CorticalArea>,
258 genome: &RuntimeGenome,
259 ) -> BduResult<(usize, usize)> {
260 info!(target: "feagi-bdu", "🧬 Incrementally adding {} cortical areas", areas.len());
261
262 let mut total_neurons = 0;
263 let mut total_synapses = 0;
264
265 for area in &areas {
267 let mut manager = self.connectome_manager.write();
268 manager.add_cortical_area(area.clone())?;
269 info!(target: "feagi-bdu", " ✓ Added cortical area structure: {}", area.cortical_id.as_base_64());
270 }
271
272 use feagi_structures::genomic::cortical_area::CoreCorticalType;
275 let death_id = CoreCorticalType::Death.to_cortical_id();
276 let power_id = CoreCorticalType::Power.to_cortical_id();
277 let fatigue_id = CoreCorticalType::Fatigue.to_cortical_id();
278 let pain_id = CoreCorticalType::Pain.to_cortical_id();
279 let pleasure_id = CoreCorticalType::Pleasure.to_cortical_id();
280 let fear_id = CoreCorticalType::Fear.to_cortical_id();
281 let hope_id = CoreCorticalType::Hope.to_cortical_id();
282
283 let mut core_areas = Vec::new();
284 let mut other_areas = Vec::new();
285
286 for area in &areas {
288 if area.cortical_id == death_id {
289 core_areas.push((0, area)); } else if area.cortical_id == power_id {
291 core_areas.push((1, area)); } else if area.cortical_id == fatigue_id {
293 core_areas.push((2, area)); } else if area.cortical_id == pain_id {
295 core_areas.push((3, area)); } else if area.cortical_id == pleasure_id {
297 core_areas.push((4, area)); } else if area.cortical_id == fear_id {
299 core_areas.push((5, area)); } else if area.cortical_id == hope_id {
301 core_areas.push((6, area)); } else {
303 other_areas.push(area);
304 }
305 }
306
307 core_areas.sort_by_key(|(idx, _)| *idx);
309
310 if !core_areas.is_empty() {
312 info!(target: "feagi-bdu", " 🎯 Creating core area neurons FIRST ({} areas) for deterministic IDs", core_areas.len());
313 for (core_idx, area) in &core_areas {
314 let existing_core_neurons = {
315 let manager = self.connectome_manager.read();
316 let npu = manager.get_npu();
317 match npu {
318 Some(npu_arc) => {
319 let npu_lock = npu_arc.lock();
320 match npu_lock {
321 Ok(npu_guard) => {
322 npu_guard.get_neurons_in_cortical_area(*core_idx).len()
323 }
324 Err(_) => 0,
325 }
326 }
327 None => 0,
328 }
329 };
330
331 if existing_core_neurons > 0 {
332 self.sync_core_neuron_params(*core_idx, area)?;
333 let refreshed = {
334 let manager = self.connectome_manager.read();
335 manager.refresh_neuron_count_for_area(&area.cortical_id)
336 };
337 let count = refreshed.unwrap_or(existing_core_neurons);
338 total_neurons += count;
339 info!(
340 target: "feagi-bdu",
341 " ↪ Skipping core neuron creation for {} (existing={}, idx={})",
342 area.cortical_id.as_base_64(),
343 count,
344 core_idx
345 );
346 continue;
347 }
348 let neurons_created = {
349 let mut manager = self.connectome_manager.write();
350 manager.create_neurons_for_area(&area.cortical_id)
351 };
352
353 match neurons_created {
354 Ok(count) => {
355 total_neurons += count as usize;
356 info!(target: "feagi-bdu", " ✅ Created {} neurons for core area {} (deterministic ID: neuron {})",
357 count, area.cortical_id.as_base_64(), core_idx);
358 }
359 Err(e) => {
360 error!(target: "feagi-bdu", " ❌ FATAL: Failed to create neurons for core area {}: {}", area.cortical_id.as_base_64(), e);
361 return Err(e);
362 }
363 }
364 }
365 }
366
367 for area in &other_areas {
369 let neurons_created = {
370 let mut manager = self.connectome_manager.write();
371 manager.create_neurons_for_area(&area.cortical_id)
372 };
373
374 match neurons_created {
375 Ok(count) => {
376 total_neurons += count as usize;
377 trace!(
378 target: "feagi-bdu",
379 "Created {} neurons for area {}",
380 count,
381 area.cortical_id.as_base_64()
382 );
383 }
384 Err(e) => {
385 error!(target: "feagi-bdu", " ❌ FATAL: Failed to create neurons for {}: {}", area.cortical_id.as_base_64(), e);
386 return Err(e);
388 }
389 }
390 }
391
392 for area in &areas {
394 let has_dstmap = area
396 .properties
397 .get("cortical_mapping_dst")
398 .and_then(|v| v.as_object())
399 .map(|m| !m.is_empty())
400 .unwrap_or(false);
401
402 if !has_dstmap {
403 debug!(target: "feagi-bdu", " No mappings for area {}", area.cortical_id.as_base_64());
404 continue;
405 }
406
407 let synapses_created = {
408 let mut manager = self.connectome_manager.write();
409 manager.apply_cortical_mapping(&area.cortical_id)
410 };
411
412 match synapses_created {
413 Ok(count) => {
414 total_synapses += count as usize;
415 trace!(
416 target: "feagi-bdu",
417 "Created {} synapses for area {}",
418 count,
419 area.cortical_id
420 );
421 }
422 Err(e) => {
423 warn!(target: "feagi-bdu", " ⚠️ Failed to create synapses for {}: {}", area.cortical_id, e);
424 let estimated = estimate_synapses_for_area(area, genome);
425 total_synapses += estimated;
426 }
427 }
428 }
429
430 info!(target: "feagi-bdu", "✅ Incremental add complete: {} areas, {} neurons, {} synapses",
431 areas.len(), total_neurons, total_synapses);
432
433 Ok((total_neurons, total_synapses))
434 }
435
436 pub fn develop_from_genome(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
440 info!(target: "feagi-bdu","🧬 Starting neuroembryogenesis for genome: {}", genome.metadata.genome_id);
441
442 let _quantization_precision = &genome.physiology.quantization_precision;
444 let quant_spec = QuantizationSpec::default();
446
447 info!(target: "feagi-bdu",
448 " Quantization precision: {:?} (range: [{}, {}] for membrane potential)",
449 quant_spec.precision,
450 quant_spec.membrane_potential_min,
451 quant_spec.membrane_potential_max
452 );
453
454 match quant_spec.precision {
458 Precision::FP32 => {
459 info!(target: "feagi-bdu", " ✓ Using FP32 (32-bit floating-point) - highest precision");
460 info!(target: "feagi-bdu", " Memory usage: Baseline (4 bytes/neuron for membrane potential)");
461 }
462 Precision::INT8 => {
463 info!(target: "feagi-bdu", " ✓ Using INT8 (8-bit integer) - memory efficient");
464 info!(target: "feagi-bdu", " Memory reduction: 42% (1 byte/neuron for membrane potential)");
465 info!(target: "feagi-bdu", " Quantization range: [{}, {}]",
466 quant_spec.membrane_potential_min,
467 quant_spec.membrane_potential_max);
468 }
471 Precision::FP16 => {
472 warn!(target: "feagi-bdu", " FP16 quantization requested but not yet implemented.");
473 warn!(target: "feagi-bdu", " FP16 support planned for future GPU optimization.");
474 }
476 }
477
478 info!(target: "feagi-bdu", " ✓ Quantization handled by DynamicNPU (dispatches at runtime)");
481
482 self.update_stage(DevelopmentStage::Initialization, 0);
484
485 self.corticogenesis(genome)?;
487
488 self.voxelogenesis(genome)?;
490
491 self.neurogenesis(genome)?;
493
494 self.synaptogenesis(genome)?;
496
497 self.update_stage(DevelopmentStage::Completed, 100);
499
500 let progress = self.progress.read();
501 info!(target: "feagi-bdu",
502 "✅ Neuroembryogenesis completed in {}ms: {} cortical areas, {} neurons, {} synapses",
503 progress.duration_ms,
504 progress.cortical_areas_created,
505 progress.neurons_created,
506 progress.synapses_created
507 );
508
509 Ok(())
510 }
511
512 fn corticogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
514 self.update_stage(DevelopmentStage::Corticogenesis, 0);
515 info!(target: "feagi-bdu","🧠 Stage 1: Corticogenesis - Creating {} cortical areas", genome.cortical_areas.len());
516 info!(target: "feagi-bdu","🔍 Genome brain_regions check: is_empty={}, count={}",
517 genome.brain_regions.is_empty(), genome.brain_regions.len());
518 if !genome.brain_regions.is_empty() {
519 info!(target: "feagi-bdu"," Existing regions: {:?}", genome.brain_regions.keys().collect::<Vec<_>>());
520 }
521
522 let total_areas = genome.cortical_areas.len();
523
524 for (idx, (cortical_id, area)) in genome.cortical_areas.iter().enumerate() {
526 {
528 let mut manager = self.connectome_manager.write();
529 manager.add_cortical_area(area.clone())?;
530 } let progress_pct = ((idx + 1) * 100 / total_areas.max(1)) as u8;
534 self.update_progress(|p| {
535 p.cortical_areas_created = idx + 1;
536 p.progress = progress_pct;
537 });
538
539 trace!(target: "feagi-bdu", "Created cortical area: {} ({})", cortical_id, area.name);
540 }
541
542 info!(target: "feagi-bdu","🔍 BRAIN REGION AUTO-GEN CHECK: genome.brain_regions.is_empty() = {}", genome.brain_regions.is_empty());
545 let (brain_regions_to_add, region_parent_map) = if genome.brain_regions.is_empty() {
546 info!(target: "feagi-bdu"," ✅ TRIGGERING AUTO-GENERATION: No brain_regions in genome - auto-generating default root region");
547 info!(target: "feagi-bdu"," 📊 Genome has {} cortical areas to process", genome.cortical_areas.len());
548
549 let all_cortical_ids = genome.cortical_areas.keys().cloned().collect::<Vec<_>>();
551 info!(target: "feagi-bdu"," 📊 Collected {} cortical area IDs: {:?}", all_cortical_ids.len(),
552 if all_cortical_ids.len() <= 5 {
553 format!("{:?}", all_cortical_ids.iter().map(|id| id.to_string()).collect::<Vec<_>>())
554 } else {
555 format!("{:?}...", all_cortical_ids[0..5].iter().map(|id| id.to_string()).collect::<Vec<_>>())
556 });
557
558 let mut auto_inputs = Vec::new();
560 let mut auto_outputs = Vec::new();
561
562 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() {
569 let area_id_str = area_id.to_string();
576 let category = if area_id_str.starts_with("___") {
578 "CORE"
579 } else if let Ok(cortical_type) = area.cortical_id.as_cortical_type() {
580 use feagi_structures::genomic::cortical_area::CorticalAreaType;
582 match cortical_type {
583 CorticalAreaType::Core(_) => "CORE",
584 CorticalAreaType::BrainInput(_) => "IPU",
585 CorticalAreaType::BrainOutput(_) => "OPU",
586 CorticalAreaType::Memory(_) => "MEMORY",
587 CorticalAreaType::Custom(_) => "CUSTOM",
588 }
589 } else {
590 let cortical_group = area
592 .properties
593 .get("cortical_group")
594 .and_then(|v| v.as_str())
595 .map(|s| s.to_uppercase());
596
597 match cortical_group.as_deref() {
598 Some("IPU") => "IPU",
599 Some("OPU") => "OPU",
600 Some("CORE") => "CORE",
601 Some("MEMORY") => "MEMORY",
602 Some("CUSTOM") => "CUSTOM",
603 _ => "CUSTOM", }
605 };
606
607 if ipu_areas.len() + opu_areas.len() + core_areas.len() + custom_memory_areas.len()
609 < 5
610 {
611 let source = if area.cortical_id.as_cortical_type().is_ok() {
612 "cortical_id_type"
613 } else if area.properties.contains_key("cortical_group") {
614 "cortical_group"
615 } else {
616 "default_fallback"
617 };
618
619 if area.cortical_id.as_cortical_type().is_ok() {
621 let type_desc = crate::cortical_type_utils::describe_cortical_type(area);
622 let frame_handling =
623 if crate::cortical_type_utils::uses_absolute_frames(area) {
624 "absolute"
625 } else if crate::cortical_type_utils::uses_incremental_frames(area) {
626 "incremental"
627 } else {
628 "n/a"
629 };
630 info!(target: "feagi-bdu"," 🔍 {}, frames={}, source={}",
631 type_desc, frame_handling, source);
632 } else {
633 info!(target: "feagi-bdu"," 🔍 Area {}: category={}, source={}",
634 area_id_str, category, source);
635 }
636 }
637
638 match category {
640 "IPU" => {
641 ipu_areas.push(*area_id);
642 auto_inputs.push(*area_id);
643 }
644 "OPU" => {
645 opu_areas.push(*area_id);
646 auto_outputs.push(*area_id);
647 }
648 "CORE" => {
649 core_areas.push(*area_id);
650 }
651 "MEMORY" | "CUSTOM" => {
652 custom_memory_areas.push(*area_id);
653 }
654 _ => {}
655 }
656 }
657
658 info!(target: "feagi-bdu"," 📊 Classification complete: IPU={}, OPU={}, CORE={}, CUSTOM/MEMORY={}",
659 ipu_areas.len(), opu_areas.len(), core_areas.len(), custom_memory_areas.len());
660
661 use feagi_structures::genomic::brain_regions::{BrainRegion, RegionID, RegionType};
663 let mut regions_map = std::collections::HashMap::new();
664
665 let mut root_area_ids = Vec::new();
667 root_area_ids.extend(ipu_areas.iter().cloned());
668 root_area_ids.extend(opu_areas.iter().cloned());
669 root_area_ids.extend(core_areas.iter().cloned());
670
671 let (root_inputs, root_outputs) =
673 Self::analyze_region_io(&root_area_ids, &genome.cortical_areas);
674
675 let root_region_id = RegionID::new();
678 let root_region_id_str = root_region_id.to_string();
679
680 let mut root_region = BrainRegion::new(
681 root_region_id,
682 ROOT_BRAIN_REGION_NAME.to_string(),
683 RegionType::Undefined,
684 )
685 .expect("Failed to create root region")
686 .with_areas(root_area_ids.iter().cloned());
687
688 if !root_inputs.is_empty() {
690 root_region
691 .add_property("inputs".to_string(), serde_json::json!(root_inputs.clone()));
692 }
693 if !root_outputs.is_empty() {
694 root_region.add_property(
695 "outputs".to_string(),
696 serde_json::json!(root_outputs.clone()),
697 );
698 }
699
700 info!(target: "feagi-bdu"," ✅ Created root region with {} areas (IPU={}, OPU={}, CORE={}) - analyzed: {} inputs, {} outputs",
701 root_area_ids.len(), ipu_areas.len(), opu_areas.len(), core_areas.len(),
702 root_inputs.len(), root_outputs.len());
703
704 let mut subregion_id = None;
706 if !custom_memory_areas.is_empty() {
707 let mut custom_memory_strs: Vec<String> = custom_memory_areas
709 .iter()
710 .map(|id| id.as_base_64())
711 .collect();
712 custom_memory_strs.sort(); let combined = custom_memory_strs.join("|");
714
715 use std::collections::hash_map::DefaultHasher;
717 use std::hash::{Hash, Hasher};
718 let mut hasher = DefaultHasher::new();
719 combined.hash(&mut hasher);
720 let hash = hasher.finish();
721 let hash_hex = format!("{:08x}", hash as u32);
722 let region_id = format!("region_autogen_{}", hash_hex);
723
724 let (subregion_inputs, subregion_outputs) =
726 Self::analyze_region_io(&custom_memory_areas, &genome.cortical_areas);
727
728 let autogen_position =
730 Self::calculate_autogen_region_position(&root_area_ids, genome);
731
732 let subregion_name = autogen_subregion_display_name(&genome.metadata.genome_title);
734 let mut subregion = BrainRegion::new(
735 RegionID::new(), subregion_name,
737 RegionType::Undefined, )
739 .expect("Failed to create subregion")
740 .with_areas(custom_memory_areas.iter().cloned());
741
742 subregion.add_property(
744 "coordinate_3d".to_string(),
745 serde_json::json!(autogen_position),
746 );
747 subregion.add_property("coordinate_2d".to_string(), serde_json::json!([0, 0]));
748
749 if !subregion_inputs.is_empty() {
751 subregion.add_property(
752 "inputs".to_string(),
753 serde_json::json!(subregion_inputs.clone()),
754 );
755 }
756 if !subregion_outputs.is_empty() {
757 subregion.add_property(
758 "outputs".to_string(),
759 serde_json::json!(subregion_outputs.clone()),
760 );
761 }
762
763 let subregion_id_str = subregion.region_id.to_string();
764
765 info!(target: "feagi-bdu"," ✅ Created subregion '{}' with {} CUSTOM/MEMORY areas ({} inputs, {} outputs)",
766 region_id, custom_memory_areas.len(), subregion_inputs.len(), subregion_outputs.len());
767
768 regions_map.insert(subregion_id_str.clone(), subregion);
769 subregion_id = Some(subregion_id_str);
770 }
771
772 regions_map.insert(root_region_id_str.clone(), root_region);
773
774 let total_inputs = root_inputs.len()
776 + if let Some(ref sid) = subregion_id {
777 regions_map
778 .get(sid)
779 .and_then(|r| r.properties.get("inputs"))
780 .and_then(|v| v.as_array())
781 .map(|a| a.len())
782 .unwrap_or(0)
783 } else {
784 0
785 };
786
787 let total_outputs = root_outputs.len()
788 + if let Some(ref sid) = subregion_id {
789 regions_map
790 .get(sid)
791 .and_then(|r| r.properties.get("outputs"))
792 .and_then(|v| v.as_array())
793 .map(|a| a.len())
794 .unwrap_or(0)
795 } else {
796 0
797 };
798
799 info!(target: "feagi-bdu"," ✅ Auto-generated {} brain region(s) with {} total cortical areas ({} total inputs, {} total outputs)",
800 regions_map.len(), all_cortical_ids.len(), total_inputs, total_outputs);
801
802 let mut parent_map = std::collections::HashMap::new();
804 if let Some(ref sub_id) = subregion_id {
805 parent_map.insert(sub_id.clone(), root_region_id_str.clone());
806 info!(target: "feagi-bdu"," 🔗 Parent relationship: {} -> {}", sub_id, root_region_id_str);
807 }
808
809 (regions_map, parent_map)
810 } else {
811 info!(target: "feagi-bdu"," 📋 Genome already has {} brain regions - using existing structure", genome.brain_regions.len());
812 let mut regions_map = genome.brain_regions.clone();
813 if let Some(wrapper_id) = wrap_parentless_regions_under_named_root(&mut regions_map) {
814 info!(
815 target: "feagi-bdu",
816 " 🔗 Wrapped parentless circuit(s) under new {} ({}); original circuit names preserved",
817 ROOT_BRAIN_REGION_NAME,
818 wrapper_id
819 );
820 }
821 if let Some(circuit_name) = apply_genome_title_to_unique_top_circuit(
822 &mut regions_map,
823 &genome.metadata.genome_title,
824 ) {
825 info!(
826 target: "feagi-bdu",
827 " Applied genome_title to unique top-level circuit: {}",
828 circuit_name
829 );
830 }
831 let mut region_parent_map: std::collections::HashMap<String, String> =
835 std::collections::HashMap::new();
836 for (region_id, region) in ®ions_map {
837 if let Some(pid) = region
838 .properties
839 .get("parent_region_id")
840 .and_then(|v| v.as_str())
841 {
842 region_parent_map.insert(region_id.clone(), pid.to_string());
843 }
844 }
845 if region_parent_map.is_empty() {
846 if let Some((root_id, _)) = regions_map
847 .iter()
848 .find(|(_, r)| r.name == ROOT_BRAIN_REGION_NAME)
849 {
850 for (region_id, region) in ®ions_map {
851 if region.name == ROOT_BRAIN_REGION_NAME {
852 continue;
853 }
854 region_parent_map.insert(region_id.clone(), root_id.clone());
855 }
856 if !region_parent_map.is_empty() {
857 info!(target: "feagi-bdu",
858 " 🔗 Inferred {} sub-region parent link(s) under root {}",
859 region_parent_map.len(),
860 root_id
861 );
862 }
863 } else {
864 warn!(target: "feagi-bdu",
865 " ⚠️ brain_regions present but no '{}' and no parent_region_id — hierarchy may not load in BV",
866 ROOT_BRAIN_REGION_NAME
867 );
868 }
869 }
870 (regions_map, region_parent_map)
871 };
872
873 {
875 let mut manager = self.connectome_manager.write();
876 let brain_region_count = brain_regions_to_add.len();
877 info!(target: "feagi-bdu"," Adding {} brain regions from genome", brain_region_count);
878
879 let root_entry = brain_regions_to_add
882 .iter()
883 .find(|(_, region)| region.name == ROOT_BRAIN_REGION_NAME);
884 if let Some((root_id, root_region)) = root_entry {
885 manager.add_brain_region(root_region.clone(), None)?;
886 debug!(target: "feagi-bdu"," ✓ Added brain region: {} ({}) [parent=None]", root_id, ROOT_BRAIN_REGION_NAME);
887 }
888
889 let remaining_ids: Vec<String> = brain_regions_to_add
890 .iter()
891 .filter(|(_, region)| region.name != ROOT_BRAIN_REGION_NAME)
892 .map(|(region_id, _)| region_id.clone())
893 .collect();
894 let ordered_ids = order_regions_parent_before_child(&remaining_ids, ®ion_parent_map);
895
896 for region_id in ordered_ids {
897 let region = brain_regions_to_add.get(®ion_id).ok_or_else(|| {
898 BduError::Internal(format!(
899 "Ordered region {} missing from genome region map",
900 region_id
901 ))
902 })?;
903 let parent_id = region_parent_map.get(®ion_id).cloned();
904 manager.add_brain_region(region.clone(), parent_id.clone())?;
905 debug!(target: "feagi-bdu"," ✓ Added brain region: {} ({}) [parent={:?}]",
906 region_id, region.name, parent_id);
907 }
908
909 info!(target: "feagi-bdu"," Total brain regions in ConnectomeManager: {}", manager.get_brain_region_ids().len());
910 } self.update_stage(DevelopmentStage::Corticogenesis, 100);
913 info!(target: "feagi-bdu"," ✅ Corticogenesis complete: {} cortical areas created", total_areas);
914
915 Ok(())
916 }
917
918 fn voxelogenesis(&mut self, _genome: &RuntimeGenome) -> BduResult<()> {
920 self.update_stage(DevelopmentStage::Voxelogenesis, 0);
921 info!(target: "feagi-bdu","📐 Stage 2: Voxelogenesis - Establishing spatial framework");
922
923 self.update_stage(DevelopmentStage::Voxelogenesis, 100);
927 info!(target: "feagi-bdu"," ✅ Voxelogenesis complete: Spatial framework established");
928
929 Ok(())
930 }
931
932 fn neurogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
941 self.update_stage(DevelopmentStage::Neurogenesis, 0);
942 info!(target: "feagi-bdu","🔬 Stage 3: Neurogenesis - Generating neurons (SIMD-optimized batches)");
943
944 let expected_neurons = genome.stats.innate_neuron_count;
945 info!(target: "feagi-bdu"," Expected innate neurons from genome: {}", expected_neurons);
946
947 use feagi_structures::genomic::cortical_area::CoreCorticalType;
949 let death_id = CoreCorticalType::Death.to_cortical_id();
950 let power_id = CoreCorticalType::Power.to_cortical_id();
951 let fatigue_id = CoreCorticalType::Fatigue.to_cortical_id();
952 let pain_id = CoreCorticalType::Pain.to_cortical_id();
953 let pleasure_id = CoreCorticalType::Pleasure.to_cortical_id();
954 let fear_id = CoreCorticalType::Fear.to_cortical_id();
955 let hope_id = CoreCorticalType::Hope.to_cortical_id();
956
957 let mut core_areas = Vec::new();
958 let mut other_areas = Vec::new();
959
960 for (cortical_id, area) in genome.cortical_areas.iter() {
962 if *cortical_id == death_id {
963 core_areas.push((0, *cortical_id, area)); } else if *cortical_id == power_id {
965 core_areas.push((1, *cortical_id, area)); } else if *cortical_id == fatigue_id {
967 core_areas.push((2, *cortical_id, area)); } else if *cortical_id == pain_id {
969 core_areas.push((3, *cortical_id, area)); } else if *cortical_id == pleasure_id {
971 core_areas.push((4, *cortical_id, area)); } else if *cortical_id == fear_id {
973 core_areas.push((5, *cortical_id, area)); } else if *cortical_id == hope_id {
975 core_areas.push((6, *cortical_id, area)); } else {
977 other_areas.push((*cortical_id, area));
978 }
979 }
980
981 core_areas.sort_by_key(|(idx, _, _)| *idx);
983
984 info!(target: "feagi-bdu"," 🎯 Creating core area neurons FIRST ({} areas) for deterministic IDs", core_areas.len());
985
986 let mut total_neurons_created = 0;
987 let mut processed_count = 0;
988 let total_areas = genome.cortical_areas.len();
989
990 for (core_idx, cortical_id, area) in &core_areas {
992 let existing_core_neurons = {
993 let manager = self.connectome_manager.read();
994 let npu = manager.get_npu();
995 match npu {
996 Some(npu_arc) => {
997 let npu_lock = npu_arc.lock();
998 match npu_lock {
999 Ok(npu_guard) => {
1000 npu_guard.get_neurons_in_cortical_area(*core_idx).len()
1001 }
1002 Err(_) => 0,
1003 }
1004 }
1005 None => 0,
1006 }
1007 };
1008
1009 if existing_core_neurons > 0 {
1010 self.sync_core_neuron_params(*core_idx, area)?;
1011 let refreshed = {
1012 let manager = self.connectome_manager.read();
1013 manager.refresh_neuron_count_for_area(cortical_id)
1014 };
1015 let count = refreshed.unwrap_or(existing_core_neurons);
1016 total_neurons_created += count;
1017 info!(
1018 target: "feagi-bdu",
1019 " ↪ Skipping core neuron creation for {} (existing={}, idx={})",
1020 cortical_id.as_base_64(),
1021 count,
1022 core_idx
1023 );
1024 processed_count += 1;
1025 let progress_pct = (processed_count * 100 / total_areas.max(1)) as u8;
1026 self.update_progress(|p| {
1027 p.neurons_created = total_neurons_created;
1028 p.progress = progress_pct;
1029 });
1030 continue;
1031 }
1032 let per_voxel_count = area
1033 .properties
1034 .get("neurons_per_voxel")
1035 .and_then(|v| v.as_u64())
1036 .unwrap_or(1) as i64;
1037
1038 let cortical_id_str = cortical_id.to_string();
1039 info!(target: "feagi-bdu"," 🔋 [CORE-AREA {}] {} - dimensions: {:?}, per_voxel: {}",
1040 core_idx, cortical_id_str, area.dimensions, per_voxel_count);
1041
1042 if per_voxel_count == 0 {
1043 warn!(target: "feagi-bdu"," ⚠️ Skipping core area {} - per_voxel_neuron_cnt is 0", cortical_id_str);
1044 continue;
1045 }
1046
1047 let neurons_created = {
1049 let manager_arc = self.connectome_manager.clone();
1050 let mut manager = manager_arc.write();
1051 manager.create_neurons_for_area(cortical_id)
1052 };
1053
1054 match neurons_created {
1055 Ok(count) => {
1056 total_neurons_created += count as usize;
1057 info!(target: "feagi-bdu"," ✅ Created {} neurons for core area {} (deterministic ID: neuron {})",
1058 count, cortical_id_str, core_idx);
1059 }
1060 Err(e) => {
1061 error!(target: "feagi-bdu"," ❌ FATAL: Failed to create neurons for core area {}: {}", cortical_id_str, e);
1062 return Err(e);
1063 }
1064 }
1065
1066 processed_count += 1;
1067 let progress_pct = (processed_count * 100 / total_areas.max(1)) as u8;
1068 self.update_progress(|p| {
1069 p.neurons_created = total_neurons_created;
1070 p.progress = progress_pct;
1071 });
1072 }
1073
1074 info!(target: "feagi-bdu"," 📦 Creating neurons for {} other areas", other_areas.len());
1076 for (cortical_id, area) in &other_areas {
1077 let _per_voxel_count = area
1079 .properties
1080 .get("neurons_per_voxel")
1081 .and_then(|v| v.as_u64())
1082 .unwrap_or(1) as i64;
1083
1084 let per_voxel_count = area
1085 .properties
1086 .get("neurons_per_voxel")
1087 .and_then(|v| v.as_u64())
1088 .unwrap_or(1) as i64;
1089
1090 let cortical_id_str = cortical_id.to_string();
1091
1092 if per_voxel_count == 0 {
1093 warn!(target: "feagi-bdu"," ⚠️ Skipping area {} - per_voxel_neuron_cnt is 0 (will have NO neurons!)", cortical_id_str);
1094 continue;
1095 }
1096
1097 let neurons_created = {
1100 let manager_arc = self.connectome_manager.clone();
1101 let mut manager = manager_arc.write();
1102 manager.create_neurons_for_area(cortical_id)
1103 }; match neurons_created {
1106 Ok(count) => {
1107 total_neurons_created += count as usize;
1108 trace!(
1109 target: "feagi-bdu",
1110 "Created {} neurons for area {}",
1111 count,
1112 cortical_id_str
1113 );
1114 }
1115 Err(e) => {
1116 warn!(target: "feagi-bdu"," Failed to create neurons for {}: {} (NPU may not be connected)",
1118 cortical_id_str, e);
1119 let total_voxels = area.dimensions.width as usize
1120 * area.dimensions.height as usize
1121 * area.dimensions.depth as usize;
1122 let expected = total_voxels * per_voxel_count as usize;
1123 total_neurons_created += expected;
1124 }
1125 }
1126
1127 processed_count += 1;
1128 let progress_pct = (processed_count * 100 / total_areas.max(1)) as u8;
1130 self.update_progress(|p| {
1131 p.neurons_created = total_neurons_created;
1132 p.progress = progress_pct;
1133 });
1134 }
1135
1136 if expected_neurons > 0 && total_neurons_created != expected_neurons {
1138 trace!(target: "feagi-bdu",
1139 created_neurons = total_neurons_created,
1140 genome_stats_innate = expected_neurons,
1141 "Neuron creation complete (genome stats may only count innate neurons)"
1142 );
1143 }
1144
1145 self.update_stage(DevelopmentStage::Neurogenesis, 100);
1146 info!(target: "feagi-bdu"," ✅ Neurogenesis complete: {} neurons created", total_neurons_created);
1147
1148 Ok(())
1149 }
1150
1151 fn synaptogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
1157 self.update_stage(DevelopmentStage::Synaptogenesis, 0);
1158 info!(target: "feagi-bdu","🔗 Stage 4: Synaptogenesis - Forming synaptic connections (SIMD-optimized batches)");
1159
1160 let expected_synapses = genome.stats.innate_synapse_count;
1161 info!(target: "feagi-bdu"," Expected innate synapses from genome: {}", expected_synapses);
1162
1163 self.rebuild_memory_twin_mappings_from_genome(genome)?;
1164
1165 let mut total_synapses_created = 0;
1166 let total_areas = genome.cortical_areas.len();
1167
1168 for (idx, (_src_cortical_id, src_area)) in genome.cortical_areas.iter().enumerate() {
1171 let has_dstmap = src_area
1173 .properties
1174 .get("cortical_mapping_dst")
1175 .and_then(|v| v.as_object())
1176 .map(|m| !m.is_empty())
1177 .unwrap_or(false);
1178
1179 if !has_dstmap {
1180 trace!(target: "feagi-bdu", "No dstmap for area {}", &src_area.cortical_id);
1181 continue;
1182 }
1183
1184 let src_cortical_id = &src_area.cortical_id;
1188 let src_cortical_id_str = src_cortical_id.to_string(); let synapses_created = {
1190 let manager_arc = self.connectome_manager.clone();
1191 let mut manager = manager_arc.write();
1192 if let Some(dstmap) = src_area.properties.get("cortical_mapping_dst") {
1193 if let Some(area) = manager.get_cortical_area_mut(src_cortical_id) {
1194 area.properties
1195 .insert("cortical_mapping_dst".to_string(), dstmap.clone());
1196 }
1197 }
1198 manager.apply_cortical_mapping(src_cortical_id)
1199 }; match synapses_created {
1202 Ok(count) => {
1203 total_synapses_created += count as usize;
1204 trace!(
1205 target: "feagi-bdu",
1206 "Created {} synapses for area {}",
1207 count,
1208 src_cortical_id_str
1209 );
1210 }
1211 Err(e) => {
1212 warn!(target: "feagi-bdu"," Failed to create synapses for {}: {} (NPU may not be connected)",
1214 src_cortical_id_str, e);
1215 let estimated = estimate_synapses_for_area(src_area, genome);
1216 total_synapses_created += estimated;
1217 }
1218 }
1219
1220 let progress_pct = ((idx + 1) * 100 / total_areas.max(1)) as u8;
1222 self.update_progress(|p| {
1223 p.synapses_created = total_synapses_created;
1224 p.progress = progress_pct;
1225 });
1226 }
1227
1228 let npu_arc = {
1233 let manager = self.connectome_manager.read();
1234 manager.get_npu().cloned()
1235 };
1236 if let Some(npu_arc) = npu_arc {
1237 let mut npu_lock = npu_arc
1238 .lock()
1239 .map_err(|e| BduError::Internal(format!("Failed to lock NPU: {}", e)))?;
1240 npu_lock.rebuild_synapse_index();
1241
1242 let manager = self.connectome_manager.read();
1244 manager.update_cached_synapse_count();
1245 }
1246
1247 #[cfg(feature = "plasticity")]
1250 {
1251 use feagi_evolutionary::extract_memory_properties;
1252 use feagi_npu_plasticity::{MemoryNeuronLifecycleConfig, PlasticityExecutor};
1253
1254 let manager = self.connectome_manager.read();
1255 if let Some(executor) = manager.get_plasticity_executor() {
1256 let mut registered_count = 0;
1257
1258 for area_id in manager.get_cortical_area_ids() {
1260 if let Some(area) = manager.get_cortical_area(area_id) {
1261 if let Some(mem_props) = extract_memory_properties(&area.properties) {
1262 let upstream_areas =
1263 manager.get_episodic_memory_upstream_cortical_areas(area_id);
1264
1265 if let Some(npu_arc) = manager.get_npu() {
1268 if let Ok(mut npu) = npu_arc.lock() {
1269 let existing_configs = npu.get_all_fire_ledger_configs();
1270 for &upstream_idx in &upstream_areas {
1271 let existing = existing_configs
1272 .iter()
1273 .find(|(idx, _)| *idx == upstream_idx)
1274 .map(|(_, w)| *w)
1275 .unwrap_or(0);
1276
1277 let desired = mem_props.temporal_depth as usize;
1278 let resolved = existing.max(desired);
1279 if resolved != existing {
1280 if let Err(e) = npu.configure_fire_ledger_window(
1281 upstream_idx,
1282 resolved,
1283 ) {
1284 warn!(
1285 target: "feagi-bdu",
1286 "Failed to configure FireLedger window for upstream area idx={} (requested={}): {}",
1287 upstream_idx,
1288 resolved,
1289 e
1290 );
1291 }
1292 }
1293 }
1294 } else {
1295 warn!(target: "feagi-bdu", "Failed to lock NPU for FireLedger configuration");
1296 }
1297 }
1298
1299 if let Ok(exec) = executor.lock() {
1300 let lifecycle_config = MemoryNeuronLifecycleConfig {
1301 initial_lifespan: mem_props.init_lifespan,
1302 lifespan_growth_rate: mem_props.lifespan_growth_rate,
1303 longterm_threshold: mem_props.longterm_threshold,
1304 max_reactivations: 1000,
1305 };
1306
1307 exec.register_memory_area(
1308 area.cortical_idx,
1309 area_id.as_base_64(),
1310 mem_props.temporal_depth,
1311 upstream_areas.clone(),
1312 Some(lifecycle_config),
1313 mem_props.mp_learning_enabled,
1314 );
1315
1316 registered_count += 1;
1317 }
1318 }
1319 }
1320 }
1321 let _ = registered_count; }
1323 }
1324
1325 if expected_synapses > 0 {
1327 let diff = (total_synapses_created as i64 - expected_synapses as i64).abs();
1328 let diff_pct = (diff as f64 / expected_synapses.max(1) as f64) * 100.0;
1329
1330 if diff_pct > 10.0 {
1331 warn!(target: "feagi-bdu",
1332 "Synapse count variance: created {} but genome stats expected {} ({:.1}% difference)",
1333 total_synapses_created, expected_synapses, diff_pct
1334 );
1335 } else {
1336 info!(target: "feagi-bdu",
1337 "Synapse count matches genome stats within {:.1}% ({} vs {})",
1338 diff_pct, total_synapses_created, expected_synapses
1339 );
1340 }
1341 }
1342
1343 self.update_stage(DevelopmentStage::Synaptogenesis, 100);
1344 info!(target: "feagi-bdu"," ✅ Synaptogenesis complete: {} synapses created", total_synapses_created);
1345
1346 Ok(())
1347 }
1348
1349 fn rebuild_memory_twin_mappings_from_genome(
1350 &mut self,
1351 genome: &RuntimeGenome,
1352 ) -> BduResult<()> {
1353 use feagi_structures::genomic::cortical_area::CorticalAreaType;
1354 let mut repaired = 0usize;
1355
1356 for (memory_id, memory_area) in genome.cortical_areas.iter() {
1357 let is_memory = matches!(
1358 memory_area.cortical_id.as_cortical_type(),
1359 Ok(CorticalAreaType::Memory(_))
1360 ) || memory_area
1361 .properties
1362 .get("is_mem_type")
1363 .and_then(|v| v.as_bool())
1364 .unwrap_or(false)
1365 || memory_area
1366 .properties
1367 .get("cortical_group")
1368 .and_then(|v| v.as_str())
1369 .is_some_and(|v| v.eq_ignore_ascii_case("MEMORY"));
1370 if !is_memory {
1371 continue;
1372 }
1373
1374 let Some(dstmap) = memory_area
1375 .properties
1376 .get("cortical_mapping_dst")
1377 .and_then(|v| v.as_object())
1378 else {
1379 continue;
1380 };
1381
1382 for (dst_id_str, rules) in dstmap {
1383 let Some(rule_array) = rules.as_array() else {
1384 continue;
1385 };
1386 let has_replay = rule_array.iter().any(|rule| {
1387 rule.get("morphology_id")
1388 .and_then(|v| v.as_str())
1389 .is_some_and(|id| id == "memory_replay")
1390 });
1391 if !has_replay {
1392 continue;
1393 }
1394
1395 let dst_id = match CorticalID::try_from_base_64(dst_id_str) {
1396 Ok(id) => id,
1397 Err(_) => {
1398 warn!(
1399 target: "feagi-bdu",
1400 "Invalid twin cortical ID in memory_replay dstmap: {}",
1401 dst_id_str
1402 );
1403 continue;
1404 }
1405 };
1406
1407 let Some(twin_area) = genome.cortical_areas.get(&dst_id) else {
1408 continue;
1409 };
1410 let Some(upstream_id_str) = twin_area
1411 .properties
1412 .get("memory_twin_of")
1413 .and_then(|v| v.as_str())
1414 else {
1415 continue;
1416 };
1417 let upstream_id = match CorticalID::try_from_base_64(upstream_id_str) {
1418 Ok(id) => id,
1419 Err(_) => {
1420 warn!(
1421 target: "feagi-bdu",
1422 "Invalid memory_twin_of value on twin area {}: {}",
1423 dst_id.as_base_64(),
1424 upstream_id_str
1425 );
1426 continue;
1427 }
1428 };
1429
1430 let mut manager = self.connectome_manager.write();
1431 if let Err(e) = manager.ensure_memory_twin_area(memory_id, &upstream_id) {
1432 warn!(
1433 target: "feagi-bdu",
1434 "Failed to rebuild memory twin mapping for memory {} upstream {}: {}",
1435 memory_id.as_base_64(),
1436 upstream_id.as_base_64(),
1437 e
1438 );
1439 continue;
1440 }
1441 repaired += 1;
1442 }
1443 }
1444
1445 info!(
1446 target: "feagi-bdu",
1447 "Rebuilt {} memory twin mapping(s) from genome",
1448 repaired
1449 );
1450 Ok(())
1451 }
1452}
1453
1454fn estimate_synapses_for_area(
1458 src_area: &CorticalArea,
1459 genome: &feagi_evolutionary::RuntimeGenome,
1460) -> usize {
1461 let dstmap = match src_area.properties.get("cortical_mapping_dst") {
1462 Some(serde_json::Value::Object(map)) => map,
1463 _ => return 0,
1464 };
1465
1466 let mut total = 0;
1467
1468 for (dst_id, rules) in dstmap {
1469 let dst_cortical_id = match feagi_evolutionary::string_to_cortical_id(dst_id) {
1471 Ok(id) => id,
1472 Err(_) => continue,
1473 };
1474 let dst_area = match genome.cortical_areas.get(&dst_cortical_id) {
1475 Some(area) => area,
1476 None => continue,
1477 };
1478
1479 let rules_array = match rules.as_array() {
1480 Some(arr) => arr,
1481 None => continue,
1482 };
1483
1484 for rule in rules_array {
1485 let morphology_id = rule
1486 .get("morphology_id")
1487 .and_then(|v| v.as_str())
1488 .unwrap_or("unknown");
1489 let scalar = rule
1490 .get("morphology_scalar")
1491 .and_then(|v| v.as_i64())
1492 .unwrap_or(1) as usize;
1493
1494 let src_per_voxel = src_area
1496 .properties
1497 .get("neurons_per_voxel")
1498 .and_then(|v| v.as_u64())
1499 .unwrap_or(1) as usize;
1500 let dst_per_voxel = dst_area
1501 .properties
1502 .get("neurons_per_voxel")
1503 .and_then(|v| v.as_u64())
1504 .unwrap_or(1) as usize;
1505
1506 let src_voxels =
1507 src_area.dimensions.width * src_area.dimensions.height * src_area.dimensions.depth;
1508 let dst_voxels =
1509 dst_area.dimensions.width * dst_area.dimensions.height * dst_area.dimensions.depth;
1510
1511 let src_neurons = src_voxels as usize * src_per_voxel;
1512 let dst_neurons = dst_voxels as usize * dst_per_voxel as usize;
1513
1514 let count = match morphology_id {
1516 "block_to_block" => src_neurons * dst_per_voxel * scalar,
1517 "projector" | "transpose_xy" | "transpose_yz" | "transpose_xz"
1518 | "centered_projector" => src_neurons * dst_neurons * scalar,
1519 _ if morphology_id.contains("lateral") => src_neurons * scalar,
1520 _ => (src_neurons * scalar).min(src_neurons * dst_neurons / 10),
1521 };
1522
1523 total += count;
1524 }
1525 }
1526
1527 total
1528}
1529
1530impl Neuroembryogenesis {
1531 fn calculate_autogen_region_position(
1533 root_area_ids: &[CorticalID],
1534 genome: &feagi_evolutionary::RuntimeGenome,
1535 ) -> [i32; 3] {
1536 if root_area_ids.is_empty() {
1537 return [100, 0, 0];
1538 }
1539
1540 let mut min_x = i32::MAX;
1541 let mut max_x = i32::MIN;
1542 let mut min_y = i32::MAX;
1543 let mut max_y = i32::MIN;
1544 let mut min_z = i32::MAX;
1545 let mut max_z = i32::MIN;
1546
1547 for cortical_id in root_area_ids {
1548 if let Some(area) = genome.cortical_areas.get(cortical_id) {
1549 let pos: (i32, i32, i32) = area.position.into();
1550 let dims = (
1551 area.dimensions.width as i32,
1552 area.dimensions.height as i32,
1553 area.dimensions.depth as i32,
1554 );
1555
1556 min_x = min_x.min(pos.0);
1557 max_x = max_x.max(pos.0 + dims.0);
1558 min_y = min_y.min(pos.1);
1559 max_y = max_y.max(pos.1 + dims.1);
1560 min_z = min_z.min(pos.2);
1561 max_z = max_z.max(pos.2 + dims.2);
1562 }
1563 }
1564
1565 let bbox_width = (max_x - min_x).max(1);
1566 let padding = (bbox_width / 5).max(50);
1567 let autogen_x = max_x + padding;
1568 let autogen_y = (min_y + max_y) / 2;
1569 let autogen_z = (min_z + max_z) / 2;
1570
1571 info!(target: "feagi-bdu",
1572 " 📐 Autogen position: ({}, {}, {}) [padding: {}]",
1573 autogen_x, autogen_y, autogen_z, padding);
1574
1575 [autogen_x, autogen_y, autogen_z]
1576 }
1577
1578 fn analyze_region_io(
1584 region_area_ids: &[feagi_structures::genomic::cortical_area::CorticalID],
1585 all_cortical_areas: &std::collections::HashMap<CorticalID, CorticalArea>,
1586 ) -> (Vec<String>, Vec<String>) {
1587 let area_set: std::collections::HashSet<_> = region_area_ids.iter().cloned().collect();
1588 let mut inputs = Vec::new();
1589 let mut outputs = Vec::new();
1590
1591 let extract_destinations = |area: &CorticalArea| -> Vec<String> {
1593 area.properties
1594 .get("cortical_mapping_dst")
1595 .and_then(|v| v.as_object())
1596 .map(|obj| obj.keys().cloned().collect())
1597 .unwrap_or_default()
1598 };
1599
1600 for area_id in region_area_ids {
1602 if let Some(area) = all_cortical_areas.get(area_id) {
1603 let destinations = extract_destinations(area);
1604 let external_destinations: Vec<_> = destinations
1606 .iter()
1607 .filter_map(|dest| feagi_evolutionary::string_to_cortical_id(dest).ok())
1608 .filter(|dest_id| !area_set.contains(dest_id))
1609 .collect();
1610
1611 if !external_destinations.is_empty() {
1612 outputs.push(area_id.as_base_64());
1613 }
1614 }
1615 }
1616
1617 for (source_area_id, source_area) in all_cortical_areas.iter() {
1619 if area_set.contains(source_area_id) {
1621 continue;
1622 }
1623
1624 let destinations = extract_destinations(source_area);
1625 for dest_str in destinations {
1626 if let Ok(dest_id) = feagi_evolutionary::string_to_cortical_id(&dest_str) {
1627 if area_set.contains(&dest_id) {
1628 let dest_string = dest_id.as_base_64();
1629 if !inputs.contains(&dest_string) {
1630 inputs.push(dest_string);
1631 }
1632 }
1633 }
1634 }
1635 }
1636
1637 (inputs, outputs)
1638 }
1639
1640 fn update_stage(&self, stage: DevelopmentStage, progress: u8) {
1642 let mut p = self.progress.write();
1643 p.stage = stage;
1644 p.progress = progress;
1645 p.duration_ms = self.start_time.elapsed().as_millis() as u64;
1646 }
1647
1648 fn update_progress<F>(&self, f: F)
1650 where
1651 F: FnOnce(&mut DevelopmentProgress),
1652 {
1653 let mut p = self.progress.write();
1654 f(&mut p);
1655 p.duration_ms = self.start_time.elapsed().as_millis() as u64;
1656 }
1657}
1658
1659#[cfg(test)]
1660mod tests {
1661 use super::*;
1662 use feagi_evolutionary::create_genome_with_core_morphologies;
1663 use feagi_structures::genomic::cortical_area::CorticalAreaDimensions;
1664
1665 #[test]
1666 fn test_neuroembryogenesis_creation() {
1667 let manager = ConnectomeManager::instance();
1668 let neuro = Neuroembryogenesis::new(manager);
1669
1670 let progress = neuro.get_progress();
1671 assert_eq!(progress.stage, DevelopmentStage::Initialization);
1672 assert_eq!(progress.progress, 0);
1673 }
1674
1675 #[test]
1676 fn autogen_subregion_display_name_uses_title_when_meaningful() {
1677 assert_eq!(
1678 autogen_subregion_display_name("My Shared Circuit"),
1679 "My Shared Circuit"
1680 );
1681 }
1682
1683 #[test]
1684 fn autogen_subregion_display_name_falls_back_for_untitled() {
1685 assert_eq!(
1686 autogen_subregion_display_name("Untitled"),
1687 "Autogen Circuit"
1688 );
1689 assert_eq!(
1690 autogen_subregion_display_name("untitled"),
1691 "Autogen Circuit"
1692 );
1693 }
1694
1695 #[test]
1696 fn autogen_subregion_display_name_falls_back_for_blank() {
1697 assert_eq!(autogen_subregion_display_name(""), "Autogen Circuit");
1698 assert_eq!(autogen_subregion_display_name(" "), "Autogen Circuit");
1699 }
1700
1701 #[test]
1702 fn order_regions_puts_parent_before_grandchild_even_when_child_is_listed_first() {
1703 let root = "root".to_string();
1704 let parent = "look-for-people".to_string();
1705 let grandchild = "wave".to_string();
1706 let sibling = "look-for-ball".to_string();
1707
1708 let remaining = vec![grandchild.clone(), sibling.clone(), parent.clone()];
1710 let mut parent_map = std::collections::HashMap::new();
1711 parent_map.insert(parent.clone(), root.clone());
1712 parent_map.insert(grandchild.clone(), parent.clone());
1713 parent_map.insert(sibling.clone(), root);
1714
1715 let ordered = order_regions_parent_before_child(&remaining, &parent_map);
1716 let parent_idx = ordered.iter().position(|id| id == &parent).unwrap();
1717 let grandchild_idx = ordered.iter().position(|id| id == &grandchild).unwrap();
1718
1719 assert_eq!(ordered.len(), 3);
1720 assert!(
1721 parent_idx < grandchild_idx,
1722 "parent must precede grandchild, got {:?}",
1723 ordered
1724 );
1725 }
1726
1727 #[test]
1728 fn order_regions_keeps_flat_children_when_parent_is_already_inserted() {
1729 let remaining = vec!["a".to_string(), "b".to_string(), "c".to_string()];
1730 let mut parent_map = std::collections::HashMap::new();
1731 parent_map.insert("a".to_string(), "root".to_string());
1732 parent_map.insert("b".to_string(), "root".to_string());
1733 parent_map.insert("c".to_string(), "root".to_string());
1734
1735 let ordered = order_regions_parent_before_child(&remaining, &parent_map);
1736 let mut sorted = ordered.clone();
1737 sorted.sort();
1738 assert_eq!(sorted, remaining);
1739 }
1740
1741 #[test]
1742 fn order_regions_appends_cycle_members_after_acyclic_prefix() {
1743 let remaining = vec!["a".to_string(), "b".to_string(), "ok".to_string()];
1744 let mut parent_map = std::collections::HashMap::new();
1745 parent_map.insert("a".to_string(), "b".to_string());
1746 parent_map.insert("b".to_string(), "a".to_string());
1747 parent_map.insert("ok".to_string(), "root".to_string());
1748
1749 let ordered = order_regions_parent_before_child(&remaining, &parent_map);
1750 assert_eq!(ordered.first().map(String::as_str), Some("ok"));
1751 assert_eq!(ordered.len(), 3);
1752 assert!(ordered.contains(&"a".to_string()));
1753 assert!(ordered.contains(&"b".to_string()));
1754 }
1755
1756 #[test]
1757 fn test_development_from_minimal_genome() {
1758 ConnectomeManager::reset_for_testing(); let manager = ConnectomeManager::instance();
1760 let mut neuro = Neuroembryogenesis::new(manager.clone());
1761
1762 let mut genome = create_genome_with_core_morphologies(
1764 "test_genome".to_string(),
1765 "Test Genome".to_string(),
1766 );
1767
1768 let cortical_id = CorticalID::try_from_bytes(b"cst_neur").unwrap(); let cortical_type = cortical_id
1770 .as_cortical_type()
1771 .expect("Failed to get cortical type");
1772 let area = CorticalArea::new(
1773 cortical_id,
1774 0,
1775 "Test Area".to_string(),
1776 CorticalAreaDimensions::new(10, 10, 10).unwrap(),
1777 (0, 0, 0).into(),
1778 cortical_type,
1779 )
1780 .expect("Failed to create cortical area");
1781 genome.cortical_areas.insert(cortical_id, area);
1782
1783 let result = neuro.develop_from_genome(&genome);
1785 assert!(result.is_ok(), "Development failed: {:?}", result);
1786
1787 let progress = neuro.get_progress();
1789 assert_eq!(progress.stage, DevelopmentStage::Completed);
1790 assert_eq!(progress.progress, 100);
1791 assert_eq!(progress.cortical_areas_created, 1);
1792
1793 println!("✅ Development completed in {}ms", progress.duration_ms);
1797 }
1798}