tycho-solver
Version:
Evolutionary computation and optimization library
122 lines • 5.21 kB
JavaScript
/**
* Genetic Algorithm implementation
*/
import { GALoopOperator } from './components/GALoopOperator';
import { GAInitializationOperator } from './components/InitializationOperator';
import { GAEvaluationOperator } from './components/EvaluationOperator';
import { seededRandom } from '../../utils/rng';
export class GeneticAlgorithm {
population;
bestSolution;
bestFitness;
generation;
config;
fitnessFunction;
rng;
constructor(fitnessFunction, config) {
this.fitnessFunction = fitnessFunction;
this.config = config;
this.generation = 0;
// RNG: create seeded RNG if seed provided, or use provided rng
const seed = config.seed;
const rng = config.rng || seededRandom(seed);
this.rng = rng;
// Step 1: Always use InitializationOperator to create the population
const initializationOperator = config.initializationOperator || new GAInitializationOperator();
const initResult = initializationOperator.initialize({
populationSize: config.populationSize || 100,
individualFactory: config.individualFactory,
rng
});
// `initialize` may be async in other operators; GeneticAlgorithm constructor is synchronous
// so reject async initialization here to keep `this.population` a T[].
if (initResult && typeof initResult.then === 'function') {
throw new Error('Asynchronous initialization is not supported in GeneticAlgorithm constructor. Provide a synchronous InitializationOperator or use a factory that awaits initialization.');
}
this.population = initResult;
if (!Array.isArray(this.population) || this.population.length === 0) {
throw new Error('InitializationOperator produced an empty population. This is not allowed.');
}
// Step 2: Evaluation
const evaluationOperator = config.evaluationOperator || new GAEvaluationOperator(fitnessFunction);
this.bestSolution = this.population[0];
const initialEval = evaluationOperator.evaluate(this.bestSolution);
if (initialEval && typeof initialEval.then === 'function') {
throw new Error('Asynchronous evaluation is not supported in GeneticAlgorithm constructor. Provide a synchronous EvaluationOperator.');
}
this.bestFitness = initialEval;
for (const individual of this.population) {
const fitnessResult = evaluationOperator.evaluate(individual);
if (fitnessResult && typeof fitnessResult.then === 'function') {
throw new Error('Asynchronous evaluation is not supported in GeneticAlgorithm constructor. Provide a synchronous EvaluationOperator.');
}
const fitness = fitnessResult;
if (fitness > this.bestFitness) {
this.bestFitness = fitness;
this.bestSolution = individual;
}
}
}
async evolve(generations) {
const gens = generations || this.config.maxGenerations;
const eliteCount = this.config.eliteCount ?? 1;
const fitnessLimit = this.config.fitnessLimit;
const gaConfig = {
population: this.population,
fitnessFunction: this.fitnessFunction,
maxGenerations: gens,
eliteCount
};
if (this.rng) {
gaConfig.rng = this.rng;
}
if (fitnessLimit !== undefined) {
gaConfig.fitnessLimit = fitnessLimit;
}
if (this.config.initializationOperator) {
gaConfig.initializationOperator = this.config.initializationOperator;
}
if (this.config.evaluationOperator) {
gaConfig.evaluationOperator = this.config.evaluationOperator;
}
if (this.config.selectionOperator) {
gaConfig.selectionOperator = this.config.selectionOperator;
}
if (this.config.crossoverOperator) {
gaConfig.crossoverOperator = this.config.crossoverOperator;
}
if (this.config.mutationOperator) {
gaConfig.mutationOperator = this.config.mutationOperator;
}
if (this.config.replacementOperator) {
gaConfig.replacementOperator = this.config.replacementOperator;
}
if (this.config.elitismOperator) {
gaConfig.elitismOperator = this.config.elitismOperator;
}
if (this.config.terminationOperator) {
gaConfig.terminationOperator = this.config.terminationOperator;
}
const result = await GALoopOperator(gaConfig);
this.population = result.population;
this.bestSolution = result.bestSolution;
this.bestFitness = result.bestFitness;
this.generation = result.generation;
return this.bestSolution;
}
getBestSolution() {
return this.bestSolution;
}
getBestFitness() {
return this.bestFitness;
}
getPopulation() {
return this.population;
}
getGeneration() {
return this.generation;
}
}
// Export components for advanced usage
export * from './components';
//# sourceMappingURL=index.js.map