exo_backend_classical/
lib.rs1#![warn(missing_docs)]
8
9pub mod dither_quantizer;
10pub mod domain_bridge;
11pub mod graph;
12pub mod thermo_layer;
13pub mod transfer_orchestrator;
14pub mod vector;
15
16use exo_core::{
17 Error as ExoError, Filter, ManifoldDelta, Pattern, Result as ExoResult, SearchResult,
18 SubstrateBackend,
19};
20use parking_lot::RwLock;
21use std::sync::Arc;
22use vector::VectorIndexWrapper;
23
24pub use graph::GraphWrapper;
25
26#[derive(Debug, Clone)]
28pub struct ClassicalConfig {
29 pub dimensions: usize,
31 pub distance_metric: ruvector_core::DistanceMetric,
33}
34
35impl Default for ClassicalConfig {
36 fn default() -> Self {
37 Self {
38 dimensions: 768,
39 distance_metric: ruvector_core::DistanceMetric::Cosine,
40 }
41 }
42}
43
44pub struct ClassicalBackend {
50 vector_index: Arc<RwLock<VectorIndexWrapper>>,
52 graph_db: Arc<RwLock<GraphWrapper>>,
54 config: ClassicalConfig,
56}
57
58impl ClassicalBackend {
59 pub fn new(config: ClassicalConfig) -> ExoResult<Self> {
61 let vector_index = VectorIndexWrapper::new(config.dimensions, config.distance_metric)
62 .map_err(|e| ExoError::Backend(format!("Failed to create vector index: {}", e)))?;
63
64 let graph_db = GraphWrapper::new();
65
66 Ok(Self {
67 vector_index: Arc::new(RwLock::new(vector_index)),
68 graph_db: Arc::new(RwLock::new(graph_db)),
69 config,
70 })
71 }
72
73 pub fn with_dimensions(dimensions: usize) -> ExoResult<Self> {
75 let mut config = ClassicalConfig::default();
76 config.dimensions = dimensions;
77 Self::new(config)
78 }
79
80 pub fn graph_db(&self) -> Arc<RwLock<GraphWrapper>> {
82 Arc::clone(&self.graph_db)
83 }
84}
85
86impl SubstrateBackend for ClassicalBackend {
87 fn similarity_search(
88 &self,
89 query: &[f32],
90 k: usize,
91 filter: Option<&Filter>,
92 ) -> ExoResult<Vec<SearchResult>> {
93 if query.len() != self.config.dimensions {
95 return Err(ExoError::InvalidDimension {
96 expected: self.config.dimensions,
97 got: query.len(),
98 });
99 }
100
101 let index = self.vector_index.read();
103 index.search(query, k, filter)
104 }
105
106 fn manifold_deform(&self, pattern: &Pattern, _learning_rate: f32) -> ExoResult<ManifoldDelta> {
107 if pattern.embedding.len() != self.config.dimensions {
109 return Err(ExoError::InvalidDimension {
110 expected: self.config.dimensions,
111 got: pattern.embedding.len(),
112 });
113 }
114
115 let mut index = self.vector_index.write();
117 let id = index.insert(pattern)?;
118
119 Ok(ManifoldDelta::DiscreteInsert { id })
120 }
121
122 fn dimension(&self) -> usize {
123 self.config.dimensions
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use exo_core::{Metadata, PatternId, SubstrateTime};
131
132 #[test]
133 fn test_classical_backend_creation() {
134 let backend = ClassicalBackend::with_dimensions(128).unwrap();
135 assert_eq!(backend.dimension(), 128);
136 }
137
138 #[test]
139 fn test_insert_and_search() {
140 let backend = ClassicalBackend::with_dimensions(3).unwrap();
141
142 let pattern = Pattern {
144 id: PatternId::new(),
145 embedding: vec![1.0, 2.0, 3.0],
146 metadata: Metadata::default(),
147 timestamp: SubstrateTime::now(),
148 antecedents: vec![],
149 salience: 1.0,
150 };
151
152 let result = backend.manifold_deform(&pattern, 0.0);
154 assert!(result.is_ok());
155
156 let query = vec![1.1, 2.1, 3.1];
158 let results = backend.similarity_search(&query, 1, None);
159 assert!(results.is_ok());
160 let results = results.unwrap();
161 assert_eq!(results.len(), 1);
162 }
163}