do_memory_mcp/patterns/predictive/
kdtree.rs1#[allow(clippy::cast_precision_loss)]
7#[allow(clippy::cast_possible_wrap)]
8#[allow(clippy::cast_sign_loss)]
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct Point {
16 pub id: usize,
18 pub values: Vec<f64>,
20 pub embedding: Option<Vec<f32>>,
22 pub timestamp: f64,
24 pub features: Vec<f64>,
26}
27
28impl Point {
29 pub fn new(id: usize, values: &[f64], embedding: Option<Vec<f32>>, timestamp: f64) -> Self {
31 let features = Self::extract_features(values);
32 Self {
33 id,
34 values: values.to_vec(),
35 embedding,
36 timestamp,
37 features,
38 }
39 }
40
41 fn extract_features(values: &[f64]) -> Vec<f64> {
43 if values.len() < 3 {
44 return vec![0.0; 8]; }
46
47 let mean: f64 = values.iter().sum::<f64>() / values.len() as f64;
48 let variance: f64 =
49 values.iter().map(|&x| (x - mean).powi(2)).sum::<f64>() / values.len() as f64;
50 let std_dev = variance.sqrt();
51
52 let mut features = Vec::with_capacity(8);
53
54 features.push(mean);
56 features.push(std_dev);
57
58 #[allow(clippy::cast_precision_loss)]
60 let trend = if let (Some(&first_val), Some(&last_val)) = (values.first(), values.last()) {
61 (last_val - first_val) / (values.len() - 1) as f64
62 } else {
63 0.0
64 };
65 features.push(trend);
66
67 let volatility = if values.len() > 3 {
69 let window = values.len().min(5);
70 let mut rolling_std = Vec::new();
71 for i in (window - 1)..values.len() {
72 let start = i.saturating_sub(window - 1);
73 let window_data = &values[start..=i];
74 let window_mean: f64 = window_data.iter().sum::<f64>() / window_data.len() as f64;
75 let window_var: f64 = window_data
76 .iter()
77 .map(|&x| (x - window_mean).powi(2))
78 .sum::<f64>()
79 / window_data.len() as f64;
80 rolling_std.push(window_var.sqrt());
81 }
82 rolling_std.iter().sum::<f64>() / rolling_std.len() as f64
83 } else {
84 std_dev
85 };
86 features.push(volatility);
87
88 let skewness = if std_dev > 0.0 {
90 values
91 .iter()
92 .map(|&x| ((x - mean) / std_dev).powi(3))
93 .sum::<f64>()
94 / values.len() as f64
95 } else {
96 0.0
97 };
98 features.push(skewness);
99
100 let kurtosis = if std_dev > 0.0 {
102 values
103 .iter()
104 .map(|&x| ((x - mean) / std_dev).powi(4))
105 .sum::<f64>()
106 / values.len() as f64
107 - 3.0
108 } else {
109 0.0
110 };
111 features.push(kurtosis);
112
113 let autocorr = if values.len() > 1 {
115 let mut numerator = 0.0;
116 let mut denom_x = 0.0;
117 let mut denom_y = 0.0;
118 for i in 1..values.len() {
119 numerator += (values[i - 1] - mean) * (values[i] - mean);
120 denom_x += (values[i - 1] - mean).powi(2);
121 denom_y += (values[i] - mean).powi(2);
122 }
123 if denom_x > 0.0 && denom_y > 0.0 {
124 numerator / (denom_x.sqrt() * denom_y.sqrt())
125 } else {
126 0.0
127 }
128 } else {
129 0.0
130 };
131 features.push(autocorr);
132
133 let recent_change = if values.len() >= 3 {
135 let recent_mean: f64 = values.iter().rev().take(3).sum::<f64>() / 3.0;
136 (recent_mean - mean) / std_dev.max(1e-6)
137 } else {
138 0.0
139 };
140 features.push(recent_change);
141
142 features
143 }
144}
145
146#[derive(Debug)]
148struct KDNode {
149 point: Point,
150 axis: usize,
151 left: Option<Box<KDNode>>,
152 right: Option<Box<KDNode>>,
153}
154
155impl KDNode {
156 fn new(point: Point, axis: usize) -> Self {
157 Self {
158 point,
159 axis,
160 left: None,
161 right: None,
162 }
163 }
164
165 fn insert(&mut self, point: Point) {
167 let axis = self.axis;
168 let comparison = if point.features.len() > axis {
169 point.features[axis] < self.point.features[axis]
170 } else {
171 point.id < self.point.id
172 };
173
174 if comparison {
175 if let Some(left) = &mut self.left {
176 left.insert(point);
177 } else {
178 self.left = Some(Box::new(KDNode::new(
179 point,
180 (axis + 1) % self.point.features.len(),
181 )));
182 }
183 } else if let Some(right) = &mut self.right {
184 right.insert(point);
185 } else {
186 self.right = Some(Box::new(KDNode::new(
187 point,
188 (axis + 1) % self.point.features.len(),
189 )));
190 }
191 }
192
193 fn range_query(&self, epsilon: f64, center: &[f64], results: &mut Vec<Point>) {
195 let distance = calculate_distance(&self.point.features, center);
197 if distance <= epsilon {
198 results.push(self.point.clone());
199 }
200
201 let axis = self.axis;
203 if self.point.features.len() > axis {
204 let plane_distance = if center.len() > axis {
205 (center[axis] - self.point.features[axis]).abs()
206 } else {
207 0.0
208 };
209
210 if plane_distance <= epsilon {
211 if let Some(left) = &self.left {
212 left.range_query(epsilon, center, results);
213 }
214 if let Some(right) = &self.right {
215 right.range_query(epsilon, center, results);
216 }
217 } else {
218 if center.len() > axis && center[axis] < self.point.features[axis] {
220 if let Some(left) = &self.left {
221 left.range_query(epsilon, center, results);
222 }
223 } else if let Some(right) = &self.right {
224 right.range_query(epsilon, center, results);
225 }
226 }
227 }
228 }
229}
230
231#[derive(Debug)]
233#[allow(dead_code)]
234pub struct KDTree {
235 root: Option<Box<KDNode>>,
236 max_depth: usize,
237}
238
239impl KDTree {
240 pub(crate) fn new() -> Self {
241 Self {
242 root: None,
243 max_depth: 10,
244 }
245 }
246
247 pub fn build(points: &[Point]) -> Self {
249 if points.is_empty() {
250 return Self::new();
251 }
252
253 let mut tree = Self::new();
254 let axis = 0; if let Some(root_point) = points.first() {
257 tree.root = Some(Box::new(KDNode::new(root_point.clone(), axis)));
258
259 for point in points.iter().skip(1) {
260 if let Some(root) = tree.root.as_mut() {
261 root.insert(point.clone());
262 }
263 }
264 }
265
266 tree
267 }
268
269 pub fn find_neighbors(&self, center: &[f64], epsilon: f64) -> Vec<Point> {
271 let mut results = Vec::new();
272 if let Some(root) = &self.root {
273 root.range_query(epsilon, center, &mut results);
274 }
275 results
276 }
277}
278
279pub fn calculate_distance(a: &[f64], b: &[f64]) -> f64 {
281 let mut sum = 0.0;
282 for (x, y) in a.iter().zip(b.iter()) {
283 let diff = x - y;
284 sum += diff * diff;
285 }
286 sum.sqrt()
287}