scirs2_interpolate/bspline_modules/
types.rs

1//! Core type definitions for B-spline functionality
2//!
3//! This module contains all the foundational data types used throughout
4//! the B-spline system.
5
6use scirs2_core::ndarray::{Array1, Array2};
7use scirs2_core::numeric::{Float, FromPrimitive, Zero};
8use std::cell::RefCell;
9use std::fmt::{Debug, Display};
10use std::ops::{Add, Div, Mul, Sub};
11
12/// Extrapolation mode for B-splines
13#[derive(Debug, Clone, Copy, PartialEq, Default)]
14pub enum ExtrapolateMode {
15    /// Extrapolate based on the first and last polynomials
16    #[default]
17    Extrapolate,
18    /// Periodic extrapolation
19    Periodic,
20    /// Return NaN for points outside the domain
21    Nan,
22    /// Return an error for points outside the domain
23    Error,
24}
25
26/// Workspace for reusable memory allocations during B-spline evaluation
27/// This reduces memory allocation overhead in hot paths
28#[derive(Debug)]
29pub struct BSplineWorkspace<T> {
30    /// Reusable coefficient buffer for de Boor's algorithm
31    pub(crate) coeffs: RefCell<Array1<T>>,
32    /// Reusable buffer for polynomial evaluation
33    pub(crate) poly_buf: RefCell<Array1<T>>,
34    /// Reusable buffer for basis function computation
35    pub(crate) basis_buf: RefCell<Array1<T>>,
36    /// Reusable buffer for matrix operations
37    pub(crate) matrix_buf: RefCell<Array2<T>>,
38    /// Memory usage statistics
39    pub(crate) memory_stats: RefCell<WorkspaceMemoryStats>,
40}
41
42/// Memory usage statistics for workspace optimization
43#[derive(Debug, Clone, Default)]
44pub struct WorkspaceMemoryStats {
45    /// Peak memory usage in bytes
46    pub peak_memory_bytes: usize,
47    /// Current memory usage in bytes
48    pub current_memory_bytes: usize,
49    /// Number of allocations avoided by reuse
50    pub allocations_avoided: usize,
51    /// Number of times workspace was resized
52    pub resize_count: usize,
53    /// Total evaluation count
54    pub evaluation_count: usize,
55}
56
57impl WorkspaceMemoryStats {
58    /// Get memory efficiency ratio (allocations avoided / total evaluations)
59    pub fn efficiency_ratio(&self) -> f64 {
60        if self.evaluation_count == 0 {
61            0.0
62        } else {
63            self.allocations_avoided as f64 / self.evaluation_count as f64
64        }
65    }
66
67    /// Get peak memory usage in MB
68    pub fn peak_memory_mb(&self) -> f64 {
69        self.peak_memory_bytes as f64 / (1024.0 * 1024.0)
70    }
71
72    /// Update memory usage statistics
73    pub fn update_memory_usage(&mut self, current_bytes: usize) {
74        self.current_memory_bytes = current_bytes;
75        if current_bytes > self.peak_memory_bytes {
76            self.peak_memory_bytes = current_bytes;
77        }
78    }
79
80    /// Record an avoided allocation
81    pub fn record_allocation_avoided(&mut self) {
82        self.allocations_avoided += 1;
83    }
84
85    /// Record a workspace resize
86    pub fn record_resize(&mut self) {
87        self.resize_count += 1;
88    }
89
90    /// Record an evaluation
91    pub fn record_evaluation(&mut self) {
92        self.evaluation_count += 1;
93    }
94
95    /// Reset all statistics
96    pub fn reset(&mut self) {
97        self.peak_memory_bytes = 0;
98        self.current_memory_bytes = 0;
99        self.allocations_avoided = 0;
100        self.resize_count = 0;
101        self.evaluation_count = 0;
102    }
103
104    /// Get total memory saved (approximation based on avoided allocations)
105    pub fn estimated_memory_saved_mb(&self, avg_allocation_size_bytes: usize) -> f64 {
106        (self.allocations_avoided * avg_allocation_size_bytes) as f64 / (1024.0 * 1024.0)
107    }
108}
109
110impl<T> BSplineWorkspace<T>
111where
112    T: Float + FromPrimitive + Zero + Clone,
113{
114    /// Create a new workspace with default capacity
115    pub fn new() -> Self {
116        Self::with_capacity(16, 16)
117    }
118
119    /// Create a new workspace with specified capacity
120    pub fn with_capacity(array_capacity: usize, matrix_capacity: usize) -> Self {
121        Self {
122            coeffs: RefCell::new(Array1::zeros(array_capacity)),
123            poly_buf: RefCell::new(Array1::zeros(array_capacity)),
124            basis_buf: RefCell::new(Array1::zeros(array_capacity)),
125            matrix_buf: RefCell::new(Array2::zeros((matrix_capacity, matrix_capacity))),
126            memory_stats: RefCell::new(WorkspaceMemoryStats::default()),
127        }
128    }
129
130    /// Get current memory usage in bytes
131    pub fn current_memory_bytes(&self) -> usize {
132        self.memory_stats.borrow().current_memory_bytes
133    }
134
135    /// Get peak memory usage in bytes
136    pub fn peak_memory_bytes(&self) -> usize {
137        self.memory_stats.borrow().peak_memory_bytes
138    }
139
140    /// Get memory efficiency ratio
141    pub fn efficiency_ratio(&self) -> f64 {
142        self.memory_stats.borrow().efficiency_ratio()
143    }
144
145    /// Get the total number of evaluations performed
146    pub fn evaluation_count(&self) -> usize {
147        self.memory_stats.borrow().evaluation_count
148    }
149
150    /// Get the number of allocations avoided
151    pub fn allocations_avoided(&self) -> usize {
152        self.memory_stats.borrow().allocations_avoided
153    }
154
155    /// Get workspace statistics
156    pub fn get_statistics(&self) -> WorkspaceMemoryStats {
157        self.memory_stats.borrow().clone()
158    }
159
160    /// Reset workspace statistics
161    pub fn reset_statistics(&self) {
162        self.memory_stats.borrow_mut().reset();
163    }
164
165    /// Ensure the coefficient buffer has at least the specified capacity
166    pub fn ensure_coeffs_capacity(&self, capacity: usize) {
167        let mut coeffs = self.coeffs.borrow_mut();
168        if coeffs.len() < capacity {
169            *coeffs = Array1::zeros(capacity);
170            self.memory_stats.borrow_mut().record_resize();
171        } else {
172            self.memory_stats.borrow_mut().record_allocation_avoided();
173        }
174    }
175
176    /// Ensure the polynomial buffer has at least the specified capacity
177    pub fn ensure_poly_capacity(&self, capacity: usize) {
178        let mut poly_buf = self.poly_buf.borrow_mut();
179        if poly_buf.len() < capacity {
180            *poly_buf = Array1::zeros(capacity);
181            self.memory_stats.borrow_mut().record_resize();
182        } else {
183            self.memory_stats.borrow_mut().record_allocation_avoided();
184        }
185    }
186
187    /// Ensure the basis buffer has at least the specified capacity
188    pub fn ensure_basis_capacity(&self, capacity: usize) {
189        let mut basis_buf = self.basis_buf.borrow_mut();
190        if basis_buf.len() < capacity {
191            *basis_buf = Array1::zeros(capacity);
192            self.memory_stats.borrow_mut().record_resize();
193        } else {
194            self.memory_stats.borrow_mut().record_allocation_avoided();
195        }
196    }
197
198    /// Ensure the matrix buffer has at least the specified capacity
199    pub fn ensure_matrix_capacity(&self, rows: usize, cols: usize) {
200        let mut matrix_buf = self.matrix_buf.borrow_mut();
201        if matrix_buf.nrows() < rows || matrix_buf.ncols() < cols {
202            *matrix_buf = Array2::zeros((rows, cols));
203            self.memory_stats.borrow_mut().record_resize();
204        } else {
205            self.memory_stats.borrow_mut().record_allocation_avoided();
206        }
207    }
208
209    /// Update memory usage and record evaluation
210    pub fn record_evaluation(&self) {
211        self.memory_stats.borrow_mut().record_evaluation();
212
213        // Estimate current memory usage
214        let coeffs_size = self.coeffs.borrow().len() * std::mem::size_of::<T>();
215        let poly_size = self.poly_buf.borrow().len() * std::mem::size_of::<T>();
216        let basis_size = self.basis_buf.borrow().len() * std::mem::size_of::<T>();
217        let matrix_size = {
218            let matrix = self.matrix_buf.borrow();
219            matrix.nrows() * matrix.ncols() * std::mem::size_of::<T>()
220        };
221
222        let total_size = coeffs_size + poly_size + basis_size + matrix_size;
223        self.memory_stats
224            .borrow_mut()
225            .update_memory_usage(total_size);
226    }
227
228    /// Clear all buffers and reset to minimum size
229    pub fn clear(&self) {
230        *self.coeffs.borrow_mut() = Array1::zeros(1);
231        *self.poly_buf.borrow_mut() = Array1::zeros(1);
232        *self.basis_buf.borrow_mut() = Array1::zeros(1);
233        *self.matrix_buf.borrow_mut() = Array2::zeros((1, 1));
234    }
235
236    /// Get memory usage report as a formatted string
237    pub fn memory_report(&self) -> String {
238        let stats = self.get_statistics();
239        format!(
240            "BSpline Workspace Memory Report:\n\
241             Peak Memory: {:.2} MB\n\
242             Current Memory: {:.2} MB\n\
243             Evaluations: {}\n\
244             Allocations Avoided: {}\n\
245             Efficiency Ratio: {:.2}%\n\
246             Resizes: {}",
247            stats.peak_memory_mb(),
248            stats.current_memory_bytes as f64 / (1024.0 * 1024.0),
249            stats.evaluation_count,
250            stats.allocations_avoided,
251            stats.efficiency_ratio() * 100.0,
252            stats.resize_count
253        )
254    }
255}
256
257impl<T> Default for BSplineWorkspace<T>
258where
259    T: Float + FromPrimitive + Zero + Clone,
260{
261    fn default() -> Self {
262        Self::new()
263    }
264}
265
266impl<T> Clone for BSplineWorkspace<T>
267where
268    T: Float + FromPrimitive + Zero + Clone,
269{
270    fn clone(&self) -> Self {
271        Self {
272            coeffs: RefCell::new(self.coeffs.borrow().clone()),
273            poly_buf: RefCell::new(self.poly_buf.borrow().clone()),
274            basis_buf: RefCell::new(self.basis_buf.borrow().clone()),
275            matrix_buf: RefCell::new(self.matrix_buf.borrow().clone()),
276            memory_stats: RefCell::new(self.memory_stats.borrow().clone()),
277        }
278    }
279}
280
281/// Builder pattern for creating workspaces with specific configurations
282pub struct BSplineWorkspaceBuilder<T> {
283    array_capacity: usize,
284    matrix_capacity: usize,
285    _phantom: std::marker::PhantomData<T>,
286}
287
288impl<T> Default for BSplineWorkspaceBuilder<T> {
289    fn default() -> Self {
290        Self {
291            array_capacity: 16,
292            matrix_capacity: 16,
293            _phantom: std::marker::PhantomData,
294        }
295    }
296}
297
298impl<T> BSplineWorkspaceBuilder<T>
299where
300    T: Float + FromPrimitive + Zero + Clone,
301{
302    /// Create a new workspace builder
303    pub fn new() -> Self {
304        Self::default()
305    }
306
307    /// Set the initial capacity for array buffers
308    pub fn with_array_capacity(mut self, capacity: usize) -> Self {
309        self.array_capacity = capacity;
310        self
311    }
312
313    /// Set the initial capacity for matrix buffers
314    pub fn with_matrix_capacity(mut self, capacity: usize) -> Self {
315        self.matrix_capacity = capacity;
316        self
317    }
318
319    /// Build the workspace
320    pub fn build(self) -> BSplineWorkspace<T> {
321        BSplineWorkspace::with_capacity(self.array_capacity, self.matrix_capacity)
322    }
323}
324
325/// Trait for types that can provide workspace optimization
326pub trait WorkspaceProvider<T> {
327    /// Get or create a workspace for B-spline operations
328    fn get_workspace(&self) -> &BSplineWorkspace<T>;
329
330    /// Check if workspace optimization is enabled
331    fn is_workspace_enabled(&self) -> bool;
332}
333
334/// Configuration for workspace memory management
335#[derive(Debug, Clone)]
336pub struct WorkspaceConfig {
337    /// Initial array buffer capacity
338    pub initial_array_capacity: usize,
339    /// Initial matrix buffer capacity
340    pub initial_matrix_capacity: usize,
341    /// Maximum memory usage before forced cleanup (in MB)
342    pub max_memory_mb: f64,
343    /// Enable automatic memory management
344    pub auto_memory_management: bool,
345}
346
347impl Default for WorkspaceConfig {
348    fn default() -> Self {
349        Self {
350            initial_array_capacity: 16,
351            initial_matrix_capacity: 16,
352            max_memory_mb: 100.0, // 100 MB default limit
353            auto_memory_management: true,
354        }
355    }
356}