feagi_evolutionary/
plasticity_detector.rs1use serde_json::Value;
7use std::collections::HashMap;
8use tracing::debug;
9
10pub fn genome_has_plasticity(genome_json: &Value) -> bool {
23 let has_memory = has_memory_areas(genome_json);
24 let has_stdp = has_stdp_connections(genome_json);
25
26 debug!(
27 target: "feagi-evolutionary",
28 "Plasticity detection: memory_areas={}, stdp_connections={}",
29 has_memory, has_stdp
30 );
31
32 has_memory || has_stdp
33}
34
35fn has_memory_areas(genome_json: &Value) -> bool {
41 if let Some(blueprint) = genome_json.get("blueprint").and_then(|b| b.as_object()) {
42 for (key, value) in blueprint {
43 if key.ends_with("-cx-memory-b") {
45 if let Some(is_memory) = value.as_bool() {
46 if is_memory {
47 debug!(
48 target: "feagi-evolutionary",
49 "Found memory area via key: {}", key
50 );
51 return true;
52 }
53 }
54 }
55 }
56 }
57 false
58}
59
60fn has_stdp_connections(genome_json: &Value) -> bool {
65 if let Some(blueprint) = genome_json.get("blueprint").and_then(|b| b.as_object()) {
66 for (key, value) in blueprint {
67 if key.ends_with("-cx-dstmap-d") {
69 if let Some(dstmap) = value.as_object() {
70 for (dst_area_id, morphology_list) in dstmap {
71 if let Some(morphologies) = morphology_list.as_array() {
72 for morph in morphologies {
73 if let Some(plasticity_flag) = morph.get("plasticity_flag") {
74 if plasticity_flag.as_bool() == Some(true) {
75 debug!(
76 target: "feagi-evolutionary",
77 "Found STDP connection: key={}, dst={}", key, dst_area_id
78 );
79 return true;
80 }
81 }
82 }
83 }
84 }
85 }
86 }
87 }
88 }
89 false
90}
91
92#[derive(Debug, Clone)]
94pub struct MemoryAreaProperties {
95 pub temporal_depth: u32,
97 pub longterm_threshold: u32,
99 pub lifespan_growth_rate: f32,
101 pub init_lifespan: u32,
103 pub mp_learning_enabled: bool,
105 pub min_window_activity: u32,
107 pub scan_skip_density: f32,
109}
110
111impl Default for MemoryAreaProperties {
112 fn default() -> Self {
113 Self {
114 temporal_depth: 1,
117 longterm_threshold: 100,
118 lifespan_growth_rate: 1.0,
119 init_lifespan: 9,
120 mp_learning_enabled: false,
121 min_window_activity: 1,
122 scan_skip_density: 1.0,
123 }
124 }
125}
126
127pub fn extract_memory_properties(
136 properties: &HashMap<String, Value>,
137) -> Option<MemoryAreaProperties> {
138 let is_memory = properties
139 .get("is_mem_type")
140 .and_then(|v| v.as_bool())
141 .unwrap_or(false);
142
143 if !is_memory {
144 return None;
145 }
146
147 Some(MemoryAreaProperties {
148 temporal_depth: properties
149 .get("temporal_depth")
150 .and_then(|v| v.as_u64())
151 .unwrap_or(1)
152 .max(1) as u32,
153 longterm_threshold: properties
154 .get("longterm_mem_threshold")
155 .and_then(|v| v.as_u64())
156 .unwrap_or(100) as u32,
157 lifespan_growth_rate: properties
158 .get("lifespan_growth_rate")
159 .and_then(|v| v.as_f64())
160 .unwrap_or(1.0) as f32,
161 init_lifespan: properties
162 .get("init_lifespan")
163 .and_then(|v| v.as_u64())
164 .unwrap_or(9) as u32,
165 mp_learning_enabled: properties
166 .get("mp_learning_enabled")
167 .and_then(|v| v.as_bool())
168 .unwrap_or(false),
169 min_window_activity: properties
170 .get("min_window_activity")
171 .and_then(|v| v.as_u64())
172 .unwrap_or(1)
173 .max(1) as u32,
174 scan_skip_density: properties
175 .get("scan_skip_density")
176 .and_then(|v| v.as_f64())
177 .unwrap_or(1.0)
178 .clamp(0.0, 1.0) as f32,
179 })
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185 use serde_json::json;
186
187 #[test]
188 fn test_no_plasticity() {
189 let genome = json!({
190 "blueprint": {
191 "_____10c-Y2FfX19fX50=-cx-_group-t": "CUSTOM",
192 "_____10c-Y2FfX19fX50=-cx-memory-b": false,
193 "_____10c-Y2FfX19fX50=-cx-dstmap-d": {
194 "b2ltZwkAAAA=": [{
195 "morphology_id": "projector",
196 "plasticity_flag": false
197 }]
198 }
199 }
200 });
201
202 assert!(!genome_has_plasticity(&genome));
203 }
204
205 #[test]
206 fn test_has_memory_areas() {
207 let genome = json!({
208 "blueprint": {
209 "_____10c-Y21fX19fXxg=-cx-_group-t": "CUSTOM",
210 "_____10c-Y21fX19fXxg=-cx-memory-b": true,
211 "_____10c-Y21fX19fXxg=-cx-mem__t-i": 100
212 }
213 });
214
215 assert!(genome_has_plasticity(&genome));
216 assert!(has_memory_areas(&genome));
217 assert!(!has_stdp_connections(&genome));
218 }
219
220 #[test]
221 fn test_has_stdp_connections() {
222 let genome = json!({
223 "blueprint": {
224 "_____10c-Y2FfX19fX50=-cx-_group-t": "CUSTOM",
225 "_____10c-Y2FfX19fX50=-cx-memory-b": false,
226 "_____10c-Y2FfX19fX50=-cx-dstmap-d": {
227 "b2ltZwkAAAA=": [{
228 "morphology_id": "projector",
229 "plasticity_flag": true,
230 "postSynapticCurrent_multiplier": 1
231 }]
232 }
233 }
234 });
235
236 assert!(genome_has_plasticity(&genome));
237 assert!(!has_memory_areas(&genome));
238 assert!(has_stdp_connections(&genome));
239 }
240
241 #[test]
242 fn test_has_both_plasticity_types() {
243 let genome = json!({
244 "blueprint": {
245 "_____10c-Y21fX19fXxg=-cx-memory-b": true,
246 "_____10c-Y2FfX19fX50=-cx-dstmap-d": {
247 "b2ltZwkAAAA=": [{
248 "morphology_id": "projector",
249 "plasticity_flag": true
250 }]
251 }
252 }
253 });
254
255 assert!(genome_has_plasticity(&genome));
256 assert!(has_memory_areas(&genome));
257 assert!(has_stdp_connections(&genome));
258 }
259
260 #[test]
261 fn test_extract_memory_properties() {
262 let mut properties = HashMap::new();
263 properties.insert("is_mem_type".to_string(), json!(true));
264 properties.insert("temporal_depth".to_string(), json!(5));
265 properties.insert("longterm_mem_threshold".to_string(), json!(200));
266 properties.insert("lifespan_growth_rate".to_string(), json!(1.5));
267 properties.insert("init_lifespan".to_string(), json!(15));
268
269 let mem_props = extract_memory_properties(&properties).expect("Should extract properties");
270 assert_eq!(mem_props.temporal_depth, 5);
271 assert_eq!(mem_props.longterm_threshold, 200);
272 assert_eq!(mem_props.lifespan_growth_rate, 1.5);
273 assert_eq!(mem_props.init_lifespan, 15);
274 }
275
276 #[test]
277 fn test_extract_memory_properties_defaults() {
278 let mut properties = HashMap::new();
279 properties.insert("is_mem_type".to_string(), json!(true));
280
281 let mem_props = extract_memory_properties(&properties).expect("Should extract properties");
282 assert_eq!(mem_props.temporal_depth, 1);
283 assert_eq!(mem_props.longterm_threshold, 100);
284 assert_eq!(mem_props.lifespan_growth_rate, 1.0);
285 assert_eq!(mem_props.init_lifespan, 9);
286 }
287
288 #[test]
289 fn test_extract_memory_properties_non_memory() {
290 let mut properties = HashMap::new();
291 properties.insert("is_mem_type".to_string(), json!(false));
292
293 assert!(extract_memory_properties(&properties).is_none());
294 }
295}