Languages
[Edit]
EN

JavaScript - Particle Swarm Optimization (PSO) algorithm example

11 points
Created by:
Welsh
772

In this article, we would like to show example implementation of Particle Swarm Optimization (PSO) algorithm using JavaScript.

PSO is optimization technique that in iterative way finds the best solution. It is non-deterministic algorithm. The main concept is to use small objects (particles) that seaches space for the best solution. Each particle stores own best solution. Algoritm stores the best known global solution - selected from best particle solution. Particles looking for the best solution moves in direction calcluated by weighted miltplication from: ramdom direction, particle best solution and global best solution.

PSO is good to use when we want to find quite good global solution for complex problem - it is resistant for local minimas.

Hint: use it when goal function does not take too much computations time.

 

Practical example

In this section, you can find PSO that tries to find zero place of y = f(x1, x2) = x1^2 + x2^2 equation. The searched space is -4 < x1 < +4 and -4 < x2 < +4. Finaly algorithm should find place that is very close to (0, 0) point.

Hint: in the source code, particle inertia (0.729), global and local accelerations (1.49445) come from science literature. They are selected empirically giving the best results for many existing problems.

// ONLINE-RUNNER:browser;

function PsoAlgorithm(config) {
    var positionRanges = config.positionRanges;  // range array
    var calculateError = config.calculateError;  // goal function
    if (positionRanges == null) {
        throw new Error('Input ranges array has not been defined.');
    }
    if (calculateError == null) {
        throw new Error('Calculate error function has not been defined.');
    }
    var particleSpeed = config.particleSpeed || 0.1;  // should be in range from 0.0 to 1.0
    var particleInertia = config.particleInertia || 0.729;
    var globalAcceleration = config.globalAcceleration || 1.49445;
    var localAcceleration = config.localAcceleration || 1.49445;
    var onIterationStarted = config.onIterationStarted;
    var onParticleComputed = config.onParticleComputed;
    var onIterationFinished = config.onIterationFinished;
    function createSolution() {
        var buffer = [ ];
        for (var i = 0; i < positionRanges.length; ++i) {
            buffer.push(Math.random());
        }
        var result = {
            buffer: buffer,
            position: null,
            error: Number.POSITIVE_INFINITY
        };
        return result;
    }
    function correctPosition(position) {
        if (position < 0.0) {
            return 0.0;
        }
        if (position > 1.0) {
            return 1.0;
        }
        return position;
    }
    function denormalizePosition(position) {
        var result = [ ];
        for (var i = 0; i < positionRanges.length; ++i) {
            var range = positionRanges[i];
            result.push(position[i] * (range.maximum - range.minimum) + range.minimum);
        }
        return result;
    }
    this.createParticle = function() {
        var actualBuffer = [ ];
        var actualVelocity = [ ];
        for (var i = 0; i < positionRanges.length; ++i) {
            var position = Math.random();
            var velocity = particleSpeed * Math.random();
            actualVelocity.push(velocity);
            actualBuffer.push(position);
        }
        var particle = {
            bestSolution: createSolution(),
            actualSolution: {
                velocity: actualVelocity,  // normalized particle velocity
                buffer: actualBuffer,      // normalized particle position
                position: null,
                error: Number.POSITIVE_INFINITY
            }
        };
        return particle;
    };
    this.createParticles = function(particlesCount) {
        var result = [ ];
        for (var i = 0; i < particlesCount; ++i) {
            result.push(this.createParticle());
        }
        return result;
    };
    this.computeParticle = function(particle, globalBuffer) {
        var bestSolution = particle.bestSolution;
        var bestBuffer = bestSolution.buffer;
        var actualSolution = particle.actualSolution;
        var actualBuffer = actualSolution.buffer;
        var actualVelocity = actualSolution.velocity;
        for (var i = 0; i < bestBuffer.length; ++i) {
            var globalDifference = globalBuffer[i] - actualBuffer[i];
            var localDifference = bestBuffer[i] - actualBuffer[i];
           	var globalInfluance = globalAcceleration * globalDifference * Math.random();
          	var localInfluance = localAcceleration * localDifference * Math.random();
            actualVelocity[i] = particleInertia * actualVelocity[i] + globalInfluance + localInfluance;
            actualBuffer[i] = correctPosition(actualBuffer[i] + actualVelocity[i]);
        }
        var computedPosition = denormalizePosition(actualBuffer);
        var computedError = calculateError(computedPosition);
        actualSolution.error = computedError;
        actualSolution.position = computedPosition;
        if (computedError < bestSolution.error) {
            bestSolution = particle.bestSolution = {
                position: computedPosition,
                buffer: actualBuffer.slice(),
                error: computedError
            };
        }
        return bestSolution;
    };
    this.computeEpoch = function(particles, globalBuffer) {
        var epochSolution = {
            error: Number.POSITIVE_INFINITY
        };
        for (var i = 0; i < particles.length; ++i) {
            var particle = particles[i];
            var particleSolution = this.computeParticle(particle, globalBuffer);
            if (particleSolution.error < epochSolution.error) {
                epochSolution = particleSolution;
            }
            if (onParticleComputed) {
                onParticleComputed(i, particle.actualSolution, particleSolution);
            }
        }
        return epochSolution;
    };
    this.computeEpoches$1 = function(toleratedError, epochesLimit, particles) {
        if (particles.length == 0) {
            throw new Error("Particles have not been defined.");
        }
        var globalSolution = createSolution();
        for (var i = 0; i < epochesLimit; ++i) {
            if (onIterationStarted) {
                onIterationStarted(i);
            }
            var epochSolution = this.computeEpoch(particles, globalSolution.buffer);
            if (epochSolution.error < globalSolution.error) {
                globalSolution = epochSolution;
            }
            if (onIterationFinished) {
                onIterationFinished(i, globalSolution);
            }
            if (globalSolution.error < toleratedError) {
                break;
            }
        }
        return globalSolution;
    };
    this.computeEpoches$2 = function(toleratedError, epochesLimit, particlesCount) {
        var particles = this.createParticles(particlesCount);
        return this.computeEpoches$1(toleratedError, epochesLimit, particles);
    };
}



// Usage example:

var algorithm = new PsoAlgorithm({
    positionRanges: [
      	{
            // x1 search range
            minimum: -4, maximum: +4
        },
        {
            // x2 search range
            minimum: -4, maximum: +4
        }
    ],
    particleSpeed: 0.1,
    particleInertia: 0.729,
    globalAcceleration: 1.49445,
    localAcceleration: 1.49445,
    calculateError: function(position) {
        var x1 = position[0];
        var x2 = position[1];
        // we work with this function
        var y = x1 * x1 + x2 * x2;  // y = f(x1, x2) = x1^2 + x2^2
        // we look for zero place of y=f(x1, x2), so as unversal error formula we can use Math.abs(y)
        return Math.abs(y);
    },
    onIterationStarted: function(epochIndex) {
        // Nothing here ...
    },
    onParticleComputed: function(epochIndex, actualSolution, particleSolution) {
        // Nothing here ...
    },
    onIterationFinished: function(epochIndex, globalSolution) {
        console.log(epochIndex + '\t' + globalSolution.error + '\t[' + globalSolution.position + ']');
    }
});

var toleratedError = 0.01;  // samller or equal computation error stops algorithm
var epochesLimit = 100;     // maximal number of executed iterations
var particlesCount = 10;

console.log('[epoch]\t[error]\t\t\t[position]\n');

var solution = algorithm.computeEpoches$2(toleratedError, epochesLimit, particlesCount);

console.log('-------------------------------');
console.log('Optimal solution:');
console.log('  error: ' + solution.error);
console.log('  position: [' + solution.position + ']');


/*
    // OR:
    var particles = algorithm.createParticles(particlesCount);  // initial particles can be loaded from memory letting to continue previously stopped computations
    var solution = algorithm.computeEpoches$1(toleratedError, epochesLimit, particles);
*/

 

See also

  1. JavaScript - async loop based scattered Particle Swarm Optimization (PSO) algorithm example

References

  1. Particle swarm optimization - Wikipedia
Donate to Dirask
Our content is created by volunteers - like Wikipedia. If you think, the things we do are good, donate us. Thanks!
Join to our subscribers to be up to date with content, news and offers.
Native Advertising
🚀
Get your tech brand or product in front of software developers.
For more information Contact us
Dirask - we help you to
solve coding problems.
Ask question.

❤️💻 🙂

Join