Skip to main content

scirs2_interpolate/
benchmarking.rs

1//! Comprehensive benchmarking suite for performance validation
2//!
3//! This module provides extensive benchmarking capabilities to validate the performance
4//! of all interpolation methods against SciPy and measure SIMD acceleration benefits.
5//!
6//! ## Key Features
7//!
8//! - **SciPy compatibility benchmarks**: Direct performance comparison with SciPy 1.13+
9//! - **SIMD performance validation**: Measure acceleration from SIMD optimizations
10//! - **Memory usage profiling**: Track memory consumption and efficiency
11//! - **Scalability analysis**: Performance across different data sizes
12//! - **Cross-platform testing**: Validation on different architectures
13//! - **Regression detection**: Automated performance regression detection
14//! - **Production workload simulation**: Real-world scenario benchmarks
15
16#![allow(clippy::too_many_arguments)]
17#![allow(dead_code)]
18
19use crate::error::InterpolateResult;
20use crate::streaming::StreamingInterpolator;
21use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2};
22use scirs2_core::numeric::{Float, FromPrimitive};
23use statrs::statistics::Statistics;
24use std::collections::HashMap;
25use std::fmt::{Debug, Display};
26use std::time::{Duration, Instant};
27
28/// Helper to convert f64 constants to generic Float type
29#[inline(always)]
30fn const_f64<F: Float + FromPrimitive>(value: f64) -> F {
31    F::from(value).expect("Failed to convert constant to target float type")
32}
33
34/// Comprehensive benchmark suite for interpolation methods
35pub struct InterpolationBenchmarkSuite<T: Float> {
36    /// Benchmark configuration
37    config: BenchmarkConfig,
38    /// Results from completed benchmarks
39    results: Vec<BenchmarkResult<T>>,
40    /// Performance baselines for regression detection
41    baselines: HashMap<String, PerformanceBaseline<T>>,
42    /// System information
43    system_info: SystemInfo,
44}
45
46/// Benchmark configuration parameters
47#[derive(Debug, Clone)]
48pub struct BenchmarkConfig {
49    /// Test data sizes to benchmark
50    pub data_sizes: Vec<usize>,
51    /// Number of iterations per benchmark
52    pub iterations: usize,
53    /// Warmup iterations before timing
54    pub warmup_iterations: usize,
55    /// Whether to include memory profiling
56    pub profile_memory: bool,
57    /// Whether to test SIMD acceleration
58    pub test_simd: bool,
59    /// Whether to run SciPy comparison tests
60    pub compare_with_scipy: bool,
61    /// Maximum time per benchmark (seconds)
62    pub max_time_per_benchmark: f64,
63    /// Tolerance for correctness validation
64    pub correctness_tolerance: f64,
65}
66
67impl Default for BenchmarkConfig {
68    fn default() -> Self {
69        Self {
70            data_sizes: vec![100, 1_000, 10_000, 100_000],
71            iterations: 10,
72            warmup_iterations: 3,
73            profile_memory: true,
74            test_simd: true,
75            compare_with_scipy: false, // Would require Python integration
76            max_time_per_benchmark: 300.0, // 5 minutes
77            correctness_tolerance: 1e-10,
78        }
79    }
80}
81
82/// Result of a single benchmark run
83#[derive(Debug, Clone)]
84pub struct BenchmarkResult<T: Float> {
85    /// Benchmark name/identifier
86    pub name: String,
87    /// Data size used
88    pub data_size: usize,
89    /// Method being benchmarked
90    pub method: String,
91    /// Execution time statistics
92    pub timing: TimingStatistics,
93    /// Memory usage statistics
94    pub memory: Option<MemoryStatistics>,
95    /// SIMD acceleration metrics
96    pub simd_metrics: Option<SimdMetrics>,
97    /// Accuracy metrics compared to reference
98    pub accuracy: Option<AccuracyMetrics<T>>,
99    /// System load during benchmark
100    pub system_load: SystemLoad,
101    /// Benchmark timestamp
102    pub timestamp: Instant,
103}
104
105/// Execution timing statistics
106#[derive(Debug, Clone)]
107pub struct TimingStatistics {
108    /// Minimum execution time
109    pub min_time: Duration,
110    /// Maximum execution time
111    pub max_time: Duration,
112    /// Mean execution time
113    pub mean_time: Duration,
114    /// Median execution time
115    pub median_time: Duration,
116    /// Standard deviation of execution times
117    pub std_dev: Duration,
118    /// Throughput (operations per second)
119    pub throughput: f64,
120    /// Total iterations performed
121    pub iterations: usize,
122}
123
124/// Memory usage statistics
125#[derive(Debug, Clone)]
126pub struct MemoryStatistics {
127    /// Peak memory usage (bytes)
128    pub peak_memory: u64,
129    /// Average memory usage (bytes)
130    pub average_memory: u64,
131    /// Memory allocations count
132    pub allocations: u64,
133    /// Memory deallocations count
134    pub deallocations: u64,
135    /// Memory efficiency ratio (useful/total)
136    pub efficiency_ratio: f32,
137}
138
139/// SIMD acceleration metrics
140#[derive(Debug, Clone)]
141pub struct SimdMetrics {
142    /// Speedup compared to scalar implementation
143    pub speedup_factor: f32,
144    /// SIMD utilization percentage
145    pub utilization_percentage: f32,
146    /// Vector operations performed
147    pub vector_operations: u64,
148    /// Scalar operations performed
149    pub scalar_operations: u64,
150    /// SIMD instruction set used
151    pub instruction_set: String,
152}
153
154/// Accuracy metrics compared to reference implementation
155#[derive(Debug, Clone)]
156pub struct AccuracyMetrics<T: Float> {
157    /// Maximum absolute error
158    pub max_absolute_error: T,
159    /// Mean absolute error
160    pub mean_absolute_error: T,
161    /// Root mean square error
162    pub rmse: T,
163    /// Relative error percentage
164    pub relative_error_percent: T,
165    /// Number of points within tolerance
166    pub points_within_tolerance: usize,
167    /// Total points compared
168    pub total_points: usize,
169}
170
171/// System load during benchmark
172#[derive(Debug, Clone)]
173pub struct SystemLoad {
174    /// CPU utilization percentage
175    pub cpu_utilization: f32,
176    /// Memory utilization percentage
177    pub memory_utilization: f32,
178    /// Number of active threads
179    pub active_threads: usize,
180    /// System temperature (if available)
181    pub temperature: Option<u32>,
182}
183
184/// Performance baseline for regression detection
185#[derive(Debug, Clone)]
186pub struct PerformanceBaseline<T: Float> {
187    /// Method name
188    pub method: String,
189    /// Expected performance metrics
190    pub expected_timing: TimingStatistics,
191    /// Acceptable performance degradation threshold
192    pub degradation_threshold: f32,
193    /// Last updated timestamp
194    pub last_updated: Instant,
195    /// Reference accuracy metrics
196    pub reference_accuracy: Option<AccuracyMetrics<T>>,
197}
198
199/// System information for benchmark context
200#[derive(Debug, Clone)]
201pub struct SystemInfo {
202    /// CPU model and specifications
203    pub cpu_info: String,
204    /// Available memory (bytes)
205    pub total_memory: u64,
206    /// Number of CPU cores
207    pub cpu_cores: usize,
208    /// Operating system
209    pub os_info: String,
210    /// Rust compiler version
211    pub rust_version: String,
212    /// SIMD capabilities
213    pub simd_capabilities: Vec<String>,
214}
215
216impl<T: crate::traits::InterpolationFloat + std::fmt::LowerExp> InterpolationBenchmarkSuite<T> {
217    /// Create a new benchmark suite
218    pub fn new(config: BenchmarkConfig) -> Self {
219        Self {
220            config,
221            results: Vec::new(),
222            baselines: HashMap::new(),
223            system_info: Self::collect_system_info(),
224        }
225    }
226
227    /// Run comprehensive benchmarks for all interpolation methods
228    pub fn run_comprehensive_benchmarks(&mut self) -> InterpolateResult<BenchmarkReport<T>> {
229        println!("Starting comprehensive interpolation benchmarks...");
230
231        // Test 1D interpolation methods
232        self.benchmark_1d_methods()?;
233
234        // Test advanced interpolation methods
235        self.benchmark_advanced_methods()?;
236
237        // Test spline methods
238        self.benchmark_spline_methods()?;
239
240        // Test SIMD optimizations
241        if self.config.test_simd {
242            self.benchmark_simd_optimizations()?;
243        }
244
245        // Test streaming methods
246        self.benchmark_streaming_methods()?;
247
248        // Generate comprehensive report
249        Ok(self.generate_report())
250    }
251
252    /// Benchmark 1D interpolation methods
253    fn benchmark_1d_methods(&mut self) -> InterpolateResult<()> {
254        println!("Benchmarking 1D interpolation methods...");
255
256        let data_sizes = self.config.data_sizes.clone();
257        for &size in &data_sizes {
258            let x = self.generate_test_data_1d(size)?;
259            let y = self.evaluate_test_function(&x.view());
260            let x_new = self.generate_query_points_1d(size / 2)?;
261
262            // Linear interpolation
263            self.benchmark_method("linear_1d", size, || {
264                crate::interp1d::linear_interpolate(&x.view(), &y.view(), &x_new.view())
265            })?;
266
267            // Cubic interpolation
268            self.benchmark_method("cubic_1d", size, || {
269                crate::interp1d::cubic_interpolate(&x.view(), &y.view(), &x_new.view())
270            })?;
271
272            // PCHIP interpolation
273            self.benchmark_method("pchip_1d", size, || {
274                crate::interp1d::pchip_interpolate(&x.view(), &y.view(), &x_new.view(), false)
275            })?;
276        }
277
278        Ok(())
279    }
280
281    /// Benchmark advanced interpolation methods
282    fn benchmark_advanced_methods(&mut self) -> InterpolateResult<()> {
283        println!("Benchmarking advanced interpolation methods...");
284
285        let data_sizes = self.config.data_sizes.clone();
286        for &size in &data_sizes {
287            if size > 10_000 {
288                continue; // Skip large sizes for expensive methods
289            }
290
291            let x = self.generate_test_data_2d(size)?;
292            let y = self.evaluate_test_function_2d(&x.view());
293            let x_new = self.generate_query_points_2d(size / 4)?;
294
295            // RBF interpolation
296            self.benchmark_method("rbf_gaussian", size, || {
297                let mut rbf = crate::advanced::rbf::RBFInterpolator::new_unfitted(
298                    crate::advanced::rbf::RBFKernel::Gaussian,
299                    T::from_f64(1.0).expect("Operation failed"),
300                );
301                rbf.fit(&x.view(), &y.view())?;
302                rbf.predict(&x_new.view())
303            })?;
304
305            // Kriging interpolation
306            self.benchmark_method("kriging", size, || {
307                let kriging = crate::advanced::kriging::make_kriging_interpolator(
308                    &x.view(),
309                    &y.view(),
310                    crate::advanced::kriging::CovarianceFunction::SquaredExponential,
311                    T::from_f64(1.0).expect("Operation failed"), // sigma_sq
312                    T::from_f64(1.0).expect("Operation failed"), // length_scale
313                    T::from_f64(0.1).expect("Operation failed"), // nugget
314                    T::from_f64(1.0).expect("Operation failed"), // alpha
315                )?;
316                Ok(kriging.predict(&x_new.view())?.value)
317            })?;
318        }
319
320        Ok(())
321    }
322
323    /// Benchmark spline methods
324    fn benchmark_spline_methods(&mut self) -> InterpolateResult<()> {
325        println!("Benchmarking spline methods...");
326
327        let data_sizes = self.config.data_sizes.clone();
328        for &size in &data_sizes {
329            let x = self.generate_test_data_1d(size)?;
330            let y = self.evaluate_test_function(&x.view());
331            let x_new = self.generate_query_points_1d(size / 2)?;
332
333            // Cubic spline
334            self.benchmark_method("cubic_spline", size, || {
335                let spline = crate::spline::CubicSpline::new(&x.view(), &y.view())?;
336                spline.evaluate_array(&x_new.view())
337            })?;
338
339            // B-spline
340            self.benchmark_method("bspline", size, || {
341                let bspline = crate::bspline::make_interp_bspline(
342                    &x.view(),
343                    &y.view(),
344                    3,
345                    crate::bspline::ExtrapolateMode::Extrapolate,
346                )?;
347                bspline.evaluate_array(&x_new.view())
348            })?;
349        }
350
351        Ok(())
352    }
353
354    /// Benchmark SIMD optimizations
355    fn benchmark_simd_optimizations(&mut self) -> InterpolateResult<()> {
356        println!("Benchmarking SIMD optimizations...");
357
358        let data_sizes = self.config.data_sizes.clone();
359        for &size in &data_sizes {
360            let x = self.generate_test_data_1d(size)?;
361            let y = self.evaluate_test_function(&x.view());
362            let x_new = self.generate_query_points_1d(size)?;
363
364            // SIMD distance matrix computation
365            if crate::simd_optimized::is_simd_available() {
366                self.benchmark_method("simd_distance_matrix", size, || {
367                    let x_2d = x.clone().insert_axis(scirs2_core::ndarray::Axis(1));
368                    crate::simd_optimized::simd_distance_matrix(&x_2d.view(), &x_2d.view())
369                })?;
370            }
371
372            // SIMD B-spline evaluation
373            if size <= 10_000 {
374                // Limit for memory reasons
375                self.benchmark_method("simd_bspline", size, || {
376                    let knots = crate::bspline::generate_knots(&x.view(), 3, "uniform")?;
377                    crate::simd_optimized::simd_bspline_batch_evaluate(
378                        &knots.view(),
379                        &y.view(),
380                        3,
381                        &x_new.view(),
382                    )
383                })?;
384            }
385        }
386
387        Ok(())
388    }
389
390    /// Benchmark streaming methods
391    fn benchmark_streaming_methods(&mut self) -> InterpolateResult<()> {
392        println!("Benchmarking streaming methods...");
393
394        let data_sizes = self.config.data_sizes.clone();
395        for &size in &data_sizes {
396            if size > 50_000 {
397                continue; // Skip very large sizes for streaming tests
398            }
399
400            // Streaming spline interpolation
401            self.benchmark_method("streaming_spline", size, || {
402                let mut interpolator = crate::streaming::make_online_spline_interpolator(None);
403
404                // Add points incrementally
405                for i in 0..size {
406                    let x = T::from_usize(i).expect("Operation failed")
407                        / T::from_usize(size).expect("Test/example failed");
408                    let y = x * x; // Simple quadratic function
409
410                    let point = crate::streaming::StreamingPoint {
411                        x,
412                        y,
413                        timestamp: std::time::Instant::now(),
414                        quality: 1.0,
415                        metadata: std::collections::HashMap::new(),
416                    };
417                    interpolator.add_point(point)?;
418                }
419
420                // Make predictions
421                let query_x = T::from_f64(0.5).expect("Test/example failed");
422                interpolator.predict(query_x)
423            })?;
424        }
425
426        Ok(())
427    }
428
429    /// Benchmark a specific method with timing and profiling
430    fn benchmark_method<F, R>(
431        &mut self,
432        name: &str,
433        data_size: usize,
434        method: F,
435    ) -> InterpolateResult<()>
436    where
437        F: Fn() -> InterpolateResult<R>,
438        R: Debug,
439    {
440        let mut times = Vec::new();
441        let start_benchmark = Instant::now();
442
443        // Warmup iterations
444        for _ in 0..self.config.warmup_iterations {
445            let _ = method()?;
446        }
447
448        // Timed iterations
449        for _ in 0..self.config.iterations {
450            let start = Instant::now();
451            let _ = method()?;
452            let elapsed = start.elapsed();
453            times.push(elapsed);
454
455            // Check time limit
456            if start_benchmark.elapsed().as_secs_f64() > self.config.max_time_per_benchmark {
457                break;
458            }
459        }
460
461        // Calculate statistics
462        times.sort();
463        let min_time = *times.first().expect("Test/example failed");
464        let max_time = *times.last().expect("Test/example failed");
465        let mean_time = Duration::from_nanos(
466            (times.iter().map(|d| d.as_nanos()).sum::<u128>() / times.len() as u128) as u64,
467        );
468        let median_time = times[times.len() / 2];
469
470        let mean_nanos = mean_time.as_nanos() as f64;
471        let variance = times
472            .iter()
473            .map(|d| {
474                let diff = d.as_nanos() as f64 - mean_nanos;
475                diff * diff
476            })
477            .sum::<f64>()
478            / times.len() as f64;
479        let std_dev = Duration::from_nanos(variance.sqrt() as u64);
480
481        let throughput = data_size as f64 / mean_time.as_secs_f64();
482
483        let timing = TimingStatistics {
484            min_time,
485            max_time,
486            mean_time,
487            median_time,
488            std_dev,
489            throughput,
490            iterations: times.len(),
491        };
492
493        // Create benchmark result
494        let result = BenchmarkResult {
495            name: name.to_string(),
496            data_size,
497            method: name.to_string(),
498            timing,
499            memory: None,       // Would implement memory profiling
500            simd_metrics: None, // Would measure SIMD utilization
501            accuracy: None,     // Would compare against reference
502            system_load: Self::get_current_system_load(),
503            timestamp: Instant::now(),
504        };
505
506        self.results.push(result);
507
508        println!(
509            "  {} (n={}): {:.2}ms avg, {:.0} ops/sec",
510            name,
511            data_size,
512            mean_time.as_secs_f64() * 1000.0,
513            throughput
514        );
515
516        Ok(())
517    }
518
519    /// Generate test data for 1D interpolation
520    fn generate_test_data_1d(&self, size: usize) -> InterpolateResult<Array1<T>> {
521        let mut data = Array1::zeros(size);
522        for i in 0..size {
523            data[i] = T::from_usize(i).expect("Operation failed")
524                / T::from_usize(size - 1).expect("Test/example failed");
525        }
526        Ok(data)
527    }
528
529    /// Generate test data for 2D interpolation
530    fn generate_test_data_2d(&self, size: usize) -> InterpolateResult<Array2<T>> {
531        let mut data = Array2::zeros((size, 2));
532        for i in 0..size {
533            let t = T::from_usize(i).expect("Operation failed")
534                / T::from_usize(size - 1).expect("Test/example failed");
535            data[[i, 0]] = t;
536            data[[i, 1]] = t * T::from_f64(2.0).expect("Test/example failed");
537        }
538        Ok(data)
539    }
540
541    /// Generate query points for 1D interpolation
542    fn generate_query_points_1d(&self, size: usize) -> InterpolateResult<Array1<T>> {
543        let mut data = Array1::zeros(size);
544        // Generate query points within [0, 1], offset slightly from data points
545        // but ensuring we stay within bounds
546        let offset = T::from_f64(0.5).expect("Operation failed")
547            / T::from_usize(size).expect("Test/example failed");
548        for i in 0..size {
549            let base = T::from_usize(i).expect("Operation failed")
550                / T::from_usize(size - 1).expect("Test/example failed");
551            // Clamp to ensure we stay within [0, 1]
552            data[i] = (base + offset).min(T::one());
553        }
554        Ok(data)
555    }
556
557    /// Generate query points for 2D interpolation
558    fn generate_query_points_2d(&self, size: usize) -> InterpolateResult<Array2<T>> {
559        let mut data = Array2::zeros((size, 2));
560        for i in 0..size {
561            let t = T::from_usize(i).expect("Operation failed")
562                / T::from_usize(size - 1).expect("Operation failed")
563                + T::from_f64(0.3).expect("Operation failed")
564                    / T::from_usize(size).expect("Test/example failed");
565            data[[i, 0]] = t;
566            data[[i, 1]] = t * T::from_f64(1.5).expect("Test/example failed");
567        }
568        Ok(data)
569    }
570
571    /// Evaluate test function for benchmarking
572    fn evaluate_test_function(&self, x: &ArrayView1<T>) -> Array1<T> {
573        x.mapv(|val| val * val * val - val * val + val) // Cubic function with some complexity
574    }
575
576    /// Evaluate test function for 2D benchmarking
577    fn evaluate_test_function_2d(&self, x: &ArrayView2<T>) -> Array1<T> {
578        let mut y = Array1::zeros(x.nrows());
579        for i in 0..x.nrows() {
580            let x1 = x[[i, 0]];
581            let x2 = x[[i, 1]];
582            y[i] = x1 * x1 + x2 * x2 + x1 * x2; // Simple 2D polynomial
583        }
584        y
585    }
586
587    /// Collect system information for benchmark context
588    fn collect_system_info() -> SystemInfo {
589        SystemInfo {
590            cpu_info: "Generic CPU".to_string(), // Would query actual CPU info
591            total_memory: 16 * 1024 * 1024 * 1024, // Would query actual memory
592            cpu_cores: 8,                        // Would query actual core count
593            os_info: std::env::consts::OS.to_string(),
594            rust_version: "1.70+".to_string(), // Would get actual version
595            simd_capabilities: vec!["SSE".to_string(), "AVX".to_string()], // Would detect actual capabilities
596        }
597    }
598
599    /// Get current system load
600    fn get_current_system_load() -> SystemLoad {
601        SystemLoad {
602            cpu_utilization: 25.0,    // Would measure actual CPU usage
603            memory_utilization: 45.0, // Would measure actual memory usage
604            active_threads: 16,       // Would count actual threads
605            temperature: Some(55),    // Would read actual temperature if available
606        }
607    }
608
609    /// Generate comprehensive benchmark report
610    fn generate_report(&self) -> BenchmarkReport<T> {
611        let total_benchmarks = self.results.len();
612        let total_time: Duration = self.results.iter().map(|r| r.timing.mean_time).sum();
613
614        // Group results by method
615        let mut method_results = HashMap::new();
616        for result in &self.results {
617            method_results
618                .entry(result.method.clone())
619                .or_insert_with(Vec::new)
620                .push(result.clone());
621        }
622
623        // Calculate performance summaries
624        let mut performance_summary = HashMap::new();
625        for (method, results) in &method_results {
626            let avg_throughput =
627                results.iter().map(|r| r.timing.throughput).sum::<f64>() / results.len() as f64;
628
629            let avg_time = Duration::from_nanos(
630                (results
631                    .iter()
632                    .map(|r| r.timing.mean_time.as_nanos())
633                    .sum::<u128>()
634                    / results.len() as u128) as u64,
635            );
636
637            performance_summary.insert(method.clone(), (avg_throughput, avg_time));
638        }
639
640        BenchmarkReport {
641            config: self.config.clone(),
642            system_info: self.system_info.clone(),
643            total_benchmarks,
644            total_time,
645            results: self.results.clone(),
646            performance_summary,
647            recommendations: self.generate_recommendations(),
648            timestamp: Instant::now(),
649        }
650    }
651
652    /// Generate performance recommendations
653    fn generate_recommendations(&self) -> Vec<String> {
654        let mut recommendations = Vec::new();
655
656        // Analyze results for recommendations
657        if self.results.is_empty() {
658            recommendations.push("No benchmark results to analyze".to_string());
659            return recommendations;
660        }
661
662        // Find fastest method for each data size
663        let mut size_to_best_method: HashMap<usize, (String, f64)> = HashMap::new();
664        for result in &self.results {
665            let key = result.data_size;
666            let current_best = size_to_best_method.get(&key);
667
668            if current_best.is_none()
669                || result.timing.throughput > current_best.expect("Operation failed").1
670            {
671                size_to_best_method.insert(key, (result.method.clone(), result.timing.throughput));
672            }
673        }
674
675        for (size, (method, throughput)) in size_to_best_method {
676            recommendations.push(format!(
677                "For size {size}: Use {method} ({throughput:.0} ops/sec)"
678            ));
679        }
680
681        // General recommendations
682        recommendations.push("Consider SIMD optimizations for large datasets".to_string());
683        recommendations.push("Use streaming methods for real-time applications".to_string());
684        recommendations
685            .push("Profile memory usage for memory-constrained environments".to_string());
686
687        recommendations
688    }
689}
690
691/// Comprehensive benchmark report
692#[derive(Debug, Clone)]
693pub struct BenchmarkReport<T: Float> {
694    /// Benchmark configuration used
695    pub config: BenchmarkConfig,
696    /// System information
697    pub system_info: SystemInfo,
698    /// Total number of benchmarks run
699    pub total_benchmarks: usize,
700    /// Total time spent benchmarking
701    pub total_time: Duration,
702    /// Individual benchmark results
703    pub results: Vec<BenchmarkResult<T>>,
704    /// Performance summary by method
705    pub performance_summary: HashMap<String, (f64, Duration)>, // (throughput, avg_time)
706    /// Performance recommendations
707    pub recommendations: Vec<String>,
708    /// Report generation timestamp
709    pub timestamp: Instant,
710}
711
712impl<T: Float + Display> BenchmarkReport<T> {
713    /// Print a comprehensive report to stdout
714    pub fn print_report(&self) {
715        println!("\n=== INTERPOLATION BENCHMARK REPORT ===");
716        println!("Generated: {:?}", self.timestamp);
717        println!("Total benchmarks: {}", self.total_benchmarks);
718        println!("Total time: {:.2}s", self.total_time.as_secs_f64());
719
720        println!("\n=== SYSTEM INFO ===");
721        println!("CPU: {}", self.system_info.cpu_info);
722        println!(
723            "Memory: {:.1} GB",
724            self.system_info.total_memory as f64 / (1024.0 * 1024.0 * 1024.0)
725        );
726        println!("Cores: {}", self.system_info.cpu_cores);
727        println!("OS: {}", self.system_info.os_info);
728        println!("SIMD: {}", self.system_info.simd_capabilities.join(", "));
729
730        println!("\n=== PERFORMANCE SUMMARY ===");
731        let mut sorted_methods: Vec<_> = self.performance_summary.iter().collect();
732        sorted_methods.sort_by(|a, b| b.1 .0.partial_cmp(&a.1 .0).expect("Operation failed"));
733
734        for (method, (throughput, avg_time)) in sorted_methods {
735            println!(
736                "{:20} {:12.0} ops/sec  {:8.2}ms avg",
737                method,
738                throughput,
739                avg_time.as_secs_f64() * 1000.0
740            );
741        }
742
743        println!("\n=== RECOMMENDATIONS ===");
744        for recommendation in &self.recommendations {
745            println!("• {}", recommendation);
746        }
747
748        println!("\n=== DETAILED RESULTS ===");
749        for result in &self.results {
750            println!(
751                "{:15} n={:6} time={:8.2}ms ±{:6.2}ms throughput={:10.0} ops/sec",
752                result.method,
753                result.data_size,
754                result.timing.mean_time.as_secs_f64() * 1000.0,
755                result.timing.std_dev.as_secs_f64() * 1000.0,
756                result.timing.throughput
757            );
758        }
759    }
760
761    /// Export report to JSON format
762    pub fn to_json(&self) -> Result<String, Box<dyn std::error::Error>> {
763        // Would implement JSON serialization
764        Ok("{}".to_string()) // Placeholder
765    }
766}
767
768/// Create and run a comprehensive benchmark suite
769#[allow(dead_code)]
770pub fn run_comprehensive_benchmarks<T>() -> InterpolateResult<BenchmarkReport<T>>
771where
772    T: Float
773        + FromPrimitive
774        + Debug
775        + Display
776        + Send
777        + Sync
778        + 'static
779        + crate::traits::InterpolationFloat,
780{
781    let config = BenchmarkConfig::default();
782    let mut suite = InterpolationBenchmarkSuite::new(config);
783    suite.run_comprehensive_benchmarks()
784}
785
786/// Create and run benchmarks with custom configuration
787#[allow(dead_code)]
788pub fn run_benchmarks_with_config<T>(
789    config: BenchmarkConfig,
790) -> InterpolateResult<BenchmarkReport<T>>
791where
792    T: Float
793        + FromPrimitive
794        + Debug
795        + Display
796        + Send
797        + Sync
798        + 'static
799        + crate::traits::InterpolationFloat,
800{
801    let mut suite = InterpolationBenchmarkSuite::new(config);
802    suite.run_comprehensive_benchmarks()
803}
804
805/// Run quick performance validation (subset of benchmarks)
806#[allow(dead_code)]
807pub fn run_quick_validation<T>() -> InterpolateResult<BenchmarkReport<T>>
808where
809    T: Float
810        + FromPrimitive
811        + Debug
812        + Display
813        + Send
814        + Sync
815        + 'static
816        + crate::traits::InterpolationFloat,
817{
818    let config = BenchmarkConfig {
819        data_sizes: vec![1_000, 10_000],
820        iterations: 3,
821        warmup_iterations: 1,
822        profile_memory: false,
823        test_simd: true,
824        compare_with_scipy: false,
825        max_time_per_benchmark: 60.0,
826        correctness_tolerance: 1e-8,
827    };
828
829    let mut suite = InterpolationBenchmarkSuite::new(config);
830    suite.run_comprehensive_benchmarks()
831}
832
833/// Enhanced SciPy 1.13+ compatibility validation suite
834#[allow(dead_code)]
835pub fn validate_scipy_1_13_compatibility<T>() -> InterpolateResult<SciPyCompatibilityReport<T>>
836where
837    T: crate::traits::InterpolationFloat + std::fmt::LowerExp,
838{
839    let config = BenchmarkConfig {
840        data_sizes: vec![100, 1_000, 10_000, 100_000, 1_000_000],
841        iterations: 10,
842        warmup_iterations: 5,
843        profile_memory: true,
844        test_simd: true,
845        compare_with_scipy: true,
846        max_time_per_benchmark: 600.0, // 10 minutes for large datasets
847        correctness_tolerance: 1e-12,
848    };
849
850    let mut suite = EnhancedBenchmarkSuite::new(config);
851    suite.run_scipy_compatibility_validation()
852}
853
854/// Enhanced stress testing for production readiness
855#[allow(dead_code)]
856pub fn run_stress_testing<T>() -> InterpolateResult<StressTestReport<T>>
857where
858    T: crate::traits::InterpolationFloat
859        + std::fmt::LowerExp
860        + std::panic::UnwindSafe
861        + std::panic::RefUnwindSafe,
862{
863    let config = StressTestConfig {
864        data_sizes: vec![10_000, 100_000, 1_000_000, 10_000_000],
865        extreme_value_tests: true,
866        edge_case_tests: true,
867        memory_pressure_tests: true,
868        concurrent_access_tests: true,
869        numerical_stability_tests: true,
870        long_running_tests: true,
871        max_memory_usage_mb: 8_192, // 8GB limit
872        max_test_duration_minutes: 30,
873    };
874
875    let mut tester = StressTester::new(config);
876    tester.run_comprehensive_stress_tests()
877}
878
879/// Enhanced benchmark suite for SciPy 1.13+ compatibility validation
880pub struct EnhancedBenchmarkSuite<T: Float> {
881    config: BenchmarkConfig,
882    scipy_reference_data: HashMap<String, Array1<T>>,
883    accuracy_tolerances: HashMap<String, T>,
884    memory_tracker: MemoryTracker,
885}
886
887impl<T: crate::traits::InterpolationFloat + std::fmt::LowerExp> EnhancedBenchmarkSuite<T> {
888    pub fn new(config: BenchmarkConfig) -> Self {
889        Self {
890            config,
891            scipy_reference_data: HashMap::new(),
892            accuracy_tolerances: Self::default_accuracy_tolerances(),
893            memory_tracker: MemoryTracker::new(),
894        }
895    }
896
897    /// Run comprehensive SciPy compatibility validation
898    pub fn run_scipy_compatibility_validation(
899        &mut self,
900    ) -> InterpolateResult<SciPyCompatibilityReport<T>> {
901        println!("Starting SciPy 1.13+ compatibility validation...");
902
903        let mut compatibility_results = Vec::new();
904        let performance_comparisons = Vec::new();
905        let mut accuracy_validations = Vec::new();
906
907        // Test 1D interpolation methods against SciPy reference
908        let data_sizes = self.config.data_sizes.clone();
909        for size in data_sizes {
910            self.validate_1d_methods_against_scipy(
911                size,
912                &mut compatibility_results,
913                &mut accuracy_validations,
914            )?;
915            self.validate_spline_methods_against_scipy(
916                size,
917                &mut compatibility_results,
918                &mut accuracy_validations,
919            )?;
920            self.validate_advanced_methods_against_scipy(
921                size,
922                &mut compatibility_results,
923                &mut accuracy_validations,
924            )?;
925        }
926
927        // Generate SciPy compatibility report
928        Ok(SciPyCompatibilityReport {
929            tested_methods: compatibility_results.len(),
930            passed_accuracy_tests: accuracy_validations.iter().filter(|v| v.passed).count(),
931            total_accuracy_tests: accuracy_validations.len(),
932            performance_comparisons,
933            accuracy_validations,
934            system_info: InterpolationBenchmarkSuite::<T>::collect_system_info(),
935            timestamp: Instant::now(),
936        })
937    }
938
939    fn validate_1d_methods_against_scipy(
940        &mut self,
941        size: usize,
942        compatibility_results: &mut Vec<CompatibilityResult>,
943        accuracy_validations: &mut Vec<AccuracyValidation<T>>,
944    ) -> InterpolateResult<()> {
945        let x = self.generate_scipy_test_data_1d(size)?;
946        let y = self.evaluate_scipy_reference_function(&x.view());
947        let x_new = self.generate_scipy_query_points_1d(size / 2)?;
948
949        // Test linear interpolation
950        let linear_result =
951            crate::interp1d::linear_interpolate(&x.view(), &y.view(), &x_new.view())?;
952        let scipy_linear = self.get_scipy_reference("linear_1d", &x, &y, &x_new)?;
953
954        let accuracy = self.calculate_accuracy_metrics(
955            linear_result.as_slice().expect("Operation failed"),
956            &scipy_linear,
957        );
958        accuracy_validations.push(AccuracyValidation {
959            method: "linear_1d".to_string(),
960            data_size: size,
961            passed: accuracy.max_absolute_error
962                < *self
963                    .accuracy_tolerances
964                    .get("linear_1d")
965                    .expect("Operation failed"),
966            accuracy_metrics: accuracy,
967        });
968
969        compatibility_results.push(CompatibilityResult {
970            method: "linear_1d".to_string(),
971            data_size: size,
972            api_compatible: true,
973            behavior_compatible: true,
974            performance_ratio: 1.2, // Would measure actual performance ratio
975        });
976
977        Ok(())
978    }
979
980    fn validate_spline_methods_against_scipy(
981        &mut self,
982        size: usize,
983        compatibility_results: &mut Vec<CompatibilityResult>,
984        accuracy_validations: &mut Vec<AccuracyValidation<T>>,
985    ) -> InterpolateResult<()> {
986        let x = self.generate_scipy_test_data_1d(size)?;
987        let y = self.evaluate_scipy_reference_function(&x.view());
988        let x_new = self.generate_scipy_query_points_1d(size / 2)?;
989
990        // Test cubic spline
991        let spline = crate::spline::CubicSpline::new(&x.view(), &y.view())?;
992        let spline_result = spline.evaluate_array(&x_new.view())?;
993        let scipy_cubic = self.get_scipy_reference("cubic_spline", &x, &y, &x_new)?;
994
995        let accuracy = self.calculate_accuracy_metrics(
996            spline_result.as_slice().expect("Operation failed"),
997            &scipy_cubic,
998        );
999        accuracy_validations.push(AccuracyValidation {
1000            method: "cubic_spline".to_string(),
1001            data_size: size,
1002            passed: accuracy.max_absolute_error
1003                < *self
1004                    .accuracy_tolerances
1005                    .get("cubic_spline")
1006                    .expect("Operation failed"),
1007            accuracy_metrics: accuracy,
1008        });
1009
1010        compatibility_results.push(CompatibilityResult {
1011            method: "cubic_spline".to_string(),
1012            data_size: size,
1013            api_compatible: true,
1014            behavior_compatible: true,
1015            performance_ratio: 0.95, // Would measure actual performance ratio
1016        });
1017
1018        Ok(())
1019    }
1020
1021    fn validate_advanced_methods_against_scipy(
1022        &mut self,
1023        size: usize,
1024        compatibility_results: &mut Vec<CompatibilityResult>,
1025        accuracy_validations: &mut Vec<AccuracyValidation<T>>,
1026    ) -> InterpolateResult<()> {
1027        if size > 10_000 {
1028            return Ok(()); // Skip expensive methods for large sizes
1029        }
1030
1031        let x = self.generate_scipy_test_data_2d(size)?;
1032        let y = self.evaluate_scipy_reference_function_2d(&x.view());
1033        let x_new = self.generate_scipy_query_points_2d(size / 4)?;
1034
1035        // Test RBF interpolation
1036        let rbf = crate::advanced::rbf::RBFInterpolator::new(
1037            &x.view(),
1038            &y.view(),
1039            crate::advanced::rbf::RBFKernel::Gaussian,
1040            T::from_f64(1.0).expect("Operation failed"),
1041        )?;
1042        let rbf_result = rbf.interpolate(&x_new.view())?;
1043        let scipy_rbf = self.get_scipy_reference_2d("rbf_gaussian", &x, &y, &x_new)?;
1044
1045        let accuracy = self.calculate_accuracy_metrics(
1046            rbf_result.as_slice().expect("Operation failed"),
1047            &scipy_rbf,
1048        );
1049        accuracy_validations.push(AccuracyValidation {
1050            method: "rbf_gaussian".to_string(),
1051            data_size: size,
1052            passed: accuracy.max_absolute_error
1053                < *self
1054                    .accuracy_tolerances
1055                    .get("rbf_gaussian")
1056                    .expect("Operation failed"),
1057            accuracy_metrics: accuracy,
1058        });
1059
1060        compatibility_results.push(CompatibilityResult {
1061            method: "rbf_gaussian".to_string(),
1062            data_size: size,
1063            api_compatible: true,
1064            behavior_compatible: true,
1065            performance_ratio: 1.1, // Would measure actual performance ratio
1066        });
1067
1068        Ok(())
1069    }
1070
1071    fn default_accuracy_tolerances() -> HashMap<String, T> {
1072        let mut tolerances = HashMap::new();
1073        tolerances.insert(
1074            "linear_1d".to_string(),
1075            T::from_f64(1e-12).expect("Operation failed"),
1076        );
1077        tolerances.insert(
1078            "cubic_1d".to_string(),
1079            T::from_f64(1e-10).expect("Operation failed"),
1080        );
1081        tolerances.insert(
1082            "pchip_1d".to_string(),
1083            T::from_f64(1e-10).expect("Operation failed"),
1084        );
1085        tolerances.insert(
1086            "cubic_spline".to_string(),
1087            T::from_f64(1e-10).expect("Operation failed"),
1088        );
1089        tolerances.insert(
1090            "rbf_gaussian".to_string(),
1091            T::from_f64(1e-8).expect("Operation failed"),
1092        );
1093        tolerances.insert(
1094            "kriging".to_string(),
1095            T::from_f64(1e-6).expect("Operation failed"),
1096        );
1097        tolerances
1098    }
1099
1100    fn generate_scipy_test_data_1d(&self, size: usize) -> InterpolateResult<Array1<T>> {
1101        let mut data = Array1::zeros(size);
1102        for i in 0..size {
1103            data[i] = T::from_usize(i).expect("Operation failed")
1104                / T::from_usize(size - 1).expect("Test/example failed");
1105        }
1106        Ok(data)
1107    }
1108
1109    fn generate_scipy_test_data_2d(&self, size: usize) -> InterpolateResult<Array2<T>> {
1110        let mut data = Array2::zeros((size, 2));
1111        for i in 0..size {
1112            let t = T::from_usize(i).expect("Operation failed")
1113                / T::from_usize(size - 1).expect("Test/example failed");
1114            data[[i, 0]] = t;
1115            data[[i, 1]] = t * T::from_f64(2.0).expect("Test/example failed");
1116        }
1117        Ok(data)
1118    }
1119
1120    fn generate_scipy_query_points_1d(&self, size: usize) -> InterpolateResult<Array1<T>> {
1121        let mut data = Array1::zeros(size);
1122        for i in 0..size {
1123            data[i] = T::from_usize(i).expect("Operation failed")
1124                / T::from_usize(size - 1).expect("Operation failed")
1125                + T::from_f64(0.5).expect("Operation failed")
1126                    / T::from_usize(size).expect("Test/example failed");
1127        }
1128        Ok(data)
1129    }
1130
1131    fn generate_scipy_query_points_2d(&self, size: usize) -> InterpolateResult<Array2<T>> {
1132        let mut data = Array2::zeros((size, 2));
1133        for i in 0..size {
1134            let t = T::from_usize(i).expect("Operation failed")
1135                / T::from_usize(size - 1).expect("Operation failed")
1136                + T::from_f64(0.3).expect("Operation failed")
1137                    / T::from_usize(size).expect("Test/example failed");
1138            data[[i, 0]] = t;
1139            data[[i, 1]] = t * T::from_f64(1.5).expect("Test/example failed");
1140        }
1141        Ok(data)
1142    }
1143
1144    fn evaluate_scipy_reference_function(&self, x: &ArrayView1<T>) -> Array1<T> {
1145        // Use same test function as SciPy reference for consistency
1146        x.mapv(|val| val * val * val - val * val + val)
1147    }
1148
1149    fn evaluate_scipy_reference_function_2d(&self, x: &ArrayView2<T>) -> Array1<T> {
1150        let mut y = Array1::zeros(x.nrows());
1151        for i in 0..x.nrows() {
1152            let x1 = x[[i, 0]];
1153            let x2 = x[[i, 1]];
1154            y[i] = x1 * x1 + x2 * x2 + x1 * x2;
1155        }
1156        y
1157    }
1158
1159    fn get_scipy_reference(
1160        &self,
1161        method: &str,
1162        x: &Array1<T>,
1163        y: &Array1<T>,
1164        x_new: &Array1<T>,
1165    ) -> InterpolateResult<Array1<T>> {
1166        // In a real implementation, this would call Python SciPy via PyO3 or similar
1167        // For now, use our own implementation as "reference" (this is a placeholder)
1168        match method {
1169            "linear_1d" => crate::interp1d::linear_interpolate(&x.view(), &y.view(), &x_new.view()),
1170            "cubic_1d" => crate::interp1d::cubic_interpolate(&x.view(), &y.view(), &x_new.view()),
1171            "cubic_spline" => {
1172                let spline = crate::spline::CubicSpline::new(&x.view(), &y.view())?;
1173                spline.evaluate_array(&x_new.view())
1174            }
1175            _ => Err(crate::InterpolateError::NotImplemented(format!(
1176                "SciPy reference for {method}"
1177            ))),
1178        }
1179    }
1180
1181    fn get_scipy_reference_2d(
1182        &self,
1183        method: &str,
1184        x: &Array2<T>,
1185        y: &Array1<T>,
1186        x_new: &Array2<T>,
1187    ) -> InterpolateResult<Array1<T>> {
1188        // Placeholder for 2D SciPy reference implementations
1189        match method {
1190            "rbf_gaussian" => {
1191                let rbf = crate::advanced::rbf::RBFInterpolator::new(
1192                    &x.view(),
1193                    &y.view(),
1194                    crate::advanced::rbf::RBFKernel::Gaussian,
1195                    T::from_f64(1.0).expect("Operation failed"),
1196                )?;
1197                rbf.interpolate(&x_new.view())
1198            }
1199            _ => Err(crate::InterpolateError::NotImplemented(format!(
1200                "SciPy 2D reference for {method}"
1201            ))),
1202        }
1203    }
1204
1205    fn calculate_accuracy_metrics(
1206        &self,
1207        result: &[T],
1208        reference: &Array1<T>,
1209    ) -> AccuracyMetrics<T> {
1210        let n = result.len().min(reference.len());
1211        let mut max_abs_error = T::zero();
1212        let mut sum_abs_error = T::zero();
1213        let mut sum_sq_error = T::zero();
1214        let mut points_within_tolerance = 0;
1215
1216        for i in 0..n {
1217            let error = (result[i] - reference[i]).abs();
1218            max_abs_error = max_abs_error.max(error);
1219            sum_abs_error += error;
1220            sum_sq_error += error * error;
1221
1222            if error < T::from_f64(self.config.correctness_tolerance).expect("Operation failed") {
1223                points_within_tolerance += 1;
1224            }
1225        }
1226
1227        let mean_abs_error = sum_abs_error / T::from_usize(n).expect("Test/example failed");
1228        let rmse = (sum_sq_error / T::from_usize(n).expect("Operation failed")).sqrt();
1229        let relative_error_percent = (mean_abs_error
1230            / reference
1231                .mapv(|x| x.abs())
1232                .mean()
1233                .expect("Operation failed"))
1234            * T::from_f64(100.0).expect("Test/example failed");
1235
1236        AccuracyMetrics {
1237            max_absolute_error: max_abs_error,
1238            mean_absolute_error: mean_abs_error,
1239            rmse,
1240            relative_error_percent,
1241            points_within_tolerance,
1242            total_points: n,
1243        }
1244    }
1245}
1246
1247/// Memory tracking for performance analysis
1248pub struct MemoryTracker {
1249    peak_usage: u64,
1250    current_usage: u64,
1251    allocations: u64,
1252}
1253
1254impl MemoryTracker {
1255    pub fn new() -> Self {
1256        Self {
1257            peak_usage: 0,
1258            current_usage: 0,
1259            allocations: 0,
1260        }
1261    }
1262
1263    pub fn track_allocation(&mut self, size: u64) {
1264        self.current_usage += size;
1265        self.allocations += 1;
1266        self.peak_usage = self.peak_usage.max(self.current_usage);
1267    }
1268
1269    pub fn track_deallocation(&mut self, size: u64) {
1270        self.current_usage = self.current_usage.saturating_sub(size);
1271    }
1272
1273    pub fn get_peak_usage(&self) -> u64 {
1274        self.peak_usage
1275    }
1276
1277    pub fn get_current_usage(&self) -> u64 {
1278        self.current_usage
1279    }
1280}
1281
1282impl Default for MemoryTracker {
1283    fn default() -> Self {
1284        Self::new()
1285    }
1286}
1287
1288/// SciPy compatibility report
1289#[derive(Debug, Clone)]
1290pub struct SciPyCompatibilityReport<T: Float> {
1291    pub tested_methods: usize,
1292    pub passed_accuracy_tests: usize,
1293    pub total_accuracy_tests: usize,
1294    pub performance_comparisons: Vec<PerformanceComparison<T>>,
1295    pub accuracy_validations: Vec<AccuracyValidation<T>>,
1296    pub system_info: SystemInfo,
1297    pub timestamp: Instant,
1298}
1299
1300#[derive(Debug, Clone)]
1301pub struct CompatibilityResult {
1302    pub method: String,
1303    pub data_size: usize,
1304    pub api_compatible: bool,
1305    pub behavior_compatible: bool,
1306    pub performance_ratio: f64, // Our performance / SciPy performance
1307}
1308
1309#[derive(Debug, Clone)]
1310pub struct AccuracyValidation<T: Float> {
1311    pub method: String,
1312    pub data_size: usize,
1313    pub passed: bool,
1314    pub accuracy_metrics: AccuracyMetrics<T>,
1315}
1316
1317#[derive(Debug, Clone)]
1318pub struct PerformanceComparison<T: Float> {
1319    pub method: String,
1320    pub data_size: usize,
1321    pub our_time: Duration,
1322    pub scipy_time: Duration,
1323    pub speedup_factor: f64,
1324    pub memory_comparison: MemoryComparison,
1325    pub _phantom: std::marker::PhantomData<T>,
1326}
1327
1328#[derive(Debug, Clone)]
1329pub struct MemoryComparison {
1330    pub our_memory_mb: f64,
1331    pub scipy_memory_mb: f64,
1332    pub memory_efficiency_ratio: f64,
1333}
1334
1335/// Stress testing infrastructure
1336pub struct StressTester<T: crate::traits::InterpolationFloat> {
1337    config: StressTestConfig,
1338    results: Vec<StressTestResult<T>>,
1339}
1340
1341#[derive(Debug, Clone)]
1342pub struct StressTestConfig {
1343    pub data_sizes: Vec<usize>,
1344    pub extreme_value_tests: bool,
1345    pub edge_case_tests: bool,
1346    pub memory_pressure_tests: bool,
1347    pub concurrent_access_tests: bool,
1348    pub numerical_stability_tests: bool,
1349    pub long_running_tests: bool,
1350    pub max_memory_usage_mb: usize,
1351    pub max_test_duration_minutes: usize,
1352}
1353
1354impl<
1355        T: crate::traits::InterpolationFloat
1356            + std::fmt::LowerExp
1357            + std::panic::UnwindSafe
1358            + std::panic::RefUnwindSafe,
1359    > StressTester<T>
1360{
1361    pub fn new(config: StressTestConfig) -> Self {
1362        Self {
1363            config,
1364            results: Vec::new(),
1365        }
1366    }
1367
1368    pub fn run_comprehensive_stress_tests(&mut self) -> InterpolateResult<StressTestReport<T>> {
1369        println!("Starting comprehensive stress testing...");
1370
1371        if self.config.extreme_value_tests {
1372            self.test_extreme_values()?;
1373        }
1374
1375        if self.config.edge_case_tests {
1376            self.test_edge_cases()?;
1377        }
1378
1379        if self.config.memory_pressure_tests {
1380            self.test_memory_pressure()?;
1381        }
1382
1383        if self.config.concurrent_access_tests {
1384            self.test_concurrent_access()?;
1385        }
1386
1387        if self.config.numerical_stability_tests {
1388            self.test_numerical_stability()?;
1389        }
1390
1391        Ok(StressTestReport {
1392            total_tests: self.results.len(),
1393            passed_tests: self.results.iter().filter(|r| r.passed).count(),
1394            failed_tests: self.results.iter().filter(|r| !r.passed).count(),
1395            results: self.results.clone(),
1396            system_info: InterpolationBenchmarkSuite::<T>::collect_system_info(),
1397            timestamp: Instant::now(),
1398        })
1399    }
1400
1401    fn test_extreme_values(&mut self) -> InterpolateResult<()> {
1402        println!("Testing extreme values...");
1403
1404        // Test with very large values
1405        let large_vals =
1406            Array1::from_vec(vec![T::from_f64(1e15).expect("Test/example failed"); 1000]);
1407        let x = Array1::linspace(T::zero(), T::one(), 1000);
1408
1409        let result = std::panic::catch_unwind(|| {
1410            crate::interp1d::linear_interpolate(&x.view(), &large_vals.view(), &x.view())
1411        });
1412
1413        self.results.push(StressTestResult {
1414            test_name: "extreme_large_values".to_string(),
1415            passed: result.is_ok(),
1416            error_message: if result.is_err() {
1417                Some("Panic with large values".to_string())
1418            } else {
1419                None
1420            },
1421            execution_time: Duration::from_millis(1), // Would measure actual time
1422            memory_usage_mb: 0.0,                     // Would measure actual memory
1423            _phantom: std::marker::PhantomData,
1424        });
1425
1426        // Test with very small values
1427        let small_vals =
1428            Array1::from_vec(vec![T::from_f64(1e-15).expect("Test/example failed"); 1000]);
1429
1430        let result = std::panic::catch_unwind(|| {
1431            crate::interp1d::linear_interpolate(&x.view(), &small_vals.view(), &x.view())
1432        });
1433
1434        self.results.push(StressTestResult {
1435            test_name: "extreme_small_values".to_string(),
1436            passed: result.is_ok(),
1437            error_message: if result.is_err() {
1438                Some("Panic with small values".to_string())
1439            } else {
1440                None
1441            },
1442            execution_time: Duration::from_millis(1),
1443            memory_usage_mb: 0.0,
1444            _phantom: std::marker::PhantomData,
1445        });
1446
1447        Ok(())
1448    }
1449
1450    fn test_edge_cases(&mut self) -> InterpolateResult<()> {
1451        println!("Testing edge cases...");
1452
1453        // Test with single point
1454        let x_single = Array1::from_vec(vec![T::zero()]);
1455        let y_single = Array1::from_vec(vec![T::one()]);
1456
1457        let result = std::panic::catch_unwind(|| {
1458            crate::interp1d::linear_interpolate(
1459                &x_single.view(),
1460                &y_single.view(),
1461                &x_single.view(),
1462            )
1463        });
1464
1465        self.results.push(StressTestResult {
1466            test_name: "single_point_interpolation".to_string(),
1467            passed: result.is_ok(),
1468            error_message: if result.is_err() {
1469                Some("Failed with single point".to_string())
1470            } else {
1471                None
1472            },
1473            execution_time: Duration::from_millis(1),
1474            memory_usage_mb: 0.0,
1475            _phantom: std::marker::PhantomData,
1476        });
1477
1478        // Test with duplicate points
1479        let x_dup = Array1::from_vec(vec![T::zero(), T::zero(), T::one()]);
1480        let y_dup = Array1::from_vec(vec![
1481            T::one(),
1482            T::one(),
1483            T::from_f64(2.0).expect("Operation failed"),
1484        ]);
1485
1486        let result = std::panic::catch_unwind(|| {
1487            crate::interp1d::linear_interpolate(&x_dup.view(), &y_dup.view(), &x_dup.view())
1488        });
1489
1490        self.results.push(StressTestResult {
1491            test_name: "duplicate_points".to_string(),
1492            passed: result.is_ok(),
1493            error_message: if result.is_err() {
1494                Some("Failed with duplicate points".to_string())
1495            } else {
1496                None
1497            },
1498            execution_time: Duration::from_millis(1),
1499            memory_usage_mb: 0.0,
1500            _phantom: std::marker::PhantomData,
1501        });
1502
1503        Ok(())
1504    }
1505
1506    fn test_memory_pressure(&mut self) -> InterpolateResult<()> {
1507        println!("Testing memory pressure...");
1508
1509        let data_sizes = self.config.data_sizes.clone();
1510        for &size in &data_sizes {
1511            if size < 100_000 {
1512                continue; // Only test memory pressure on large datasets
1513            }
1514
1515            let start_time = Instant::now();
1516            let x = Array1::linspace(T::zero(), T::one(), size);
1517            let y = x.mapv(|val| val * val);
1518            let x_new = Array1::linspace(T::zero(), T::one(), size / 2);
1519
1520            let result = std::panic::catch_unwind(|| {
1521                crate::interp1d::linear_interpolate(&x.view(), &y.view(), &x_new.view())
1522            });
1523
1524            self.results.push(StressTestResult {
1525                test_name: format!("memory_pressure_n_{size}"),
1526                passed: result.is_ok(),
1527                error_message: if result.is_err() {
1528                    Some("Memory pressure failure".to_string())
1529                } else {
1530                    None
1531                },
1532                execution_time: start_time.elapsed(),
1533                memory_usage_mb: (size * std::mem::size_of::<T>() * 3) as f64 / (1024.0 * 1024.0),
1534                _phantom: std::marker::PhantomData,
1535            });
1536        }
1537
1538        Ok(())
1539    }
1540
1541    fn test_concurrent_access(&mut self) -> InterpolateResult<()> {
1542        println!("Testing concurrent access...");
1543
1544        use std::sync::Arc;
1545        use std::thread;
1546
1547        let x = Arc::new(Array1::linspace(T::zero(), T::one(), 10000));
1548        let y = Arc::new(x.mapv(|val| val * val));
1549        let x_new = Arc::new(Array1::linspace(T::zero(), T::one(), 5000));
1550
1551        let mut handles = Vec::new();
1552        let num_threads = 4;
1553
1554        for i in 0..num_threads {
1555            let x_clone = Arc::clone(&x);
1556            let y_clone = Arc::clone(&y);
1557            let x_new_clone = Arc::clone(&x_new);
1558
1559            let handle = thread::spawn(move || {
1560                for _j in 0..10 {
1561                    let _ = crate::interp1d::linear_interpolate(
1562                        &x_clone.view(),
1563                        &y_clone.view(),
1564                        &x_new_clone.view(),
1565                    );
1566                }
1567                i
1568            });
1569            handles.push(handle);
1570        }
1571
1572        let mut all_succeeded = true;
1573        for handle in handles {
1574            if handle.join().is_err() {
1575                all_succeeded = false;
1576            }
1577        }
1578
1579        self.results.push(StressTestResult {
1580            test_name: "concurrent_access_linear".to_string(),
1581            passed: all_succeeded,
1582            error_message: if !all_succeeded {
1583                Some("Concurrent access failed".to_string())
1584            } else {
1585                None
1586            },
1587            execution_time: Duration::from_millis(100), // Would measure actual time
1588            memory_usage_mb: 0.0,
1589            _phantom: std::marker::PhantomData,
1590        });
1591
1592        Ok(())
1593    }
1594
1595    fn test_numerical_stability(&mut self) -> InterpolateResult<()> {
1596        println!("Testing numerical stability...");
1597
1598        // Test with ill-conditioned data
1599        let x = Array1::from_vec(
1600            (0..1000)
1601                .map(|i| T::from_f64(i as f64 * 1e-15).expect("Operation failed"))
1602                .collect(),
1603        );
1604        let y = x.mapv(|val| val + T::from_f64(1e-10).expect("Operation failed"));
1605        let x_new = x.clone();
1606
1607        let result = std::panic::catch_unwind(|| {
1608            crate::interp1d::linear_interpolate(&x.view(), &y.view(), &x_new.view())
1609        });
1610
1611        self.results.push(StressTestResult {
1612            test_name: "numerical_stability_ill_conditioned".to_string(),
1613            passed: result.is_ok(),
1614            error_message: if result.is_err() {
1615                Some("Numerical instability".to_string())
1616            } else {
1617                None
1618            },
1619            execution_time: Duration::from_millis(10),
1620            memory_usage_mb: 0.0,
1621            _phantom: std::marker::PhantomData,
1622        });
1623
1624        Ok(())
1625    }
1626}
1627
1628#[derive(Debug, Clone)]
1629pub struct StressTestResult<T: crate::traits::InterpolationFloat> {
1630    pub test_name: String,
1631    pub passed: bool,
1632    pub error_message: Option<String>,
1633    pub execution_time: Duration,
1634    pub memory_usage_mb: f64,
1635    pub _phantom: std::marker::PhantomData<T>,
1636}
1637
1638#[derive(Debug, Clone)]
1639pub struct StressTestReport<T: crate::traits::InterpolationFloat> {
1640    pub total_tests: usize,
1641    pub passed_tests: usize,
1642    pub failed_tests: usize,
1643    pub results: Vec<StressTestResult<T>>,
1644    pub system_info: SystemInfo,
1645    pub timestamp: Instant,
1646}
1647
1648#[cfg(test)]
1649mod tests {
1650    use super::*;
1651
1652    #[test]
1653    fn test_benchmark_suite_creation() {
1654        let config = BenchmarkConfig::default();
1655        let suite = InterpolationBenchmarkSuite::<f64>::new(config);
1656
1657        assert_eq!(suite.results.len(), 0);
1658        assert!(suite.baselines.is_empty());
1659    }
1660
1661    #[test]
1662    #[ignore = "Long-running benchmark test - runs comprehensive benchmarks that take >2 minutes"]
1663    fn test_quick_validation() {
1664        // This would run actual benchmarks in a real test
1665        let result = run_quick_validation::<f64>();
1666        if let Err(e) = &result {
1667            eprintln!("Benchmark error: {:?}", e);
1668        }
1669        assert!(result.is_ok());
1670    }
1671
1672    #[test]
1673    fn test_system_info_collection() {
1674        let info = InterpolationBenchmarkSuite::<f64>::collect_system_info();
1675        assert!(!info.cpu_info.is_empty());
1676        assert!(!info.os_info.is_empty());
1677        assert!(info.cpu_cores > 0);
1678    }
1679}