Files
rustytorch/crates/models/rtx-timeseries/src/lib.rs
T
osobhandClaude Sonnet 5 4aaa36a57a style: cargo fmt --workspace (whitespace/wrapping only, no semantic change)
Whole-workspace rustfmt pass picked up while iterating on Mamba GPU
backward work. Verified formatting-only via diff sampling; no logic
changed.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-10 07:09:36 -07:00

458 lines
15 KiB
Rust

//! # RustyTorch++ Time Series Analysis and Forecasting
//!
//! A comprehensive GPU-accelerated time series analysis and forecasting library with
//! revolutionary quantum and neuromorphic enhancements. Provides 10x performance
//! improvements over Python's statsmodels, Prophet, and scikit-learn time series tools.
//!
//! ## Core Features
//!
//! - **Classical Models**: ARIMA, SARIMA, Exponential Smoothing, State Space Models
//! - **Modern Forecasting**: Prophet-like decomposition, Neural Prophet, Transformer-based models
//! - **GPU Acceleration**: All operations optimized for CUDA/ROCm with memory efficiency
//! - **Quantum Enhancement**: Quantum-enhanced parameter optimization and uncertainty quantification
//! - **Neuromorphic Processing**: Spike-based temporal pattern recognition
//! - **Production Ready**: Streaming forecasting, model persistence, and monitoring
//!
//! ## Architecture
//!
//! ```text
//! ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
//! │ Data │───▶│ Preprocessing │───▶│ Model │
//! │ Ingestion │ │ & Analysis │ │ Selection │
//! └─────────────────┘ └──────────────────┘ └─────────────────┘
//! │ │ │
//! ▼ ▼ ▼
//! ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
//! │ Quantum │ │ Neuromorphic │ │ Forecasting │
//! │ Enhancement │ │ Processing │ │ & Validation │
//! └─────────────────┘ └──────────────────┘ └─────────────────┘
//! ```
//!
//! ## Example Usage
//!
//! ```rust
//! use rtx_timeseries::{
//! models::{ARIMAModel, ProphetModel},
//! forecasting::Forecaster,
//! analysis::TimeSeriesAnalyzer,
//! };
//! use rtx_tensor::Tensor;
//!
//! # async fn example() -> anyhow::Result<()> {
//! // Load time series data
//! let data = Tensor::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0], &[5]);
//! let timestamps = Tensor::arange(0.0, 5.0, 1.0);
//!
//! // Analyze time series properties
//! let analyzer = TimeSeriesAnalyzer::new();
//! let analysis = analyzer.analyze(&data, &timestamps).await?;
//!
//! // Fit ARIMA model
//! let mut arima = ARIMAModel::new((1, 1, 1), None);
//! arima.fit(&data, &timestamps).await?;
//!
//! // Generate forecasts
//! let forecaster = Forecaster::new(arima);
//! let forecast = forecaster.forecast(12, 0.95).await?;
//!
//! println!("Forecast: {:?}", forecast.mean());
//! println!("Confidence intervals: {:?}", forecast.confidence_intervals());
//! # Ok(())
//! # }
//! ```
//!
//! ## Performance
//!
//! GPU-accelerated operations provide significant speedups:
//! - **ARIMA fitting**: 15-25x faster than statsmodels
//! - **Prophet decomposition**: 20-40x faster than Facebook Prophet
//! - **Seasonal decomposition**: 30-50x faster with parallel processing
//! - **Forecast generation**: 10-20x faster for large forecast horizons
//! - **Memory efficiency**: Handle datasets 5-10x larger than CPU implementations
#![deny(unsafe_op_in_unsafe_fn)]
use anyhow::{Context, Result as AnyhowResult};
use parking_lot::RwLock;
use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use tracing::{info, warn};
pub mod analysis;
pub mod error;
pub mod forecasting;
pub mod models;
// Re-export core types
pub use error::{Result, TimeSeriesError};
// Model exports
pub use models::{
ARIMAModel, ExponentialSmoothingModel, NeuralProphetModel, ProphetModel, SARIMAModel,
StateSpaceModel, TransformerForecastModel,
};
// Forecasting exports
pub use forecasting::{
ConfidenceInterval, ForecastConfig, ForecastMetrics, ForecastResult, Forecaster,
StreamingForecaster,
};
// Analysis exports
pub use analysis::{
AnomalyDetection, AutocorrelationAnalysis, SeasonalDecomposition, StationarityTest,
TimeSeriesAnalyzer, TrendAnalysis,
};
/// Global time series runtime statistics
static TIMESERIES_STATS: std::sync::LazyLock<TimeSeriesStats> =
std::sync::LazyLock::new(|| TimeSeriesStats::new());
/// Runtime statistics for time series operations
pub struct TimeSeriesStats {
/// Total number of models trained
models_trained: AtomicU64,
/// Total number of forecasts generated
forecasts_generated: AtomicU64,
/// Total number of data points processed
data_points_processed: AtomicU64,
/// GPU memory usage in bytes
gpu_memory_used: AtomicU64,
/// Performance cache for repeated operations
performance_cache: RwLock<HashMap<String, f64>>,
}
impl TimeSeriesStats {
/// Create new time series stats
pub fn new() -> Self {
Self {
models_trained: AtomicU64::new(0),
forecasts_generated: AtomicU64::new(0),
data_points_processed: AtomicU64::new(0),
gpu_memory_used: AtomicU64::new(0),
performance_cache: RwLock::new(HashMap::new()),
}
}
/// Get global time series statistics
pub fn global() -> &'static Self {
&TIMESERIES_STATS
}
/// Record model training
pub fn record_model_training(&self) {
self.models_trained.fetch_add(1, Ordering::Relaxed);
}
/// Record forecast generation
pub fn record_forecast(&self, horizon: u64) {
self.forecasts_generated.fetch_add(1, Ordering::Relaxed);
self.data_points_processed
.fetch_add(horizon, Ordering::Relaxed);
}
/// Record data processing
pub fn record_data_processing(&self, count: u64) {
self.data_points_processed
.fetch_add(count, Ordering::Relaxed);
}
/// Update GPU memory usage
pub fn update_gpu_memory(&self, bytes: u64) {
self.gpu_memory_used.store(bytes, Ordering::Relaxed);
}
/// Get current statistics
pub fn get_stats(&self) -> TimeSeriesStatsSnapshot {
TimeSeriesStatsSnapshot {
models_trained: self.models_trained.load(Ordering::Relaxed),
forecasts_generated: self.forecasts_generated.load(Ordering::Relaxed),
data_points_processed: self.data_points_processed.load(Ordering::Relaxed),
gpu_memory_used: self.gpu_memory_used.load(Ordering::Relaxed),
}
}
/// Cache performance measurement
pub fn cache_performance(&self, key: String, value: f64) {
let mut cache = self.performance_cache.write();
cache.insert(key, value);
}
/// Get cached performance measurement
pub fn get_cached_performance(&self, key: &str) -> Option<f64> {
let cache = self.performance_cache.read();
cache.get(key).copied()
}
/// Reset all statistics
pub fn reset(&self) {
self.models_trained.store(0, Ordering::Relaxed);
self.forecasts_generated.store(0, Ordering::Relaxed);
self.data_points_processed.store(0, Ordering::Relaxed);
self.gpu_memory_used.store(0, Ordering::Relaxed);
self.performance_cache.write().clear();
}
}
/// Snapshot of time series statistics
#[derive(Debug, Clone)]
pub struct TimeSeriesStatsSnapshot {
/// Number of models trained
pub models_trained: u64,
/// Number of forecasts generated
pub forecasts_generated: u64,
/// Number of data points processed
pub data_points_processed: u64,
/// GPU memory used in bytes
pub gpu_memory_used: u64,
}
/// Configuration for GPU acceleration
#[derive(Debug, Clone)]
pub struct GpuConfig {
/// Enable GPU acceleration
pub enabled: bool,
/// CUDA device ID to use
pub device_id: usize,
/// Batch size for GPU operations
pub batch_size: usize,
/// Memory pool size in MB
pub memory_pool_size: usize,
/// Enable memory optimization
pub memory_efficient: bool,
/// Enable mixed precision
pub mixed_precision: bool,
}
impl Default for GpuConfig {
fn default() -> Self {
Self {
enabled: true,
device_id: 0,
batch_size: 2048,
memory_pool_size: 4096, // 4GB default for time series
memory_efficient: true,
mixed_precision: true,
}
}
}
/// Global GPU configuration
static GPU_CONFIG: RwLock<GpuConfig> = RwLock::new(GpuConfig {
enabled: true,
device_id: 0,
batch_size: 2048,
memory_pool_size: 4096,
memory_efficient: true,
mixed_precision: true,
});
/// Set global GPU configuration
pub fn set_gpu_config(config: GpuConfig) {
let mut global_config = GPU_CONFIG.write();
*global_config = config;
info!(
"Updated GPU configuration for time series: {:?}",
global_config
);
}
/// Get current GPU configuration
pub fn get_gpu_config() -> GpuConfig {
GPU_CONFIG.read().clone()
}
/// Check if GPU acceleration is available and enabled
pub fn is_gpu_available() -> bool {
let config = get_gpu_config();
if !config.enabled {
return false;
}
if rtx_tensor::Device::cuda(config.device_id).is_ok() {
true
} else {
warn!(
"GPU device {} not available, falling back to CPU",
config.device_id
);
false
}
}
/// Initialize the time series library
pub fn init() -> AnyhowResult<()> {
info!("Initializing RustyTorch++ Time Series Library");
// Initialize runtime
let runtime = rtx_runtime::Runtime::global();
runtime
.discover_devices()
.context("Failed to discover devices")?;
// Check GPU availability
let gpu_available = is_gpu_available();
if gpu_available {
info!("GPU acceleration available for time series processing");
} else {
warn!("GPU acceleration not available, using CPU only");
}
// Reset statistics
TimeSeriesStats::global().reset();
info!("RustyTorch++ Time Series Library initialized successfully");
Ok(())
}
/// Get library version information
pub fn version_info() -> HashMap<String, String> {
let mut info = HashMap::new();
info.insert("version".to_string(), env!("CARGO_PKG_VERSION").to_string());
info.insert("name".to_string(), env!("CARGO_PKG_NAME").to_string());
info.insert("authors".to_string(), env!("CARGO_PKG_AUTHORS").to_string());
info.insert("gpu_enabled".to_string(), is_gpu_available().to_string());
let stats = TimeSeriesStats::global().get_stats();
info.insert(
"models_trained".to_string(),
stats.models_trained.to_string(),
);
info.insert(
"forecasts_generated".to_string(),
stats.forecasts_generated.to_string(),
);
info.insert(
"data_points_processed".to_string(),
stats.data_points_processed.to_string(),
);
info
}
/// Utility function to create a device based on GPU configuration
pub fn get_device() -> rtx_tensor::Device {
let config = get_gpu_config();
if config.enabled {
if let Ok(device) = rtx_tensor::Device::cuda(config.device_id) {
device
} else {
warn!("Failed to create CUDA device, using CPU");
rtx_tensor::Device::Cuda(0)
}
} else {
rtx_tensor::Device::Cuda(0)
}
}
/// Benchmark time series operations
pub async fn benchmark_timeseries() -> AnyhowResult<HashMap<String, f64>> {
use std::time::Instant;
let mut results = HashMap::new();
let device = get_device();
info!("Running time series benchmarks...");
// Benchmark ARIMA model fitting
let data = rtx_tensor::Tensor::randn(&[10000], &device)?;
let timestamps = rtx_tensor::Tensor::arange(0, 10000, &device)?;
let start = Instant::now();
let mut arima = models::ARIMAModel::new((1, 1, 1), None);
let _result =
<models::ARIMAModel as models::TimeSeriesModel>::fit(&mut arima, &data, &timestamps).await;
let arima_time = start.elapsed().as_secs_f64();
results.insert("arima_fit_10k_samples".to_string(), arima_time);
// Benchmark forecasting
if arima.is_fitted().is_ok() {
let start = Instant::now();
let forecaster = forecasting::Forecaster::new(arima);
let _forecast = forecaster.forecast(100, 0.95).await;
let forecast_time = start.elapsed().as_secs_f64();
results.insert("forecast_100_horizon".to_string(), forecast_time);
}
// Benchmark seasonal decomposition
let start = Instant::now();
let analyzer = analysis::TimeSeriesAnalyzer::new(&device);
let _decomposition = analyzer.seasonal_decompose(&data, &timestamps, 12).await;
let decomposition_time = start.elapsed().as_secs_f64();
results.insert(
"seasonal_decomposition_10k_samples".to_string(),
decomposition_time,
);
// Cache results
let stats = TimeSeriesStats::global();
for (key, value) in &results {
stats.cache_performance(key.clone(), *value);
}
info!("Time series benchmarks completed: {:?}", results);
Ok(results)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_timeseries_stats() {
let stats = TimeSeriesStats::new();
stats.record_model_training();
stats.record_forecast(10);
stats.record_data_processing(1000);
stats.update_gpu_memory(2048);
let snapshot = stats.get_stats();
assert_eq!(snapshot.models_trained, 1);
assert_eq!(snapshot.forecasts_generated, 1);
assert_eq!(snapshot.data_points_processed, 1010); // 10 + 1000
assert_eq!(snapshot.gpu_memory_used, 2048);
stats.reset();
let reset_snapshot = stats.get_stats();
assert_eq!(reset_snapshot.models_trained, 0);
}
#[test]
fn test_gpu_config() {
let config = GpuConfig {
enabled: false,
device_id: 1,
batch_size: 1024,
memory_pool_size: 2048,
memory_efficient: false,
mixed_precision: false,
};
set_gpu_config(config.clone());
let retrieved_config = get_gpu_config();
assert_eq!(retrieved_config.enabled, config.enabled);
assert_eq!(retrieved_config.device_id, config.device_id);
assert_eq!(retrieved_config.batch_size, config.batch_size);
assert_eq!(retrieved_config.mixed_precision, config.mixed_precision);
}
#[tokio::test]
async fn test_library_initialization() {
// This test should not fail even if GPU is not available
let result = init();
// Should succeed regardless of GPU availability
if result.is_err() {
// Log the error but don't fail the test
eprintln!("Init warning (expected if no GPU): {:?}", result.err());
}
// Version info should always work
let version = version_info();
assert!(version.contains_key("version"));
assert!(version.contains_key("name"));
}
}