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
911 manager.replace_classifiers(genome.classifiers.clone());
912 for classifier in genome.classifiers.values() {
913 if let Some(region) = manager.get_brain_region_mut(&classifier.parent_region_id) {
914 for area_id in classifier.owned_area_ids() {
915 if let Ok(cortical_id) =
916 feagi_structures::genomic::cortical_area::CorticalID::try_from_base_64(
917 &area_id,
918 )
919 {
920 region.add_area(cortical_id);
921 }
922 }
923 }
924 }
925 manager.apply_loaded_classifier_assemblies();
926 info!(
927 target: "feagi-bdu",
928 " Loaded {} classifier assemblies from genome",
929 genome.classifiers.len()
930 );
931 } self.update_stage(DevelopmentStage::Corticogenesis, 100);
934 info!(target: "feagi-bdu"," ✅ Corticogenesis complete: {} cortical areas created", total_areas);
935
936 Ok(())
937 }
938
939 fn voxelogenesis(&mut self, _genome: &RuntimeGenome) -> BduResult<()> {
941 self.update_stage(DevelopmentStage::Voxelogenesis, 0);
942 info!(target: "feagi-bdu","📐 Stage 2: Voxelogenesis - Establishing spatial framework");
943
944 self.update_stage(DevelopmentStage::Voxelogenesis, 100);
948 info!(target: "feagi-bdu"," ✅ Voxelogenesis complete: Spatial framework established");
949
950 Ok(())
951 }
952
953 fn neurogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
962 self.update_stage(DevelopmentStage::Neurogenesis, 0);
963 info!(target: "feagi-bdu","🔬 Stage 3: Neurogenesis - Generating neurons (SIMD-optimized batches)");
964
965 let expected_neurons = genome.stats.innate_neuron_count;
966 info!(target: "feagi-bdu"," Expected innate neurons from genome: {}", expected_neurons);
967
968 use feagi_structures::genomic::cortical_area::CoreCorticalType;
970 let death_id = CoreCorticalType::Death.to_cortical_id();
971 let power_id = CoreCorticalType::Power.to_cortical_id();
972 let fatigue_id = CoreCorticalType::Fatigue.to_cortical_id();
973 let pain_id = CoreCorticalType::Pain.to_cortical_id();
974 let pleasure_id = CoreCorticalType::Pleasure.to_cortical_id();
975 let fear_id = CoreCorticalType::Fear.to_cortical_id();
976 let hope_id = CoreCorticalType::Hope.to_cortical_id();
977
978 let mut core_areas = Vec::new();
979 let mut other_areas = Vec::new();
980
981 for (cortical_id, area) in genome.cortical_areas.iter() {
983 if *cortical_id == death_id {
984 core_areas.push((0, *cortical_id, area)); } else if *cortical_id == power_id {
986 core_areas.push((1, *cortical_id, area)); } else if *cortical_id == fatigue_id {
988 core_areas.push((2, *cortical_id, area)); } else if *cortical_id == pain_id {
990 core_areas.push((3, *cortical_id, area)); } else if *cortical_id == pleasure_id {
992 core_areas.push((4, *cortical_id, area)); } else if *cortical_id == fear_id {
994 core_areas.push((5, *cortical_id, area)); } else if *cortical_id == hope_id {
996 core_areas.push((6, *cortical_id, area)); } else {
998 other_areas.push((*cortical_id, area));
999 }
1000 }
1001
1002 core_areas.sort_by_key(|(idx, _, _)| *idx);
1004
1005 info!(target: "feagi-bdu"," 🎯 Creating core area neurons FIRST ({} areas) for deterministic IDs", core_areas.len());
1006
1007 let mut total_neurons_created = 0;
1008 let mut processed_count = 0;
1009 let total_areas = genome.cortical_areas.len();
1010
1011 for (core_idx, cortical_id, area) in &core_areas {
1013 let existing_core_neurons = {
1014 let manager = self.connectome_manager.read();
1015 let npu = manager.get_npu();
1016 match npu {
1017 Some(npu_arc) => {
1018 let npu_lock = npu_arc.lock();
1019 match npu_lock {
1020 Ok(npu_guard) => {
1021 npu_guard.get_neurons_in_cortical_area(*core_idx).len()
1022 }
1023 Err(_) => 0,
1024 }
1025 }
1026 None => 0,
1027 }
1028 };
1029
1030 if existing_core_neurons > 0 {
1031 self.sync_core_neuron_params(*core_idx, area)?;
1032 let refreshed = {
1033 let manager = self.connectome_manager.read();
1034 manager.refresh_neuron_count_for_area(cortical_id)
1035 };
1036 let count = refreshed.unwrap_or(existing_core_neurons);
1037 total_neurons_created += count;
1038 info!(
1039 target: "feagi-bdu",
1040 " ↪ Skipping core neuron creation for {} (existing={}, idx={})",
1041 cortical_id.as_base_64(),
1042 count,
1043 core_idx
1044 );
1045 processed_count += 1;
1046 let progress_pct = (processed_count * 100 / total_areas.max(1)) as u8;
1047 self.update_progress(|p| {
1048 p.neurons_created = total_neurons_created;
1049 p.progress = progress_pct;
1050 });
1051 continue;
1052 }
1053 let per_voxel_count = area
1054 .properties
1055 .get("neurons_per_voxel")
1056 .and_then(|v| v.as_u64())
1057 .unwrap_or(1) as i64;
1058
1059 let cortical_id_str = cortical_id.to_string();
1060 info!(target: "feagi-bdu"," 🔋 [CORE-AREA {}] {} - dimensions: {:?}, per_voxel: {}",
1061 core_idx, cortical_id_str, area.dimensions, per_voxel_count);
1062
1063 if per_voxel_count == 0 {
1064 warn!(target: "feagi-bdu"," ⚠️ Skipping core area {} - per_voxel_neuron_cnt is 0", cortical_id_str);
1065 continue;
1066 }
1067
1068 let neurons_created = {
1070 let manager_arc = self.connectome_manager.clone();
1071 let mut manager = manager_arc.write();
1072 manager.create_neurons_for_area(cortical_id)
1073 };
1074
1075 match neurons_created {
1076 Ok(count) => {
1077 total_neurons_created += count as usize;
1078 info!(target: "feagi-bdu"," ✅ Created {} neurons for core area {} (deterministic ID: neuron {})",
1079 count, cortical_id_str, core_idx);
1080 }
1081 Err(e) => {
1082 error!(target: "feagi-bdu"," ❌ FATAL: Failed to create neurons for core area {}: {}", cortical_id_str, e);
1083 return Err(e);
1084 }
1085 }
1086
1087 processed_count += 1;
1088 let progress_pct = (processed_count * 100 / total_areas.max(1)) as u8;
1089 self.update_progress(|p| {
1090 p.neurons_created = total_neurons_created;
1091 p.progress = progress_pct;
1092 });
1093 }
1094
1095 info!(target: "feagi-bdu"," 📦 Creating neurons for {} other areas", other_areas.len());
1097 for (cortical_id, area) in &other_areas {
1098 let _per_voxel_count = area
1100 .properties
1101 .get("neurons_per_voxel")
1102 .and_then(|v| v.as_u64())
1103 .unwrap_or(1) as i64;
1104
1105 let per_voxel_count = area
1106 .properties
1107 .get("neurons_per_voxel")
1108 .and_then(|v| v.as_u64())
1109 .unwrap_or(1) as i64;
1110
1111 let cortical_id_str = cortical_id.to_string();
1112
1113 if per_voxel_count == 0 {
1114 warn!(target: "feagi-bdu"," ⚠️ Skipping area {} - per_voxel_neuron_cnt is 0 (will have NO neurons!)", cortical_id_str);
1115 continue;
1116 }
1117
1118 let neurons_created = {
1121 let manager_arc = self.connectome_manager.clone();
1122 let mut manager = manager_arc.write();
1123 manager.create_neurons_for_area(cortical_id)
1124 }; match neurons_created {
1127 Ok(count) => {
1128 total_neurons_created += count as usize;
1129 trace!(
1130 target: "feagi-bdu",
1131 "Created {} neurons for area {}",
1132 count,
1133 cortical_id_str
1134 );
1135 }
1136 Err(e) => {
1137 warn!(target: "feagi-bdu"," Failed to create neurons for {}: {} (NPU may not be connected)",
1139 cortical_id_str, e);
1140 let total_voxels = area.dimensions.width as usize
1141 * area.dimensions.height as usize
1142 * area.dimensions.depth as usize;
1143 let expected = total_voxels * per_voxel_count as usize;
1144 total_neurons_created += expected;
1145 }
1146 }
1147
1148 processed_count += 1;
1149 let progress_pct = (processed_count * 100 / total_areas.max(1)) as u8;
1151 self.update_progress(|p| {
1152 p.neurons_created = total_neurons_created;
1153 p.progress = progress_pct;
1154 });
1155 }
1156
1157 if expected_neurons > 0 && total_neurons_created != expected_neurons {
1159 trace!(target: "feagi-bdu",
1160 created_neurons = total_neurons_created,
1161 genome_stats_innate = expected_neurons,
1162 "Neuron creation complete (genome stats may only count innate neurons)"
1163 );
1164 }
1165
1166 self.update_stage(DevelopmentStage::Neurogenesis, 100);
1167 info!(target: "feagi-bdu"," ✅ Neurogenesis complete: {} neurons created", total_neurons_created);
1168
1169 Ok(())
1170 }
1171
1172 fn synaptogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
1178 self.update_stage(DevelopmentStage::Synaptogenesis, 0);
1179 info!(target: "feagi-bdu","🔗 Stage 4: Synaptogenesis - Forming synaptic connections (SIMD-optimized batches)");
1180
1181 let expected_synapses = genome.stats.innate_synapse_count;
1182 info!(target: "feagi-bdu"," Expected innate synapses from genome: {}", expected_synapses);
1183
1184 self.rebuild_memory_twin_mappings_from_genome(genome)?;
1185
1186 let mut total_synapses_created = 0;
1187 let total_areas = genome.cortical_areas.len();
1188
1189 for (idx, (_src_cortical_id, src_area)) in genome.cortical_areas.iter().enumerate() {
1192 let has_dstmap = src_area
1194 .properties
1195 .get("cortical_mapping_dst")
1196 .and_then(|v| v.as_object())
1197 .map(|m| !m.is_empty())
1198 .unwrap_or(false);
1199
1200 if !has_dstmap {
1201 trace!(target: "feagi-bdu", "No dstmap for area {}", &src_area.cortical_id);
1202 continue;
1203 }
1204
1205 let src_cortical_id = &src_area.cortical_id;
1209 let src_cortical_id_str = src_cortical_id.to_string(); let synapses_created = {
1211 let manager_arc = self.connectome_manager.clone();
1212 let mut manager = manager_arc.write();
1213 if let Some(dstmap) = src_area.properties.get("cortical_mapping_dst") {
1214 if let Some(area) = manager.get_cortical_area_mut(src_cortical_id) {
1215 area.properties
1216 .insert("cortical_mapping_dst".to_string(), dstmap.clone());
1217 }
1218 }
1219 manager.apply_cortical_mapping(src_cortical_id)
1220 }; match synapses_created {
1223 Ok(count) => {
1224 total_synapses_created += count as usize;
1225 trace!(
1226 target: "feagi-bdu",
1227 "Created {} synapses for area {}",
1228 count,
1229 src_cortical_id_str
1230 );
1231 }
1232 Err(e) => {
1233 warn!(target: "feagi-bdu"," Failed to create synapses for {}: {} (NPU may not be connected)",
1235 src_cortical_id_str, e);
1236 let estimated = estimate_synapses_for_area(src_area, genome);
1237 total_synapses_created += estimated;
1238 }
1239 }
1240
1241 let progress_pct = ((idx + 1) * 100 / total_areas.max(1)) as u8;
1243 self.update_progress(|p| {
1244 p.synapses_created = total_synapses_created;
1245 p.progress = progress_pct;
1246 });
1247 }
1248
1249 let npu_arc = {
1254 let manager = self.connectome_manager.read();
1255 manager.get_npu().cloned()
1256 };
1257 if let Some(npu_arc) = npu_arc {
1258 let mut npu_lock = npu_arc
1259 .lock()
1260 .map_err(|e| BduError::Internal(format!("Failed to lock NPU: {}", e)))?;
1261 npu_lock.rebuild_synapse_index();
1262
1263 let manager = self.connectome_manager.read();
1265 manager.update_cached_synapse_count();
1266 }
1267
1268 #[cfg(feature = "plasticity")]
1271 {
1272 use feagi_evolutionary::extract_memory_properties;
1273 use feagi_npu_plasticity::{MemoryNeuronLifecycleConfig, PlasticityExecutor};
1274
1275 let manager = self.connectome_manager.read();
1276 if let Some(executor) = manager.get_plasticity_executor() {
1277 let mut registered_count = 0;
1278
1279 for area_id in manager.get_cortical_area_ids() {
1281 if let Some(area) = manager.get_cortical_area(area_id) {
1282 if let Some(mem_props) = extract_memory_properties(&area.properties) {
1283 let upstream_areas =
1284 manager.get_episodic_memory_upstream_cortical_areas(area_id);
1285
1286 if let Some(npu_arc) = manager.get_npu() {
1289 if let Ok(mut npu) = npu_arc.lock() {
1290 let existing_configs = npu.get_all_fire_ledger_configs();
1291 for &upstream_idx in &upstream_areas {
1292 let existing = existing_configs
1293 .iter()
1294 .find(|(idx, _)| *idx == upstream_idx)
1295 .map(|(_, w)| *w)
1296 .unwrap_or(0);
1297
1298 let desired = mem_props.temporal_depth as usize;
1299 let resolved = existing.max(desired);
1300 if resolved != existing {
1301 if let Err(e) = npu.configure_fire_ledger_window(
1302 upstream_idx,
1303 resolved,
1304 ) {
1305 warn!(
1306 target: "feagi-bdu",
1307 "Failed to configure FireLedger window for upstream area idx={} (requested={}): {}",
1308 upstream_idx,
1309 resolved,
1310 e
1311 );
1312 }
1313 }
1314 }
1315 } else {
1316 warn!(target: "feagi-bdu", "Failed to lock NPU for FireLedger configuration");
1317 }
1318 }
1319
1320 if let Ok(exec) = executor.lock() {
1321 let lifecycle_config = MemoryNeuronLifecycleConfig {
1322 initial_lifespan: mem_props.init_lifespan,
1323 lifespan_growth_rate: mem_props.lifespan_growth_rate,
1324 longterm_threshold: mem_props.longterm_threshold,
1325 max_reactivations: 1000,
1326 };
1327
1328 exec.register_memory_area(
1329 area.cortical_idx,
1330 area_id.as_base_64(),
1331 mem_props.temporal_depth,
1332 upstream_areas.clone(),
1333 Some(lifecycle_config),
1334 mem_props.mp_learning_enabled,
1335 );
1336 manager.configure_memory_scan_on_executor(&*exec, area_id);
1337
1338 registered_count += 1;
1339 }
1340 }
1341 }
1342 }
1343 let _ = registered_count; }
1345 }
1346
1347 if expected_synapses > 0 {
1349 let diff = (total_synapses_created as i64 - expected_synapses as i64).abs();
1350 let diff_pct = (diff as f64 / expected_synapses.max(1) as f64) * 100.0;
1351
1352 if diff_pct > 10.0 {
1353 warn!(target: "feagi-bdu",
1354 "Synapse count variance: created {} but genome stats expected {} ({:.1}% difference)",
1355 total_synapses_created, expected_synapses, diff_pct
1356 );
1357 } else {
1358 info!(target: "feagi-bdu",
1359 "Synapse count matches genome stats within {:.1}% ({} vs {})",
1360 diff_pct, total_synapses_created, expected_synapses
1361 );
1362 }
1363 }
1364
1365 self.update_stage(DevelopmentStage::Synaptogenesis, 100);
1366 info!(target: "feagi-bdu"," ✅ Synaptogenesis complete: {} synapses created", total_synapses_created);
1367
1368 Ok(())
1369 }
1370
1371 fn rebuild_memory_twin_mappings_from_genome(
1372 &mut self,
1373 genome: &RuntimeGenome,
1374 ) -> BduResult<()> {
1375 use feagi_structures::genomic::cortical_area::CorticalAreaType;
1376 let mut repaired = 0usize;
1377
1378 for (memory_id, memory_area) in genome.cortical_areas.iter() {
1379 let is_memory = matches!(
1380 memory_area.cortical_id.as_cortical_type(),
1381 Ok(CorticalAreaType::Memory(_))
1382 ) || memory_area
1383 .properties
1384 .get("is_mem_type")
1385 .and_then(|v| v.as_bool())
1386 .unwrap_or(false)
1387 || memory_area
1388 .properties
1389 .get("cortical_group")
1390 .and_then(|v| v.as_str())
1391 .is_some_and(|v| v.eq_ignore_ascii_case("MEMORY"));
1392 if !is_memory {
1393 continue;
1394 }
1395
1396 let Some(dstmap) = memory_area
1397 .properties
1398 .get("cortical_mapping_dst")
1399 .and_then(|v| v.as_object())
1400 else {
1401 continue;
1402 };
1403
1404 for (dst_id_str, rules) in dstmap {
1405 let Some(rule_array) = rules.as_array() else {
1406 continue;
1407 };
1408 let has_replay = rule_array.iter().any(|rule| {
1409 rule.get("morphology_id")
1410 .and_then(|v| v.as_str())
1411 .is_some_and(|id| id == "memory_replay")
1412 });
1413 if !has_replay {
1414 continue;
1415 }
1416
1417 let dst_id = match CorticalID::try_from_base_64(dst_id_str) {
1418 Ok(id) => id,
1419 Err(_) => {
1420 warn!(
1421 target: "feagi-bdu",
1422 "Invalid twin cortical ID in memory_replay dstmap: {}",
1423 dst_id_str
1424 );
1425 continue;
1426 }
1427 };
1428
1429 let Some(twin_area) = genome.cortical_areas.get(&dst_id) else {
1430 continue;
1431 };
1432 let Some(upstream_id_str) = twin_area
1433 .properties
1434 .get("memory_twin_of")
1435 .and_then(|v| v.as_str())
1436 else {
1437 continue;
1438 };
1439 let upstream_id = match CorticalID::try_from_base_64(upstream_id_str) {
1440 Ok(id) => id,
1441 Err(_) => {
1442 warn!(
1443 target: "feagi-bdu",
1444 "Invalid memory_twin_of value on twin area {}: {}",
1445 dst_id.as_base_64(),
1446 upstream_id_str
1447 );
1448 continue;
1449 }
1450 };
1451
1452 let mut manager = self.connectome_manager.write();
1453 if let Err(e) = manager.ensure_memory_twin_area(memory_id, &upstream_id) {
1454 warn!(
1455 target: "feagi-bdu",
1456 "Failed to rebuild memory twin mapping for memory {} upstream {}: {}",
1457 memory_id.as_base_64(),
1458 upstream_id.as_base_64(),
1459 e
1460 );
1461 continue;
1462 }
1463 repaired += 1;
1464 }
1465 }
1466
1467 info!(
1468 target: "feagi-bdu",
1469 "Rebuilt {} memory twin mapping(s) from genome",
1470 repaired
1471 );
1472 Ok(())
1473 }
1474}
1475
1476fn estimate_synapses_for_area(
1480 src_area: &CorticalArea,
1481 genome: &feagi_evolutionary::RuntimeGenome,
1482) -> usize {
1483 let dstmap = match src_area.properties.get("cortical_mapping_dst") {
1484 Some(serde_json::Value::Object(map)) => map,
1485 _ => return 0,
1486 };
1487
1488 let mut total = 0;
1489
1490 for (dst_id, rules) in dstmap {
1491 let dst_cortical_id = match feagi_evolutionary::string_to_cortical_id(dst_id) {
1493 Ok(id) => id,
1494 Err(_) => continue,
1495 };
1496 let dst_area = match genome.cortical_areas.get(&dst_cortical_id) {
1497 Some(area) => area,
1498 None => continue,
1499 };
1500
1501 let rules_array = match rules.as_array() {
1502 Some(arr) => arr,
1503 None => continue,
1504 };
1505
1506 for rule in rules_array {
1507 let morphology_id = rule
1508 .get("morphology_id")
1509 .and_then(|v| v.as_str())
1510 .unwrap_or("unknown");
1511 let scalar = rule
1512 .get("morphology_scalar")
1513 .and_then(|v| v.as_i64())
1514 .unwrap_or(1) as usize;
1515
1516 let src_per_voxel = src_area
1518 .properties
1519 .get("neurons_per_voxel")
1520 .and_then(|v| v.as_u64())
1521 .unwrap_or(1) as usize;
1522 let dst_per_voxel = dst_area
1523 .properties
1524 .get("neurons_per_voxel")
1525 .and_then(|v| v.as_u64())
1526 .unwrap_or(1) as usize;
1527
1528 let src_voxels =
1529 src_area.dimensions.width * src_area.dimensions.height * src_area.dimensions.depth;
1530 let dst_voxels =
1531 dst_area.dimensions.width * dst_area.dimensions.height * dst_area.dimensions.depth;
1532
1533 let src_neurons = src_voxels as usize * src_per_voxel;
1534 let dst_neurons = dst_voxels as usize * dst_per_voxel as usize;
1535
1536 let count = match morphology_id {
1538 "block_to_block" => src_neurons * dst_per_voxel * scalar,
1539 "projector" | "transpose_xy" | "transpose_yz" | "transpose_xz"
1540 | "centered_projector" => src_neurons * dst_neurons * scalar,
1541 _ if morphology_id.contains("lateral") => src_neurons * scalar,
1542 _ => (src_neurons * scalar).min(src_neurons * dst_neurons / 10),
1543 };
1544
1545 total += count;
1546 }
1547 }
1548
1549 total
1550}
1551
1552impl Neuroembryogenesis {
1553 fn calculate_autogen_region_position(
1555 root_area_ids: &[CorticalID],
1556 genome: &feagi_evolutionary::RuntimeGenome,
1557 ) -> [i32; 3] {
1558 if root_area_ids.is_empty() {
1559 return [100, 0, 0];
1560 }
1561
1562 let mut min_x = i32::MAX;
1563 let mut max_x = i32::MIN;
1564 let mut min_y = i32::MAX;
1565 let mut max_y = i32::MIN;
1566 let mut min_z = i32::MAX;
1567 let mut max_z = i32::MIN;
1568
1569 for cortical_id in root_area_ids {
1570 if let Some(area) = genome.cortical_areas.get(cortical_id) {
1571 let pos: (i32, i32, i32) = area.position.into();
1572 let dims = (
1573 area.dimensions.width as i32,
1574 area.dimensions.height as i32,
1575 area.dimensions.depth as i32,
1576 );
1577
1578 min_x = min_x.min(pos.0);
1579 max_x = max_x.max(pos.0 + dims.0);
1580 min_y = min_y.min(pos.1);
1581 max_y = max_y.max(pos.1 + dims.1);
1582 min_z = min_z.min(pos.2);
1583 max_z = max_z.max(pos.2 + dims.2);
1584 }
1585 }
1586
1587 let bbox_width = (max_x - min_x).max(1);
1588 let padding = (bbox_width / 5).max(50);
1589 let autogen_x = max_x + padding;
1590 let autogen_y = (min_y + max_y) / 2;
1591 let autogen_z = (min_z + max_z) / 2;
1592
1593 info!(target: "feagi-bdu",
1594 " 📐 Autogen position: ({}, {}, {}) [padding: {}]",
1595 autogen_x, autogen_y, autogen_z, padding);
1596
1597 [autogen_x, autogen_y, autogen_z]
1598 }
1599
1600 fn analyze_region_io(
1606 region_area_ids: &[feagi_structures::genomic::cortical_area::CorticalID],
1607 all_cortical_areas: &std::collections::HashMap<CorticalID, CorticalArea>,
1608 ) -> (Vec<String>, Vec<String>) {
1609 let area_set: std::collections::HashSet<_> = region_area_ids.iter().cloned().collect();
1610 let mut inputs = Vec::new();
1611 let mut outputs = Vec::new();
1612
1613 let extract_destinations = |area: &CorticalArea| -> Vec<String> {
1615 area.properties
1616 .get("cortical_mapping_dst")
1617 .and_then(|v| v.as_object())
1618 .map(|obj| obj.keys().cloned().collect())
1619 .unwrap_or_default()
1620 };
1621
1622 for area_id in region_area_ids {
1624 if let Some(area) = all_cortical_areas.get(area_id) {
1625 let destinations = extract_destinations(area);
1626 let external_destinations: Vec<_> = destinations
1628 .iter()
1629 .filter_map(|dest| feagi_evolutionary::string_to_cortical_id(dest).ok())
1630 .filter(|dest_id| !area_set.contains(dest_id))
1631 .collect();
1632
1633 if !external_destinations.is_empty() {
1634 outputs.push(area_id.as_base_64());
1635 }
1636 }
1637 }
1638
1639 for (source_area_id, source_area) in all_cortical_areas.iter() {
1641 if area_set.contains(source_area_id) {
1643 continue;
1644 }
1645
1646 let destinations = extract_destinations(source_area);
1647 for dest_str in destinations {
1648 if let Ok(dest_id) = feagi_evolutionary::string_to_cortical_id(&dest_str) {
1649 if area_set.contains(&dest_id) {
1650 let dest_string = dest_id.as_base_64();
1651 if !inputs.contains(&dest_string) {
1652 inputs.push(dest_string);
1653 }
1654 }
1655 }
1656 }
1657 }
1658
1659 (inputs, outputs)
1660 }
1661
1662 fn update_stage(&self, stage: DevelopmentStage, progress: u8) {
1664 let mut p = self.progress.write();
1665 p.stage = stage;
1666 p.progress = progress;
1667 p.duration_ms = self.start_time.elapsed().as_millis() as u64;
1668 }
1669
1670 fn update_progress<F>(&self, f: F)
1672 where
1673 F: FnOnce(&mut DevelopmentProgress),
1674 {
1675 let mut p = self.progress.write();
1676 f(&mut p);
1677 p.duration_ms = self.start_time.elapsed().as_millis() as u64;
1678 }
1679}
1680
1681#[cfg(test)]
1682mod tests {
1683 use super::*;
1684 use feagi_evolutionary::create_genome_with_core_morphologies;
1685 use feagi_structures::genomic::cortical_area::CorticalAreaDimensions;
1686
1687 #[test]
1688 fn test_neuroembryogenesis_creation() {
1689 let manager = ConnectomeManager::instance();
1690 let neuro = Neuroembryogenesis::new(manager);
1691
1692 let progress = neuro.get_progress();
1693 assert_eq!(progress.stage, DevelopmentStage::Initialization);
1694 assert_eq!(progress.progress, 0);
1695 }
1696
1697 #[test]
1698 fn autogen_subregion_display_name_uses_title_when_meaningful() {
1699 assert_eq!(
1700 autogen_subregion_display_name("My Shared Circuit"),
1701 "My Shared Circuit"
1702 );
1703 }
1704
1705 #[test]
1706 fn autogen_subregion_display_name_falls_back_for_untitled() {
1707 assert_eq!(
1708 autogen_subregion_display_name("Untitled"),
1709 "Autogen Circuit"
1710 );
1711 assert_eq!(
1712 autogen_subregion_display_name("untitled"),
1713 "Autogen Circuit"
1714 );
1715 }
1716
1717 #[test]
1718 fn autogen_subregion_display_name_falls_back_for_blank() {
1719 assert_eq!(autogen_subregion_display_name(""), "Autogen Circuit");
1720 assert_eq!(autogen_subregion_display_name(" "), "Autogen Circuit");
1721 }
1722
1723 #[test]
1724 fn order_regions_puts_parent_before_grandchild_even_when_child_is_listed_first() {
1725 let root = "root".to_string();
1726 let parent = "look-for-people".to_string();
1727 let grandchild = "wave".to_string();
1728 let sibling = "look-for-ball".to_string();
1729
1730 let remaining = vec![grandchild.clone(), sibling.clone(), parent.clone()];
1732 let mut parent_map = std::collections::HashMap::new();
1733 parent_map.insert(parent.clone(), root.clone());
1734 parent_map.insert(grandchild.clone(), parent.clone());
1735 parent_map.insert(sibling.clone(), root);
1736
1737 let ordered = order_regions_parent_before_child(&remaining, &parent_map);
1738 let parent_idx = ordered.iter().position(|id| id == &parent).unwrap();
1739 let grandchild_idx = ordered.iter().position(|id| id == &grandchild).unwrap();
1740
1741 assert_eq!(ordered.len(), 3);
1742 assert!(
1743 parent_idx < grandchild_idx,
1744 "parent must precede grandchild, got {:?}",
1745 ordered
1746 );
1747 }
1748
1749 #[test]
1750 fn order_regions_keeps_flat_children_when_parent_is_already_inserted() {
1751 let remaining = vec!["a".to_string(), "b".to_string(), "c".to_string()];
1752 let mut parent_map = std::collections::HashMap::new();
1753 parent_map.insert("a".to_string(), "root".to_string());
1754 parent_map.insert("b".to_string(), "root".to_string());
1755 parent_map.insert("c".to_string(), "root".to_string());
1756
1757 let ordered = order_regions_parent_before_child(&remaining, &parent_map);
1758 let mut sorted = ordered.clone();
1759 sorted.sort();
1760 assert_eq!(sorted, remaining);
1761 }
1762
1763 #[test]
1764 fn order_regions_appends_cycle_members_after_acyclic_prefix() {
1765 let remaining = vec!["a".to_string(), "b".to_string(), "ok".to_string()];
1766 let mut parent_map = std::collections::HashMap::new();
1767 parent_map.insert("a".to_string(), "b".to_string());
1768 parent_map.insert("b".to_string(), "a".to_string());
1769 parent_map.insert("ok".to_string(), "root".to_string());
1770
1771 let ordered = order_regions_parent_before_child(&remaining, &parent_map);
1772 assert_eq!(ordered.first().map(String::as_str), Some("ok"));
1773 assert_eq!(ordered.len(), 3);
1774 assert!(ordered.contains(&"a".to_string()));
1775 assert!(ordered.contains(&"b".to_string()));
1776 }
1777
1778 #[test]
1779 fn test_development_from_minimal_genome() {
1780 ConnectomeManager::reset_for_testing(); let manager = ConnectomeManager::instance();
1782 let mut neuro = Neuroembryogenesis::new(manager.clone());
1783
1784 let mut genome = create_genome_with_core_morphologies(
1786 "test_genome".to_string(),
1787 "Test Genome".to_string(),
1788 );
1789
1790 let cortical_id = CorticalID::try_from_bytes(b"cst_neur").unwrap(); let cortical_type = cortical_id
1792 .as_cortical_type()
1793 .expect("Failed to get cortical type");
1794 let area = CorticalArea::new(
1795 cortical_id,
1796 0,
1797 "Test Area".to_string(),
1798 CorticalAreaDimensions::new(10, 10, 10).unwrap(),
1799 (0, 0, 0).into(),
1800 cortical_type,
1801 )
1802 .expect("Failed to create cortical area");
1803 genome.cortical_areas.insert(cortical_id, area);
1804
1805 let result = neuro.develop_from_genome(&genome);
1807 assert!(result.is_ok(), "Development failed: {:?}", result);
1808
1809 let progress = neuro.get_progress();
1811 assert_eq!(progress.stage, DevelopmentStage::Completed);
1812 assert_eq!(progress.progress, 100);
1813 assert_eq!(progress.cortical_areas_created, 1);
1814
1815 println!("✅ Development completed in {}ms", progress.duration_ms);
1819 }
1820}