@adrianperea/genie.js
Version:
A highly flexible, data-agnostic, and UI-independent Genetic Algorithm Library
280 lines (237 loc) • 10.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Simulation = void 0;
var _ga = require("../ga");
var _random = require("../utils/random");
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
var Simulation = /*#__PURE__*/function () {
function Simulation(_ref) {
var prototype = _ref.prototype,
data = _ref.data,
_ref$popSize = _ref.popSize,
popSize = _ref$popSize === void 0 ? 100 : _ref$popSize,
_ref$maxGenerations = _ref.maxGenerations,
maxGenerations = _ref$maxGenerations === void 0 ? 1000 : _ref$maxGenerations,
_ref$numParents = _ref.numParents,
numParents = _ref$numParents === void 0 ? popSize : _ref$numParents,
_ref$selection = _ref.selection,
selection = _ref$selection === void 0 ? _ga.Selection.rouletteWheel : _ref$selection,
_ref$crossover = _ref.crossover,
crossover = _ref$crossover === void 0 ? _ga.Crossover.onepoint : _ref$crossover,
_ref$mutationRate = _ref.mutationRate,
mutationRate = _ref$mutationRate === void 0 ? 0.01 : _ref$mutationRate,
_ref$optimizer = _ref.optimizer,
optimizer = _ref$optimizer === void 0 ? _ga.Optimizer.maximizer : _ref$optimizer,
_ref$elitism = _ref.elitism,
elitism = _ref$elitism === void 0 ? false : _ref$elitism,
_ref$onInit = _ref.onInit,
onInit = _ref$onInit === void 0 ? null : _ref$onInit,
_ref$onUpdate = _ref.onUpdate,
onUpdate = _ref$onUpdate === void 0 ? null : _ref$onUpdate,
_ref$onCalculateFitne = _ref.onCalculateFitness,
onCalculateFitness = _ref$onCalculateFitne === void 0 ? null : _ref$onCalculateFitne,
_ref$onFinish = _ref.onFinish,
onFinish = _ref$onFinish === void 0 ? null : _ref$onFinish;
_classCallCheck(this, Simulation);
if (prototype === undefined) {
throw new Error('Please provide an Individual as a prototype');
}
if (prototype.dna.length === 0) {
throw new Error('Please add a chromosome to your prototype');
}
this.prototype = prototype;
this.data = data;
this.popSize = popSize;
this.maxGenerations = maxGenerations;
this.numParents = numParents;
this.selection = selection;
this.crossover = crossover;
this.mutationRate = mutationRate;
this.optimizer = optimizer;
this.elitism = elitism;
this.population = Array(popSize).fill(null);
this.currentGeneration = 1;
this.finished = false;
this.updateFinished = false;
this.top = null;
this.averageFitness = 0;
this.history = [];
this.rafId = null;
this.onInit = onInit;
this.onUpdate = onUpdate;
this.onCalculateFitness = onCalculateFitness;
this.onFinish = onFinish;
this._init();
}
_createClass(Simulation, [{
key: "start",
value: function start() {
var _this = this;
this.rafId = setTimeout(function () {
return _this.loop();
}, 1 / 60);
}
}, {
key: "stop",
value: function stop() {
clearTimeout(this.rafId);
this.rafId = null;
}
}, {
key: "loop",
value: function loop() {
var _this2 = this;
if (!this.finished) {
if (!this.updateFinished) {
this._update();
}
this._calculateFitness();
this._evaluate();
if (!this.finished) {
this._generate();
}
this.rafId = setTimeout(function () {
return _this2.loop();
}, 1 / 60);
}
}
}, {
key: "_init",
value: function _init() {
var _this3 = this;
this.population = this.population.map(function () {
return _this3.prototype.fromTheLikenessOf();
});
this.init();
this.onInit && this.onInit(this._getState());
}
}, {
key: "_update",
value: function _update() {
this.updateFinished = this.update();
this.onUpdate && this.onUpdate(this._getState());
}
}, {
key: "_calculateFitness",
value: function _calculateFitness() {
var _this4 = this;
this.population.forEach(function (individual) {
return individual.fitness = _this4.calculateFitness(individual, _this4.data);
});
this.population = this.population.sort(this.optimizer);
this.top = this.population[0];
this.averageFitness = this._getAverageFitness();
this.onCalculateFitness && this.onCalculateFitness(this._getState());
}
}, {
key: "_evaluate",
value: function _evaluate() {
if (this.shouldFinish(this.top) || this.currentGeneration === this.maxGenerations) {
this.finished = true;
this.onFinish && this.onFinish(this._getState());
} else {
this._reset();
}
}
}, {
key: "_getAverageFitness",
value: function _getAverageFitness() {
return this.population.reduce(function (average, individual, _, population) {
return average + individual.fitness / population.length;
}, 0);
}
}, {
key: "_reset",
value: function _reset() {
this.reset();
this.updateFinished = false;
}
}, {
key: "_generate",
value: function _generate() {
var _this5 = this;
var elites = [];
if (this.elitism === true) {
elites = this.population.sort(function (a, b) {
return b.fitness - a.fitness;
}).slice(0, this.numParents);
}
var parents = _ga.Selection.selection(this.population, this.numParents, this.selection);
var children = Array(this.popSize - elites.length).fill(null).map(function () {
var parentOne = parents[(0, _random.randBetween)(0, parents.length)];
var parentTwo = parents[(0, _random.randBetween)(0, parents.length)];
var child = _ga.Mutate.mutate(_ga.Crossover.crossover(parentOne, parentTwo, _this5.crossover), _this5.mutationRate);
return child;
});
this.updateHistory();
this.currentGeneration += 1;
this.population = [].concat(_toConsumableArray(elites), _toConsumableArray(children));
}
}, {
key: "updateHistory",
value: function updateHistory() {
this.history = [].concat(_toConsumableArray(this.history), [{
generation: this.currentGeneration,
population: this.population,
averageFitness: this.averageFitness,
topFitness: this.top.fitness
}]);
}
}, {
key: "_getState",
value: function _getState() {
var userState = this.getState();
return _objectSpread(_objectSpread({}, userState), {}, {
population: this.population,
currentGeneration: this.currentGeneration,
top: this.top,
averageFitness: this.averageFitness,
maxGenerations: this.maxGenerations,
popSize: this.popSize,
history: this.history
});
} // Optional Override
}, {
key: "init",
value: function init() {} // Optional Override
}, {
key: "getState",
value: function getState() {
return {};
} // Optional Override
}, {
key: "update",
value: function update() {
return true;
} // Optional Override
}, {
key: "reset",
value: function reset() {} // Optional Override
}, {
key: "shouldFinish",
value: function shouldFinish(top) {
// Run until max generations are exhausted by default
return false;
}
}, {
key: "calculateFitness",
value: function calculateFitness(individual, data) {
throw new Error('method `calculateFitness` must be implemented');
}
}]);
return Simulation;
}();
exports.Simulation = Simulation;