use amari_holographic::BindingAlgebra;
use crate::error::MinuetResult;
pub trait MemoryTrace: Clone + Send + Sync {
type Algebra: BindingAlgebra;
fn dimension(&self) -> usize;
fn item_count(&self) -> usize;
fn is_empty(&self) -> bool {
self.item_count() == 0
}
fn theoretical_capacity(&self) -> usize {
let d = self.dimension() as f64;
(d / d.ln().max(1.0)) as usize
}
fn utilization(&self) -> f64 {
self.item_count() as f64 / self.theoretical_capacity().max(1) as f64
}
fn add(&mut self, item: &Self::Algebra, weight: f64);
fn add_unit(&mut self, item: &Self::Algebra) {
self.add(item, 1.0);
}
fn add_all<'a, I>(&mut self, items: I)
where
I: IntoIterator<Item = &'a Self::Algebra>,
Self::Algebra: 'a,
{
for item in items {
self.add_unit(item);
}
}
fn merge(&mut self, other: &Self, weight: f64);
fn clear(&mut self);
fn similarity(&self, query: &Self::Algebra) -> f64;
fn unbind(&self, query: &Self::Algebra) -> Self::Algebra;
fn as_algebra(&self) -> Self::Algebra;
fn estimated_snr(&self) -> f64 {
if self.item_count() == 0 {
return f64::INFINITY;
}
(self.dimension() as f64 / self.item_count() as f64).sqrt()
}
fn near_capacity(&self, threshold: f64) -> bool {
self.utilization() > threshold
}
}
pub trait MemoryStore: Send + Sync {
type Trace: MemoryTrace;
type Algebra: BindingAlgebra;
fn store(&self, key: &Self::Algebra, value: &Self::Algebra) -> MinuetResult<StoreReceipt>;
fn store_with_options(
&self,
key: &Self::Algebra,
value: &Self::Algebra,
options: StoreOptions,
) -> MinuetResult<StoreReceipt>;
fn store_batch(
&self,
pairs: &[(Self::Algebra, Self::Algebra)],
) -> MinuetResult<Vec<StoreReceipt>>;
fn retrieve(&self, key: &Self::Algebra) -> MinuetResult<RetrievalResult<Self::Algebra>>;
fn capacity_info(&self) -> CapacityInfo;
fn clear(&self) -> MinuetResult<()>;
fn trace_count(&self) -> usize;
fn total_items(&self) -> usize;
}
#[derive(Clone, Debug, Default)]
pub struct StoreOptions {
pub partition: Option<String>,
pub weight: f64,
pub source_id: Option<u64>,
pub force: bool,
}
impl StoreOptions {
pub fn new() -> Self {
Self {
weight: 1.0,
..Default::default()
}
}
pub fn partition(mut self, name: impl Into<String>) -> Self {
self.partition = Some(name.into());
self
}
pub fn weight(mut self, w: f64) -> Self {
self.weight = w;
self
}
pub fn source_id(mut self, id: u64) -> Self {
self.source_id = Some(id);
self
}
pub fn force(mut self) -> Self {
self.force = true;
self
}
}
#[derive(Clone, Debug)]
pub struct StoreReceipt {
pub id: u64,
pub post_snr: f64,
pub warning: Option<CapacityWarning>,
pub location: String,
}
#[derive(Clone, Debug)]
pub struct RetrievalResult<A> {
pub value: A,
pub confidence: f64,
pub attribution: Vec<(u64, f64)>,
}
#[derive(Clone, Debug)]
pub struct CapacityInfo {
pub total_items: usize,
pub theoretical_capacity: usize,
pub utilization: f64,
pub estimated_snr: f64,
pub per_trace: Vec<TraceCapacityInfo>,
}
#[derive(Clone, Debug)]
pub struct TraceCapacityInfo {
pub name: String,
pub items: usize,
pub capacity: usize,
pub utilization: f64,
}
#[derive(Clone, Debug)]
pub enum CapacityWarning {
ApproachingCapacity {
utilization: f64,
},
AtCapacity,
SNRDegraded {
snr: f64,
threshold: f64,
},
}
pub trait Retriever: Send + Sync {
type Algebra: BindingAlgebra;
fn cleanup(
&self,
raw: &Self::Algebra,
context: &RetrievalContext<Self::Algebra>,
) -> MinuetResult<CleanupResult<Self::Algebra>>;
fn cleanup_batch(
&self,
raws: &[Self::Algebra],
context: &RetrievalContext<Self::Algebra>,
) -> MinuetResult<Vec<CleanupResult<Self::Algebra>>> {
raws.iter().map(|r| self.cleanup(r, context)).collect()
}
}
#[derive(Clone)]
pub struct RetrievalContext<A> {
pub codebook: Option<Vec<A>>,
pub temperature: f64,
pub max_iterations: usize,
pub convergence_threshold: f64,
}
impl<A> Default for RetrievalContext<A> {
fn default() -> Self {
Self {
codebook: None,
temperature: f64::INFINITY, max_iterations: 50,
convergence_threshold: 0.999,
}
}
}
impl<A> RetrievalContext<A> {
pub fn with_codebook(mut self, codebook: Vec<A>) -> Self {
self.codebook = Some(codebook);
self
}
pub fn with_temperature(mut self, t: f64) -> Self {
self.temperature = t;
self
}
pub fn soft() -> Self {
Self {
temperature: 1.0,
..Default::default()
}
}
pub fn hard() -> Self {
Self {
temperature: f64::INFINITY,
..Default::default()
}
}
}
#[derive(Clone, Debug)]
pub struct CleanupResult<A> {
pub value: A,
pub confidence: f64,
pub iterations: usize,
pub converged: bool,
pub codebook_match: Option<usize>,
}
pub trait Encoder: Send + Sync {
type Input: ?Sized;
type Algebra: BindingAlgebra;
fn encode(&self, input: &Self::Input) -> MinuetResult<Self::Algebra>;
fn encode_batch<'a, I>(&self, inputs: I) -> MinuetResult<Vec<Self::Algebra>>
where
I: IntoIterator<Item = &'a Self::Input>,
Self::Input: 'a,
{
inputs.into_iter().map(|i| self.encode(i)).collect()
}
fn decode(&self, _repr: &Self::Algebra) -> MinuetResult<Option<Self::Input>>
where
Self::Input: Sized,
{
Ok(None) }
}
pub trait Codebook: Send + Sync {
type Algebra: BindingAlgebra;
fn symbol(&self, name: &str) -> Self::Algebra;
fn get(&self, name: &str) -> Option<Self::Algebra>;
fn contains(&self, name: &str) -> bool {
self.get(name).is_some()
}
fn register(&self, name: &str, repr: Self::Algebra) -> MinuetResult<()>;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn all_symbols(&self) -> Vec<Self::Algebra>;
fn all_names(&self) -> Vec<String>;
fn closest(&self, repr: &Self::Algebra) -> Option<(String, f64)>;
}
pub trait CapacityPolicy: Send + Sync {
fn can_accept(&self, info: &CapacityInfo) -> bool;
fn warning_threshold(&self) -> f64 {
0.8
}
fn critical_threshold(&self) -> f64 {
0.95
}
}
#[derive(Clone, Debug)]
pub enum PressureResponse {
Accepted,
Evicted {
count: usize,
},
Consolidated {
items_before: usize,
items_after: usize,
},
Rejected {
reason: String,
},
}