use crate::{EvoResult, RuntimeGenome};
use serde_json::{json, Value};
use std::collections::HashMap;
pub fn convert_hierarchical_to_flat(genome: &RuntimeGenome) -> EvoResult<Value> {
let mut flat_blueprint = serde_json::Map::new();
let mut visualization_overrides = serde_json::Map::new();
for (cortical_id, area) in &genome.cortical_areas {
let cortical_id_base64 = cortical_id.as_base_64();
convert_area_to_flat(&cortical_id_base64, area, &mut flat_blueprint)?;
if let Some(granularity_value) = area.properties.get("visualization_voxel_granularity") {
if let Some(arr) = granularity_value.as_array() {
if arr.len() == 3 {
if let (Some(x), Some(y), Some(z)) =
(arr[0].as_u64(), arr[1].as_u64(), arr[2].as_u64())
{
if x != 1 || y != 1 || z != 1 {
visualization_overrides
.insert(cortical_id_base64.clone(), json!([x, y, z]));
}
}
}
}
}
}
let mut flat_genome = serde_json::Map::new();
flat_genome.insert("genome_id".to_string(), json!(genome.metadata.genome_id));
flat_genome.insert(
"genome_title".to_string(),
json!(genome.metadata.genome_title),
);
flat_genome.insert(
"genome_description".to_string(),
json!(genome.metadata.genome_description),
);
flat_genome.insert("version".to_string(), json!("3.0"));
flat_genome.insert(
"genome_schema_version".to_string(),
json!(crate::genome::schema::CURRENT_SCHEMA_VERSION.as_u32()),
);
flat_genome.insert("timestamp".to_string(), json!(genome.metadata.timestamp));
if let Some(root_id) = &genome.metadata.brain_regions_root {
flat_genome.insert("brain_regions_root".to_string(), json!(root_id));
}
if !visualization_overrides.is_empty() {
flat_genome.insert(
"visualization_voxel_granularity_overrides".to_string(),
Value::Object(visualization_overrides),
);
}
flat_genome.insert("blueprint".to_string(), Value::Object(flat_blueprint));
let mut morphologies_map = serde_json::Map::new();
for (morphology_id, morphology) in genome.morphologies.iter() {
let mut morph_data = serde_json::Map::new();
let type_str = match morphology.morphology_type {
crate::MorphologyType::Vectors => "vectors",
crate::MorphologyType::Patterns => "patterns",
crate::MorphologyType::Functions => "functions",
crate::MorphologyType::Composite => "composite",
};
morph_data.insert("type".to_string(), json!(type_str));
let params = morphology_parameters_to_json(&morphology.parameters);
morph_data.insert("parameters".to_string(), params);
morph_data.insert("class".to_string(), json!(morphology.class));
morphologies_map.insert(morphology_id.clone(), Value::Object(morph_data));
}
flat_genome.insert(
"neuron_morphologies".to_string(),
Value::Object(morphologies_map),
);
let physiology = json!({
"simulation_timestep": genome.physiology.simulation_timestep,
"max_age": genome.physiology.max_age,
"evolution_burst_count": genome.physiology.evolution_burst_count,
"ipu_idle_threshold": genome.physiology.ipu_idle_threshold,
"plasticity_queue_depth": genome.physiology.plasticity_queue_depth,
"lifespan_mgmt_interval": genome.physiology.lifespan_mgmt_interval,
"quantization_precision": "fp32", });
flat_genome.insert("physiology".to_string(), physiology);
let stats = json!({
"innate_cortical_area_count": genome.stats.innate_cortical_area_count,
"innate_neuron_count": genome.stats.innate_neuron_count,
"innate_synapse_count": genome.stats.innate_synapse_count,
});
flat_genome.insert("stats".to_string(), stats);
let signatures = json!({
"genome": genome.signatures.genome,
"blueprint": genome.signatures.blueprint,
"physiology": genome.signatures.physiology,
});
flat_genome.insert("signatures".to_string(), signatures);
flat_genome.insert("hosts".to_string(), json!({}));
let mut brain_regions_map = serde_json::Map::new();
for (region_id, region) in &genome.brain_regions {
let mut region_data = serde_json::Map::new();
let region_json =
serde_json::to_value(region).map_err(|e| crate::EvoError::JsonError(e.to_string()))?;
if let Value::Object(mut props) = region_json {
let keys_to_convert = vec!["areas", "inputs", "outputs", "cortical_areas"];
for key in keys_to_convert {
if let Some(Value::Array(ids)) = props.get(key) {
let converted_ids: Vec<String> = ids
.iter()
.filter_map(|v| v.as_str())
.map(|id_str| {
crate::genome::parser::string_to_cortical_id(id_str)
.map(|cid| cid.as_base_64())
.unwrap_or_else(|_| id_str.to_string())
})
.collect();
props.insert(key.to_string(), json!(converted_ids));
}
}
region_data = props;
}
brain_regions_map.insert(region_id.clone(), Value::Object(region_data));
}
flat_genome.insert(
"brain_regions".to_string(),
Value::Object(brain_regions_map),
);
Ok(Value::Object(flat_genome))
}
fn convert_area_to_flat(
cortical_id_base64: &str,
area: &feagi_structures::genomic::cortical_area::CorticalArea,
flat_blueprint: &mut serde_json::Map<String, Value>,
) -> EvoResult<()> {
let prefix = format!("_____10c-{}", cortical_id_base64);
flat_blueprint.insert(
format!("{}-cx-___bbx-i", prefix),
json!(area.dimensions.width),
);
flat_blueprint.insert(
format!("{}-cx-___bby-i", prefix),
json!(area.dimensions.height),
);
flat_blueprint.insert(
format!("{}-cx-___bbz-i", prefix),
json!(area.dimensions.depth),
);
flat_blueprint.insert(format!("{}-cx-rcordx-i", prefix), json!(area.position.x));
flat_blueprint.insert(format!("{}-cx-rcordy-i", prefix), json!(area.position.y));
flat_blueprint.insert(format!("{}-cx-rcordz-i", prefix), json!(area.position.z));
if !area.properties.contains_key("cortical_name") {
flat_blueprint.insert(format!("{}-cx-__name-t", prefix), json!(area.name));
}
let mut properties_with_group = area.properties.clone();
if !properties_with_group.contains_key("cortical_group") {
use feagi_structures::genomic::cortical_area::CorticalAreaType;
let cortical_group = match area.cortical_type {
CorticalAreaType::BrainInput(_) => "IPU",
CorticalAreaType::BrainOutput(_) => "OPU",
CorticalAreaType::Memory(_) => "MEMORY",
CorticalAreaType::Core(_) => "CORE",
CorticalAreaType::Custom(_) => "CUSTOM",
};
properties_with_group.insert("cortical_group".to_string(), json!(cortical_group));
}
convert_properties_to_flat(&prefix, &properties_with_group, flat_blueprint)?;
Ok(())
}
fn convert_properties_to_flat(
prefix: &str,
properties: &HashMap<String, Value>,
flat_blueprint: &mut serde_json::Map<String, Value>,
) -> EvoResult<()> {
let property_mapping: HashMap<&str, (&str, &str)> = [
("per_voxel_neuron_cnt", ("_n_cnt-i", "cx")),
("visualization", ("gd_vis-b", "cx")),
("cortical_name", ("__name-t", "cx")),
("synapse_attractivity", ("synatt-f", "cx")),
("postsynaptic_current", ("pstcr_-f", "nx")),
("postsynaptic_current_max", ("pstcrm-f", "nx")),
("firing_threshold", ("fire_t-f", "nx")),
("firing_threshold_increment_x", ("ftincx-f", "nx")),
("firing_threshold_increment_y", ("ftincy-f", "nx")),
("firing_threshold_increment_z", ("ftincz-f", "nx")),
("firing_threshold_limit", ("fthlim-f", "nx")),
("refractory_period", ("refrac-i", "nx")),
("leak_coefficient", ("leak_c-f", "nx")),
("leak_variability", ("leak_v-f", "nx")),
("consecutive_fire_cnt_max", ("c_fr_c-i", "nx")),
("snooze_length", ("snooze-i", "nx")),
("group_id", ("_group-t", "cx")),
("sub_group_id", ("subgrp-t", "cx")),
("degeneration", ("de_gen-f", "cx")),
("psp_uniform_distribution", ("pspuni-b", "cx")),
("mp_charge_accumulation", ("mp_acc-b", "nx")),
("mp_driven_psp", ("mp_psp-b", "nx")),
("is_mem_type", ("memory-b", "cx")),
("longterm_mem_threshold", ("mem__t-i", "cx")),
("lifespan_growth_rate", ("mem_gr-i", "cx")),
("init_lifespan", ("mem_ls-i", "cx")),
("temporal_depth", ("tmpdpt-i", "cx")),
("mp_learning_enabled", ("mplrn-b", "cx")),
("neuron_excitability", ("excite-f", "nx")),
("dev_count", ("devcnt-i", "cx")),
("memory_twin_of", ("twinrf-t", "cx")),
]
.iter()
.cloned()
.collect();
let required_defaults: HashMap<&str, Value> = [
("per_voxel_neuron_cnt", json!(1)),
("visualization", json!(true)),
("synapse_attractivity", json!(100.0)),
("postsynaptic_current", json!(1.0)),
("postsynaptic_current_max", json!(35.0)),
("firing_threshold", json!(0.1)),
("firing_threshold_increment_x", json!(0.0)),
("firing_threshold_increment_y", json!(0.0)),
("firing_threshold_increment_z", json!(0.0)),
("firing_threshold_limit", json!(0.0)),
("refractory_period", json!(0)),
("leak_coefficient", json!(0.0)),
("leak_variability", json!(0.0)),
("consecutive_fire_cnt_max", json!(0)),
("snooze_length", json!(0)),
("group_id", json!("CUSTOM")),
("sub_group_id", json!("")),
("degeneration", json!(0.0)),
("psp_uniform_distribution", json!(false)),
("mp_charge_accumulation", json!(false)),
("mp_driven_psp", json!(false)),
("is_mem_type", json!(false)),
("longterm_mem_threshold", json!(100)),
("lifespan_growth_rate", json!(1)),
("init_lifespan", json!(9)),
("neuron_excitability", json!(100.0)),
]
.iter()
.map(|(k, v)| (*k, v.clone()))
.collect();
for (prop_key, (suffix, scope)) in &property_mapping {
let value = properties
.get(*prop_key)
.cloned()
.or_else(|| required_defaults.get(prop_key).cloned())
.unwrap_or(Value::Null);
let debug_props = [
"mp_driven_psp",
"snooze_length",
"consecutive_fire_cnt_max",
"firing_threshold_increment_x",
"firing_threshold",
"leak_coefficient",
];
if debug_props.contains(prop_key) {
if let Some(prop_value) = properties.get(*prop_key) {
tracing::debug!(
"[GENOME-CONVERT] Found {}={} in properties for area {}, writing to flat format",
prop_key, prop_value, prefix
);
} else {
let default_val = required_defaults.get(*prop_key).unwrap_or(&json!(null));
tracing::debug!(
"[GENOME-CONVERT] {} not in properties for area {}, using default={}",
prop_key,
prefix,
default_val
);
}
}
if !value.is_null() {
flat_blueprint.insert(format!("{}-{}-{}", prefix, scope, suffix), value);
}
}
for (key, value) in properties {
if key == "rate_modulated_leak" {
if !value.is_null() {
flat_blueprint.insert(format!("{}-cx-hmlk-d", prefix), value.clone());
}
} else if key == "cortical_mapping_dst" {
if let Some(dstmap_obj) = value.as_object() {
flat_blueprint.insert(format!("{}-cx-dstmap-d", prefix), json!(dstmap_obj));
}
} else if key == "2d_coordinate" || key == "coordinate_2d" || key == "coordinates_2d" {
continue;
} else if key == "block_boundaries" || key == "relative_coordinate" {
} else if key == "cortical_group" {
flat_blueprint.insert(format!("{}-cx-_group-t", prefix), value.clone());
}
}
let coord2d = properties
.get("coordinate_2d")
.or_else(|| properties.get("coordinates_2d"))
.or_else(|| properties.get("2d_coordinate"));
if let Some(coords) = coord2d.and_then(|v| v.as_array()) {
if coords.len() >= 2 {
flat_blueprint.insert(format!("{}-cx-2dcorx-i", prefix), coords[0].clone());
flat_blueprint.insert(format!("{}-cx-2dcory-i", prefix), coords[1].clone());
}
}
Ok(())
}
fn morphology_parameters_to_json(params: &crate::MorphologyParameters) -> Value {
match params {
crate::MorphologyParameters::Vectors { vectors } => {
json!({
"vectors": vectors
})
}
crate::MorphologyParameters::Patterns { patterns } => {
let patterns_json: Vec<Value> = patterns
.iter()
.map(|pattern| {
json!([
pattern_elements_to_json(&pattern[0]),
pattern_elements_to_json(&pattern[1])
])
})
.collect();
json!({
"patterns": patterns_json
})
}
crate::MorphologyParameters::Functions {} => {
json!({})
}
crate::MorphologyParameters::Composite {
src_seed,
src_pattern,
mapper_morphology,
} => {
json!({
"src_seed": src_seed,
"src_pattern": src_pattern,
"mapper_morphology": mapper_morphology
})
}
}
}
fn pattern_elements_to_json(elements: &[crate::PatternElement]) -> Value {
let json_elements: Vec<Value> = elements
.iter()
.map(|elem| match elem {
crate::PatternElement::Value(v) => json!(v),
crate::PatternElement::Wildcard => json!("*"),
crate::PatternElement::Skip => json!("?"),
crate::PatternElement::Exclude => json!("!"),
crate::PatternElement::DirectionPositive => json!("?+"),
crate::PatternElement::DirectionNegative => json!("?-"),
crate::PatternElement::DirectionPositiveInclusive => json!("?+="),
crate::PatternElement::DirectionNegativeInclusive => json!("?-="),
crate::PatternElement::Offset(off) => {
if *off >= 0 {
json!(format!("?+{}", off))
} else {
json!(format!("?{}", off))
}
}
crate::PatternElement::Range(lo, hi) => {
let lo_str = if *lo >= 0 {
format!("?+{}", lo)
} else {
format!("?{}", lo)
};
let hi_str = if *hi >= 0 {
format!("?+{}", hi)
} else {
format!("?{}", hi)
};
json!(format!("{}:{}", lo_str, hi_str))
}
})
.collect();
json!(json_elements)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{GenomeMetadata, GenomeSignatures, GenomeStats, PhysiologyConfig, RuntimeGenome};
use std::collections::HashMap;
#[test]
fn test_convert_minimal_genome() {
let genome = RuntimeGenome {
metadata: GenomeMetadata {
genome_id: "test_genome".to_string(),
genome_title: "Test Genome".to_string(),
genome_description: "A test genome".to_string(),
version: "2.0".to_string(),
timestamp: 1234567890.0,
brain_regions_root: None,
},
cortical_areas: HashMap::new(),
brain_regions: HashMap::new(),
morphologies: crate::MorphologyRegistry::new(),
physiology: PhysiologyConfig::default(),
signatures: GenomeSignatures {
genome: "0000000000000000".to_string(),
blueprint: "0000000000000000".to_string(),
physiology: "0000000000000000".to_string(),
morphologies: None,
},
stats: GenomeStats::default(),
};
let flat = convert_hierarchical_to_flat(&genome).unwrap();
assert_eq!(flat["genome_id"], "test_genome");
assert_eq!(flat["version"], "3.0");
assert!(flat["blueprint"].is_object());
assert!(flat["neuron_morphologies"].is_object());
assert!(flat["physiology"].is_object());
}
#[test]
fn test_cortical_group_derived_from_type() {
use feagi_structures::genomic::cortical_area::{
io_cortical_area_configuration_flag::FrameChangeHandling, CorticalArea,
CorticalAreaDimensions, CorticalAreaType, CorticalID, IOCorticalAreaConfigurationFlag,
};
use feagi_structures::genomic::descriptors::GenomeCoordinate3D;
let mut genome = RuntimeGenome {
metadata: GenomeMetadata {
genome_id: "test_genome".to_string(),
genome_title: "Test Genome".to_string(),
genome_description: "Test cortical_group derivation".to_string(),
version: "2.0".to_string(),
timestamp: 1234567890.0,
brain_regions_root: None,
},
cortical_areas: HashMap::new(),
brain_regions: HashMap::new(),
morphologies: crate::MorphologyRegistry::new(),
physiology: PhysiologyConfig::default(),
signatures: GenomeSignatures {
genome: "0000000000000000".to_string(),
blueprint: "0000000000000000".to_string(),
physiology: "0000000000000000".to_string(),
morphologies: None,
},
stats: GenomeStats::default(),
};
let opu_id = CorticalID::try_from_base_64("b2ltZwkAAAA=").unwrap();
let opu_area = CorticalArea::new(
opu_id,
0,
"Test OPU".to_string(),
CorticalAreaDimensions::new(10, 10, 1).unwrap(),
GenomeCoordinate3D { x: 0, y: 0, z: 0 },
CorticalAreaType::BrainOutput(IOCorticalAreaConfigurationFlag::CartesianPlane(
FrameChangeHandling::Absolute,
)),
)
.unwrap();
let ipu_id = CorticalID::try_from_base_64("aXN2aQkABAA=").unwrap();
let ipu_area = CorticalArea::new(
ipu_id,
1,
"Test IPU".to_string(),
CorticalAreaDimensions::new(10, 10, 1).unwrap(),
GenomeCoordinate3D { x: 0, y: 0, z: 0 },
CorticalAreaType::BrainInput(IOCorticalAreaConfigurationFlag::CartesianPlane(
FrameChangeHandling::Absolute,
)),
)
.unwrap();
genome.cortical_areas.insert(opu_id, opu_area);
genome.cortical_areas.insert(ipu_id, ipu_area);
let flat = convert_hierarchical_to_flat(&genome).unwrap();
let blueprint = flat["blueprint"].as_object().unwrap();
let opu_group_key = "_____10c-b2ltZwkAAAA=-cx-_group-t";
assert_eq!(
blueprint[opu_group_key], "OPU",
"OPU area should have _group-t set to OPU"
);
let ipu_group_key = "_____10c-aXN2aQkABAA=-cx-_group-t";
assert_eq!(
blueprint[ipu_group_key], "IPU",
"IPU area should have _group-t set to IPU"
);
}
#[test]
fn test_memory_twin_reference_saved_to_flat() {
use feagi_structures::genomic::cortical_area::{
CorticalArea, CorticalAreaDimensions, CorticalAreaType, CorticalID,
IOCorticalAreaConfigurationFlag,
};
use feagi_structures::genomic::descriptors::GenomeCoordinate3D;
let mut genome = RuntimeGenome {
metadata: GenomeMetadata {
genome_id: "test_genome".to_string(),
genome_title: "Test Genome".to_string(),
genome_description: "Test twin reference".to_string(),
version: "2.0".to_string(),
timestamp: 1234567890.0,
brain_regions_root: None,
},
cortical_areas: HashMap::new(),
brain_regions: HashMap::new(),
morphologies: crate::MorphologyRegistry::new(),
physiology: PhysiologyConfig::default(),
signatures: GenomeSignatures {
genome: "0000000000000000".to_string(),
blueprint: "0000000000000000".to_string(),
physiology: "0000000000000000".to_string(),
morphologies: None,
},
stats: GenomeStats::default(),
};
let twin_id = CorticalID::try_from_base_64("Y7Gx8Xy7Fpo=").unwrap();
let mut twin_area = CorticalArea::new(
twin_id,
0,
"a2_twin".to_string(),
CorticalAreaDimensions::new(1, 1, 1).unwrap(),
GenomeCoordinate3D::new(0, 0, 0),
CorticalAreaType::BrainInput(IOCorticalAreaConfigurationFlag::Boolean),
)
.unwrap();
twin_area.properties.insert(
"memory_twin_of".to_string(),
serde_json::json!("Y2EyX19fX/U="),
);
genome.cortical_areas.insert(twin_id, twin_area);
let flat = convert_hierarchical_to_flat(&genome).unwrap();
let key = "_____10c-Y7Gx8Xy7Fpo=-cx-twinrf-t";
assert_eq!(flat["blueprint"][key], "Y2EyX19fX/U=");
}
#[test]
fn test_coordinate_2d_from_api_exports_to_flat_2dcor() {
use feagi_structures::genomic::cortical_area::{
CorticalArea, CorticalAreaDimensions, CorticalAreaType, CorticalID,
IOCorticalAreaConfigurationFlag,
};
use feagi_structures::genomic::descriptors::GenomeCoordinate3D;
let mut genome = RuntimeGenome {
metadata: GenomeMetadata {
genome_id: "test_genome".to_string(),
genome_title: "Test Genome".to_string(),
genome_description: "2d export".to_string(),
version: "2.0".to_string(),
timestamp: 1234567890.0,
brain_regions_root: None,
},
cortical_areas: HashMap::new(),
brain_regions: HashMap::new(),
morphologies: crate::MorphologyRegistry::new(),
physiology: PhysiologyConfig::default(),
signatures: GenomeSignatures {
genome: "0000000000000000".to_string(),
blueprint: "0000000000000000".to_string(),
physiology: "0000000000000000".to_string(),
morphologies: None,
},
stats: GenomeStats::default(),
};
let cid = CorticalID::try_from_base_64("Y7Gx8Xy7Fpo=").unwrap();
let mut area = CorticalArea::new(
cid,
0,
"movable".to_string(),
CorticalAreaDimensions::new(1, 1, 1).unwrap(),
GenomeCoordinate3D::new(0, 0, 0),
CorticalAreaType::BrainInput(IOCorticalAreaConfigurationFlag::Boolean),
)
.unwrap();
area.properties
.insert("coordinate_2d".to_string(), json!([400, 120]));
genome.cortical_areas.insert(cid, area);
let flat = convert_hierarchical_to_flat(&genome).unwrap();
let blueprint = flat["blueprint"].as_object().unwrap();
let b64 = cid.as_base_64();
assert_eq!(blueprint[&format!("_____10c-{}-cx-2dcorx-i", b64)], 400);
assert_eq!(blueprint[&format!("_____10c-{}-cx-2dcory-i", b64)], 120);
}
}