/// <summary> /// Called before Optimize() to setup the optimizer. /// Always called in main thread which would allow for putting additional GUI in here. /// </summary> public override void Initialize() { // don't forget to call Initialize() on the base class before any custom handling base.Initialize(); bool optionWindowRequired = false; StrategyParameters = new Chromosomes(Strategy, true); if (StrategyParameters.HasBoolOrEnum) { optionWindowRequired = true; if (options == null) { options = new GoForm(StrategyParameters); } } if (optionWindowRequired && options.ShowDialog(Form.ActiveForm) != DialogResult.OK) { StrategyParameters = new Chromosomes(Strategy, true); } }
/// <summary> /// Runs the optimizer on a given parameter set. /// Beware: this method might be called in a non-main, worker thread. E.g. driving GUI logic from here forward would cause issues. /// </summary> public override void Optimize() { Individual supremeIndividual = null; try { var realStabilitySize = (int)Math.Ceiling(populationSize * stabilitySize); var realResetSize = (int)Math.Ceiling(populationSize * resetSize); var numberOfStrategyParameters = StrategyParameters.Count; var combinations = StrategyParameters.CountCombinations; var siblings = new Dictionary <Individual, bool>(); var children = new List <Individual>(); var parents = new List <Individual>(); var prevStabilityScore = double.NaN; // Start optimization process for (int generation = 1; generation <= maxGenerations; generation++) { //If Initial generation, seed the population with random individuals while (children.Count < populationSize && siblings.Count < combinations) { ValidateIndividual(children, siblings, CreateRandomIndividual()); } foreach (Individual individual in children) { if (UserAbort) { throw new AbortException(); } StrategyParameters.SetStrategyParameters(individual, Strategy, Parameters); RunIteration(IterationCompleted, individual); } WaitForIterationsCompleted(false); //Make sure that each individual completed its iteration parents.AddRange(children); parents.Sort(); Individual generationBest = parents[0]; //This is the best performing individual since stability reset if (parents.Count > 0 && generationBest != null && (supremeIndividual == null || supremeIndividual.Performance < generationBest.Performance)) { supremeIndividual = generationBest; } int stabilityIndividuals = Math.Max(1, Math.Min(parents.Count, realStabilitySize)); double stabilityScore = 0.0; for (int i = 0; i < stabilityIndividuals; i++) { stabilityScore += parents[i].Performance; } if (supremeIndividual != null && minimumPerformance > 0 && supremeIndividual.Performance > minimumPerformance) { break; } children.Clear(); //Clearing up space for the next generation if (siblings.Count >= combinations || generation == maxGenerations) { break; } //Repopulating the next generation. //If Population is stable, leave just best performing individuals if (stabilityScore == prevStabilityScore) { if (realResetSize <= 0) { parents.Clear(); } else if (parents.Count > 0) { parents = parents.GetRange(0, Math.Min(parents.Count, realResetSize)); } continue; } //Assign a weight for each parent RankPopulation(parents); //Reproduce extra individuals mutated from the best ShakeBestPerformer(children, siblings, combinations, generationBest, numberOfStrategyParameters); //Reproduce children using Crossover, Mutation and totally random genomes while (children.Count < populationSize && siblings.Count < combinations) { if (UserAbort) { throw new AbortException(); } if (random.NextDouble() <= crossoverRate) { int[] chromosomeIndexesForSon; int[] chromosomeIndexesForDaughter; //Produce two children from two parents //Parents are selected using Roulette Selection Method //Note: There are many other selection algorithms (Google them out...) //TODO: Implements some additional selection methods //Crossover is performed using single break point //TODO: implement 2 or 3 break points Crossover algorithms Crossover(numberOfStrategyParameters, parents[RouletteSelection(parents)], parents[RouletteSelection(parents)], out chromosomeIndexesForSon, out chromosomeIndexesForDaughter); for (var idx = 0; idx < numberOfStrategyParameters; idx++) { if (StrategyParameters[idx].Count <= 1) { continue; } //After children are produced from crossover, small part should be mutated if (random.NextDouble() <= mutationRate) { chromosomeIndexesForSon[idx] = Mutate(mutationStrength, StrategyParameters[idx].Count, chromosomeIndexesForSon[idx]); } if (random.NextDouble() <= mutationRate) { chromosomeIndexesForDaughter[idx] = Mutate(mutationStrength, StrategyParameters[idx].Count, chromosomeIndexesForDaughter[idx]); } } //Make sure that children are unique, so we wont waste any time for evaluation of their performance ValidateIndividual(children, siblings, new Individual(chromosomeIndexesForSon)); ValidateIndividual(children, siblings, new Individual(chromosomeIndexesForDaughter)); } else { //Create random children //Make sure that children are unique, so we wont waste any time for evaluation of their performance ValidateIndividual(children, siblings, CreateRandomIndividual()); ValidateIndividual(children, siblings, CreateRandomIndividual()); } } prevStabilityScore = stabilityScore; } } catch { } finally { WaitForIterationsCompleted(true); if (options != null) { options.Dispose(); options = null; } } }