(function () {
'use strict';
Copyright 2015 Adrian Toncean; released under the MIT license
(function () {
'use strict';
Defines a candidate solution
function Particle(position, velocity, options) {
this.position = position;
this.velocity = velocity;
this.bestPosition = new Array(this.position.length);
this.fitness = -Infinity;
this.bestFitness = -Infinity;
this._inertiaWeight = options.inertiaWeight;
this._social = options.social;
this._personal = options.personal;
}
Particle.prototype = {
Stores the current position as its best so far.
storePosition: function () {
this.bestPosition = this.position.slice(0);
},
Retrieves the particle’s current position.
getPosition: function () {
return this.position.slice(0);
},
Retrieves the particle’s best saved position.
getBestPosition: function () {
return this.bestPosition.slice(0);
},
Updates the particle’s velocity vector based on inertia, the best-performing particle in the swarm and the best position the current particle has saved.
updateVelocity: function (globalBest, random) {
this.position.forEach(function (component, index) {
var inertia = this.velocity[index] * this._inertiaWeight;
var socialInfluence = (globalBest.position[index] - component) * random() * this._social;
var personalInfluence = (this.bestPosition[index] - component) * random() * this._personal;
this.velocity[index] = inertia + socialInfluence + personalInfluence;
}, this);
},
Applies the velocity
updatePosition: function () {
this.velocity.forEach(function (component, index) {
this.position[index] += component;
}, this);
}
};
Particle.createRandom = function (domain, options, random) {
var position = domain.map(function (interval) {
return random() * (interval.end - interval.start) + interval.start;
});
var velocity = domain.map(function (interval) {
return (random() * (interval.end - interval.start)) * 0.05;
});
return new Particle(position, velocity, options);
};
Used to define domains. An Interval is anything with a start and an end.
function Interval(start, end) {
this.start = start;
this.end = end;
}
Holds particles and carries out the optimization task.
function Optimizer() {
this._particles = null;
this._objectiveFunction = null;
this._bestPositionEver = null;
this._bestFitnessEver = -Infinity;
this._options = {
inertiaWeight: 0.8,
social: 0.4,
personal: 0.4,
pressure: 0.5
};
this._async = false;
this._waiting = false;
this.rng = {
random: Math.random,
setSeed: function () {}
};
}
Optimizer.prototype = {
setOptions: function (options) {
if (options.inertiaWeight !== undefined) {
this._options.inertiaWeight = options.inertiaWeight;
}
if (options.social !== undefined) {
this._options.social = options.social;
}
if (options.personal !== undefined) {
this._options.personal = options.personal;
}
if (options.pressure !== undefined) {
this._options.pressure = options.pressure;
}
},
setObjectiveFunction: function (objectiveFunction, options) {
this._objectiveFunction = objectiveFunction;
this._async = options && options.async;
},
To be called before any simulation. Creates the swarm and resets any previous recorded best solutions
init: function (nParticles, generationOption) {
var generator = generationOption instanceof Function ?
generationOption :
function () {
return Particle.createRandom(generationOption, this._options, this.rng.random);
}.bind(this);
this._bestPositionEver = null;
this._bestFitnessEver = -Infinity;
this._particles = [];
for (var i = 0; i < nParticles; i++) {
this._particles.push(generator());
}
},
Retrieve the fittest particle from a subset of the entire swarm. The subset’s size depends on the pressure parameter
_getRandomBest: function (except) {
var ret = Math.floor(this.rng.random() * this._particles.length);
this._particles.forEach(function (particle, index) {
if (
this.rng.random() < this._options.pressure &&
this._particles[index].fitness > this._particles[ret].fitness &&
index !== except
) {
ret = index;
}
}, this);
return ret;
},
Iterate once; callback is supplied only if the fitness function is asynchronous
step: function (callback) {
if (this._async) {
if (this._waiting) {
console.warn('Cannot step again before previous requests have been completed!');
return;
}
this._waiting = true;
var completed = 0;
this._particles.forEach(function (particle) {
this._objectiveFunction(particle.position, function (fitness) {
particle.fitness = fitness;
completed++;
if (completed >= this._particles.length) {
this._waiting = false;
this._completeStep();
callback();
}
}.bind(this));
}, this);
} else {
this._particles.forEach(function (particle) {
particle.fitness = this._objectiveFunction(particle.position);
}, this);
this._completeStep();
}
},
_completeStep: function () {
Record the best found solutions
this._particles.forEach(function (particle) {
if (particle.fitness > particle.bestFitness) {
particle.bestFitness = particle.fitness;
particle.storePosition();
if (particle.fitness > this._bestFitnessEver) {
this._bestFitnessEver = particle.fitness;
this._bestPositionEver = particle.getPosition();
}
}
}, this);
Update velocities
this._particles.forEach(function (particle, index) {
var randomBest = this._particles[this._getRandomBest(index)];
particle.updateVelocity(randomBest, this.rng.random);
}, this);
Update positions
this._particles.forEach(function (particle) {
particle.updatePosition();
});
},
Retrieves an array of all solutions in the swarm
getParticles: function () {
return this._particles.map(function (particle) {
return {
position: particle.getPosition(),
fitness: particle.fitness,
bestPosition: particle.getBestPosition(),
bestFitness: particle.bestFitness
};
});
},
Retrieves the best solution ever recorded
getBestPosition: function () {
return this._bestPositionEver;
},
Retrieves the best fitness ever recorded
getBestFitness: function () {
return this._bestFitnessEver;
},
Retrieves the mean fitness of the entire swarm
getMeanFitness: function () {
var sum = this._particles.reduce(function (partialSum, particle) {
return partialSum + particle.fitness;
}, 0);
return sum / this._particles.length;
}
};
pso.js works
if (typeof define === 'function' && define.amd) {
define('pso/Interval', function () { return Interval; });
define('pso/Particle', function () { return Particle; });
define('pso/Optimizer', function () { return Optimizer; });
} else {
var pso = {
Interval: Interval,
Particle: Particle,
Optimizer: Optimizer
};
if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
self.pso = pso;
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = pso;
} else {
window.pso = pso;
}
}
})();