Averaging Operator
The first and simplest breeding operator I could think of is one where the value of each gene is an average of the corresponding values in the parents:
Chromosome | Gene 1 | Gene 2 |
---|---|---|
Parent 1 | 3 | 6.2 |
Parent 2 | 5 | 4.1 |
Child | 4 | 5.15 |
One problem with this is that it will instantly violate the simple sum of gene values constraint we imposed. As such the actual code goes a step further and 'normalises' the genes of the child chromosome to have a sum that matches that of the parents.
This operator is not a true breeding operator in its purest form as it does modify the values of the genes rather than simply create a new chromosome by resequencing genes selected from the parents. As such it is more of a breeding and mutation operator in one. The reason behind the decision to do this was that in a continuous variable problem, the best solution may possibly exist in a space 'between' the solutions that have already been explored, so a mutation and a resequencing combined may help to explore the problem space more rapidly. Remember that in the first post a single point crossover breeding operator was used, this takes part of each parent and concatenates to form a child, the table below showing how the same parents can produce 2 different children by changing the point of crossover
Chromosome | Gene 1 | Gene 2 | Gene 3 | Gene 4 |
---|---|---|---|---|
Parent 1 | 1 | 2 | 3 | 4 |
Parent 2 | 9 | 8 | 7 | 6 |
Child | 1 | 2 | 7 | 6 |
Child | 1 | 8 | 7 | 6 |
and we have the results of this non-mutating breeding operator to compare with those of a hybrid breeder-mutator operation.
Results do show that this operator gives no noticeable advantage over the out of the box pure crossover operation, with R=0.941 and F=0.235 with the SwapMutate operator unmodified from the first post.
the code for this operator is:
public void Invoke(Population currentPopulation, ref Population newPopulation, FitnessFunction fitnesFunctionDelegate)
{
Debug.WriteLine("Breeding from pop size of {0} into pop size of {1}", currentPopulation.PopulationSize, newPopulation.PopulationSize);
//first check sixe of new population, if its big enough already just return
if (newPopulation.PopulationSize >= currentPopulation.PopulationSize)
return;
//also cannot perform with less than 2 chromosomes
if (currentPopulation.PopulationSize < 2)
return;
while (newPopulation.PopulationSize < currentPopulation.PopulationSize)
{
//otherwise take 2 chromosomes at random from the current population (include elite ones)
var parents = currentPopulation.Solutions.SelectRandomMembers(2);
//craete the new chromosome
//assumes both parents have the same the sum of gene values
var sum = parents.First().Genes.Sum(g => g.RealValue);
var newGenes = new List<double>();
for (int i = 0; i < parents.First().Genes.Count; i++)
{
newGenes.Add((parents.First().Genes.ElementAt(i).RealValue + parents.Last().Genes.ElementAt(i).RealValue) / 2);
}
//now normalise to the same sum
var newsum = newGenes.Sum();
var m = sum / newsum;
var ch = new Chromosome(newGenes.Select(g => g * m));
newPopulation.Solutions.Add(ch);
}
}
Normalised Sum Operator
A slight variation on the averaging breeder is to instead simply sum the gene values of the parents, then 'normalise' the sum of gene values to meet the constraint. Again this does not offer any great advantage with R=0.978 and F=0.244
from code
public void Invoke(Population currentPopulation, ref Population newPopulation, FitnessFunction fitnesFunctionDelegate)
{
Debug.WriteLine("Breeding from pop size of {0} into pop size of {1}", currentPopulation.PopulationSize, newPopulation.PopulationSize);
//first check sixe of new population, if its big enough already just return
if (newPopulation.PopulationSize >= currentPopulation.PopulationSize)
return;
//also cannot perform with less than 2 chromosomes
if (currentPopulation.PopulationSize < 2)
return;
while (newPopulation.PopulationSize < currentPopulation.PopulationSize)
{
//otherwise take 2 chromosomes at random from the current population (include elite ones)
var parents = currentPopulation.Solutions.SelectRandomMembers(2);
//craete the new chromosome
//assumes both parents have the same the sum of gene values
var sum = parents.First().Genes.Sum(g => g.RealValue);
var newGenes = new List<double>();
for (int i = 0; i < parents.First().Genes.Count; i++)
{
newGenes.Add(parents.First().Genes.ElementAt(i).RealValue + parents.Last().Genes.ElementAt(i).RealValue);
}
//now normalise to the same sum
var newsum = newGenes.Sum();
var m = sum / newsum;
var ch = new Chromosome(newGenes.Select(g => g * m));
newPopulation.Solutions.Add(ch);
}
Conclusion
Although the operators selected for use in the algorithm obviously do have a factor in the obtaining of a good solution, it appears that they do not have a significant impact on just how good a solution can be found. It may be a leap of faith at this stage but it is my belief that the fitness calculation will have a far more significant impact, and that is something that I will explore in the next post.
And on that theme, the next post may possibly be delayed as I am currently in hospital and have no access to my PC to do any coding, but hopefully I will get my hands dirty again fairly soon and be able to produce another post.
No comments:
Post a Comment