genetic-search
Version:
Multiprocessing genetic algorithm implementation library
1,067 lines (922 loc) • 35.9 kB
text/typescript
import { describe, expect, it } from "@jest/globals";
import {
AveragePhenomeCache,
ComposedGeneticSearchConfig,
GeneticSearchConfig,
GeneticSearchStrategyConfig,
DummyPhenomeCache,
SimplePhenomeCache,
Scheduler,
DescendingSortingStrategy,
AscendingSortingStrategy,
RandomSelectionStrategy,
TruncationSelectionStrategy,
ProportionalSelectionStrategy,
TournamentSelectionStrategy,
checkSchedulerCondition,
} from "../../src";
import {
ComposedGeneticSearch,
GeneticSearch,
IdGenerator,
} from "../../src";
import {
ParabolaArgumentGenome,
ParabolaCrossoverStrategy,
ParabolaMutationStrategy,
ParabolaPopulateStrategy,
ParabolaSinglePhenomeStrategy,
ParabolaReferenceFitnessStrategy,
ParabolaMaxValueFitnessStrategy,
ParabolaTaskConfig,
// @ts-ignore
} from "./fixtures";
// @ts-ignore
import { dataProviderForGetParabolaMax } from "./data";
import { WeightedAgeAveragePhenomeCache } from "../../src";
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola With Random Selection Max Test',
([a, b], [x, y]) => {
it('', async () => {
const config: GeneticSearchConfig = {
populationSize: 100,
survivalRate: 0.5,
crossoverRate: 0.5,
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
onTaskResult: () => void 0,
}),
fitness: new ParabolaMaxValueFitnessStrategy(),
sorting: new DescendingSortingStrategy(),
selection: new RandomSelectionStrategy(2),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new DummyPhenomeCache(),
}
const search = new GeneticSearch<ParabolaArgumentGenome>(config, strategies, new IdGenerator());
expect(search.cache).toBeInstanceOf(DummyPhenomeCache);
expect(search.partitions).toEqual([50, 25, 25]);
await search.fit({
generationsCount: 200,
beforeStep: () => void 0,
afterStep: () => {
const summary = search.getPopulationSummary();
expect(summary.fitnessSummary.count).toBe(100);
const roundedSummary = search.getPopulationSummary(4);
expect(roundedSummary.fitnessSummary.count).toBe(100);
},
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
const population = search.population;
expect(population.length).toBe(100);
{
const oldFirstIdx = population[0].id;
search.setPopulation(population);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, false);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, true);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola With Random Selection Max (startPopulationSize > populationSize) Test',
([a, b], [x, y]) => {
it('', async () => {
const config: GeneticSearchConfig = {
populationSize: 100,
startPopulationSize: 200,
survivalRate: 0.5,
crossoverRate: 0.5,
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
onTaskResult: () => void 0,
}),
fitness: new ParabolaMaxValueFitnessStrategy(),
sorting: new DescendingSortingStrategy(),
selection: new RandomSelectionStrategy(2),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new DummyPhenomeCache(),
}
const search = new GeneticSearch<ParabolaArgumentGenome>(config, strategies, new IdGenerator());
expect(search.cache).toBeInstanceOf(DummyPhenomeCache);
await search.fit({
generationsCount: 200,
beforeStep: () => void 0,
afterStep: (generation: number) => {
const summary = search.getPopulationSummary();
expect(summary.fitnessSummary.count).toBe(generation === 1 ? 200 : 100);
const roundedSummary = search.getPopulationSummary(4);
expect(roundedSummary.fitnessSummary.count).toBe(generation === 1 ? 200 : 100);
},
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
const population = search.population;
expect(population.length).toBe(100);
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola With Truncation Selection Max Test',
([a, b], [x, y]) => {
it('', async () => {
const config: GeneticSearchConfig = {
populationSize: 100,
survivalRate: 0.5,
crossoverRate: 0.5,
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
onTaskResult: () => void 0,
}),
fitness: new ParabolaMaxValueFitnessStrategy(),
sorting: new DescendingSortingStrategy(),
selection: new TruncationSelectionStrategy(2, config.survivalRate),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new DummyPhenomeCache(),
}
const search = new GeneticSearch<ParabolaArgumentGenome>(config, strategies, new IdGenerator());
expect(search.cache).toBeInstanceOf(DummyPhenomeCache);
expect(search.partitions).toEqual([50, 25, 25]);
await search.fit({
generationsCount: 100,
beforeStep: () => void 0,
afterStep: () => {
const summary = search.getPopulationSummary();
expect(summary.fitnessSummary.count).toBe(100);
const roundedSummary = search.getPopulationSummary(4);
expect(roundedSummary.fitnessSummary.count).toBe(100);
},
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
const population = search.population;
expect(population.length).toBe(100);
{
const oldFirstIdx = population[0].id;
search.setPopulation(population);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, false);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, true);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola With Proportional Selection Max Test',
([a, b], [x, y]) => {
it('', async () => {
const config: GeneticSearchConfig = {
populationSize: 200,
survivalRate: 0.3,
crossoverRate: 0.5,
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
onTaskResult: () => void 0,
}),
fitness: new ParabolaMaxValueFitnessStrategy(),
sorting: new DescendingSortingStrategy(),
selection: new ProportionalSelectionStrategy(2),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new DummyPhenomeCache(),
}
const search = new GeneticSearch<ParabolaArgumentGenome>(config, strategies, new IdGenerator());
expect(search.cache).toBeInstanceOf(DummyPhenomeCache);
expect(search.partitions).toEqual([60, 70, 70]);
await search.fit({
generationsCount: 200,
beforeStep: () => void 0,
afterStep: () => {
const summary = search.getPopulationSummary();
expect(summary.fitnessSummary.count).toBe(200);
const roundedSummary = search.getPopulationSummary(4);
expect(roundedSummary.fitnessSummary.count).toBe(200);
},
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
const population = search.population;
expect(population.length).toBe(200);
{
const oldFirstIdx = population[0].id;
search.setPopulation(population);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, false);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, true);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola With Tournament Selection Max Test',
([a, b], [x, y]) => {
it('', async () => {
const config: GeneticSearchConfig = {
populationSize: 100,
survivalRate: 0.5,
crossoverRate: 0.5,
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
onTaskResult: () => void 0,
}),
fitness: new ParabolaMaxValueFitnessStrategy(),
sorting: new DescendingSortingStrategy(),
selection: new TournamentSelectionStrategy(2, 5),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new DummyPhenomeCache(),
}
const search = new GeneticSearch<ParabolaArgumentGenome>(config, strategies, new IdGenerator());
expect(search.cache).toBeInstanceOf(DummyPhenomeCache);
expect(search.partitions).toEqual([50, 25, 25]);
await search.fit({
generationsCount: 200,
beforeStep: () => void 0,
afterStep: () => {
const summary = search.getPopulationSummary();
expect(summary.fitnessSummary.count).toBe(100);
const roundedSummary = search.getPopulationSummary(4);
expect(roundedSummary.fitnessSummary.count).toBe(100);
},
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
const population = search.population;
expect(population.length).toBe(100);
{
const oldFirstIdx = population[0].id;
search.setPopulation(population);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, false);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, true);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola With Ascending Sort Max Test',
([a, b], [x, y]) => {
it('', async () => {
const config: GeneticSearchConfig = {
populationSize: 100,
survivalRate: 0.5,
crossoverRate: 0.5,
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-(-((data[0]+a)**2) + b)],
onTaskResult: () => void 0,
}),
fitness: new ParabolaMaxValueFitnessStrategy(),
sorting: new AscendingSortingStrategy(),
selection: new TruncationSelectionStrategy(2, config.survivalRate),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new DummyPhenomeCache(),
}
const search = new GeneticSearch<ParabolaArgumentGenome>(config, strategies, new IdGenerator());
expect(search.cache).toBeInstanceOf(DummyPhenomeCache);
expect(search.partitions).toEqual([50, 25, 25]);
await search.fit({
generationsCount: 100,
beforeStep: () => void 0,
afterStep: () => {
const summary = search.getPopulationSummary();
expect(summary.fitnessSummary.count).toBe(100);
const roundedSummary = search.getPopulationSummary(4);
expect(roundedSummary.fitnessSummary.count).toBe(100);
},
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
const population = search.population;
expect(population.length).toBe(100);
{
const oldFirstIdx = population[0].id;
search.setPopulation(population);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, false);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, true);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola Max With Scheduler Test',
([a, b], [x, y]) => {
it('', async () => {
const config: GeneticSearchConfig = {
populationSize: 100,
survivalRate: 0.5,
crossoverRate: 0.5,
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
onTaskResult: () => void 0,
}),
fitness: new ParabolaMaxValueFitnessStrategy(),
sorting: new DescendingSortingStrategy(),
selection: new TruncationSelectionStrategy(2, config.survivalRate),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new DummyPhenomeCache(),
}
const search = new GeneticSearch<ParabolaArgumentGenome>(config, strategies, new IdGenerator());
expect(search.cache).toBeInstanceOf(DummyPhenomeCache);
expect(search.partitions).toEqual([50, 25, 25]);
const scheduler = new Scheduler({
runner: search,
config: config,
actions: [
(input) => {
checkSchedulerCondition(input.runner.generation === 10);
input.config.populationSize = 50;
input.logger('set population size to 50');
},
],
maxHistoryLength: 0,
})
await search.fit({
generationsCount: 100,
beforeStep: () => void 0,
afterStep: (generation) => {
const population = search.population;
const expectedPopulationSize = generation <= 10 ? 100 : 50;
expect(population.length).toBe(expectedPopulationSize);
if (generation === 10) {
expect(scheduler.logs).toEqual(['set population size to 50']);
} else {
expect(scheduler.logs).toEqual([]);
}
},
scheduler,
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
const population = search.population;
expect(population.length).toBe(50);
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola Max With Scheduler With Uncaught Exception Test',
([a, b], [x, y]) => {
it('', async () => {
const config: GeneticSearchConfig = {
populationSize: 100,
survivalRate: 0.5,
crossoverRate: 0.5,
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
onTaskResult: () => void 0,
}),
fitness: new ParabolaMaxValueFitnessStrategy(),
sorting: new DescendingSortingStrategy(),
selection: new TruncationSelectionStrategy(2, config.survivalRate),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new DummyPhenomeCache(),
}
const search = new GeneticSearch<ParabolaArgumentGenome>(config, strategies, new IdGenerator());
expect(search.cache).toBeInstanceOf(DummyPhenomeCache);
expect(search.partitions).toEqual([50, 25, 25]);
const scheduler = new Scheduler({
runner: search,
config: config,
actions: [
(input) => {
throw new Error('uncaught exception');
},
],
maxHistoryLength: 0,
})
try {
await search.fit({
generationsCount: 100,
beforeStep: () => void 0,
afterStep: (generation) => {
const population = search.population;
const expectedPopulationSize = generation <= 10 ? 100 : 50;
expect(population.length).toBe(expectedPopulationSize);
if (generation === 10) {
expect(scheduler.logs).toEqual(['set population size to 50']);
} else {
expect(scheduler.logs).toEqual([]);
}
},
scheduler,
});
} catch (e) {
expect((e as Error).message).toBe('uncaught exception');
expect(search.generation).toBe(1);
}
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola Max Simple Cached Test',
([a, b], [x, y]) => {
it('', async () => {
const config: GeneticSearchConfig = {
populationSize: 100,
survivalRate: 0.5,
crossoverRate: 0.5,
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
onTaskResult: () => void 0,
}),
fitness: new ParabolaMaxValueFitnessStrategy(),
sorting: new DescendingSortingStrategy(),
selection: new TruncationSelectionStrategy(2, config.survivalRate),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new SimplePhenomeCache(),
}
const search = new GeneticSearch<ParabolaArgumentGenome>(config, strategies, new IdGenerator());
expect(search.cache).toBeInstanceOf(SimplePhenomeCache);
expect(search.partitions).toEqual([50, 25, 25]);
await search.fit({
beforeStep: () => void 0,
afterStep: () => void 0,
stopCondition: (scores) => Math.abs(scores[0] - y) < 10e-9,
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
const population = search.population;
expect(population.length).toBe(100);
{
const oldFirstIdx = population[0].id;
search.setPopulation(population);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, false);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, true);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola Max Average Cached Test',
([a, b], [x, y]) => {
it('', async () => {
const config: GeneticSearchConfig = {
populationSize: 100,
survivalRate: 0.5,
crossoverRate: 0.5,
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
onTaskResult: () => void 0,
}),
fitness: new ParabolaMaxValueFitnessStrategy(),
sorting: new DescendingSortingStrategy(),
selection: new TruncationSelectionStrategy(2, config.survivalRate),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new AveragePhenomeCache(),
}
const search = new GeneticSearch<ParabolaArgumentGenome>(config, strategies, new IdGenerator());
expect(search.cache).toBeInstanceOf(AveragePhenomeCache);
expect(search.partitions).toEqual([50, 25, 25]);
await search.fit({
beforeStep: () => void 0,
afterStep: () => void 0,
stopCondition: (scores) => Math.abs(scores[0] - y) < 10e-9,
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
const population = search.population;
expect(population.length).toBe(100);
{
const oldFirstIdx = population[0].id;
search.setPopulation(population);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, false);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, true);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola Max Weighted Age Average Cached Test',
([a, b], [x, y]) => {
it('', async () => {
const config: GeneticSearchConfig = {
populationSize: 100,
survivalRate: 0.5,
crossoverRate: 0.5,
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
onTaskResult: () => void 0,
}),
fitness: new ParabolaMaxValueFitnessStrategy(),
sorting: new DescendingSortingStrategy(),
selection: new TruncationSelectionStrategy(2, config.survivalRate),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new WeightedAgeAveragePhenomeCache(0.2),
}
const search = new GeneticSearch<ParabolaArgumentGenome>(config, strategies, new IdGenerator());
await search.fit({
beforeStep: () => void 0,
afterStep: () => void 0,
stopCondition: (scores) => Math.abs(scores[0] - y) < 10e-5,
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x, 2);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
const population = search.population;
expect(population.length).toBe(100);
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola Max Reference Test',
([a, b], [x, y]) => {
it('', async () => {
const config: GeneticSearchConfig = {
populationSize: 100,
survivalRate: 0.5,
crossoverRate: 0.5,
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
}),
fitness: new ParabolaReferenceFitnessStrategy(y),
sorting: new DescendingSortingStrategy(),
selection: new TruncationSelectionStrategy(2, config.survivalRate),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new DummyPhenomeCache(),
}
const search = new GeneticSearch<ParabolaArgumentGenome>(config, strategies);
expect(search.cache).toBeInstanceOf(DummyPhenomeCache);
expect(search.partitions).toEqual([50, 25, 25]);
await search.fit({
generationsCount: 100,
beforeStep: () => void 0,
afterStep: () => void 0,
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
const population = search.population;
expect(population.length).toBe(100);
{
const oldFirstIdx = population[0].id;
search.setPopulation(population);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, false);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, true);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.population = population;
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola Max Single Process Composed Test',
([a, b], [x, y]) => {
it('', async () => {
const config: ComposedGeneticSearchConfig = {
eliminators: {
populationSize: 10,
survivalRate: 0.5,
crossoverRate: 0.5,
},
final: {
populationSize: 10,
survivalRate: 0.5,
crossoverRate: 0.5,
}
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
}),
fitness: new ParabolaReferenceFitnessStrategy(y),
sorting: new DescendingSortingStrategy(),
selection: new TruncationSelectionStrategy(2, config.eliminators.survivalRate),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new DummyPhenomeCache(),
}
const search = new ComposedGeneticSearch<ParabolaArgumentGenome>(config, strategies);
expect(search.generation).toEqual(1);
expect(search.cache).toBeInstanceOf(DummyPhenomeCache);
expect(search.partitions).toEqual([50, 30, 20]);
await search.fit({
generationsCount: 100,
beforeStep: () => void 0,
afterStep: () => {
const summary = search.getPopulationSummary();
expect(summary.fitnessSummary.count >= 10).toBe(true);
const roundedSummary = search.getPopulationSummary(4);
expect(roundedSummary.fitnessSummary.count >= 10).toBe(true);
},
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
expect(search.population.length).toBe(110);
const population = search.population;
{
const oldFirstIdx = population[0].id;
search.setPopulation(population);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, false);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, true);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.population = population;
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
});
},
);
describe.each([
...dataProviderForGetParabolaMax(),
] as Array<[[number, number], [number, number]]>)(
'Get Parabola Max Single Process Composed Another Test',
([a, b], [x, y]) => {
it('', async () => {
const config: ComposedGeneticSearchConfig = {
eliminators: {
populationSize: 10,
survivalRate: 0.5,
crossoverRate: 0.5,
},
final: {
populationSize: 10,
survivalRate: 0.5,
crossoverRate: 0.5,
},
};
const strategies: GeneticSearchStrategyConfig<ParabolaArgumentGenome> = {
populate: new ParabolaPopulateStrategy(),
phenome: new ParabolaSinglePhenomeStrategy({
task: async (data: ParabolaTaskConfig) => [-((data[0]+a)**2) + b],
}),
fitness: new ParabolaMaxValueFitnessStrategy(),
sorting: new DescendingSortingStrategy(),
selection: new TruncationSelectionStrategy(2, config.eliminators.survivalRate),
mutation: new ParabolaMutationStrategy(),
crossover: new ParabolaCrossoverStrategy(),
cache: new DummyPhenomeCache(),
}
const search = new ComposedGeneticSearch<ParabolaArgumentGenome>(config, strategies, new IdGenerator());
expect(search.generation).toEqual(1);
expect(search.cache).toBeInstanceOf(DummyPhenomeCache);
expect(search.partitions).toEqual([50, 30, 20]);
expect(search.getPopulationSummary().stagnationCounter).toEqual(0);
await search.fit({
stopCondition: (scores) => Math.abs(scores[0] - y) < 10e-6,
beforeStep: () => void 0,
afterStep: () => {
const summary = search.getPopulationSummary();
expect(summary.fitnessSummary.count >= 10).toBe(true);
const roundedSummary = search.getPopulationSummary(4);
expect(roundedSummary.fitnessSummary.count >= 10).toBe(true);
},
});
const bestGenome = search.bestGenome;
expect(bestGenome.x).toBeCloseTo(x, 2);
expect(-((bestGenome.x+a)**2) + b).toBeCloseTo(y);
expect(search.population.length).toBe(110);
const population = search.population;
{
const oldFirstIdx = population[0].id;
search.setPopulation(population);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, false);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.setPopulation(population, true);
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
{
const oldFirstIdx = population[0].id;
search.population = population;
const newFirstIdx = search.population[0].id;
expect(search.population).toEqual(population);
expect(oldFirstIdx).toEqual(newFirstIdx);
}
search.refreshPopulation();
});
},
);