123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- import {math} from "./math.js";
- export const population = (function() {
- return {
- Population: class {
- constructor(params) {
- this._params = params;
- this._population = [...Array(this._params.population_size)].map(
- _ => ({fitness: 1, genotype: this._CreateRandomGenotype()}));
- this._lastGeneration = null;
- this._generations = 0;
- }
- _CreateRandomGenotype() {
- return [...Array(this._params.genotype.size)].map(
- _ => Math.random() * 2 - 1);
- }
- Fittest() {
- return this._lastGeneration.parents[0];
- }
- Step(tgtImgData) {
- const parents = this._population.sort(
- (a, b) => (b.fitness - a.fitness));
- this._lastGeneration = {parents: parents};
- this._generations += 1;
- this._population = this._BreedNewPopulation(parents);
- }
- _BreedNewPopulation(parents) {
- function _RouletteSelection(sortedParents, totalFitness) {
- const roll = Math.random() * totalFitness;
- let sum = 0;
- for (let p of sortedParents) {
- sum += p.fitness;
- if (roll < sum) {
- return p;
- }
- }
- return sortedParents[sortedParents.length - 1];
- }
- function _RandomParent(sortedParents, otherParent, totalFitness) {
- const p = _RouletteSelection(sortedParents, totalFitness);
- return p;
- }
- function _CopyGenotype(g) {
- return ({
- fitness: g.fitness,
- genotype: [...g.genotype],
- });
- }
- const newPopulation = [];
- const totalFitness = parents.reduce((t, p) => t + p.fitness, 0);
- const numChildren = Math.ceil(
- parents.length * this._params.breed.childrenPercentage);
- const top = [...parents.slice(0, Math.ceil(
- parents.length * this._params.breed.selectionCutoff))];
- for (let j = 0; j < numChildren; j++) {
- const i = j % top.length;
- const p1 = top[i];
- const p2 = _RandomParent(parents, p1, totalFitness);
- const index = Math.round(Math.random() * p1.genotype.length);
- const g = p1.genotype.slice(0, index).concat(
- p2.genotype.slice(index));
- newPopulation.push(_CopyGenotype({fitness: 1, genotype: g}));
- }
- // Let's say keep top X% go through, but with mutations
- const topX = [...parents.slice(0, Math.ceil(
- parents.length * this._params.breed.immortalityCutoff))];
- newPopulation.push(...topX.map(x => _CopyGenotype(x)));
- // Mutations!
- for (let p of newPopulation) {
- const genotypeLength = p.genotype.length;
- const mutationOdds = this._params.mutation.odds;
- const mutationMagnitude = this._params.mutation.magnitude;
- function _Mutate(x) {
- const roll = Math.random();
- if (roll < mutationOdds) {
- const magnitude = mutationMagnitude * math.rand_normalish();
- return x + magnitude;
- }
- return x;
- }
- p.genotype = p.genotype.map(g => _Mutate(g));
- }
- // Immortality granted to the winners from the last life.
- // May the odds be forever in your favour.
- newPopulation.push(...topX.map(x => _CopyGenotype(x)));
- // Create a bunch of random crap to fill out the rest.
- while (newPopulation.length < parents.length) {
- newPopulation.push(
- {fitness: 1, genotype: this._CreateRandomGenotype()});
- }
- return newPopulation;
- }
- },
- };
- })();
|