1pub 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#[derive(Debug, Error)]
84pub enum TemporalError {
85 #[error("Pattern not found: {0}")]
87 PatternNotFound(PatternId),
88
89 #[error("Invalid query: {0}")]
91 InvalidQuery(String),
92
93 #[error("Storage error: {0}")]
95 StorageError(String),
96}
97
98pub type Result<T> = std::result::Result<T, TemporalError>;
100
101#[derive(Debug, Clone)]
103pub struct TemporalConfig {
104 pub short_term: ShortTermConfig,
106 pub long_term: LongTermConfig,
108 pub consolidation: ConsolidationConfig,
110 pub prefetch_capacity: usize,
112 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
128pub struct TemporalMemory {
130 short_term: ShortTermBuffer,
132 long_term: LongTermStore,
134 causal_graph: CausalGraph,
136 prefetch_cache: PrefetchCache,
138 sequential_tracker: SequentialPatternTracker,
140 config: TemporalConfig,
142}
143
144impl TemporalMemory {
145 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 pub fn store(&self, pattern: Pattern, antecedents: &[PatternId]) -> Result<PatternId> {
159 let id = pattern.id;
160 let timestamp = pattern.timestamp;
161
162 let temporal_pattern = TemporalPattern::new(pattern);
164
165 self.short_term.insert(temporal_pattern);
167
168 self.causal_graph.add_pattern(id, timestamp);
170 for &antecedent in antecedents {
171 self.causal_graph.add_edge(antecedent, id);
172 }
173
174 if self.config.auto_consolidate && self.short_term.should_consolidate() {
176 self.consolidate();
177 }
178
179 Ok(id)
180 }
181
182 pub fn get(&self, id: &PatternId) -> Option<Pattern> {
184 if let Some(temporal_pattern) = self.short_term.get(id) {
186 return Some(temporal_pattern.pattern);
187 }
188
189 self.long_term.get(id).map(|tp| tp.pattern)
191 }
192
193 pub fn mark_accessed(&self, id: &PatternId) {
195 self.short_term.get_mut(id, |p| p.mark_accessed());
197
198 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 pub fn causal_query(
207 &self,
208 query: &Query,
209 reference_time: SubstrateTime,
210 cone_type: CausalConeType,
211 ) -> Vec<CausalResult> {
212 let time_range = match cone_type {
214 CausalConeType::Past => TimeRange::past(reference_time),
215 CausalConeType::Future => TimeRange::future(reference_time),
216 CausalConeType::LightCone { .. } => {
217 TimeRange::new(SubstrateTime::MIN, SubstrateTime::MAX)
220 }
221 };
222
223 let search_results = self.long_term.search_with_time_range(query, time_range);
225
226 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 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 let time_diff = (reference_time - temporal_pattern.pattern.timestamp).abs();
242 let temporal_distance_ns = time_diff.0;
243
244 const ALPHA: f32 = 0.5; const BETA: f32 = 0.25; const GAMMA: f32 = 0.25; let temporal_score = 1.0 / (1.0 + (temporal_distance_ns / 1_000_000_000) as f32); 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 results.sort_by(|a, b| b.combined_score.partial_cmp(&a.combined_score).unwrap());
269
270 results
271 }
272
273 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 pub fn check_cache(&self, query: &Query) -> Option<Vec<SearchResult>> {
286 self.prefetch_cache.get(query.hash())
287 }
288
289 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 pub fn forget(&self) {
301 self.long_term
302 .decay_low_salience(self.config.long_term.decay_rate);
303 }
304
305 pub fn causal_graph(&self) -> &CausalGraph {
307 &self.causal_graph
308 }
309
310 pub fn short_term(&self) -> &ShortTermBuffer {
312 &self.short_term
313 }
314
315 pub fn long_term(&self) -> &LongTermStore {
317 &self.long_term
318 }
319
320 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#[derive(Debug, Clone)]
339pub struct TemporalStats {
340 pub short_term: ShortTermStats,
342 pub long_term: LongTermStats,
344 pub causal_graph: CausalGraphStats,
346 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 let config = TemporalConfig {
377 consolidation: ConsolidationConfig {
378 salience_threshold: 0.0, ..Default::default()
380 },
381 ..Default::default()
382 };
383 let memory = TemporalMemory::new(config);
384
385 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 let result = memory.consolidate();
421 assert!(
422 result.num_consolidated >= 3,
423 "Should consolidate all patterns"
424 );
425
426 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, CausalConeType::Future,
432 );
433
434 assert!(
436 !results.is_empty(),
437 "Should find causal descendants in future cone"
438 );
439 }
440}