genetic-search
Version:
Multiprocessing genetic algorithm implementation library
922 lines (921 loc) • 27.6 kB
TypeScript
/**
* The origin of a genome, either from crossover, mutation or initial.
*
* @category Genome
* @category Statistics
*/
export type GenomeOrigin = "crossover" | "mutation" | "initial";
/**
* Represents the statistics of a genome.
*
* @category Genome
* @category Statistics
*/
export type GenomeStats = {
/**
* The age of the genome.
*/
age: number;
/**
* The fitness score of the genome.
*/
fitness: number;
/**
* The phenome associated with the genome.
*/
phenome: PhenomeRow;
/**
* The origin of the genome, indicating how it was created.
*/
origin: GenomeOrigin;
/**
* The number of times the genome was created through crossover and mutation.
*/
originCounters: {
crossover: number;
mutation: number;
};
/**
* The IDs of the parents of the genome.
*/
parentIds: number[];
};
/**
* The base interface for a genome.
*
* A genome is a candidate solution in a genetic search.
*
* @category Genome
* @category Genetic Algorithm Components
*/
export type BaseGenome = {
/**
* The unique identifier of the genome.
*/
id: number;
/**
* The statistics of the genome, which is automatically generated
* when the genome participates in the genetic algorithm.
*/
stats?: GenomeStats;
};
/**
* Represents a population of genomes in a genetic algorithm.
*
* @template TGenome The specific type of genome within the population.
*
* @category Genome
* @category Genetic Algorithm Components
*/
export type Population<TGenome extends BaseGenome> = TGenome[];
/**
* Represents a row of phenome associated with a genome.
*
* Each number in the array corresponds to a specific metric value
* calculated for the genome.
*
* @category Genome
* @category Genetic Algorithm Components
*/
export type PhenomeRow = number[];
/**
* Represents a column of fitness scores for a generation.
*
* Each number in the array represents the fitness score of a genome
* within the generation.
*
* @category Genome
* @category Genetic Algorithm Components
*/
export type GenerationFitnessColumn = number[];
/**
* Represents a matrix of phenome for a generation of genomes.
*
* Each row in the matrix corresponds to a PhenomeRow, storing
* the phenome for a single genome.
*
* @category Genome
* @category Genetic Algorithm Components
*/
export type GenerationPhenomeMatrix = PhenomeRow[];
/**
* Represents a genome that has been evaluated.
*
* @template TGenome The specific type of genome being evaluated.
*
* @category Genome
* @category Genetic Algorithm Components
*/
export type EvaluatedGenome<TGenome extends BaseGenome> = {
/**
* The genome being evaluated.
*/
genome: TGenome;
/**
* The fitness score of the genome.
*/
fitness: number;
/**
* The phenome of the genome.
*/
phenome: PhenomeRow;
};
/**
* A callback function that is called before each generation.
*
* Used in configuration type {@link GeneticSearchFitConfig}.
*
* @param generation The current generation number.
*
* @category Genetic Algorithm Components
* @category Genetic Algorithm Config
*/
export type GenerationBeforeCallback = (generation: number) => void;
/**
* A callback function that is called after each generation.
*
* @param generation The current generation number.
* @param scores The fitness scores of the current generation.
*
* @category Genetic Algorithm Components
* @category Genetic Algorithm Config
*/
export type GenerationAfterCallback = (generation: number, scores: GenerationFitnessColumn) => void;
/**
* A function that calculates the phenome for a genome.
*
* This function is called for each genome in the population by {@link PhenomeStrategyInterface}.
*
* Used in configuration type {@link PhenomeStrategyConfig}.
*
* @template TTaskConfig The type of configuration required to execute the task.
*
* @param data The configuration required to execute the task.
*
* @returns A promise that resolves to the phenome of the genome.
*
* @category Genetic Algorithm Components
* @category Strategies Config
*/
export type CalcPhenomeTask<TTaskConfig> = (data: TTaskConfig) => Promise<PhenomeRow>;
/**
* The main configuration for {@link GeneticSearch}.
*
* @category Genetic Algorithm Config
*/
export interface GeneticSearchConfig {
/**
* The size of the population of genomes.
*/
populationSize: number;
/**
* The size of the initial population of genomes.
*/
startPopulationSize?: number;
/**
* The rate of survival for the genomes in the population.
*/
survivalRate: number;
/**
* The rate of crossover for the difference between the total population and the survivors.
*/
crossoverRate: number;
}
/**
* The configuration for running a genetic search algorithm.
*
* Used in {@link GeneticSearchInterface}.
*
* @template TGenome The type of genome objects in the population.
*
* @category Genetic Algorithm Config
*/
export type GeneticSearchFitConfig<TGenome extends BaseGenome> = {
/**
* The number of generations to run the algorithm for.
*
* Defaults to Infinity.
*/
generationsCount?: number;
/**
* A callback function that is called before each generation.
*
* @param generation The current generation number.
*/
beforeStep?: GenerationBeforeCallback;
/**
* A callback function that is called after each generation.
*
* @param generation The current generation number.
* @param scores The fitness scores of the current generation.
*/
afterStep?: GenerationAfterCallback;
/**
* A callback function that is called after each generation and can cause the algorithm to stop.
*
* @param scores The fitness scores of the current generation.
* @returns Whether to stop the algorithm.
*/
stopCondition?: (scores: GenerationFitnessColumn) => boolean;
/**
* The scheduler to use to schedule the algorithm.
*/
scheduler?: SchedulerInterface<TGenome>;
};
/**
* The main configuration for {@link ComposedGeneticSearch}.
*
* The algorithm is configured by providing a separate configuration for
* the eliminators and the final population.
*
* @category Genetic Algorithm Config
*/
export type ComposedGeneticSearchConfig = {
/**
* The configuration for the eliminator genetic algorithms.
*
* The eliminators are separate genetic algorithms that run in parallel, each with their own population
* and independent selection process.
*
* The winners of each are added to the population of the final population.
*/
eliminators: GeneticSearchConfig;
/**
* The configuration for the final genetic algorithm.
*
* The final population is used as the final result of the algorithm.
*/
final: GeneticSearchConfig;
};
/**
* The configuration for a {@link BaseMutationStrategy} that mutates a genome with a certain probability.
*
* @category Strategies Config
*/
export type BaseMutationStrategyConfig = {
/**
* The probability of a genome being mutated.
*
* Defaults to 1.
*/
probability: number;
};
/**
* The configuration for a {@link BasePhenomeStrategy}.
*
* A phenome strategy is a component of a genetic search algorithm that
* calculates the phenome of a population of genomes.
*
* @template TTaskConfig The type of configuration required to execute the task.
*
* @category Strategies Config
*/
export type PhenomeStrategyConfig<TTaskConfig> = {
/**
* The function to call to calculate the phenome for a genome.
*
* This function is called for each genome in the population.
*/
task: CalcPhenomeTask<TTaskConfig>;
/**
* A callback function that is called after the phenome for a genome has been calculated.
*
* @param result The phenome of the genome.
* @param input The configuration required to execute the task.
*/
onTaskResult?: (result: PhenomeRow, input: TTaskConfig) => void;
};
/**
* The configuration for {@link GeneticSearch} and {@link ComposedGeneticSearch} strategies.
*
* This configuration is used to define the behavior of a genetic search algorithm.
*
* @template TGenome The type of genome objects in the population.
*
* @category Genetic Algorithm Config
*/
export type GeneticSearchStrategyConfig<TGenome extends BaseGenome> = {
/**
* The strategy to generate the initial population.
*/
populate: PopulateStrategyInterface<TGenome>;
/**
* The strategy to calculate the phenome of the population.
*/
phenome: PhenomeStrategyInterface<TGenome>;
/**
* The strategy to calculate the fitness of the population.
*/
fitness: FitnessStrategyInterface;
/**
* The strategy to mutate a genome.
*/
mutation: MutationStrategyInterface<TGenome>;
/**
* The strategy to cross over two genomes.
*/
crossover: CrossoverStrategyInterface<TGenome>;
/**
* The strategy to sort the population.
*/
sorting: SortStrategyInterface<TGenome>;
/**
* The strategy to select genomes for crossover and mutation.
*/
selection: SelectionStrategyInterface<TGenome>;
/**
* The cache to store the phenome of the population.
*/
cache: PhenomeCacheInterface;
};
/**
* The configuration for a {@link ReferenceLossFitnessStrategy}.
*
* This configuration is used to define the reference row and the weights for a reference loss fitness strategy.
*
* @category Strategies Config
*/
export type GeneticSearchReferenceConfig = {
/**
* The reference row of phenome used to calculate the fitness of the population.
*/
readonly reference: PhenomeRow;
/**
* The weights used to calculate the fitness of the population.
*/
readonly weights: PhenomeRow;
};
/**
* A summary of the statistics of a population of genomes.
*
* This object contains the count of genomes in the population,
* the best and second best values, the mean, median, and worst values.
*
* @category Statistics
*/
export type StatSummary = {
/**
* The count of genomes in the population.
*/
readonly count: number;
/**
* The best value in the population.
*/
readonly best: number;
/**
* The second best value in the population.
*/
readonly second: number;
/**
* The mean value in the population.
*/
readonly mean: number;
/**
* The median value in the population.
*/
readonly median: number;
/**
* The worst value in the population.
*/
readonly worst: number;
};
/**
* Represents a summary of range statistics.
*
* This type captures the minimum, mean, and maximum values
* for a set of numerical data.
*
* @category Statistics
*/
export type RangeStatSummary = {
/**
* The minimum value in the dataset.
*/
readonly min: number;
/**
* The mean (average) value in the dataset.
*/
readonly mean: number;
/**
* The maximum value in the dataset.
*/
readonly max: number;
};
/**
* A summary of the statistics of a population of genomes, grouped by origin.
*
* This object contains the count of genomes in the population,
* the best and second best values, the mean, median, and worst values.
* It is grouped by origin into three categories: initial, crossover, and mutation.
*
* @category Statistics
*/
export type GroupedStatSummary = {
/**
* The summary of the initial population.
*/
readonly initial: StatSummary;
/**
* The summary of the crossover population.
*/
readonly crossover: StatSummary;
/**
* The summary of the mutation population.
*/
readonly mutation: StatSummary;
};
/**
* A summary of the statistics for a population of genomes.
*
* @category Statistics
*/
export type PopulationSummary = {
/**
* A summary of fitness statistics for the population.
*/
readonly fitnessSummary: StatSummary;
/**
* A grouped summary of fitness statistics based on genome origin.
*/
readonly groupedFitnessSummary: GroupedStatSummary;
/**
* A summary of age statistics for the population.
*/
readonly ageSummary: RangeStatSummary;
/**
* The counter indicating the number of generations without significant improvement (bestGenome not changing).
*/
readonly stagnationCounter: number;
};
/**
* The input for a {@link SchedulerAction}.
*
* @template TGenome The type of genome objects in the population.
* @template TConfig The type of configuration object of macro parameters,
* which the scheduler will be able to manipulate.
*
* @category Scheduler
*/
export type SchedulerActionInput<TGenome extends BaseGenome, TConfig> = {
/**
* The genetic search algorithm.
*/
runner: GeneticSearchInterface<TGenome>;
/**
* The current sorted population with stats.
*/
evaluatedPopulation: EvaluatedGenome<TGenome>[];
/**
* The manager for the evaluated population.
*/
evaluatedPopulationManager: EvaluatedPopulationManagerInterface<TGenome>;
/**
* The history of population summaries.
*/
history: PopulationSummary[];
/**
* The configuration for the genetic search algorithm.
*/
config: TConfig;
/**
* The logger function.
*
* @param message The message to log.
*/
logger: (message: string) => void;
};
/**
* A single action to be executed by the {@link Scheduler}.
*
* @template TGenome The type of genome objects in the population.
* @template TConfig The type of configuration object of macro parameters,
* which the scheduler will be able to manipulate.
*
* @param input The input data for the action.
*
* @category Scheduler
*/
export type SchedulerAction<TGenome extends BaseGenome, TConfig> = (input: SchedulerActionInput<TGenome, TConfig>) => void;
/**
* The configuration for the {@link Scheduler}.
*
* @template TGenome The type of genome objects in the population.
* @template TConfig The type of configuration object of macro parameters,
* which the scheduler will be able to manipulate.
*
* @category Scheduler
*/
export type SchedulerConfig<TGenome extends BaseGenome, TConfig> = {
/**
* The runner for the genetic search algorithm.
*/
runner: GeneticSearchInterface<TGenome>;
/**
* The configuration object of macro parameters, which the scheduler will be able to manipulate.
*/
config: TConfig;
/**
* The actions for the scheduler.
*/
actions: SchedulerAction<TGenome, TConfig>[];
/**
* The maximum length of the generations history summaries array.
*/
maxHistoryLength: number;
};
/**
* An interface for a population generator.
*
* Used in {@link GeneticSearchStrategyConfig}.
*
* @template TGenome The type of genome objects in the population.
*
* @category Strategies
* @category Populate
*/
export interface PopulateStrategyInterface<TGenome extends BaseGenome> {
/**
* Generate a population of the given size.
*
* @param size The size of the population to generate.
* @param idGenerator The ID generator to use for generating IDs of the genomes.
*/
populate(size: number, idGenerator: IdGeneratorInterface<TGenome>): Population<TGenome>;
}
/**
* An interface for a mutation strategy.
*
* Used in {@link GeneticSearchStrategyConfig}.
*
* @template TGenome The type of genome objects in the population.
*
* @category Strategies
* @category Mutation
*/
export interface MutationStrategyInterface<TGenome extends BaseGenome> {
/**
* Mutate a genome.
*
* @param genome The genome to mutate.
* @param newGenomeId The ID to assign to the new genome.
*/
mutate(genome: TGenome, newGenomeId: number): TGenome;
}
/**
* An interface for a crossover strategy.
*
* Used in {@link GeneticSearchStrategyConfig}.
*
* @template TGenome The type of genome objects in the population.
*
* @category Strategies
* @category Crossover
*/
export interface CrossoverStrategyInterface<TGenome extends BaseGenome> {
/**
* Cross two genomes.
*
* @param parents The parents to cross.
* @param newGenomeId The ID to assign to the new genome.
*/
cross(parents: TGenome[], newGenomeId: number): TGenome;
}
/**
* An interface for a phenome strategy.
*
* Used in {@link GeneticSearchStrategyConfig}.
*
* @template TGenome The type of genome objects in the population.
*
* @category Strategies
* @category Phenome
*/
export interface PhenomeStrategyInterface<TGenome extends BaseGenome> {
/**
* Collect phenome for a population.
*
* @param population The population to collect phenome for.
* @param cache The cache to use for storing the phenome.
*/
collect(population: Population<TGenome>, cache: PhenomeCacheInterface): Promise<GenerationPhenomeMatrix>;
}
/**
* An interface for a fitness strategy.
*
* Used in {@link GeneticSearchStrategyConfig}.
*
* @category Strategies
* @category Fitness
*/
export interface FitnessStrategyInterface {
/**
* Score a population.
*
* @param results The results of the phenome collection.
*/
score(results: GenerationPhenomeMatrix): GenerationFitnessColumn;
}
/**
* An interface for a sorting strategy.
*
* Used in {@link GeneticSearchStrategyConfig}.
*
* @template TGenome The type of genome objects in the population.
*
* @category Strategies
* @category Sorting
*/
export interface SortStrategyInterface<TGenome extends BaseGenome> {
/**
* Sorts a given iterable of genomes, fitness scores, and phenome rows.
*
* @param input The array of genomes extended with fitness scores and phenome.
* @returns An array of sorted tuples of genomes, fitness scores, and phenome rows.
*/
sort(input: Array<EvaluatedGenome<TGenome>>): Array<EvaluatedGenome<TGenome>>;
}
/**
* An interface for selection strategy.
*
* Used in {@link GeneticSearchStrategyConfig}.
*
* @template TGenome The type of genome objects in the population.
*
* @category Strategies
* @category Selection
*/
export interface SelectionStrategyInterface<TGenome extends BaseGenome> {
/**
* Selects parents pairs for crossover.
*
* @param input The population extended with fitness scores and phenome to select parents from.
* @param count The number of parents to select.
* @returns An array of parent pairs.
*/
selectForCrossover(input: Array<EvaluatedGenome<TGenome>>, count: number): Array<TGenome[]>;
/**
* Selects parents for mutation.
*
* @param input The population extended with fitness scores and phenome to select parents from.
* @param count The number of parents to select.
* @returns An array of parents.
*/
selectForMutation(input: Array<EvaluatedGenome<TGenome>>, count: number): TGenome[];
}
/**
* A genetic search algorithm interface.
*
* @template TGenome The type of genome objects in the population.
*
* @category Genetic Algorithm
*/
export interface GeneticSearchInterface<TGenome extends BaseGenome> {
/**
* Current best genome in the population.
*/
readonly bestGenome: TGenome;
/**
* Partition sizes of the population.
*
* The first element is the size of the elite population, the second element is the size of the
* crossover population, and the third element is the size of the mutation population.
*/
readonly partitions: [number, number, number];
/**
* Phenome cache.
*/
readonly cache: PhenomeCacheInterface;
/**
* Current generation number.
*/
readonly generation: number;
/**
* Current population.
*/
population: Population<TGenome>;
/**
* Sets the current population.
*
* @param population new population.
* @param resetIdGenerator Whether to reset the ID generator.
*/
setPopulation(population: Population<TGenome>, resetIdGenerator: boolean): void;
/**
* Refreshes the population.
*/
refreshPopulation(): void;
/**
* Gets the population summary.
*
* @param roundPrecision The precision to round the summary to.
* @returns The population summary.
*/
getPopulationSummary(roundPrecision?: number): PopulationSummary;
/**
* Runs a single step of the genetic search algorithm.
*
* You need to call `clearCache()` after calling this method.
*
* @param scheduler The scheduler.
* @returns The fitness of the best genome in the population.
*/
fitStep(scheduler?: SchedulerInterface<TGenome>): Promise<GenerationFitnessColumn>;
/**
* Clears the cache.
*/
clearCache(): void;
/**
* Runs the genetic search algorithm.
*
* @param config The configuration.
*/
fit(config: GeneticSearchFitConfig<TGenome>): Promise<void>;
}
/**
* Interface for generating unique identifiers for genomes.
*
* @template TGenome The type of genome objects.
*
* @category Utils
*/
export interface IdGeneratorInterface<TGenome extends BaseGenome> {
/**
* Generates the next unique identifier.
*
* @returns The next unique identifier as a number.
*/
nextId(): number;
/**
* Resets the ID generator based on the provided population.
*
* @param population The population of genomes to reset the ID generator with.
*/
reset(population: TGenome[]): void;
}
/**
* Interface for a cache of phenome associated with genomes.
*
* This cache is used by the genetic search algorithm to store and retrieve
* the phenome of genomes.
*
* Used in {@link GeneticSearchStrategyConfig}.
*
* @remarks
* The cache is used to store the phenome of genomes, which are used to calculate
* the fitness of the population.
*
* @category Cache
* @category Strategies
*/
export interface PhenomeCacheInterface {
/**
* Gets the phenome of a genome, or undefined if the genome is not ready.
*
* @param genomeId The ID of the genome.
* @returns The phenome of the genome, or undefined if the genome is not ready.
*/
getReady(genomeId: number): PhenomeRow | undefined;
/**
* Gets the phenome of a genome.
*
* @param genomeId The ID of the genome.
* @param defaultValue The default value to return if the genome is not found.
* @returns The phenome of the genome, or the default value if the genome phenome is not found.
*/
get(genomeId: number, defaultValue?: PhenomeRow): PhenomeRow | undefined;
/**
* Sets the phenome of a genome.
*
* @param genomeId The ID of the genome.
* @param phenome The phenome of the genome.
*/
set(genomeId: number, phenome: PhenomeRow): void;
/**
* Clears the cache, excluding the specified genome IDs.
*
* @param excludeGenomeIds The IDs of the genomes to exclude from the clear operation.
*/
clear(excludeGenomeIds: number[]): void;
/**
* Exports the cache as a record of genome IDs to phenome.
*
* @returns The cache as a record of genome IDs to phenome.
*/
export(): Record<number, unknown>;
/**
* Imports the cache from a record of genome IDs to phenome.
*
* @param data The cache as a record of genome IDs to phenome.
*/
import(data: Record<number, unknown>): void;
}
/**
* An interface for a genome stats manager.
*
* A genome stats manager is used to manage the statistics of a population of genomes.
*
* @category Statistics
*/
export interface GenomeStatsManagerInterface<TGenome extends BaseGenome> {
/**
* Initializes the genome stats manager with the given population and origin.
*
* @param population The population to initialize the manager with.
* @param origin The origin of the population.
*/
init(population: Population<TGenome>, origin: GenomeOrigin): void;
/**
* Initializes the statistics of a genome.
*
* @param genome The genome to initialize.
* @param origin The origin of the genome.
* @param parents The parents of the genome.
* @returns The initialized genome statistics.
*/
initItem(genome: BaseGenome, origin: GenomeOrigin, parents?: BaseGenome[]): GenomeStats;
/**
* Updates the genome stats manager with the given population, phenome matrix, and fitness column.
*
* @param population The population to update the manager with.
* @param phenomeMatrix The phenome matrix of the population.
* @param fitnessColumn The fitness column of the population.
*/
update(population: Population<TGenome>, phenomeMatrix: GenerationPhenomeMatrix, fitnessColumn: GenerationFitnessColumn): void;
}
/**
* Interface for managing population summaries in a genetic algorithm.
*
* @template TGenome The type of genome objects in the population.
*
* @category Statistics
*/
export interface PopulationSummaryManagerInterface<TGenome extends BaseGenome> {
/**
* Retrieves the current population summary.
*
* @returns The population summary.
*/
get(): PopulationSummary;
/**
* Retrieves the population summary with rounded statistics.
*
* @param precision The number of decimal places to round to.
* @returns The rounded population summary.
*/
getRounded(precision: number): PopulationSummary;
/**
* Updates the population summary based on the provided sorted population.
*
* @param sortedPopulation The population sorted by fitness score.
*/
update(sortedPopulation: Population<TGenome>): void;
}
/**
* Interface for a scheduler.
*
* This interface defines the structure and behavior of a scheduler,
* which is responsible for executing scheduled tasks or operations.
*
* @category Scheduler
*/
export interface SchedulerInterface<TGenome extends BaseGenome> {
/**
* An array of log messages generated by the scheduler.
*/
readonly logs: string[];
/**
* Executes a single step or iteration in the scheduler.
*/
step(evaluatedPopulation: EvaluatedGenome<TGenome>[]): void;
}
/**
* Interface for managing arrays.
*
* @template T The type of objects in the array.
*/
export interface ArrayManagerInterface<T> {
/**
* Updates objects in the array based on a filter condition.
*
* @param filter - A function to determine which objects to update.
* @param update - A function to apply to each object that matches the filter.
*
* @returns The updated items.
*/
update(filter: (genome: T) => boolean, update: (genome: T) => void): T[];
/**
* Removes objects from the array based on a filter condition, with optional sorting and count limit.
*
* @param filter - A function to determine which objects to remove.
* @param maxCount - An optional maximum number of objects to remove.
* @param order - An optional sorting order ('asc' or 'desc').
*
* @returns The removed items.
*/
remove(filter: (genome: T) => boolean, maxCount?: number, order?: 'asc' | 'desc'): T[];
}
/**
* Interface for managing evaluated populations.
*/
export interface EvaluatedPopulationManagerInterface<TGenome extends BaseGenome> extends ArrayManagerInterface<EvaluatedGenome<TGenome>> {
}