1use super::types::*;
7use crate::error::InterpolateResult;
8use scirs2_core::numeric::Float;
9use std::collections::{HashMap, VecDeque};
10use std::fmt::Debug;
11use std::time::Instant;
12
13#[derive(Debug)]
15pub struct IntelligentMethodSelector<F: Float + Debug> {
16 method_db: HashMap<MethodKey, MethodPerformanceData>,
18 current_data_profile: Option<DataProfile<F>>,
20 selection_model: MethodSelectionModel<F>,
22 performance_history: VecDeque<MethodPerformanceRecord>,
24}
25
26#[derive(Debug)]
28pub struct MethodSelectionModel<F: Float> {
29 feature_weights: HashMap<String, f64>,
31 decision_tree: Vec<MethodSelectionRule>,
33 learning_rate: f64,
35 model_confidence: F,
37}
38
39#[derive(Debug, Clone)]
41pub struct MethodSelectionRule {
42 pub condition: MethodSelectionCondition,
44 pub method: InterpolationMethodType,
46 pub confidence: f64,
48 pub expected_accuracy: f64,
50}
51
52#[derive(Debug, Clone)]
54pub enum MethodSelectionCondition {
55 DataSizeRange { min: usize, max: usize },
57 SmoothnessThreshold { threshold: f64 },
59 NoiseLevel { max_noise: f64 },
61 PatternTypeMatch { pattern: DataPatternType },
63 AccuracyRequirement { min_accuracy: f64 },
65 PerformanceRequirement { max_time: f64 },
67 And {
69 conditions: Vec<MethodSelectionCondition>,
70 },
71 Or {
73 conditions: Vec<MethodSelectionCondition>,
74 },
75}
76
77impl<F: Float + Debug> IntelligentMethodSelector<F> {
78 pub fn new() -> InterpolateResult<Self> {
80 Ok(Self {
81 method_db: HashMap::new(),
82 current_data_profile: None,
83 selection_model: MethodSelectionModel::new()?,
84 performance_history: VecDeque::new(),
85 })
86 }
87
88 pub fn select_method(
90 &mut self,
91 data_profile: &DataProfile<F>,
92 performance_targets: &PerformanceTargets,
93 ) -> InterpolateResult<InterpolationRecommendation<F>> {
94 self.current_data_profile = Some(data_profile.clone());
95
96 let method_scores = self.evaluate_methods(data_profile, performance_targets)?;
98
99 let best_method = self.select_best_method(&method_scores)?;
101
102 self.generate_recommendation(best_method, &method_scores, data_profile)
104 }
105
106 pub fn update_performance(
108 &mut self,
109 method: InterpolationMethodType,
110 data_profile: &DataProfile<F>,
111 performance: &MethodPerformanceData,
112 ) -> InterpolateResult<()> {
113 let key = self.create_method_key(method, data_profile);
114
115 let existing = self
117 .method_db
118 .entry(key)
119 .or_insert_with(|| MethodPerformanceData {
120 avg_execution_time: 0.0,
121 memory_usage: 0,
122 accuracy: 0.0,
123 noise_robustness: 0.0,
124 sample_count: 0,
125 last_update: Instant::now(),
126 });
127
128 let alpha = 0.1; existing.avg_execution_time =
131 (1.0 - alpha) * existing.avg_execution_time + alpha * performance.avg_execution_time;
132 existing.accuracy = (1.0 - alpha) * existing.accuracy + alpha * performance.accuracy;
133 existing.noise_robustness =
134 (1.0 - alpha) * existing.noise_robustness + alpha * performance.noise_robustness;
135 existing.memory_usage = ((1.0 - alpha) * existing.memory_usage as f64
136 + alpha * performance.memory_usage as f64) as usize;
137 existing.sample_count += 1;
138 existing.last_update = Instant::now();
139
140 self.performance_history.push_back(MethodPerformanceRecord {
142 timestamp: Instant::now(),
143 method,
144 execution_time: performance.avg_execution_time,
145 memory_usage: performance.memory_usage,
146 accuracy: performance.accuracy,
147 data_size: data_profile.size,
148 success: true,
149 });
150
151 if self.performance_history.len() > 1000 {
153 self.performance_history.pop_front();
154 }
155
156 self.update_selection_model()?;
158
159 Ok(())
160 }
161
162 pub fn get_method_performance(
164 &self,
165 method: InterpolationMethodType,
166 data_profile: &DataProfile<F>,
167 ) -> Option<&MethodPerformanceData> {
168 let key = self.create_method_key(method, data_profile);
169 self.method_db.get(&key)
170 }
171
172 pub fn get_available_methods(
174 &self,
175 data_profile: &DataProfile<F>,
176 ) -> Vec<InterpolationMethodType> {
177 let mut methods = Vec::new();
178
179 methods.push(InterpolationMethodType::Linear);
181 methods.push(InterpolationMethodType::CubicSpline);
182
183 if data_profile.size < 10000 {
184 methods.push(InterpolationMethodType::Polynomial);
185 methods.push(InterpolationMethodType::RadialBasisFunction);
186 }
187
188 if data_profile.dimensionality <= 3 {
189 methods.push(InterpolationMethodType::Kriging);
190 methods.push(InterpolationMethodType::ThinPlateSpline);
191 }
192
193 methods.push(InterpolationMethodType::BSpline);
194 methods.push(InterpolationMethodType::PchipInterpolation);
195 methods.push(InterpolationMethodType::AkimaSpline);
196
197 if data_profile.size >= 1000 {
198 methods.push(InterpolationMethodType::NaturalNeighbor);
199 methods.push(InterpolationMethodType::ShepardsMethod);
200 }
201
202 methods
203 }
204
205 fn evaluate_methods(
207 &self,
208 data_profile: &DataProfile<F>,
209 performance_targets: &PerformanceTargets,
210 ) -> InterpolateResult<HashMap<InterpolationMethodType, f64>> {
211 let mut scores = HashMap::new();
212 let available_methods = self.get_available_methods(data_profile);
213
214 for method in available_methods {
215 let score = self.calculate_method_score(method, data_profile, performance_targets)?;
216 scores.insert(method, score);
217 }
218
219 Ok(scores)
220 }
221
222 fn calculate_method_score(
224 &self,
225 method: InterpolationMethodType,
226 data_profile: &DataProfile<F>,
227 performance_targets: &PerformanceTargets,
228 ) -> InterpolateResult<f64> {
229 let mut score = 0.0;
230
231 let base_score = match method {
233 InterpolationMethodType::Linear => 0.6,
234 InterpolationMethodType::CubicSpline => 0.8,
235 InterpolationMethodType::BSpline => 0.85,
236 InterpolationMethodType::RadialBasisFunction => 0.75,
237 InterpolationMethodType::Kriging => 0.9,
238 InterpolationMethodType::Polynomial => 0.7,
239 InterpolationMethodType::PchipInterpolation => 0.8,
240 InterpolationMethodType::AkimaSpline => 0.82,
241 InterpolationMethodType::ThinPlateSpline => 0.78,
242 InterpolationMethodType::NaturalNeighbor => 0.76,
243 InterpolationMethodType::ShepardsMethod => 0.65,
244 InterpolationMethodType::QuantumInspired => 0.95,
245 };
246
247 score += base_score * 0.3; let smoothness_factor = data_profile.smoothness.to_f64().unwrap_or(0.5);
251 let noise_factor = 1.0 - data_profile.noise_level.to_f64().unwrap_or(0.1);
252
253 score += smoothness_factor * 0.2; score += noise_factor * 0.2; if let Some(perf_data) = self.get_method_performance(method, data_profile) {
258 let accuracy_score = perf_data.accuracy;
259 let speed_score = 1.0 / (1.0 + perf_data.avg_execution_time / 1000.0); score += accuracy_score * 0.15; score += speed_score * 0.15; }
264
265 for rule in &self.selection_model.decision_tree {
267 if self.evaluate_condition(&rule.condition, data_profile, performance_targets)?
268 && rule.method == method
269 {
270 score += rule.confidence * 0.1; }
272 }
273
274 Ok(score.min(1.0)) }
276
277 fn evaluate_condition(
279 &self,
280 condition: &MethodSelectionCondition,
281 data_profile: &DataProfile<F>,
282 performance_targets: &PerformanceTargets,
283 ) -> InterpolateResult<bool> {
284 match condition {
285 MethodSelectionCondition::DataSizeRange { min, max } => {
286 Ok(data_profile.size >= *min && data_profile.size <= *max)
287 }
288 MethodSelectionCondition::SmoothnessThreshold { threshold } => {
289 Ok(data_profile.smoothness.to_f64().unwrap_or(0.0) >= *threshold)
290 }
291 MethodSelectionCondition::NoiseLevel { max_noise } => {
292 Ok(data_profile.noise_level.to_f64().unwrap_or(1.0) <= *max_noise)
293 }
294 MethodSelectionCondition::PatternTypeMatch { pattern } => {
295 Ok(true) }
298 MethodSelectionCondition::AccuracyRequirement { min_accuracy } => {
299 Ok(performance_targets.target_accuracy >= *min_accuracy)
300 }
301 MethodSelectionCondition::PerformanceRequirement { max_time } => {
302 Ok(performance_targets.max_time <= *max_time)
303 }
304 MethodSelectionCondition::And { conditions } => {
305 for cond in conditions {
306 if !self.evaluate_condition(cond, data_profile, performance_targets)? {
307 return Ok(false);
308 }
309 }
310 Ok(true)
311 }
312 MethodSelectionCondition::Or { conditions } => {
313 for cond in conditions {
314 if self.evaluate_condition(cond, data_profile, performance_targets)? {
315 return Ok(true);
316 }
317 }
318 Ok(false)
319 }
320 }
321 }
322
323 fn select_best_method(
325 &self,
326 method_scores: &HashMap<InterpolationMethodType, f64>,
327 ) -> InterpolateResult<InterpolationMethodType> {
328 let best = method_scores
329 .iter()
330 .max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
331 .map(|(method, _)| *method);
332
333 best.ok_or_else(|| {
334 crate::error::InterpolateError::invalid_input("No suitable method found".to_string())
335 })
336 }
337
338 fn generate_recommendation(
340 &self,
341 best_method: InterpolationMethodType,
342 method_scores: &HashMap<InterpolationMethodType, f64>,
343 data_profile: &DataProfile<F>,
344 ) -> InterpolateResult<InterpolationRecommendation<F>> {
345 let primary_score = method_scores.get(&best_method).copied().unwrap_or(0.0);
346
347 let mut alternatives: Vec<_> = method_scores
349 .iter()
350 .filter(|(method, _)| **method != best_method)
351 .map(|(method, score)| (*method, *score))
352 .collect();
353 alternatives.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
354 alternatives.truncate(3); let primary_recommendation = MethodRecommendation {
357 method: best_method,
358 confidence: 0.8, expected_performance: ExpectedPerformance {
360 accuracy_range: (0.8, 0.95),
361 time_range: (100.0, 1000.0),
362 memory_range: (1024, 10240),
363 performance_score: 0.8,
364 },
365 parameters: self.get_default_parameters(best_method),
366 configuration: self.get_method_configuration(best_method),
367 benefits: self.get_method_benefits(best_method),
368 limitations: self.get_method_limitations(best_method),
369 };
370
371 let alternative_recommendations: Vec<_> = alternatives
372 .into_iter()
373 .map(|(method, _)| MethodRecommendation {
374 method,
375 confidence: 0.6, expected_performance: ExpectedPerformance {
377 accuracy_range: (0.8, 0.95),
378 time_range: (100.0, 1000.0),
379 memory_range: (1024, 10240),
380 performance_score: 0.8,
381 },
382 parameters: self.get_default_parameters(method),
383 configuration: self.get_method_configuration(method),
384 benefits: self.get_method_benefits(method),
385 limitations: self.get_method_limitations(method),
386 })
387 .collect();
388
389 let expected_performance = self.estimate_performance(best_method, data_profile);
390
391 Ok(InterpolationRecommendation {
392 primary_method: primary_recommendation,
393 alternatives: alternative_recommendations,
394 expected_performance,
395 confidence: F::from(primary_score).expect("Failed to convert to float"),
396 reasoning: self.generate_reasoning(best_method, data_profile),
397 })
398 }
399
400 fn create_method_key(
402 &self,
403 method: InterpolationMethodType,
404 data_profile: &DataProfile<F>,
405 ) -> MethodKey {
406 let size_class = match data_profile.size {
407 0..=1000 => DataSizeClass::Small,
408 1001..=100000 => DataSizeClass::Medium,
409 100001..=10000000 => DataSizeClass::Large,
410 _ => DataSizeClass::Massive,
411 };
412
413 let pattern_type = self.classify_pattern(data_profile);
414
415 MethodKey {
416 method_type: method,
417 size_class,
418 pattern_type,
419 dimensionality: data_profile.dimensionality as u8,
420 }
421 }
422
423 fn classify_pattern(&self, data_profile: &DataProfile<F>) -> DataPatternType {
425 let smoothness = data_profile.smoothness.to_f64().unwrap_or(0.5);
426 let noise = data_profile.noise_level.to_f64().unwrap_or(0.1);
427 let sparsity = data_profile.sparsity.to_f64().unwrap_or(0.0);
428
429 if noise > 0.1 {
430 DataPatternType::Noisy
431 } else if sparsity > 0.5 {
432 DataPatternType::Sparse
433 } else if smoothness > 0.8 {
434 DataPatternType::Smooth
435 } else if smoothness < 0.3 {
436 DataPatternType::Irregular
437 } else {
438 DataPatternType::Structured
439 }
440 }
441
442 fn update_selection_model(&mut self) -> InterpolateResult<()> {
444 if self.performance_history.len() >= 10 {
446 let recent_records: Vec<_> = self.performance_history.iter().rev().take(10).collect();
448
449 let success_rate = recent_records.iter().filter(|r| r.success).count() as f64
451 / recent_records.len() as f64;
452 self.selection_model.model_confidence =
453 F::from(success_rate * 0.9 + 0.1).expect("Failed to convert to float");
454 }
455
456 Ok(())
457 }
458
459 fn get_default_parameters(&self, method: InterpolationMethodType) -> Vec<f64> {
461 match method {
462 InterpolationMethodType::Linear => vec![],
463 InterpolationMethodType::CubicSpline => vec![0.0], InterpolationMethodType::BSpline => vec![3.0], InterpolationMethodType::RadialBasisFunction => vec![1.0], InterpolationMethodType::Kriging => vec![1.0, 1.0], InterpolationMethodType::Polynomial => vec![3.0], _ => vec![],
469 }
470 }
471
472 fn get_method_configuration(&self, method: InterpolationMethodType) -> String {
474 match method {
475 InterpolationMethodType::Linear => "Standard linear interpolation".to_string(),
476 InterpolationMethodType::CubicSpline => "Natural boundary conditions".to_string(),
477 InterpolationMethodType::BSpline => "Cubic B-spline with clamped ends".to_string(),
478 InterpolationMethodType::RadialBasisFunction => {
479 "Gaussian RBF with automatic scaling".to_string()
480 }
481 InterpolationMethodType::Kriging => {
482 "Ordinary kriging with exponential variogram".to_string()
483 }
484 _ => "Default configuration".to_string(),
485 }
486 }
487
488 fn get_method_benefits(&self, method: InterpolationMethodType) -> Vec<String> {
490 match method {
491 InterpolationMethodType::Linear => {
492 vec!["Fast".to_string(), "Memory efficient".to_string()]
493 }
494 InterpolationMethodType::CubicSpline => {
495 vec!["Smooth".to_string(), "Good for smooth data".to_string()]
496 }
497 InterpolationMethodType::BSpline => {
498 vec!["Flexible".to_string(), "Numerical stability".to_string()]
499 }
500 InterpolationMethodType::RadialBasisFunction => vec![
501 "Handles scattered data".to_string(),
502 "High accuracy".to_string(),
503 ],
504 InterpolationMethodType::Kriging => vec![
505 "Uncertainty quantification".to_string(),
506 "Optimal for spatial data".to_string(),
507 ],
508 _ => vec!["General purpose".to_string()],
509 }
510 }
511
512 fn get_method_limitations(&self, method: InterpolationMethodType) -> Vec<String> {
514 match method {
515 InterpolationMethodType::Linear => {
516 vec!["Low accuracy".to_string(), "Not smooth".to_string()]
517 }
518 InterpolationMethodType::CubicSpline => vec![
519 "Sensitive to outliers".to_string(),
520 "Requires ordered data".to_string(),
521 ],
522 InterpolationMethodType::RadialBasisFunction => vec![
523 "Computationally expensive".to_string(),
524 "Memory intensive".to_string(),
525 ],
526 _ => vec!["None significant".to_string()],
527 }
528 }
529
530 fn estimate_performance(
532 &self,
533 method: InterpolationMethodType,
534 data_profile: &DataProfile<F>,
535 ) -> ExpectedPerformance {
536 let base_time = match method {
537 InterpolationMethodType::Linear => 10.0,
538 InterpolationMethodType::CubicSpline => 100.0,
539 InterpolationMethodType::BSpline => 150.0,
540 InterpolationMethodType::RadialBasisFunction => 1000.0,
541 InterpolationMethodType::Kriging => 2000.0,
542 _ => 500.0,
543 };
544
545 let time_factor = (data_profile.size as f64).log10();
546 let estimated_time = base_time * time_factor;
547
548 ExpectedPerformance {
549 accuracy_range: (0.95, 0.99),
550 time_range: (estimated_time * 0.5, estimated_time * 2.0),
551 memory_range: (data_profile.size * 8, data_profile.size * 32),
552 performance_score: 0.8,
553 }
554 }
555
556 fn generate_reasoning(
558 &self,
559 method: InterpolationMethodType,
560 data_profile: &DataProfile<F>,
561 ) -> String {
562 format!(
563 "Selected {} for {} points with dimensionality {} based on data characteristics and performance history.",
564 format!("{:?}", method),
565 data_profile.size,
566 data_profile.dimensionality
567 )
568 }
569}
570
571impl<F: Float> MethodSelectionModel<F> {
572 pub fn new() -> InterpolateResult<Self> {
574 let mut model = Self {
575 feature_weights: HashMap::new(),
576 decision_tree: Vec::new(),
577 learning_rate: 0.01,
578 model_confidence: F::from(0.8).expect("Failed to convert constant to float"),
579 };
580
581 model.initialize_default_rules();
583
584 Ok(model)
585 }
586
587 fn initialize_default_rules(&mut self) {
589 self.decision_tree.push(MethodSelectionRule {
591 condition: MethodSelectionCondition::And {
592 conditions: vec![
593 MethodSelectionCondition::DataSizeRange { min: 0, max: 1000 },
594 MethodSelectionCondition::SmoothnessThreshold { threshold: 0.8 },
595 ],
596 },
597 method: InterpolationMethodType::CubicSpline,
598 confidence: 0.9,
599 expected_accuracy: 0.95,
600 });
601
602 self.decision_tree.push(MethodSelectionRule {
604 condition: MethodSelectionCondition::DataSizeRange {
605 min: 100000,
606 max: usize::MAX,
607 },
608 method: InterpolationMethodType::Linear,
609 confidence: 0.7,
610 expected_accuracy: 0.8,
611 });
612
613 self.decision_tree.push(MethodSelectionRule {
615 condition: MethodSelectionCondition::NoiseLevel { max_noise: 0.05 },
616 method: InterpolationMethodType::BSpline,
617 confidence: 0.85,
618 expected_accuracy: 0.9,
619 });
620
621 self.decision_tree.push(MethodSelectionRule {
623 condition: MethodSelectionCondition::AccuracyRequirement { min_accuracy: 0.99 },
624 method: InterpolationMethodType::Kriging,
625 confidence: 0.9,
626 expected_accuracy: 0.99,
627 });
628 }
629
630 pub fn get_confidence(&self) -> F {
632 self.model_confidence
633 }
634
635 pub fn update_model(&mut self, feedback: &[MethodPerformanceRecord]) -> InterpolateResult<()> {
637 if feedback.len() >= 5 {
639 let success_rate =
640 feedback.iter().filter(|r| r.success).count() as f64 / feedback.len() as f64;
641 self.model_confidence =
642 F::from(success_rate * 0.9 + 0.1).expect("Failed to convert to float");
643 }
644 Ok(())
645 }
646}