From ad4e22e87d7ea73cb178d113a369f19ee2fbac6a Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Sun, 29 Mar 2026 19:05:16 +0200 Subject: [PATCH] =?UTF-8?q?fix(hyperopt):=20PSO=20premature=20convergence?= =?UTF-8?q?=20=E2=80=94=20tune=20inertia/cognitive/social?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Argmin defaults: inertia=0.72, cognitive=1.19, social=1.19 Problem: social >> inertia causes particles to collapse toward the global best immediately. With only 1 LHS trial as the initial best, the entire swarm clusters around that point and can't explore. Every PSO trial was worse than the random LHS trial. Fix: inertia=0.9 (high momentum, maintains exploration), cognitive=1.5 (strong personal best memory), social=0.8 (weak global pull, prevents premature convergence). Applied to both sequential and parallel optimizer paths. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/ml-hyperopt/src/optimizer.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/crates/ml-hyperopt/src/optimizer.rs b/crates/ml-hyperopt/src/optimizer.rs index d2f3c396e..43f3d0d17 100644 --- a/crates/ml-hyperopt/src/optimizer.rs +++ b/crates/ml-hyperopt/src/optimizer.rs @@ -374,8 +374,18 @@ impl ArgminOptimizer { let lower_bounds: Vec = bounds.iter().map(|(min, _)| *min).collect(); let upper_bounds: Vec = bounds.iter().map(|(_, max)| *max).collect(); - // Create Particle Swarm solver - let solver = ParticleSwarm::new((lower_bounds, upper_bounds), self.n_particles); + // Create Particle Swarm solver with tuned parameters. + // Default argmin: inertia=0.72, cognitive=1.19, social=1.19 + // Problem: social >> inertia → premature convergence to global best. + // Fix: higher inertia (0.9) for exploration, lower social (0.8) to + // reduce collapse toward the single LHS best point. + let solver = ParticleSwarm::new((lower_bounds, upper_bounds), self.n_particles) + .with_inertia_factor(0.9) + .map_err(|e| anyhow::anyhow!("PSO inertia: {e}"))? + .with_cognitive_factor(1.5) + .map_err(|e| anyhow::anyhow!("PSO cognitive: {e}"))? + .with_social_factor(0.8) + .map_err(|e| anyhow::anyhow!("PSO social: {e}"))?; // Run optimization (parallel execution enabled via rayon feature) // CRITICAL FIX (2025-11-03): Removed .target_cost(0.0) to prevent early termination @@ -798,7 +808,13 @@ impl ArgminOptimizer { let lower_bounds: Vec = bounds.iter().map(|(min, _)| *min).collect(); let upper_bounds: Vec = bounds.iter().map(|(_, max)| *max).collect(); - let solver = ParticleSwarm::new((lower_bounds, upper_bounds), effective_particles); + let solver = ParticleSwarm::new((lower_bounds, upper_bounds), effective_particles) + .with_inertia_factor(0.9) + .map_err(|e| anyhow::anyhow!("PSO inertia: {e}"))? + .with_cognitive_factor(1.5) + .map_err(|e| anyhow::anyhow!("PSO cognitive: {e}"))? + .with_social_factor(0.8) + .map_err(|e| anyhow::anyhow!("PSO social: {e}"))?; let res = Executor::new(cost_fn, solver) .configure(|state| state.max_iters(max_iters as u64))