Particle Swarm Optimization

+- HP Forums (https://www.hpmuseum.org/forum)
+-- Forum: HP Software Libraries (https://www.hpmuseum.org/forum/forum-10.html)
+--- Forum: HP Prime Software Library (https://www.hpmuseum.org/forum/forum-15.html)
+--- Thread: Particle Swarm Optimization (/thread-24079.html)



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}