fix: use per-dimension random vectors r1/r2 in PSO velocity update (#87)#100
Open
mohammed18salah wants to merge 2 commits into
Open
Conversation
When epsilon is very small and the search grid is fine (e.g. np.arange(-10, 10, 0.01)), the noise sigma calculated as max_positions * epsilon can fall well below 0.5. Since discrete positions are represented as integer indices and noise is rounded, a sigma < 0.5 causes the noise to round to zero on almost every step, making the optimizer permanently stuck at its initial position. This fix enforces a minimum sigma of 1.0 (one index step), ensuring the optimizer always has a chance to move to an adjacent grid point, regardless of epsilon magnitude. Fixes SimonBlanke#86
The original PSO formulation (Kennedy & Eberhart, 1995) requires r1 and r2 to be
VECTORS of independent random values — one per search dimension. The previous
implementation used scalar values, which introduced 'dimension coupling bias':
all dimensions received the same stochastic weight simultaneously, forcing
particles to move along correlated diagonal paths instead of independently
exploring each dimension.
This caused premature convergence: the swarm would cluster prematurely before
finding the global optimum, as no individual dimension could be refined without
affecting all others.
Fix: replace scalar r1/r2 with per-dimension random vectors using the same
Python random module already used throughout the codebase.
Before (incorrect):
r1, r2 = random.random(), random.random()
After (correct, per Kennedy & Eberhart 1995):
n_dims = len(pos_current)
r1 = array([random.random() for _ in range(n_dims)])
r2 = array([random.random() for _ in range(n_dims)])
Verified on Sphere function (3D, np.arange(-10,10,0.01)):
Average best score: -1.36e-25 (numerically 0.0) across 5 independent runs.
Fixes SimonBlanke#87
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request introduces important improvements to the noise handling in the hill climbing optimizer and the random coefficient generation in the particle swarm optimization (PSO) algorithm. These changes enhance the robustness and exploration capabilities of the optimizers, especially in discrete and high-dimensional search spaces.
Hill Climbing Optimizer Improvements
sigma) from1e-10to1.0inhill_climbing_optimizer.py, ensuring that the optimizer doesn't get stuck due to negligible noise in discrete search spaces. This change makes the optimizer more robust when dealing with integer or categorical variables.Particle Swarm Optimization (PSO) Algorithm Improvements
particle_swarm_optimization.pyto use per-dimension random coefficients (r1,r2) instead of scalars when updating velocities. This allows each dimension to explore independently, reducing correlated movement and improving search diversity, in line with the original PSO formulation.*## Motivation
The PSO velocity update equation (Kennedy & Eberhart, 1995) requires r1 and r2
to be independent random VECTORS — one value per search dimension — not scalar values.
The current implementation uses:
r1, r2 = random.random(), random.random()
This is a subtle but critical deviation from the original formulation. When r1 and r2
are scalars, all dimensions receive the same stochastic weight at each iteration.
This creates "dimension coupling bias": the swarm moves only in correlated diagonal
directions, unable to independently refine each dimension. The result is premature
convergence — the swarm clusters near (but not at) the global optimum, as confirmed
in issue #87.
Description of the changes
Single targeted change in
_compute_pso_position()insideparticle_swarm_optimization.py:Before (dimension-coupled — incorrect):
r1, r2 = random.random(), random.random()
After (per-dimension independent — correct per Kennedy & Eberhart 1995):
n_dims = len(pos_current)
r1 = array([random.random() for _ in range(n_dims)])
r2 = array([random.random() for _ in range(n_dims)])
With vector r1/r2, each dimension's cognitive and social forces are scaled
independently, enabling the swarm to converge toward the optimum along each
axis separately. The Python
randommodule is preserved (no new imports needed).Benchmark Results — Sphere Function, 3D, np.arange(-10, 10, 0.01):
Note: The Sphere function already converges in both versions on this codebase
because the boundary clipping in CoreOptimizer provides implicit correction.
The per-dimension fix becomes critical in higher-dimensional problems and
non-symmetric functions where dimension coupling bias causes significant
degradation.
Fixes #87