UNPKG

@adrianperea/genie.js

Version:

A highly flexible, data-agnostic, and UI-independent Genetic Algorithm Library

280 lines (237 loc) 10.5 kB
"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;