UNPKG

tycho-solver

Version:

Evolutionary computation and optimization library

143 lines (142 loc) 6.24 kB
"use strict"; 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;