1use crate::ExpertDomain;
7use lazy_static::lazy_static;
8use rustc_hash::{FxHashSet, FxHashMap};
9
10const MAX_INPUT_SIZE: usize = 256;
12
13lazy_static! {
14 static ref DOMAIN_PATTERN_HASHES: FxHashMap<ExpertDomain, FxHashSet<u64>> = {
16 let mut map = FxHashMap::default();
17
18 for domain in [
19 ExpertDomain::Reasoning,
20 ExpertDomain::Coding,
21 ExpertDomain::Language,
22 ExpertDomain::Mathematics,
23 ExpertDomain::ToolUse,
24 ExpertDomain::Context,
25 ] {
26 let patterns = domain.domain_patterns();
27 let hash_set: FxHashSet<u64> = patterns.iter()
28 .map(|&pattern| hash_string_fast(pattern))
29 .collect();
30 map.insert(domain, hash_set);
31 }
32
33 map
34 };
35
36 static ref NGRAM_FEATURES: FxHashMap<u64, f32> = {
38 let mut map = FxHashMap::default();
39
40 let prog_terms = ["func", "code", "prog", "algo", "debu", "comp"];
42 for term in prog_terms {
43 map.insert(hash_string_fast(term), 0.9);
44 }
45
46 let math_terms = ["calc", "equa", "solv", "deri", "inte", "form"];
48 for term in math_terms {
49 map.insert(hash_string_fast(term), 0.8);
50 }
51
52 let reason_terms = ["anal", "logi", "reas", "beca", "ther", "conc"];
54 for term in reason_terms {
55 map.insert(hash_string_fast(term), 0.85);
56 }
57
58 map
59 };
60
61 static ref OPTIMIZED_VOCAB: FxHashMap<u64, [f32; 16]> = {
63 let mut map = FxHashMap::default();
64
65 let common_words = [
66 "the", "and", "or", "but", "if", "then", "else", "when",
67 "how", "what", "why", "where", "function", "class", "method",
68 "variable", "calculate", "solve", "analyze", "explain", "code",
69 "program", "algorithm", "data", "neural", "network", "ai", "machine"
70 ];
71
72 for (i, word) in common_words.iter().enumerate() {
73 let hash = hash_string_fast(word);
74 let mut embedding = [0.0f32; 16];
75
76 for j in 0..16 {
78 embedding[j] = ((i * 37 + j * 17) as f32 / 1000.0).sin() * 0.5;
79 }
80
81 map.insert(hash, embedding);
82 }
83
84 map
85 };
86}
87
88#[inline]
90fn hash_string_fast(s: &str) -> u64 {
91 use std::hash::{Hash, Hasher};
92 let mut hasher = rustc_hash::FxHasher::default();
93 s.hash(&mut hasher);
94 hasher.finish()
95}
96
97#[derive(Debug, Clone)]
99pub struct OptimizedFeatureExtractor {
100 domain: ExpertDomain,
101 input_size: usize,
102 feature_cache: FxHashMap<u64, Vec<f32>>,
103}
104
105impl OptimizedFeatureExtractor {
106 pub fn new(domain: ExpertDomain, input_size: usize) -> Self {
108 Self {
109 domain,
110 input_size: input_size.min(MAX_INPUT_SIZE),
111 feature_cache: FxHashMap::default(),
112 }
113 }
114
115 pub fn extract_features(&mut self, text: &str) -> Vec<f32> {
117 let text_hash = hash_string_fast(text);
119 if let Some(cached) = self.feature_cache.get(&text_hash) {
120 return cached.clone();
121 }
122
123 let features = self.extract_features_internal(text);
124
125 if self.feature_cache.len() < 1000 {
127 self.feature_cache.insert(text_hash, features.clone());
128 }
129
130 features
131 }
132
133 fn extract_features_internal(&self, text: &str) -> Vec<f32> {
135 let mut features = vec![0.0; self.input_size];
136 let text_bytes = text.as_bytes();
137 let text_len = text_bytes.len();
138
139 if text_len == 0 {
140 return features;
141 }
142
143 let mut feature_idx = 0;
144
145 if let Some(pattern_hashes) = DOMAIN_PATTERN_HASHES.get(&self.domain) {
147 let pattern_score = self.calculate_pattern_score_fast(text, pattern_hashes);
148 if feature_idx < features.len() {
149 features[feature_idx] = pattern_score;
150 feature_idx += 1;
151 }
152 }
153
154 let (word_count, avg_word_len) = self.calculate_text_stats_fast(text_bytes);
156
157 if feature_idx < features.len() {
158 features[feature_idx] = (word_count as f32 / 50.0).min(1.0);
159 feature_idx += 1;
160 }
161
162 if feature_idx < features.len() {
163 features[feature_idx] = (text_len as f32 / 500.0).min(1.0);
164 feature_idx += 1;
165 }
166
167 if feature_idx < features.len() {
168 features[feature_idx] = (avg_word_len / 15.0).min(1.0);
169 feature_idx += 1;
170 }
171
172 let char_features = self.calculate_char_features_fast(text_bytes);
174 for &char_feat in char_features.iter().take(4) {
175 if feature_idx < features.len() {
176 features[feature_idx] = char_feat;
177 feature_idx += 1;
178 }
179 }
180
181 let ngram_score = self.calculate_ngram_features_fast(text);
183 if feature_idx < features.len() {
184 features[feature_idx] = ngram_score;
185 feature_idx += 1;
186 }
187
188 let embedding_features = self.calculate_embedding_features_fast(text);
190 let remaining_slots = features.len() - feature_idx;
191 let embedding_to_use = embedding_features.len().min(remaining_slots);
192
193 for i in 0..embedding_to_use {
194 features[feature_idx + i] = embedding_features[i];
195 }
196 feature_idx += embedding_to_use;
197
198 for i in feature_idx..features.len() {
200 let hash_val = (text_len.wrapping_mul(i).wrapping_mul(self.domain as usize + 1)) as f32;
201 features[i] = (hash_val % 1000.0) / 1000.0;
202 }
203
204 features
205 }
206
207 #[inline]
209 fn calculate_pattern_score_fast(&self, text: &str, pattern_hashes: &FxHashSet<u64>) -> f32 {
210 let text_lower = text.to_lowercase();
211 let words: Vec<&str> = text_lower.split_whitespace().collect();
212
213 let mut matches = 0;
214 let total_patterns = pattern_hashes.len();
215
216 for word in words {
218 let word_hash = hash_string_fast(word);
219 if pattern_hashes.contains(&word_hash) {
220 matches += 1;
221 }
222
223 if word.len() > 4 {
225 for i in 0..=word.len().saturating_sub(4) {
226 let substr = &word[i..i+4];
227 let substr_hash = hash_string_fast(substr);
228 if pattern_hashes.contains(&substr_hash) {
229 matches += 1;
230 break; }
232 }
233 }
234 }
235
236 (matches as f32 / total_patterns as f32).min(1.0)
237 }
238
239 #[inline]
241 fn calculate_text_stats_fast(&self, text_bytes: &[u8]) -> (usize, f32) {
242 let mut word_count = 0;
243 let mut char_count = 0;
244 let mut in_word = false;
245
246 for &byte in text_bytes {
247 if byte.is_ascii_whitespace() {
248 if in_word {
249 word_count += 1;
250 in_word = false;
251 }
252 } else if byte.is_ascii_alphabetic() {
253 char_count += 1;
254 in_word = true;
255 }
256 }
257
258 if in_word {
260 word_count += 1;
261 }
262
263 let avg_word_len = if word_count > 0 {
264 char_count as f32 / word_count as f32
265 } else {
266 0.0
267 };
268
269 (word_count, avg_word_len)
270 }
271
272 #[inline]
274 fn calculate_char_features_fast(&self, text_bytes: &[u8]) -> [f32; 4] {
275 let mut vowel_count = 0;
276 let mut consonant_count = 0;
277 let mut digit_count = 0;
278 let mut punct_count = 0;
279
280 for &byte in text_bytes {
281 match byte {
282 b'a' | b'e' | b'i' | b'o' | b'u' |
283 b'A' | b'E' | b'I' | b'O' | b'U' => vowel_count += 1,
284 b'b'..=b'z' | b'B'..=b'Z' => consonant_count += 1,
285 b'0'..=b'9' => digit_count += 1,
286 b'!' | b'?' | b'.' | b',' | b';' | b':' => punct_count += 1,
287 _ => {}
288 }
289 }
290
291 let total_chars = text_bytes.len() as f32;
292 if total_chars == 0.0 {
293 return [0.0; 4];
294 }
295
296 [
297 vowel_count as f32 / total_chars,
298 consonant_count as f32 / total_chars,
299 digit_count as f32 / total_chars,
300 punct_count as f32 / total_chars,
301 ]
302 }
303
304 #[inline]
306 fn calculate_ngram_features_fast(&self, text: &str) -> f32 {
307 let text_lower = text.to_lowercase();
308 let mut score = 0.0;
309 let mut count = 0;
310
311 if text_lower.len() >= 4 {
313 for i in 0..=text_lower.len() - 4 {
314 let ngram = &text_lower[i..i+4];
315 let ngram_hash = hash_string_fast(ngram);
316
317 if let Some(&weight) = NGRAM_FEATURES.get(&ngram_hash) {
318 score += weight;
319 count += 1;
320 }
321 }
322 }
323
324 if count > 0 {
325 score / count as f32
326 } else {
327 0.0
328 }
329 }
330
331 #[inline]
333 fn calculate_embedding_features_fast(&self, text: &str) -> Vec<f32> {
334 let words: Vec<&str> = text.split_whitespace().take(8).collect(); let mut features = Vec::with_capacity(16);
336
337 for word in words {
338 let word_lower = word.to_lowercase();
339 let word_hash = hash_string_fast(&word_lower);
340
341 if let Some(embedding) = OPTIMIZED_VOCAB.get(&word_hash) {
342 for &val in embedding.iter().take(2) { features.push(val);
345 }
346
347 if features.len() >= 16 { break;
349 }
350 }
351 }
352
353 features.resize(16, 0.0);
355 features
356 }
357
358 pub fn clear_cache(&mut self) {
360 self.feature_cache.clear();
361 }
362
363 pub fn cache_stats(&self) -> (usize, usize) {
365 (self.feature_cache.len(), 1000) }
367}
368
369#[derive(Debug, Clone)]
371pub struct OptimizedPatternMatcher {
372 domain_scores: FxHashMap<ExpertDomain, f32>,
373}
374
375impl OptimizedPatternMatcher {
376 pub fn new() -> Self {
378 Self {
379 domain_scores: FxHashMap::default(),
380 }
381 }
382
383 pub fn calculate_domain_scores(&mut self, text: &str) -> &FxHashMap<ExpertDomain, f32> {
385 self.domain_scores.clear();
386
387 let text_lower = text.to_lowercase();
388 let words: Vec<&str> = text_lower.split_whitespace().collect();
389
390 let word_hashes: Vec<u64> = words.iter()
392 .map(|&word| hash_string_fast(word))
393 .collect();
394
395 let has_arithmetic = self.detect_arithmetic_pattern(text);
397
398 for (domain, pattern_hashes) in DOMAIN_PATTERN_HASHES.iter() {
400 let mut matches = 0;
401
402 for &word_hash in &word_hashes {
404 if pattern_hashes.contains(&word_hash) {
405 matches += 1;
406 }
407 }
408
409 let mut score = (matches as f32 / pattern_hashes.len() as f32).min(1.0);
410
411 if *domain == ExpertDomain::Mathematics && has_arithmetic {
413 score = (score + 0.8).min(1.0); }
415
416 self.domain_scores.insert(*domain, score);
417 }
418
419 &self.domain_scores
420 }
421
422 pub fn get_best_domain(&self) -> Option<(ExpertDomain, f32)> {
424 self.domain_scores.iter()
425 .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
426 .map(|(&domain, &score)| (domain, score))
427 }
428
429 fn detect_arithmetic_pattern(&self, text: &str) -> bool {
431 let has_operators = text.contains('+') || text.contains('-') || text.contains('*') || text.contains('/') || text.contains('^');
433 let has_numbers = text.chars().any(|c| c.is_numeric());
434
435 if has_operators && has_numbers {
436 return true;
437 }
438
439 let text_lower = text.to_lowercase();
441 let arithmetic_words = ["plus", "minus", "times", "divided", "add", "subtract", "multiply", "divide", "sum", "difference"];
442
443 has_numbers && arithmetic_words.iter().any(|&word| text_lower.contains(word))
444 }
445}
446
447#[derive(Debug, Clone)]
449pub struct FeatureExtractionMetrics {
450 pub total_extractions: u64,
451 pub cache_hits: u64,
452 pub cache_misses: u64,
453 pub avg_extraction_time_ns: u64,
454 pub total_features_extracted: u64,
455}
456
457impl FeatureExtractionMetrics {
458 pub fn new() -> Self {
459 Self {
460 total_extractions: 0,
461 cache_hits: 0,
462 cache_misses: 0,
463 avg_extraction_time_ns: 0,
464 total_features_extracted: 0,
465 }
466 }
467
468 pub fn cache_hit_rate(&self) -> f32 {
469 if self.total_extractions == 0 {
470 0.0
471 } else {
472 self.cache_hits as f32 / self.total_extractions as f32
473 }
474 }
475}
476
477#[cfg(test)]
478mod tests {
479 use super::*;
480
481 #[test]
482 fn test_optimized_feature_extraction() {
483 let mut extractor = OptimizedFeatureExtractor::new(ExpertDomain::Coding, 128);
484
485 let features = extractor.extract_features("write a function to sort an array");
486 assert_eq!(features.len(), 128);
487 assert!(features[0] > 0.0); }
489
490 #[test]
491 fn test_pattern_matcher_performance() {
492 let mut matcher = OptimizedPatternMatcher::new();
493
494 let _scores = matcher.calculate_domain_scores("calculate the derivative of x^2");
495 let (best_domain, score) = matcher.get_best_domain().unwrap();
496
497 assert_eq!(best_domain, ExpertDomain::Mathematics);
498 assert!(score > 0.0);
499 }
500
501 #[test]
502 fn test_feature_extraction_cache() {
503 let mut extractor = OptimizedFeatureExtractor::new(ExpertDomain::Language, 64);
504
505 let text = "translate hello world";
506
507 let features1 = extractor.extract_features(text);
509
510 let features2 = extractor.extract_features(text);
512
513 assert_eq!(features1, features2);
514 assert_eq!(extractor.cache_stats().0, 1); }
516}