UNPKG

ixjs-evolution

Version:

A set of evolutionary search algorithms done through IxJS

220 lines (188 loc) 7.12 kB
var GA = require('./lib/GA/index'); var ES = require('./lib/ES/index'); var DE = require('./lib/DE/index'); var gaCrossOver = GA.crossOver; var gaMutation = GA.mutation; var esCrossOver = ES.crossOver; var esMutation = ES.mutation; var deCrossOver = DE.crossOver; var deMutation = DE.mutation; var util = require('./lib/util'); var selection = require('./lib/selection'); var next = util.next; var selectN = util.selectN; var _ = require('lodash'); var selectionType = { FIRST_FIT: selection.firstFit, RANDOM_FIT: selection.randomFit, WEIGHTED_ROULETTE: selection.weightedRoulette, }; var mutationType = { GA_GAUSSIAN: gaMutation.gaussian, ES_GAUSSIAN: esMutation.gaussian, DE_TRIAL_VECTOR: deMutation.trialVector }; var xOverType = { GA_ONE_POINT: gaCrossOver.onePoint, GA_TWO_POINT: gaCrossOver.twoPoint, GA_N_POINT: gaCrossOver.nPoint, ES_N_POINT: esCrossOver.nPoint, DE_BINOMIAL: deCrossOver.binomial, DE_EXPONENTIAL: deCrossOver.exponential }; // Exports the set of enumerables. var Evolution = { xOverType: xOverType, selectionType: selectionType, mutationType: mutationType, /** * Grabs the first fittest individual from the population. It uses the * sequential enumerator for selection so the results will always be * consistent. * * @param {Array} basePopulation * @param {Function} fitnessFn * @param {Number} minFit * @param {Number} prop * @return {Ix.Enumerable} */ constructGA: function(options) { var settings = _.assign({ selectionFn: selectionType.FIRST_FIT, xOverFn: xOverType.GA_ONE_POINT, mutationFn: mutationType.GA_GAUSSIAN, fitnessFn: null, basePopulation: [], maximize: true, lambda: 0, heightAdjust: 1, //Used for Gaussian mutation sigma: 1, //Used for Gaussian mutation minFit: null, //Used for the selection schemes utilizing a min/max fitness for selection }, options); if (settings.lambda === 0) { settings.lambda = settings.basePopulation.length; } var select = settings.selectionFn(settings); var xOver = settings.xOverFn(select, settings.lambda); var mutate = settings.mutationFn(xOver, settings); return mutate.getEnumerator(); }, constructDE: function(options) { var settings = _.assign({ xOverFn: xOverType.DE_EXPONENTIAL, mutationFn: mutationType.DE_TRIAL_VECTOR, fitnessFn: null, basePopulation: [], maximize: true, lambda: 0, beta: 0.05, p: 0.05, minFit: null //Used for the selection schemes utilizing a min/max fitness for selection }, options); if (settings.lambda === 0) { settings.lambda = settings.basePopulation.length; } var mutate = settings.mutationFn(settings); var xOver = settings.xOverFn(mutate, settings); return xOver.getEnumerator(); }, constructES: function(options) { var settings = _.assign({ selectionFn: selectionType.FIRST_FIT, xOverFn: xOverType.ES_N_POINT, mutationFn: mutationType.ES_GAUSSIAN, fitnessFn: null, basePopulation: [], maximize: true, lambda: 0, sigmaMutationAmount: 0.99, sigmaMutationRate: 0.2, heightAdjust: 1, //Used for Gaussian sigma: 1, //Used for Gaussian mutation minFit: null, //Used for the selection schemes utilizing a min/max fitness for selection numParents: 2, numChildren: 2 }, options); Evolution.initializePopulationParameters(options.basePopulation, settings.sigma); if (settings.lambda === 0) { settings.lambda = settings.basePopulation.length; } var select = settings.selectionFn(settings); var xOver = settings.xOverFn(select, settings.lambda, settings.numChildren, settings.numParents); var mutate = settings.mutationFn(xOver, settings); return mutate.getEnumerator(); }, /** * Performs a roulette selection from the provided population * @param {Array} basePopulation * @param {Function} fitnessFn * @param {Number} lambda The amount to select from the population * @return {Array} A subset of the base population selected by their fitness */ rouletteSelection: function(basePopulation, fitnessFn, lambda) { var enu = selection.rouletteSelector({ basePopulation: basePopulation, fitnessFn: fitnessFn, lambda: lambda }); enu = selectN(enu, lambda); return next(enu.getEnumerator()); }, /** * Its the evolution circle of life. This will perform the lambda selection on the children * and mutate the base population through the provided populationSelector. * * @param {{ * generational: Boolean, * fixedPopulation: Boolean, * maximize: Boolean, * basePopulation: Array, * selector: Ix.Enumerable, * selectionFn: Function, * fitnessFn: Function * }} options * @return {Function} The function that will progress the population one more generation */ loop: function(options) { var config = _.assign({ generational: false, fixedPopulation: true, maximize: false }, options); // Purely for optimizing. It kind of defeats the purpose of constructing a config var basePopulation = config.basePopulation; var selector = config.selector; var selectionFn = config.selectionFn; var generational = config.generational; var max = config.maximize; var lambda = config.lambda || config.basePopulation.length; return function() { // sets up the children and the next population. var children = next(selector); var newPop = children; if (!generational) { newPop = newPop.concat(basePopulation); } // Mutates the base population with the selectionFn var selectionEnum = selectionFn({ fitnessFn: config.fitnessFn, basePopulation: newPop, maximize: config.maximize }).getEnumerator(); for (var i = 0; i < lambda; i++) { basePopulation.pop(); basePopulation.unshift(next(selectionEnum)); } return children; }; }, initializePopulationParameters: function(population, sigmaRange) { for(var i = 0; i < population.length; i++) { population[i].params = []; for (var j = 0; j < population[i].length; j++) { population[i].params.push(Math.random() * sigmaRange); } } } }; module.exports = Evolution;