-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpopulation.js
117 lines (94 loc) · 3.6 KB
/
population.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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;
}
},
};
})();