I'm trying to implement a genetic algorithm using node and I looked at the other question posted here, and it seems unrelated. When I try to call the determine_fitness
method of my populant
class I get this error:
Creating new population
Starting iteration 1
/home/aaron/Documents/workspace/playground/screeps_ga/ga.js:116
p.determine_fitness();
^
TypeError: p.determine_fitness is not a function
at population.fitness (/home/aaron/Documents/workspace/playground/screeps_ga/ga.js:116:15)
at population.train (/home/aaron/Documents/workspace/playground/screeps_ga/ga.js:130:18)
I'm really new to javascript, so I'm not sure what to do here besides just follow the stacktrace and it's not giving me any answers.
But, when I look at my classes:
class populant {
constructor(genes) {
this.genes = genes;
this.score = -1;
}
mutate() {
if(this.genes.length == 0)
this.genes += random_char();
else {
let r = getRndInteger(0, 3);
let i = getRndInteger(0, this.genes.length);
switch(r) {
case 0:
// Remove a character
let before = this.genes.substring(0, i);
let after = this.genes.substring(i + 1);
this.genes = before + after;
break;
case 1:
// Replace a character
let c = random_char();
let before = this.genes.substring(0, i);
let after = this.genes.substring(i + 1);
this.genes = before + c + after;
break;
case 2:
// Add a character
let c = random_char();
let before = this.genes.substring(0, i);
let after = this.genes.substring(i);
this.genes = before + c + after;
break;
}
}
}
mate(other, mutate) {
let i = 0;
let temp = '';
for(i = 0; i < this.genes.length; i++)
{
if(getRndInteger(0, 2) == 0)
temp += other.genes.substring(i, i + 1);
else
temp += this.genes.substring(i, i + 1);
}
this.genes = temp;
if(mutate)
this.mutate();
}
determine_fitness() {
return null;
}
}
As you can see, it's very clearly defined here, and it doesn't conflict with any of my other property names, so I'm really confused.
class population {
constructor(count, mutation_rate, populant_type) {
this.count = count;
this.mutation_rate = mutation_rate;
this.populant = populant_type;
this.pop = [];
}
initialize(parent_pool) {
this.pop = [];
if(parent_pool.length == 0)
for(let i = 0; i < this.count; i++)
this.pop.push(new this.populant(random_gene()));
else if(parent_pool.length == 1)
for(let i = 0; i < this.count; i++)
this.pop.push(new this.populant(parent_pool[0].genes));
else
{
for(let i = 0; i < this.count; i++)
{
let pai = getRndInteger(0, parent_pool.length);
let pbi = getRndInteger(0, parent_pool.length);
let temp_pa = new this.populant(parent_pool[pai].genes);
let should_mute = Math.random() < this.mutation_rate;
temp_pa.mate(parent_pool[pbi], should_mute);
this.pop.push(temp_pa);
}
}
}
fitness() {
for(let p in this.pop)
p.determine_fitness();
}
select_parents(count) {
this.pop.sort(function(a, b){ return a.score - b.score; });
return this.pop.slice(0, count);
}
train(iterations, crossover_size, initial_parents) {
let selected_parents = initial_parents;
for(let i = 0; i < iterations; i++)
{
this.initialize(selected_parents);
this.fitness();
selected_parents = this.select_parents(crossover_size);
}
return selected_parents;
}
}
The way that I have the classes organized right now is that the populant
class and the population
class are in their own file ga.js
and then I import them into my test.js
file. Because the determine_fitness()
method of populant
isn't defined by default, I created a child class and overwrote it as seen below:
let ga = require('./ga');
class StringPop extends ga.populant {
constructor(genes) {
super(genes);
}
determine_fitness() {
let sum = 0;
for(let i = 0; i < this.genes.length; i++)
sum += this.genes.charCodeAt(i);
this.score = sum;
return sum;
}
}
And then I'm testing the structure as shown:
pop = new ga.population(20, 0.25, StringPop);
let iteration = 1;
let parents = []
while(true)
{
console.log('\nStarting iteration ' + iteration.toString());
parents = pop.train(1, 5, parents);
console.log('Population after iteration');
print_pops(pop);
}
Where print_pops()
just prints out the genes
property of each populant on their own line.