UNPKG

genetic-search

Version:

Multiprocessing genetic algorithm implementation library

530 lines 17.8 kB
"use strict"; 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; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.fullCopyObject = exports.ArrayManager = exports.IdGenerator = void 0; exports.round = round; exports.createFilledArray = createFilledArray; exports.arraySum = arraySum; exports.arrayMean = arrayMean; exports.arrayMedian = arrayMedian; exports.arrayBinaryOperation = arrayBinaryOperation; exports.getRandomArrayItem = getRandomArrayItem; exports.normalizePhenomeRow = normalizePhenomeRow; exports.normalizePhenomeMatrixColumns = normalizePhenomeMatrixColumns; exports.normalizePhenomeMatrix = normalizePhenomeMatrix; exports.createEmptyStatSummary = createEmptyStatSummary; exports.createEmptyGroupedStatSummary = createEmptyGroupedStatSummary; exports.createEmptyRangeStatSummary = createEmptyRangeStatSummary; exports.calcStatSummary = calcStatSummary; exports.calcRangeStatSummary = calcRangeStatSummary; exports.roundStatSummary = roundStatSummary; exports.roundGroupedStatSummary = roundGroupedStatSummary; exports.roundRangeStatSummary = roundRangeStatSummary; exports.createEvaluatedPopulation = createEvaluatedPopulation; exports.extractEvaluatedPopulation = extractEvaluatedPopulation; var itertools_1 = require("./itertools"); /** * Generates unique identifiers for genomes. * * @template TGenome The type of genome objects in the population. * * @category Utils */ var IdGenerator = /** @class */ (function () { function IdGenerator() { this.id = 1; } IdGenerator.prototype.nextId = function () { return this.id++; }; IdGenerator.prototype.reset = function (population) { this.id = population.reduce(function (max, genome) { return Math.max(max, genome.id); }, 0) + 1; }; return IdGenerator; }()); exports.IdGenerator = IdGenerator; /** * Manages an array of `T` objects. * * @template T The type of the objects to manage. */ var ArrayManager = /** @class */ (function () { /** * Creates a new `ArrayManager` from an array of `T` objects. * * @param data The array of `T` objects to manage. */ function ArrayManager(data) { this._data = data; } /** * Updates elements of the managed array that match the given filter. * * @param filter A function that returns true if the element should be updated. * @param update A function that updates the element. * * @returns The updated items. */ ArrayManager.prototype.update = function (filter, update) { var e_1, _a; var updated = []; try { for (var _b = __values(this._data), _c = _b.next(); !_c.done; _c = _b.next()) { var genome = _c.value; if (filter(genome)) { update(genome); updated.push(genome); } } } 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; } } return updated; }; /** * Removes elements of the managed array that match the given filter and optionally sorts the rest of the array. * * @param filter A function that returns true if the element should be removed. * @param maxCount The maximum number of elements to remove. * @param order The order to sort the remaining elements. * * @returns The removed items. */ ArrayManager.prototype.remove = function (filter, maxCount, order) { var e_2, _a; if (maxCount === void 0) { maxCount = Infinity; } if (order === void 0) { order = 'asc'; } var buf = __spreadArray([], __read(this._data), false); if (order === 'desc') { buf.reverse(); } this._data.length = 0; var removed = []; try { for (var buf_1 = __values(buf), buf_1_1 = buf_1.next(); !buf_1_1.done; buf_1_1 = buf_1.next()) { var genome = buf_1_1.value; if (filter(genome) && removed.length < maxCount) { removed.push(genome); } else { this._data.push(genome); } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (buf_1_1 && !buf_1_1.done && (_a = buf_1.return)) _a.call(buf_1); } finally { if (e_2) throw e_2.error; } } if (order === 'desc') { this._data.reverse(); } return removed; }; return ArrayManager; }()); exports.ArrayManager = ArrayManager; /** * Creates a deep copy of an object. * * @template T The type of the object to copy. * @param obj The object to copy. * @returns A deep copy of the object. * * @category Utils */ var fullCopyObject = function (obj) { return JSON.parse(JSON.stringify(obj)); }; exports.fullCopyObject = fullCopyObject; /** * Rounds a number to a given precision. * * @param value The number to round. * @param precision The precision to round to. * @returns The rounded number. * * @category Utils */ function round(value, precision) { return Number(value.toFixed(precision)); } /** * Generates an array of a given length, filled with a specified value. * * @template T The type of the values in the array. * @param length The length of the array to generate. * @param value The value to fill the array with. * @returns An array of the specified length, filled with the specified value. * * @category Utils */ function createFilledArray(length, value) { return Array.from({ length: length }, function () { return value; }); } /** * Calculates the sum of an array of numbers. * * @param input The array of numbers to sum. * @returns The sum of the input array. * * @category Utils */ function arraySum(input) { return input.reduce(function (acc, val) { return acc + val; }, 0); } /** * Calculates the mean of an array of numbers. * * @param input The array of numbers to calculate the mean of. * @returns The mean of the input array. * * @category Utils */ function arrayMean(input) { return arraySum(input) / input.length; } /** * Calculates the median of a sorted array of numbers. * * @param sortedInput The sorted array of numbers to find the median of. * @returns The median value of the input array. * * @category Utils */ function arrayMedian(sortedInput) { var middleIndex = Math.floor(sortedInput.length / 2); if (sortedInput.length % 2 !== 0) { return sortedInput[middleIndex]; } return (sortedInput[middleIndex - 1] + sortedInput[middleIndex]) / 2; } /** * Applies a binary operator to two arrays of the same length. * * @template T The type of the values in the arrays. * @param lhs The left-hand side array. * @param rhs The right-hand side array. * @param operator The binary operator to apply. * @returns A new array with the result of applying the operator to each pair of values. * * @category Utils */ function arrayBinaryOperation(lhs, rhs, operator) { var result = []; var len = Math.min(lhs.length, rhs.length); for (var i = 0; i < len; ++i) { result.push(operator(lhs[i], rhs[i])); } return result; } /** * Returns a random element from the input array. * * @template T The type of the values in the array. * @param input The array to select a random element from. * @returns A random element from the input array. * * @category Utils */ function getRandomArrayItem(input) { return input[Math.floor(Math.random() * input.length)]; } /** * Normalizes an array of numbers to the range from -1 to 1, where the `reference` value is mapped to 0. * * @param input The array of numbers to normalize. * @param reference The reference value to map to 0. * @returns The normalized array of numbers. * * @category Utils */ function normalizePhenomeRow(input, reference) { // Find the minimum and maximum values in the array var minVal = Math.min.apply(Math, __spreadArray([], __read(input), false)); var maxVal = Math.max.apply(Math, __spreadArray([], __read(input), false)); // Calculate the maximum distance relative to the reference var maxDistance = Math.max(Math.abs(maxVal - reference), Math.abs(minVal - reference)); var denominator = maxDistance || 1; return input.map(function (num) { // Normalize each number to the range from -1 to 1, where reference = 0 return (num - reference) / denominator; }); } /** * Normalizes the columns of a matrix of phenome to the range from -1 to 1, where the `reference` value is mapped to 0. * * @param input The matrix of phenome to normalize. * @param reference The reference value to map to 0. * @returns The normalized matrix of phenome. * * @category Utils */ function normalizePhenomeMatrixColumns(input, reference) { var result = (0, exports.fullCopyObject)(input); if (result.length === 0) { return result; } var _loop_1 = function (i) { var columnNormalized = normalizePhenomeRow(result.map(function (row) { return row[i]; }), reference[i]); for (var j = 0; j < result.length; j++) { result[j][i] = columnNormalized[j]; } }; for (var i = 0; i < result[0].length; i++) { _loop_1(i); } return result; } /** * Normalizes the columns of a matrix of phenome to the range from -1 to 1, where the `reference` value is mapped to 0. * * @param matrix The matrix of phenome to normalize. * @param reference The reference value to map to 0. * @param abs Whether to take the absolute value of the normalized values. * @returns The normalized matrix of phenome. * * @category Utils */ function normalizePhenomeMatrix(matrix, reference, abs) { if (abs === void 0) { abs = true; } var result = normalizePhenomeMatrixColumns(matrix, reference); if (abs) { return result.map(function (row) { return row.map(function (x) { return Math.abs(x); }); }); } return result; } /** * Creates an empty `StatSummary` object. * * A `StatSummary` object contains the count of genomes in the population, * as well as the best, second best, mean, median, and worst values. * This function initializes a `StatSummary` with all values set to zero. * * @returns An initialized `StatSummary` object with all fields set to zero. * * @category Utils */ function createEmptyStatSummary() { return { count: 0, best: 0, second: 0, mean: 0, median: 0, worst: 0, }; } /** * Creates an empty `GroupedStatSummary` object. * * A `GroupedStatSummary` object contains a summary of the statistics of a population of genomes, * grouped by origin into three categories: initial, crossover, and mutation. * This function initializes a `GroupedStatSummary` with all values set to zero. * * @returns An initialized `GroupedStatSummary` object with all fields set to zero. * * @category Utils */ function createEmptyGroupedStatSummary() { return { initial: createEmptyStatSummary(), crossover: createEmptyStatSummary(), mutation: createEmptyStatSummary(), }; } /** * Creates an empty `RangeStatSummary` object. * * A `RangeStatSummary` object contains the minimum, mean, and maximum values * for a set of numerical data. * This function initializes a `RangeStatSummary` with all values set to zero. * * @returns An initialized `RangeStatSummary` object with all fields set to zero. * * @category Utils */ function createEmptyRangeStatSummary() { return { min: 0, mean: 0, max: 0, }; } /** * Calculates a summary of the statistics for a sorted array of numbers. * * The summary includes the count of numbers in the array, * as well as the best, second best, mean, median, and worst values. * * @param sortedSource The sorted array of numbers. * @returns A summary of the statistics for the array of numbers. * * @category Utils */ function calcStatSummary(sortedSource) { var _a; if (sortedSource.length === 0) { return createEmptyStatSummary(); } return { count: sortedSource.length, best: sortedSource[0], second: (_a = sortedSource[1]) !== null && _a !== void 0 ? _a : 0, mean: arrayMean(sortedSource), median: arrayMedian(sortedSource), worst: sortedSource[sortedSource.length - 1], }; } /** * Calculates a summary of the statistics for a sorted array of numbers. * * The summary includes the minimum, mean, and maximum values * for a set of numerical data. * * @param source The array of numbers. * @returns A summary of the statistics for the array of numbers. * * @category Utils */ function calcRangeStatSummary(source) { if (source.length === 0) { return createEmptyRangeStatSummary(); } return { min: Math.min.apply(Math, __spreadArray([], __read(source), false)), mean: arrayMean(source), max: Math.max.apply(Math, __spreadArray([], __read(source), false)), }; } /** * Rounds the fields of a StatSummary object to a given precision. * * @param summary The StatSummary object to round. * @param precision The number of decimal places to round to. * @returns A new StatSummary object with rounded fields. * * @category Utils */ function roundStatSummary(summary, precision) { return { count: summary.count, best: round(summary.best, precision), second: round(summary.second, precision), mean: round(summary.mean, precision), median: round(summary.median, precision), worst: round(summary.worst, precision), }; } /** * Rounds the fields of a GroupedStatSummary object to a given precision. * * This function rounds the statistics in each category of the GroupedStatSummary * (initial, crossover, mutation) to the specified number of decimal places. * * @param summary The GroupedStatSummary object to round. * @param precision The number of decimal places to round to. * @returns A new GroupedStatSummary object with rounded fields. * * @category Utils */ function roundGroupedStatSummary(summary, precision) { return { initial: roundStatSummary(summary.initial, precision), crossover: roundStatSummary(summary.crossover, precision), mutation: roundStatSummary(summary.mutation, precision), }; } /** * Rounds the fields of a RangeStatSummary object to a given precision. * * This function rounds the minimum, mean, and maximum values of the RangeStatSummary * to the specified number of decimal places. * * @param summary The RangeStatSummary object to round. * @param precision The number of decimal places to round to. * @returns A new RangeStatSummary object with rounded fields. * * @category Utils */ function roundRangeStatSummary(summary, precision) { return { min: round(summary.min, precision), mean: round(summary.mean, precision), max: round(summary.max, precision), }; } /** * Creates an array of `EvaluatedGenome` objects from a population and its fitness and phenome. * * @param population The population of genomes. * @param fitnessColumn The fitness column of the population. * @param phenomeMatrix The phenome matrix of the population. * @returns An array of EvaluatedGenome objects. * * @category Utils */ function createEvaluatedPopulation(population, fitnessColumn, phenomeMatrix) { var zipped = __spreadArray([], __read((0, itertools_1.zip)(population, fitnessColumn, phenomeMatrix)), false); return zipped.map(function (_a) { var _b = __read(_a, 3), genome = _b[0], fitness = _b[1], phenome = _b[2]; return ({ genome: genome, fitness: fitness, phenome: phenome }); }); } /** * Extracts the population, fitness column, and phenome matrix from an array of * [[EvaluatedGenome]] objects. * * @param input The array of [[EvaluatedGenome]] objects. * @returns An array of three elements: the population, fitness column, and phenome matrix. * * @category Utils */ function extractEvaluatedPopulation(input) { return [ input.map(function (x) { return x.genome; }), input.map(function (x) { return x.fitness; }), input.map(function (x) { return x.phenome; }), ]; } //# sourceMappingURL=utils.js.map