Skip to main content

ronn_memory/
lib.rs

1//! Multi-Tier Memory System - Brain-Inspired Memory Architecture
2//!
3//! Implements a three-tier memory system inspired by human cognition:
4//! - **Working Memory**: Short-term, attention-weighted storage
5//! - **Episodic Memory**: Experience storage with temporal/spatial indexing
6//! - **Semantic Memory**: Long-term knowledge graph
7//!
8//! Also includes a sleep consolidation engine for offline memory processing.
9//!
10//! ## Architecture
11//!
12//! ```text
13//! Input → Working Memory (Short-term, Limited Capacity)
14//!              ↓
15//!         [Attention Filter]
16//!              ↓
17//!         Episodic Memory (Experiences, Temporal Index)
18//!              ↓
19//!       [Sleep Consolidation]
20//!              ↓
21//!         Semantic Memory (Knowledge Graph, Long-term)
22//! ```
23
24pub mod consolidation;
25pub mod episodic;
26pub mod semantic;
27pub mod working;
28
29pub use consolidation::{ConsolidationConfig, SleepConsolidation};
30pub use episodic::{Episode, EpisodeQuery, EpisodicMemory};
31pub use semantic::{Concept, ConceptGraph, SemanticMemory};
32pub use working::{WorkingMemory, WorkingMemoryConfig};
33
34use ronn_core::tensor::Tensor;
35use std::time::{SystemTime, UNIX_EPOCH};
36use thiserror::Error;
37
38/// Errors that can occur in the memory system
39#[derive(Error, Debug)]
40pub enum MemoryError {
41    #[error("Working memory error: {0}")]
42    WorkingMemory(String),
43
44    #[error("Episodic memory error: {0}")]
45    EpisodicMemory(String),
46
47    #[error("Semantic memory error: {0}")]
48    SemanticMemory(String),
49
50    #[error("Consolidation error: {0}")]
51    Consolidation(String),
52
53    #[error("Core error: {0}")]
54    Core(#[from] ronn_core::error::CoreError),
55}
56
57pub type Result<T> = std::result::Result<T, MemoryError>;
58
59/// Main memory system coordinator
60pub struct MultiTierMemory {
61    working: WorkingMemory,
62    episodic: EpisodicMemory,
63    semantic: SemanticMemory,
64    consolidation: SleepConsolidation,
65}
66
67impl MultiTierMemory {
68    /// Create a new multi-tier memory system with default configuration
69    pub fn new() -> Self {
70        Self {
71            working: WorkingMemory::new(WorkingMemoryConfig::default()),
72            episodic: EpisodicMemory::new(),
73            semantic: SemanticMemory::new(),
74            consolidation: SleepConsolidation::new(ConsolidationConfig::default()),
75        }
76    }
77
78    /// Store data in working memory
79    pub fn store(&mut self, data: Tensor, importance: f64) -> Result<MemoryId> {
80        // Store in working memory
81        let id = self.working.store(data, importance)?;
82
83        // If important enough, also store in episodic memory
84        if importance > 0.7 {
85            let tensor = self.working.get(id)?;
86            let episode = Episode {
87                id,
88                data: tensor,
89                timestamp: current_timestamp(),
90                importance,
91            };
92            self.episodic.store_episode(episode)?;
93        }
94
95        Ok(id)
96    }
97
98    /// Retrieve data from memory hierarchy
99    pub fn retrieve(&self, id: MemoryId) -> Result<Option<Tensor>> {
100        // Try working memory first
101        if let Ok(tensor) = self.working.get(id) {
102            return Ok(Some(tensor));
103        }
104
105        // Try episodic memory
106        if let Some(episode) = self.episodic.get_episode(id) {
107            return Ok(Some(episode.data));
108        }
109
110        Ok(None)
111    }
112
113    /// Search memory by similarity
114    pub fn search_similar(&self, query: &Tensor, limit: usize) -> Result<Vec<MemoryId>> {
115        // For MVP: search working memory
116        self.working.search_similar(query, limit)
117    }
118
119    /// Run sleep consolidation to transfer memories
120    pub async fn consolidate(&mut self) -> Result<ConsolidationResult> {
121        // Transfer important memories from working to episodic
122        let working_items = self.working.drain_old_items()?;
123        let items_count = working_items.len();
124
125        for (id, tensor, importance) in working_items {
126            if importance > 0.5 {
127                let episode = Episode {
128                    id,
129                    data: tensor,
130                    timestamp: current_timestamp(),
131                    importance,
132                };
133                self.episodic.store_episode(episode)?;
134            }
135        }
136
137        // Extract patterns from episodic to semantic
138        let patterns = self.consolidation.extract_patterns(&self.episodic).await?;
139        let patterns_count = patterns.len();
140
141        for concept in patterns {
142            self.semantic.store_concept(concept)?;
143        }
144
145        Ok(ConsolidationResult {
146            episodes_consolidated: items_count,
147            patterns_extracted: patterns_count,
148        })
149    }
150
151    /// Get memory statistics
152    pub fn stats(&self) -> MemoryStats {
153        MemoryStats {
154            working_items: self.working.len(),
155            episodic_episodes: self.episodic.len(),
156            semantic_concepts: self.semantic.len(),
157        }
158    }
159}
160
161impl Default for MultiTierMemory {
162    fn default() -> Self {
163        Self::new()
164    }
165}
166
167/// Unique identifier for memory items
168pub type MemoryId = u64;
169
170/// Result of consolidation process
171#[derive(Debug, Clone)]
172pub struct ConsolidationResult {
173    pub episodes_consolidated: usize,
174    pub patterns_extracted: usize,
175}
176
177/// Statistics about the memory system
178#[derive(Debug, Clone)]
179pub struct MemoryStats {
180    pub working_items: usize,
181    pub episodic_episodes: usize,
182    pub semantic_concepts: usize,
183}
184
185/// Get current timestamp in milliseconds since Unix epoch
186pub fn current_timestamp() -> u64 {
187    SystemTime::now()
188        .duration_since(UNIX_EPOCH)
189        .unwrap()
190        .as_millis() as u64
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196    use ronn_core::types::{DataType, TensorLayout};
197
198    type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
199
200    #[test]
201    fn test_memory_creation() {
202        let memory = MultiTierMemory::new();
203        let stats = memory.stats();
204
205        assert_eq!(stats.working_items, 0);
206        assert_eq!(stats.episodic_episodes, 0);
207        assert_eq!(stats.semantic_concepts, 0);
208    }
209
210    #[test]
211    fn test_store_and_retrieve() -> Result<()> {
212        let mut memory = MultiTierMemory::new();
213
214        let data = vec![1.0f32, 2.0, 3.0, 4.0];
215        let tensor = Tensor::from_data(data, vec![1, 4], DataType::F32, TensorLayout::RowMajor)?;
216
217        // Store with low importance (stays in working memory)
218        let id = memory.store(tensor.clone(), 0.3)?;
219
220        // Should be able to retrieve
221        let retrieved = memory.retrieve(id)?;
222        assert!(retrieved.is_some());
223
224        Ok(())
225    }
226
227    #[test]
228    fn test_high_importance_storage() -> Result<()> {
229        let mut memory = MultiTierMemory::new();
230
231        let data = vec![1.0f32, 2.0, 3.0, 4.0];
232        let tensor = Tensor::from_data(data, vec![1, 4], DataType::F32, TensorLayout::RowMajor)?;
233
234        // Store with high importance (should go to episodic)
235        let id = memory.store(tensor, 0.9)?;
236
237        let stats = memory.stats();
238        assert_eq!(stats.working_items, 1);
239        assert_eq!(stats.episodic_episodes, 1); // Should also be in episodic
240
241        Ok(())
242    }
243
244    #[tokio::test]
245    async fn test_consolidation() -> Result<()> {
246        let mut memory = MultiTierMemory::new();
247
248        // Store several items
249        for i in 0..5 {
250            let data = vec![i as f32; 4];
251            let tensor =
252                Tensor::from_data(data, vec![1, 4], DataType::F32, TensorLayout::RowMajor)?;
253            memory.store(tensor, 0.6)?;
254        }
255
256        // Run consolidation (may or may not consolidate based on timing)
257        let result = memory.consolidate().await?;
258
259        // Consolidation completed successfully (even if no episodes were old enough)
260        assert!(result.episodes_consolidated >= 0);
261        assert!(result.patterns_extracted >= 0);
262
263        Ok(())
264    }
265}