use std::collections::HashMap;
use crate::types::{BduError, BduResult, Position};
pub use feagi_structures::genomic::cortical_area::{
CoreCorticalType, CorticalArea, CorticalAreaDimensions, CorticalID,
};
pub trait CorticalAreaExt {
fn with_properties(self, properties: HashMap<String, serde_json::Value>) -> Self;
fn add_property(self, key: String, value: serde_json::Value) -> Self;
fn add_property_mut(&mut self, key: String, value: serde_json::Value);
fn contains_position(&self, pos: (i32, i32, i32)) -> bool;
fn to_relative_position(&self, pos: (i32, i32, i32)) -> BduResult<Position>;
fn to_absolute_position(&self, rel_pos: Position) -> BduResult<(i32, i32, i32)>;
fn neurons_per_voxel(&self) -> u32;
fn refractory_period(&self) -> u16;
fn snooze_period(&self) -> u16;
fn leak_coefficient(&self) -> f32;
fn firing_threshold(&self) -> f32;
fn firing_threshold_limit(&self) -> f32;
fn get_u32_property(&self, key: &str, default: u32) -> u32;
fn get_u16_property(&self, key: &str, default: u16) -> u16;
fn get_f32_property(&self, key: &str, default: f32) -> f32;
fn get_f64_property(&self, key: &str, default: f64) -> f64;
fn get_bool_property(&self, key: &str, default: bool) -> bool;
fn is_input_area(&self) -> bool;
fn is_output_area(&self) -> bool;
fn get_cortical_group(&self) -> Option<String>;
fn visible(&self) -> bool;
fn sub_group(&self) -> Option<String>;
fn plasticity_constant(&self) -> f32;
fn postsynaptic_current(&self) -> f32;
fn psp_uniform_distribution(&self) -> bool;
fn degeneration(&self) -> f32;
fn burst_engine_active(&self) -> bool;
fn firing_threshold_increment(&self) -> f32;
fn firing_threshold_increment_x(&self) -> f32;
fn firing_threshold_increment_y(&self) -> f32;
fn firing_threshold_increment_z(&self) -> f32;
fn consecutive_fire_count(&self) -> u32;
fn leak_variability(&self) -> f32;
fn neuron_excitability(&self) -> f32;
fn postsynaptic_current_max(&self) -> f32;
fn mp_charge_accumulation(&self) -> bool;
fn mp_driven_psp(&self) -> bool;
fn init_lifespan(&self) -> u32;
fn lifespan_growth_rate(&self) -> f32;
fn longterm_mem_threshold(&self) -> u32;
}
impl CorticalAreaExt for CorticalArea {
fn with_properties(mut self, properties: HashMap<String, serde_json::Value>) -> Self {
self.properties = properties;
self
}
fn add_property(mut self, key: String, value: serde_json::Value) -> Self {
self.properties.insert(key, value);
self
}
fn add_property_mut(&mut self, key: String, value: serde_json::Value) {
self.properties.insert(key, value);
}
fn contains_position(&self, pos: (i32, i32, i32)) -> bool {
let (x, y, z) = pos;
let ox = self.position.x;
let oy = self.position.y;
let oz = self.position.z;
x >= ox
&& y >= oy
&& z >= oz
&& x < ox + self.dimensions.width as i32
&& y < oy + self.dimensions.height as i32
&& z < oz + self.dimensions.depth as i32
}
fn to_relative_position(&self, pos: (i32, i32, i32)) -> BduResult<Position> {
if !self.contains_position(pos) {
return Err(BduError::OutOfBounds {
pos: (pos.0 as u32, pos.1 as u32, pos.2 as u32),
dims: (
self.dimensions.width as usize,
self.dimensions.height as usize,
self.dimensions.depth as usize,
),
});
}
let ox = self.position.x;
let oy = self.position.y;
let oz = self.position.z;
Ok((
(pos.0 - ox) as u32,
(pos.1 - oy) as u32,
(pos.2 - oz) as u32,
))
}
fn to_absolute_position(&self, rel_pos: Position) -> BduResult<(i32, i32, i32)> {
if !self.dimensions.contains(rel_pos) {
return Err(BduError::OutOfBounds {
pos: rel_pos,
dims: (
self.dimensions.width as usize,
self.dimensions.height as usize,
self.dimensions.depth as usize,
),
});
}
let ox = self.position.x;
let oy = self.position.y;
let oz = self.position.z;
Ok((
ox + rel_pos.0 as i32,
oy + rel_pos.1 as i32,
oz + rel_pos.2 as i32,
))
}
fn neurons_per_voxel(&self) -> u32 {
self.get_u32_property("neurons_per_voxel", 1)
}
fn refractory_period(&self) -> u16 {
self.get_u16_property("refractory_period", 0)
}
fn snooze_period(&self) -> u16 {
self.get_u16_property("snooze_period", 0)
}
fn leak_coefficient(&self) -> f32 {
self.get_f32_property("leak_coefficient", 0.0)
}
fn firing_threshold(&self) -> f32 {
self.get_f32_property("firing_threshold", 1.0)
}
fn get_u32_property(&self, key: &str, default: u32) -> u32 {
self.properties
.get(key)
.and_then(|v| v.as_u64())
.map(|v| v as u32)
.unwrap_or(default)
}
fn get_u16_property(&self, key: &str, default: u16) -> u16 {
self.properties
.get(key)
.and_then(|v| v.as_u64())
.map(|v| v as u16)
.unwrap_or(default)
}
fn get_f32_property(&self, key: &str, default: f32) -> f32 {
self.properties
.get(key)
.and_then(|v| v.as_f64())
.map(|v| v as f32)
.unwrap_or(default)
}
fn get_f64_property(&self, key: &str, default: f64) -> f64 {
self.properties
.get(key)
.and_then(|v| v.as_f64())
.unwrap_or(default)
}
fn get_bool_property(&self, key: &str, default: bool) -> bool {
self.properties
.get(key)
.and_then(|v| v.as_bool())
.unwrap_or(default)
}
fn is_input_area(&self) -> bool {
matches!(
self.cortical_type,
feagi_structures::genomic::cortical_area::CorticalAreaType::BrainInput(_)
)
}
fn is_output_area(&self) -> bool {
matches!(
self.cortical_type,
feagi_structures::genomic::cortical_area::CorticalAreaType::BrainOutput(_)
)
}
fn get_cortical_group(&self) -> Option<String> {
self.properties
.get("cortical_group")
.and_then(|v| v.as_str())
.map(|s| s.to_string())
.or_else(|| {
use feagi_structures::genomic::cortical_area::CorticalAreaType;
match self.cortical_type {
CorticalAreaType::BrainInput(_) => Some("IPU".to_string()),
CorticalAreaType::BrainOutput(_) => Some("OPU".to_string()),
CorticalAreaType::Memory(_) => Some("MEMORY".to_string()),
CorticalAreaType::Custom(_) => Some("CUSTOM".to_string()),
CorticalAreaType::Core(_) => Some("CORE".to_string()),
}
})
}
fn visible(&self) -> bool {
self.get_bool_property("visible", true)
}
fn sub_group(&self) -> Option<String> {
self.properties
.get("sub_group")
.and_then(|v| v.as_str())
.map(|s| s.to_string())
}
fn plasticity_constant(&self) -> f32 {
self.get_f32_property("plasticity_constant", 0.0)
}
fn postsynaptic_current(&self) -> f32 {
self.get_f32_property("postsynaptic_current", 1.0)
}
fn psp_uniform_distribution(&self) -> bool {
let default = matches!(
self.cortical_type,
feagi_structures::genomic::cortical_area::CorticalAreaType::Memory(_)
);
self.get_bool_property("psp_uniform_distribution", default)
}
fn degeneration(&self) -> f32 {
self.get_f32_property("degeneration", 0.0)
}
fn burst_engine_active(&self) -> bool {
self.get_bool_property("burst_engine_active", false)
}
fn firing_threshold_increment(&self) -> f32 {
self.get_f32_property("firing_threshold_increment", 0.0)
}
fn firing_threshold_increment_x(&self) -> f32 {
self.get_f32_property("firing_threshold_increment_x", 0.0)
}
fn firing_threshold_increment_y(&self) -> f32 {
self.get_f32_property("firing_threshold_increment_y", 0.0)
}
fn firing_threshold_increment_z(&self) -> f32 {
self.get_f32_property("firing_threshold_increment_z", 0.0)
}
fn firing_threshold_limit(&self) -> f32 {
self.get_f32_property("firing_threshold_limit", 0.0)
}
fn consecutive_fire_count(&self) -> u32 {
self.get_u32_property("consecutive_fire_limit", 0)
}
fn leak_variability(&self) -> f32 {
self.get_f32_property("leak_variability", 0.0)
}
fn neuron_excitability(&self) -> f32 {
self.get_f32_property("neuron_excitability", 100.0)
}
fn postsynaptic_current_max(&self) -> f32 {
self.get_f32_property("postsynaptic_current_max", 0.0)
}
fn mp_charge_accumulation(&self) -> bool {
self.get_bool_property("mp_charge_accumulation", false)
}
fn mp_driven_psp(&self) -> bool {
self.get_bool_property("mp_driven_psp", false)
}
fn init_lifespan(&self) -> u32 {
self.get_u32_property("init_lifespan", 0)
}
fn lifespan_growth_rate(&self) -> f32 {
self.get_f32_property("lifespan_growth_rate", 0.0)
}
fn longterm_mem_threshold(&self) -> u32 {
self.get_u32_property("longterm_mem_threshold", 0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_contains_position() {
let cortical_id = CoreCorticalType::Power.to_cortical_id();
let cortical_type = cortical_id
.as_cortical_type()
.expect("Failed to get cortical type");
let dims = CorticalAreaDimensions::new(10, 10, 10).unwrap();
let area = CorticalArea::new(
cortical_id,
0,
"Test Area".to_string(),
dims,
(5, 5, 5).into(),
cortical_type,
)
.unwrap();
assert!(area.contains_position((5, 5, 5))); assert!(area.contains_position((14, 14, 14))); assert!(!area.contains_position((4, 5, 5))); assert!(!area.contains_position((15, 5, 5))); }
#[test]
fn test_position_conversion() {
let cortical_id = CoreCorticalType::Power.to_cortical_id();
let cortical_type = cortical_id
.as_cortical_type()
.expect("Failed to get cortical type");
let dims = CorticalAreaDimensions::new(10, 10, 10).unwrap();
let area = CorticalArea::new(
cortical_id,
0,
"Test Area".to_string(),
dims,
(100, 200, 300).into(),
cortical_type,
)
.unwrap();
let rel_pos = area.to_relative_position((105, 207, 308)).unwrap();
assert_eq!(rel_pos, (5, 7, 8));
let abs_pos = area.to_absolute_position(rel_pos).unwrap();
assert_eq!(abs_pos, (105, 207, 308));
let result = area.to_relative_position((99, 200, 300));
assert!(result.is_err());
}
#[test]
fn test_properties() {
let cortical_id = CoreCorticalType::Power.to_cortical_id();
let cortical_type = cortical_id
.as_cortical_type()
.expect("Failed to get cortical type");
let dims = CorticalAreaDimensions::new(10, 10, 10).unwrap();
let area = CorticalArea::new(
cortical_id,
0,
"Test".to_string(),
dims,
(0, 0, 0).into(),
cortical_type,
)
.unwrap()
.add_property("resolution".to_string(), serde_json::json!(128))
.add_property("modality".to_string(), serde_json::json!("visual"));
assert_eq!(
area.get_property("resolution"),
Some(&serde_json::json!(128))
);
assert_eq!(
area.get_property("modality"),
Some(&serde_json::json!("visual"))
);
assert_eq!(area.get_property("nonexistent"), None);
}
}