feagi_npu_plasticity/
memory_stats_cache.rs1use parking_lot::RwLock;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14use std::sync::Arc;
15
16#[cfg(feature = "std")]
17use feagi_state_manager::StateManager;
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct MemoryAreaStats {
22 pub neuron_count: usize,
24
25 pub created_total: usize,
27
28 pub deleted_total: usize,
30
31 pub last_updated: u64,
33}
34
35impl Default for MemoryAreaStats {
36 fn default() -> Self {
37 Self {
38 neuron_count: 0,
39 created_total: 0,
40 deleted_total: 0,
41 last_updated: std::time::SystemTime::now()
42 .duration_since(std::time::UNIX_EPOCH)
43 .unwrap()
44 .as_millis() as u64,
45 }
46 }
47}
48
49pub type MemoryStatsCache = Arc<RwLock<HashMap<String, MemoryAreaStats>>>;
53
54pub fn create_memory_stats_cache() -> MemoryStatsCache {
56 Arc::new(RwLock::new(HashMap::new()))
57}
58
59pub fn on_neuron_created(cache: &MemoryStatsCache, area_name: &str) {
61 let mut stats = cache.write();
62 let area_stats = stats.entry(area_name.to_string()).or_default();
63 area_stats.neuron_count += 1;
64 area_stats.created_total += 1;
65 area_stats.last_updated = std::time::SystemTime::now()
66 .duration_since(std::time::UNIX_EPOCH)
67 .unwrap()
68 .as_millis() as u64;
69
70 #[cfg(feature = "std")]
71 if let Some(state_manager) = StateManager::instance().try_read() {
72 state_manager.add_cortical_area_neuron_count(area_name, 1);
74 state_manager.get_core_state().add_neuron_count(1);
75 state_manager.get_core_state().add_memory_neuron_count(1);
76 }
77}
78
79pub fn on_neuron_deleted(cache: &MemoryStatsCache, area_name: &str) {
81 let mut stats = cache.write();
82 if let Some(area_stats) = stats.get_mut(area_name) {
83 area_stats.neuron_count = area_stats.neuron_count.saturating_sub(1);
84 area_stats.deleted_total += 1;
85 area_stats.last_updated = std::time::SystemTime::now()
86 .duration_since(std::time::UNIX_EPOCH)
87 .unwrap()
88 .as_millis() as u64;
89 }
90
91 #[cfg(feature = "std")]
92 if let Some(state_manager) = StateManager::instance().try_read() {
93 state_manager.subtract_cortical_area_neuron_count(area_name, 1);
95 state_manager.get_core_state().subtract_neuron_count(1);
96 state_manager
97 .get_core_state()
98 .subtract_memory_neuron_count(1);
99 }
100}
101
102pub fn init_memory_area(cache: &MemoryStatsCache, area_name: &str) {
104 let mut stats = cache.write();
105 stats.entry(area_name.to_string()).or_default();
106
107 #[cfg(feature = "std")]
108 if let Some(state_manager) = StateManager::instance().try_read() {
109 state_manager.init_cortical_area_stats(area_name);
111 }
112}
113
114pub fn remove_memory_area(cache: &MemoryStatsCache, area_name: &str) {
116 let mut stats = cache.write();
117 stats.remove(area_name);
118}
119
120pub fn get_stats_snapshot(cache: &MemoryStatsCache) -> HashMap<String, MemoryAreaStats> {
122 cache.read().clone()
123}
124
125pub fn get_area_stats(cache: &MemoryStatsCache, area_name: &str) -> Option<MemoryAreaStats> {
127 cache.read().get(area_name).cloned()
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use std::sync::Mutex;
134
135 static CORE_STATE_LOCK: Mutex<()> = Mutex::new(());
136
137 #[test]
138 fn test_memory_stats_creation() {
139 let _lock = CORE_STATE_LOCK.lock().unwrap();
140 let cache = create_memory_stats_cache();
141
142 on_neuron_created(&cache, "mem_00");
143 on_neuron_created(&cache, "mem_00");
144 on_neuron_created(&cache, "mem_01");
145
146 let snapshot = get_stats_snapshot(&cache);
147 assert_eq!(snapshot.len(), 2);
148 assert_eq!(snapshot.get("mem_00").unwrap().neuron_count, 2);
149 assert_eq!(snapshot.get("mem_01").unwrap().neuron_count, 1);
150 }
151
152 #[test]
153 fn test_memory_stats_deletion() {
154 let _lock = CORE_STATE_LOCK.lock().unwrap();
155 let cache = create_memory_stats_cache();
156
157 on_neuron_created(&cache, "mem_00");
158 on_neuron_created(&cache, "mem_00");
159 on_neuron_deleted(&cache, "mem_00");
160
161 let stats = get_area_stats(&cache, "mem_00").unwrap();
162 assert_eq!(stats.neuron_count, 1);
163 assert_eq!(stats.created_total, 2);
164 assert_eq!(stats.deleted_total, 1);
165 }
166
167 #[test]
168 fn test_memory_area_removal() {
169 let _lock = CORE_STATE_LOCK.lock().unwrap();
170 let cache = create_memory_stats_cache();
171
172 on_neuron_created(&cache, "mem_00");
173 remove_memory_area(&cache, "mem_00");
174
175 assert!(get_area_stats(&cache, "mem_00").is_none());
176 }
177
178 #[cfg(feature = "std")]
179 #[test]
180 fn test_memory_stats_updates_core_state_counts() {
181 let _lock = CORE_STATE_LOCK.lock().unwrap();
182 let cache = create_memory_stats_cache();
183 let state_manager = StateManager::instance();
184 let state_manager = state_manager.read();
185 let core_state = state_manager.get_core_state();
186 let start_total = core_state.get_neuron_count();
187 let start_memory = core_state.get_memory_neuron_count();
188
189 on_neuron_created(&cache, "mem_00");
190 assert_eq!(core_state.get_neuron_count(), start_total + 1);
191 assert_eq!(core_state.get_memory_neuron_count(), start_memory + 1);
192
193 on_neuron_deleted(&cache, "mem_00");
194 assert_eq!(core_state.get_neuron_count(), start_total);
195 assert_eq!(core_state.get_memory_neuron_count(), start_memory);
196 }
197}