genetic-search
Version:
Multiprocessing genetic algorithm implementation library
303 lines • 11.4 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WeightedAgeAveragePhenomeCache = exports.AveragePhenomeCache = exports.SimplePhenomeCache = exports.DummyPhenomeCache = void 0;
var utils_1 = require("./utils");
/**
* A dummy phenome cache implementation that does nothing.
*
* This class is used when the {@link GeneticSearch} is created without a
* phenome cache.
*
* @category Cache
* @category Strategies
*/
var DummyPhenomeCache = /** @class */ (function () {
function DummyPhenomeCache() {
}
DummyPhenomeCache.prototype.getReady = function (_) {
return undefined;
};
DummyPhenomeCache.prototype.get = function (_, defaultValue) {
return defaultValue;
};
DummyPhenomeCache.prototype.set = function (_, __) {
return;
};
DummyPhenomeCache.prototype.clear = function (_) {
return;
};
DummyPhenomeCache.prototype.export = function () {
return {};
};
DummyPhenomeCache.prototype.import = function (_) {
return;
};
return DummyPhenomeCache;
}());
exports.DummyPhenomeCache = DummyPhenomeCache;
/**
* A simple phenome cache implementation.
*
* This cache stores the constant phenome value for each genome.
*
* @category Cache
* @category Strategies
*/
var SimplePhenomeCache = /** @class */ (function () {
function SimplePhenomeCache() {
this.cache = new Map();
}
SimplePhenomeCache.prototype.get = function (genomeId, defaultValue) {
return this.cache.has(genomeId)
? this.cache.get(genomeId)
: defaultValue;
};
SimplePhenomeCache.prototype.getReady = function (genomeId) {
return this.cache.has(genomeId) ? this.get(genomeId) : undefined;
};
SimplePhenomeCache.prototype.set = function (genomeId, phenome) {
this.cache.set(genomeId, phenome);
};
SimplePhenomeCache.prototype.clear = function (excludeGenomeIds) {
var e_1, _a;
var excludeIdsSet = new Set(excludeGenomeIds);
try {
for (var _b = __values(this.cache.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
var id = _c.value;
if (!excludeIdsSet.has(id)) {
this.cache.delete(id);
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_1) throw e_1.error; }
}
};
SimplePhenomeCache.prototype.export = function () {
return Object.fromEntries(this.cache);
};
SimplePhenomeCache.prototype.import = function (data) {
var e_2, _a;
this.cache.clear();
try {
for (var _b = __values(Object.entries(data)), _c = _b.next(); !_c.done; _c = _b.next()) {
var _d = __read(_c.value, 2), id = _d[0], cacheItem = _d[1];
this.cache.set(Number(id), cacheItem);
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_2) throw e_2.error; }
}
};
return SimplePhenomeCache;
}());
exports.SimplePhenomeCache = SimplePhenomeCache;
/**
* A phenome cache implementation that stores the phenome for each genome as a
* weighted average of all phenome that have been set for that genome.
*
* @category Cache
* @category Strategies
*/
var AveragePhenomeCache = /** @class */ (function () {
function AveragePhenomeCache() {
/**
* A map of genome IDs to their respective phenome and the number of times they have been set.
*
* The key is the genome ID, and the value is an array with two elements. The first element is the
* current phenome for the genome, and the second element is the number of times the phenome have
* been set.
*/
this.cache = new Map();
}
AveragePhenomeCache.prototype.get = function (genomeId, defaultValue) {
if (!this.cache.has(genomeId)) {
return defaultValue;
}
var _a = __read(this.cache.get(genomeId), 2), row = _a[0], count = _a[1];
return row.map(function (x) { return x / count; });
};
AveragePhenomeCache.prototype.getReady = function () {
return undefined;
};
AveragePhenomeCache.prototype.set = function (genomeId, phenome) {
if (!this.cache.has(genomeId)) {
this.cache.set(genomeId, [phenome, 1]);
return;
}
var _a = __read(this.cache.get(genomeId), 2), row = _a[0], count = _a[1];
this.cache.set(genomeId, [row.map(function (x, i) { return x + phenome[i]; }), count + 1]);
};
AveragePhenomeCache.prototype.clear = function (excludeGenomeIds) {
var e_3, _a;
var excludeIdsSet = new Set(excludeGenomeIds);
try {
for (var _b = __values(this.cache.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
var id = _c.value;
if (!excludeIdsSet.has(id)) {
this.cache.delete(id);
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_3) throw e_3.error; }
}
};
AveragePhenomeCache.prototype.export = function () {
return Object.fromEntries(this.cache);
};
AveragePhenomeCache.prototype.import = function (data) {
var e_4, _a;
this.cache.clear();
try {
for (var _b = __values(Object.entries(data)), _c = _b.next(); !_c.done; _c = _b.next()) {
var _d = __read(_c.value, 2), id = _d[0], cacheItem = _d[1];
this.cache.set(Number(id), cacheItem);
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_4) throw e_4.error; }
}
};
return AveragePhenomeCache;
}());
exports.AveragePhenomeCache = AveragePhenomeCache;
/**
* A phenome cache implementation that stores the phenome for each genome as a
* weighted average of all phenome that have been set for that genome.
*
* The closer the genome age is to 0, the closer the phenome are to the average phenome of the population,
* which helps to combat outliers for new genomes.
*
* @category Cache
* @category Strategies
*/
var WeightedAgeAveragePhenomeCache = /** @class */ (function (_super) {
__extends(WeightedAgeAveragePhenomeCache, _super);
/**
* Constructs a new WeightedAgeAveragePhenomeCache.
* @param weight The weight factor used for calculating the weighted average.
*/
function WeightedAgeAveragePhenomeCache(weight) {
var _this = _super.call(this) || this;
/**
* The current average phenome row, or undefined if not yet calculated.
*/
_this.averageRow = undefined;
_this.weight = weight;
return _this;
}
WeightedAgeAveragePhenomeCache.prototype.set = function (genomeId, phenome) {
_super.prototype.set.call(this, genomeId, phenome);
this.resetAverageRow();
};
WeightedAgeAveragePhenomeCache.prototype.get = function (genomeId, defaultValue) {
var _this = this;
var row = _super.prototype.get.call(this, genomeId, defaultValue);
if (row === undefined) {
return undefined;
}
if (!this.refreshAverageRow()) {
return row;
}
var _a = __read(this.cache.get(genomeId), 2), age = _a[1];
var averageDiff = (0, utils_1.arrayBinaryOperation)(row, this.averageRow, function (lhs, rhs) { return lhs - rhs; });
var weightedAverageDiff = averageDiff.map(function (x) { return x * _this.weight / age; });
return (0, utils_1.arrayBinaryOperation)(row, weightedAverageDiff, function (lhs, rhs) { return lhs - rhs; });
};
WeightedAgeAveragePhenomeCache.prototype.refreshAverageRow = function () {
var e_5, _a;
if (this.cache.size === 0) {
this.resetAverageRow();
return false;
}
var weightedTotal = 0;
var result = (0, utils_1.createFilledArray)(this.getPhenomeCount(), 0);
try {
for (var _b = __values(this.cache.values()), _c = _b.next(); !_c.done; _c = _b.next()) {
var phenome = _c.value;
var _d = __read(phenome, 2), row = _d[0], weight = _d[1];
for (var i = 0; i < row.length; ++i) {
result[i] += row[i];
}
weightedTotal += weight;
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_5) throw e_5.error; }
}
this.averageRow = result.map(function (x) { return x / weightedTotal; });
return true;
};
WeightedAgeAveragePhenomeCache.prototype.resetAverageRow = function () {
this.averageRow = undefined;
};
WeightedAgeAveragePhenomeCache.prototype.getPhenomeCount = function () {
return this.cache.values().next().value[0].length;
};
return WeightedAgeAveragePhenomeCache;
}(AveragePhenomeCache));
exports.WeightedAgeAveragePhenomeCache = WeightedAgeAveragePhenomeCache;
//# sourceMappingURL=cache.js.map