UNPKG

genetic-search

Version:

Multiprocessing genetic algorithm implementation library

190 lines 5.62 kB
import { arrayBinaryOperation, createFilledArray } from "./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 */ export class DummyPhenomeCache { getReady(_) { return undefined; } get(_, defaultValue) { return defaultValue; } set(_, __) { return; } clear(_) { return; } export() { return {}; } import(_) { return; } } /** * A simple phenome cache implementation. * * This cache stores the constant phenome value for each genome. * * @category Cache * @category Strategies */ export class SimplePhenomeCache { constructor() { this.cache = new Map(); } get(genomeId, defaultValue) { return this.cache.has(genomeId) ? this.cache.get(genomeId) : defaultValue; } getReady(genomeId) { return this.cache.has(genomeId) ? this.get(genomeId) : undefined; } set(genomeId, phenome) { this.cache.set(genomeId, phenome); } clear(excludeGenomeIds) { const excludeIdsSet = new Set(excludeGenomeIds); for (const id of this.cache.keys()) { if (!excludeIdsSet.has(id)) { this.cache.delete(id); } } } export() { return Object.fromEntries(this.cache); } import(data) { this.cache.clear(); for (const [id, cacheItem] of Object.entries(data)) { this.cache.set(Number(id), cacheItem); } } } /** * 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 */ export class AveragePhenomeCache { constructor() { /** * 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(); } get(genomeId, defaultValue) { if (!this.cache.has(genomeId)) { return defaultValue; } const [row, count] = this.cache.get(genomeId); return row.map((x) => x / count); } getReady() { return undefined; } set(genomeId, phenome) { if (!this.cache.has(genomeId)) { this.cache.set(genomeId, [phenome, 1]); return; } const [row, count] = this.cache.get(genomeId); this.cache.set(genomeId, [row.map((x, i) => x + phenome[i]), count + 1]); } clear(excludeGenomeIds) { const excludeIdsSet = new Set(excludeGenomeIds); for (const id of this.cache.keys()) { if (!excludeIdsSet.has(id)) { this.cache.delete(id); } } } export() { return Object.fromEntries(this.cache); } import(data) { this.cache.clear(); for (const [id, cacheItem] of Object.entries(data)) { this.cache.set(Number(id), cacheItem); } } } /** * 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 */ export class WeightedAgeAveragePhenomeCache extends AveragePhenomeCache { /** * Constructs a new WeightedAgeAveragePhenomeCache. * @param weight The weight factor used for calculating the weighted average. */ constructor(weight) { super(); /** * The current average phenome row, or undefined if not yet calculated. */ this.averageRow = undefined; this.weight = weight; } set(genomeId, phenome) { super.set(genomeId, phenome); this.resetAverageRow(); } get(genomeId, defaultValue) { const row = super.get(genomeId, defaultValue); if (row === undefined) { return undefined; } if (!this.refreshAverageRow()) { return row; } const [, age] = this.cache.get(genomeId); const averageDiff = arrayBinaryOperation(row, this.averageRow, (lhs, rhs) => lhs - rhs); const weightedAverageDiff = averageDiff.map((x) => x * this.weight / age); return arrayBinaryOperation(row, weightedAverageDiff, (lhs, rhs) => lhs - rhs); } refreshAverageRow() { if (this.cache.size === 0) { this.resetAverageRow(); return false; } let weightedTotal = 0; const result = createFilledArray(this.getPhenomeCount(), 0); for (const phenome of this.cache.values()) { const [row, weight] = phenome; for (let i = 0; i < row.length; ++i) { result[i] += row[i]; } weightedTotal += weight; } this.averageRow = result.map((x) => x / weightedTotal); return true; } resetAverageRow() { this.averageRow = undefined; } getPhenomeCount() { return this.cache.values().next().value[0].length; } } //# sourceMappingURL=cache.js.map