79f51e4775
Major Features: • Comprehensive Differential Evolution with 5 strategies (rand1, best1, currenttobest1, rand2, best2) • Adaptive jDE algorithm for self-tuning F and CR parameters • Convergence tracking with history records and early stopping • Mathematical toolkit module (780 lines): gradient, hessian, jacobian, statistics, linear algebra • Optimal control framework: HJB solvers, regime switching, jump diffusion, MRSJD • Sparse optimization: Sparse PCA, Box-Tao decomposition, ADMM, Elastic Net • Rayon parallelization infrastructure (ready for pure Rust objectives) Performance: • 74-88× speedup for DE vs SciPy • 50-100× speedup overall vs pure Python Refactoring & Cleanup: • Removed 5 legacy files (de_refactored.rs, hmm_legacy.rs, hmm_refactored.rs, mcmc_legacy.rs, mcmc_refactored.rs) • Modular architecture with trait-based design • Generic implementations (no domain-specific code) • Updated Python bindings for new DE API • Fixed ALL compilation warnings (0 errors, 0 warnings) Documentation: • Updated README with v0.2.0 features and benchmarks • Created RELEASE_NOTES_v0.2.0.md (comprehensive changelog) • New optimal control tutorial notebook (03_optimal_control_tutorial.ipynb) • Updated API examples in README • Created test_release.py for release validation Version Bumps: • Cargo.toml: 0.1.0 → 0.2.0 • pyproject.toml: 0.1.0 → 0.2.0 • python/__init__.py: 0.1.0 → 0.2.0 Breaking Changes: • DE API: mutation_factor/crossover_rate → f/cr • DE API: use_adaptive_jde → adaptive • DE API: strategy names simplified (e.g., 'rand/1/bin' → 'rand1') • DE returns: (x, fun) tuple instead of dict-like object Known Items (Post-Release): • Mathematical toolkit functions available in Rust but not yet exposed to Python • MCMC Python wrapper needs API update to match new Rust implementation • Tutorial notebooks need DE API updates Tests: 34 Rust tests passing, core Python functionality validated with test_release.py
181 lines
4.6 KiB
Rust
181 lines
4.6 KiB
Rust
//! Core traits and types for optimization algorithms
|
|
//!
|
|
//! This module defines the foundational traits and types used across all
|
|
//! optimization and inference algorithms in OptimizR.
|
|
|
|
use thiserror::Error;
|
|
|
|
/// Custom error type for OptimizR operations
|
|
#[derive(Error, Debug, Clone)]
|
|
pub enum OptimizrError {
|
|
#[error("Invalid parameter: {0}")]
|
|
InvalidParameter(String),
|
|
|
|
#[error("Invalid input: {0}")]
|
|
InvalidInput(String),
|
|
|
|
#[error("Dimension mismatch: expected {expected}, got {actual}")]
|
|
DimensionMismatch { expected: usize, actual: usize },
|
|
|
|
#[error("Empty data provided")]
|
|
EmptyData,
|
|
|
|
#[error("Convergence failed after {0} iterations")]
|
|
ConvergenceFailed(usize),
|
|
|
|
#[error("Numerical error: {0}")]
|
|
NumericalError(String),
|
|
|
|
#[error("Computation error: {0}")]
|
|
ComputationError(String),
|
|
}
|
|
|
|
/// Result type for OptimizR operations
|
|
pub type Result<T> = std::result::Result<T, OptimizrError>;
|
|
|
|
/// Alias for OptimizR result type (legacy compatibility)
|
|
pub type OptimizrResult<T> = Result<T>;
|
|
|
|
/// Trait for optimization algorithms
|
|
pub trait Optimizer {
|
|
type Config;
|
|
type Output;
|
|
|
|
/// Optimize to find best solution
|
|
fn optimize(&mut self) -> Result<Self::Output>;
|
|
|
|
/// Get current best solution
|
|
fn best(&self) -> Result<Vec<f64>>;
|
|
}
|
|
|
|
/// Trait for sampling algorithms (MCMC, etc.)
|
|
pub trait Sampler {
|
|
type Config;
|
|
type Output;
|
|
|
|
/// Draw samples from the target distribution
|
|
fn sample(&mut self) -> Result<Self::Output>;
|
|
|
|
/// Get diagnostics about sampling performance
|
|
fn diagnostics(&self, samples: &Self::Output) -> Result<SamplerDiagnostics>;
|
|
}
|
|
|
|
/// Diagnostics for sampling algorithms
|
|
#[derive(Debug, Clone)]
|
|
pub struct SamplerDiagnostics {
|
|
pub n_samples: usize,
|
|
pub means: Vec<f64>,
|
|
pub std_devs: Vec<f64>,
|
|
pub autocorrelations: Vec<f64>,
|
|
}
|
|
|
|
/// Trait for configuration builders
|
|
pub trait ConfigBuilder {
|
|
type Config;
|
|
|
|
fn build(self) -> Result<Self::Config>;
|
|
}
|
|
|
|
/// Trait for information measures (entropy, MI, etc.)
|
|
pub trait InformationMeasure {
|
|
/// Compute the measure for given data
|
|
fn compute(&self, data: &[f64]) -> Result<f64>;
|
|
|
|
/// Compute pairwise measure (for MI)
|
|
fn compute_pairwise(&self, _x: &[f64], _y: &[f64]) -> Result<f64> {
|
|
Err(OptimizrError::ComputationError(
|
|
"Pairwise computation not supported".to_string(),
|
|
))
|
|
}
|
|
}
|
|
|
|
/// Bounds for optimization
|
|
#[derive(Debug, Clone)]
|
|
pub struct Bounds {
|
|
pub lower: Vec<f64>,
|
|
pub upper: Vec<f64>,
|
|
}
|
|
|
|
impl Bounds {
|
|
pub fn new(bounds: Vec<(f64, f64)>) -> Result<Self> {
|
|
if bounds.is_empty() {
|
|
return Err(OptimizrError::InvalidParameter(
|
|
"Bounds cannot be empty".to_string(),
|
|
));
|
|
}
|
|
|
|
for (lower, upper) in &bounds {
|
|
if lower >= upper {
|
|
return Err(OptimizrError::InvalidParameter(format!(
|
|
"Invalid bounds: lower ({}) >= upper ({})",
|
|
lower, upper
|
|
)));
|
|
}
|
|
}
|
|
|
|
let (lower, upper): (Vec<_>, Vec<_>) = bounds.into_iter().unzip();
|
|
Ok(Self { lower, upper })
|
|
}
|
|
|
|
pub fn dim(&self) -> usize {
|
|
self.lower.len()
|
|
}
|
|
|
|
pub fn clip(&self, x: &[f64]) -> Vec<f64> {
|
|
x.iter()
|
|
.enumerate()
|
|
.map(|(i, &val)| val.max(self.lower[i]).min(self.upper[i]))
|
|
.collect()
|
|
}
|
|
|
|
pub fn is_valid(&self, x: &[f64]) -> bool {
|
|
x.len() == self.dim()
|
|
&& x.iter()
|
|
.enumerate()
|
|
.all(|(i, &val)| val >= self.lower[i] && val <= self.upper[i])
|
|
}
|
|
|
|
pub fn sample(&self, rng: &mut impl rand::Rng) -> Vec<f64> {
|
|
(0..self.dim())
|
|
.map(|i| rng.gen_range(self.lower[i]..self.upper[i]))
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
/// Trait for parallel execution strategies
|
|
pub trait ParallelExecutor {
|
|
fn execute_parallel<F, T>(&self, tasks: Vec<F>) -> Vec<T>
|
|
where
|
|
F: Fn() -> T + Send,
|
|
T: Send;
|
|
}
|
|
|
|
/// Standard rayon-based parallel executor
|
|
#[cfg(feature = "parallel")]
|
|
pub struct RayonExecutor;
|
|
|
|
#[cfg(feature = "parallel")]
|
|
impl ParallelExecutor for RayonExecutor {
|
|
fn execute_parallel<F, T>(&self, tasks: Vec<F>) -> Vec<T>
|
|
where
|
|
F: Fn() -> T + Send,
|
|
T: Send,
|
|
{
|
|
use rayon::prelude::*;
|
|
tasks.into_par_iter().map(|f| f()).collect()
|
|
}
|
|
}
|
|
|
|
/// Sequential executor (fallback)
|
|
pub struct SequentialExecutor;
|
|
|
|
impl ParallelExecutor for SequentialExecutor {
|
|
fn execute_parallel<F, T>(&self, tasks: Vec<F>) -> Vec<T>
|
|
where
|
|
F: Fn() -> T + Send,
|
|
T: Send,
|
|
{
|
|
tasks.into_iter().map(|f| f()).collect()
|
|
}
|
|
}
|