public override NodeBaseType <T, S> SelectRandom(ref int numNodesConsidered)
        {
            float selectPercentage = 1F / numNodesConsidered;

            // possibly pick myself
            NodeBaseType <T, S> selected = null;

            if (Randomizer.GetFloatFromZeroToOne() < selectPercentage)
            {
                selected = this;
            }

            // but also consider all of my children
            foreach (var child in Children)
            {
                numNodesConsidered++;
                var selectedFromChild = child.SelectRandom(ref numNodesConsidered);
                if (selectedFromChild != null)
                {
                    selected = selectedFromChild;
                }
            }

            return(selected);
        }
Exemple #2
0
        internal void Mutate()
        {
            // pick any node but the root (since that would be a replacement of the entire tree, not a mutation)
            NodeBaseType <T, S> randomNode = null;

            do
            {
                randomNode = SelectRandomNode();
            } while (randomNode == Root);

            // build a random subtree
            bool buildFullTrees = Randomizer.GetFloatFromZeroToOne() < 0.5F;
            int  depth          = Randomizer.IntBetween(TreeMinDepth, TreeMaxDepth);
            var  newSubtree     = AddRandomNode(null, depth, buildFullTrees);

            // replace the randomly selected node with the new random one
            var parent = (FunctionNode <T, S>)randomNode.Parent;

            newSubtree.Parent = parent;
            // find the link from the parent to the old child, and replace it
            for (int i = 0; i < parent.Children.Count; i++)
            {
                if (parent.Children[i] == randomNode)
                {
                    parent.Children[i] = newSubtree;
                    break;
                }
            }
        }
Exemple #3
0
        public void CreateRandom()
        {
            // ramped half-and-half approach:
            // do grow or full approach, evenly split
            // full: choose function nodes for all levels except the maximum, which is always terminal
            // grow: randomly choose function or terminal, except for max depth which is always terminal
            bool buildFullTrees = Randomizer.GetFloatFromZeroToOne() < 0.5;
            int  depth          = Randomizer.IntBetween(TreeMinDepth, TreeMaxDepth);

            Root = AddRandomNode(null, depth, buildFullTrees);
        }
        public CandidateSolution <T, S> FindBestSolution()
        {
            // keep track of best overall, average fitness scores
            float bestFitnessScoreAllTime = (IsLowerFitnessBetter ? float.MaxValue : float.MinValue);
            float bestAverageFitnessScore = (IsLowerFitnessBetter ? float.MaxValue : float.MinValue);
            CandidateSolution <T, S> bestTreeAllTime = null;
            int bestSolutionGenerationNumber = 0, bestAverageFitnessGenerationNumber = 0;

            // elitism
            int numElitesToAdd = (int)(ElitismRate * PopulationSize);

            // depending on whether elitism is used, or the selection type, we may need to sort candidates by fitness (which is slower)
            bool needToSortByFitness =
                SelectionStyle == SelectionStyle.RouletteWheel ||
                SelectionStyle == SelectionStyle.Ranked ||
                ElitismRate > 0;

            Stopwatch timer = new Stopwatch();

            // create an initial population of random trees, passing the possible functions, consts, and variables
            for (int p = 0; p < PopulationSize; p++)
            {
                CandidateSolution <T, S> tree = new CandidateSolution <T, S>(primitiveSet, RandomTreeMinDepth, RandomTreeMaxDepth);
                tree.CreateRandom();
                currentGeneration.Add(tree);
            }

            // loop over generations
            int currentGenerationNumber = 0;

            while (true)
            {
                timer.Restart();

                // for each tree, find and store the fitness score
                // multithread the fitness evaluation
                Parallel.ForEach(currentGeneration, (candidate) =>
                {
                    // calc the fitness by calling the user-supplied function via the delegate
                    float fitness     = myFitnessFunction(candidate);
                    candidate.Fitness = fitness;
                });

                // now check if we have a new best
                float bestFitnessScoreThisGeneration            = (IsLowerFitnessBetter ? float.MaxValue : float.MinValue);
                CandidateSolution <T, S> bestTreeThisGeneration = null;
                float totalFitness = 0;
                foreach (var tree in currentGeneration)
                {
                    totalFitness += tree.Fitness;

                    // find best of this generation, update best all-time if needed
                    bool isBestThisGeneration = false;
                    if (IsLowerFitnessBetter)
                    {
                        isBestThisGeneration = tree.Fitness < bestFitnessScoreThisGeneration;
                    }
                    else
                    {
                        isBestThisGeneration = tree.Fitness > bestFitnessScoreThisGeneration;
                    }

                    if (isBestThisGeneration)
                    {
                        bestFitnessScoreThisGeneration = tree.Fitness;
                        bestTreeThisGeneration         = tree;

                        bool isBestEver = false;
                        if (IsLowerFitnessBetter)
                        {
                            isBestEver = bestFitnessScoreThisGeneration < bestFitnessScoreAllTime;
                        }
                        else
                        {
                            isBestEver = bestFitnessScoreThisGeneration > bestFitnessScoreAllTime;
                        }

                        if (isBestEver)
                        {
                            bestFitnessScoreAllTime      = bestFitnessScoreThisGeneration;
                            bestTreeAllTime              = tree.Clone();
                            bestSolutionGenerationNumber = currentGenerationNumber;
                        }
                    }
                }

                // determine average fitness and store if it's all-time best
                float averageFitness = totalFitness / PopulationSize;
                if (IsLowerFitnessBetter)
                {
                    if (averageFitness < bestAverageFitnessScore)
                    {
                        bestAverageFitnessGenerationNumber = currentGenerationNumber;
                        bestAverageFitnessScore            = averageFitness;
                    }
                }
                else
                {
                    if (averageFitness > bestAverageFitnessScore)
                    {
                        bestAverageFitnessGenerationNumber = currentGenerationNumber;
                        bestAverageFitnessScore            = averageFitness;
                    }
                }

                // report progress back to the user, and allow them to terminate the loop
                EngineProgress progress = new EngineProgress()
                {
                    GenerationNumber   = currentGenerationNumber,
                    AvgFitnessThisGen  = averageFitness,
                    BestFitnessThisGen = bestFitnessScoreThisGeneration,
                    BestFitnessSoFar   = bestFitnessScoreAllTime,
                    TimeForGeneration  = timer.Elapsed
                };
                bool keepGoing = myProgressFunction(progress);
                if (!keepGoing)
                {
                    break;              // user signalled to end looping
                }
                // termination conditions
                if (currentGenerationNumber >= MinGenerations)
                {
                    // exit the loop if we're not making any progress in our average fitness score
                    if ((currentGenerationNumber - bestAverageFitnessGenerationNumber)
                        >= StagnantGenerationLimit)
                    {
                        break;
                    }

                    // maxed out?
                    if (currentGenerationNumber >= MaxGenerations)
                    {
                        break;
                    }
                }

                // we may need to sort the current generation by fitness, depending on SelectionStyle
                if (needToSortByFitness)
                {
                    if (IsLowerFitnessBetter)
                    {
                        currentGeneration = currentGeneration.OrderBy(c => c.Fitness).ToList();
                    }
                    else
                    {
                        currentGeneration = currentGeneration.OrderByDescending(c => c.Fitness).ToList();
                    }
                }

                // depending on the SelectionStyle, we may need to adjust all candidate's fitness scores
                AdjustFitnessScores(currentGeneration);

                // Start building the next generation
                List <CandidateSolution <T, S> > nextGeneration = new List <CandidateSolution <T, S> >();

                // Elitism
                var theBest = currentGeneration.Take(numElitesToAdd);
                foreach (var peakPerformer in theBest)
                {
                    nextGeneration.Add(peakPerformer);
                }

                // now create a new generation using fitness scores for selection, and crossover and mutation
                while (nextGeneration.Count < PopulationSize)
                {
                    // select parents
                    CandidateSolution <T, S> parent1 = null, parent2 = null;
                    switch (SelectionStyle)
                    {
                    case SelectionStyle.Tourney:
                        parent1 = TournamentSelectParent();
                        parent2 = TournamentSelectParent();
                        break;

                    case SelectionStyle.RouletteWheel:
                    case SelectionStyle.Ranked:
                        parent1 = RouletteSelectParent();
                        parent2 = RouletteSelectParent();
                        break;
                    }

                    // cross them over to generate two new children
                    CandidateSolution <T, S> child1, child2;
                    CrossOverParents(parent1, parent2, out child1, out child2);

                    // Mutation
                    if (Randomizer.GetFloatFromZeroToOne() < MutationRate)
                    {
                        child1.Mutate();
                    }
                    if (Randomizer.GetFloatFromZeroToOne() < MutationRate)
                    {
                        child2.Mutate();
                    }

                    // then add to the new generation
                    nextGeneration.Add(child1);
                    nextGeneration.Add(child2);
                }

                // move to the next generation
                currentGeneration = nextGeneration;
                currentGenerationNumber++;
            }

            bestTreeAllTime.Root.SetCandidateRef(bestTreeAllTime);
            return(bestTreeAllTime);
        }