public static void Main(String[] args) { GeneticSelectionProcess <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Chromosome <Boolean>, Boolean, ProcessInformation> .Builder builder = new GeneticSelectionProcess <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Chromosome <Boolean>, Boolean, ProcessInformation> .Builder(); Random rng = new Random(); Int32 genomeSize = 100; Int32 targetFitness = 90; Int32 fitnessDeltaTolerance = 10; Int32 populationSize = 1000; Double mutationRate = 0.01; GeneticSelectionProcess <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Chromosome <Boolean>, Boolean, ProcessInformation> process = builder .SetInitialPopulationSize(populationSize).UseDefaultConsoleLogger() .SetProcessInformationComposer( new ProcessInformationComposer <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean> , Boolean>()) .SetMatingFunction( new CrossOverMatingFunction <TestIndividual <Boolean>, Chromosome <Boolean>, Boolean>(3)) // new RandomCombinationMatingFunction< // TestIndividual<Boolean> // , Chromosome<Boolean> // , Boolean>()) .SetParentSelector( new SingleGroupFittestParentsSelector <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Boolean>()).SetFitnessFunction(new TestFitnessFunction()) .SetSurvivorSelector( new ReplaceLeastFitSelector <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Boolean>()) .SetPopulationInitializer( new BooleanRandomPopulationInitializer <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Chromosome <Boolean> >(genomeSize)) .AddTerminationCondition( new FitnessTerminator <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Boolean >(targetFitness)) .AddPopulationModifier( new RandomMutationModifier <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Boolean>(mutationRate, () => rng.Next(2) == 1)).Build(); process.Initialize(); process.RunUntilConvergence(); IndividualWithFitness <TestIndividual <Boolean>, Boolean> fittest = process.GetPopulation().GetFittest(); Double fittestFitness = fittest.GetFitness(); Boolean[] fittestGenome = fittest.GetIndividual().GetChromosome().GetGenome(); Console.WriteLine($"Max fitness {fittestFitness}"); Console.WriteLine($"Genome {string.Join("", fittestGenome.Select(b => b ? 1 : 0))}"); Console.WriteLine("Done"); }
public void TrivialMaximizeTruesProcessShouldRun() { Random rng = new Random(); Int32 genomeSize = 100; Int32 targetFitness = 90; Int32 populationSize = 100; Double mutationRate = 0.01; var matingFunctions = new List <IMatingFunction <TestIndividual <Boolean>, Chromosome <Boolean>, Boolean> > { new CrossOverMatingFunction <TestIndividual <Boolean>, Chromosome <Boolean>, Boolean>(1) , new CrossOverMatingFunction <TestIndividual <Boolean>, Chromosome <Boolean>, Boolean>(3) , new RandomCombinationMatingFunction <TestIndividual <Boolean>, Chromosome <Boolean>, Boolean>() }; foreach (IMatingFunction <TestIndividual <Boolean>, Chromosome <Boolean>, Boolean> matingFunction in matingFunctions) { var builder = new GeneticSelectionProcess <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Chromosome <Boolean>, Boolean, ProcessInformation> .Builder(); var process = builder.SetInitialPopulationSize(populationSize).UseDefaultConsoleLogger() .SetProcessInformationComposer( new ProcessInformationComposer <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Boolean>()).SetMatingFunction(matingFunction) .SetParentSelector( new SingleGroupFittestParentsSelector <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Boolean>()).SetFitnessFunction(new TestFitnessFunction()) .SetSurvivorSelector( new ReplaceLeastFitSelector <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Boolean>()) .SetPopulationInitializer( new BooleanRandomPopulationInitializer <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Chromosome <Boolean> >(genomeSize)) .AddTerminationCondition( new FitnessTerminator <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean>, Boolean>(targetFitness)) .AddPopulationModifier( new RandomMutationModifier <Population <TestIndividual <Boolean>, Boolean>, TestIndividual <Boolean> , Boolean>(mutationRate, () => rng.Next(2) == 1)).Build(); process.Initialize(); process.RunUntilConvergence(); IndividualWithFitness <TestIndividual <Boolean>, Boolean> fittest = process.GetPopulation().GetFittest(); Double fittestFitness = fittest.GetFitness(); Boolean[] fittestGenome = fittest.GetIndividual().GetChromosome().GetGenome(); Double expectedFitness = fittestGenome.Select(b => b ? 1 : 0).Sum(); Assert.True(fittestFitness >= 90); Assert.True(expectedFitness >= 90); Assert.True(expectedFitness.AreDoublesEqual(fittestFitness)); IList <ProcessInformation> history = process.GetHistory(); Assert.True(history.Count > 1); Assert.True(fittestFitness.AreDoublesEqual(history[history.Count - 1].FittestFitness)); } }
/// <summary> /// Run a single generation of the process. /// </summary> /// <exception cref="InvalidOperationException"></exception> // ReSharper disable always PossibleNullReferenceException public void RunGeneration() { #region Validation if (_population == null) { throw new InvalidOperationException($"{nameof(_population)} was null"); } if (_survivorSelector == null) { throw new InvalidOperationException($"{nameof(_survivorSelector)} was null"); } if (_populationModifiers == null) { throw new InvalidOperationException($"{nameof(_populationModifiers)} was null"); } if (_parentSelector == null) { throw new InvalidOperationException($"{nameof(_parentSelector)} was null"); } if (_processInformationComposer == null) { throw new InvalidOperationException($"{nameof(_processInformationComposer)} was null"); } #endregion _isRunning = true; UInt64 generationCount = _previousProcessInformation?.GetGeneration() ?? 0; LogDebug($"Starting generation {generationCount}"); // Select parents to produce new generation of offspring. LogDebug("Selecting parents"); IList <IEnumerable <TIndividual> > parentGroups = _parentSelector.GetParents(_population).ToList(); // Log parents and their fitness. Int32 parentGroupCount = 0; foreach (IEnumerable <TIndividual> parentGroup in parentGroups) { Double totalFitness = 0; IList <String> parentStrings = new List <String>(); foreach (TIndividual parent in parentGroup) { IndividualWithFitness <TIndividual, TGene> individualParent = _population.GetIndividualWithFitness(parent.GetGuid()); Double parentFitness = individualParent.GetFitness(); parentStrings.Add($"P {parent.GetGuidStringShort()} F {parentFitness}"); totalFitness += parentFitness; } LogInfo($"PG {parentGroupCount}, TF {totalFitness}, " + $"{string.Join(", ", parentStrings)}"); parentGroupCount++; } if (parentGroups == null || !parentGroups.Any()) { throw new InvalidOperationException("Failed to get parents"); } // Mate selected parents, produce offspring. LogDebug("Mating parents; creating offspring"); IList <TIndividual> offspring = parentGroups.Select(parentGroup => _matingFunction.Mate(parentGroup.ToList())).ToList(); // Log unmodified offspring fitness. foreach (TIndividual individualOffspring in offspring) { Double offspringFitness = _fitnessFunction.CalculateFitness(individualOffspring); LogInfo($"O {individualOffspring.GetGuidStringShort()} F {offspringFitness}"); } // Add new offspring to population. LogDebug("Selecting survivors; adding offspring to population"); _survivorSelector.AddOffspring(_population, offspring); // Apply population modifiers to population. LogDebug("Applying population modifier"); foreach (IPopulationModifier <TPopulation, TIndividual, TGene> populationModifier in _populationModifiers) { _population = populationModifier.ModifyPopulation(_population); } // Order by fitness. LogDebug("Ordering population by fitness"); _population.OrderByFitness(_fitnessFunction); // Create information object. TProcessInformation currentGenerationProcessInformation = _processInformationComposer.ComposeProcessInformation(_previousProcessInformation, _population); AddToHistory(currentGenerationProcessInformation); LogInfo($"G{generationCount} TF {currentGenerationProcessInformation.GetTotalFitness()}"); // Check if population has converged according to provided checks. if (!_hasConverged) { if (_terminationConditions.Any(terminationCondition => terminationCondition.ShouldTerminate(_population, currentGenerationProcessInformation))) { _hasConverged = true; LogInfo("Population has converged! Termination condition met!"); } } // Compose new process information object. _previousProcessInformation = currentGenerationProcessInformation; if (!_isRunningLocked) { _isRunning = false; } }