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 firing_threshold_limit(&self) -> f32;
58
59 fn get_u32_property(&self, key: &str, default: u32) -> u32;
61
62 fn get_u16_property(&self, key: &str, default: u16) -> u16;
64
65 fn get_f32_property(&self, key: &str, default: f32) -> f32;
67
68 fn get_bool_property(&self, key: &str, default: bool) -> bool;
70
71 fn is_input_area(&self) -> bool;
73
74 fn is_output_area(&self) -> bool;
76
77 fn get_cortical_group(&self) -> Option<String>;
79
80 fn visible(&self) -> bool;
82
83 fn sub_group(&self) -> Option<String>;
85
86 fn plasticity_constant(&self) -> f32;
88
89 fn postsynaptic_current(&self) -> f32;
91
92 fn psp_uniform_distribution(&self) -> bool;
95
96 fn degeneration(&self) -> f32;
98
99 fn burst_engine_active(&self) -> bool;
101
102 fn firing_threshold_increment(&self) -> f32;
104
105 fn firing_threshold_increment_x(&self) -> f32;
107
108 fn firing_threshold_increment_y(&self) -> f32;
110
111 fn firing_threshold_increment_z(&self) -> f32;
113
114 fn consecutive_fire_count(&self) -> u32;
116
117 fn leak_variability(&self) -> f32;
119
120 fn neuron_excitability(&self) -> f32;
122
123 fn postsynaptic_current_max(&self) -> f32;
125
126 fn mp_charge_accumulation(&self) -> bool;
128
129 fn mp_driven_psp(&self) -> bool;
131
132 fn init_lifespan(&self) -> u32;
134
135 fn lifespan_growth_rate(&self) -> f32;
137
138 fn longterm_mem_threshold(&self) -> u32;
140}
141
142impl CorticalAreaExt for CorticalArea {
143 fn with_properties(mut self, properties: HashMap<String, serde_json::Value>) -> Self {
144 self.properties = properties;
145 self
146 }
147
148 fn add_property(mut self, key: String, value: serde_json::Value) -> Self {
149 self.properties.insert(key, value);
150 self
151 }
152
153 fn add_property_mut(&mut self, key: String, value: serde_json::Value) {
154 self.properties.insert(key, value);
155 }
156
157 fn contains_position(&self, pos: (i32, i32, i32)) -> bool {
158 let (x, y, z) = pos;
159 let ox = self.position.x;
160 let oy = self.position.y;
161 let oz = self.position.z;
162
163 x >= ox
164 && y >= oy
165 && z >= oz
166 && x < ox + self.dimensions.width as i32
167 && y < oy + self.dimensions.height as i32
168 && z < oz + self.dimensions.depth as i32
169 }
170
171 fn to_relative_position(&self, pos: (i32, i32, i32)) -> BduResult<Position> {
172 if !self.contains_position(pos) {
173 return Err(BduError::OutOfBounds {
174 pos: (pos.0 as u32, pos.1 as u32, pos.2 as u32),
175 dims: (
176 self.dimensions.width as usize,
177 self.dimensions.height as usize,
178 self.dimensions.depth as usize,
179 ),
180 });
181 }
182
183 let ox = self.position.x;
184 let oy = self.position.y;
185 let oz = self.position.z;
186 Ok((
187 (pos.0 - ox) as u32,
188 (pos.1 - oy) as u32,
189 (pos.2 - oz) as u32,
190 ))
191 }
192
193 fn to_absolute_position(&self, rel_pos: Position) -> BduResult<(i32, i32, i32)> {
194 if !self.dimensions.contains(rel_pos) {
195 return Err(BduError::OutOfBounds {
196 pos: rel_pos,
197 dims: (
198 self.dimensions.width as usize,
199 self.dimensions.height as usize,
200 self.dimensions.depth as usize,
201 ),
202 });
203 }
204
205 let ox = self.position.x;
206 let oy = self.position.y;
207 let oz = self.position.z;
208 Ok((
209 ox + rel_pos.0 as i32,
210 oy + rel_pos.1 as i32,
211 oz + rel_pos.2 as i32,
212 ))
213 }
214
215 fn neurons_per_voxel(&self) -> u32 {
216 self.get_u32_property("neurons_per_voxel", 1)
217 }
218
219 fn refractory_period(&self) -> u16 {
220 self.get_u16_property("refractory_period", 0)
221 }
222
223 fn snooze_period(&self) -> u16 {
224 self.get_u16_property("snooze_period", 0)
225 }
226
227 fn leak_coefficient(&self) -> f32 {
228 self.get_f32_property("leak_coefficient", 0.0)
229 }
230
231 fn firing_threshold(&self) -> f32 {
232 self.get_f32_property("firing_threshold", 1.0)
233 }
234
235 fn get_u32_property(&self, key: &str, default: u32) -> u32 {
236 self.properties
237 .get(key)
238 .and_then(|v| v.as_u64())
239 .map(|v| v as u32)
240 .unwrap_or(default)
241 }
242
243 fn get_u16_property(&self, key: &str, default: u16) -> u16 {
244 self.properties
245 .get(key)
246 .and_then(|v| v.as_u64())
247 .map(|v| v as u16)
248 .unwrap_or(default)
249 }
250
251 fn get_f32_property(&self, key: &str, default: f32) -> f32 {
252 self.properties
253 .get(key)
254 .and_then(|v| v.as_f64())
255 .map(|v| v as f32)
256 .unwrap_or(default)
257 }
258
259 fn get_bool_property(&self, key: &str, default: bool) -> bool {
260 self.properties
261 .get(key)
262 .and_then(|v| v.as_bool())
263 .unwrap_or(default)
264 }
265
266 fn is_input_area(&self) -> bool {
267 matches!(
268 self.cortical_type,
269 feagi_structures::genomic::cortical_area::CorticalAreaType::BrainInput(_)
270 )
271 }
272
273 fn is_output_area(&self) -> bool {
274 matches!(
275 self.cortical_type,
276 feagi_structures::genomic::cortical_area::CorticalAreaType::BrainOutput(_)
277 )
278 }
279
280 fn get_cortical_group(&self) -> Option<String> {
281 self.properties
282 .get("cortical_group")
283 .and_then(|v| v.as_str())
284 .map(|s| s.to_string())
285 .or_else(|| {
286 use feagi_structures::genomic::cortical_area::CorticalAreaType;
288 match self.cortical_type {
289 CorticalAreaType::BrainInput(_) => Some("IPU".to_string()),
290 CorticalAreaType::BrainOutput(_) => Some("OPU".to_string()),
291 CorticalAreaType::Memory(_) => Some("MEMORY".to_string()),
292 CorticalAreaType::Custom(_) => Some("CUSTOM".to_string()),
293 CorticalAreaType::Core(_) => Some("CORE".to_string()),
294 }
295 })
296 }
297
298 fn visible(&self) -> bool {
299 self.get_bool_property("visible", true)
300 }
301
302 fn sub_group(&self) -> Option<String> {
303 self.properties
304 .get("sub_group")
305 .and_then(|v| v.as_str())
306 .map(|s| s.to_string())
307 }
308
309 fn plasticity_constant(&self) -> f32 {
310 self.get_f32_property("plasticity_constant", 0.0)
311 }
312
313 fn postsynaptic_current(&self) -> f32 {
314 self.get_f32_property("postsynaptic_current", 1.0)
315 }
316
317 fn psp_uniform_distribution(&self) -> bool {
318 let default = matches!(
319 self.cortical_type,
320 feagi_structures::genomic::cortical_area::CorticalAreaType::Memory(_)
321 );
322 self.get_bool_property("psp_uniform_distribution", default)
323 }
324
325 fn degeneration(&self) -> f32 {
326 self.get_f32_property("degeneration", 0.0)
327 }
328
329 fn burst_engine_active(&self) -> bool {
330 self.get_bool_property("burst_engine_active", false)
331 }
332
333 fn firing_threshold_increment(&self) -> f32 {
334 self.get_f32_property("firing_threshold_increment", 0.0)
335 }
336
337 fn firing_threshold_increment_x(&self) -> f32 {
338 self.get_f32_property("firing_threshold_increment_x", 0.0)
339 }
340
341 fn firing_threshold_increment_y(&self) -> f32 {
342 self.get_f32_property("firing_threshold_increment_y", 0.0)
343 }
344
345 fn firing_threshold_increment_z(&self) -> f32 {
346 self.get_f32_property("firing_threshold_increment_z", 0.0)
347 }
348
349 fn firing_threshold_limit(&self) -> f32 {
350 self.get_f32_property("firing_threshold_limit", 0.0)
351 }
352
353 fn consecutive_fire_count(&self) -> u32 {
354 self.get_u32_property("consecutive_fire_limit", 0)
355 }
356
357 fn leak_variability(&self) -> f32 {
358 self.get_f32_property("leak_variability", 0.0)
359 }
360
361 fn neuron_excitability(&self) -> f32 {
362 self.get_f32_property("neuron_excitability", 100.0)
363 }
364
365 fn postsynaptic_current_max(&self) -> f32 {
366 self.get_f32_property("postsynaptic_current_max", 0.0)
367 }
368
369 fn mp_charge_accumulation(&self) -> bool {
370 self.get_bool_property("mp_charge_accumulation", false)
371 }
372
373 fn mp_driven_psp(&self) -> bool {
374 self.get_bool_property("mp_driven_psp", false)
375 }
376
377 fn init_lifespan(&self) -> u32 {
378 self.get_u32_property("init_lifespan", 0)
379 }
380
381 fn lifespan_growth_rate(&self) -> f32 {
382 self.get_f32_property("lifespan_growth_rate", 0.0)
383 }
384
385 fn longterm_mem_threshold(&self) -> u32 {
386 self.get_u32_property("longterm_mem_threshold", 0)
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn test_contains_position() {
396 let cortical_id = CoreCorticalType::Power.to_cortical_id();
397 let cortical_type = cortical_id
398 .as_cortical_type()
399 .expect("Failed to get cortical type");
400 let dims = CorticalAreaDimensions::new(10, 10, 10).unwrap();
401 let area = CorticalArea::new(
402 cortical_id,
403 0,
404 "Test Area".to_string(),
405 dims,
406 (5, 5, 5).into(),
407 cortical_type,
408 )
409 .unwrap();
410
411 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))); }
416
417 #[test]
418 fn test_position_conversion() {
419 let cortical_id = CoreCorticalType::Power.to_cortical_id();
420 let cortical_type = cortical_id
421 .as_cortical_type()
422 .expect("Failed to get cortical type");
423 let dims = CorticalAreaDimensions::new(10, 10, 10).unwrap();
424 let area = CorticalArea::new(
425 cortical_id,
426 0,
427 "Test Area".to_string(),
428 dims,
429 (100, 200, 300).into(),
430 cortical_type,
431 )
432 .unwrap();
433
434 let rel_pos = area.to_relative_position((105, 207, 308)).unwrap();
437 assert_eq!(rel_pos, (5, 7, 8));
438
439 let abs_pos = area.to_absolute_position(rel_pos).unwrap();
441 assert_eq!(abs_pos, (105, 207, 308));
442
443 let result = area.to_relative_position((99, 200, 300));
445 assert!(result.is_err());
446 }
447
448 #[test]
449 fn test_properties() {
450 let cortical_id = CoreCorticalType::Power.to_cortical_id();
451 let cortical_type = cortical_id
452 .as_cortical_type()
453 .expect("Failed to get cortical type");
454 let dims = CorticalAreaDimensions::new(10, 10, 10).unwrap();
455 let area = CorticalArea::new(
456 cortical_id,
457 0,
458 "Test".to_string(),
459 dims,
460 (0, 0, 0).into(),
461 cortical_type,
462 )
463 .unwrap()
464 .add_property("resolution".to_string(), serde_json::json!(128))
465 .add_property("modality".to_string(), serde_json::json!("visual"));
466
467 assert_eq!(
468 area.get_property("resolution"),
469 Some(&serde_json::json!(128))
470 );
471 assert_eq!(
472 area.get_property("modality"),
473 Some(&serde_json::json!("visual"))
474 );
475 assert_eq!(area.get_property("nonexistent"), None);
476 }
477}