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}
104
105impl Default for MemoryAreaProperties {
106 fn default() -> Self {
107 Self {
108 temporal_depth: 1,
111 longterm_threshold: 100,
112 lifespan_growth_rate: 1.0,
113 init_lifespan: 9,
114 }
115 }
116}
117
118pub fn extract_memory_properties(
127 properties: &HashMap<String, Value>,
128) -> Option<MemoryAreaProperties> {
129 let is_memory = properties
130 .get("is_mem_type")
131 .and_then(|v| v.as_bool())
132 .unwrap_or(false);
133
134 if !is_memory {
135 return None;
136 }
137
138 Some(MemoryAreaProperties {
139 temporal_depth: properties
140 .get("temporal_depth")
141 .and_then(|v| v.as_u64())
142 .unwrap_or(1)
143 .max(1) as u32,
144 longterm_threshold: properties
145 .get("longterm_mem_threshold")
146 .and_then(|v| v.as_u64())
147 .unwrap_or(100) as u32,
148 lifespan_growth_rate: properties
149 .get("lifespan_growth_rate")
150 .and_then(|v| v.as_f64())
151 .unwrap_or(1.0) as f32,
152 init_lifespan: properties
153 .get("init_lifespan")
154 .and_then(|v| v.as_u64())
155 .unwrap_or(9) as u32,
156 })
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162 use serde_json::json;
163
164 #[test]
165 fn test_no_plasticity() {
166 let genome = json!({
167 "blueprint": {
168 "_____10c-Y2FfX19fX50=-cx-_group-t": "CUSTOM",
169 "_____10c-Y2FfX19fX50=-cx-memory-b": false,
170 "_____10c-Y2FfX19fX50=-cx-dstmap-d": {
171 "b2ltZwkAAAA=": [{
172 "morphology_id": "projector",
173 "plasticity_flag": false
174 }]
175 }
176 }
177 });
178
179 assert!(!genome_has_plasticity(&genome));
180 }
181
182 #[test]
183 fn test_has_memory_areas() {
184 let genome = json!({
185 "blueprint": {
186 "_____10c-Y21fX19fXxg=-cx-_group-t": "CUSTOM",
187 "_____10c-Y21fX19fXxg=-cx-memory-b": true,
188 "_____10c-Y21fX19fXxg=-cx-mem__t-i": 100
189 }
190 });
191
192 assert!(genome_has_plasticity(&genome));
193 assert!(has_memory_areas(&genome));
194 assert!(!has_stdp_connections(&genome));
195 }
196
197 #[test]
198 fn test_has_stdp_connections() {
199 let genome = json!({
200 "blueprint": {
201 "_____10c-Y2FfX19fX50=-cx-_group-t": "CUSTOM",
202 "_____10c-Y2FfX19fX50=-cx-memory-b": false,
203 "_____10c-Y2FfX19fX50=-cx-dstmap-d": {
204 "b2ltZwkAAAA=": [{
205 "morphology_id": "projector",
206 "plasticity_flag": true,
207 "postSynapticCurrent_multiplier": 1
208 }]
209 }
210 }
211 });
212
213 assert!(genome_has_plasticity(&genome));
214 assert!(!has_memory_areas(&genome));
215 assert!(has_stdp_connections(&genome));
216 }
217
218 #[test]
219 fn test_has_both_plasticity_types() {
220 let genome = json!({
221 "blueprint": {
222 "_____10c-Y21fX19fXxg=-cx-memory-b": true,
223 "_____10c-Y2FfX19fX50=-cx-dstmap-d": {
224 "b2ltZwkAAAA=": [{
225 "morphology_id": "projector",
226 "plasticity_flag": true
227 }]
228 }
229 }
230 });
231
232 assert!(genome_has_plasticity(&genome));
233 assert!(has_memory_areas(&genome));
234 assert!(has_stdp_connections(&genome));
235 }
236
237 #[test]
238 fn test_extract_memory_properties() {
239 let mut properties = HashMap::new();
240 properties.insert("is_mem_type".to_string(), json!(true));
241 properties.insert("temporal_depth".to_string(), json!(5));
242 properties.insert("longterm_mem_threshold".to_string(), json!(200));
243 properties.insert("lifespan_growth_rate".to_string(), json!(1.5));
244 properties.insert("init_lifespan".to_string(), json!(15));
245
246 let mem_props = extract_memory_properties(&properties).expect("Should extract properties");
247 assert_eq!(mem_props.temporal_depth, 5);
248 assert_eq!(mem_props.longterm_threshold, 200);
249 assert_eq!(mem_props.lifespan_growth_rate, 1.5);
250 assert_eq!(mem_props.init_lifespan, 15);
251 }
252
253 #[test]
254 fn test_extract_memory_properties_defaults() {
255 let mut properties = HashMap::new();
256 properties.insert("is_mem_type".to_string(), json!(true));
257
258 let mem_props = extract_memory_properties(&properties).expect("Should extract properties");
259 assert_eq!(mem_props.temporal_depth, 1);
260 assert_eq!(mem_props.longterm_threshold, 100);
261 assert_eq!(mem_props.lifespan_growth_rate, 1.0);
262 assert_eq!(mem_props.init_lifespan, 9);
263 }
264
265 #[test]
266 fn test_extract_memory_properties_non_memory() {
267 let mut properties = HashMap::new();
268 properties.insert("is_mem_type".to_string(), json!(false));
269
270 assert!(extract_memory_properties(&properties).is_none());
271 }
272}