// Domain specific classes for seeding and fitness functions. These classes are
// used to define the problem domain and are passed to the GeneticAlgorithm class
// as parameters.
// Gene seeding class with a fixed value.
class Seed {
constructor(value = 0) {
this.value = value;
}
run() {
return this.value;
}
}
// Gene random seeding class.
class Seed_Random {
constructor(max) {
this.max = max;
}
run() {
return random(this.max);
}
}
// Returns sum of all genes in a chromosome.
class Fitness_Accumulate {
run(genes) {
let s = 0;
for (let gene of genes)
s += gene;
return s;
}
}
// Returns a measure in percentages that indicates the fitness of
// a particular chromosome (genes). Result is proportional to the number of
// 1s in the genes. Fittest chromosome has all genes equal 1.
class Fitness_Nbits {
run(genes) {
if (genes.length) {
let s = 0;
for (let gene of genes)
s += gene;
return 100 * s / genes.length;
}
return 0;
}
}
// Returns a measure in percentages that indicates the fitness of
// a particular chromosome (genes) as a solution of 8-Queens problem.
// Result is proportional to the number of non-attacking pairs of
// queens. Implemented by counting the number of all horizontal and diagonal
// threats. Note that number of vertical threats is always 0 because of
// the gene representation.
class Fitness_8queens {
run(genes) {
let size = genes.length;
let max = (size - 1) * size / 2;
// Check horizontal threats
let counter = 0;
for (let j = 0; j < size - 1; ++j)
for (let i = j + 1; i < size; ++i)
if (genes[j] - genes[i] == 0)
counter++;
// Check diagonal threats
for (let j = 0; j < size - 1; ++j)
for (let i = j + 1; i < size; ++i)
if (Math.abs(genes[j] - genes[i]) == i - j)
counter++;
return Math.round(100 * (max - counter) / max);
}
}
// Test 1 tests the fitness functions.
function test1() {
let actual1 = new Fitness_Accumulate().run([1, 2, 3, 4, 5, 6, 7, 8]);
let expected1 = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8;
let actual2 = new Fitness_Nbits().run([0, 1, 0, 1, 0, 1, 0, 1]);
let expected2 = 50;
let actual3 = new Fitness_8queens().run([0, 1, 2, 3, 4, 5, 6, 7]);
let expected3 = 0;
let actual4 = new Fitness_8queens().run([4, 2, 0, 6, 1, 7, 5, 3]);
let expected4 = 100;
let actual = actual1 + ',' + actual2 + ',' + actual3 + ',' + actual4;
let expected = expected1 + ',' + expected2 + ',' + expected3 + ',' + expected4;
compareAndReport('Test 1', actual, expected);
}
// Test 2 tests the fitness function of a chromosome.
function test2() {
let sizeOfChromosome = 8;
let individual = new GeneticAlgorithm.Population.Individual(
new Seed(0), new Fitness_Accumulate(), sizeOfChromosome);
individual.setGenes([1, 2, 3, 4, 5, 6, 7, 8]);
let actual = individual.getFitness();
let expected = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8;
compareAndReport('Test 2', actual, expected);
}
// Test 3 tests the fitness function of an individual.
function test3() {
let sizeOfChromosome = 8;
let seedValue = 3;
let individual = new GeneticAlgorithm.Population.Individual(
new Seed(seedValue), new Fitness_Accumulate(), sizeOfChromosome);
let actual = individual.getFitness();
let expected = seedValue * sizeOfChromosome;
compareAndReport('Test 3', actual, expected);
}
// Test 4 tests the seeding of an individual.
function test4() {
let sizeOfChromosome = 8;
let Max = 10;
let i = new GeneticAlgorithm.Population.Individual(
new Seed_Random(Max), new Fitness_Accumulate(), sizeOfChromosome);
let actual = (
i.getGene(0) >= 0 && i.getGene(0) < Max &&
i.getGene(1) >= 0 && i.getGene(1) < Max &&
i.getGene(2) >= 0 && i.getGene(2) < Max &&
i.getGene(3) >= 0 && i.getGene(3) < Max &&
i.getGene(4) >= 0 && i.getGene(4) < Max &&
i.getGene(5) >= 0 && i.getGene(5) < Max &&
i.getGene(6) >= 0 && i.getGene(6) < Max &&
i.getGene(7) >= 0 && i.getGene(7) < Max &&
(
i.getGene(0) != i.getGene(1) ||
i.getGene(1) != i.getGene(2) ||
i.getGene(2) != i.getGene(3) ||
i.getGene(3) != i.getGene(4) ||
i.getGene(4) != i.getGene(5) ||
i.getGene(5) != i.getGene(6) ||
i.getGene(6) != i.getGene(7)
)
);
let expected = true;
compareAndReport('Test 4', actual, expected);
}
// Test 5 tests the fittest individual in a population.
function test5() {
let sizeOfChromosome = 3;
let sizeOfPopulation = 5;
let population = new GeneticAlgorithm.Population(
new Seed(0), new Fitness_Accumulate(), sizeOfChromosome, sizeOfPopulation);
population.getIndividual(0).setGenes([1, 2, 3]);
population.getIndividual(1).setGenes([10, 20, 30]);
population.getIndividual(2).setGenes([10000, 20000, 30000]);
population.getIndividual(3).setGenes([100, 200, 300]);
population.getIndividual(4).setGenes([1000, 2000, 3000]);
population.updateFittest();
let actual = population.getFittest().getFitness();
let expected = 10000 + 20000 + 30000;
compareAndReport('Test 5', actual, expected);
}
// Test 6 tests the selection of a new generation from a population.
function test6() {
let sizeOfChromosome = 8;
let Max = 2;
let sizeOfPopulation = 10;
let ga = new GeneticAlgorithm(new Seed_Random(Max), new Fitness_Nbits());
let newGeneration = ga.selection(sizeOfChromosome, sizeOfPopulation);
let actual = true;
for (let i = 0; i < newGeneration.getSize(); ++i) {
if (new Fitness_Nbits().run(newGeneration.getIndividual(i).getGenes()) !=
newGeneration.getIndividual(i).getFitness()) {
actual = false;
break;
}
}
let expected = true;
compareAndReport('Test 6', actual, expected);
}
// Test 7 tests the crossover of a new generation from a population.
function test7() {
let sizeOfChromosome = 8;
let sizeOfPopulation = 2;
let newGeneration = new GeneticAlgorithm.Population(
new Seed(0), new Fitness_Nbits(), sizeOfChromosome, sizeOfPopulation);
newGeneration.getIndividual(0).setGenes([0, 0, 0, 0, 0, 0, 0, 0]);
newGeneration.getIndividual(1).setGenes([1, 1, 1, 1, 1, 1, 1, 1]);
let ga = new GeneticAlgorithm(new Seed(0), new Fitness_Nbits());
ga.crossover(newGeneration, sizeOfChromosome, GeneticAlgorithm.CrossoverMethod.Middle);
let actual = newGeneration.getIndividual(0).toString();
let expected = "[1,1,1,1,0,0,0,0]=50";
compareAndReport('Test 7', actual, expected);
}
// Test 8 tests the run method of the genetic algorithm.
function test8() {
let sizeOfChromosome = 8;
let Max = 2;
let sizeOfPopulation = 10;
let mutationProbability = 70;
let crossoverMethod = GeneticAlgorithm.CrossoverMethod.Middle;
let ga = new GeneticAlgorithm(new Seed_Random(Max), new Fitness_Nbits());
ga.run(sizeOfChromosome, sizeOfPopulation, mutationProbability, crossoverMethod);
let actual = ga.getFittest().toString();
let expected = "[1,1,1,1,1,1,1,1]=100";
compareAndReport('Test 8', actual, expected);
}
// Test 9 tests the run method of the genetic algorithm for 8-Queens problem.
function test9() {
let sizeOfChromosome = 8;
let Max = 8;
let sizeOfPopulation = 100;
let mutationProbability = 70;
let ga = new GeneticAlgorithm(new Seed_Random(Max), new Fitness_8queens());
ga.run(sizeOfChromosome, sizeOfPopulation, mutationProbability,
GeneticAlgorithm.CrossoverMethod.Middle);
let actual = new Fitness_8queens().run(ga.getFittest().getGenes());
let expected = 100;
compareAndReport('Test 9', actual, expected);
}
// Same as 9 but using GeneticAlgorithm.CrossoverMethod.Random.
function test10() {
let sizeOfChromosome = 8;
let Max = 8;
let sizeOfPopulation = 100;
let mutationProbability = 70;
let ga = new GeneticAlgorithm(new Seed_Random(Max), new Fitness_8queens());
ga.run(sizeOfChromosome, sizeOfPopulation, mutationProbability,
GeneticAlgorithm.CrossoverMethod.Random);
let actual = new Fitness_8queens().run(ga.getFittest().getGenes());
let expected = 100;
compareAndReport('Test 10', actual, expected);
}