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