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