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