Reputation: 759
I (almost) successfully created a genetic algorithm to find a given string, but for some reason, I semi-frequently get an error when running the program, which otherwise works perfectly.
Cannot read property 'dna' of undefined at Population.populate
I can't seem to figure out what causes this issue only a handful of times.
<!DOCTYPE html>
<html>
<head>
<title>Word Search</title>
<link href='https://fonts.googleapis.com/css?family=Ubuntu' rel='stylesheet'>
</head>
<body style ='font-family: Ubuntu, sans-serif'>
Target: <input type='text' value='Hello World!' id='target'> <br>
Population Size: <input type='number' id='size' min='0' max='100' step='1' value='10'><br>
Mutation Rate: <input type='number' id='rate' min='0' max='100' step='1' value='10'>%<br>
<input type='submit' id='submit' onclick='evolution()'>
<div style='border-width: 2px; border-style: dashed; width: 250px'>
<div style='text-align: center;' id='value'>
<p id='generation';>Generation | 0</p>
<div id='pop'>
</div>
</div>
</div>
<script type="text/javascript">
window.onload = function() {
evolution();
}
function evolution() {
var population = new Population(
document.getElementById('target').value,
document.getElementById('rate').value/100,
document.getElementById('size').value);
var running = setInterval(function() {
document.getElementById('submit').disabled = true;
population.natSelection();
population.populate();
population.evaluate();
population.display();
if(population.completed) {
clearInterval(running);
document.getElementById('submit').disabled = false;
}
}, 50);
}
function Population(target, mutationRate, size) {
this.target = target;
this.mutationRate = mutationRate;
this.size = size;
this.members = [];
this.genePool = [];
this.completed = false;
this.generation = 0;
for(var i = 0; i < this.size; i++)
this.members.push(new Genome(this.target, this.mutationRate));
this.natSelection = function() {
for(var i = 0; i < this.members.length; i++)
this.members[i].calcFitness();
this.genePool = [];
for(var i = 0; i < this.members.length; i++)
for(var j = 0; j < this.members[i].fitness*10; j++)
this.genePool.push(this.members[i]);
}
this.populate = function() {
this.generation++;
this.members = [];
for(var i = 0; i < this.size; i++) {
var a = this.genePool[Math.floor(Math.random()*this.genePool.length)].dna;
var b = this.genePool[Math.floor(Math.random()*this.genePool.length)].dna;
this.members.push(new Genome(this.target, this.mutationRate, a, b));
}
}
this.evaluate = function() {
for(var i = 0; i < this.members.length; i++) {
if(this.members[i].dna === this.target)
this.completed = true;
}
}
this.calcMaxFitness = function() {
var fittest = this.memebers[0].fitness;
for(var i = 1; i < this.members.length; i++)
if(this.memebers[i].fitness > fittest)
fittest = this.memebers[i].fitness;
return fittest;
}
this.display = function() {
document.getElementById('generation').innerHTML = 'Generation | '+this.generation;
var div = document.getElementById('pop');
div.innerHTML = '';
for(var i = 0; i < this.members.length; i++) {
div.innerHTML += this.members[i].dna+'<br>'
}
}
}
function Genome(target, mutationRate, parentA, parentB) {
this.dna = '';
this.fitness = 0;
this.target = target;
this.mutationRate = mutationRate;
this.mutate = function() {
for(var i = 0; i < this.target.length; i++) {
if(this.dna.charCodeAt(i) != this.target.charCodeAt(i))
if(Math.random() > this.mutationRate)
this.dna = this.dna.replaceAt(i, String.fromCharCode(Math.floor(Math.random()*94+32)));
}
}
if(!parentA && !parentB) {
for(var i = 0; i < this.target.length; i++)
this.dna += String.fromCharCode(Math.floor(Math.random()*94+32));
} else {
var mid = Math.floor(Math.random()*this.target.length);
this.dna = parentA.substr(0, mid) + parentB.substr(mid, parentB.length);
this.mutate();
}
this.calcFitness = function() {
this.fitness = 0;
for(var i = 0; i < this.target.length; i++) {
if(this.dna.charCodeAt(i) === this.target.charCodeAt(i))
this.fitness++;
}
}
}
Number.prototype.map = function (in_min, in_max, out_min, out_max) {
return (this - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
String.prototype.replaceAt=function(index, replacement) {
return this.substr(0, index) + replacement+ this.substr(index + replacement.length);
}
</script>
</body>
</html>
Upvotes: 2
Views: 54
Reputation: 25361
This error happens when all members have a fitness of zero, then the loop in the natSelection
function is looping until j < this.members[i].fitness*10
which is zero, so no loop and nothing is added to genePool
.
Now when that happens, obviously this.genePool[Math.floor(Math.random()*this.genePool.length)]
will be undefined and when you try to access the property dna
, you get the error.
To solve this, you must add some validation so when all members have fitness of zero, you do something about it. Alternatively, at the end of the natSelection
function, check genePool.length
and if it is still zero, then add at least one item.
Upvotes: 3
Reputation: 20007
You are trying to read property dna
of undefined in your populate
method.
Said another way:
// Your populate method
this.populate = function() {
this.generation++;
this.members = [];
for(var i = 0; i < this.size; i++) {
var a = this.genePool[Math.floor(Math.random()*this.genePool.length)].dna;
var b = this.genePool[Math.floor(Math.random()*this.genePool.length)].dna;
this.members.push(new Genome(this.target, this.mutationRate, a, b));
}
}
JavasScript is seeing
this.genePool[Math.floor(Math.random()*this.genePool.length)]
as undefined
and then, you're asking to JavaScript to read the dna
property of undefined
.
undefined.dna
Personally, I would log the following:
// Your populate method
this.populate = function() {
this.generation++;
this.members = [];
console.log('SIZE', this.size);
for(var i = 0; i < this.size; i++) {
console.log('I', i);
console.log('this.genePool', this.genePool);
console.log('this.genePool.length', this.genePool.length);
var a = this.genePool[Math.floor(Math.random()*this.genePool.length)].dna;
var b = this.genePool[Math.floor(Math.random()*this.genePool.length)].dna;
this.members.push(new Genome(this.target, this.mutationRate, a, b));
}
}
Maybe we have using i < this.size
when we should be using i < this.size - 1
, or something similar elsewhere (aka off-by-one error).
Maybe this.genePool
is undefined sometimes.
If you still have trouble, update your question with some log output.
Upvotes: 1