use crate::Result;
use crate::episodic::{Episode, EpisodicMemory};
use crate::semantic::Concept;
use std::collections::HashSet;
#[derive(Debug, Clone)]
pub struct ConsolidationConfig {
pub importance_threshold: f64,
pub max_patterns: usize,
pub pattern_discovery_enabled: bool,
}
impl Default for ConsolidationConfig {
fn default() -> Self {
Self {
importance_threshold: 0.5,
max_patterns: 10,
pattern_discovery_enabled: true,
}
}
}
pub struct SleepConsolidation {
config: ConsolidationConfig,
cycles_run: u64,
}
impl SleepConsolidation {
pub fn new(config: ConsolidationConfig) -> Self {
Self {
config,
cycles_run: 0,
}
}
pub async fn extract_patterns(&mut self, episodic: &EpisodicMemory) -> Result<Vec<Concept>> {
self.cycles_run += 1;
if !self.config.pattern_discovery_enabled {
return Ok(Vec::new());
}
let episodes = episodic.all_episodes();
let important: Vec<&Episode> = episodes
.into_iter()
.filter(|ep| ep.importance >= self.config.importance_threshold)
.collect();
let mut patterns = Vec::new();
for (idx, episode) in important.iter().enumerate().take(self.config.max_patterns) {
let concept = Concept {
id: episode.id + 10000, name: format!("Pattern_{}", idx),
activation: episode.importance,
related_concepts: HashSet::new(),
};
patterns.push(concept);
}
Ok(patterns)
}
pub fn assess_importance(&self, episode: &Episode) -> f64 {
let base_importance = episode.importance;
base_importance
}
pub async fn consolidate_cycle(
&mut self,
episodic: &EpisodicMemory,
) -> Result<ConsolidationStats> {
let patterns = self.extract_patterns(episodic).await?;
let episodes_processed = episodic.len();
Ok(ConsolidationStats {
episodes_processed,
patterns_extracted: patterns.len(),
cycle_number: self.cycles_run,
})
}
pub fn cycles_run(&self) -> u64 {
self.cycles_run
}
}
#[derive(Debug, Clone)]
pub struct ConsolidationStats {
pub episodes_processed: usize,
pub patterns_extracted: usize,
pub cycle_number: u64,
}
#[cfg(test)]
mod tests {
use super::*;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
use crate::MemoryId;
use crate::current_timestamp;
use ronn_core::tensor::Tensor;
use ronn_core::types::{DataType, TensorLayout};
#[tokio::test]
async fn test_pattern_extraction() -> Result<()> {
let mut consolidation = SleepConsolidation::new(ConsolidationConfig::default());
let mut episodic = EpisodicMemory::new();
for i in 0..5 {
let data = vec![i as f32; 3];
let tensor =
Tensor::from_data(data, vec![1, 3], DataType::F32, TensorLayout::RowMajor)?;
let episode = Episode {
id: i as MemoryId,
data: tensor,
timestamp: current_timestamp(),
importance: 0.6 + (i as f64 * 0.1),
};
episodic.store_episode(episode)?;
}
let patterns = consolidation.extract_patterns(&episodic).await?;
assert!(!patterns.is_empty());
assert_eq!(consolidation.cycles_run(), 1);
Ok(())
}
#[tokio::test]
async fn test_full_cycle() -> Result<()> {
let mut consolidation = SleepConsolidation::new(ConsolidationConfig::default());
let mut episodic = EpisodicMemory::new();
for i in 0..3 {
let data = vec![i as f32; 2];
let tensor =
Tensor::from_data(data, vec![1, 2], DataType::F32, TensorLayout::RowMajor)?;
let episode = Episode {
id: i as MemoryId,
data: tensor,
timestamp: current_timestamp(),
importance: 0.7,
};
episodic.store_episode(episode)?;
}
let stats = consolidation.consolidate_cycle(&episodic).await?;
assert_eq!(stats.episodes_processed, 3);
assert!(stats.patterns_extracted > 0);
assert_eq!(stats.cycle_number, 1);
Ok(())
}
#[test]
fn test_importance_assessment() -> Result<()> {
let consolidation = SleepConsolidation::new(ConsolidationConfig::default());
let data = vec![1.0f32, 2.0, 3.0];
let tensor = Tensor::from_data(data, vec![1, 3], DataType::F32, TensorLayout::RowMajor)?;
let episode = Episode {
id: 1,
data: tensor,
timestamp: current_timestamp(),
importance: 0.8,
};
let assessed = consolidation.assess_importance(&episode);
assert_eq!(assessed, 0.8);
Ok(())
}
}