Skip to main content

scirs2_interpolate/
extrapolation.rs

1//! Enhanced extrapolation methods for interpolation
2//!
3//! This module provides advanced extrapolation capabilities that go beyond
4//! the basic ExtrapolateMode enum. It allows for more sophisticated boundary
5//! handling and domain extension methods.
6//!
7//! # Features
8//!
9//! - Physics-informed extrapolation based on boundary derivatives
10//! - Polynomial extrapolation of various orders
11//! - Decay/growth models for asymptotic behavior
12//! - Periodic extension of the domain
13//! - Reflection-based extrapolation
14//! - Domain-specific extrapolation models
15//! - Ensemble methods for robust extrapolation
16//! - Adaptive method selection
17//! - Confidence-based extrapolation with uncertainty estimation
18//! - Autoregressive modeling for time series data
19//!
20//! # Examples
21//!
22//! ## Basic Linear Extrapolation
23//!
24//! ```rust
25//! use scirs2_interpolate::extrapolation::{create_basic_extrapolator, ExtrapolationMethod};
26//!
27//! let extrapolator = create_basic_extrapolator(0.0, 10.0, 0.0, 10.0);
28//! let result = extrapolator.extrapolate(-5.0).expect("Operation failed");
29//! assert_eq!(result, -5.0);
30//! ```
31//!
32//! ## Advanced Ensemble Extrapolation
33//!
34//! ```rust
35//! use scirs2_interpolate::extrapolation::create_robust_extrapolator;
36//!
37//! let extrapolator = create_robust_extrapolator(0.0, 10.0, 0.0, 10.0, 1.0, 1.0);
38//! let result = extrapolator.extrapolate_advanced(-5.0).expect("Operation failed");
39//! ```
40//!
41//! ## Physics-Informed Extrapolation
42//!
43//! ```rust
44//! use scirs2_interpolate::extrapolation::{make_physics_informed_extrapolator, PhysicsLaw};
45//!
46//! let extrapolator = make_physics_informed_extrapolator(
47//!     0.0, 10.0, 0.0, 10.0, 1.0, 1.0,
48//!     PhysicsLaw::MassConservation
49//! );
50//! let result = extrapolator.extrapolate_advanced(-5.0).expect("Operation failed");
51//! ```
52
53// Re-export all functionality from the modular implementation
54pub use crate::extrapolation_modules::*;
55
56// Provide convenience functions for backward compatibility
57
58/// Creates a simple extrapolator with specified method for both boundaries
59pub fn create_simple_extrapolator<T: scirs2_core::numeric::Float + std::fmt::Display>(
60    lower_bound: T,
61    upper_bound: T,
62    lower_value: T,
63    upper_value: T,
64    method: ExtrapolationMethod,
65) -> Extrapolator<T> {
66    Extrapolator::new(
67        lower_bound,
68        upper_bound,
69        lower_value,
70        upper_value,
71        method,
72        method,
73    )
74}
75
76/// Creates an extrapolator with different methods for lower and upper boundaries
77pub fn create_asymmetric_extrapolator<T: scirs2_core::numeric::Float + std::fmt::Display>(
78    lower_bound: T,
79    upper_bound: T,
80    lower_value: T,
81    upper_value: T,
82    lower_method: ExtrapolationMethod,
83    upper_method: ExtrapolationMethod,
84) -> Extrapolator<T> {
85    Extrapolator::new(
86        lower_bound,
87        upper_bound,
88        lower_value,
89        upper_value,
90        lower_method,
91        upper_method,
92    )
93}
94
95/// Creates a production-ready extrapolator with conservative settings
96pub fn create_production_extrapolator<T: scirs2_core::numeric::Float + std::fmt::Display>(
97    lower_bound: T,
98    upper_bound: T,
99    lower_value: T,
100    upper_value: T,
101    lower_derivative: T,
102    upper_derivative: T,
103) -> Extrapolator<T> {
104    make_conservative_extrapolator(
105        lower_bound,
106        upper_bound,
107        lower_value,
108        upper_value,
109        lower_derivative,
110        upper_derivative,
111        (upper_bound - lower_bound) * T::from(2.0).expect("Operation failed"), // Max 2x domain width
112    )
113}
114
115/// Creates an extrapolator optimized for the given data characteristics
116pub fn create_optimized_extrapolator<
117    T: scirs2_core::numeric::Float
118        + scirs2_core::numeric::FromPrimitive
119        + std::fmt::Display
120        + std::default::Default
121        + std::ops::AddAssign,
122>(
123    lower_bound: T,
124    upper_bound: T,
125    lower_value: T,
126    upper_value: T,
127    lower_derivative: T,
128    upper_derivative: T,
129    values: &[T],
130    gradients: Option<&[T]>,
131) -> AdvancedExtrapolator<T> {
132    let characteristics = analyze_data_for_extrapolation(values, gradients);
133    create_smart_extrapolator(
134        lower_bound,
135        upper_bound,
136        lower_value,
137        upper_value,
138        lower_derivative,
139        upper_derivative,
140        &characteristics,
141    )
142}
143
144/// Quick extrapolation function for single values
145pub fn extrapolate_value<T: scirs2_core::numeric::Float + std::fmt::Display>(
146    x: T,
147    domain: (T, T),
148    boundary_values: (T, T),
149    method: ExtrapolationMethod,
150) -> crate::error::InterpolateResult<T> {
151    let (lower_bound, upper_bound) = domain;
152    let (lower_value, upper_value) = boundary_values;
153
154    let extrapolator =
155        create_simple_extrapolator(lower_bound, upper_bound, lower_value, upper_value, method);
156
157    extrapolator.extrapolate(x)
158}
159
160/// Batch extrapolation for multiple values
161pub fn extrapolate_batch<T: scirs2_core::numeric::Float + std::fmt::Display>(
162    x_values: &[T],
163    domain: (T, T),
164    boundary_values: (T, T),
165    method: ExtrapolationMethod,
166) -> Vec<crate::error::InterpolateResult<T>> {
167    let (lower_bound, upper_bound) = domain;
168    let (lower_value, upper_value) = boundary_values;
169
170    let extrapolator =
171        create_simple_extrapolator(lower_bound, upper_bound, lower_value, upper_value, method);
172
173    x_values
174        .iter()
175        .map(|&x| extrapolator.extrapolate(x))
176        .collect()
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182    use approx::assert_abs_diff_eq;
183
184    #[test]
185    fn test_simple_extrapolator() {
186        let extrapolator =
187            create_simple_extrapolator(0.0, 10.0, 0.0, 10.0, ExtrapolationMethod::Linear);
188
189        let result = extrapolator.extrapolate(-5.0).expect("Operation failed");
190        assert_abs_diff_eq!(result, -5.0, epsilon = 1e-10);
191    }
192
193    #[test]
194    fn test_asymmetric_extrapolator() {
195        let extrapolator = create_asymmetric_extrapolator(
196            0.0,
197            10.0,
198            0.0,
199            10.0,
200            ExtrapolationMethod::Linear,
201            ExtrapolationMethod::Constant,
202        );
203
204        // Test lower boundary (linear)
205        let result = extrapolator.extrapolate(-5.0).expect("Operation failed");
206        assert!(result != 0.0); // Should be linear, not constant
207
208        // Test upper boundary (constant)
209        let result = extrapolator.extrapolate(15.0).expect("Operation failed");
210        assert_abs_diff_eq!(result, 10.0, epsilon = 1e-10); // Should be constant
211    }
212
213    #[test]
214    fn test_production_extrapolator() {
215        let extrapolator = create_production_extrapolator(0.0, 10.0, 0.0, 10.0, 1.0, 1.0);
216
217        let result = extrapolator.extrapolate(-5.0).expect("Operation failed");
218        assert_abs_diff_eq!(result, -5.0, epsilon = 1e-10);
219    }
220
221    #[test]
222    fn test_quick_extrapolation() {
223        let result = extrapolate_value(-5.0, (0.0, 10.0), (0.0, 10.0), ExtrapolationMethod::Linear)
224            .expect("Operation failed");
225
226        assert_abs_diff_eq!(result, -5.0, epsilon = 1e-10);
227    }
228
229    #[test]
230    fn test_batch_extrapolation() {
231        let x_values = vec![-5.0, -2.0, 12.0, 15.0];
232        let results = extrapolate_batch(
233            &x_values,
234            (0.0, 10.0),
235            (0.0, 10.0),
236            ExtrapolationMethod::Linear,
237        );
238
239        assert_eq!(results.len(), 4);
240        assert!(results.iter().all(|r| r.is_ok()));
241
242        assert_abs_diff_eq!(
243            results[0].as_ref().expect("Operation failed"),
244            &-5.0,
245            epsilon = 1e-10
246        );
247        assert_abs_diff_eq!(
248            results[1].as_ref().expect("Operation failed"),
249            &-2.0,
250            epsilon = 1e-10
251        );
252        assert_abs_diff_eq!(
253            results[2].as_ref().expect("Operation failed"),
254            &12.0,
255            epsilon = 1e-10
256        );
257        assert_abs_diff_eq!(
258            results[3].as_ref().expect("Operation failed"),
259            &15.0,
260            epsilon = 1e-10
261        );
262    }
263
264    #[test]
265    fn test_optimized_extrapolator() {
266        let values = vec![1.0, 2.0, 3.0, 4.0, 5.0];
267        let gradient_vec = vec![1.0, 1.0, 1.0, 1.0];
268        let gradients = Some(gradient_vec.as_slice());
269
270        let extrapolator =
271            create_optimized_extrapolator(0.0, 4.0, 1.0, 5.0, 1.0, 1.0, &values, gradients);
272
273        let result = extrapolator.extrapolate_advanced(-1.0);
274        assert!(result.is_ok());
275    }
276
277    #[test]
278    fn test_zeros_method() {
279        let result = extrapolate_value(-5.0, (0.0, 10.0), (5.0, 15.0), ExtrapolationMethod::Zeros)
280            .expect("Operation failed");
281
282        assert_abs_diff_eq!(result, 0.0, epsilon = 1e-10);
283    }
284
285    #[test]
286    fn test_nearest_method() {
287        let extrapolator =
288            create_simple_extrapolator(0.0, 10.0, 5.0, 15.0, ExtrapolationMethod::Nearest);
289
290        // Test lower extrapolation
291        let result = extrapolator.extrapolate(-5.0).expect("Operation failed");
292        assert_abs_diff_eq!(result, 5.0, epsilon = 1e-10);
293
294        // Test upper extrapolation
295        let result = extrapolator.extrapolate(15.0).expect("Operation failed");
296        assert_abs_diff_eq!(result, 15.0, epsilon = 1e-10);
297    }
298
299    #[test]
300    fn test_error_for_in_domain_points() {
301        let extrapolator =
302            create_simple_extrapolator(0.0, 10.0, 0.0, 10.0, ExtrapolationMethod::Linear);
303
304        // Point inside domain should return error
305        let result = extrapolator.extrapolate(5.0);
306        assert!(result.is_err());
307    }
308}