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 _pain cortical area (cortical_idx = 3) from template
142pub fn create_pain_area() -> CorticalArea {
143    let cortical_id = CoreCorticalType::Pain.to_cortical_id();
144    let cortical_type = cortical_id
145        .as_cortical_type()
146        .expect("Pain cortical ID should map to Core type");
147
148    let mut area = CorticalArea::new(
149        cortical_id,
150        3, // cortical_idx = 3 (reserved)
151        "Pain".to_string(),
152        CorticalAreaDimensions::new(1, 1, 1).expect("Failed to create dimensions"),
153        GenomeCoordinate3D::new(0, 0, -40),
154        cortical_type,
155    )
156    .expect("Failed to create _pain area");
157
158    let mut props = get_default_neural_properties();
159    props.insert("cortical_group".to_string(), Value::from("CORE"));
160    props.insert("2d_coordinate".to_string(), Value::from(vec![-10, 10]));
161    area.properties = props;
162    area
163}
164
165/// Create _pleasure cortical area (cortical_idx = 4) from template
166pub fn create_pleasure_area() -> CorticalArea {
167    let cortical_id = CoreCorticalType::Pleasure.to_cortical_id();
168    let cortical_type = cortical_id
169        .as_cortical_type()
170        .expect("Pleasure cortical ID should map to Core type");
171
172    let mut area = CorticalArea::new(
173        cortical_id,
174        4, // cortical_idx = 4 (reserved)
175        "Pleasure".to_string(),
176        CorticalAreaDimensions::new(1, 1, 1).expect("Failed to create dimensions"),
177        GenomeCoordinate3D::new(0, 0, -50),
178        cortical_type,
179    )
180    .expect("Failed to create _pleasure area");
181
182    let mut props = get_default_neural_properties();
183    props.insert("cortical_group".to_string(), Value::from("CORE"));
184    props.insert("2d_coordinate".to_string(), Value::from(vec![-10, 20]));
185    area.properties = props;
186    area
187}
188
189/// Create _fear cortical area (cortical_idx = 5) from template
190pub fn create_fear_area() -> CorticalArea {
191    let cortical_id = CoreCorticalType::Fear.to_cortical_id();
192    let cortical_type = cortical_id
193        .as_cortical_type()
194        .expect("Fear cortical ID should map to Core type");
195
196    let mut area = CorticalArea::new(
197        cortical_id,
198        5, // cortical_idx = 5 (reserved)
199        "Fear".to_string(),
200        CorticalAreaDimensions::new(1, 1, 1).expect("Failed to create dimensions"),
201        GenomeCoordinate3D::new(0, 0, -60),
202        cortical_type,
203    )
204    .expect("Failed to create _fear area");
205
206    let mut props = get_default_neural_properties();
207    props.insert("cortical_group".to_string(), Value::from("CORE"));
208    props.insert("2d_coordinate".to_string(), Value::from(vec![-10, 30]));
209    area.properties = props;
210    area
211}
212
213/// Create _hope cortical area (cortical_idx = 6) from template
214pub fn create_hope_area() -> CorticalArea {
215    let cortical_id = CoreCorticalType::Hope.to_cortical_id();
216    let cortical_type = cortical_id
217        .as_cortical_type()
218        .expect("Hope cortical ID should map to Core type");
219
220    let mut area = CorticalArea::new(
221        cortical_id,
222        6, // cortical_idx = 6 (reserved)
223        "Hope".to_string(),
224        CorticalAreaDimensions::new(1, 1, 1).expect("Failed to create dimensions"),
225        GenomeCoordinate3D::new(0, 0, -70),
226        cortical_type,
227    )
228    .expect("Failed to create _hope area");
229
230    let mut props = get_default_neural_properties();
231    props.insert("cortical_group".to_string(), Value::from("CORE"));
232    props.insert("2d_coordinate".to_string(), Value::from(vec![-10, 40]));
233    area.properties = props;
234    area
235}
236
237/// Create a minimal empty genome
238pub fn create_minimal_genome(genome_id: String, genome_title: String) -> RuntimeGenome {
239    RuntimeGenome {
240        metadata: GenomeMetadata {
241            genome_id,
242            genome_title,
243            genome_description: "Minimal genome template".to_string(),
244            version: "2.0".to_string(),
245            timestamp: std::time::SystemTime::now()
246                .duration_since(std::time::UNIX_EPOCH)
247                .unwrap()
248                .as_secs_f64(),
249            brain_regions_root: None, // Will be set after neuroembryogenesis
250        },
251        cortical_areas: HashMap::new(),
252        brain_regions: HashMap::new(),
253        morphologies: MorphologyRegistry::new(),
254        physiology: PhysiologyConfig::default(),
255        signatures: GenomeSignatures {
256            genome: String::new(),
257            blueprint: String::new(),
258            physiology: String::new(),
259            morphologies: None,
260        },
261        stats: GenomeStats::default(),
262    }
263}
264
265/// Create a genome with core areas (_death, _power, _fatigue, _pain, _pleasure, _fear, _hope)
266pub fn create_genome_with_core_areas(genome_id: String, genome_title: String) -> RuntimeGenome {
267    let mut genome = create_minimal_genome(genome_id, genome_title);
268
269    // Add core areas (convert 6-char strings to CorticalID)
270    let death_id =
271        crate::genome::parser::string_to_cortical_id("_death").expect("Valid cortical ID");
272    let power_id =
273        crate::genome::parser::string_to_cortical_id("_power").expect("Valid cortical ID");
274    let fatigue_id =
275        crate::genome::parser::string_to_cortical_id("_fatigue").expect("Valid cortical ID");
276    let pain_id = crate::genome::parser::string_to_cortical_id("_pain").expect("Valid cortical ID");
277    let pleasure_id =
278        crate::genome::parser::string_to_cortical_id("_pleasure").expect("Valid cortical ID");
279    let fear_id = crate::genome::parser::string_to_cortical_id("_fear").expect("Valid cortical ID");
280    let hope_id = crate::genome::parser::string_to_cortical_id("_hope").expect("Valid cortical ID");
281
282    genome.cortical_areas.insert(death_id, create_death_area());
283    genome.cortical_areas.insert(power_id, create_power_area());
284    genome
285        .cortical_areas
286        .insert(fatigue_id, create_fatigue_area());
287    genome.cortical_areas.insert(pain_id, create_pain_area());
288    genome
289        .cortical_areas
290        .insert(pleasure_id, create_pleasure_area());
291    genome.cortical_areas.insert(fear_id, create_fear_area());
292    genome.cortical_areas.insert(hope_id, create_hope_area());
293
294    genome
295}
296
297/// Create a genome with core morphologies
298pub fn create_genome_with_core_morphologies(
299    genome_id: String,
300    genome_title: String,
301) -> RuntimeGenome {
302    let mut genome = create_minimal_genome(genome_id, genome_title);
303
304    // Add core morphologies
305    add_core_morphologies(&mut genome.morphologies);
306
307    genome
308}
309
310/// CRITICAL: Ensure a genome has all required core components
311///
312/// This function checks if a genome has:
313/// 1. Core cortical areas (_death, _power)
314/// 2. Core morphologies (block_to_block, projector, etc.)
315///
316/// If any are missing, they are automatically added. This ensures every genome
317/// can function properly regardless of its source.
318///
319/// # Arguments
320/// * `genome` - The genome to validate and fix
321///
322/// # Returns
323/// A tuple of (areas_added, morphologies_added) indicating what was added
324pub fn ensure_core_components(genome: &mut RuntimeGenome) -> (usize, usize) {
325    let mut areas_added = 0;
326    let mut morphologies_added = 0;
327
328    // Convert core area IDs
329    let death_id =
330        crate::genome::parser::string_to_cortical_id("_death").expect("Valid cortical ID");
331    let power_id =
332        crate::genome::parser::string_to_cortical_id("_power").expect("Valid cortical ID");
333    let fatigue_id =
334        crate::genome::parser::string_to_cortical_id("_fatigue").expect("Valid cortical ID");
335    let pain_id = crate::genome::parser::string_to_cortical_id("_pain").expect("Valid cortical ID");
336    let pleasure_id =
337        crate::genome::parser::string_to_cortical_id("_pleasure").expect("Valid cortical ID");
338    let fear_id = crate::genome::parser::string_to_cortical_id("_fear").expect("Valid cortical ID");
339    let hope_id = crate::genome::parser::string_to_cortical_id("_hope").expect("Valid cortical ID");
340
341    // 1. Ensure core cortical areas exist
342    if let std::collections::hash_map::Entry::Vacant(e) = genome.cortical_areas.entry(death_id) {
343        let death_area = create_death_area();
344        e.insert(death_area);
345        areas_added += 1;
346        tracing::info!("Added missing core area: _death (cortical_idx=0)");
347    }
348
349    if let std::collections::hash_map::Entry::Vacant(e) = genome.cortical_areas.entry(power_id) {
350        let power_area = create_power_area();
351        e.insert(power_area);
352        areas_added += 1;
353        tracing::info!("Added missing core area: _power (cortical_idx=1)");
354    }
355
356    if let std::collections::hash_map::Entry::Vacant(e) = genome.cortical_areas.entry(fatigue_id) {
357        let fatigue_area = create_fatigue_area();
358        e.insert(fatigue_area);
359        areas_added += 1;
360        tracing::info!("Added missing core area: _fatigue (cortical_idx=2)");
361    }
362
363    if let std::collections::hash_map::Entry::Vacant(e) = genome.cortical_areas.entry(pain_id) {
364        let pain_area = create_pain_area();
365        e.insert(pain_area);
366        areas_added += 1;
367        tracing::info!("Added missing core area: _pain (cortical_idx=3)");
368    }
369
370    if let std::collections::hash_map::Entry::Vacant(e) = genome.cortical_areas.entry(pleasure_id) {
371        let pleasure_area = create_pleasure_area();
372        e.insert(pleasure_area);
373        areas_added += 1;
374        tracing::info!("Added missing core area: _pleasure (cortical_idx=4)");
375    }
376
377    if let std::collections::hash_map::Entry::Vacant(e) = genome.cortical_areas.entry(fear_id) {
378        let fear_area = create_fear_area();
379        e.insert(fear_area);
380        areas_added += 1;
381        tracing::info!("Added missing core area: _fear (cortical_idx=5)");
382    }
383
384    if let std::collections::hash_map::Entry::Vacant(e) = genome.cortical_areas.entry(hope_id) {
385        let hope_area = create_hope_area();
386        e.insert(hope_area);
387        areas_added += 1;
388        tracing::info!("Added missing core area: _hope (cortical_idx=6)");
389    }
390
391    // 2. Ensure core morphologies exist
392    let required_morphologies = vec![
393        "block_to_block",
394        "projector",
395        "transpose_xy",
396        "transpose_yz",
397        "transpose_xz",
398        "sweeper",
399        "last_to_first",
400        "first_to_last",
401        "bitmask_encoder_x",
402        "bitmask_encoder_y",
403        "bitmask_encoder_z",
404        "bitmask_decoder_x",
405        "bitmask_decoder_y",
406        "bitmask_decoder_z",
407        "episodic_memory",
408        "memory_replay",
409        "associative_memory",
410        "rotator_z",
411        "all_to_0-0-0",
412        "0-0-0_to_all",
413        "tile",
414        "lateral_+x",
415        "lateral_-x",
416        "lateral_+y",
417        "lateral_-y",
418        "lateral_+z",
419        "lateral_-z",
420    ];
421
422    for morph_name in required_morphologies {
423        if !genome.morphologies.contains(morph_name) {
424            morphologies_added += 1;
425        }
426    }
427
428    // Add all missing core morphologies in one call
429    if morphologies_added > 0 {
430        add_core_morphologies(&mut genome.morphologies);
431        tracing::info!("Added {} missing core morphologies", morphologies_added);
432    }
433
434    (areas_added, morphologies_added)
435}
436
437/// Add core morphologies to a registry
438pub fn add_core_morphologies(registry: &mut MorphologyRegistry) {
439    use crate::{Morphology, MorphologyParameters, MorphologyType};
440
441    // block_to_block - Connect neurons in same position
442    registry.add_morphology(
443        "block_to_block".to_string(),
444        Morphology {
445            morphology_type: MorphologyType::Vectors,
446            parameters: MorphologyParameters::Vectors {
447                vectors: vec![[0, 0, 0]],
448            },
449            class: "core".to_string(),
450        },
451    );
452
453    // projector - Function-based morphology
454    registry.add_morphology(
455        "projector".to_string(),
456        Morphology {
457            morphology_type: MorphologyType::Functions,
458            parameters: MorphologyParameters::Functions {},
459            class: "core".to_string(),
460        },
461    );
462
463    // transpose_xy - Projector with x/y axis transposition
464    registry.add_morphology(
465        "transpose_xy".to_string(),
466        Morphology {
467            morphology_type: MorphologyType::Functions,
468            parameters: MorphologyParameters::Functions {},
469            class: "core".to_string(),
470        },
471    );
472
473    // transpose_yz - Projector with y/z axis transposition
474    registry.add_morphology(
475        "transpose_yz".to_string(),
476        Morphology {
477            morphology_type: MorphologyType::Functions,
478            parameters: MorphologyParameters::Functions {},
479            class: "core".to_string(),
480        },
481    );
482
483    // transpose_xz - Projector with x/z axis transposition
484    registry.add_morphology(
485        "transpose_xz".to_string(),
486        Morphology {
487            morphology_type: MorphologyType::Functions,
488            parameters: MorphologyParameters::Functions {},
489            class: "core".to_string(),
490        },
491    );
492
493    // sweeper - Function-based sequential sweep mapping morphology
494    registry.add_morphology(
495        "sweeper".to_string(),
496        Morphology {
497            morphology_type: MorphologyType::Functions,
498            parameters: MorphologyParameters::Functions {},
499            class: "core".to_string(),
500        },
501    );
502
503    // last_to_first - connect highest source voxel to destination origin
504    registry.add_morphology(
505        "last_to_first".to_string(),
506        Morphology {
507            morphology_type: MorphologyType::Functions,
508            parameters: MorphologyParameters::Functions {},
509            class: "core".to_string(),
510        },
511    );
512
513    // first_to_last - connect source origin to highest destination voxel
514    registry.add_morphology(
515        "first_to_last".to_string(),
516        Morphology {
517            morphology_type: MorphologyType::Functions,
518            parameters: MorphologyParameters::Functions {},
519            class: "core".to_string(),
520        },
521    );
522
523    // episodic_memory - Function-based morphology
524    registry.add_morphology(
525        "episodic_memory".to_string(),
526        Morphology {
527            morphology_type: MorphologyType::Functions,
528            parameters: MorphologyParameters::Functions {},
529            class: "core".to_string(),
530        },
531    );
532
533    // memory_replay - Function-based morphology
534    registry.add_morphology(
535        "memory_replay".to_string(),
536        Morphology {
537            morphology_type: MorphologyType::Functions,
538            parameters: MorphologyParameters::Functions {},
539            class: "core".to_string(),
540        },
541    );
542
543    // associative_memory (bi-directional STDP) - Function-based morphology
544    registry.add_morphology(
545        "associative_memory".to_string(),
546        Morphology {
547            morphology_type: MorphologyType::Functions,
548            parameters: MorphologyParameters::Functions {},
549            class: "core".to_string(),
550        },
551    );
552
553    // rotator_z - Function-based morphology for z-layered XY rotations [-90,+90]
554    registry.add_morphology(
555        "rotator_z".to_string(),
556        Morphology {
557            morphology_type: MorphologyType::Functions,
558            parameters: MorphologyParameters::Functions {},
559            class: "core".to_string(),
560        },
561    );
562
563    // bitmask_encoder_x - Bitmask encode along X axis
564    registry.add_morphology(
565        "bitmask_encoder_x".to_string(),
566        Morphology {
567            morphology_type: MorphologyType::Functions,
568            parameters: MorphologyParameters::Functions {},
569            class: "core".to_string(),
570        },
571    );
572
573    // bitmask_encoder_y - Bitmask encode along Y axis
574    registry.add_morphology(
575        "bitmask_encoder_y".to_string(),
576        Morphology {
577            morphology_type: MorphologyType::Functions,
578            parameters: MorphologyParameters::Functions {},
579            class: "core".to_string(),
580        },
581    );
582
583    // bitmask_encoder_z - Bitmask encode along Z axis
584    registry.add_morphology(
585        "bitmask_encoder_z".to_string(),
586        Morphology {
587            morphology_type: MorphologyType::Functions,
588            parameters: MorphologyParameters::Functions {},
589            class: "core".to_string(),
590        },
591    );
592
593    // bitmask_decoder_x - Bitmask decode along X axis
594    registry.add_morphology(
595        "bitmask_decoder_x".to_string(),
596        Morphology {
597            morphology_type: MorphologyType::Functions,
598            parameters: MorphologyParameters::Functions {},
599            class: "core".to_string(),
600        },
601    );
602
603    // bitmask_decoder_y - Bitmask decode along Y axis
604    registry.add_morphology(
605        "bitmask_decoder_y".to_string(),
606        Morphology {
607            morphology_type: MorphologyType::Functions,
608            parameters: MorphologyParameters::Functions {},
609            class: "core".to_string(),
610        },
611    );
612
613    // bitmask_decoder_z - Bitmask decode along Z axis
614    registry.add_morphology(
615        "bitmask_decoder_z".to_string(),
616        Morphology {
617            morphology_type: MorphologyType::Functions,
618            parameters: MorphologyParameters::Functions {},
619            class: "core".to_string(),
620        },
621    );
622
623    // all_to_0-0-0 - Connect all neurons to origin
624    registry.add_morphology(
625        "all_to_0-0-0".to_string(),
626        Morphology {
627            morphology_type: MorphologyType::Patterns,
628            parameters: MorphologyParameters::Patterns {
629                patterns: vec![[
630                    vec![
631                        crate::PatternElement::Wildcard,
632                        crate::PatternElement::Wildcard,
633                        crate::PatternElement::Wildcard,
634                    ],
635                    vec![
636                        crate::PatternElement::Value(0),
637                        crate::PatternElement::Value(0),
638                        crate::PatternElement::Value(0),
639                    ],
640                ]],
641            },
642            class: "core".to_string(),
643        },
644    );
645
646    // 0-0-0_to_all - Connect origin to all neurons
647    registry.add_morphology(
648        "0-0-0_to_all".to_string(),
649        Morphology {
650            morphology_type: MorphologyType::Patterns,
651            parameters: MorphologyParameters::Patterns {
652                patterns: vec![[
653                    vec![
654                        crate::PatternElement::Value(0),
655                        crate::PatternElement::Value(0),
656                        crate::PatternElement::Value(0),
657                    ],
658                    vec![
659                        crate::PatternElement::Wildcard,
660                        crate::PatternElement::Wildcard,
661                        crate::PatternElement::Wildcard,
662                    ],
663                ]],
664            },
665            class: "core".to_string(),
666        },
667    );
668
669    // tile - Composite tiling morphology (mapper + subregion parameters)
670    registry.add_morphology(
671        "tile".to_string(),
672        Morphology {
673            morphology_type: MorphologyType::Composite,
674            parameters: MorphologyParameters::Composite {
675                src_seed: [16, 16, 1],
676                src_pattern: vec![[1, 0], [1, 0], [1, 0]],
677                mapper_morphology: "projector".to_string(),
678            },
679            class: "core".to_string(),
680        },
681    );
682
683    // lateral_+x - Connect along +X axis
684    registry.add_morphology(
685        "lateral_+x".to_string(),
686        Morphology {
687            morphology_type: MorphologyType::Vectors,
688            parameters: MorphologyParameters::Vectors {
689                vectors: vec![[1, 0, 0]],
690            },
691            class: "core".to_string(),
692        },
693    );
694
695    // lateral_-x - Connect along -X axis
696    registry.add_morphology(
697        "lateral_-x".to_string(),
698        Morphology {
699            morphology_type: MorphologyType::Vectors,
700            parameters: MorphologyParameters::Vectors {
701                vectors: vec![[-1, 0, 0]],
702            },
703            class: "core".to_string(),
704        },
705    );
706
707    // lateral_+y - Connect along +Y axis
708    registry.add_morphology(
709        "lateral_+y".to_string(),
710        Morphology {
711            morphology_type: MorphologyType::Vectors,
712            parameters: MorphologyParameters::Vectors {
713                vectors: vec![[0, 1, 0]],
714            },
715            class: "core".to_string(),
716        },
717    );
718
719    // lateral_-y - Connect along -Y axis
720    registry.add_morphology(
721        "lateral_-y".to_string(),
722        Morphology {
723            morphology_type: MorphologyType::Vectors,
724            parameters: MorphologyParameters::Vectors {
725                vectors: vec![[0, -1, 0]],
726            },
727            class: "core".to_string(),
728        },
729    );
730
731    // lateral_+z - Connect along +Z axis
732    registry.add_morphology(
733        "lateral_+z".to_string(),
734        Morphology {
735            morphology_type: MorphologyType::Vectors,
736            parameters: MorphologyParameters::Vectors {
737                vectors: vec![[0, 0, 1]],
738            },
739            class: "core".to_string(),
740        },
741    );
742
743    // lateral_-z - Connect along -Z axis
744    registry.add_morphology(
745        "lateral_-z".to_string(),
746        Morphology {
747            morphology_type: MorphologyType::Vectors,
748            parameters: MorphologyParameters::Vectors {
749                vectors: vec![[0, 0, -1]],
750            },
751            class: "core".to_string(),
752        },
753    );
754}
755
756/// Load essential genome from embedded JSON
757///
758/// Automatically ensures core components (_death, _power, core morphologies) are present
759pub fn load_essential_genome() -> Result<RuntimeGenome, crate::types::EvoError> {
760    use crate::genome::loader::load_genome_from_json;
761    let mut genome = load_genome_from_json(ESSENTIAL_GENOME_JSON)?;
762    let (areas_added, morphs_added) = ensure_core_components(&mut genome);
763    if areas_added > 0 || morphs_added > 0 {
764        tracing::info!(
765            "Essential genome: added {} core areas, {} core morphologies",
766            areas_added,
767            morphs_added
768        );
769    }
770    Ok(genome)
771}
772
773/// Load barebones genome from embedded JSON
774///
775/// Automatically ensures core components (_death, _power, core morphologies) are present
776pub fn load_barebones_genome() -> Result<RuntimeGenome, crate::types::EvoError> {
777    use crate::genome::loader::load_genome_from_json;
778    let mut genome = load_genome_from_json(BAREBONES_GENOME_JSON)?;
779    let (areas_added, morphs_added) = ensure_core_components(&mut genome);
780    if areas_added > 0 || morphs_added > 0 {
781        tracing::info!(
782            "Barebones genome: added {} core areas, {} core morphologies",
783            areas_added,
784            morphs_added
785        );
786    }
787    Ok(genome)
788}
789
790/// Load test genome from embedded JSON
791///
792/// Automatically ensures core components (_death, _power, core morphologies) are present
793pub fn load_test_genome() -> Result<RuntimeGenome, crate::types::EvoError> {
794    use crate::genome::loader::load_genome_from_json;
795    let mut genome = load_genome_from_json(TEST_GENOME_JSON)?;
796    let (areas_added, morphs_added) = ensure_core_components(&mut genome);
797    if areas_added > 0 || morphs_added > 0 {
798        tracing::info!(
799            "Test genome: added {} core areas, {} core morphologies",
800            areas_added,
801            morphs_added
802        );
803    }
804    Ok(genome)
805}
806
807/// Load vision genome from embedded JSON
808///
809/// Automatically ensures core components (_death, _power, core morphologies) are present
810pub fn load_vision_genome() -> Result<RuntimeGenome, crate::types::EvoError> {
811    use crate::genome::loader::load_genome_from_json;
812    let mut genome = load_genome_from_json(VISION_GENOME_JSON)?;
813    let (areas_added, morphs_added) = ensure_core_components(&mut genome);
814    if areas_added > 0 || morphs_added > 0 {
815        tracing::info!(
816            "Vision genome: added {} core areas, {} core morphologies",
817            areas_added,
818            morphs_added
819        );
820    }
821    Ok(genome)
822}
823
824#[cfg(test)]
825mod tests {
826    use super::*;
827
828    #[test]
829    fn test_create_minimal_genome() {
830        let genome = create_minimal_genome("test_genome".to_string(), "Test Genome".to_string());
831
832        assert_eq!(genome.metadata.genome_id, "test_genome");
833        assert_eq!(genome.metadata.version, "2.0");
834        assert_eq!(genome.cortical_areas.len(), 0);
835        assert_eq!(genome.morphologies.count(), 0);
836    }
837
838    #[test]
839    fn test_create_genome_with_core_areas() {
840        let genome =
841            create_genome_with_core_areas("test_genome".to_string(), "Test Genome".to_string());
842
843        assert_eq!(genome.metadata.genome_id, "test_genome");
844        assert_eq!(genome.cortical_areas.len(), 7);
845
846        let death_id = crate::genome::parser::string_to_cortical_id("_death").expect("Valid ID");
847        let power_id = crate::genome::parser::string_to_cortical_id("_power").expect("Valid ID");
848        let fatigue_id =
849            crate::genome::parser::string_to_cortical_id("_fatigue").expect("Valid ID");
850        let pain_id = crate::genome::parser::string_to_cortical_id("_pain").expect("Valid ID");
851        let pleasure_id =
852            crate::genome::parser::string_to_cortical_id("_pleasure").expect("Valid ID");
853        let fear_id = crate::genome::parser::string_to_cortical_id("_fear").expect("Valid ID");
854        let hope_id = crate::genome::parser::string_to_cortical_id("_hope").expect("Valid ID");
855        assert!(genome.cortical_areas.contains_key(&death_id));
856        assert!(genome.cortical_areas.contains_key(&power_id));
857        assert!(genome.cortical_areas.contains_key(&fatigue_id));
858        assert!(genome.cortical_areas.contains_key(&pain_id));
859        assert!(genome.cortical_areas.contains_key(&pleasure_id));
860        assert!(genome.cortical_areas.contains_key(&fear_id));
861        assert!(genome.cortical_areas.contains_key(&hope_id));
862
863        // Verify _power has correct properties
864        let power = genome.cortical_areas.get(&power_id).unwrap();
865        assert_eq!(power.cortical_id.as_base_64(), power_id.as_base_64());
866        assert_eq!(power.cortical_idx, 1);
867        assert_eq!(power.dimensions.width, 1);
868        assert_eq!(power.dimensions.height, 1);
869        assert_eq!(power.dimensions.depth, 1);
870    }
871
872    #[test]
873    fn test_create_genome_with_core_morphologies() {
874        let genome = create_genome_with_core_morphologies(
875            "test_genome".to_string(),
876            "Test Genome".to_string(),
877        );
878
879        assert_eq!(genome.metadata.genome_id, "test_genome");
880        assert!(genome.morphologies.count() > 0);
881        assert!(genome.morphologies.contains("block_to_block"));
882        assert!(genome.morphologies.contains("projector"));
883        assert!(genome.morphologies.contains("transpose_xy"));
884        assert!(genome.morphologies.contains("transpose_yz"));
885        assert!(genome.morphologies.contains("transpose_xz"));
886        assert!(genome.morphologies.contains("first_to_last"));
887        assert!(genome.morphologies.contains("lateral_+x"));
888    }
889
890    #[test]
891    fn test_add_core_morphologies() {
892        let mut registry = MorphologyRegistry::new();
893        add_core_morphologies(&mut registry);
894
895        // Should have at least 11 core morphologies
896        assert!(registry.count() >= 11);
897        assert!(registry.contains("block_to_block"));
898        assert!(registry.contains("projector"));
899        assert!(registry.contains("transpose_xy"));
900        assert!(registry.contains("transpose_yz"));
901        assert!(registry.contains("transpose_xz"));
902        assert!(registry.contains("first_to_last"));
903        assert!(registry.contains("all_to_0-0-0"));
904        assert!(registry.contains("lateral_+x"));
905        assert!(registry.contains("lateral_-z"));
906    }
907
908    #[test]
909    fn test_embedded_genomes_exist() {
910        // Test that embedded genome strings are not empty
911        // These are compile-time constants, so they're always non-empty
912        // The assertions verify the constants are defined correctly
913        #[allow(clippy::const_is_empty)]
914        {
915            assert!(!ESSENTIAL_GENOME_JSON.is_empty());
916            assert!(!BAREBONES_GENOME_JSON.is_empty());
917            assert!(!TEST_GENOME_JSON.is_empty());
918            assert!(!VISION_GENOME_JSON.is_empty());
919        }
920    }
921
922    #[test]
923    fn test_load_essential_genome() {
924        let genome = load_essential_genome().expect("Failed to load essential genome");
925        assert!(!genome.cortical_areas.is_empty());
926        // Essential genome should have _power
927        let power_id = crate::genome::parser::string_to_cortical_id("_power").expect("Valid ID");
928        assert!(genome.cortical_areas.contains_key(&power_id));
929    }
930
931    #[test]
932    fn test_ensure_core_components_adds_missing_areas() {
933        // Create a minimal genome without core areas
934        let mut genome = create_minimal_genome("test".to_string(), "Test".to_string());
935
936        assert_eq!(genome.cortical_areas.len(), 0);
937
938        // Ensure core components
939        let (areas_added, _) = ensure_core_components(&mut genome);
940
941        // Should have added _death, _power, _fatigue, _pain, _pleasure, _fear, _hope
942        assert_eq!(areas_added, 7);
943
944        let death_id = crate::genome::parser::string_to_cortical_id("_death").expect("Valid ID");
945        let power_id = crate::genome::parser::string_to_cortical_id("_power").expect("Valid ID");
946        let fatigue_id =
947            crate::genome::parser::string_to_cortical_id("_fatigue").expect("Valid ID");
948        let pain_id = crate::genome::parser::string_to_cortical_id("_pain").expect("Valid ID");
949        let pleasure_id =
950            crate::genome::parser::string_to_cortical_id("_pleasure").expect("Valid ID");
951        let fear_id = crate::genome::parser::string_to_cortical_id("_fear").expect("Valid ID");
952        let hope_id = crate::genome::parser::string_to_cortical_id("_hope").expect("Valid ID");
953        assert!(genome.cortical_areas.contains_key(&death_id));
954        assert!(genome.cortical_areas.contains_key(&power_id));
955        assert!(genome.cortical_areas.contains_key(&fatigue_id));
956        assert!(genome.cortical_areas.contains_key(&pain_id));
957        assert!(genome.cortical_areas.contains_key(&pleasure_id));
958        assert!(genome.cortical_areas.contains_key(&fear_id));
959        assert!(genome.cortical_areas.contains_key(&hope_id));
960
961        // Verify cortical_idx assignments
962        assert_eq!(
963            genome.cortical_areas.get(&death_id).unwrap().cortical_idx,
964            0
965        );
966        assert_eq!(
967            genome.cortical_areas.get(&power_id).unwrap().cortical_idx,
968            1
969        );
970        assert_eq!(
971            genome.cortical_areas.get(&fatigue_id).unwrap().cortical_idx,
972            2
973        );
974        assert_eq!(genome.cortical_areas.get(&pain_id).unwrap().cortical_idx, 3);
975        assert_eq!(
976            genome
977                .cortical_areas
978                .get(&pleasure_id)
979                .unwrap()
980                .cortical_idx,
981            4
982        );
983        assert_eq!(genome.cortical_areas.get(&fear_id).unwrap().cortical_idx, 5);
984        assert_eq!(genome.cortical_areas.get(&hope_id).unwrap().cortical_idx, 6);
985    }
986
987    #[test]
988    fn test_ensure_core_components_adds_missing_morphologies() {
989        // Create a genome with core areas but no morphologies
990        let mut genome = create_genome_with_core_areas("test".to_string(), "Test".to_string());
991
992        assert_eq!(genome.morphologies.count(), 0);
993
994        // Ensure core components
995        let (_, morphs_added) = ensure_core_components(&mut genome);
996
997        // Should have added core morphologies
998        assert!(morphs_added > 0);
999        assert!(genome.morphologies.contains("block_to_block"));
1000        assert!(genome.morphologies.contains("projector"));
1001        assert!(genome.morphologies.contains("transpose_xy"));
1002        assert!(genome.morphologies.contains("transpose_yz"));
1003        assert!(genome.morphologies.contains("transpose_xz"));
1004        assert!(genome.morphologies.contains("first_to_last"));
1005        assert!(genome.morphologies.contains("episodic_memory"));
1006        assert!(genome.morphologies.contains("lateral_+x"));
1007    }
1008
1009    #[test]
1010    fn test_ensure_core_components_idempotent() {
1011        // Create a genome with all core components
1012        let mut genome = create_genome_with_core_areas("test".to_string(), "Test".to_string());
1013        add_core_morphologies(&mut genome.morphologies);
1014
1015        // Run ensure_core_components
1016        let (areas_added, morphs_added) = ensure_core_components(&mut genome);
1017
1018        // Should not add anything (already present)
1019        assert_eq!(areas_added, 0);
1020        assert_eq!(morphs_added, 0);
1021    }
1022}