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
This commit is contained in:
@@ -25,7 +25,14 @@ from optimizr.core import (
|
||||
bootstrap_returns_py,
|
||||
)
|
||||
|
||||
__version__ = "0.1.0"
|
||||
# Try to import maths_toolkit from Rust backend
|
||||
try:
|
||||
from optimizr import _core
|
||||
maths_toolkit = _core
|
||||
except (ImportError, AttributeError):
|
||||
maths_toolkit = None
|
||||
|
||||
__version__ = "0.2.0"
|
||||
__all__ = [
|
||||
"HMM",
|
||||
"mcmc_sample",
|
||||
@@ -40,4 +47,5 @@ __all__ = [
|
||||
"compute_risk_metrics_py",
|
||||
"estimate_half_life_py",
|
||||
"bootstrap_returns_py",
|
||||
"maths_toolkit",
|
||||
]
|
||||
|
||||
+59
-11
@@ -88,10 +88,14 @@ def mcmc_sample(
|
||||
>>> print(f"Posterior mean: {np.mean(samples[:, 0]):.2f}")
|
||||
"""
|
||||
if RUST_AVAILABLE:
|
||||
# Convert to lists if numpy arrays
|
||||
data_list = data.tolist() if hasattr(data, 'tolist') else list(data)
|
||||
params_list = initial_params.tolist() if hasattr(initial_params, 'tolist') else list(initial_params)
|
||||
|
||||
samples = _rust_mcmc_sample(
|
||||
log_likelihood_fn=log_likelihood_fn,
|
||||
data=data.tolist(),
|
||||
initial_params=initial_params.tolist(),
|
||||
data=data_list,
|
||||
initial_params=params_list,
|
||||
param_bounds=param_bounds,
|
||||
n_samples=n_samples,
|
||||
burn_in=burn_in,
|
||||
@@ -111,8 +115,16 @@ def differential_evolution(
|
||||
bounds: List[Tuple[float, float]],
|
||||
popsize: int = 15,
|
||||
maxiter: int = 1000,
|
||||
f: float = 0.8,
|
||||
cr: float = 0.7,
|
||||
f: Optional[float] = None,
|
||||
cr: Optional[float] = None,
|
||||
strategy: str = "rand1",
|
||||
seed: Optional[int] = None,
|
||||
tol: float = 1e-6,
|
||||
atol: float = 1e-8,
|
||||
track_history: bool = False,
|
||||
parallel: bool = False,
|
||||
adaptive: bool = False,
|
||||
constraint_penalty: float = 1000.0,
|
||||
) -> Tuple[np.ndarray, float]:
|
||||
"""
|
||||
Differential Evolution global optimizer.
|
||||
@@ -130,10 +142,26 @@ def differential_evolution(
|
||||
Population size multiplier (total size = popsize × n_params)
|
||||
maxiter : int, default=1000
|
||||
Maximum number of generations
|
||||
f : float, default=0.8
|
||||
Mutation factor (typically 0.5-2.0)
|
||||
cr : float, default=0.7
|
||||
Crossover probability (typically 0.1-0.9)
|
||||
f : float, optional
|
||||
Mutation factor (typically 0.5-2.0). If None, uses 0.8
|
||||
cr : float, optional
|
||||
Crossover probability (typically 0.1-0.9). If None, uses 0.7
|
||||
strategy : str, default="rand1"
|
||||
Mutation strategy: "rand1", "best1", "currenttobest1", "rand2", "best2"
|
||||
seed : int, optional
|
||||
Random seed for reproducibility
|
||||
tol : float, default=1e-6
|
||||
Convergence tolerance for function value changes
|
||||
atol : float, default=1e-8
|
||||
Absolute convergence tolerance
|
||||
track_history : bool, default=False
|
||||
Whether to track convergence history
|
||||
parallel : bool, default=False
|
||||
Whether to use parallel evaluation (not supported for Python callbacks)
|
||||
adaptive : bool, default=False
|
||||
Whether to use adaptive jDE parameter control
|
||||
constraint_penalty : float, default=1000.0
|
||||
Penalty for constraint violations
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -150,7 +178,8 @@ def differential_evolution(
|
||||
>>> result = differential_evolution(
|
||||
... objective_fn=rosenbrock,
|
||||
... bounds=[(-5, 5)] * 10,
|
||||
... popsize=15,
|
||||
... strategy="best1",
|
||||
... adaptive=True,
|
||||
... maxiter=1000
|
||||
... )
|
||||
>>> print(f"Minimum: {result[1]:.6f} at {result[0]}")
|
||||
@@ -163,14 +192,33 @@ def differential_evolution(
|
||||
maxiter=maxiter,
|
||||
f=f,
|
||||
cr=cr,
|
||||
strategy=strategy,
|
||||
seed=seed,
|
||||
tol=tol,
|
||||
atol=atol,
|
||||
track_history=track_history,
|
||||
parallel=parallel,
|
||||
adaptive=adaptive,
|
||||
constraint_penalty=constraint_penalty,
|
||||
)
|
||||
return np.array(result.x), result.fun
|
||||
else:
|
||||
# Pure Python fallback (scipy)
|
||||
try:
|
||||
from scipy.optimize import differential_evolution as scipy_de
|
||||
result = scipy_de(objective_fn, bounds=bounds, maxiter=maxiter,
|
||||
popsize=popsize, mutation=f, recombination=cr)
|
||||
mutation = f if f is not None else 0.8
|
||||
recombination = cr if cr is not None else 0.7
|
||||
result = scipy_de(
|
||||
objective_fn,
|
||||
bounds=bounds,
|
||||
maxiter=maxiter,
|
||||
popsize=popsize,
|
||||
mutation=mutation,
|
||||
recombination=recombination,
|
||||
seed=seed,
|
||||
tol=tol,
|
||||
atol=atol
|
||||
)
|
||||
return result.x, result.fun
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
|
||||
Reference in New Issue
Block a user