Skip to main content

feagi_evolutionary/
templates.rs

1// Copyright 2025 Neuraville Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4/*!
5Genome templates for FEAGI.
6
7Provides templates for creating genomes from scratch, including:
8- Minimal genome template
9- Cortical area templates (IPU, OPU, CORE)
10- Default neural parameters
11- Embedded default genomes
12
13Copyright 2025 Neuraville Inc.
14Licensed under the Apache License, Version 2.0
15*/
16
17use crate::{
18    GenomeMetadata, GenomeSignatures, GenomeStats, MorphologyRegistry, PhysiologyConfig,
19    RuntimeGenome,
20};
21use feagi_structures::genomic::cortical_area::CoreCorticalType;
22use feagi_structures::genomic::cortical_area::{CorticalArea, CorticalAreaDimensions};
23use feagi_structures::genomic::descriptors::GenomeCoordinate3D;
24use serde_json::Value;
25use std::collections::HashMap;
26
27/// Embedded essential genome (loaded at compile time)
28pub const ESSENTIAL_GENOME_JSON: &str = include_str!("../genomes/essential_genome.json");
29
30/// Embedded barebones genome (loaded at compile time)
31pub const BAREBONES_GENOME_JSON: &str = include_str!("../genomes/barebones_genome.json");
32
33/// Embedded test genome (loaded at compile time)
34pub const TEST_GENOME_JSON: &str = include_str!("../genomes/test_genome.json");
35
36/// Embedded vision genome (loaded at compile time)
37pub const VISION_GENOME_JSON: &str = include_str!("../genomes/vision_genome.json");
38
39/// Default neural properties for all cortical areas
40pub fn get_default_neural_properties() -> HashMap<String, Value> {
41    let mut props = HashMap::new();
42    props.insert("per_voxel_neuron_cnt".to_string(), Value::from(1));
43    props.insert("synapse_attractivity".to_string(), Value::from(100.0));
44    props.insert("degeneration".to_string(), Value::from(0.0));
45    props.insert("psp_uniform_distribution".to_string(), Value::from(true));
46    props.insert("postsynaptic_current_max".to_string(), Value::from(10000.0));
47    props.insert("postsynaptic_current".to_string(), Value::from(500.0));
48    props.insert("firing_threshold".to_string(), Value::from(0.1));
49    props.insert("refractory_period".to_string(), Value::from(0));
50    props.insert("leak_coefficient".to_string(), Value::from(0.0));
51    props.insert("leak_variability".to_string(), Value::from(0.0));
52    props.insert("consecutive_fire_cnt_max".to_string(), Value::from(0));
53    props.insert("snooze_length".to_string(), Value::from(0));
54    props.insert("mp_charge_accumulation".to_string(), Value::from(false));
55    props.insert("mp_driven_psp".to_string(), Value::from(false));
56    props.insert("neuron_excitability".to_string(), Value::from(1.0));
57    props.insert("visualization".to_string(), Value::from(true));
58    props.insert("memory_twin_of".to_string(), Value::Null);
59    props.insert(
60        "cortical_mapping_dst".to_string(),
61        Value::Object(serde_json::Map::new()),
62    );
63    props
64}
65
66/// Create _death cortical area (cortical_idx = 0) from template
67pub fn create_death_area() -> CorticalArea {
68    let cortical_id = CoreCorticalType::Death.to_cortical_id();
69    let cortical_type = cortical_id
70        .as_cortical_type()
71        .expect("Death cortical ID should map to Core type");
72
73    let mut area = CorticalArea::new(
74        cortical_id,
75        0, // cortical_idx = 0 (reserved)
76        "Death".to_string(),
77        CorticalAreaDimensions::new(1, 1, 1).expect("Failed to create dimensions"),
78        GenomeCoordinate3D::new(0, 0, -20),
79        cortical_type,
80    )
81    .expect("Failed to create _death area");
82
83    let mut props = get_default_neural_properties();
84    props.insert("cortical_group".to_string(), Value::from("CORE"));
85    props.insert("2d_coordinate".to_string(), Value::from(vec![-10, -20]));
86    area.properties = props;
87    area
88}
89
90/// Create _power cortical area (cortical_idx = 1) from template
91pub fn create_power_area() -> CorticalArea {
92    let cortical_id = CoreCorticalType::Power.to_cortical_id();
93    let cortical_type = cortical_id
94        .as_cortical_type()
95        .expect("Power cortical ID should map to Core type");
96
97    let mut area = CorticalArea::new(
98        cortical_id,
99        1, // cortical_idx = 1 (reserved)
100        "Brain_Power".to_string(),
101        CorticalAreaDimensions::new(1, 1, 1).expect("Failed to create dimensions"),
102        GenomeCoordinate3D::new(0, 0, -20),
103        cortical_type,
104    )
105    .expect("Failed to create _power area");
106
107    let mut props = get_default_neural_properties();
108    props.insert("cortical_group".to_string(), Value::from("CORE"));
109    props.insert("2d_coordinate".to_string(), Value::from(vec![-10, -10]));
110    props.insert("firing_threshold".to_string(), Value::from(0.1));
111    props.insert("postsynaptic_current".to_string(), Value::from(500.0));
112    props.insert("neuron_excitability".to_string(), Value::from(100.0));
113    area.properties = props;
114    area
115}
116
117/// Create _fatigue cortical area (cortical_idx = 2) from template
118pub fn create_fatigue_area() -> CorticalArea {
119    let cortical_id = CoreCorticalType::Fatigue.to_cortical_id();
120    let cortical_type = cortical_id
121        .as_cortical_type()
122        .expect("Fatigue cortical ID should map to Core type");
123
124    let mut area = CorticalArea::new(
125        cortical_id,
126        2, // cortical_idx = 2 (reserved)
127        "Fatigue".to_string(),
128        CorticalAreaDimensions::new(1, 1, 1).expect("Failed to create dimensions"),
129        GenomeCoordinate3D::new(0, 0, -30),
130        cortical_type,
131    )
132    .expect("Failed to create _fatigue area");
133
134    let mut props = get_default_neural_properties();
135    props.insert("cortical_group".to_string(), Value::from("CORE"));
136    props.insert("2d_coordinate".to_string(), Value::from(vec![-10, 0]));
137    area.properties = props;
138    area
139}
140
141/// Create a minimal empty genome
142pub fn create_minimal_genome(genome_id: String, genome_title: String) -> RuntimeGenome {
143    RuntimeGenome {
144        metadata: GenomeMetadata {
145            genome_id,
146            genome_title,
147            genome_description: "Minimal genome template".to_string(),
148            version: "2.0".to_string(),
149            timestamp: std::time::SystemTime::now()
150                .duration_since(std::time::UNIX_EPOCH)
151                .unwrap()
152                .as_secs_f64(),
153            brain_regions_root: None, // Will be set after neuroembryogenesis
154        },
155        cortical_areas: HashMap::new(),
156        brain_regions: HashMap::new(),
157        morphologies: MorphologyRegistry::new(),
158        physiology: PhysiologyConfig::default(),
159        signatures: GenomeSignatures {
160            genome: String::new(),
161            blueprint: String::new(),
162            physiology: String::new(),
163            morphologies: None,
164        },
165        stats: GenomeStats::default(),
166    }
167}
168
169/// Create a genome with core areas (_death, _power)
170pub fn create_genome_with_core_areas(genome_id: String, genome_title: String) -> RuntimeGenome {
171    let mut genome = create_minimal_genome(genome_id, genome_title);
172
173    // Add core areas (convert 6-char strings to CorticalID)
174    let death_id =
175        crate::genome::parser::string_to_cortical_id("_death").expect("Valid cortical ID");
176    let power_id =
177        crate::genome::parser::string_to_cortical_id("_power").expect("Valid cortical ID");
178    let fatigue_id =
179        crate::genome::parser::string_to_cortical_id("_fatigue").expect("Valid cortical ID");
180
181    genome.cortical_areas.insert(death_id, create_death_area());
182    genome.cortical_areas.insert(power_id, create_power_area());
183    genome
184        .cortical_areas
185        .insert(fatigue_id, create_fatigue_area());
186
187    genome
188}
189
190/// Create a genome with core morphologies
191pub fn create_genome_with_core_morphologies(
192    genome_id: String,
193    genome_title: String,
194) -> RuntimeGenome {
195    let mut genome = create_minimal_genome(genome_id, genome_title);
196
197    // Add core morphologies
198    add_core_morphologies(&mut genome.morphologies);
199
200    genome
201}
202
203/// CRITICAL: Ensure a genome has all required core components
204///
205/// This function checks if a genome has:
206/// 1. Core cortical areas (_death, _power)
207/// 2. Core morphologies (block_to_block, projector, etc.)
208///
209/// If any are missing, they are automatically added. This ensures every genome
210/// can function properly regardless of its source.
211///
212/// # Arguments
213/// * `genome` - The genome to validate and fix
214///
215/// # Returns
216/// A tuple of (areas_added, morphologies_added) indicating what was added
217pub fn ensure_core_components(genome: &mut RuntimeGenome) -> (usize, usize) {
218    let mut areas_added = 0;
219    let mut morphologies_added = 0;
220
221    // Convert core area IDs
222    let death_id =
223        crate::genome::parser::string_to_cortical_id("_death").expect("Valid cortical ID");
224    let power_id =
225        crate::genome::parser::string_to_cortical_id("_power").expect("Valid cortical ID");
226    let fatigue_id =
227        crate::genome::parser::string_to_cortical_id("_fatigue").expect("Valid cortical ID");
228
229    // 1. Ensure core cortical areas exist
230    if let std::collections::hash_map::Entry::Vacant(e) = genome.cortical_areas.entry(death_id) {
231        let death_area = create_death_area();
232        e.insert(death_area);
233        areas_added += 1;
234        tracing::info!("Added missing core area: _death (cortical_idx=0)");
235    }
236
237    if let std::collections::hash_map::Entry::Vacant(e) = genome.cortical_areas.entry(power_id) {
238        let power_area = create_power_area();
239        e.insert(power_area);
240        areas_added += 1;
241        tracing::info!("Added missing core area: _power (cortical_idx=1)");
242    }
243
244    if let std::collections::hash_map::Entry::Vacant(e) = genome.cortical_areas.entry(fatigue_id) {
245        let fatigue_area = create_fatigue_area();
246        e.insert(fatigue_area);
247        areas_added += 1;
248        tracing::info!("Added missing core area: _fatigue (cortical_idx=2)");
249    }
250
251    // 2. Ensure core morphologies exist
252    let required_morphologies = vec![
253        "block_to_block",
254        "projector",
255        "sweeper",
256        "last_to_first",
257        "bitmask_encoder_x",
258        "bitmask_encoder_y",
259        "bitmask_encoder_z",
260        "bitmask_decoder_x",
261        "bitmask_decoder_y",
262        "bitmask_decoder_z",
263        "episodic_memory",
264        "memory_replay",
265        "associative_memory",
266        "rotator_z",
267        "all_to_0-0-0",
268        "0-0-0_to_all",
269        "tile",
270        "lateral_+x",
271        "lateral_-x",
272        "lateral_+y",
273        "lateral_-y",
274        "lateral_+z",
275        "lateral_-z",
276    ];
277
278    for morph_name in required_morphologies {
279        if !genome.morphologies.contains(morph_name) {
280            morphologies_added += 1;
281        }
282    }
283
284    // Add all missing core morphologies in one call
285    if morphologies_added > 0 {
286        add_core_morphologies(&mut genome.morphologies);
287        tracing::info!("Added {} missing core morphologies", morphologies_added);
288    }
289
290    (areas_added, morphologies_added)
291}
292
293/// Add core morphologies to a registry
294pub fn add_core_morphologies(registry: &mut MorphologyRegistry) {
295    use crate::{Morphology, MorphologyParameters, MorphologyType};
296
297    // block_to_block - Connect neurons in same position
298    registry.add_morphology(
299        "block_to_block".to_string(),
300        Morphology {
301            morphology_type: MorphologyType::Vectors,
302            parameters: MorphologyParameters::Vectors {
303                vectors: vec![[0, 0, 0]],
304            },
305            class: "core".to_string(),
306        },
307    );
308
309    // projector - Function-based morphology
310    registry.add_morphology(
311        "projector".to_string(),
312        Morphology {
313            morphology_type: MorphologyType::Functions,
314            parameters: MorphologyParameters::Functions {},
315            class: "core".to_string(),
316        },
317    );
318
319    // sweeper - Function-based sequential sweep mapping morphology
320    registry.add_morphology(
321        "sweeper".to_string(),
322        Morphology {
323            morphology_type: MorphologyType::Functions,
324            parameters: MorphologyParameters::Functions {},
325            class: "core".to_string(),
326        },
327    );
328
329    // last_to_first - connect highest source voxel to destination origin
330    registry.add_morphology(
331        "last_to_first".to_string(),
332        Morphology {
333            morphology_type: MorphologyType::Functions,
334            parameters: MorphologyParameters::Functions {},
335            class: "core".to_string(),
336        },
337    );
338
339    // episodic_memory - Function-based morphology
340    registry.add_morphology(
341        "episodic_memory".to_string(),
342        Morphology {
343            morphology_type: MorphologyType::Functions,
344            parameters: MorphologyParameters::Functions {},
345            class: "core".to_string(),
346        },
347    );
348
349    // memory_replay - Function-based morphology
350    registry.add_morphology(
351        "memory_replay".to_string(),
352        Morphology {
353            morphology_type: MorphologyType::Functions,
354            parameters: MorphologyParameters::Functions {},
355            class: "core".to_string(),
356        },
357    );
358
359    // associative_memory (bi-directional STDP) - Function-based morphology
360    registry.add_morphology(
361        "associative_memory".to_string(),
362        Morphology {
363            morphology_type: MorphologyType::Functions,
364            parameters: MorphologyParameters::Functions {},
365            class: "core".to_string(),
366        },
367    );
368
369    // rotator_z - Function-based morphology for z-layered XY rotations [-90,+90]
370    registry.add_morphology(
371        "rotator_z".to_string(),
372        Morphology {
373            morphology_type: MorphologyType::Functions,
374            parameters: MorphologyParameters::Functions {},
375            class: "core".to_string(),
376        },
377    );
378
379    // bitmask_encoder_x - Bitmask encode along X axis
380    registry.add_morphology(
381        "bitmask_encoder_x".to_string(),
382        Morphology {
383            morphology_type: MorphologyType::Functions,
384            parameters: MorphologyParameters::Functions {},
385            class: "core".to_string(),
386        },
387    );
388
389    // bitmask_encoder_y - Bitmask encode along Y axis
390    registry.add_morphology(
391        "bitmask_encoder_y".to_string(),
392        Morphology {
393            morphology_type: MorphologyType::Functions,
394            parameters: MorphologyParameters::Functions {},
395            class: "core".to_string(),
396        },
397    );
398
399    // bitmask_encoder_z - Bitmask encode along Z axis
400    registry.add_morphology(
401        "bitmask_encoder_z".to_string(),
402        Morphology {
403            morphology_type: MorphologyType::Functions,
404            parameters: MorphologyParameters::Functions {},
405            class: "core".to_string(),
406        },
407    );
408
409    // bitmask_decoder_x - Bitmask decode along X axis
410    registry.add_morphology(
411        "bitmask_decoder_x".to_string(),
412        Morphology {
413            morphology_type: MorphologyType::Functions,
414            parameters: MorphologyParameters::Functions {},
415            class: "core".to_string(),
416        },
417    );
418
419    // bitmask_decoder_y - Bitmask decode along Y axis
420    registry.add_morphology(
421        "bitmask_decoder_y".to_string(),
422        Morphology {
423            morphology_type: MorphologyType::Functions,
424            parameters: MorphologyParameters::Functions {},
425            class: "core".to_string(),
426        },
427    );
428
429    // bitmask_decoder_z - Bitmask decode along Z axis
430    registry.add_morphology(
431        "bitmask_decoder_z".to_string(),
432        Morphology {
433            morphology_type: MorphologyType::Functions,
434            parameters: MorphologyParameters::Functions {},
435            class: "core".to_string(),
436        },
437    );
438
439    // all_to_0-0-0 - Connect all neurons to origin
440    registry.add_morphology(
441        "all_to_0-0-0".to_string(),
442        Morphology {
443            morphology_type: MorphologyType::Patterns,
444            parameters: MorphologyParameters::Patterns {
445                patterns: vec![[
446                    vec![
447                        crate::PatternElement::Wildcard,
448                        crate::PatternElement::Wildcard,
449                        crate::PatternElement::Wildcard,
450                    ],
451                    vec![
452                        crate::PatternElement::Value(0),
453                        crate::PatternElement::Value(0),
454                        crate::PatternElement::Value(0),
455                    ],
456                ]],
457            },
458            class: "core".to_string(),
459        },
460    );
461
462    // 0-0-0_to_all - Connect origin to all neurons
463    registry.add_morphology(
464        "0-0-0_to_all".to_string(),
465        Morphology {
466            morphology_type: MorphologyType::Patterns,
467            parameters: MorphologyParameters::Patterns {
468                patterns: vec![[
469                    vec![
470                        crate::PatternElement::Value(0),
471                        crate::PatternElement::Value(0),
472                        crate::PatternElement::Value(0),
473                    ],
474                    vec![
475                        crate::PatternElement::Wildcard,
476                        crate::PatternElement::Wildcard,
477                        crate::PatternElement::Wildcard,
478                    ],
479                ]],
480            },
481            class: "core".to_string(),
482        },
483    );
484
485    // tile - Composite tiling morphology (mapper + subregion parameters)
486    registry.add_morphology(
487        "tile".to_string(),
488        Morphology {
489            morphology_type: MorphologyType::Composite,
490            parameters: MorphologyParameters::Composite {
491                src_seed: [16, 16, 1],
492                src_pattern: vec![[1, 0], [1, 0], [1, 0]],
493                mapper_morphology: "projector".to_string(),
494            },
495            class: "core".to_string(),
496        },
497    );
498
499    // lateral_+x - Connect along +X axis
500    registry.add_morphology(
501        "lateral_+x".to_string(),
502        Morphology {
503            morphology_type: MorphologyType::Vectors,
504            parameters: MorphologyParameters::Vectors {
505                vectors: vec![[1, 0, 0]],
506            },
507            class: "core".to_string(),
508        },
509    );
510
511    // lateral_-x - Connect along -X axis
512    registry.add_morphology(
513        "lateral_-x".to_string(),
514        Morphology {
515            morphology_type: MorphologyType::Vectors,
516            parameters: MorphologyParameters::Vectors {
517                vectors: vec![[-1, 0, 0]],
518            },
519            class: "core".to_string(),
520        },
521    );
522
523    // lateral_+y - Connect along +Y axis
524    registry.add_morphology(
525        "lateral_+y".to_string(),
526        Morphology {
527            morphology_type: MorphologyType::Vectors,
528            parameters: MorphologyParameters::Vectors {
529                vectors: vec![[0, 1, 0]],
530            },
531            class: "core".to_string(),
532        },
533    );
534
535    // lateral_-y - Connect along -Y axis
536    registry.add_morphology(
537        "lateral_-y".to_string(),
538        Morphology {
539            morphology_type: MorphologyType::Vectors,
540            parameters: MorphologyParameters::Vectors {
541                vectors: vec![[0, -1, 0]],
542            },
543            class: "core".to_string(),
544        },
545    );
546
547    // lateral_+z - Connect along +Z axis
548    registry.add_morphology(
549        "lateral_+z".to_string(),
550        Morphology {
551            morphology_type: MorphologyType::Vectors,
552            parameters: MorphologyParameters::Vectors {
553                vectors: vec![[0, 0, 1]],
554            },
555            class: "core".to_string(),
556        },
557    );
558
559    // lateral_-z - Connect along -Z axis
560    registry.add_morphology(
561        "lateral_-z".to_string(),
562        Morphology {
563            morphology_type: MorphologyType::Vectors,
564            parameters: MorphologyParameters::Vectors {
565                vectors: vec![[0, 0, -1]],
566            },
567            class: "core".to_string(),
568        },
569    );
570}
571
572/// Load essential genome from embedded JSON
573///
574/// Automatically ensures core components (_death, _power, core morphologies) are present
575pub fn load_essential_genome() -> Result<RuntimeGenome, crate::types::EvoError> {
576    use crate::genome::loader::load_genome_from_json;
577    let mut genome = load_genome_from_json(ESSENTIAL_GENOME_JSON)?;
578    let (areas_added, morphs_added) = ensure_core_components(&mut genome);
579    if areas_added > 0 || morphs_added > 0 {
580        tracing::info!(
581            "Essential genome: added {} core areas, {} core morphologies",
582            areas_added,
583            morphs_added
584        );
585    }
586    Ok(genome)
587}
588
589/// Load barebones genome from embedded JSON
590///
591/// Automatically ensures core components (_death, _power, core morphologies) are present
592pub fn load_barebones_genome() -> Result<RuntimeGenome, crate::types::EvoError> {
593    use crate::genome::loader::load_genome_from_json;
594    let mut genome = load_genome_from_json(BAREBONES_GENOME_JSON)?;
595    let (areas_added, morphs_added) = ensure_core_components(&mut genome);
596    if areas_added > 0 || morphs_added > 0 {
597        tracing::info!(
598            "Barebones genome: added {} core areas, {} core morphologies",
599            areas_added,
600            morphs_added
601        );
602    }
603    Ok(genome)
604}
605
606/// Load test genome from embedded JSON
607///
608/// Automatically ensures core components (_death, _power, core morphologies) are present
609pub fn load_test_genome() -> Result<RuntimeGenome, crate::types::EvoError> {
610    use crate::genome::loader::load_genome_from_json;
611    let mut genome = load_genome_from_json(TEST_GENOME_JSON)?;
612    let (areas_added, morphs_added) = ensure_core_components(&mut genome);
613    if areas_added > 0 || morphs_added > 0 {
614        tracing::info!(
615            "Test genome: added {} core areas, {} core morphologies",
616            areas_added,
617            morphs_added
618        );
619    }
620    Ok(genome)
621}
622
623/// Load vision genome from embedded JSON
624///
625/// Automatically ensures core components (_death, _power, core morphologies) are present
626pub fn load_vision_genome() -> Result<RuntimeGenome, crate::types::EvoError> {
627    use crate::genome::loader::load_genome_from_json;
628    let mut genome = load_genome_from_json(VISION_GENOME_JSON)?;
629    let (areas_added, morphs_added) = ensure_core_components(&mut genome);
630    if areas_added > 0 || morphs_added > 0 {
631        tracing::info!(
632            "Vision genome: added {} core areas, {} core morphologies",
633            areas_added,
634            morphs_added
635        );
636    }
637    Ok(genome)
638}
639
640#[cfg(test)]
641mod tests {
642    use super::*;
643
644    #[test]
645    fn test_create_minimal_genome() {
646        let genome = create_minimal_genome("test_genome".to_string(), "Test Genome".to_string());
647
648        assert_eq!(genome.metadata.genome_id, "test_genome");
649        assert_eq!(genome.metadata.version, "2.0");
650        assert_eq!(genome.cortical_areas.len(), 0);
651        assert_eq!(genome.morphologies.count(), 0);
652    }
653
654    #[test]
655    fn test_create_genome_with_core_areas() {
656        let genome =
657            create_genome_with_core_areas("test_genome".to_string(), "Test Genome".to_string());
658
659        assert_eq!(genome.metadata.genome_id, "test_genome");
660        assert_eq!(genome.cortical_areas.len(), 3);
661
662        let death_id = crate::genome::parser::string_to_cortical_id("_death").expect("Valid ID");
663        let power_id = crate::genome::parser::string_to_cortical_id("_power").expect("Valid ID");
664        let fatigue_id =
665            crate::genome::parser::string_to_cortical_id("_fatigue").expect("Valid ID");
666        assert!(genome.cortical_areas.contains_key(&death_id));
667        assert!(genome.cortical_areas.contains_key(&power_id));
668        assert!(genome.cortical_areas.contains_key(&fatigue_id));
669
670        // Verify _power has correct properties
671        let power = genome.cortical_areas.get(&power_id).unwrap();
672        assert_eq!(power.cortical_id.as_base_64(), power_id.as_base_64());
673        assert_eq!(power.cortical_idx, 1);
674        assert_eq!(power.dimensions.width, 1);
675        assert_eq!(power.dimensions.height, 1);
676        assert_eq!(power.dimensions.depth, 1);
677    }
678
679    #[test]
680    fn test_create_genome_with_core_morphologies() {
681        let genome = create_genome_with_core_morphologies(
682            "test_genome".to_string(),
683            "Test Genome".to_string(),
684        );
685
686        assert_eq!(genome.metadata.genome_id, "test_genome");
687        assert!(genome.morphologies.count() > 0);
688        assert!(genome.morphologies.contains("block_to_block"));
689        assert!(genome.morphologies.contains("projector"));
690        assert!(genome.morphologies.contains("lateral_+x"));
691    }
692
693    #[test]
694    fn test_add_core_morphologies() {
695        let mut registry = MorphologyRegistry::new();
696        add_core_morphologies(&mut registry);
697
698        // Should have at least 11 core morphologies
699        assert!(registry.count() >= 11);
700        assert!(registry.contains("block_to_block"));
701        assert!(registry.contains("projector"));
702        assert!(registry.contains("all_to_0-0-0"));
703        assert!(registry.contains("lateral_+x"));
704        assert!(registry.contains("lateral_-z"));
705    }
706
707    #[test]
708    fn test_embedded_genomes_exist() {
709        // Test that embedded genome strings are not empty
710        // These are compile-time constants, so they're always non-empty
711        // The assertions verify the constants are defined correctly
712        #[allow(clippy::const_is_empty)]
713        {
714            assert!(!ESSENTIAL_GENOME_JSON.is_empty());
715            assert!(!BAREBONES_GENOME_JSON.is_empty());
716            assert!(!TEST_GENOME_JSON.is_empty());
717            assert!(!VISION_GENOME_JSON.is_empty());
718        }
719    }
720
721    #[test]
722    fn test_load_essential_genome() {
723        let genome = load_essential_genome().expect("Failed to load essential genome");
724        assert!(!genome.cortical_areas.is_empty());
725        // Essential genome should have _power
726        let power_id = crate::genome::parser::string_to_cortical_id("_power").expect("Valid ID");
727        assert!(genome.cortical_areas.contains_key(&power_id));
728    }
729
730    #[test]
731    fn test_ensure_core_components_adds_missing_areas() {
732        // Create a minimal genome without core areas
733        let mut genome = create_minimal_genome("test".to_string(), "Test".to_string());
734
735        assert_eq!(genome.cortical_areas.len(), 0);
736
737        // Ensure core components
738        let (areas_added, _) = ensure_core_components(&mut genome);
739
740        // Should have added _death, _power, and _fatigue
741        assert_eq!(areas_added, 3);
742
743        let death_id = crate::genome::parser::string_to_cortical_id("_death").expect("Valid ID");
744        let power_id = crate::genome::parser::string_to_cortical_id("_power").expect("Valid ID");
745        let fatigue_id =
746            crate::genome::parser::string_to_cortical_id("_fatigue").expect("Valid ID");
747        assert!(genome.cortical_areas.contains_key(&death_id));
748        assert!(genome.cortical_areas.contains_key(&power_id));
749        assert!(genome.cortical_areas.contains_key(&fatigue_id));
750
751        // Verify cortical_idx assignments
752        assert_eq!(
753            genome.cortical_areas.get(&death_id).unwrap().cortical_idx,
754            0
755        );
756        assert_eq!(
757            genome.cortical_areas.get(&power_id).unwrap().cortical_idx,
758            1
759        );
760        assert_eq!(
761            genome.cortical_areas.get(&fatigue_id).unwrap().cortical_idx,
762            2
763        );
764    }
765
766    #[test]
767    fn test_ensure_core_components_adds_missing_morphologies() {
768        // Create a genome with core areas but no morphologies
769        let mut genome = create_genome_with_core_areas("test".to_string(), "Test".to_string());
770
771        assert_eq!(genome.morphologies.count(), 0);
772
773        // Ensure core components
774        let (_, morphs_added) = ensure_core_components(&mut genome);
775
776        // Should have added core morphologies
777        assert!(morphs_added > 0);
778        assert!(genome.morphologies.contains("block_to_block"));
779        assert!(genome.morphologies.contains("projector"));
780        assert!(genome.morphologies.contains("episodic_memory"));
781        assert!(genome.morphologies.contains("lateral_+x"));
782    }
783
784    #[test]
785    fn test_ensure_core_components_idempotent() {
786        // Create a genome with all core components
787        let mut genome = create_genome_with_core_areas("test".to_string(), "Test".to_string());
788        add_core_morphologies(&mut genome.morphologies);
789
790        // Run ensure_core_components
791        let (areas_added, morphs_added) = ensure_core_components(&mut genome);
792
793        // Should not add anything (already present)
794        assert_eq!(areas_added, 0);
795        assert_eq!(morphs_added, 0);
796    }
797}