1use feagi_structures::genomic::classifiers::Classifier;
15use feagi_structures::genomic::cortical_area::CorticalArea;
16use feagi_structures::genomic::cortical_area::CorticalID;
17use feagi_structures::genomic::BrainRegion;
18use serde::{Deserialize, Serialize};
19use std::collections::HashMap;
20
21#[derive(Debug, Clone)]
23pub struct RuntimeGenome {
24 pub metadata: GenomeMetadata,
26
27 pub cortical_areas: HashMap<CorticalID, CorticalArea>,
29
30 pub brain_regions: HashMap<String, BrainRegion>,
32
33 pub classifiers: HashMap<String, Classifier>,
36
37 pub morphologies: MorphologyRegistry,
39
40 pub physiology: PhysiologyConfig,
42
43 pub signatures: GenomeSignatures,
45
46 pub stats: GenomeStats,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct GenomeMetadata {
53 pub genome_id: String,
54 pub genome_title: String,
55 pub genome_description: String,
56 pub version: String,
57 pub timestamp: f64, #[serde(skip_serializing_if = "Option::is_none")]
62 pub brain_regions_root: Option<String>,
63}
64
65#[derive(Debug, Clone, Default)]
67pub struct MorphologyRegistry {
68 morphologies: HashMap<String, Morphology>,
70}
71
72impl MorphologyRegistry {
73 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn add_morphology(&mut self, id: String, morphology: Morphology) {
80 self.morphologies.insert(id, morphology);
81 }
82
83 pub fn get(&self, id: &str) -> Option<&Morphology> {
85 self.morphologies.get(id)
86 }
87
88 pub fn contains(&self, id: &str) -> bool {
90 self.morphologies.contains_key(id)
91 }
92
93 pub fn morphology_ids(&self) -> Vec<String> {
95 self.morphologies.keys().cloned().collect()
96 }
97
98 pub fn remove_morphology(&mut self, id: &str) -> bool {
102 self.morphologies.remove(id).is_some()
103 }
104
105 pub fn count(&self) -> usize {
107 self.morphologies.len()
108 }
109
110 pub fn iter(&self) -> impl Iterator<Item = (&String, &Morphology)> {
112 self.morphologies.iter()
113 }
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct Morphology {
119 pub morphology_type: MorphologyType,
121
122 pub parameters: MorphologyParameters,
124
125 pub class: String,
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
131#[serde(rename_all = "lowercase")]
132pub enum MorphologyType {
133 Vectors,
135
136 Patterns,
138
139 Functions,
141
142 Composite,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148#[serde(untagged)]
149pub enum MorphologyParameters {
150 Vectors { vectors: Vec<[i32; 3]> },
152
153 Patterns {
155 patterns: Vec<[Vec<PatternElement>; 2]>,
156 },
157
158 Functions {},
160
161 Composite {
163 src_seed: [u32; 3],
164 src_pattern: Vec<[i32; 2]>,
165 mapper_morphology: String,
166 },
167}
168
169#[derive(Debug, Clone, PartialEq, Eq)]
171pub enum PatternElement {
172 Value(i32),
174 Wildcard, Skip, Exclude, DirectionPositive, DirectionNegative, DirectionPositiveInclusive, DirectionNegativeInclusive, Offset(i32), Range(i32, i32), AbsoluteRange(i32, i32), }
195
196impl Serialize for PatternElement {
198 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
199 where
200 S: serde::Serializer,
201 {
202 match self {
203 PatternElement::Value(v) => serializer.serialize_i32(*v),
204 PatternElement::Wildcard => serializer.serialize_str("*"),
205 PatternElement::Skip => serializer.serialize_str("?"),
206 PatternElement::Exclude => serializer.serialize_str("!"),
207 PatternElement::DirectionPositive => serializer.serialize_str("?+"),
208 PatternElement::DirectionNegative => serializer.serialize_str("?-"),
209 PatternElement::DirectionPositiveInclusive => serializer.serialize_str("?+="),
210 PatternElement::DirectionNegativeInclusive => serializer.serialize_str("?-="),
211 PatternElement::Offset(off) => {
212 if *off >= 0 {
213 serializer.serialize_str(&format!("?+{}", off))
214 } else {
215 serializer.serialize_str(&format!("?{}", off))
216 }
217 }
218 PatternElement::Range(lo, hi) => {
219 let lo_str = if *lo >= 0 {
220 format!("?+{}", lo)
221 } else {
222 format!("?{}", lo)
223 };
224 let hi_str = if *hi >= 0 {
225 format!("?+{}", hi)
226 } else {
227 format!("?{}", hi)
228 };
229 serializer.serialize_str(&format!("{}:{}", lo_str, hi_str))
230 }
231 PatternElement::AbsoluteRange(lo, hi) => {
232 serializer.serialize_str(&format!("{}..{}", lo, hi))
233 }
234 }
235 }
236}
237
238impl<'de> Deserialize<'de> for PatternElement {
240 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
241 where
242 D: serde::Deserializer<'de>,
243 {
244 let value = serde_json::Value::deserialize(deserializer)?;
245 match value {
246 serde_json::Value::Number(n) => {
247 if let Some(i) = n.as_i64() {
248 Ok(PatternElement::Value(i as i32))
249 } else {
250 Err(serde::de::Error::custom(
251 "Pattern element must be an integer",
252 ))
253 }
254 }
255 serde_json::Value::String(s) => Self::parse_string(&s)
256 .ok_or_else(|| serde::de::Error::custom(format!("Unknown pattern element: {}", s))),
257 _ => Err(serde::de::Error::custom(
258 "Pattern element must be number or string",
259 )),
260 }
261 }
262}
263
264impl PatternElement {
265 pub fn parse_string(s: &str) -> Option<Self> {
267 match s {
268 "*" => Some(PatternElement::Wildcard),
269 "?" => Some(PatternElement::Skip),
270 "!" => Some(PatternElement::Exclude),
271 "?+" => Some(PatternElement::DirectionPositive),
272 "?-" => Some(PatternElement::DirectionNegative),
273 "?+=" => Some(PatternElement::DirectionPositiveInclusive),
274 "?-=" => Some(PatternElement::DirectionNegativeInclusive),
275 _ => {
276 if let Some(range) = Self::try_parse_range(s) {
277 return Some(range);
278 }
279 if let Some(abs_range) = Self::try_parse_absolute_range(s) {
280 return Some(abs_range);
281 }
282 if let Some(offset) = Self::try_parse_offset(s) {
283 return Some(offset);
284 }
285 None
286 }
287 }
288 }
289
290 fn try_parse_range(s: &str) -> Option<Self> {
291 let parts: Vec<&str> = s.split(':').collect();
292 if parts.len() != 2 {
293 return None;
294 }
295 let lo = Self::extract_relative_offset(parts[0])?;
296 let hi = Self::extract_relative_offset(parts[1])?;
297 Some(PatternElement::Range(lo, hi))
298 }
299
300 fn try_parse_absolute_range(s: &str) -> Option<Self> {
301 let idx = s.find("..")?;
302 if s[idx + 2..].contains("..") {
303 return None;
304 }
305 let lo = s[..idx].parse::<i32>().ok()?;
306 let hi = s[idx + 2..].parse::<i32>().ok()?;
307 Some(PatternElement::AbsoluteRange(lo, hi))
308 }
309
310 fn try_parse_offset(s: &str) -> Option<Self> {
311 let offset = Self::extract_relative_offset(s)?;
312 Some(PatternElement::Offset(offset))
313 }
314
315 fn extract_relative_offset(s: &str) -> Option<i32> {
316 if !s.starts_with('?') {
317 return None;
318 }
319 let rest = &s[1..];
320 if rest.is_empty() || rest == "+" || rest == "-" || rest == "+=" || rest == "-=" {
321 return None;
322 }
323 rest.parse::<i32>().ok()
324 }
325}
326
327#[derive(Debug, Clone, Serialize, Deserialize)]
329pub struct PhysiologyConfig {
330 pub simulation_timestep: f64,
332
333 pub max_age: u64,
335
336 pub evolution_burst_count: u64,
338
339 pub ipu_idle_threshold: u64,
341
342 pub plasticity_queue_depth: usize,
344
345 pub lifespan_mgmt_interval: u64,
347
348 #[serde(default = "default_quantization_precision")]
351 pub quantization_precision: String,
352}
353
354pub fn default_quantization_precision() -> String {
355 "int8".to_string() }
357
358impl Default for PhysiologyConfig {
359 fn default() -> Self {
360 Self {
361 simulation_timestep: 0.025,
362 max_age: 10_000_000,
363 evolution_burst_count: 50,
364 ipu_idle_threshold: 1000,
365 plasticity_queue_depth: 3,
366 lifespan_mgmt_interval: 10,
367 quantization_precision: default_quantization_precision(),
368 }
369 }
370}
371
372#[derive(Debug, Clone, Serialize, Deserialize)]
374pub struct GenomeSignatures {
375 pub genome: String,
377
378 pub blueprint: String,
380
381 pub physiology: String,
383
384 #[serde(skip_serializing_if = "Option::is_none")]
386 pub morphologies: Option<String>,
387}
388
389#[derive(Debug, Clone, Serialize, Deserialize, Default)]
391pub struct GenomeStats {
392 pub innate_cortical_area_count: usize,
394
395 pub innate_neuron_count: usize,
397
398 pub innate_synapse_count: usize,
400}
401
402#[cfg(test)]
403mod tests {
404 use super::*;
405
406 #[test]
407 fn test_morphology_registry_creation() {
408 let registry = MorphologyRegistry::new();
409 assert_eq!(registry.count(), 0);
410 }
411
412 #[test]
413 fn test_morphology_registry_add_and_get() {
414 let mut registry = MorphologyRegistry::new();
415
416 let morphology = Morphology {
417 morphology_type: MorphologyType::Vectors,
418 parameters: MorphologyParameters::Vectors {
419 vectors: vec![[1, 0, 0], [0, 1, 0]],
420 },
421 class: "test".to_string(),
422 };
423
424 registry.add_morphology("test_morph".to_string(), morphology);
425
426 assert_eq!(registry.count(), 1);
427 assert!(registry.contains("test_morph"));
428 assert!(registry.get("test_morph").is_some());
429 }
430
431 #[test]
432 fn test_physiology_config_default() {
433 let config = PhysiologyConfig::default();
434 assert_eq!(config.simulation_timestep, 0.025);
435 assert_eq!(config.max_age, 10_000_000);
436 }
437}