exo_manifold/
lib.rs

1//! Learned Manifold Engine for EXO-AI Cognitive Substrate
2//!
3//! This crate implements a simplified manifold storage system.
4//! The burn dependency has been removed to avoid bincode version conflicts.
5//!
6//! # Key Concepts
7//!
8//! - **Retrieval**: Vector similarity search
9//! - **Storage**: Pattern storage with embeddings
10//! - **Forgetting**: Strategic pattern pruning
11//!
12//! # Architecture
13//!
14//! ```text
15//! Query → Vector Search → Nearest Patterns
16//!            ↓
17//!      Pattern Storage
18//!      (Vec-based)
19//!            ↓
20//!      Similarity Scores
21//! ```
22
23use exo_core::{Error, ManifoldConfig, ManifoldDelta, Pattern, Result, SearchResult};
24use parking_lot::RwLock;
25use std::sync::Arc;
26
27mod network;
28mod retrieval;
29mod deformation;
30mod forgetting;
31
32pub use network::LearnedManifold;
33pub use retrieval::GradientDescentRetriever;
34pub use deformation::ManifoldDeformer;
35pub use forgetting::StrategicForgetting;
36
37/// Simplified manifold storage using vector similarity
38pub struct ManifoldEngine {
39    /// Simple pattern storage
40    network: Arc<RwLock<LearnedManifold>>,
41    /// Configuration
42    config: ManifoldConfig,
43    /// Stored patterns (for extraction)
44    patterns: Arc<RwLock<Vec<Pattern>>>,
45}
46
47impl ManifoldEngine {
48    /// Create a new manifold engine
49    pub fn new(config: ManifoldConfig) -> Self {
50        let network = LearnedManifold::new(
51            config.dimension,
52            config.hidden_dim,
53            config.hidden_layers,
54        );
55
56        Self {
57            network: Arc::new(RwLock::new(network)),
58            config,
59            patterns: Arc::new(RwLock::new(Vec::new())),
60        }
61    }
62
63    /// Query manifold via vector similarity
64    pub fn retrieve(&self, query: &[f32], k: usize) -> Result<Vec<SearchResult>> {
65        if query.len() != self.config.dimension {
66            return Err(Error::InvalidDimension {
67                expected: self.config.dimension,
68                got: query.len(),
69            });
70        }
71
72        let retriever = GradientDescentRetriever::new(
73            self.network.clone(),
74            self.config.clone(),
75        );
76
77        retriever.retrieve(query, k, &self.patterns)
78    }
79
80    /// Store pattern (simplified deformation)
81    pub fn deform(&mut self, pattern: Pattern, salience: f32) -> Result<ManifoldDelta> {
82        if pattern.embedding.len() != self.config.dimension {
83            return Err(Error::InvalidDimension {
84                expected: self.config.dimension,
85                got: pattern.embedding.len(),
86            });
87        }
88
89        // Store pattern for later extraction
90        self.patterns.write().push(pattern.clone());
91
92        let mut deformer = ManifoldDeformer::new(
93            self.network.clone(),
94            self.config.learning_rate,
95        );
96
97        deformer.deform(&pattern, salience)
98    }
99
100    /// Strategic forgetting via pattern pruning
101    pub fn forget(&mut self, salience_threshold: f32, decay_rate: f32) -> Result<usize> {
102        let forgetter = StrategicForgetting::new(self.network.clone());
103
104        forgetter.forget(
105            &self.patterns,
106            salience_threshold,
107            decay_rate,
108        )
109    }
110
111    /// Get number of stored patterns
112    pub fn len(&self) -> usize {
113        self.patterns.read().len()
114    }
115
116    /// Check if engine is empty
117    pub fn is_empty(&self) -> bool {
118        self.patterns.read().is_empty()
119    }
120
121    /// Get configuration
122    pub fn config(&self) -> &ManifoldConfig {
123        &self.config
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130    use exo_core::{Metadata, PatternId, SubstrateTime};
131
132    fn create_test_pattern(embedding: Vec<f32>, salience: f32) -> Pattern {
133        Pattern {
134            id: PatternId::new(),
135            embedding,
136            metadata: Metadata::default(),
137            timestamp: SubstrateTime::now(),
138            antecedents: vec![],
139            salience,
140        }
141    }
142
143    #[test]
144    fn test_manifold_engine_creation() {
145        let config = ManifoldConfig {
146            dimension: 128,
147            ..Default::default()
148        };
149        let engine = ManifoldEngine::new(config);
150
151        assert_eq!(engine.len(), 0);
152        assert!(engine.is_empty());
153        assert_eq!(engine.config().dimension, 128);
154    }
155
156    #[test]
157    fn test_deform_and_retrieve() {
158        let config = ManifoldConfig {
159            dimension: 64,
160            max_descent_steps: 10,
161            learning_rate: 0.01,
162            ..Default::default()
163        };
164        let mut engine = ManifoldEngine::new(config);
165
166        // Create and deform with a pattern
167        let embedding = vec![1.0; 64];
168        let pattern = create_test_pattern(embedding.clone(), 0.9);
169
170        let result = engine.deform(pattern, 0.9);
171        assert!(result.is_ok());
172        assert_eq!(engine.len(), 1);
173
174        // Retrieve similar patterns
175        let results = engine.retrieve(&embedding, 1);
176        assert!(results.is_ok());
177    }
178
179    #[test]
180    fn test_invalid_dimension() {
181        let config = ManifoldConfig {
182            dimension: 128,
183            ..Default::default()
184        };
185        let mut engine = ManifoldEngine::new(config);
186
187        // Wrong dimension
188        let embedding = vec![1.0; 64];
189        let pattern = create_test_pattern(embedding.clone(), 0.9);
190
191        let result = engine.deform(pattern, 0.9);
192        assert!(result.is_err());
193
194        let retrieve_result = engine.retrieve(&embedding, 1);
195        assert!(retrieve_result.is_err());
196    }
197}