genetic-js-no-ww
Version:
Advanced genetic and evolutionary algorithm library (no web workers fork)
233 lines (192 loc) • 7.37 kB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
window.Genetic = require('./genetic');
},{"./genetic":2}],2:[function(require,module,exports){
var Genetic = Genetic || (function(){
'use strict';
// facilitates communcation between web workers
var Serialization = {
"stringify": function (obj) {
return JSON.stringify(obj, function (key, value) {
if (value instanceof Function || typeof value == "function") return "__func__:" + value.toString();
if (value instanceof RegExp) return "__regex__:" + value;
return value;
});
}, "parse": function (str) {
return JSON.parse(str, function (key, value) {
if (typeof value != "string") return value;
if (value.lastIndexOf("__func__:", 0) === 0) return eval('(' + value.slice(9) + ')');
if (value.lastIndexOf("__regex__:", 0) === 0) return eval('(' + value.slice(10) + ')');
return value;
});
}
};
var Clone = function(obj) {
if (obj == null || typeof obj != "object")
return obj;
return JSON.parse(JSON.stringify(obj));
};
var Optimize = {
"Maximize": function (a, b) { return a >= b; }
, "Minimize": function (a, b) { return a < b; }
};
var Select1 = {
"Tournament2": function(pop) {
var n = pop.length;
var a = pop[Math.floor(Math.random()*n)];
var b = pop[Math.floor(Math.random()*n)];
return this.optimize(a.fitness, b.fitness) ? a.entity : b.entity;
}, "Tournament3": function(pop) {
var n = pop.length;
var a = pop[Math.floor(Math.random()*n)];
var b = pop[Math.floor(Math.random()*n)];
var c = pop[Math.floor(Math.random()*n)];
var best = this.optimize(a.fitness, b.fitness) ? a : b;
best = this.optimize(best.fitness, c.fitness) ? best : c;
return best.entity;
}, "Fittest": function (pop) {
return pop[0].entity;
}, "Random": function (pop) {
return pop[Math.floor(Math.random()*pop.length)].entity;
}, "RandomLinearRank": function (pop) {
this.internalGenState["rlr"] = this.internalGenState["rlr"]||0;
return pop[Math.floor(Math.random()*Math.min(pop.length,(this.internalGenState["rlr"]++)))].entity;
}, "Sequential": function (pop) {
this.internalGenState["seq"] = this.internalGenState["seq"]||0;
return pop[(this.internalGenState["seq"]++)%pop.length].entity;
}
};
var Select2 = {
"Tournament2": function(pop) {
return [Select1.Tournament2.call(this, pop), Select1.Tournament2.call(this, pop)];
}, "Tournament3": function(pop) {
return [Select1.Tournament3.call(this, pop), Select1.Tournament3.call(this, pop)];
}, "Random": function (pop) {
return [Select1.Random.call(this, pop), Select1.Random.call(this, pop)];
}, "RandomLinearRank": function (pop) {
return [Select1.RandomLinearRank.call(this, pop), Select1.RandomLinearRank.call(this, pop)];
}, "Sequential": function (pop) {
return [Select1.Sequential.call(this, pop), Select1.Sequential.call(this, pop)];
}, "FittestRandom": function (pop) {
return [Select1.Fittest.call(this, pop), Select1.Random.call(this, pop)];
}
};
function Genetic() {
// population
this.fitness = null;
this.seed = null;
this.mutate = null;
this.crossover = null;
this.select1 = null;
this.select2 = null;
this.optimize = null;
this.generation = null;
this.notification = null;
this.configuration = {
"size": 250
, "crossover": 0.9
, "mutation": 0.2
, "iterations": 100
, "fittestAlwaysSurvives": true
, "maxResults": 100
, "skip": 0
};
this.userData = {};
this.internalGenState = {};
this.entities = [];
this.start = function() {
var i;
var self = this;
function mutateOrNot(entity) {
// applies mutation based on mutation probability
return Math.random() <= self.configuration.mutation && self.mutate ? self.mutate(Clone(entity)) : entity;
}
// seed the population
for (i=0;i<this.configuration.size;++i) {
this.entities.push(Clone(this.seed()));
}
for (i=0;i<this.configuration.iterations;++i) {
// reset for each generation
this.internalGenState = {};
// score and sort
var pop = this.entities
.map(function (entity) {
return {"fitness": self.fitness(entity), "entity": entity };
})
.sort(function (a, b) {
return self.optimize(a.fitness, b.fitness) ? -1 : 1;
});
// generation notification
var mean = pop.reduce(function (a, b) { return a + b.fitness; }, 0)/pop.length;
var stdev = Math.sqrt(pop
.map(function (a) { return (a.fitness - mean) * (a.fitness - mean); })
.reduce(function (a, b) { return a+b; }, 0)/pop.length);
var stats = {
"maximum": pop[0].fitness
, "minimum": pop[pop.length-1].fitness
, "mean": mean
, "stdev": stdev
};
var r = this.generation ? this.generation(pop.slice(0, this.configuration["maxResults"]), i, stats) : true;
var isFinished = (typeof r != "undefined" && !r) || (i == this.configuration.iterations-1);
if (
this.notification
&& (isFinished || this.configuration["skip"] == 0 || i%this.configuration["skip"] == 0)
) {
this.sendNotification(pop.slice(0, this.configuration["maxResults"]), i, stats, isFinished);
}
if (isFinished)
break;
// crossover and mutate
var newPop = [];
if (this.configuration.fittestAlwaysSurvives) // lets the best solution fall through
newPop.push(pop[0].entity);
while (newPop.length < self.configuration.size) {
if (
this.crossover // if there is a crossover function
&& Math.random() <= this.configuration.crossover // base crossover on specified probability
&& newPop.length+1 < self.configuration.size // keeps us from going 1 over the max population size
) {
var parents = this.select2(pop);
var children = this.crossover(Clone(parents[0]), Clone(parents[1])).map(mutateOrNot);
newPop.push(children[0], children[1]);
} else {
newPop.push(mutateOrNot(self.select1(pop)));
}
}
this.entities = newPop;
}
}
this.sendNotification = function(pop, generation, stats, isFinished) {
var response = {
"pop": pop.map(Serialization.stringify)
, "generation": generation
, "stats": stats
, "isFinished": isFinished
};
this.notification(response.pop.map(Serialization.parse), response.generation, response.stats, response.isFinished);
};
}
Genetic.prototype.evolve = function(config, userData) {
var k;
for (k in config) {
this.configuration[k] = config[k];
}
for (k in userData) {
this.userData[k] = userData[k];
}
this.start();
}
return {
"create": function() {
return new Genetic();
}, "Select1": Select1
, "Select2": Select2
, "Optimize": Optimize
, "Clone": Clone
};
})();
// so we don't have to build to run in the browser
if (typeof module != "undefined") {
module.exports = Genetic;
}
},{}]},{},[1]);