public void AddNeuron(double mutationRate, int numTrysToFindOldLink)
{
// should we add a neuron?
if (ThreadSafeRandom.NextDouble() > mutationRate)
{
return;
}
// the link to split
NEATLinkGene splitLink = null;
int sizeThreshold = inputCount + outputCount + 10;
// if there are not at least
int upperLimit;
if (linksChromosome.Genes.Count < sizeThreshold)
{
upperLimit = NumGenes - 1 - (int)Math.Sqrt(NumGenes);
}
else
{
upperLimit = NumGenes - 1;
}
while ((numTrysToFindOldLink--) > 0)
{
// choose a link, use the square root to prefer the older links
int i = RangeRandomizer.RandomInt(0, upperLimit);
NEATLinkGene link = (NEATLinkGene)linksChromosome.Genes[i];
// get the from neuron
long fromNeuron = link.FromNeuronID;
if ((link.Enabled)
&& (!link.IsRecurrent)
&& (((NEATNeuronGene)Neurons.Genes[
GetElementPos(fromNeuron)]).NeuronType != NEATNeuronType.Bias))
{
splitLink = link;
break;
}
}
if (splitLink == null)
{
return;
}
splitLink.Enabled = false;
double originalWeight = splitLink.Weight;
long from = splitLink.FromNeuronID;
long to = splitLink.ToNeuronID;
NEATNeuronGene fromGene = (NEATNeuronGene)Neurons.Genes[
GetElementPos(from)];
NEATNeuronGene toGene = (NEATNeuronGene)Neurons.Genes[
GetElementPos(to)];
double newDepth = (fromGene.SplitY + toGene.SplitY) / 2;
double newWidth = (fromGene.SplitX + toGene.SplitX) / 2;
// has this innovation already been tried?
NEATInnovation innovation = ((NEATTraining)GA).Innovations.CheckInnovation(
from, to, NEATInnovationType.NewNeuron);
// prevent chaining
if (innovation != null)
{
long neuronID = innovation.NeuronID;
if (AlreadyHaveThisNeuronID(neuronID))
{
innovation = null;
}
}
if (innovation == null)
{
// this innovation has not been tried, create it
long newNeuronID = ((NEATTraining)GA).Innovations
.CreateNewInnovation(from, to,
NEATInnovationType.NewNeuron,
NEATNeuronType.Hidden, newWidth, newDepth);
neuronsChromosome.Genes.Add(new NEATNeuronGene(NEATNeuronType.Hidden,
newNeuronID, newDepth, newWidth));
// add the first link
long link1ID = ((NEATTraining)GA).Population.AssignInnovationID();
((NEATTraining)GA).Innovations.CreateNewInnovation(from, newNeuronID,
NEATInnovationType.NewLink);
NEATLinkGene link1 = new NEATLinkGene(from, newNeuronID,
true, link1ID, 1.0, false);
linksChromosome.Genes.Add(link1);
// add the second link
long link2ID = ((NEATTraining)GA).Population.AssignInnovationID();
((NEATTraining)GA).Innovations.CreateNewInnovation(newNeuronID, to,
NEATInnovationType.NewLink);
NEATLinkGene link2 = new NEATLinkGene(newNeuronID, to, true,
link2ID, originalWeight, false);
linksChromosome.Genes.Add(link2);
}
else
{
// existing innovation
long newNeuronID = innovation.NeuronID;
NEATInnovation innovationLink1 = ((NEATTraining)GA).Innovations
.CheckInnovation(from, newNeuronID,
NEATInnovationType.NewLink);
NEATInnovation innovationLink2 = ((NEATTraining)GA).Innovations
.CheckInnovation(newNeuronID, to,
NEATInnovationType.NewLink);
if ((innovationLink1 == null) || (innovationLink2 == null))
{
throw new NeuralNetworkError("NEAT Error");
}
NEATLinkGene link1 = new NEATLinkGene(from, newNeuronID,
true, innovationLink1.InnovationID, 1.0, false);
NEATLinkGene link2 = new NEATLinkGene(newNeuronID, to, true,
innovationLink2.InnovationID, originalWeight, false);
linksChromosome.Genes.Add(link1);
linksChromosome.Genes.Add(link2);
NEATNeuronGene newNeuron = new NEATNeuronGene(
NEATNeuronType.Hidden, newNeuronID, newDepth, newWidth);
neuronsChromosome.Genes.Add(newNeuron);
}
return;
}