tycho-solver
Version:
Evolutionary computation and optimization library
143 lines (142 loc) • 6.24 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GALoopOperator = void 0;
const SequentialOperator_1 = require("../../../core/pipeline/SequentialOperator");
// Import default implementations
const InitializationOperator_1 = require("./InitializationOperator");
const EvaluationOperator_1 = require("./EvaluationOperator");
const TerminationOperator_1 = require("./TerminationOperator");
const SelectionOperator_1 = require("./SelectionOperator");
const CrossoverOperator_1 = require("./CrossoverOperator");
const MutationOperator_1 = require("./MutationOperator");
const ReplacementOperator_1 = require("./ReplacementOperator");
const ElitismOperator_1 = require("./ElitismOperator");
const GALoopOperator = (_a) => __awaiter(void 0, [_a], void 0, function* ({ population, initializationOperator, evaluationOperator, selectionOperator, crossoverOperator, mutationOperator, replacementOperator, elitismOperator, terminationOperator, fitnessFunction, maxGenerations, eliteCount = 0, fitnessLimit, populationSize = 100 // default size if not provided
}) {
// --- InitializationOperator creates the population if not provided ---
const initOp = initializationOperator || new InitializationOperator_1.GAInitializationOperator();
let pop = population ? population : (yield (initOp.initialize ? initOp.initialize(populationSize) : []));
if (!pop || pop.length === 0)
throw new Error('Population could not be initialized.');
// --- EvaluationOperator ---
const isSyncFitness = (fn) => {
try {
const res = fn(pop[0]);
return !(res instanceof Promise);
}
catch (_a) {
return true;
}
};
let evalOp;
if (evaluationOperator) {
evalOp = evaluationOperator;
}
else if (isSyncFitness(fitnessFunction)) {
evalOp = new EvaluationOperator_1.GAEvaluationOperator(fitnessFunction);
}
else {
evalOp = { evaluate: (solution) => fitnessFunction(solution) };
}
// --- Evaluate initial population ---
let fitnesses = yield Promise.all(pop.map(ind => evalOp.evaluate(ind)));
let bestSolution = pop[0];
let bestFitness = Math.max(...fitnesses);
let generation = 0;
// --- ElitismOperator ---
const elitOp = elitismOperator || new ElitismOperator_1.ElitismOperatorImpl();
// --- ReplacementOperator (now handles elitism internally) ---
const replOp = replacementOperator || new ReplacementOperator_1.ReplacementOperatorImpl({
elitismOperator: elitOp,
eliteCount,
fitnessFunction
});
// --- SelectionOperator ---
const selectOp = selectionOperator || new SelectionOperator_1.SelectionOperatorImpl();
// --- CrossoverOperator ---
let crossOp;
if (crossoverOperator) {
crossOp = crossoverOperator;
}
else {
try {
crossOp = new CrossoverOperator_1.CrossoverOperatorImpl();
}
catch (_b) {
throw new Error('No default crossover operator for this individual type. Please provide one.');
}
}
// --- MutationOperator ---
let mutOp;
if (mutationOperator) {
mutOp = mutationOperator;
}
else {
try {
mutOp = new MutationOperator_1.MutationOperatorImpl();
}
catch (_c) {
throw new Error('No default mutation operator for this individual type. Please provide one.');
}
}
// --- TerminationOperator ---
const termOp = terminationOperator || new TerminationOperator_1.GATerminationOperator();
// --- Main Evolutionary Loop ---
// Define pipeline steps as pipeline operators
const selectionStep = {
apply: (currentPop) => selectOp.select(currentPop, fitnesses, currentPop.length)
};
const crossoverMutationStep = {
apply: (parents) => {
let offspring = [];
for (let i = 0; i < parents.length; i += 2) {
const parent1 = parents[i];
const parent2 = parents[i + 1] || parents[0];
const [child1, child2] = crossOp.crossover(parent1, parent2);
offspring.push(mutOp.mutate(child1));
offspring.push(mutOp.mutate(child2));
}
return offspring;
}
};
const replacementStep = {
apply: (offspring) => __awaiter(void 0, void 0, void 0, function* () { return yield replOp.replace(pop, offspring, fitnesses); })
};
const evaluationStep = {
apply: (newPop) => __awaiter(void 0, void 0, void 0, function* () {
fitnesses = yield Promise.all(newPop.map(ind => evalOp.evaluate(ind)));
return newPop;
})
};
// Compose pipeline
const pipeline = new SequentialOperator_1.SequentialOperator([
selectionStep,
crossoverMutationStep,
replacementStep,
evaluationStep
]);
while (generation < maxGenerations && !termOp.shouldTerminate(pop)) {
// Apply pipeline steps
pop = yield pipeline.apply(pop);
// Update best
const genBestIdx = fitnesses.indexOf(Math.max(...fitnesses));
if (fitnesses[genBestIdx] > bestFitness) {
bestFitness = fitnesses[genBestIdx];
bestSolution = pop[genBestIdx];
}
if (fitnessLimit !== undefined && bestFitness >= fitnessLimit)
break;
generation++;
}
return { bestSolution, bestFitness, population: pop, generation };
});
exports.GALoopOperator = GALoopOperator;