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