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