Skip to main content

exo_temporal/
lib.rs

1//! # exo-temporal: Temporal Memory Coordinator
2//!
3//! Causal memory coordination for the EXO-AI cognitive substrate.
4//!
5//! This crate implements temporal memory with:
6//! - Short-term volatile buffer
7//! - Long-term consolidated store
8//! - Causal graph tracking antecedent relationships
9//! - Memory consolidation with salience-based filtering
10//! - Predictive anticipation and pre-fetching
11//!
12//! ## Architecture
13//!
14//! ```text
15//! ┌─────────────────────────────────────────────────────────┐
16//! │                  TemporalMemory                         │
17//! ├─────────────────────────────────────────────────────────┤
18//! │ ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │
19//! │ │ Short-Term  │  │ Long-Term   │  │   Causal    │      │
20//! │ │   Buffer    │→ │    Store    │  │    Graph    │      │
21//! │ └─────────────┘  └─────────────┘  └─────────────┘      │
22//! │        ↓                ↑                 ↑             │
23//! │ ┌─────────────────────────────────────────────┐         │
24//! │ │          Consolidation Engine               │         │
25//! │ │  (Salience computation & filtering)         │         │
26//! │ └─────────────────────────────────────────────┘         │
27//! │        ↓                                                │
28//! │ ┌─────────────────────────────────────────────┐         │
29//! │ │       Anticipation & Prefetch               │         │
30//! │ └─────────────────────────────────────────────┘         │
31//! └─────────────────────────────────────────────────────────┘
32//! ```
33//!
34//! ## Example
35//!
36//! ```rust,ignore
37//! use exo_temporal::{TemporalMemory, TemporalConfig};
38//! use exo_core::Pattern;
39//!
40//! // Create temporal memory
41//! let memory = TemporalMemory::new(TemporalConfig::default());
42//!
43//! // Store pattern with causal context
44//! let pattern = Pattern::new(vec![1.0, 2.0, 3.0], metadata);
45//! let id = memory.store(pattern, &[]).unwrap();
46//!
47//! // Causal query
48//! let results = memory.causal_query(
49//!     &query,
50//!     reference_time,
51//!     CausalConeType::Past,
52//! );
53//!
54//! // Trigger consolidation
55//! memory.consolidate();
56//! ```
57
58pub mod anticipation;
59pub mod causal;
60pub mod consolidation;
61pub mod long_term;
62pub mod quantum_decay;
63pub mod short_term;
64pub mod transfer_timeline;
65pub mod types;
66
67pub use anticipation::{
68    anticipate, AnticipationHint, PrefetchCache, SequentialPatternTracker, TemporalPhase,
69};
70pub use causal::{CausalConeType, CausalGraph, CausalGraphStats};
71pub use consolidation::{
72    compute_salience, compute_salience_batch, consolidate, ConsolidationConfig,
73    ConsolidationResult, ConsolidationStats,
74};
75pub use long_term::{LongTermConfig, LongTermStats, LongTermStore};
76pub use quantum_decay::{PatternDecoherence, QuantumDecayPool};
77pub use short_term::{ShortTermBuffer, ShortTermConfig, ShortTermStats};
78pub use types::*;
79
80use thiserror::Error;
81
82/// Error type for temporal memory operations
83#[derive(Debug, Error)]
84pub enum TemporalError {
85    /// Pattern not found
86    #[error("Pattern not found: {0}")]
87    PatternNotFound(PatternId),
88
89    /// Invalid query
90    #[error("Invalid query: {0}")]
91    InvalidQuery(String),
92
93    /// Storage error
94    #[error("Storage error: {0}")]
95    StorageError(String),
96}
97
98/// Result type for temporal operations
99pub type Result<T> = std::result::Result<T, TemporalError>;
100
101/// Configuration for temporal memory
102#[derive(Debug, Clone)]
103pub struct TemporalConfig {
104    /// Short-term buffer configuration
105    pub short_term: ShortTermConfig,
106    /// Long-term store configuration
107    pub long_term: LongTermConfig,
108    /// Consolidation configuration
109    pub consolidation: ConsolidationConfig,
110    /// Prefetch cache capacity
111    pub prefetch_capacity: usize,
112    /// Auto-consolidation enabled
113    pub auto_consolidate: bool,
114}
115
116impl Default for TemporalConfig {
117    fn default() -> Self {
118        Self {
119            short_term: ShortTermConfig::default(),
120            long_term: LongTermConfig::default(),
121            consolidation: ConsolidationConfig::default(),
122            prefetch_capacity: 1000,
123            auto_consolidate: true,
124        }
125    }
126}
127
128/// Temporal memory coordinator
129pub struct TemporalMemory {
130    /// Short-term volatile memory
131    short_term: ShortTermBuffer,
132    /// Long-term consolidated memory
133    long_term: LongTermStore,
134    /// Causal graph tracking antecedent relationships
135    causal_graph: CausalGraph,
136    /// Prefetch cache for anticipated queries
137    prefetch_cache: PrefetchCache,
138    /// Sequential pattern tracker
139    sequential_tracker: SequentialPatternTracker,
140    /// Configuration
141    config: TemporalConfig,
142}
143
144impl TemporalMemory {
145    /// Create new temporal memory
146    pub fn new(config: TemporalConfig) -> Self {
147        Self {
148            short_term: ShortTermBuffer::new(config.short_term.clone()),
149            long_term: LongTermStore::new(config.long_term.clone()),
150            causal_graph: CausalGraph::new(),
151            prefetch_cache: PrefetchCache::new(config.prefetch_capacity),
152            sequential_tracker: SequentialPatternTracker::new(),
153            config,
154        }
155    }
156
157    /// Store pattern with causal context
158    pub fn store(&self, pattern: Pattern, antecedents: &[PatternId]) -> Result<PatternId> {
159        let id = pattern.id;
160        let timestamp = pattern.timestamp;
161
162        // Wrap in TemporalPattern
163        let temporal_pattern = TemporalPattern::new(pattern);
164
165        // Add to short-term buffer
166        self.short_term.insert(temporal_pattern);
167
168        // Record causal relationships
169        self.causal_graph.add_pattern(id, timestamp);
170        for &antecedent in antecedents {
171            self.causal_graph.add_edge(antecedent, id);
172        }
173
174        // Auto-consolidate if needed
175        if self.config.auto_consolidate && self.short_term.should_consolidate() {
176            self.consolidate();
177        }
178
179        Ok(id)
180    }
181
182    /// Retrieve pattern by ID
183    pub fn get(&self, id: &PatternId) -> Option<Pattern> {
184        // Check short-term first
185        if let Some(temporal_pattern) = self.short_term.get(id) {
186            return Some(temporal_pattern.pattern);
187        }
188
189        // Check long-term
190        self.long_term.get(id).map(|tp| tp.pattern)
191    }
192
193    /// Update pattern access tracking
194    pub fn mark_accessed(&self, id: &PatternId) {
195        // Update in short-term if present
196        self.short_term.get_mut(id, |p| p.mark_accessed());
197
198        // Update in long-term if present
199        if let Some(mut temporal_pattern) = self.long_term.get(id) {
200            temporal_pattern.mark_accessed();
201            self.long_term.update(temporal_pattern);
202        }
203    }
204
205    /// Causal cone query: retrieve within light-cone constraints
206    pub fn causal_query(
207        &self,
208        query: &Query,
209        reference_time: SubstrateTime,
210        cone_type: CausalConeType,
211    ) -> Vec<CausalResult> {
212        // Determine time range based on cone type
213        let time_range = match cone_type {
214            CausalConeType::Past => TimeRange::past(reference_time),
215            CausalConeType::Future => TimeRange::future(reference_time),
216            CausalConeType::LightCone { .. } => {
217                // Simplified: use full range for now
218                // In full implementation, would compute relativistic constraint
219                TimeRange::new(SubstrateTime::MIN, SubstrateTime::MAX)
220            }
221        };
222
223        // Search long-term with temporal filter
224        let search_results = self.long_term.search_with_time_range(query, time_range);
225
226        // Compute causal and temporal distances
227        let mut results = Vec::new();
228
229        for search_result in search_results {
230            let temporal_pattern = search_result.pattern;
231            let similarity = search_result.score;
232
233            // Causal distance
234            let causal_distance = if let Some(origin) = query.origin {
235                self.causal_graph.distance(origin, temporal_pattern.id())
236            } else {
237                None
238            };
239
240            // Temporal distance (in nanoseconds)
241            let time_diff = (reference_time - temporal_pattern.pattern.timestamp).abs();
242            let temporal_distance_ns = time_diff.0;
243
244            // Combined score (weighted combination)
245            const ALPHA: f32 = 0.5; // Similarity weight
246            const BETA: f32 = 0.25; // Temporal weight
247            const GAMMA: f32 = 0.25; // Causal weight
248
249            let temporal_score = 1.0 / (1.0 + (temporal_distance_ns / 1_000_000_000) as f32); // Convert to seconds
250            let causal_score = if let Some(dist) = causal_distance {
251                1.0 / (1.0 + dist as f32)
252            } else {
253                0.0
254            };
255
256            let combined_score = ALPHA * similarity + BETA * temporal_score + GAMMA * causal_score;
257
258            results.push(CausalResult {
259                pattern: temporal_pattern,
260                similarity,
261                causal_distance,
262                temporal_distance_ns,
263                combined_score,
264            });
265        }
266
267        // Sort by combined score
268        results.sort_by(|a, b| b.combined_score.partial_cmp(&a.combined_score).unwrap());
269
270        results
271    }
272
273    /// Anticipatory pre-fetch for predictive retrieval
274    pub fn anticipate(&self, hints: &[AnticipationHint]) {
275        anticipate(
276            hints,
277            &self.long_term,
278            &self.causal_graph,
279            &self.prefetch_cache,
280            &self.sequential_tracker,
281        );
282    }
283
284    /// Check prefetch cache for query
285    pub fn check_cache(&self, query: &Query) -> Option<Vec<SearchResult>> {
286        self.prefetch_cache.get(query.hash())
287    }
288
289    /// Memory consolidation: short-term -> long-term
290    pub fn consolidate(&self) -> ConsolidationResult {
291        consolidate(
292            &self.short_term,
293            &self.long_term,
294            &self.causal_graph,
295            &self.config.consolidation,
296        )
297    }
298
299    /// Strategic forgetting in long-term memory
300    pub fn forget(&self) {
301        self.long_term
302            .decay_low_salience(self.config.long_term.decay_rate);
303    }
304
305    /// Get causal graph reference
306    pub fn causal_graph(&self) -> &CausalGraph {
307        &self.causal_graph
308    }
309
310    /// Get short-term buffer reference
311    pub fn short_term(&self) -> &ShortTermBuffer {
312        &self.short_term
313    }
314
315    /// Get long-term store reference
316    pub fn long_term(&self) -> &LongTermStore {
317        &self.long_term
318    }
319
320    /// Get statistics
321    pub fn stats(&self) -> TemporalStats {
322        TemporalStats {
323            short_term: self.short_term.stats(),
324            long_term: self.long_term.stats(),
325            causal_graph: self.causal_graph.stats(),
326            prefetch_cache_size: self.prefetch_cache.len(),
327        }
328    }
329}
330
331impl Default for TemporalMemory {
332    fn default() -> Self {
333        Self::new(TemporalConfig::default())
334    }
335}
336
337/// Temporal memory statistics
338#[derive(Debug, Clone)]
339pub struct TemporalStats {
340    /// Short-term buffer stats
341    pub short_term: ShortTermStats,
342    /// Long-term store stats
343    pub long_term: LongTermStats,
344    /// Causal graph stats
345    pub causal_graph: CausalGraphStats,
346    /// Prefetch cache size
347    pub prefetch_cache_size: usize,
348}
349
350#[cfg(test)]
351mod tests {
352    use super::*;
353
354    #[test]
355    fn test_temporal_memory() {
356        let memory = TemporalMemory::default();
357
358        let pattern = Pattern {
359            id: PatternId::new(),
360            embedding: vec![1.0, 2.0, 3.0],
361            metadata: Metadata::default(),
362            timestamp: SubstrateTime::now(),
363            antecedents: Vec::new(),
364            salience: 1.0,
365        };
366        let id = pattern.id;
367
368        memory.store(pattern, &[]).unwrap();
369
370        assert!(memory.get(&id).is_some());
371    }
372
373    #[test]
374    fn test_causal_query() {
375        // Use low salience threshold to ensure all patterns are consolidated
376        let config = TemporalConfig {
377            consolidation: ConsolidationConfig {
378                salience_threshold: 0.0, // Accept all patterns
379                ..Default::default()
380            },
381            ..Default::default()
382        };
383        let memory = TemporalMemory::new(config);
384
385        // Create causal chain: p1 -> p2 -> p3
386        let t1 = SubstrateTime::now();
387        let p1 = Pattern {
388            id: PatternId::new(),
389            embedding: vec![1.0, 0.0, 0.0],
390            metadata: Metadata::default(),
391            timestamp: t1,
392            antecedents: Vec::new(),
393            salience: 1.0,
394        };
395        let id1 = p1.id;
396        memory.store(p1, &[]).unwrap();
397
398        let p2 = Pattern {
399            id: PatternId::new(),
400            embedding: vec![0.9, 0.1, 0.0],
401            metadata: Metadata::default(),
402            timestamp: SubstrateTime::now(),
403            antecedents: Vec::new(),
404            salience: 1.0,
405        };
406        let id2 = p2.id;
407        memory.store(p2, &[id1]).unwrap();
408
409        let p3 = Pattern {
410            id: PatternId::new(),
411            embedding: vec![0.8, 0.2, 0.0],
412            metadata: Metadata::default(),
413            timestamp: SubstrateTime::now(),
414            antecedents: Vec::new(),
415            salience: 1.0,
416        };
417        memory.store(p3, &[id2]).unwrap();
418
419        // Consolidate to long-term
420        let result = memory.consolidate();
421        assert!(
422            result.num_consolidated >= 3,
423            "Should consolidate all patterns"
424        );
425
426        // Query with causal context - use p1's timestamp as reference for future cone
427        let query = Query::from_embedding(vec![1.0, 0.0, 0.0]).with_origin(id1);
428        let results = memory.causal_query(
429            &query,
430            t1, // Use p1's timestamp as reference, so p2 and p3 are in the future
431            CausalConeType::Future,
432        );
433
434        // Should find patterns in the causal future of p1
435        assert!(
436            !results.is_empty(),
437            "Should find causal descendants in future cone"
438        );
439    }
440}