use crate::core::atom_memory::AtomMemory;
use crate::core::cognition::refiner::{DistributionalRefiner, RefinementReport, RefinerConfig};
use crate::core::composite_memory::CompositeMemory;
use crate::core::triple_store::TripleStore;
#[derive(Clone, Debug, Default)]
pub struct GovernanceReport {
pub composites_merged: usize,
pub composites_forgotten: usize,
pub atoms_forgotten: usize,
pub idf_refreshed: bool,
pub refinement: RefinementReport,
}
#[derive(Clone, Debug)]
pub struct GovernorConfig {
pub duplicate_threshold: f64,
pub max_scan_size: usize,
pub forget_unreferenced_atoms: bool,
pub refine_atoms: bool,
pub refiner: RefinerConfig,
}
impl Default for GovernorConfig {
fn default() -> Self {
Self {
duplicate_threshold: 0.95,
max_scan_size: 1000,
forget_unreferenced_atoms: false,
refine_atoms: false,
refiner: RefinerConfig::default(),
}
}
}
pub struct MemoryGovernor;
impl MemoryGovernor {
pub fn govern(
atom_memory: &AtomMemory,
composite_memory: &CompositeMemory,
triple_store: &TripleStore,
config: &GovernorConfig,
) -> GovernanceReport {
let composites_merged = Self::consolidate_composites(composite_memory, config);
let composites_forgotten = Self::forget_stale_composites(composite_memory, triple_store);
let atoms_forgotten = if config.forget_unreferenced_atoms {
Self::forget_unreferenced_atoms(atom_memory, triple_store)
} else {
0
};
let refinement = if config.refine_atoms {
DistributionalRefiner::refine(atom_memory, triple_store, &config.refiner)
} else {
RefinementReport::default()
};
Self::refresh_indices(atom_memory, composite_memory);
GovernanceReport {
composites_merged,
composites_forgotten,
atoms_forgotten,
idf_refreshed: true,
refinement,
}
}
pub fn consolidate_composites(
composite_memory: &CompositeMemory,
config: &GovernorConfig,
) -> usize {
if config.max_scan_size == 0
|| config.duplicate_threshold.is_nan()
|| config.duplicate_threshold > 1.0
{
return 0;
}
let all = composite_memory
.inner()
.vector_page(&mut 0, config.max_scan_size);
let positions: crate::core::hash::FastMap<_, _> = all
.iter()
.enumerate()
.map(|(i, (slot, _, _))| (*slot, i))
.collect();
let mut deleted = crate::core::hash::FastSet::default();
let mut merged = 0;
for (i, (slot, _, vector)) in all.iter().enumerate() {
if deleted.contains(slot) {
continue;
}
let candidates: Vec<usize> = if config.duplicate_threshold <= 0.0 {
(i + 1..all.len()).collect()
} else if vector.indices().is_empty() {
(i + 1..all.len())
.filter(|&j| all[j].2.indices().is_empty())
.collect()
} else {
composite_memory
.inner()
.overlapping_slots(vector, *slot, all.last().unwrap().0)
.into_iter()
.filter_map(|slot| positions.get(&slot).copied())
.filter(|&j| j > i)
.collect()
};
for j in candidates {
let (slot, id, other) = &all[j];
if !deleted.contains(slot)
&& vector.similarity(other) >= config.duplicate_threshold
&& composite_memory.delete(id)
{
deleted.insert(*slot);
merged += 1;
}
}
}
merged
}
pub fn forget_stale_composites(
composite_memory: &CompositeMemory,
triple_store: &TripleStore,
) -> usize {
Self::forget_unreferenced(composite_memory.inner(), |id| {
triple_store.has_composite(id)
})
}
pub fn forget_unreferenced_atoms(
atom_memory: &AtomMemory,
triple_store: &TripleStore,
) -> usize {
Self::forget_unreferenced(atom_memory.inner(), |id| {
triple_store.has(Some(id), None, None)
|| triple_store.has(None, Some(id), None)
|| triple_store.has(None, None, Some(id))
})
}
fn forget_unreferenced(
memory: &crate::core::indexed_memory::IndexedMemory,
referenced: impl Fn(&str) -> bool,
) -> usize {
let mut cursor = 0;
let mut forgotten = 0;
loop {
let page = memory.id_page(&mut cursor, 256);
if page.is_empty() {
break;
}
for id in page {
if !referenced(&id) && memory.delete(&id) {
forgotten += 1;
}
}
}
forgotten
}
pub fn refresh_indices(atom_memory: &AtomMemory, composite_memory: &CompositeMemory) {
atom_memory.rebuild_indices();
composite_memory.rebuild_indices();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::entangled::EntangledHVec;
#[test]
fn indexed_consolidation_matches_exhaustive_thresholds_and_scan_limits() {
let vectors: Vec<_> = [vec![], vec![], vec![1, 2], vec![1, 2], vec![2, 3], vec![9]]
.into_iter()
.map(|indices| EntangledHVec::from_indices(indices, 256))
.collect();
for threshold in [0.0, 0.3, 1.0, 1.1] {
for limit in [0, 3, 4, 5, 6, 7] {
let memory = CompositeMemory::new(256, 3.0);
memory.insert("deleted".into(), vectors[2].clone());
memory.delete("deleted");
for (i, vector) in vectors.iter().enumerate() {
memory.insert(i.to_string(), vector.clone());
}
let mut deleted = vec![false; vectors.len()];
let scan = limit.min(vectors.len());
for i in 0..scan {
if deleted[i] {
continue;
}
for j in i + 1..scan {
if !deleted[j] && vectors[i].similarity(&vectors[j]) >= threshold {
deleted[j] = true;
}
}
}
let config = GovernorConfig {
duplicate_threshold: threshold,
max_scan_size: limit,
..Default::default()
};
assert_eq!(
MemoryGovernor::consolidate_composites(&memory, &config),
deleted.iter().filter(|&&d| d).count()
);
let remaining: Vec<_> = memory
.inner()
.all_vectors()
.into_iter()
.map(|(_, id, _)| id)
.collect();
let expected: Vec<_> = deleted
.iter()
.enumerate()
.filter(|(_, d)| !**d)
.map(|(i, _)| i.to_string())
.collect();
assert_eq!(remaining, expected);
}
}
}
#[test]
fn test_consolidate_duplicates() {
let dim = 16384;
let comp_mem = CompositeMemory::new(dim, 3.0);
let v1 = EntangledHVec::new_deterministic(dim, 42);
comp_mem.insert("c1".to_string(), v1.clone());
comp_mem.insert("c2".to_string(), v1.clone());
let config = GovernorConfig {
duplicate_threshold: 0.95,
max_scan_size: 100,
..Default::default()
};
let merged = MemoryGovernor::consolidate_composites(&comp_mem, &config);
assert!(merged >= 1);
}
#[test]
fn test_forget_stale_composites() {
let dim = 16384;
let comp_mem = CompositeMemory::new(dim, 3.0);
let triple_store = TripleStore::new();
let v1 = EntangledHVec::new_deterministic(dim, 1);
let v2 = EntangledHVec::new_deterministic(dim, 2);
comp_mem.insert("c_live".to_string(), v1);
comp_mem.insert("c_stale".to_string(), v2);
triple_store.add("a", "r", "b", "c_live");
let forgotten = MemoryGovernor::forget_stale_composites(&comp_mem, &triple_store);
assert_eq!(forgotten, 1);
}
#[test]
fn test_forget_unreferenced_atoms() {
let dim = 16384;
let atom_mem = AtomMemory::new(dim, 3.0);
let triple_store = TripleStore::new();
atom_mem.get_or_insert("used_atom");
atom_mem.get_or_insert("orphan_atom");
triple_store.add("used_atom", "r", "x", "c1");
let forgotten = MemoryGovernor::forget_unreferenced_atoms(&atom_mem, &triple_store);
assert!(forgotten >= 1);
}
#[test]
fn test_full_govern_cycle() {
let dim = 16384;
let atom_mem = AtomMemory::new(dim, 3.0);
let comp_mem = CompositeMemory::new(dim, 3.0);
let triple_store = TripleStore::new();
atom_mem.get_or_insert("a");
let v = EntangledHVec::new_deterministic(dim, 1);
comp_mem.insert("c1".to_string(), v);
triple_store.add("a", "r", "b", "c1");
let config = GovernorConfig::default();
let report = MemoryGovernor::govern(&atom_mem, &comp_mem, &triple_store, &config);
assert!(report.idf_refreshed);
}
#[test]
fn test_govern_empty() {
let dim = 16384;
let atom_mem = AtomMemory::new(dim, 3.0);
let comp_mem = CompositeMemory::new(dim, 3.0);
let triple_store = TripleStore::new();
let config = GovernorConfig::default();
let report = MemoryGovernor::govern(&atom_mem, &comp_mem, &triple_store, &config);
assert_eq!(report.composites_merged, 0);
assert_eq!(report.composites_forgotten, 0);
assert!(report.idf_refreshed);
}
}