Files
optimiz-rs/src/core.rs
T
Melvin Avarez 79f51e4775 Release v0.2.0: Comprehensive DE, Mathematical Toolkit, Optimal Control
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
2025-12-10 18:54:32 +01:00

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()
}
}