scirs2_interpolate/advanced_coordinator_modules/
method_selection.rs

1//! Intelligent method selection system for interpolation
2//!
3//! This module provides AI-driven selection of optimal interpolation methods
4//! based on data characteristics, performance requirements, and historical data.
5
6use 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/// Intelligent method selection system
14#[derive(Debug)]
15pub struct IntelligentMethodSelector<F: Float + Debug> {
16    /// Method performance database
17    method_db: HashMap<MethodKey, MethodPerformanceData>,
18    /// Current data characteristics
19    current_data_profile: Option<DataProfile<F>>,
20    /// Method selection model
21    selection_model: MethodSelectionModel<F>,
22    /// Historical performance data
23    performance_history: VecDeque<MethodPerformanceRecord>,
24}
25
26/// Method selection model for decision making
27#[derive(Debug)]
28pub struct MethodSelectionModel<F: Float> {
29    /// Feature weights for method selection
30    feature_weights: HashMap<String, f64>,
31    /// Decision tree for method selection
32    decision_tree: Vec<MethodSelectionRule>,
33    /// Learning rate for weight updates
34    learning_rate: f64,
35    /// Model confidence
36    model_confidence: F,
37}
38
39/// Selection rule for decision tree
40#[derive(Debug, Clone)]
41pub struct MethodSelectionRule {
42    /// Condition for rule activation
43    pub condition: MethodSelectionCondition,
44    /// Recommended method
45    pub method: InterpolationMethodType,
46    /// Confidence score
47    pub confidence: f64,
48    /// Expected accuracy
49    pub expected_accuracy: f64,
50}
51
52/// Condition for method selection
53#[derive(Debug, Clone)]
54pub enum MethodSelectionCondition {
55    /// Data size based condition
56    DataSizeRange { min: usize, max: usize },
57    /// Smoothness threshold condition
58    SmoothnessThreshold { threshold: f64 },
59    /// Noise level condition
60    NoiseLevel { max_noise: f64 },
61    /// Pattern type condition
62    PatternTypeMatch { pattern: DataPatternType },
63    /// Accuracy requirement condition
64    AccuracyRequirement { min_accuracy: f64 },
65    /// Performance requirement condition
66    PerformanceRequirement { max_time: f64 },
67    /// Composite condition (AND)
68    And {
69        conditions: Vec<MethodSelectionCondition>,
70    },
71    /// Composite condition (OR)
72    Or {
73        conditions: Vec<MethodSelectionCondition>,
74    },
75}
76
77impl<F: Float + Debug> IntelligentMethodSelector<F> {
78    /// Create a new intelligent method selector
79    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    /// Select the best method for given data characteristics
89    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        // Apply decision tree rules
97        let method_scores = self.evaluate_methods(data_profile, performance_targets)?;
98
99        // Select best method based on scores
100        let best_method = self.select_best_method(&method_scores)?;
101
102        // Generate recommendation
103        self.generate_recommendation(best_method, &method_scores, data_profile)
104    }
105
106    /// Update method performance data
107    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        // Update or insert performance data
116        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        // Exponential moving average update
129        let alpha = 0.1; // Learning rate
130        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        // Add to performance history
141        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        // Limit history size
152        if self.performance_history.len() > 1000 {
153            self.performance_history.pop_front();
154        }
155
156        // Update selection model if needed
157        self.update_selection_model()?;
158
159        Ok(())
160    }
161
162    /// Get performance statistics for a method
163    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    /// Get all available methods for given data characteristics
173    pub fn get_available_methods(
174        &self,
175        data_profile: &DataProfile<F>,
176    ) -> Vec<InterpolationMethodType> {
177        let mut methods = Vec::new();
178
179        // Add methods based on data characteristics
180        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    /// Evaluate all methods for given data characteristics
206    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    /// Calculate score for a specific method
223    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        // Base scores for different methods
232        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; // 30% base score
248
249        // Adjust score based on data characteristics
250        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; // 20% smoothness
254        score += noise_factor * 0.2; // 20% noise tolerance
255
256        // Historical performance adjustment
257        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); // Normalize speed
260
261            score += accuracy_score * 0.15; // 15% historical accuracy
262            score += speed_score * 0.15; // 15% historical speed
263        }
264
265        // Apply decision tree rules
266        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; // 10% rule bonus
271            }
272        }
273
274        Ok(score.min(1.0)) // Cap at 1.0
275    }
276
277    /// Evaluate a selection condition
278    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                // This would need access to pattern analysis results
296                Ok(true) // Simplified for now
297            }
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    /// Select the best method from scores
324    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    /// Generate method recommendation
339    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        // Get alternative methods
348        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); // Top 3 alternatives
355
356        let primary_recommendation = MethodRecommendation {
357            method: best_method,
358            confidence: 0.8, // Default confidence score
359            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, // Lower confidence for alternatives
376                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).unwrap(),
396            reasoning: self.generate_reasoning(best_method, data_profile),
397        })
398    }
399
400    /// Create method key for database storage
401    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    /// Classify data pattern based on profile
424    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    /// Update the selection model based on performance history
443    fn update_selection_model(&mut self) -> InterpolateResult<()> {
444        // Simplified model update - would be more sophisticated in practice
445        if self.performance_history.len() >= 10 {
446            // Analyze recent performance trends
447            let recent_records: Vec<_> = self.performance_history.iter().rev().take(10).collect();
448
449            // Update model confidence based on recent success rate
450            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 = F::from(success_rate * 0.9 + 0.1).unwrap();
453        }
454
455        Ok(())
456    }
457
458    /// Get default parameters for a method
459    fn get_default_parameters(&self, method: InterpolationMethodType) -> Vec<f64> {
460        match method {
461            InterpolationMethodType::Linear => vec![],
462            InterpolationMethodType::CubicSpline => vec![0.0], // smoothing factor
463            InterpolationMethodType::BSpline => vec![3.0],     // degree
464            InterpolationMethodType::RadialBasisFunction => vec![1.0], // shape parameter
465            InterpolationMethodType::Kriging => vec![1.0, 1.0], // nugget, sill
466            InterpolationMethodType::Polynomial => vec![3.0],  // degree
467            _ => vec![],
468        }
469    }
470
471    /// Get method configuration string
472    fn get_method_configuration(&self, method: InterpolationMethodType) -> String {
473        match method {
474            InterpolationMethodType::Linear => "Standard linear interpolation".to_string(),
475            InterpolationMethodType::CubicSpline => "Natural boundary conditions".to_string(),
476            InterpolationMethodType::BSpline => "Cubic B-spline with clamped ends".to_string(),
477            InterpolationMethodType::RadialBasisFunction => {
478                "Gaussian RBF with automatic scaling".to_string()
479            }
480            InterpolationMethodType::Kriging => {
481                "Ordinary kriging with exponential variogram".to_string()
482            }
483            _ => "Default configuration".to_string(),
484        }
485    }
486
487    /// Get method benefits
488    fn get_method_benefits(&self, method: InterpolationMethodType) -> Vec<String> {
489        match method {
490            InterpolationMethodType::Linear => {
491                vec!["Fast".to_string(), "Memory efficient".to_string()]
492            }
493            InterpolationMethodType::CubicSpline => {
494                vec!["Smooth".to_string(), "Good for smooth data".to_string()]
495            }
496            InterpolationMethodType::BSpline => {
497                vec!["Flexible".to_string(), "Numerical stability".to_string()]
498            }
499            InterpolationMethodType::RadialBasisFunction => vec![
500                "Handles scattered data".to_string(),
501                "High accuracy".to_string(),
502            ],
503            InterpolationMethodType::Kriging => vec![
504                "Uncertainty quantification".to_string(),
505                "Optimal for spatial data".to_string(),
506            ],
507            _ => vec!["General purpose".to_string()],
508        }
509    }
510
511    /// Get method limitations
512    fn get_method_limitations(&self, method: InterpolationMethodType) -> Vec<String> {
513        match method {
514            InterpolationMethodType::Linear => {
515                vec!["Low accuracy".to_string(), "Not smooth".to_string()]
516            }
517            InterpolationMethodType::CubicSpline => vec![
518                "Sensitive to outliers".to_string(),
519                "Requires ordered data".to_string(),
520            ],
521            InterpolationMethodType::RadialBasisFunction => vec![
522                "Computationally expensive".to_string(),
523                "Memory intensive".to_string(),
524            ],
525            _ => vec!["None significant".to_string()],
526        }
527    }
528
529    /// Estimate performance for a method
530    fn estimate_performance(
531        &self,
532        method: InterpolationMethodType,
533        data_profile: &DataProfile<F>,
534    ) -> ExpectedPerformance {
535        let base_time = match method {
536            InterpolationMethodType::Linear => 10.0,
537            InterpolationMethodType::CubicSpline => 100.0,
538            InterpolationMethodType::BSpline => 150.0,
539            InterpolationMethodType::RadialBasisFunction => 1000.0,
540            InterpolationMethodType::Kriging => 2000.0,
541            _ => 500.0,
542        };
543
544        let time_factor = (data_profile.size as f64).log10();
545        let estimated_time = base_time * time_factor;
546
547        ExpectedPerformance {
548            accuracy_range: (0.95, 0.99),
549            time_range: (estimated_time * 0.5, estimated_time * 2.0),
550            memory_range: (data_profile.size * 8, data_profile.size * 32),
551            performance_score: 0.8,
552        }
553    }
554
555    /// Generate reasoning for recommendation
556    fn generate_reasoning(
557        &self,
558        method: InterpolationMethodType,
559        data_profile: &DataProfile<F>,
560    ) -> String {
561        format!(
562            "Selected {} for {} points with dimensionality {} based on data characteristics and performance history.",
563            format!("{:?}", method),
564            data_profile.size,
565            data_profile.dimensionality
566        )
567    }
568}
569
570impl<F: Float> MethodSelectionModel<F> {
571    /// Create a new method selection model
572    pub fn new() -> InterpolateResult<Self> {
573        let mut model = Self {
574            feature_weights: HashMap::new(),
575            decision_tree: Vec::new(),
576            learning_rate: 0.01,
577            model_confidence: F::from(0.8).unwrap(),
578        };
579
580        // Initialize with default rules
581        model.initialize_default_rules();
582
583        Ok(model)
584    }
585
586    /// Initialize with default selection rules
587    fn initialize_default_rules(&mut self) {
588        // Rule 1: Small smooth data -> Cubic Spline
589        self.decision_tree.push(MethodSelectionRule {
590            condition: MethodSelectionCondition::And {
591                conditions: vec![
592                    MethodSelectionCondition::DataSizeRange { min: 0, max: 1000 },
593                    MethodSelectionCondition::SmoothnessThreshold { threshold: 0.8 },
594                ],
595            },
596            method: InterpolationMethodType::CubicSpline,
597            confidence: 0.9,
598            expected_accuracy: 0.95,
599        });
600
601        // Rule 2: Large data -> Linear interpolation for speed
602        self.decision_tree.push(MethodSelectionRule {
603            condition: MethodSelectionCondition::DataSizeRange {
604                min: 100000,
605                max: usize::MAX,
606            },
607            method: InterpolationMethodType::Linear,
608            confidence: 0.7,
609            expected_accuracy: 0.8,
610        });
611
612        // Rule 3: Noisy data -> B-spline for robustness
613        self.decision_tree.push(MethodSelectionRule {
614            condition: MethodSelectionCondition::NoiseLevel { max_noise: 0.05 },
615            method: InterpolationMethodType::BSpline,
616            confidence: 0.85,
617            expected_accuracy: 0.9,
618        });
619
620        // Rule 4: High accuracy requirement -> Kriging
621        self.decision_tree.push(MethodSelectionRule {
622            condition: MethodSelectionCondition::AccuracyRequirement { min_accuracy: 0.99 },
623            method: InterpolationMethodType::Kriging,
624            confidence: 0.9,
625            expected_accuracy: 0.99,
626        });
627    }
628
629    /// Get model confidence
630    pub fn get_confidence(&self) -> F {
631        self.model_confidence
632    }
633
634    /// Update model based on feedback
635    pub fn update_model(&mut self, feedback: &[MethodPerformanceRecord]) -> InterpolateResult<()> {
636        // Simplified model update
637        if feedback.len() >= 5 {
638            let success_rate =
639                feedback.iter().filter(|r| r.success).count() as f64 / feedback.len() as f64;
640            self.model_confidence = F::from(success_rate * 0.9 + 0.1).unwrap();
641        }
642        Ok(())
643    }
644}