0

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.

iggy12345
  • 1,233
  • 12
  • 31

1 Answers1

1

You could try to check if the type of p is what you expect by logging it before trying to call determine_fitness:

fitness() {
    for(let p in this.pop) {
        console.log(typeof p);
        p.determine_fitness();
    }    
}

Also, since pop is a list and I think you push an object to it, you could use for ... of syntax:

fitness() {
    for (p of this.pop) {
        p.determine_fitness();
    }
}

From the MDN article on for ... in:

Given that for...in is built for iterating object properties, not recommended for use with arrays, and options like Array.prototype.forEach() and for...of exist, what might be the use of for...in at all? [Emphasis mine]

  • Using the `for(p of this.pop)` worked, apparently using `for(p in this.pop)` was causing p to be a serialized string??? – iggy12345 Nov 02 '19 at 09:59
  • 1
    To be honest, I don't perfectly understand how for ... in works and its use cases, I just know that when you want to get each element of a list in a loop for ... of is a good solution. – Lucas De Angelis Nov 02 '19 at 10:02
  • 1
    @LucasDeAngelis `for … in` is enumerating the property names of an object. It should not be used on arrays. – Bergi Nov 02 '19 at 10:04