feagi_evolutionary/
runtime.rs

1// Copyright 2025 Neuraville Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4/*!
5Runtime genome representation for FEAGI.
6
7This module defines the in-memory Rust objects that represent a loaded genome.
8These objects are created by the genome parser and consumed by neuroembryogenesis.
9
10Copyright 2025 Neuraville Inc.
11Licensed under the Apache License, Version 2.0
12*/
13
14use feagi_structures::genomic::cortical_area::CorticalArea;
15use feagi_structures::genomic::cortical_area::CorticalID;
16use feagi_structures::genomic::BrainRegion;
17use serde::{Deserialize, Serialize};
18use std::collections::HashMap;
19
20/// Complete runtime genome representation
21#[derive(Debug, Clone)]
22pub struct RuntimeGenome {
23    /// Genome metadata
24    pub metadata: GenomeMetadata,
25
26    /// Cortical areas (by cortical_id as CorticalID)
27    pub cortical_areas: HashMap<CorticalID, CorticalArea>,
28
29    /// Brain regions (by region_id)
30    pub brain_regions: HashMap<String, BrainRegion>,
31
32    /// Morphology registry
33    pub morphologies: MorphologyRegistry,
34
35    /// Physiology configuration
36    pub physiology: PhysiologyConfig,
37
38    /// Genome signatures
39    pub signatures: GenomeSignatures,
40
41    /// Statistics
42    pub stats: GenomeStats,
43}
44
45/// Genome metadata
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct GenomeMetadata {
48    pub genome_id: String,
49    pub genome_title: String,
50    pub genome_description: String,
51    pub version: String,
52    pub timestamp: f64, // Unix timestamp
53
54    /// Root brain region ID (UUID string) - explicit identification for O(1) lookup
55    /// This eliminates the need to search through all regions to find which has no parent
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub brain_regions_root: Option<String>,
58}
59
60/// Neuron morphology registry
61#[derive(Debug, Clone, Default)]
62pub struct MorphologyRegistry {
63    /// All morphologies by morphology_id
64    morphologies: HashMap<String, Morphology>,
65}
66
67impl MorphologyRegistry {
68    /// Create empty registry
69    pub fn new() -> Self {
70        Self::default()
71    }
72
73    /// Add a morphology
74    pub fn add_morphology(&mut self, id: String, morphology: Morphology) {
75        self.morphologies.insert(id, morphology);
76    }
77
78    /// Get a morphology by ID
79    pub fn get(&self, id: &str) -> Option<&Morphology> {
80        self.morphologies.get(id)
81    }
82
83    /// Check if morphology exists
84    pub fn contains(&self, id: &str) -> bool {
85        self.morphologies.contains_key(id)
86    }
87
88    /// Get all morphology IDs
89    pub fn morphology_ids(&self) -> Vec<String> {
90        self.morphologies.keys().cloned().collect()
91    }
92
93    /// Get count of morphologies
94    pub fn count(&self) -> usize {
95        self.morphologies.len()
96    }
97
98    /// Iterate over all morphologies
99    pub fn iter(&self) -> impl Iterator<Item = (&String, &Morphology)> {
100        self.morphologies.iter()
101    }
102}
103
104/// Neuron morphology definition
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct Morphology {
107    /// Morphology type: "vectors", "patterns", "functions", or "composite"
108    pub morphology_type: MorphologyType,
109
110    /// Morphology parameters
111    pub parameters: MorphologyParameters,
112
113    /// Morphology class: "core", "custom", etc.
114    pub class: String,
115}
116
117/// Morphology type enum
118#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
119#[serde(rename_all = "lowercase")]
120pub enum MorphologyType {
121    /// Vector-based morphology (3D offset vectors)
122    Vectors,
123
124    /// Pattern-based morphology (source → destination patterns)
125    Patterns,
126
127    /// Function-based morphology (built-in algorithms)
128    Functions,
129
130    /// Composite morphology (combines multiple morphologies)
131    Composite,
132}
133
134/// Morphology parameters (type-specific)
135#[derive(Debug, Clone, Serialize, Deserialize)]
136#[serde(untagged)]
137pub enum MorphologyParameters {
138    /// Vector parameters: list of [x, y, z] offsets
139    Vectors { vectors: Vec<[i32; 3]> },
140
141    /// Pattern parameters: list of [source_pattern, dest_pattern] pairs
142    Patterns {
143        patterns: Vec<[Vec<PatternElement>; 2]>,
144    },
145
146    /// Function parameters: empty for built-in functions
147    Functions {},
148
149    /// Composite parameters: combines seed + pattern + mapper
150    Composite {
151        src_seed: [u32; 3],
152        src_pattern: Vec<[i32; 2]>,
153        mapper_morphology: String,
154    },
155}
156
157/// Pattern element: exact value, wildcard (*), skip (?), or exclude (!)
158#[derive(Debug, Clone, PartialEq, Eq)]
159pub enum PatternElement {
160    /// Exact coordinate value
161    Value(i32),
162    /// Wildcard - matches any value
163    Wildcard, // "*"
164    /// Skip - don't check this coordinate
165    Skip, // "?"
166    /// Exclude - exclude this coordinate
167    Exclude, // "!"
168}
169
170// Custom serialization to convert PatternElement back to JSON properly
171impl Serialize for PatternElement {
172    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
173    where
174        S: serde::Serializer,
175    {
176        match self {
177            PatternElement::Value(v) => serializer.serialize_i32(*v),
178            PatternElement::Wildcard => serializer.serialize_str("*"),
179            PatternElement::Skip => serializer.serialize_str("?"),
180            PatternElement::Exclude => serializer.serialize_str("!"),
181        }
182    }
183}
184
185// Custom deserialization to parse JSON into PatternElement
186impl<'de> Deserialize<'de> for PatternElement {
187    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
188    where
189        D: serde::Deserializer<'de>,
190    {
191        let value = serde_json::Value::deserialize(deserializer)?;
192        match value {
193            serde_json::Value::Number(n) => {
194                if let Some(i) = n.as_i64() {
195                    Ok(PatternElement::Value(i as i32))
196                } else {
197                    Err(serde::de::Error::custom(
198                        "Pattern element must be an integer",
199                    ))
200                }
201            }
202            serde_json::Value::String(s) => match s.as_str() {
203                "*" => Ok(PatternElement::Wildcard),
204                "?" => Ok(PatternElement::Skip),
205                "!" => Ok(PatternElement::Exclude),
206                _ => Err(serde::de::Error::custom(format!(
207                    "Unknown pattern element: {}",
208                    s
209                ))),
210            },
211            _ => Err(serde::de::Error::custom(
212                "Pattern element must be number or string",
213            )),
214        }
215    }
216}
217
218/// Physiology configuration (runtime parameters)
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct PhysiologyConfig {
221    /// Simulation timestep in seconds (formerly burst_delay)
222    pub simulation_timestep: f64,
223
224    /// Maximum neuron age
225    pub max_age: u64,
226
227    /// Evolution burst count
228    pub evolution_burst_count: u64,
229
230    /// IPU idle threshold
231    pub ipu_idle_threshold: u64,
232
233    /// Plasticity queue depth
234    pub plasticity_queue_depth: usize,
235
236    /// Lifespan management interval
237    pub lifespan_mgmt_interval: u64,
238
239    /// Quantization precision for numeric values
240    /// Options: "fp32" (default), "fp16", "int8"
241    #[serde(default = "default_quantization_precision")]
242    pub quantization_precision: String,
243}
244
245pub fn default_quantization_precision() -> String {
246    "int8".to_string() // Default to INT8 for memory efficiency
247}
248
249impl Default for PhysiologyConfig {
250    fn default() -> Self {
251        Self {
252            simulation_timestep: 0.025,
253            max_age: 10_000_000,
254            evolution_burst_count: 50,
255            ipu_idle_threshold: 1000,
256            plasticity_queue_depth: 3,
257            lifespan_mgmt_interval: 10,
258            quantization_precision: default_quantization_precision(),
259        }
260    }
261}
262
263/// Genome signatures for comparison
264#[derive(Debug, Clone, Serialize, Deserialize)]
265pub struct GenomeSignatures {
266    /// Full genome signature
267    pub genome: String,
268
269    /// Blueprint signature
270    pub blueprint: String,
271
272    /// Physiology signature
273    pub physiology: String,
274
275    /// Morphologies signature (optional, for future extension)
276    #[serde(skip_serializing_if = "Option::is_none")]
277    pub morphologies: Option<String>,
278}
279
280/// Genome statistics
281#[derive(Debug, Clone, Serialize, Deserialize, Default)]
282pub struct GenomeStats {
283    /// Innate cortical area count
284    pub innate_cortical_area_count: usize,
285
286    /// Innate neuron count
287    pub innate_neuron_count: usize,
288
289    /// Innate synapse count
290    pub innate_synapse_count: usize,
291}
292
293#[cfg(test)]
294mod tests {
295    use super::*;
296
297    #[test]
298    fn test_morphology_registry_creation() {
299        let registry = MorphologyRegistry::new();
300        assert_eq!(registry.count(), 0);
301    }
302
303    #[test]
304    fn test_morphology_registry_add_and_get() {
305        let mut registry = MorphologyRegistry::new();
306
307        let morphology = Morphology {
308            morphology_type: MorphologyType::Vectors,
309            parameters: MorphologyParameters::Vectors {
310                vectors: vec![[1, 0, 0], [0, 1, 0]],
311            },
312            class: "test".to_string(),
313        };
314
315        registry.add_morphology("test_morph".to_string(), morphology);
316
317        assert_eq!(registry.count(), 1);
318        assert!(registry.contains("test_morph"));
319        assert!(registry.get("test_morph").is_some());
320    }
321
322    #[test]
323    fn test_physiology_config_default() {
324        let config = PhysiologyConfig::default();
325        assert_eq!(config.simulation_timestep, 0.025);
326        assert_eq!(config.max_age, 10_000_000);
327    }
328}