pub mod anticipation;
pub mod causal;
pub mod consolidation;
pub mod long_term;
pub mod quantum_decay;
pub mod short_term;
pub mod transfer_timeline;
pub mod types;
pub use anticipation::{
anticipate, AnticipationHint, PrefetchCache, SequentialPatternTracker, TemporalPhase,
};
pub use causal::{CausalConeType, CausalGraph, CausalGraphStats};
pub use consolidation::{
compute_salience, compute_salience_batch, consolidate, ConsolidationConfig,
ConsolidationResult, ConsolidationStats,
};
pub use long_term::{LongTermConfig, LongTermStats, LongTermStore};
pub use quantum_decay::{PatternDecoherence, QuantumDecayPool};
pub use short_term::{ShortTermBuffer, ShortTermConfig, ShortTermStats};
pub use types::*;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum TemporalError {
#[error("Pattern not found: {0}")]
PatternNotFound(PatternId),
#[error("Invalid query: {0}")]
InvalidQuery(String),
#[error("Storage error: {0}")]
StorageError(String),
}
pub type Result<T> = std::result::Result<T, TemporalError>;
#[derive(Debug, Clone)]
pub struct TemporalConfig {
pub short_term: ShortTermConfig,
pub long_term: LongTermConfig,
pub consolidation: ConsolidationConfig,
pub prefetch_capacity: usize,
pub auto_consolidate: bool,
}
impl Default for TemporalConfig {
fn default() -> Self {
Self {
short_term: ShortTermConfig::default(),
long_term: LongTermConfig::default(),
consolidation: ConsolidationConfig::default(),
prefetch_capacity: 1000,
auto_consolidate: true,
}
}
}
pub struct TemporalMemory {
short_term: ShortTermBuffer,
long_term: LongTermStore,
causal_graph: CausalGraph,
prefetch_cache: PrefetchCache,
sequential_tracker: SequentialPatternTracker,
config: TemporalConfig,
}
impl TemporalMemory {
pub fn new(config: TemporalConfig) -> Self {
Self {
short_term: ShortTermBuffer::new(config.short_term.clone()),
long_term: LongTermStore::new(config.long_term.clone()),
causal_graph: CausalGraph::new(),
prefetch_cache: PrefetchCache::new(config.prefetch_capacity),
sequential_tracker: SequentialPatternTracker::new(),
config,
}
}
pub fn store(&self, pattern: Pattern, antecedents: &[PatternId]) -> Result<PatternId> {
let id = pattern.id;
let timestamp = pattern.timestamp;
let temporal_pattern = TemporalPattern::new(pattern);
self.short_term.insert(temporal_pattern);
self.causal_graph.add_pattern(id, timestamp);
for &antecedent in antecedents {
self.causal_graph.add_edge(antecedent, id);
}
if self.config.auto_consolidate && self.short_term.should_consolidate() {
self.consolidate();
}
Ok(id)
}
pub fn get(&self, id: &PatternId) -> Option<Pattern> {
if let Some(temporal_pattern) = self.short_term.get(id) {
return Some(temporal_pattern.pattern);
}
self.long_term.get(id).map(|tp| tp.pattern)
}
pub fn mark_accessed(&self, id: &PatternId) {
self.short_term.get_mut(id, |p| p.mark_accessed());
if let Some(mut temporal_pattern) = self.long_term.get(id) {
temporal_pattern.mark_accessed();
self.long_term.update(temporal_pattern);
}
}
pub fn causal_query(
&self,
query: &Query,
reference_time: SubstrateTime,
cone_type: CausalConeType,
) -> Vec<CausalResult> {
let time_range = match cone_type {
CausalConeType::Past => TimeRange::past(reference_time),
CausalConeType::Future => TimeRange::future(reference_time),
CausalConeType::LightCone { .. } => {
TimeRange::new(SubstrateTime::MIN, SubstrateTime::MAX)
}
};
let search_results = self.long_term.search_with_time_range(query, time_range);
let mut results = Vec::new();
for search_result in search_results {
let temporal_pattern = search_result.pattern;
let similarity = search_result.score;
let causal_distance = if let Some(origin) = query.origin {
self.causal_graph.distance(origin, temporal_pattern.id())
} else {
None
};
let time_diff = (reference_time - temporal_pattern.pattern.timestamp).abs();
let temporal_distance_ns = time_diff.0;
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 {
1.0 / (1.0 + dist as f32)
} else {
0.0
};
let combined_score = ALPHA * similarity + BETA * temporal_score + GAMMA * causal_score;
results.push(CausalResult {
pattern: temporal_pattern,
similarity,
causal_distance,
temporal_distance_ns,
combined_score,
});
}
results.sort_by(|a, b| b.combined_score.partial_cmp(&a.combined_score).unwrap());
results
}
pub fn anticipate(&self, hints: &[AnticipationHint]) {
anticipate(
hints,
&self.long_term,
&self.causal_graph,
&self.prefetch_cache,
&self.sequential_tracker,
);
}
pub fn check_cache(&self, query: &Query) -> Option<Vec<SearchResult>> {
self.prefetch_cache.get(query.hash())
}
pub fn consolidate(&self) -> ConsolidationResult {
consolidate(
&self.short_term,
&self.long_term,
&self.causal_graph,
&self.config.consolidation,
)
}
pub fn forget(&self) {
self.long_term
.decay_low_salience(self.config.long_term.decay_rate);
}
pub fn causal_graph(&self) -> &CausalGraph {
&self.causal_graph
}
pub fn short_term(&self) -> &ShortTermBuffer {
&self.short_term
}
pub fn long_term(&self) -> &LongTermStore {
&self.long_term
}
pub fn stats(&self) -> TemporalStats {
TemporalStats {
short_term: self.short_term.stats(),
long_term: self.long_term.stats(),
causal_graph: self.causal_graph.stats(),
prefetch_cache_size: self.prefetch_cache.len(),
}
}
}
impl Default for TemporalMemory {
fn default() -> Self {
Self::new(TemporalConfig::default())
}
}
#[derive(Debug, Clone)]
pub struct TemporalStats {
pub short_term: ShortTermStats,
pub long_term: LongTermStats,
pub causal_graph: CausalGraphStats,
pub prefetch_cache_size: usize,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_temporal_memory() {
let memory = TemporalMemory::default();
let pattern = Pattern {
id: PatternId::new(),
embedding: vec![1.0, 2.0, 3.0],
metadata: Metadata::default(),
timestamp: SubstrateTime::now(),
antecedents: Vec::new(),
salience: 1.0,
};
let id = pattern.id;
memory.store(pattern, &[]).unwrap();
assert!(memory.get(&id).is_some());
}
#[test]
fn test_causal_query() {
let config = TemporalConfig {
consolidation: ConsolidationConfig {
salience_threshold: 0.0, ..Default::default()
},
..Default::default()
};
let memory = TemporalMemory::new(config);
let t1 = SubstrateTime::now();
let p1 = Pattern {
id: PatternId::new(),
embedding: vec![1.0, 0.0, 0.0],
metadata: Metadata::default(),
timestamp: t1,
antecedents: Vec::new(),
salience: 1.0,
};
let id1 = p1.id;
memory.store(p1, &[]).unwrap();
let p2 = Pattern {
id: PatternId::new(),
embedding: vec![0.9, 0.1, 0.0],
metadata: Metadata::default(),
timestamp: SubstrateTime::now(),
antecedents: Vec::new(),
salience: 1.0,
};
let id2 = p2.id;
memory.store(p2, &[id1]).unwrap();
let p3 = Pattern {
id: PatternId::new(),
embedding: vec![0.8, 0.2, 0.0],
metadata: Metadata::default(),
timestamp: SubstrateTime::now(),
antecedents: Vec::new(),
salience: 1.0,
};
memory.store(p3, &[id2]).unwrap();
let result = memory.consolidate();
assert!(
result.num_consolidated >= 3,
"Should consolidate all patterns"
);
let query = Query::from_embedding(vec![1.0, 0.0, 0.0]).with_origin(id1);
let results = memory.causal_query(
&query,
t1, CausalConeType::Future,
);
assert!(
!results.is_empty(),
"Should find causal descendants in future cone"
);
}
}