1use crate::connectome_manager::ConnectomeManager;
22use crate::models::{CorticalArea, CorticalID};
23use crate::types::BduResult;
24use feagi_evolutionary::RuntimeGenome;
25use feagi_npu_neural::types::{Precision, QuantizationSpec};
26use parking_lot::RwLock;
27use std::sync::Arc;
28use tracing::{debug, error, info, trace, warn};
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum DevelopmentStage {
33 Initialization,
35 Corticogenesis,
37 Voxelogenesis,
39 Neurogenesis,
41 Synaptogenesis,
43 Completed,
45 Failed,
47}
48
49#[derive(Debug, Clone)]
51pub struct DevelopmentProgress {
52 pub stage: DevelopmentStage,
54 pub progress: u8,
56 pub cortical_areas_created: usize,
58 pub neurons_created: usize,
60 pub synapses_created: usize,
62 pub duration_ms: u64,
64}
65
66impl Default for DevelopmentProgress {
67 fn default() -> Self {
68 Self {
69 stage: DevelopmentStage::Initialization,
70 progress: 0,
71 cortical_areas_created: 0,
72 neurons_created: 0,
73 synapses_created: 0,
74 duration_ms: 0,
75 }
76 }
77}
78
79pub struct Neuroembryogenesis {
87 connectome_manager: Arc<RwLock<ConnectomeManager>>,
89
90 progress: Arc<RwLock<DevelopmentProgress>>,
92
93 start_time: std::time::Instant,
95}
96
97impl Neuroembryogenesis {
98 pub fn new(connectome_manager: Arc<RwLock<ConnectomeManager>>) -> Self {
100 Self {
101 connectome_manager,
102 progress: Arc::new(RwLock::new(DevelopmentProgress::default())),
103 start_time: std::time::Instant::now(),
104 }
105 }
106
107 pub fn get_progress(&self) -> DevelopmentProgress {
109 self.progress.read().clone()
110 }
111
112 pub fn add_cortical_areas(
124 &mut self,
125 areas: Vec<CorticalArea>,
126 genome: &RuntimeGenome,
127 ) -> BduResult<(usize, usize)> {
128 info!(target: "feagi-bdu", "🧬 Incrementally adding {} cortical areas", areas.len());
129
130 let mut total_neurons = 0;
131 let mut total_synapses = 0;
132
133 for area in &areas {
135 let mut manager = self.connectome_manager.write();
136 manager.add_cortical_area(area.clone())?;
137 info!(target: "feagi-bdu", " ✓ Added cortical area structure: {}", area.cortical_id.as_base_64());
138 }
139
140 for area in &areas {
142 let neurons_created = {
143 let mut manager = self.connectome_manager.write();
144 manager.create_neurons_for_area(&area.cortical_id)
145 };
146
147 match neurons_created {
148 Ok(count) => {
149 total_neurons += count as usize;
150 trace!(
151 target: "feagi-bdu",
152 "Created {} neurons for area {}",
153 count,
154 area.cortical_id.as_base_64()
155 );
156 }
157 Err(e) => {
158 error!(target: "feagi-bdu", " ❌ FATAL: Failed to create neurons for {}: {}", area.cortical_id.as_base_64(), e);
159 return Err(e);
161 }
162 }
163 }
164
165 for area in &areas {
167 let has_dstmap = area
169 .properties
170 .get("cortical_mapping_dst")
171 .and_then(|v| v.as_object())
172 .map(|m| !m.is_empty())
173 .unwrap_or(false);
174
175 if !has_dstmap {
176 debug!(target: "feagi-bdu", " No mappings for area {}", area.cortical_id.as_base_64());
177 continue;
178 }
179
180 let synapses_created = {
181 let mut manager = self.connectome_manager.write();
182 manager.apply_cortical_mapping(&area.cortical_id)
183 };
184
185 match synapses_created {
186 Ok(count) => {
187 total_synapses += count as usize;
188 trace!(
189 target: "feagi-bdu",
190 "Created {} synapses for area {}",
191 count,
192 area.cortical_id
193 );
194 }
195 Err(e) => {
196 warn!(target: "feagi-bdu", " ⚠️ Failed to create synapses for {}: {}", area.cortical_id, e);
197 let estimated = estimate_synapses_for_area(area, genome);
198 total_synapses += estimated;
199 }
200 }
201 }
202
203 info!(target: "feagi-bdu", "✅ Incremental add complete: {} areas, {} neurons, {} synapses",
204 areas.len(), total_neurons, total_synapses);
205
206 Ok((total_neurons, total_synapses))
207 }
208
209 pub fn develop_from_genome(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
213 info!(target: "feagi-bdu","🧬 Starting neuroembryogenesis for genome: {}", genome.metadata.genome_id);
214
215 let _quantization_precision = &genome.physiology.quantization_precision;
217 let quant_spec = QuantizationSpec::default();
219
220 info!(target: "feagi-bdu",
221 " Quantization precision: {:?} (range: [{}, {}] for membrane potential)",
222 quant_spec.precision,
223 quant_spec.membrane_potential_min,
224 quant_spec.membrane_potential_max
225 );
226
227 match quant_spec.precision {
231 Precision::FP32 => {
232 info!(target: "feagi-bdu", " ✓ Using FP32 (32-bit floating-point) - highest precision");
233 info!(target: "feagi-bdu", " Memory usage: Baseline (4 bytes/neuron for membrane potential)");
234 }
235 Precision::INT8 => {
236 info!(target: "feagi-bdu", " ✓ Using INT8 (8-bit integer) - memory efficient");
237 info!(target: "feagi-bdu", " Memory reduction: 42% (1 byte/neuron for membrane potential)");
238 info!(target: "feagi-bdu", " Quantization range: [{}, {}]",
239 quant_spec.membrane_potential_min,
240 quant_spec.membrane_potential_max);
241 }
244 Precision::FP16 => {
245 warn!(target: "feagi-bdu", " FP16 quantization requested but not yet implemented.");
246 warn!(target: "feagi-bdu", " FP16 support planned for future GPU optimization.");
247 }
249 }
250
251 info!(target: "feagi-bdu", " ✓ Quantization handled by DynamicNPU (dispatches at runtime)");
254
255 self.update_stage(DevelopmentStage::Initialization, 0);
257
258 self.corticogenesis(genome)?;
260
261 self.voxelogenesis(genome)?;
263
264 self.neurogenesis(genome)?;
266
267 self.synaptogenesis(genome)?;
269
270 self.update_stage(DevelopmentStage::Completed, 100);
272
273 let progress = self.progress.read();
274 info!(target: "feagi-bdu",
275 "✅ Neuroembryogenesis completed in {}ms: {} cortical areas, {} neurons, {} synapses",
276 progress.duration_ms,
277 progress.cortical_areas_created,
278 progress.neurons_created,
279 progress.synapses_created
280 );
281
282 Ok(())
283 }
284
285 fn corticogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
287 self.update_stage(DevelopmentStage::Corticogenesis, 0);
288 info!(target: "feagi-bdu","🧠 Stage 1: Corticogenesis - Creating {} cortical areas", genome.cortical_areas.len());
289 info!(target: "feagi-bdu","🔍 Genome brain_regions check: is_empty={}, count={}",
290 genome.brain_regions.is_empty(), genome.brain_regions.len());
291 if !genome.brain_regions.is_empty() {
292 info!(target: "feagi-bdu"," Existing regions: {:?}", genome.brain_regions.keys().collect::<Vec<_>>());
293 }
294
295 let total_areas = genome.cortical_areas.len();
296
297 for (idx, (cortical_id, area)) in genome.cortical_areas.iter().enumerate() {
299 {
301 let mut manager = self.connectome_manager.write();
302 manager.add_cortical_area(area.clone())?;
303 } let progress_pct = ((idx + 1) * 100 / total_areas.max(1)) as u8;
307 self.update_progress(|p| {
308 p.cortical_areas_created = idx + 1;
309 p.progress = progress_pct;
310 });
311
312 trace!(target: "feagi-bdu", "Created cortical area: {} ({})", cortical_id, area.name);
313 }
314
315 info!(target: "feagi-bdu","🔍 BRAIN REGION AUTO-GEN CHECK: genome.brain_regions.is_empty() = {}", genome.brain_regions.is_empty());
318 let (brain_regions_to_add, region_parent_map) = if genome.brain_regions.is_empty() {
319 info!(target: "feagi-bdu"," ✅ TRIGGERING AUTO-GENERATION: No brain_regions in genome - auto-generating default root region");
320 info!(target: "feagi-bdu"," 📊 Genome has {} cortical areas to process", genome.cortical_areas.len());
321
322 let all_cortical_ids = genome.cortical_areas.keys().cloned().collect::<Vec<_>>();
324 info!(target: "feagi-bdu"," 📊 Collected {} cortical area IDs: {:?}", all_cortical_ids.len(),
325 if all_cortical_ids.len() <= 5 {
326 format!("{:?}", all_cortical_ids.iter().map(|id| id.to_string()).collect::<Vec<_>>())
327 } else {
328 format!("{:?}...", all_cortical_ids[0..5].iter().map(|id| id.to_string()).collect::<Vec<_>>())
329 });
330
331 let mut auto_inputs = Vec::new();
333 let mut auto_outputs = Vec::new();
334
335 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() {
342 let area_id_str = area_id.to_string();
349 let category = if area_id_str.starts_with("___") {
351 "CORE"
352 } else if let Ok(cortical_type) = area.cortical_id.as_cortical_type() {
353 use feagi_structures::genomic::cortical_area::CorticalAreaType;
355 match cortical_type {
356 CorticalAreaType::Core(_) => "CORE",
357 CorticalAreaType::BrainInput(_) => "IPU",
358 CorticalAreaType::BrainOutput(_) => "OPU",
359 CorticalAreaType::Memory(_) => "MEMORY",
360 CorticalAreaType::Custom(_) => "CUSTOM",
361 }
362 } else {
363 let cortical_group = area
365 .properties
366 .get("cortical_group")
367 .and_then(|v| v.as_str())
368 .map(|s| s.to_uppercase());
369
370 match cortical_group.as_deref() {
371 Some("IPU") => "IPU",
372 Some("OPU") => "OPU",
373 Some("CORE") => "CORE",
374 Some("MEMORY") => "MEMORY",
375 Some("CUSTOM") => "CUSTOM",
376 _ => "CUSTOM", }
378 };
379
380 if ipu_areas.len() + opu_areas.len() + core_areas.len() + custom_memory_areas.len()
382 < 5
383 {
384 let source = if area.cortical_id.as_cortical_type().is_ok() {
385 "cortical_id_type"
386 } else if area.properties.contains_key("cortical_group") {
387 "cortical_group"
388 } else {
389 "default_fallback"
390 };
391
392 if area.cortical_id.as_cortical_type().is_ok() {
394 let type_desc = crate::cortical_type_utils::describe_cortical_type(area);
395 let frame_handling =
396 if crate::cortical_type_utils::uses_absolute_frames(area) {
397 "absolute"
398 } else if crate::cortical_type_utils::uses_incremental_frames(area) {
399 "incremental"
400 } else {
401 "n/a"
402 };
403 info!(target: "feagi-bdu"," 🔍 {}, frames={}, source={}",
404 type_desc, frame_handling, source);
405 } else {
406 info!(target: "feagi-bdu"," 🔍 Area {}: category={}, source={}",
407 area_id_str, category, source);
408 }
409 }
410
411 match category {
413 "IPU" => {
414 ipu_areas.push(*area_id);
415 auto_inputs.push(*area_id);
416 }
417 "OPU" => {
418 opu_areas.push(*area_id);
419 auto_outputs.push(*area_id);
420 }
421 "CORE" => {
422 core_areas.push(*area_id);
423 }
424 "MEMORY" | "CUSTOM" => {
425 custom_memory_areas.push(*area_id);
426 }
427 _ => {}
428 }
429 }
430
431 info!(target: "feagi-bdu"," 📊 Classification complete: IPU={}, OPU={}, CORE={}, CUSTOM/MEMORY={}",
432 ipu_areas.len(), opu_areas.len(), core_areas.len(), custom_memory_areas.len());
433
434 use feagi_structures::genomic::brain_regions::{BrainRegion, RegionID, RegionType};
436 let mut regions_map = std::collections::HashMap::new();
437
438 let mut root_area_ids = Vec::new();
440 root_area_ids.extend(ipu_areas.iter().cloned());
441 root_area_ids.extend(opu_areas.iter().cloned());
442 root_area_ids.extend(core_areas.iter().cloned());
443
444 let (root_inputs, root_outputs) =
446 Self::analyze_region_io(&root_area_ids, &genome.cortical_areas);
447
448 let root_region_id = RegionID::new();
451 let root_region_id_str = root_region_id.to_string();
452
453 let mut root_region = BrainRegion::new(
454 root_region_id,
455 "Root Brain Region".to_string(),
456 RegionType::Undefined,
457 )
458 .expect("Failed to create root region")
459 .with_areas(root_area_ids.iter().cloned());
460
461 if !root_inputs.is_empty() {
463 root_region
464 .add_property("inputs".to_string(), serde_json::json!(root_inputs.clone()));
465 }
466 if !root_outputs.is_empty() {
467 root_region.add_property(
468 "outputs".to_string(),
469 serde_json::json!(root_outputs.clone()),
470 );
471 }
472
473 info!(target: "feagi-bdu"," ✅ Created root region with {} areas (IPU={}, OPU={}, CORE={}) - analyzed: {} inputs, {} outputs",
474 root_area_ids.len(), ipu_areas.len(), opu_areas.len(), core_areas.len(),
475 root_inputs.len(), root_outputs.len());
476
477 let mut subregion_id = None;
479 if !custom_memory_areas.is_empty() {
480 let mut custom_memory_strs: Vec<String> = custom_memory_areas
482 .iter()
483 .map(|id| id.as_base_64())
484 .collect();
485 custom_memory_strs.sort(); let combined = custom_memory_strs.join("|");
487
488 use std::collections::hash_map::DefaultHasher;
490 use std::hash::{Hash, Hasher};
491 let mut hasher = DefaultHasher::new();
492 combined.hash(&mut hasher);
493 let hash = hasher.finish();
494 let hash_hex = format!("{:08x}", hash as u32);
495 let region_id = format!("region_autogen_{}", hash_hex);
496
497 let (subregion_inputs, subregion_outputs) =
499 Self::analyze_region_io(&custom_memory_areas, &genome.cortical_areas);
500
501 let autogen_position =
503 Self::calculate_autogen_region_position(&root_area_ids, genome);
504
505 let mut subregion = BrainRegion::new(
507 RegionID::new(), "Autogen Region".to_string(),
509 RegionType::Undefined, )
511 .expect("Failed to create subregion")
512 .with_areas(custom_memory_areas.iter().cloned());
513
514 subregion.add_property(
516 "coordinate_3d".to_string(),
517 serde_json::json!(autogen_position),
518 );
519 subregion.add_property("coordinate_2d".to_string(), serde_json::json!([0, 0]));
520
521 if !subregion_inputs.is_empty() {
523 subregion.add_property(
524 "inputs".to_string(),
525 serde_json::json!(subregion_inputs.clone()),
526 );
527 }
528 if !subregion_outputs.is_empty() {
529 subregion.add_property(
530 "outputs".to_string(),
531 serde_json::json!(subregion_outputs.clone()),
532 );
533 }
534
535 let subregion_id_str = subregion.region_id.to_string();
536
537 info!(target: "feagi-bdu"," ✅ Created subregion '{}' with {} CUSTOM/MEMORY areas ({} inputs, {} outputs)",
538 region_id, custom_memory_areas.len(), subregion_inputs.len(), subregion_outputs.len());
539
540 regions_map.insert(subregion_id_str.clone(), subregion);
541 subregion_id = Some(subregion_id_str);
542 }
543
544 regions_map.insert(root_region_id_str.clone(), root_region);
545
546 let total_inputs = root_inputs.len()
548 + if let Some(ref sid) = subregion_id {
549 regions_map
550 .get(sid)
551 .and_then(|r| r.properties.get("inputs"))
552 .and_then(|v| v.as_array())
553 .map(|a| a.len())
554 .unwrap_or(0)
555 } else {
556 0
557 };
558
559 let total_outputs = root_outputs.len()
560 + if let Some(ref sid) = subregion_id {
561 regions_map
562 .get(sid)
563 .and_then(|r| r.properties.get("outputs"))
564 .and_then(|v| v.as_array())
565 .map(|a| a.len())
566 .unwrap_or(0)
567 } else {
568 0
569 };
570
571 info!(target: "feagi-bdu"," ✅ Auto-generated {} brain region(s) with {} total cortical areas ({} total inputs, {} total outputs)",
572 regions_map.len(), all_cortical_ids.len(), total_inputs, total_outputs);
573
574 let mut parent_map = std::collections::HashMap::new();
576 if let Some(ref sub_id) = subregion_id {
577 parent_map.insert(sub_id.clone(), root_region_id_str.clone());
578 info!(target: "feagi-bdu"," 🔗 Parent relationship: {} -> {}", sub_id, root_region_id_str);
579 }
580
581 (regions_map, parent_map)
582 } else {
583 info!(target: "feagi-bdu"," 📋 Genome already has {} brain regions - using existing structure", genome.brain_regions.len());
584 (
587 genome.brain_regions.clone(),
588 std::collections::HashMap::new(),
589 )
590 };
591
592 {
594 let mut manager = self.connectome_manager.write();
595 let brain_region_count = brain_regions_to_add.len();
596 info!(target: "feagi-bdu"," Adding {} brain regions from genome", brain_region_count);
597
598 let root_entry = brain_regions_to_add
600 .iter()
601 .find(|(_, region)| region.name == "Root Brain Region");
602 if let Some((root_id, root_region)) = root_entry {
603 manager.add_brain_region(root_region.clone(), None)?;
604 debug!(target: "feagi-bdu"," ✓ Added brain region: {} (Root Brain Region) [parent=None]", root_id);
605 }
606
607 for (region_id, region) in brain_regions_to_add.iter() {
609 if region.name == "Root Brain Region" {
610 continue; }
612
613 let parent_id = region_parent_map.get(region_id).cloned();
614 manager.add_brain_region(region.clone(), parent_id.clone())?;
615 debug!(target: "feagi-bdu"," ✓ Added brain region: {} ({}) [parent={:?}]",
616 region_id, region.name, parent_id);
617 }
618
619 info!(target: "feagi-bdu"," Total brain regions in ConnectomeManager: {}", manager.get_brain_region_ids().len());
620 } self.update_stage(DevelopmentStage::Corticogenesis, 100);
623 info!(target: "feagi-bdu"," ✅ Corticogenesis complete: {} cortical areas created", total_areas);
624
625 Ok(())
626 }
627
628 fn voxelogenesis(&mut self, _genome: &RuntimeGenome) -> BduResult<()> {
630 self.update_stage(DevelopmentStage::Voxelogenesis, 0);
631 info!(target: "feagi-bdu","📐 Stage 2: Voxelogenesis - Establishing spatial framework");
632
633 self.update_stage(DevelopmentStage::Voxelogenesis, 100);
637 info!(target: "feagi-bdu"," ✅ Voxelogenesis complete: Spatial framework established");
638
639 Ok(())
640 }
641
642 fn neurogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
648 self.update_stage(DevelopmentStage::Neurogenesis, 0);
649 info!(target: "feagi-bdu","🔬 Stage 3: Neurogenesis - Generating neurons (SIMD-optimized batches)");
650
651 let expected_neurons = genome.stats.innate_neuron_count;
652 info!(target: "feagi-bdu"," Expected innate neurons from genome: {}", expected_neurons);
653
654 let mut total_neurons_created = 0;
655 let total_areas = genome.cortical_areas.len();
656
657 for (idx, (_cortical_id, area)) in genome.cortical_areas.iter().enumerate() {
660 let per_voxel_count = area
662 .properties
663 .get("neurons_per_voxel")
664 .and_then(|v| v.as_u64())
665 .unwrap_or(1) as i64;
666
667 let cortical_id_str = area.cortical_id.to_string();
670 if cortical_id_str == "___power"
672 || cortical_id_str == "___death"
673 || cortical_id_str == "_power__"
674 || cortical_id_str == "_death__"
675 {
676 info!(target: "feagi-bdu"," 🔍 [CORE-AREA] {} - dimensions: {:?}, per_voxel: {}",
677 cortical_id_str, area.dimensions, per_voxel_count);
678 }
679
680 if per_voxel_count == 0 {
681 warn!(target: "feagi-bdu"," ⚠️ Skipping area {} - per_voxel_neuron_cnt is 0 (will have NO neurons!)", cortical_id_str);
682 continue;
683 }
684
685 let neurons_created = {
688 let manager_arc = self.connectome_manager.clone();
689 let mut manager = manager_arc.write();
690 manager.create_neurons_for_area(&area.cortical_id)
691 }; match neurons_created {
694 Ok(count) => {
695 total_neurons_created += count as usize;
696 trace!(
697 target: "feagi-bdu",
698 "Created {} neurons for area {}",
699 count,
700 cortical_id_str
701 );
702 }
703 Err(e) => {
704 warn!(target: "feagi-bdu"," Failed to create neurons for {}: {} (NPU may not be connected)",
706 cortical_id_str, e);
707 let total_voxels = area.dimensions.width as usize
708 * area.dimensions.height as usize
709 * area.dimensions.depth as usize;
710 let expected = total_voxels * per_voxel_count as usize;
711 total_neurons_created += expected;
712 }
713 }
714
715 let progress_pct = ((idx + 1) * 100 / total_areas.max(1)) as u8;
717 self.update_progress(|p| {
718 p.neurons_created = total_neurons_created;
719 p.progress = progress_pct;
720 });
721 }
722
723 if expected_neurons > 0 && total_neurons_created != expected_neurons {
725 trace!(target: "feagi-bdu",
726 created_neurons = total_neurons_created,
727 genome_stats_innate = expected_neurons,
728 "Neuron creation complete (genome stats may only count innate neurons)"
729 );
730 }
731
732 self.update_stage(DevelopmentStage::Neurogenesis, 100);
733 info!(target: "feagi-bdu"," ✅ Neurogenesis complete: {} neurons created", total_neurons_created);
734
735 Ok(())
736 }
737
738 fn synaptogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
744 self.update_stage(DevelopmentStage::Synaptogenesis, 0);
745 info!(target: "feagi-bdu","🔗 Stage 4: Synaptogenesis - Forming synaptic connections (SIMD-optimized batches)");
746
747 let expected_synapses = genome.stats.innate_synapse_count;
748 info!(target: "feagi-bdu"," Expected innate synapses from genome: {}", expected_synapses);
749
750 let mut total_synapses_created = 0;
751 let total_areas = genome.cortical_areas.len();
752
753 for (idx, (_src_cortical_id, src_area)) in genome.cortical_areas.iter().enumerate() {
756 let has_dstmap = src_area
758 .properties
759 .get("cortical_mapping_dst")
760 .and_then(|v| v.as_object())
761 .map(|m| !m.is_empty())
762 .unwrap_or(false);
763
764 if !has_dstmap {
765 trace!(target: "feagi-bdu", "No dstmap for area {}", &src_area.cortical_id);
766 continue;
767 }
768
769 let src_cortical_id = &src_area.cortical_id;
773 let src_cortical_id_str = src_cortical_id.to_string(); let synapses_created = {
775 let manager_arc = self.connectome_manager.clone();
776 let mut manager = manager_arc.write();
777 manager.apply_cortical_mapping(src_cortical_id)
778 }; match synapses_created {
781 Ok(count) => {
782 total_synapses_created += count as usize;
783 trace!(
784 target: "feagi-bdu",
785 "Created {} synapses for area {}",
786 count,
787 src_cortical_id_str
788 );
789 }
790 Err(e) => {
791 warn!(target: "feagi-bdu"," Failed to create synapses for {}: {} (NPU may not be connected)",
793 src_cortical_id_str, e);
794 let estimated = estimate_synapses_for_area(src_area, genome);
795 total_synapses_created += estimated;
796 }
797 }
798
799 let progress_pct = ((idx + 1) * 100 / total_areas.max(1)) as u8;
801 self.update_progress(|p| {
802 p.synapses_created = total_synapses_created;
803 p.progress = progress_pct;
804 });
805 }
806
807 if expected_synapses > 0 {
809 let diff = (total_synapses_created as i64 - expected_synapses as i64).abs();
810 let diff_pct = (diff as f64 / expected_synapses.max(1) as f64) * 100.0;
811
812 if diff_pct > 10.0 {
813 warn!(target: "feagi-bdu",
814 "Synapse count variance: created {} but genome stats expected {} ({:.1}% difference)",
815 total_synapses_created, expected_synapses, diff_pct
816 );
817 } else {
818 info!(target: "feagi-bdu",
819 "Synapse count matches genome stats within {:.1}% ({} vs {})",
820 diff_pct, total_synapses_created, expected_synapses
821 );
822 }
823 }
824
825 self.update_stage(DevelopmentStage::Synaptogenesis, 100);
826 info!(target: "feagi-bdu"," ✅ Synaptogenesis complete: {} synapses created", total_synapses_created);
827
828 Ok(())
829 }
830}
831
832fn estimate_synapses_for_area(
836 src_area: &CorticalArea,
837 genome: &feagi_evolutionary::RuntimeGenome,
838) -> usize {
839 let dstmap = match src_area.properties.get("cortical_mapping_dst") {
840 Some(serde_json::Value::Object(map)) => map,
841 _ => return 0,
842 };
843
844 let mut total = 0;
845
846 for (dst_id, rules) in dstmap {
847 let dst_cortical_id = match feagi_evolutionary::string_to_cortical_id(dst_id) {
849 Ok(id) => id,
850 Err(_) => continue,
851 };
852 let dst_area = match genome.cortical_areas.get(&dst_cortical_id) {
853 Some(area) => area,
854 None => continue,
855 };
856
857 let rules_array = match rules.as_array() {
858 Some(arr) => arr,
859 None => continue,
860 };
861
862 for rule in rules_array {
863 let morphology_id = rule
864 .get("morphology_id")
865 .and_then(|v| v.as_str())
866 .unwrap_or("unknown");
867 let scalar = rule
868 .get("morphology_scalar")
869 .and_then(|v| v.as_i64())
870 .unwrap_or(1) as usize;
871
872 let src_per_voxel = src_area
874 .properties
875 .get("neurons_per_voxel")
876 .and_then(|v| v.as_u64())
877 .unwrap_or(1) as usize;
878 let dst_per_voxel = dst_area
879 .properties
880 .get("neurons_per_voxel")
881 .and_then(|v| v.as_u64())
882 .unwrap_or(1) as usize;
883
884 let src_voxels =
885 src_area.dimensions.width * src_area.dimensions.height * src_area.dimensions.depth;
886 let dst_voxels =
887 dst_area.dimensions.width * dst_area.dimensions.height * dst_area.dimensions.depth;
888
889 let src_neurons = src_voxels as usize * src_per_voxel;
890 let dst_neurons = dst_voxels as usize * dst_per_voxel as usize;
891
892 let count = match morphology_id {
894 "block_to_block" => src_neurons * dst_per_voxel * scalar,
895 "projector" => src_neurons * dst_neurons * scalar,
896 _ if morphology_id.contains("lateral") => src_neurons * scalar,
897 _ => (src_neurons * scalar).min(src_neurons * dst_neurons / 10),
898 };
899
900 total += count;
901 }
902 }
903
904 total
905}
906
907impl Neuroembryogenesis {
908 fn calculate_autogen_region_position(
910 root_area_ids: &[CorticalID],
911 genome: &feagi_evolutionary::RuntimeGenome,
912 ) -> [i32; 3] {
913 if root_area_ids.is_empty() {
914 return [100, 0, 0];
915 }
916
917 let mut min_x = i32::MAX;
918 let mut max_x = i32::MIN;
919 let mut min_y = i32::MAX;
920 let mut max_y = i32::MIN;
921 let mut min_z = i32::MAX;
922 let mut max_z = i32::MIN;
923
924 for cortical_id in root_area_ids {
925 if let Some(area) = genome.cortical_areas.get(cortical_id) {
926 let pos: (i32, i32, i32) = area.position.into();
927 let dims = (
928 area.dimensions.width as i32,
929 area.dimensions.height as i32,
930 area.dimensions.depth as i32,
931 );
932
933 min_x = min_x.min(pos.0);
934 max_x = max_x.max(pos.0 + dims.0);
935 min_y = min_y.min(pos.1);
936 max_y = max_y.max(pos.1 + dims.1);
937 min_z = min_z.min(pos.2);
938 max_z = max_z.max(pos.2 + dims.2);
939 }
940 }
941
942 let bbox_width = (max_x - min_x).max(1);
943 let padding = (bbox_width / 5).max(50);
944 let autogen_x = max_x + padding;
945 let autogen_y = (min_y + max_y) / 2;
946 let autogen_z = (min_z + max_z) / 2;
947
948 info!(target: "feagi-bdu",
949 " 📐 Autogen position: ({}, {}, {}) [padding: {}]",
950 autogen_x, autogen_y, autogen_z, padding);
951
952 [autogen_x, autogen_y, autogen_z]
953 }
954
955 fn analyze_region_io(
961 region_area_ids: &[feagi_structures::genomic::cortical_area::CorticalID],
962 all_cortical_areas: &std::collections::HashMap<CorticalID, CorticalArea>,
963 ) -> (Vec<String>, Vec<String>) {
964 let area_set: std::collections::HashSet<_> = region_area_ids.iter().cloned().collect();
965 let mut inputs = Vec::new();
966 let mut outputs = Vec::new();
967
968 let extract_destinations = |area: &CorticalArea| -> Vec<String> {
970 area.properties
971 .get("cortical_mapping_dst")
972 .and_then(|v| v.as_object())
973 .map(|obj| obj.keys().cloned().collect())
974 .unwrap_or_default()
975 };
976
977 for area_id in region_area_ids {
979 if let Some(area) = all_cortical_areas.get(area_id) {
980 let destinations = extract_destinations(area);
981 let external_destinations: Vec<_> = destinations
983 .iter()
984 .filter_map(|dest| feagi_evolutionary::string_to_cortical_id(dest).ok())
985 .filter(|dest_id| !area_set.contains(dest_id))
986 .collect();
987
988 if !external_destinations.is_empty() {
989 outputs.push(area_id.as_base_64());
990 }
991 }
992 }
993
994 for (source_area_id, source_area) in all_cortical_areas.iter() {
996 if area_set.contains(source_area_id) {
998 continue;
999 }
1000
1001 let destinations = extract_destinations(source_area);
1002 for dest_str in destinations {
1003 if let Ok(dest_id) = feagi_evolutionary::string_to_cortical_id(&dest_str) {
1004 if area_set.contains(&dest_id) {
1005 let dest_string = dest_id.as_base_64();
1006 if !inputs.contains(&dest_string) {
1007 inputs.push(dest_string);
1008 }
1009 }
1010 }
1011 }
1012 }
1013
1014 (inputs, outputs)
1015 }
1016
1017 fn update_stage(&self, stage: DevelopmentStage, progress: u8) {
1019 let mut p = self.progress.write();
1020 p.stage = stage;
1021 p.progress = progress;
1022 p.duration_ms = self.start_time.elapsed().as_millis() as u64;
1023 }
1024
1025 fn update_progress<F>(&self, f: F)
1027 where
1028 F: FnOnce(&mut DevelopmentProgress),
1029 {
1030 let mut p = self.progress.write();
1031 f(&mut p);
1032 p.duration_ms = self.start_time.elapsed().as_millis() as u64;
1033 }
1034}
1035
1036#[cfg(test)]
1037mod tests {
1038 use super::*;
1039 use feagi_evolutionary::create_genome_with_core_morphologies;
1040 use feagi_structures::genomic::cortical_area::CorticalAreaDimensions;
1041
1042 #[test]
1043 fn test_neuroembryogenesis_creation() {
1044 let manager = ConnectomeManager::instance();
1045 let neuro = Neuroembryogenesis::new(manager);
1046
1047 let progress = neuro.get_progress();
1048 assert_eq!(progress.stage, DevelopmentStage::Initialization);
1049 assert_eq!(progress.progress, 0);
1050 }
1051
1052 #[test]
1053 fn test_development_from_minimal_genome() {
1054 ConnectomeManager::reset_for_testing(); let manager = ConnectomeManager::instance();
1056 let mut neuro = Neuroembryogenesis::new(manager.clone());
1057
1058 let mut genome = create_genome_with_core_morphologies(
1060 "test_genome".to_string(),
1061 "Test Genome".to_string(),
1062 );
1063
1064 let cortical_id = CorticalID::try_from_bytes(b"cst_neur").unwrap(); let cortical_type = cortical_id
1066 .as_cortical_type()
1067 .expect("Failed to get cortical type");
1068 let area = CorticalArea::new(
1069 cortical_id,
1070 0,
1071 "Test Area".to_string(),
1072 CorticalAreaDimensions::new(10, 10, 10).unwrap(),
1073 (0, 0, 0).into(),
1074 cortical_type,
1075 )
1076 .expect("Failed to create cortical area");
1077 genome.cortical_areas.insert(cortical_id, area);
1078
1079 let result = neuro.develop_from_genome(&genome);
1081 assert!(result.is_ok(), "Development failed: {:?}", result);
1082
1083 let progress = neuro.get_progress();
1085 assert_eq!(progress.stage, DevelopmentStage::Completed);
1086 assert_eq!(progress.progress, 100);
1087 assert_eq!(progress.cortical_areas_created, 1);
1088
1089 let mgr = manager.read();
1091 assert!(
1093 mgr.has_cortical_area(&cortical_id),
1094 "Cortical area should have been added to connectome"
1095 );
1096
1097 println!("✅ Development completed in {}ms", progress.duration_ms);
1098 }
1099}