Particle Swarm Optimization - Namir - 2025-09-21
I present code for the Particle Swarm Optimization (PSO) for multivariable continuous functions. This algorithm i among the most popular evolutionary optimization methods and has been studied extensively.
The function PSO implements the optimization algorithm. The parameter of this function include the following:
1. Parameter Lb is a list of lower limits of the optimized variables.
2. Parameter Ub is a list of upper limits of the optimized variables.
3. Parameter Maxpop is the population size.
4. Parameter MaxIters is the maximum number of iterations.
The PSO function returns the list, bestX, that contains the values of the optimized variables, and best optimized function value bestFx.
Function MyFx is the optimized function and has two parameters:
1. Parameter x is a row vector of the optimized variables.
2. Parameter n which specifies the number of elements in parameter x.
Here is the code:
Code: // ====================================================================
// PSO - Particle Swarm Optimization Function for HP Prime
// ====================================================================
//
// This function implements the Particle Swarm Optimization algorithm
// to find the minimum of a multivariable function MyFx.
//
// SYNTAX:
// {bestX, bestFx} := PSO(Lb, Ub, Maxpop, MaxIters)
// {bestX, bestFx} := PSO(Lb, Ub, Maxpop, MaxIters, w, c1, c2)
//
// INPUTS:
// Lb - Row vector of lower bounds for each variable
// Ub - Row vector of upper bounds for each variable
// Maxpop - Population size (number of particles)
// MaxIters- Maximum number of iterations
// w - Inertia weight (optional, default = 0.7)
// c1 - Cognitive parameter (optional, default = 1.5)
// c2 - Social parameter (optional, default = 1.5)
//
// OUTPUTS:
// bestX - Row vector containing optimized variable values
// bestFx - Best function value found
//
// EXAMPLE:
// // Minimize MyFx
// Lb := {0, 0, 0, 0};
// Ub := {5, 5, 5, 5};
// {optX, optFx} := PSO(Lb, Ub, 100, 1000);
//
// AUTHOR: HP Prime Implementation
// VERSION: 1.0
// ====================================================================
EXPORT PSO(Lb, Ub, Maxpop, MaxIters)
BEGIN
// Local variable declarations
LOCAL NVar; // Number of variables
LOCAL w, c1, c2; // PSO parameters
LOCAL particles; // Particle positions [Maxpop x NVar]
LOCAL velocities; // Particle velocities [Maxpop x NVar]
LOCAL fitness; // Current fitness values [Maxpop x 1]
LOCAL pBest; // Personal best positions [Maxpop x NVar]
LOCAL pBestFx; // Personal best fitness values [Maxpop x 1]
LOCAL gBest; // Global best position [1 x NVar]
LOCAL gBestFx; // Global best fitness value
LOCAL bestX; // Final best position
LOCAL bestFx; // Final best fitness
LOCAL i, j, iter; // Loop counters
LOCAL r1, r2; // Random numbers for PSO update
LOCAL newPos; // Temporary new position
LOCAL newFx; // Temporary fitness value
LOCAL range; // Variable ranges for initialization
// ================================================================
// INPUT VALIDATION AND PARAMETER INITIALIZATION
// ================================================================
// Validate input parameters
IF TYPE(Lb) <> 6 OR TYPE(Ub) <> 6 THEN
MSGBOX("Error: Lb and Ub must be lists (vectors)");
RETURN {{}, 1E99};
END;
// Get number of variables
NVar := SIZE(Lb);
// Check dimensions consistency
IF SIZE(Ub) <> NVar THEN
MSGBOX("Error: Lb and Ub must have same dimensions");
RETURN {{}, 1E99};
END;
// Check bounds validity
FOR i FROM 1 TO NVar DO
IF Lb(i) >= Ub(i) THEN
MSGBOX("Error: Lower bounds must be less than upper bounds");
RETURN {{}, 1E99};
END;
END;
w := 0.7;
c1 := 1.5; // Default cognitive parameter
c2 := 1.5; // Default social parameter
// Validate population size and iterations
IF Maxpop < 1 OR MaxIters < 1 THEN
MSGBOX("Error: Population size and iterations must be positive");
RETURN {{}, 1E99};
END;
// ================================================================
// SWARM INITIALIZATION
// ================================================================
// Initialize matrices
particles := MAKEMAT(0, Maxpop, NVar); // Particle positions
velocities := MAKEMAT(0, Maxpop, NVar); // Particle velocities
fitness := MAKEMAT(0, Maxpop, 1); // Fitness values
pBest := MAKEMAT(0, Maxpop, NVar); // Personal best positions
pBestFx := MAKEMAT(1E99, Maxpop, 1); // Personal best fitness
// Calculate variable ranges for velocity initialization
range := {};
FOR i FROM 1 TO NVar DO
range := CONCAT(range, {Ub(i) - Lb(i)});
END;
// Initialize particles with random positions within bounds
FOR i FROM 1 TO Maxpop DO
FOR j FROM 1 TO NVar DO
// Random position within bounds
particles(i, j) := Lb(j) + RANDOM() * (Ub(j) - Lb(j));
// Random initial velocity (±20% of variable range)
velocities(i, j) := (RANDOM() - 0.5) * 0.4 * range(j);
END;
// Evaluate initial fitness
newPos := {};
FOR j FROM 1 TO NVar DO
newPos := CONCAT(newPos, {particles(i, j)});
END;
fitness(i, 1) := MyFx(newPos, NVar);
// Initialize personal best
FOR j FROM 1 TO NVar DO
pBest(i, j) := particles(i, j);
END;
pBestFx(i, 1) := fitness(i, 1);
END;
// Find initial global best
gBestFx := 1E99;
gBest := {};
FOR i FROM 1 TO Maxpop DO
IF pBestFx(i, 1) < gBestFx THEN
gBestFx := pBestFx(i, 1);
gBest := {};
FOR j FROM 1 TO NVar DO
gBest := CONCAT(gBest, {pBest(i, j)});
END;
END;
END;
// Display initial status
PRINT("PSO Optimization Started");
PRINT("Variables: " + NVar);
PRINT("Population: " + Maxpop);
PRINT("Max Iterations: " + MaxIters);
PRINT("Initial Best: " + gBestFx);
PRINT("");
// ================================================================
// MAIN PSO ITERATION LOOP
// ================================================================
FOR iter FROM 1 TO MaxIters DO
// Update each particle
FOR i FROM 1 TO Maxpop DO
// Generate random numbers for stochastic components
r1 := RANDOM();
r2 := RANDOM();
// Update velocity and position for each dimension
FOR j FROM 1 TO NVar DO
// PSO velocity update equation:
// v(t+1) = w*v(t) + c1*r1*(pBest - x(t)) + c2*r2*(gBest - x(t))
velocities(i, j) := w * velocities(i, j) +
c1 * r1 * (pBest(i, j) - particles(i, j)) +
c2 * r2 * (gBest(j) - particles(i, j));
// Velocity clamping (prevent excessive velocities)
IF ABS(velocities(i, j)) > 0.5 * range(j) THEN
velocities(i, j) := SIGN(velocities(i, j)) * 0.5 * range(j);
END;
// Position update: x(t+1) = x(t) + v(t+1)
particles(i, j) := particles(i, j) + velocities(i, j);
// Boundary handling - reflect particles that go out of bounds
IF particles(i, j) < Lb(j) THEN
particles(i, j) := Lb(j) + (Lb(j) - particles(i, j));
velocities(i, j) := -0.5 * velocities(i, j);
END;
IF particles(i, j) > Ub(j) THEN
particles(i, j) := Ub(j) - (particles(i, j) - Ub(j));
velocities(i, j) := -0.5 * velocities(i, j);
END;
// Final boundary check (clamp if still outside)
IF particles(i, j) < Lb(j) THEN
particles(i, j) := Lb(j);
velocities(i, j) := 0;
END;
IF particles(i, j) > Ub(j) THEN
particles(i, j) := Ub(j);
velocities(i, j) := 0;
END;
END; // End dimension loop
// Evaluate fitness of updated particle
newPos := {};
FOR j FROM 1 TO NVar DO
newPos := CONCAT(newPos, {particles(i, j)});
END;
newFx := MyFx(newPos, NVar);
fitness(i, 1) := newFx;
// Update personal best if improved
IF newFx < pBestFx(i, 1) THEN
pBestFx(i, 1) := newFx;
FOR j FROM 1 TO NVar DO
pBest(i, j) := particles(i, j);
END;
// Update global best if this is the new global optimum
IF newFx < gBestFx THEN
gBestFx := newFx;
gBest := {};
FOR j FROM 1 TO NVar DO
gBest := CONCAT(gBest, {particles(i, j)});
END;
END;
END;
END; // End particle loop
// Display progress every 10 iterations or at the end
IF (iter MOD 10 = 0) OR (iter = MaxIters) THEN
PRINT("Iter " + iter + ": Best = " + ROUND(gBestFx, 6));
// Optional: Early termination check for convergence
// (Can be uncommented if desired)
// IF iter > 20 AND gBestFx < 1E-12 THEN
// PRINT("Converged at iteration " + iter);
// BREAK;
// END;
END;
// Adaptive parameter adjustment (optional enhancement)
// Decrease inertia weight linearly over iterations
// w := 0.9 - 0.5 * iter / MaxIters;
END; // End iteration loop
// ================================================================
// FINALIZATION AND RESULTS
// ================================================================
// Prepare final results
bestX := gBest;
bestFx := gBestFx;
// Display final results
PRINT("");
PRINT("=== PSO OPTIMIZATION COMPLETE ===");
PRINT("Best Function Value: " + bestFx);
PRINT("Optimal Variables:");
FOR i FROM 1 TO NVar DO
PRINT(" x(" + i + ") = " + ROUND(bestX(i), 8));
END;
PRINT("");
// Return results as list: {bestX, bestFx}
RETURN {bestX, bestFx};
END; // End PSO function
// ====================================================================
// EXAMPLE OBJECTIVE FUNCTION: MyFx
// ====================================================================
//
// This is an example implementation of the objective function.
// Users should replace this with their own function to optimize.
//
// INPUTS:
// x - Row vector of variables to optimize
// n - Number of variables (length of x)
//
// OUTPUT:
// Function value at point x
// ====================================================================
EXPORT MyFx(x, n)
BEGIN
LOCAL result, z;
LOCAL i, j;
result := 0;
FOR i FROM 1 TO n DO
z := i;
FOR j FROM 1 TO 4 DO
z := z + (i+j)/10^j;
END;
result := result + (x(i) - z)^2;
END;
RETURN result;
END; // End MyFx function
Here is a sample run.
PSO({0,0,0,0},{5,5,5,5},100,1000)
where Lb is {0,0,0,0, Ub is {5,5,5,5}, Maxpop is 100, and MaxIters is 1000.
The above call returns the exact solution {{1.2345,2.3456,3.4567,4.5678},0}
|