//------------------------------------------------------------------------- // For each generation, we get information about what's going on //------------------------------------------------------------------------- private bool PerGenerationCallback(EngineProgress progress) { string summary = "Generation " + progress.GenerationNumber + " best: " + progress.BestFitnessThisGen.ToString("0") + " avg: " + progress.AvgFitnessThisGen.ToString("0"); displayGenerationCallback(summary); Debug.WriteLine(summary); // return true to keep going, false to halt the system bool keepRunning = true; return(keepRunning); }
//------------------------------------------------------------------------- // For each generation, we get information about what's going on //------------------------------------------------------------------------- private bool PerGenerationCallback(EngineProgress progress, CandidateSolution <bool, ProblemState> bestThisGeneration) { string summary = "Upcard " + currentDealerUpcardRank + " gen: " + progress.GenerationNumber + " best: " + progress.BestFitnessThisGen.ToString("0") + " avg: " + progress.AvgFitnessThisGen.ToString("0"); perGenerationCallback(summary, bestThisGeneration); Debug.WriteLine(summary); // keep track of how many gens we've searched NumGenerationsNeeded++; // return true to keep going, false to halt the system bool keepRunning = true; return(keepRunning); }
public Strategy FindBestSolution() { // this code assumes that a "best" fitness is one with the highest fitness score float bestFitnessScoreAllTime = float.MinValue; float bestAverageFitnessScore = float.MinValue; int bestSolutionGenerationNumber = 0, bestAverageFitnessGenerationNumber = 0; // a list of references to objects in our pool List <Strategy> nextGeneration = new List <Strategy>(); // use a pool of candidates, so we aren't constantly creating and destroying pool = new StrategyPool(currentEngineParams.PopulationSize * 2); // *2 to cover this gen and next // elitism int numElitesToAdd = (int)(currentEngineParams.ElitismRate * currentEngineParams.PopulationSize); // depending on whether elitism is used, or the selection type, we may need to sort candidates by fitness (which is slower) bool needToSortByFitness = currentEngineParams.SelectionStyle == SelectionStyle.Roulette || currentEngineParams.SelectionStyle == SelectionStyle.Ranked || currentEngineParams.ElitismRate > 0; // initialize generation 0 with randomness for (int n = 0; n < currentEngineParams.PopulationSize; n++) { var strategy = pool.GetRandomized(); currentGeneration.Add(strategy); } // loop over generations Stopwatch stopwatch = new Stopwatch(); int currentGenerationNumber = 0; while (true) { stopwatch.Restart(); // for each candidate, find and store the fitness score Parallel.ForEach(currentGeneration, (candidate) => { // calc the fitness by calling the user-supplied function via the delegate candidate.Fitness = FitnessFunction(candidate); }); // now check if we have a new best float bestFitnessScoreThisGeneration = float.MinValue; Strategy bestSolutionThisGeneration = null; float totalFitness = 0; foreach (var candidate in currentGeneration) { totalFitness += candidate.Fitness; // find best of this generation, update best all-time if needed bool isBestThisGeneration = candidate.Fitness > bestFitnessScoreThisGeneration; if (isBestThisGeneration) { bestFitnessScoreThisGeneration = candidate.Fitness; bestSolutionThisGeneration = candidate; bool isBestEver = bestFitnessScoreThisGeneration > bestFitnessScoreAllTime; if (isBestEver) { bestFitnessScoreAllTime = bestFitnessScoreThisGeneration; BestSolution = candidate.Clone(); bestSolutionGenerationNumber = currentGenerationNumber; } } } // determine average fitness and store if it's all-time best float averageFitness = totalFitness / currentEngineParams.PopulationSize; 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 = stopwatch.Elapsed }; bool keepGoing = ProgressCallback(progress, bestSolutionThisGeneration); if (!keepGoing) { break; // user signalled to end looping } // termination conditions if (currentGenerationNumber >= currentEngineParams.MinGenerations) { // exit the loop if we're not making any progress in our average fitness score or our overall best score if (((currentGenerationNumber - bestAverageFitnessGenerationNumber) >= currentEngineParams.MaxStagnantGenerations) && ((currentGenerationNumber - bestSolutionGenerationNumber) >= currentEngineParams.MaxStagnantGenerations)) { break; } // maxed out? if (currentGenerationNumber >= currentEngineParams.MaxGenerations) { break; } } // depending on the SelectionStyle, we may need to adjust all candidate's fitness scores AdjustFitnessScores(needToSortByFitness); // Start building the next generation nextGeneration.Clear(); // Elitism var theBest = currentGeneration.Take(numElitesToAdd); foreach (var peakPerformer in theBest) { nextGeneration.Add(pool.CopyOf(peakPerformer)); } //// if we're doing elitism and the all-time best is from a previous generation, add it if ((numElitesToAdd > 0) && (currentGenerationNumber != bestSolutionGenerationNumber)) { nextGeneration.Add(pool.CopyOf(BestSolution)); } // then do the selection, crossover and mutation to populate the rest of the next generation var children = SelectCrossOverAndMutate(currentEngineParams.PopulationSize - nextGeneration.Count); nextGeneration.AddRange(children); // move to the next generation foreach (var strategy in currentGeneration) { pool.Release(strategy); } currentGeneration.Clear(); currentGeneration.AddRange(nextGeneration); currentGenerationNumber++; } return(BestSolution); }