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        "transpose_xy",
256        "transpose_yz",
257        "transpose_xz",
258        "sweeper",
259        "last_to_first",
260        "bitmask_encoder_x",
261        "bitmask_encoder_y",
262        "bitmask_encoder_z",
263        "bitmask_decoder_x",
264        "bitmask_decoder_y",
265        "bitmask_decoder_z",
266        "episodic_memory",
267        "memory_replay",
268        "associative_memory",
269        "rotator_z",
270        "all_to_0-0-0",
271        "0-0-0_to_all",
272        "tile",
273        "lateral_+x",
274        "lateral_-x",
275        "lateral_+y",
276        "lateral_-y",
277        "lateral_+z",
278        "lateral_-z",
279    ];
280
281    for morph_name in required_morphologies {
282        if !genome.morphologies.contains(morph_name) {
283            morphologies_added += 1;
284        }
285    }
286
287    // Add all missing core morphologies in one call
288    if morphologies_added > 0 {
289        add_core_morphologies(&mut genome.morphologies);
290        tracing::info!("Added {} missing core morphologies", morphologies_added);
291    }
292
293    (areas_added, morphologies_added)
294}
295
296/// Add core morphologies to a registry
297pub fn add_core_morphologies(registry: &mut MorphologyRegistry) {
298    use crate::{Morphology, MorphologyParameters, MorphologyType};
299
300    // block_to_block - Connect neurons in same position
301    registry.add_morphology(
302        "block_to_block".to_string(),
303        Morphology {
304            morphology_type: MorphologyType::Vectors,
305            parameters: MorphologyParameters::Vectors {
306                vectors: vec![[0, 0, 0]],
307            },
308            class: "core".to_string(),
309        },
310    );
311
312    // projector - Function-based morphology
313    registry.add_morphology(
314        "projector".to_string(),
315        Morphology {
316            morphology_type: MorphologyType::Functions,
317            parameters: MorphologyParameters::Functions {},
318            class: "core".to_string(),
319        },
320    );
321
322    // transpose_xy - Projector with x/y axis transposition
323    registry.add_morphology(
324        "transpose_xy".to_string(),
325        Morphology {
326            morphology_type: MorphologyType::Functions,
327            parameters: MorphologyParameters::Functions {},
328            class: "core".to_string(),
329        },
330    );
331
332    // transpose_yz - Projector with y/z axis transposition
333    registry.add_morphology(
334        "transpose_yz".to_string(),
335        Morphology {
336            morphology_type: MorphologyType::Functions,
337            parameters: MorphologyParameters::Functions {},
338            class: "core".to_string(),
339        },
340    );
341
342    // transpose_xz - Projector with x/z axis transposition
343    registry.add_morphology(
344        "transpose_xz".to_string(),
345        Morphology {
346            morphology_type: MorphologyType::Functions,
347            parameters: MorphologyParameters::Functions {},
348            class: "core".to_string(),
349        },
350    );
351
352    // sweeper - Function-based sequential sweep mapping morphology
353    registry.add_morphology(
354        "sweeper".to_string(),
355        Morphology {
356            morphology_type: MorphologyType::Functions,
357            parameters: MorphologyParameters::Functions {},
358            class: "core".to_string(),
359        },
360    );
361
362    // last_to_first - connect highest source voxel to destination origin
363    registry.add_morphology(
364        "last_to_first".to_string(),
365        Morphology {
366            morphology_type: MorphologyType::Functions,
367            parameters: MorphologyParameters::Functions {},
368            class: "core".to_string(),
369        },
370    );
371
372    // episodic_memory - Function-based morphology
373    registry.add_morphology(
374        "episodic_memory".to_string(),
375        Morphology {
376            morphology_type: MorphologyType::Functions,
377            parameters: MorphologyParameters::Functions {},
378            class: "core".to_string(),
379        },
380    );
381
382    // memory_replay - Function-based morphology
383    registry.add_morphology(
384        "memory_replay".to_string(),
385        Morphology {
386            morphology_type: MorphologyType::Functions,
387            parameters: MorphologyParameters::Functions {},
388            class: "core".to_string(),
389        },
390    );
391
392    // associative_memory (bi-directional STDP) - Function-based morphology
393    registry.add_morphology(
394        "associative_memory".to_string(),
395        Morphology {
396            morphology_type: MorphologyType::Functions,
397            parameters: MorphologyParameters::Functions {},
398            class: "core".to_string(),
399        },
400    );
401
402    // rotator_z - Function-based morphology for z-layered XY rotations [-90,+90]
403    registry.add_morphology(
404        "rotator_z".to_string(),
405        Morphology {
406            morphology_type: MorphologyType::Functions,
407            parameters: MorphologyParameters::Functions {},
408            class: "core".to_string(),
409        },
410    );
411
412    // bitmask_encoder_x - Bitmask encode along X axis
413    registry.add_morphology(
414        "bitmask_encoder_x".to_string(),
415        Morphology {
416            morphology_type: MorphologyType::Functions,
417            parameters: MorphologyParameters::Functions {},
418            class: "core".to_string(),
419        },
420    );
421
422    // bitmask_encoder_y - Bitmask encode along Y axis
423    registry.add_morphology(
424        "bitmask_encoder_y".to_string(),
425        Morphology {
426            morphology_type: MorphologyType::Functions,
427            parameters: MorphologyParameters::Functions {},
428            class: "core".to_string(),
429        },
430    );
431
432    // bitmask_encoder_z - Bitmask encode along Z axis
433    registry.add_morphology(
434        "bitmask_encoder_z".to_string(),
435        Morphology {
436            morphology_type: MorphologyType::Functions,
437            parameters: MorphologyParameters::Functions {},
438            class: "core".to_string(),
439        },
440    );
441
442    // bitmask_decoder_x - Bitmask decode along X axis
443    registry.add_morphology(
444        "bitmask_decoder_x".to_string(),
445        Morphology {
446            morphology_type: MorphologyType::Functions,
447            parameters: MorphologyParameters::Functions {},
448            class: "core".to_string(),
449        },
450    );
451
452    // bitmask_decoder_y - Bitmask decode along Y axis
453    registry.add_morphology(
454        "bitmask_decoder_y".to_string(),
455        Morphology {
456            morphology_type: MorphologyType::Functions,
457            parameters: MorphologyParameters::Functions {},
458            class: "core".to_string(),
459        },
460    );
461
462    // bitmask_decoder_z - Bitmask decode along Z axis
463    registry.add_morphology(
464        "bitmask_decoder_z".to_string(),
465        Morphology {
466            morphology_type: MorphologyType::Functions,
467            parameters: MorphologyParameters::Functions {},
468            class: "core".to_string(),
469        },
470    );
471
472    // all_to_0-0-0 - Connect all neurons to origin
473    registry.add_morphology(
474        "all_to_0-0-0".to_string(),
475        Morphology {
476            morphology_type: MorphologyType::Patterns,
477            parameters: MorphologyParameters::Patterns {
478                patterns: vec![[
479                    vec![
480                        crate::PatternElement::Wildcard,
481                        crate::PatternElement::Wildcard,
482                        crate::PatternElement::Wildcard,
483                    ],
484                    vec![
485                        crate::PatternElement::Value(0),
486                        crate::PatternElement::Value(0),
487                        crate::PatternElement::Value(0),
488                    ],
489                ]],
490            },
491            class: "core".to_string(),
492        },
493    );
494
495    // 0-0-0_to_all - Connect origin to all neurons
496    registry.add_morphology(
497        "0-0-0_to_all".to_string(),
498        Morphology {
499            morphology_type: MorphologyType::Patterns,
500            parameters: MorphologyParameters::Patterns {
501                patterns: vec![[
502                    vec![
503                        crate::PatternElement::Value(0),
504                        crate::PatternElement::Value(0),
505                        crate::PatternElement::Value(0),
506                    ],
507                    vec![
508                        crate::PatternElement::Wildcard,
509                        crate::PatternElement::Wildcard,
510                        crate::PatternElement::Wildcard,
511                    ],
512                ]],
513            },
514            class: "core".to_string(),
515        },
516    );
517
518    // tile - Composite tiling morphology (mapper + subregion parameters)
519    registry.add_morphology(
520        "tile".to_string(),
521        Morphology {
522            morphology_type: MorphologyType::Composite,
523            parameters: MorphologyParameters::Composite {
524                src_seed: [16, 16, 1],
525                src_pattern: vec![[1, 0], [1, 0], [1, 0]],
526                mapper_morphology: "projector".to_string(),
527            },
528            class: "core".to_string(),
529        },
530    );
531
532    // lateral_+x - Connect along +X axis
533    registry.add_morphology(
534        "lateral_+x".to_string(),
535        Morphology {
536            morphology_type: MorphologyType::Vectors,
537            parameters: MorphologyParameters::Vectors {
538                vectors: vec![[1, 0, 0]],
539            },
540            class: "core".to_string(),
541        },
542    );
543
544    // lateral_-x - Connect along -X axis
545    registry.add_morphology(
546        "lateral_-x".to_string(),
547        Morphology {
548            morphology_type: MorphologyType::Vectors,
549            parameters: MorphologyParameters::Vectors {
550                vectors: vec![[-1, 0, 0]],
551            },
552            class: "core".to_string(),
553        },
554    );
555
556    // lateral_+y - Connect along +Y axis
557    registry.add_morphology(
558        "lateral_+y".to_string(),
559        Morphology {
560            morphology_type: MorphologyType::Vectors,
561            parameters: MorphologyParameters::Vectors {
562                vectors: vec![[0, 1, 0]],
563            },
564            class: "core".to_string(),
565        },
566    );
567
568    // lateral_-y - Connect along -Y axis
569    registry.add_morphology(
570        "lateral_-y".to_string(),
571        Morphology {
572            morphology_type: MorphologyType::Vectors,
573            parameters: MorphologyParameters::Vectors {
574                vectors: vec![[0, -1, 0]],
575            },
576            class: "core".to_string(),
577        },
578    );
579
580    // lateral_+z - Connect along +Z axis
581    registry.add_morphology(
582        "lateral_+z".to_string(),
583        Morphology {
584            morphology_type: MorphologyType::Vectors,
585            parameters: MorphologyParameters::Vectors {
586                vectors: vec![[0, 0, 1]],
587            },
588            class: "core".to_string(),
589        },
590    );
591
592    // lateral_-z - Connect along -Z axis
593    registry.add_morphology(
594        "lateral_-z".to_string(),
595        Morphology {
596            morphology_type: MorphologyType::Vectors,
597            parameters: MorphologyParameters::Vectors {
598                vectors: vec![[0, 0, -1]],
599            },
600            class: "core".to_string(),
601        },
602    );
603}
604
605/// Load essential genome from embedded JSON
606///
607/// Automatically ensures core components (_death, _power, core morphologies) are present
608pub fn load_essential_genome() -> Result<RuntimeGenome, crate::types::EvoError> {
609    use crate::genome::loader::load_genome_from_json;
610    let mut genome = load_genome_from_json(ESSENTIAL_GENOME_JSON)?;
611    let (areas_added, morphs_added) = ensure_core_components(&mut genome);
612    if areas_added > 0 || morphs_added > 0 {
613        tracing::info!(
614            "Essential genome: added {} core areas, {} core morphologies",
615            areas_added,
616            morphs_added
617        );
618    }
619    Ok(genome)
620}
621
622/// Load barebones genome from embedded JSON
623///
624/// Automatically ensures core components (_death, _power, core morphologies) are present
625pub fn load_barebones_genome() -> Result<RuntimeGenome, crate::types::EvoError> {
626    use crate::genome::loader::load_genome_from_json;
627    let mut genome = load_genome_from_json(BAREBONES_GENOME_JSON)?;
628    let (areas_added, morphs_added) = ensure_core_components(&mut genome);
629    if areas_added > 0 || morphs_added > 0 {
630        tracing::info!(
631            "Barebones genome: added {} core areas, {} core morphologies",
632            areas_added,
633            morphs_added
634        );
635    }
636    Ok(genome)
637}
638
639/// Load test genome from embedded JSON
640///
641/// Automatically ensures core components (_death, _power, core morphologies) are present
642pub fn load_test_genome() -> Result<RuntimeGenome, crate::types::EvoError> {
643    use crate::genome::loader::load_genome_from_json;
644    let mut genome = load_genome_from_json(TEST_GENOME_JSON)?;
645    let (areas_added, morphs_added) = ensure_core_components(&mut genome);
646    if areas_added > 0 || morphs_added > 0 {
647        tracing::info!(
648            "Test genome: added {} core areas, {} core morphologies",
649            areas_added,
650            morphs_added
651        );
652    }
653    Ok(genome)
654}
655
656/// Load vision genome from embedded JSON
657///
658/// Automatically ensures core components (_death, _power, core morphologies) are present
659pub fn load_vision_genome() -> Result<RuntimeGenome, crate::types::EvoError> {
660    use crate::genome::loader::load_genome_from_json;
661    let mut genome = load_genome_from_json(VISION_GENOME_JSON)?;
662    let (areas_added, morphs_added) = ensure_core_components(&mut genome);
663    if areas_added > 0 || morphs_added > 0 {
664        tracing::info!(
665            "Vision genome: added {} core areas, {} core morphologies",
666            areas_added,
667            morphs_added
668        );
669    }
670    Ok(genome)
671}
672
673#[cfg(test)]
674mod tests {
675    use super::*;
676
677    #[test]
678    fn test_create_minimal_genome() {
679        let genome = create_minimal_genome("test_genome".to_string(), "Test Genome".to_string());
680
681        assert_eq!(genome.metadata.genome_id, "test_genome");
682        assert_eq!(genome.metadata.version, "2.0");
683        assert_eq!(genome.cortical_areas.len(), 0);
684        assert_eq!(genome.morphologies.count(), 0);
685    }
686
687    #[test]
688    fn test_create_genome_with_core_areas() {
689        let genome =
690            create_genome_with_core_areas("test_genome".to_string(), "Test Genome".to_string());
691
692        assert_eq!(genome.metadata.genome_id, "test_genome");
693        assert_eq!(genome.cortical_areas.len(), 3);
694
695        let death_id = crate::genome::parser::string_to_cortical_id("_death").expect("Valid ID");
696        let power_id = crate::genome::parser::string_to_cortical_id("_power").expect("Valid ID");
697        let fatigue_id =
698            crate::genome::parser::string_to_cortical_id("_fatigue").expect("Valid ID");
699        assert!(genome.cortical_areas.contains_key(&death_id));
700        assert!(genome.cortical_areas.contains_key(&power_id));
701        assert!(genome.cortical_areas.contains_key(&fatigue_id));
702
703        // Verify _power has correct properties
704        let power = genome.cortical_areas.get(&power_id).unwrap();
705        assert_eq!(power.cortical_id.as_base_64(), power_id.as_base_64());
706        assert_eq!(power.cortical_idx, 1);
707        assert_eq!(power.dimensions.width, 1);
708        assert_eq!(power.dimensions.height, 1);
709        assert_eq!(power.dimensions.depth, 1);
710    }
711
712    #[test]
713    fn test_create_genome_with_core_morphologies() {
714        let genome = create_genome_with_core_morphologies(
715            "test_genome".to_string(),
716            "Test Genome".to_string(),
717        );
718
719        assert_eq!(genome.metadata.genome_id, "test_genome");
720        assert!(genome.morphologies.count() > 0);
721        assert!(genome.morphologies.contains("block_to_block"));
722        assert!(genome.morphologies.contains("projector"));
723        assert!(genome.morphologies.contains("transpose_xy"));
724        assert!(genome.morphologies.contains("transpose_yz"));
725        assert!(genome.morphologies.contains("transpose_xz"));
726        assert!(genome.morphologies.contains("lateral_+x"));
727    }
728
729    #[test]
730    fn test_add_core_morphologies() {
731        let mut registry = MorphologyRegistry::new();
732        add_core_morphologies(&mut registry);
733
734        // Should have at least 11 core morphologies
735        assert!(registry.count() >= 11);
736        assert!(registry.contains("block_to_block"));
737        assert!(registry.contains("projector"));
738        assert!(registry.contains("transpose_xy"));
739        assert!(registry.contains("transpose_yz"));
740        assert!(registry.contains("transpose_xz"));
741        assert!(registry.contains("all_to_0-0-0"));
742        assert!(registry.contains("lateral_+x"));
743        assert!(registry.contains("lateral_-z"));
744    }
745
746    #[test]
747    fn test_embedded_genomes_exist() {
748        // Test that embedded genome strings are not empty
749        // These are compile-time constants, so they're always non-empty
750        // The assertions verify the constants are defined correctly
751        #[allow(clippy::const_is_empty)]
752        {
753            assert!(!ESSENTIAL_GENOME_JSON.is_empty());
754            assert!(!BAREBONES_GENOME_JSON.is_empty());
755            assert!(!TEST_GENOME_JSON.is_empty());
756            assert!(!VISION_GENOME_JSON.is_empty());
757        }
758    }
759
760    #[test]
761    fn test_load_essential_genome() {
762        let genome = load_essential_genome().expect("Failed to load essential genome");
763        assert!(!genome.cortical_areas.is_empty());
764        // Essential genome should have _power
765        let power_id = crate::genome::parser::string_to_cortical_id("_power").expect("Valid ID");
766        assert!(genome.cortical_areas.contains_key(&power_id));
767    }
768
769    #[test]
770    fn test_ensure_core_components_adds_missing_areas() {
771        // Create a minimal genome without core areas
772        let mut genome = create_minimal_genome("test".to_string(), "Test".to_string());
773
774        assert_eq!(genome.cortical_areas.len(), 0);
775
776        // Ensure core components
777        let (areas_added, _) = ensure_core_components(&mut genome);
778
779        // Should have added _death, _power, and _fatigue
780        assert_eq!(areas_added, 3);
781
782        let death_id = crate::genome::parser::string_to_cortical_id("_death").expect("Valid ID");
783        let power_id = crate::genome::parser::string_to_cortical_id("_power").expect("Valid ID");
784        let fatigue_id =
785            crate::genome::parser::string_to_cortical_id("_fatigue").expect("Valid ID");
786        assert!(genome.cortical_areas.contains_key(&death_id));
787        assert!(genome.cortical_areas.contains_key(&power_id));
788        assert!(genome.cortical_areas.contains_key(&fatigue_id));
789
790        // Verify cortical_idx assignments
791        assert_eq!(
792            genome.cortical_areas.get(&death_id).unwrap().cortical_idx,
793            0
794        );
795        assert_eq!(
796            genome.cortical_areas.get(&power_id).unwrap().cortical_idx,
797            1
798        );
799        assert_eq!(
800            genome.cortical_areas.get(&fatigue_id).unwrap().cortical_idx,
801            2
802        );
803    }
804
805    #[test]
806    fn test_ensure_core_components_adds_missing_morphologies() {
807        // Create a genome with core areas but no morphologies
808        let mut genome = create_genome_with_core_areas("test".to_string(), "Test".to_string());
809
810        assert_eq!(genome.morphologies.count(), 0);
811
812        // Ensure core components
813        let (_, morphs_added) = ensure_core_components(&mut genome);
814
815        // Should have added core morphologies
816        assert!(morphs_added > 0);
817        assert!(genome.morphologies.contains("block_to_block"));
818        assert!(genome.morphologies.contains("projector"));
819        assert!(genome.morphologies.contains("transpose_xy"));
820        assert!(genome.morphologies.contains("transpose_yz"));
821        assert!(genome.morphologies.contains("transpose_xz"));
822        assert!(genome.morphologies.contains("episodic_memory"));
823        assert!(genome.morphologies.contains("lateral_+x"));
824    }
825
826    #[test]
827    fn test_ensure_core_components_idempotent() {
828        // Create a genome with all core components
829        let mut genome = create_genome_with_core_areas("test".to_string(), "Test".to_string());
830        add_core_morphologies(&mut genome.morphologies);
831
832        // Run ensure_core_components
833        let (areas_added, morphs_added) = ensure_core_components(&mut genome);
834
835        // Should not add anything (already present)
836        assert_eq!(areas_added, 0);
837        assert_eq!(morphs_added, 0);
838    }
839}