use serde_json::Value;
use std::collections::HashMap;
use tracing::debug;
pub fn genome_has_plasticity(genome_json: &Value) -> bool {
let has_memory = has_memory_areas(genome_json);
let has_stdp = has_stdp_connections(genome_json);
debug!(
target: "feagi-evolutionary",
"Plasticity detection: memory_areas={}, stdp_connections={}",
has_memory, has_stdp
);
has_memory || has_stdp
}
fn has_memory_areas(genome_json: &Value) -> bool {
if let Some(blueprint) = genome_json.get("blueprint").and_then(|b| b.as_object()) {
for (key, value) in blueprint {
if key.ends_with("-cx-memory-b") {
if let Some(is_memory) = value.as_bool() {
if is_memory {
debug!(
target: "feagi-evolutionary",
"Found memory area via key: {}", key
);
return true;
}
}
}
}
}
false
}
fn has_stdp_connections(genome_json: &Value) -> bool {
if let Some(blueprint) = genome_json.get("blueprint").and_then(|b| b.as_object()) {
for (key, value) in blueprint {
if key.ends_with("-cx-dstmap-d") {
if let Some(dstmap) = value.as_object() {
for (dst_area_id, morphology_list) in dstmap {
if let Some(morphologies) = morphology_list.as_array() {
for morph in morphologies {
if let Some(plasticity_flag) = morph.get("plasticity_flag") {
if plasticity_flag.as_bool() == Some(true) {
debug!(
target: "feagi-evolutionary",
"Found STDP connection: key={}, dst={}", key, dst_area_id
);
return true;
}
}
}
}
}
}
}
}
}
false
}
#[derive(Debug, Clone)]
pub struct MemoryAreaProperties {
pub temporal_depth: u32,
pub longterm_threshold: u32,
pub lifespan_growth_rate: f32,
pub init_lifespan: u32,
pub mp_learning_enabled: bool,
}
impl Default for MemoryAreaProperties {
fn default() -> Self {
Self {
temporal_depth: 1,
longterm_threshold: 100,
lifespan_growth_rate: 1.0,
init_lifespan: 9,
mp_learning_enabled: false,
}
}
}
pub fn extract_memory_properties(
properties: &HashMap<String, Value>,
) -> Option<MemoryAreaProperties> {
let is_memory = properties
.get("is_mem_type")
.and_then(|v| v.as_bool())
.unwrap_or(false);
if !is_memory {
return None;
}
Some(MemoryAreaProperties {
temporal_depth: properties
.get("temporal_depth")
.and_then(|v| v.as_u64())
.unwrap_or(1)
.max(1) as u32,
longterm_threshold: properties
.get("longterm_mem_threshold")
.and_then(|v| v.as_u64())
.unwrap_or(100) as u32,
lifespan_growth_rate: properties
.get("lifespan_growth_rate")
.and_then(|v| v.as_f64())
.unwrap_or(1.0) as f32,
init_lifespan: properties
.get("init_lifespan")
.and_then(|v| v.as_u64())
.unwrap_or(9) as u32,
mp_learning_enabled: properties
.get("mp_learning_enabled")
.and_then(|v| v.as_bool())
.unwrap_or(false),
})
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_no_plasticity() {
let genome = json!({
"blueprint": {
"_____10c-Y2FfX19fX50=-cx-_group-t": "CUSTOM",
"_____10c-Y2FfX19fX50=-cx-memory-b": false,
"_____10c-Y2FfX19fX50=-cx-dstmap-d": {
"b2ltZwkAAAA=": [{
"morphology_id": "projector",
"plasticity_flag": false
}]
}
}
});
assert!(!genome_has_plasticity(&genome));
}
#[test]
fn test_has_memory_areas() {
let genome = json!({
"blueprint": {
"_____10c-Y21fX19fXxg=-cx-_group-t": "CUSTOM",
"_____10c-Y21fX19fXxg=-cx-memory-b": true,
"_____10c-Y21fX19fXxg=-cx-mem__t-i": 100
}
});
assert!(genome_has_plasticity(&genome));
assert!(has_memory_areas(&genome));
assert!(!has_stdp_connections(&genome));
}
#[test]
fn test_has_stdp_connections() {
let genome = json!({
"blueprint": {
"_____10c-Y2FfX19fX50=-cx-_group-t": "CUSTOM",
"_____10c-Y2FfX19fX50=-cx-memory-b": false,
"_____10c-Y2FfX19fX50=-cx-dstmap-d": {
"b2ltZwkAAAA=": [{
"morphology_id": "projector",
"plasticity_flag": true,
"postSynapticCurrent_multiplier": 1
}]
}
}
});
assert!(genome_has_plasticity(&genome));
assert!(!has_memory_areas(&genome));
assert!(has_stdp_connections(&genome));
}
#[test]
fn test_has_both_plasticity_types() {
let genome = json!({
"blueprint": {
"_____10c-Y21fX19fXxg=-cx-memory-b": true,
"_____10c-Y2FfX19fX50=-cx-dstmap-d": {
"b2ltZwkAAAA=": [{
"morphology_id": "projector",
"plasticity_flag": true
}]
}
}
});
assert!(genome_has_plasticity(&genome));
assert!(has_memory_areas(&genome));
assert!(has_stdp_connections(&genome));
}
#[test]
fn test_extract_memory_properties() {
let mut properties = HashMap::new();
properties.insert("is_mem_type".to_string(), json!(true));
properties.insert("temporal_depth".to_string(), json!(5));
properties.insert("longterm_mem_threshold".to_string(), json!(200));
properties.insert("lifespan_growth_rate".to_string(), json!(1.5));
properties.insert("init_lifespan".to_string(), json!(15));
let mem_props = extract_memory_properties(&properties).expect("Should extract properties");
assert_eq!(mem_props.temporal_depth, 5);
assert_eq!(mem_props.longterm_threshold, 200);
assert_eq!(mem_props.lifespan_growth_rate, 1.5);
assert_eq!(mem_props.init_lifespan, 15);
}
#[test]
fn test_extract_memory_properties_defaults() {
let mut properties = HashMap::new();
properties.insert("is_mem_type".to_string(), json!(true));
let mem_props = extract_memory_properties(&properties).expect("Should extract properties");
assert_eq!(mem_props.temporal_depth, 1);
assert_eq!(mem_props.longterm_threshold, 100);
assert_eq!(mem_props.lifespan_growth_rate, 1.0);
assert_eq!(mem_props.init_lifespan, 9);
}
#[test]
fn test_extract_memory_properties_non_memory() {
let mut properties = HashMap::new();
properties.insert("is_mem_type".to_string(), json!(false));
assert!(extract_memory_properties(&properties).is_none());
}
}