feagi_brain_development/models/
cortical_area.rs1use std::collections::HashMap;
13
14use crate::types::{BduError, BduResult, Position};
15
16pub use feagi_structures::genomic::cortical_area::{
18 CoreCorticalType, CorticalArea, CorticalAreaDimensions, CorticalID,
19};
20
21pub trait CorticalAreaExt {
23 fn with_properties(self, properties: HashMap<String, serde_json::Value>) -> Self;
25
26 fn add_property(self, key: String, value: serde_json::Value) -> Self;
28
29 fn add_property_mut(&mut self, key: String, value: serde_json::Value);
31
32 fn contains_position(&self, pos: (i32, i32, i32)) -> bool;
34
35 fn to_relative_position(&self, pos: (i32, i32, i32)) -> BduResult<Position>;
37
38 fn to_absolute_position(&self, rel_pos: Position) -> BduResult<(i32, i32, i32)>;
40
41 fn neurons_per_voxel(&self) -> u32;
43
44 fn refractory_period(&self) -> u16;
46
47 fn snooze_period(&self) -> u16;
49
50 fn leak_coefficient(&self) -> f32;
52
53 fn firing_threshold(&self) -> f32;
55
56 fn get_u32_property(&self, key: &str, default: u32) -> u32;
58
59 fn get_u16_property(&self, key: &str, default: u16) -> u16;
61
62 fn get_f32_property(&self, key: &str, default: f32) -> f32;
64
65 fn get_bool_property(&self, key: &str, default: bool) -> bool;
67
68 fn is_input_area(&self) -> bool;
70
71 fn is_output_area(&self) -> bool;
73
74 fn get_cortical_group(&self) -> Option<String>;
76
77 fn visible(&self) -> bool;
79
80 fn sub_group(&self) -> Option<String>;
82
83 fn plasticity_constant(&self) -> f32;
85
86 fn postsynaptic_current(&self) -> f32;
88
89 fn psp_uniform_distribution(&self) -> f32;
91
92 fn degeneration(&self) -> f32;
94
95 fn burst_engine_active(&self) -> bool;
97
98 fn firing_threshold_increment(&self) -> f32;
100
101 fn firing_threshold_limit(&self) -> f32;
103
104 fn consecutive_fire_count(&self) -> u32;
106
107 fn leak_variability(&self) -> f32;
109}
110
111impl CorticalAreaExt for CorticalArea {
112 fn with_properties(mut self, properties: HashMap<String, serde_json::Value>) -> Self {
113 self.properties = properties;
114 self
115 }
116
117 fn add_property(mut self, key: String, value: serde_json::Value) -> Self {
118 self.properties.insert(key, value);
119 self
120 }
121
122 fn add_property_mut(&mut self, key: String, value: serde_json::Value) {
123 self.properties.insert(key, value);
124 }
125
126 fn contains_position(&self, pos: (i32, i32, i32)) -> bool {
127 let (x, y, z) = pos;
128 let ox = self.position.x;
129 let oy = self.position.y;
130 let oz = self.position.z;
131
132 x >= ox
133 && y >= oy
134 && z >= oz
135 && x < ox + self.dimensions.width as i32
136 && y < oy + self.dimensions.height as i32
137 && z < oz + self.dimensions.depth as i32
138 }
139
140 fn to_relative_position(&self, pos: (i32, i32, i32)) -> BduResult<Position> {
141 if !self.contains_position(pos) {
142 return Err(BduError::OutOfBounds {
143 pos: (pos.0 as u32, pos.1 as u32, pos.2 as u32),
144 dims: (
145 self.dimensions.width as usize,
146 self.dimensions.height as usize,
147 self.dimensions.depth as usize,
148 ),
149 });
150 }
151
152 let ox = self.position.x;
153 let oy = self.position.y;
154 let oz = self.position.z;
155 Ok((
156 (pos.0 - ox) as u32,
157 (pos.1 - oy) as u32,
158 (pos.2 - oz) as u32,
159 ))
160 }
161
162 fn to_absolute_position(&self, rel_pos: Position) -> BduResult<(i32, i32, i32)> {
163 if !self.dimensions.contains(rel_pos) {
164 return Err(BduError::OutOfBounds {
165 pos: rel_pos,
166 dims: (
167 self.dimensions.width as usize,
168 self.dimensions.height as usize,
169 self.dimensions.depth as usize,
170 ),
171 });
172 }
173
174 let ox = self.position.x;
175 let oy = self.position.y;
176 let oz = self.position.z;
177 Ok((
178 ox + rel_pos.0 as i32,
179 oy + rel_pos.1 as i32,
180 oz + rel_pos.2 as i32,
181 ))
182 }
183
184 fn neurons_per_voxel(&self) -> u32 {
185 self.get_u32_property("neurons_per_voxel", 1)
186 }
187
188 fn refractory_period(&self) -> u16 {
189 self.get_u16_property("refractory_period", 0)
190 }
191
192 fn snooze_period(&self) -> u16 {
193 self.get_u16_property("snooze_period", 0)
194 }
195
196 fn leak_coefficient(&self) -> f32 {
197 self.get_f32_property("leak_coefficient", 0.0)
198 }
199
200 fn firing_threshold(&self) -> f32 {
201 self.get_f32_property("firing_threshold", 1.0)
202 }
203
204 fn get_u32_property(&self, key: &str, default: u32) -> u32 {
205 self.properties
206 .get(key)
207 .and_then(|v| v.as_u64())
208 .map(|v| v as u32)
209 .unwrap_or(default)
210 }
211
212 fn get_u16_property(&self, key: &str, default: u16) -> u16 {
213 self.properties
214 .get(key)
215 .and_then(|v| v.as_u64())
216 .map(|v| v as u16)
217 .unwrap_or(default)
218 }
219
220 fn get_f32_property(&self, key: &str, default: f32) -> f32 {
221 self.properties
222 .get(key)
223 .and_then(|v| v.as_f64())
224 .map(|v| v as f32)
225 .unwrap_or(default)
226 }
227
228 fn get_bool_property(&self, key: &str, default: bool) -> bool {
229 self.properties
230 .get(key)
231 .and_then(|v| v.as_bool())
232 .unwrap_or(default)
233 }
234
235 fn is_input_area(&self) -> bool {
236 matches!(
237 self.cortical_type,
238 feagi_structures::genomic::cortical_area::CorticalAreaType::BrainInput(_)
239 )
240 }
241
242 fn is_output_area(&self) -> bool {
243 matches!(
244 self.cortical_type,
245 feagi_structures::genomic::cortical_area::CorticalAreaType::BrainOutput(_)
246 )
247 }
248
249 fn get_cortical_group(&self) -> Option<String> {
250 self.properties
251 .get("cortical_group")
252 .and_then(|v| v.as_str())
253 .map(|s| s.to_string())
254 .or_else(|| {
255 use feagi_structures::genomic::cortical_area::CorticalAreaType;
257 match self.cortical_type {
258 CorticalAreaType::BrainInput(_) => Some("IPU".to_string()),
259 CorticalAreaType::BrainOutput(_) => Some("OPU".to_string()),
260 CorticalAreaType::Memory(_) => Some("MEMORY".to_string()),
261 CorticalAreaType::Custom(_) => Some("CUSTOM".to_string()),
262 CorticalAreaType::Core(_) => Some("CORE".to_string()),
263 }
264 })
265 }
266
267 fn visible(&self) -> bool {
268 self.get_bool_property("visible", true)
269 }
270
271 fn sub_group(&self) -> Option<String> {
272 self.properties
273 .get("sub_group")
274 .and_then(|v| v.as_str())
275 .map(|s| s.to_string())
276 }
277
278 fn plasticity_constant(&self) -> f32 {
279 self.get_f32_property("plasticity_constant", 0.0)
280 }
281
282 fn postsynaptic_current(&self) -> f32 {
283 self.get_f32_property("postsynaptic_current", 0.0)
284 }
285
286 fn psp_uniform_distribution(&self) -> f32 {
287 self.get_f32_property("psp_uniform_distribution", 0.0)
288 }
289
290 fn degeneration(&self) -> f32 {
291 self.get_f32_property("degeneration", 0.0)
292 }
293
294 fn burst_engine_active(&self) -> bool {
295 self.get_bool_property("burst_engine_active", false)
296 }
297
298 fn firing_threshold_increment(&self) -> f32 {
299 self.get_f32_property("firing_threshold_increment", 0.0)
300 }
301
302 fn firing_threshold_limit(&self) -> f32 {
303 self.get_f32_property("firing_threshold_limit", 1.0)
304 }
305
306 fn consecutive_fire_count(&self) -> u32 {
307 self.get_u32_property("consecutive_fire_limit", 0)
308 }
309
310 fn leak_variability(&self) -> f32 {
311 self.get_f32_property("leak_variability", 0.0)
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn test_contains_position() {
321 let cortical_id = CoreCorticalType::Power.to_cortical_id();
322 let cortical_type = cortical_id
323 .as_cortical_type()
324 .expect("Failed to get cortical type");
325 let dims = CorticalAreaDimensions::new(10, 10, 10).unwrap();
326 let area = CorticalArea::new(
327 cortical_id,
328 0,
329 "Test Area".to_string(),
330 dims,
331 (5, 5, 5).into(),
332 cortical_type,
333 )
334 .unwrap();
335
336 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))); }
341
342 #[test]
343 fn test_position_conversion() {
344 let cortical_id = CoreCorticalType::Power.to_cortical_id();
345 let cortical_type = cortical_id
346 .as_cortical_type()
347 .expect("Failed to get cortical type");
348 let dims = CorticalAreaDimensions::new(10, 10, 10).unwrap();
349 let area = CorticalArea::new(
350 cortical_id,
351 0,
352 "Test Area".to_string(),
353 dims,
354 (100, 200, 300).into(),
355 cortical_type,
356 )
357 .unwrap();
358
359 let rel_pos = area.to_relative_position((105, 207, 308)).unwrap();
362 assert_eq!(rel_pos, (5, 7, 8));
363
364 let abs_pos = area.to_absolute_position(rel_pos).unwrap();
366 assert_eq!(abs_pos, (105, 207, 308));
367
368 let result = area.to_relative_position((99, 200, 300));
370 assert!(result.is_err());
371 }
372
373 #[test]
374 fn test_properties() {
375 let cortical_id = CoreCorticalType::Power.to_cortical_id();
376 let cortical_type = cortical_id
377 .as_cortical_type()
378 .expect("Failed to get cortical type");
379 let dims = CorticalAreaDimensions::new(10, 10, 10).unwrap();
380 let area = CorticalArea::new(
381 cortical_id,
382 0,
383 "Test".to_string(),
384 dims,
385 (0, 0, 0).into(),
386 cortical_type,
387 )
388 .unwrap()
389 .add_property("resolution".to_string(), serde_json::json!(128))
390 .add_property("modality".to_string(), serde_json::json!("visual"));
391
392 assert_eq!(
393 area.get_property("resolution"),
394 Some(&serde_json::json!(128))
395 );
396 assert_eq!(
397 area.get_property("modality"),
398 Some(&serde_json::json!("visual"))
399 );
400 assert_eq!(area.get_property("nonexistent"), None);
401 }
402}