From caee33b973f75bd6f48e4125c1ea32336420ff60 Mon Sep 17 00:00:00 2001 From: Zvi Rosenfeld <50112103+ZviRosenfeld@users.noreply.github.com> Date: Sat, 9 Nov 2019 22:08:20 +0200 Subject: [PATCH] Components (#6) * Renamed IMutationManager to IMutationProbabilityManager * Added ProbabilityUtils * Small fixes * Initial components * Added components PopulationGenerators * Added SelectKRandomNumbers method * Added K_PointCrossover * Added SinglePointCrossoverManager * Added UniformCrossoverManager * Added BitStringMutationManager * Added UniformMutationManager * Removed NumberVector project * Added documentation * Update README.md * Added GaussianMutationManager * Added DoubleVectorChromosomePopulationGenerator * Added "Int" to the int mutation managers * Added DoubleBoundaryMutationManagerTest * Added DoubleGaussianMutationManager and DoubleUniformMutationManager * Changed Bit Vector Chromosomes to use bools * Fixed bug * Added Shrink Mutation Managers --- EnvironmentGui/CrossoverManager.cs | 6 +- EnvironmentGui/MyChromosome.cs | 2 - EnvironmentGui/PopulationGenerator.cs | 5 +- GUI/BasicEvaluator.cs | 13 + GUI/GUI.csproj | 5 +- GUI/MainForm.cs | 17 +- GUI/Program.cs | 3 - .../ChildrenGeneratorTests.cs | 10 +- .../Components/CrossoverManagerTests.cs | 156 +++++++++ .../Components/MutationManagerTests.cs | 310 ++++++++++++++++++ .../Components/PopulationGeneratorsTests.cs | 127 +++++++ .../GeneticAlgorithm.UnitTests.csproj | 1 - ...cs => MutationProbabilityManagersTests.cs} | 4 +- .../NumberVectorTests.cs | 50 ++- .../ProbabilityUtilsTests.cs | 87 +++++ .../TestUtils/Assertions.cs | 6 + .../TestUtils/BasicEvaluator.cs | 13 + GeneticAlgorithm.UnitTests/TestUtils/Utils.cs | 10 + .../UpdatePopulationTests.cs | 4 +- GeneticAlgorithm.sln | 6 - GeneticAlgorithm/ChildrenGenerator.cs | 9 +- .../Chromosomes/VectorChromosome.cs | 37 +++ .../K_PointCrossoverManager.cs | 55 ++++ .../SinglePointCrossoverManager.cs | 22 ++ .../UniformCrossoverManager.cs | 40 +++ .../Components/CrossoverManagers/Utils.cs | 22 ++ .../Components/Interfaces/IEvaluator.cs | 9 + .../Components/Interfaces/IMutationManager.cs | 7 + .../BitStringMutationManager.cs | 22 ++ .../DoubleBoundaryMutationManager.cs | 29 ++ .../DoubleGaussianMutationManager.cs | 42 +++ .../DoubleShrinkMutationManager.cs | 40 +++ .../DoubleUniformMutationManager.cs | 31 ++ .../IntBoundaryMutationManager.cs | 29 ++ .../IntGaussianMutationManager.cs | 42 +++ .../IntShrinkMutationManager.cs | 40 +++ .../IntUniformMutationManager.cs | 31 ++ ...naryVectorChromosomePopulationGenerator.cs | 40 +++ ...ubleVectorChromosomePopulationGenerator.cs | 52 +++ .../IntVectorChromosomePopulationGenerator.cs | 52 +++ .../GeneticSearchEngineBuilder.cs | 6 +- GeneticAlgorithm/GeneticSearchOptions.cs | 4 +- ...ager.cs => IMutationProbabilityManager.cs} | 2 +- .../BassicMutationProbabilityManager.cs} | 4 +- .../ConvergenceMutationProbabilityManager.cs} | 2 +- GeneticAlgorithm/ProbabilityUtils.cs | 58 ++++ GeneticAlgorithm/SearchUtils.cs | 10 + NumberVector/NumberVector.csproj | 13 - NumberVector/NumberVectorChromosome.cs | 40 --- NumberVector/NumberVectorCrossoverManager.cs | 23 -- .../NumberVectorPopulationGenerator.cs | 34 -- README.md | 112 ++++++- 52 files changed, 1605 insertions(+), 189 deletions(-) create mode 100644 GUI/BasicEvaluator.cs create mode 100644 GeneticAlgorithm.UnitTests/Components/CrossoverManagerTests.cs create mode 100644 GeneticAlgorithm.UnitTests/Components/MutationManagerTests.cs create mode 100644 GeneticAlgorithm.UnitTests/Components/PopulationGeneratorsTests.cs rename GeneticAlgorithm.UnitTests/{MutationManagersTests.cs => MutationProbabilityManagersTests.cs} (93%) create mode 100644 GeneticAlgorithm.UnitTests/ProbabilityUtilsTests.cs create mode 100644 GeneticAlgorithm.UnitTests/TestUtils/BasicEvaluator.cs create mode 100644 GeneticAlgorithm/Components/Chromosomes/VectorChromosome.cs create mode 100644 GeneticAlgorithm/Components/CrossoverManagers/K_PointCrossoverManager.cs create mode 100644 GeneticAlgorithm/Components/CrossoverManagers/SinglePointCrossoverManager.cs create mode 100644 GeneticAlgorithm/Components/CrossoverManagers/UniformCrossoverManager.cs create mode 100644 GeneticAlgorithm/Components/CrossoverManagers/Utils.cs create mode 100644 GeneticAlgorithm/Components/Interfaces/IEvaluator.cs create mode 100644 GeneticAlgorithm/Components/Interfaces/IMutationManager.cs create mode 100644 GeneticAlgorithm/Components/MutationManagers/BitStringMutationManager.cs create mode 100644 GeneticAlgorithm/Components/MutationManagers/DoubleBoundaryMutationManager.cs create mode 100644 GeneticAlgorithm/Components/MutationManagers/DoubleGaussianMutationManager.cs create mode 100644 GeneticAlgorithm/Components/MutationManagers/DoubleShrinkMutationManager.cs create mode 100644 GeneticAlgorithm/Components/MutationManagers/DoubleUniformMutationManager.cs create mode 100644 GeneticAlgorithm/Components/MutationManagers/IntBoundaryMutationManager.cs create mode 100644 GeneticAlgorithm/Components/MutationManagers/IntGaussianMutationManager.cs create mode 100644 GeneticAlgorithm/Components/MutationManagers/IntShrinkMutationManager.cs create mode 100644 GeneticAlgorithm/Components/MutationManagers/IntUniformMutationManager.cs create mode 100644 GeneticAlgorithm/Components/PopulationGenerators/BinaryVectorChromosomePopulationGenerator.cs create mode 100644 GeneticAlgorithm/Components/PopulationGenerators/DoubleVectorChromosomePopulationGenerator.cs create mode 100644 GeneticAlgorithm/Components/PopulationGenerators/IntVectorChromosomePopulationGenerator.cs rename GeneticAlgorithm/Interfaces/{IMutationManager.cs => IMutationProbabilityManager.cs} (93%) rename GeneticAlgorithm/{MutationManagers/BassicMutationManager.cs => MutationProbabilityManagers/BassicMutationProbabilityManager.cs} (81%) rename GeneticAlgorithm/{MutationManagers/ConvergenceMutationManager.cs => MutationProbabilityManagers/ConvergenceMutationProbabilityManager.cs} (95%) create mode 100644 GeneticAlgorithm/ProbabilityUtils.cs delete mode 100644 NumberVector/NumberVector.csproj delete mode 100644 NumberVector/NumberVectorChromosome.cs delete mode 100644 NumberVector/NumberVectorCrossoverManager.cs delete mode 100644 NumberVector/NumberVectorPopulationGenerator.cs diff --git a/EnvironmentGui/CrossoverManager.cs b/EnvironmentGui/CrossoverManager.cs index d76ec7d..43dd9aa 100644 --- a/EnvironmentGui/CrossoverManager.cs +++ b/EnvironmentGui/CrossoverManager.cs @@ -1,12 +1,10 @@ -using System; +using GeneticAlgorithm; using GeneticAlgorithm.Interfaces; namespace Environment { class CrossoverManager : ICrossoverManager { - private readonly Random random = new Random(); - public IChromosome Crossover(IChromosome chromosome1, IChromosome chromosome2) { var type1 = ((MyChromosome) chromosome1).Type; @@ -17,7 +15,7 @@ public IChromosome Crossover(IChromosome chromosome1, IChromosome chromosome2) if (type1 == ChromosomeType.Oc2Producer && type2 == ChromosomeType.Oc2Producer) return new MyChromosome(ChromosomeType.Oc2Producer); - return new MyChromosome(random.NextDouble() < 0.5 ? ChromosomeType.OProducer : ChromosomeType.Oc2Producer); + return new MyChromosome(ProbabilityUtils.P(0.5) ? ChromosomeType.OProducer : ChromosomeType.Oc2Producer); } } } diff --git a/EnvironmentGui/MyChromosome.cs b/EnvironmentGui/MyChromosome.cs index 5b1681c..8347397 100644 --- a/EnvironmentGui/MyChromosome.cs +++ b/EnvironmentGui/MyChromosome.cs @@ -5,8 +5,6 @@ namespace Environment { class MyChromosome : IChromosome { - private readonly Random random = new Random(); - public ChromosomeType Type { get; private set; } public MyChromosome(ChromosomeType type) diff --git a/EnvironmentGui/PopulationGenerator.cs b/EnvironmentGui/PopulationGenerator.cs index 1ddc70f..fbb8201 100644 --- a/EnvironmentGui/PopulationGenerator.cs +++ b/EnvironmentGui/PopulationGenerator.cs @@ -1,18 +1,17 @@ using System; using System.Collections.Generic; +using GeneticAlgorithm; using GeneticAlgorithm.Interfaces; namespace Environment { class PopulationGenerator : IPopulationGenerator { - private readonly Random random = new Random(); - public IEnumerable GeneratePopulation(int size) { var chromosomes = new IChromosome[size]; for (int i = 0; i < size; i++) - chromosomes[i] = new MyChromosome(random.NextDouble() < 0.5 + chromosomes[i] = new MyChromosome(ProbabilityUtils.P(0.5) ? ChromosomeType.OProducer : ChromosomeType.Oc2Producer); diff --git a/GUI/BasicEvaluator.cs b/GUI/BasicEvaluator.cs new file mode 100644 index 0000000..fa0e49c --- /dev/null +++ b/GUI/BasicEvaluator.cs @@ -0,0 +1,13 @@ +using System.Linq; +using GeneticAlgorithm.Components.Chromosomes; +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Interfaces; + +namespace GUI +{ + class BasicEvaluator : IEvaluator + { + public double Evaluate(IChromosome chromosome) => + ((VectorChromosome) chromosome).GetVector().Sum(); + } +} diff --git a/GUI/GUI.csproj b/GUI/GUI.csproj index 2f026f4..31d4c05 100644 --- a/GUI/GUI.csproj +++ b/GUI/GUI.csproj @@ -46,6 +46,7 @@ + UserControl @@ -94,10 +95,6 @@ {5fadb04c-ee9d-4169-81a1-441ff61da671} GeneticAlgorithm - - {e84d6bd7-ff0d-4125-9e5c-36462247613a} - NumberVector - {77d37cc0-b19f-46a4-96c0-b9e49c3daf5e} UserControls diff --git a/GUI/MainForm.cs b/GUI/MainForm.cs index b46c725..9438655 100644 --- a/GUI/MainForm.cs +++ b/GUI/MainForm.cs @@ -1,12 +1,15 @@ using System.Windows.Forms; using GeneticAlgorithm; -using GreatestVectorTests; +using GeneticAlgorithm.Components.CrossoverManagers; +using GeneticAlgorithm.Components.MutationManagers; +using GeneticAlgorithm.Components.PopulationGenerators; namespace GUI { public partial class MainForm : Form { private const int POPULATION_SIZE = 20; + private const int VECTOR_SIZE = 10; private const int GENERATION = int.MaxValue; @@ -18,10 +21,14 @@ public MainForm() private void InitializeEngine() { - var engineBuilder = new GeneticSearchEngineBuilder(POPULATION_SIZE, GENERATION, - new NumberVectorCrossoverManager(), - new NumberVectorPopulationGenerator()).SetMutationProbability(MutationInputBox.GetValue) - .SetElitePercentage(ElitismInputBox.GetValue); + var mutationManager = new IntUniformMutationManager(0, 100); + var evaluator = new BasicEvaluator(); + var populationGenerator = + new IntVectorChromosomePopulationGenerator(VECTOR_SIZE, 0, 1, mutationManager, evaluator); + var crossoverManager = new SinglePointCrossoverManager(mutationManager, evaluator); + var engineBuilder = + new GeneticSearchEngineBuilder(POPULATION_SIZE, GENERATION, crossoverManager, populationGenerator) + .SetMutationProbability(MutationInputBox.GetValue).SetElitePercentage(ElitismInputBox.GetValue); searchRunner1.SetEngineBuilder(engineBuilder); } diff --git a/GUI/Program.cs b/GUI/Program.cs index b6a443a..cf59b03 100644 --- a/GUI/Program.cs +++ b/GUI/Program.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using System.Windows.Forms; namespace GUI diff --git a/GeneticAlgorithm.UnitTests/ChildrenGeneratorTests.cs b/GeneticAlgorithm.UnitTests/ChildrenGeneratorTests.cs index 46d3017..27a899e 100644 --- a/GeneticAlgorithm.UnitTests/ChildrenGeneratorTests.cs +++ b/GeneticAlgorithm.UnitTests/ChildrenGeneratorTests.cs @@ -27,7 +27,7 @@ public void TestMutationsHappen(double mutationProbability) var crossoverManager = A.Fake(); A.CallTo(() => crossoverManager.Crossover(A._, A._)) .ReturnsLazily((IChromosome c1, IChromosome c2) => c1); - var childrenGenerator = new ChildrenGenerator(crossoverManager, new BassicMutationManager(mutationProbability), new RouletteWheelSelection()); + var childrenGenerator = new ChildrenGenerator(crossoverManager, new BassicMutationProbabilityManager(mutationProbability), new RouletteWheelSelection()); childrenGenerator.GenerateChildren(population, populationSize, 0, null); const double errorMargin = 0.05; @@ -44,7 +44,7 @@ public void RetusnRightNumberOfChromosomes(int childrenCount) { const int count = 1500; var crossoverManager = A.Fake(); - var childrenGenerator = new ChildrenGenerator(crossoverManager, new BassicMutationManager(0), new RouletteWheelSelection()); + var childrenGenerator = new ChildrenGenerator(crossoverManager, new BassicMutationProbabilityManager(0), new RouletteWheelSelection()); var children = childrenGenerator.GenerateChildren(GetPopulation(count), childrenCount, 0, null); Assert.AreEqual(childrenCount, children.Length, "Didn't get enough children"); foreach (var chromosome in children) @@ -57,7 +57,7 @@ public void RetusnRightNumberOfChromosomes(int childrenCount) [ExpectedException(typeof(BadMutationProbabilityException))] public void BadMutationProbability_ThrowException(double probability) { - var mutationManager = A.Fake(); + var mutationManager = A.Fake(); A.CallTo(() => mutationManager.MutationProbability(A._, A._, A._)) .Returns(probability); @@ -71,7 +71,7 @@ public void BadMutationProbability_ThrowException(double probability) [DataRow(-1)] public void RequestBadNumberOfChildren_ThrowsException(int childrenCount) { - var childrenGenerator = new ChildrenGenerator(A.Fake(), new BassicMutationManager(0), + var childrenGenerator = new ChildrenGenerator(A.Fake(), new BassicMutationProbabilityManager(0), new RouletteWheelSelection()); childrenGenerator.GenerateChildren(GetPopulation(1), childrenCount, 0, null); } @@ -83,7 +83,7 @@ public void SelectionStrategyReturnsNull_ThrowsException() { var selectionStrategy = A.Fake(); A.CallTo(() => selectionStrategy.SelectChromosome()).Returns(null); - var childrenGenerator = new ChildrenGenerator(A.Fake(), new BassicMutationManager(0), + var childrenGenerator = new ChildrenGenerator(A.Fake(), new BassicMutationProbabilityManager(0), selectionStrategy); childrenGenerator.GenerateChildren(GetPopulation(1), 1, 0, null); }, typeof(GeneticAlgorithmException)); diff --git a/GeneticAlgorithm.UnitTests/Components/CrossoverManagerTests.cs b/GeneticAlgorithm.UnitTests/Components/CrossoverManagerTests.cs new file mode 100644 index 0000000..7daa36d --- /dev/null +++ b/GeneticAlgorithm.UnitTests/Components/CrossoverManagerTests.cs @@ -0,0 +1,156 @@ +using System.Collections.Generic; +using System.Linq; +using FakeItEasy; +using GeneticAlgorithm.Components.Chromosomes; +using GeneticAlgorithm.Components.CrossoverManagers; +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Components.PopulationGenerators; +using GeneticAlgorithm.Interfaces; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace GeneticAlgorithm.UnitTests.Components +{ + [TestClass] + public class CrossoverManagerTests + { + private const int TEST_RUNS = 20; + private const int SMALL_CHROMOSOME_SIZE = 10; + private const int LARGE_CHROMOSOME_SIZE = 20; + + private readonly IPopulationGenerator smallPopulationGenerator1 = + new IntVectorChromosomePopulationGenerator(SMALL_CHROMOSOME_SIZE, 0, 10, A.Fake>(), + A.Fake()); + private readonly IPopulationGenerator smallPopulationGenerator2 = + new IntVectorChromosomePopulationGenerator(SMALL_CHROMOSOME_SIZE, 11, 20, A.Fake>(), + A.Fake()); + private readonly IPopulationGenerator largePopulationGenerator = + new IntVectorChromosomePopulationGenerator(LARGE_CHROMOSOME_SIZE, 21, 30, A.Fake>(), + A.Fake()); + + [TestMethod] + [DataRow(1)] + [DataRow(2)] + [DataRow(5)] + [DataRow(SMALL_CHROMOSOME_SIZE - 1)] + public void K_PointCrossoverTest(int k) + { + var crossoverManager = new K_PointCrossoverManager(k, A.Fake>(), A.Fake()); + for (int i = 0; i < TEST_RUNS; i++) + { + var chromosome1 = (VectorChromosome) smallPopulationGenerator1.GeneratePopulation(1).First(); + var chromosome2 = (VectorChromosome) smallPopulationGenerator2.GeneratePopulation(1).First(); + var newChromosome = (VectorChromosome) crossoverManager.Crossover(chromosome1, chromosome2); + + var crossoverPoints = K_CrossoverGetCrossoverPointsAndAssertThatGenomesAreRight(newChromosome, chromosome2, chromosome1); + Assert.AreEqual(k, crossoverPoints.Count, + $"Found wrong number of crossoverPoints. 1: {chromosome1}; 2 {chromosome2}; newChromosome {newChromosome}"); + } + } + + [TestMethod] + [DataRow(1)] + [DataRow(2)] + [DataRow(5)] + [DataRow(SMALL_CHROMOSOME_SIZE - 1)] + public void K_PointCrossover_ShortAndLongChromosomes(int k) + { + var crossoverManager = new K_PointCrossoverManager(k, A.Fake>(), A.Fake()); + for (int i = 0; i < TEST_RUNS; i++) + { + var chromosome1 = (VectorChromosome)smallPopulationGenerator1.GeneratePopulation(1).First(); + var chromosome2 = (VectorChromosome)largePopulationGenerator.GeneratePopulation(1).First(); + var newChromosome = (VectorChromosome)crossoverManager.Crossover(chromosome1, chromosome2); + + var crossoverPoints = K_CrossoverGetCrossoverPointsAndAssertThatGenomesAreRight(newChromosome, chromosome2, chromosome1); + Assert.AreEqual(k, crossoverPoints.Count, + $"Found wrong number of crossoverPoints. 1: {chromosome1}; 2 {chromosome2}; newChromosome {newChromosome}"); + } + } + + [TestMethod] + public void K_PointCrossover_CrossoverPointsAreDiffrent() + { + var crossoverPoints = new List(); + var crossoverManager = new K_PointCrossoverManager(2, A.Fake>(), A.Fake()); + for (int i = 0; i < 100; i++) + { + var chromosome1 = (VectorChromosome)smallPopulationGenerator1.GeneratePopulation(1).First(); + var chromosome2 = (VectorChromosome)smallPopulationGenerator2.GeneratePopulation(1).First(); + var newChromosome = (VectorChromosome)crossoverManager.Crossover(chromosome1, chromosome2); + + crossoverPoints.AddRange(K_CrossoverGetCrossoverPointsAndAssertThatGenomesAreRight(newChromosome, chromosome2, chromosome1)); + } + + for (int i = 1; i < SMALL_CHROMOSOME_SIZE; i++) + Assert.IsTrue(crossoverPoints.Contains(i), $"{nameof(crossoverPoints)} dosn't contain {i}"); + } + + [TestMethod] + public void SinglePointCrossoverManagerTest() + { + var crossoverManager = new SinglePointCrossoverManager(A.Fake>(), A.Fake()); + for (int i = 0; i < TEST_RUNS; i++) + { + var chromosome1 = (VectorChromosome)smallPopulationGenerator1.GeneratePopulation(1).First(); + var chromosome2 = (VectorChromosome)smallPopulationGenerator2.GeneratePopulation(1).First(); + var newChromosome = (VectorChromosome)crossoverManager.Crossover(chromosome1, chromosome2); + + var crossoverPoints = K_CrossoverGetCrossoverPointsAndAssertThatGenomesAreRight(newChromosome, chromosome2, chromosome1); + Assert.AreEqual(1, crossoverPoints.Count, + $"Found wrong number of crossoverPoints. 1: {chromosome1}; 2 {chromosome2}; newChromosome {newChromosome}"); + } + } + + [TestMethod] + public void UniformCrossoverManagerTest() + { + var crossoverManager = new UniformCrossoverManager(A.Fake>(), A.Fake()); + for (int i = 0; i < TEST_RUNS; i++) + { + var chromosome1 = (VectorChromosome)smallPopulationGenerator1.GeneratePopulation(1).First(); + var chromosome2 = (VectorChromosome)smallPopulationGenerator2.GeneratePopulation(1).First(); + var newChromosome = (VectorChromosome)crossoverManager.Crossover(chromosome1, chromosome2); + + K_CrossoverGetCrossoverPointsAndAssertThatGenomesAreRight(newChromosome, chromosome2, chromosome1); + } + } + + private static List K_CrossoverGetCrossoverPointsAndAssertThatGenomesAreRight(VectorChromosome newChromosome, VectorChromosome chromosome2, + VectorChromosome chromosome1) + { + var crossoverPoints = new List(); + var takingFromFirstChromosome = true; + for (int i = 0; i < SMALL_CHROMOSOME_SIZE; i++) + { + if (takingFromFirstChromosome) + { + if (newChromosome[i] == chromosome2[i]) + { + crossoverPoints.Add(i); + takingFromFirstChromosome = !takingFromFirstChromosome; + } + else if (newChromosome[i] != chromosome1[i]) + Assert.Fail( + $"Got Genome that dosn't seem to have came from anywhere. 1: {chromosome1}; 2 {chromosome2}; newChromosome {newChromosome} "); + } + else + { + if (newChromosome[i] == chromosome1[i]) + { + crossoverPoints.Add(i); + takingFromFirstChromosome = !takingFromFirstChromosome; + } + else if (newChromosome[i] != chromosome2[i]) + Assert.Fail( + $"Got Genome that dosn't seem to have came from anywhere. 1: {chromosome1}; 2 {chromosome2}; newChromosome {newChromosome} "); + } + } + for (int j = SMALL_CHROMOSOME_SIZE; j < LARGE_CHROMOSOME_SIZE && j < newChromosome.GetVector().Length; j++) + { + Assert.AreEqual(chromosome2[j], newChromosome[j]); + } + + return crossoverPoints; + } + } +} diff --git a/GeneticAlgorithm.UnitTests/Components/MutationManagerTests.cs b/GeneticAlgorithm.UnitTests/Components/MutationManagerTests.cs new file mode 100644 index 0000000..3996c01 --- /dev/null +++ b/GeneticAlgorithm.UnitTests/Components/MutationManagerTests.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Components.MutationManagers; +using GeneticAlgorithm.UnitTests.TestUtils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace GeneticAlgorithm.UnitTests.Components +{ + [TestClass] + public class MutationManagerTests + { + private const int attempts = 1000; + + [TestMethod] + public void IntBoundaryMutationManager_MutationHappensWithRightProbability() + { + var mutationManager = new IntBoundaryMutationManager(-1, 1); + var minGenomes = 0; + var maxGenomes = 0; + for (int i = 0; i < attempts; i++) + { + var newChromosome = mutationManager.Mutate(new[] {0, 0, 0, 0, 0}); + foreach (var genome in newChromosome) + { + if (genome == -1) minGenomes++; + if (genome == 1) maxGenomes++; + } + } + + minGenomes.AssertIsWithinRange(attempts / 2.0, attempts * 0.1); + maxGenomes.AssertIsWithinRange(attempts / 2.0, attempts * 0.1); + } + + [TestMethod] + public void DoubleBoundaryMutationManager_MutationHappensWithRightProbability() + { + var mutationManager = new DoubleBoundaryMutationManager(-1.5, 1.5); + var minGenomes = 0; + var maxGenomes = 0; + for (int i = 0; i < attempts; i++) + { + var newChromosome = mutationManager.Mutate(new double[] { 0, 0, 0, 0, 0 }); + foreach (var genome in newChromosome) + { + if (genome == -1.5) minGenomes++; + if (genome == 1.5) maxGenomes++; + } + } + + minGenomes.AssertIsWithinRange(attempts / 2.0, attempts * 0.1); + maxGenomes.AssertIsWithinRange(attempts / 2.0, attempts * 0.1); + } + + [TestMethod] + public void IntUniformMutationManager_MutationHappensWithRightProbability() + { + var mutationManager = new IntUniformMutationManager(-100, 100); + CheckMutationsHappenWithRightProbability(mutationManager, g => g != 0); + } + + [TestMethod] + public void IntUniformMutationManager_AllValuesGenerated() + { + var minValue = -5; + var maxValue = 5; + var mutationManager = new IntUniformMutationManager(minValue, maxValue); + AssertAllValuesAreGenerated(maxValue, minValue, mutationManager); + } + + [TestMethod] + public void IntUniformMutationManager_AllValuesWithinRange() + { + var minValue = -5; + var maxValue = 5; + var mutationManager = new IntUniformMutationManager(minValue, maxValue); + AssertAllValuesAreWithinRange(mutationManager, maxValue, minValue); + } + + [TestMethod] + public void DoubleUniformMutationManager_MutationHappensWithRightProbability() + { + var mutationManager = new DoubleUniformMutationManager(-100, 100); + CheckMutationsHappenWithRightProbability(mutationManager, g => g != 0); + } + + [TestMethod] + public void DoubleUniformMutationManager_AllValuesGenerated() + { + var minValue = -5; + var maxValue = 5; + var mutationManager = new DoubleUniformMutationManager(minValue, maxValue); + AssertAllValuesAreGenerated(maxValue, minValue, mutationManager); + } + + [TestMethod] + public void DoubleUniformMutationManager_AllValuesWithinRange() + { + var minValue = -5; + var maxValue = 5; + var mutationManager = new DoubleUniformMutationManager(minValue, maxValue); + AssertAllValuesAreWithinRange(mutationManager, maxValue, minValue); + } + + [TestMethod] + public void IntGaussianMutationManager_MutationHappensWithRightProbability() + { + var mutationManager = new IntGaussianMutationManager(-100, 100); + CheckMutationsHappenWithRightProbability(mutationManager, g => g != 0); + } + + [TestMethod] + public void IntGaussianMutationManager_AllValuesWithinRange() + { + var minValue = -5; + var maxValue = 5; + var mutationManager = new IntGaussianMutationManager(minValue, maxValue); + AssertAllValuesAreWithinRange(mutationManager, maxValue, minValue); + } + + [TestMethod] + public void IntGaussianMutationManager_CommandValuesAreMoeLikely() + { + var minValue = -11; + var maxValue = 11; + var mutationManager = new IntGaussianMutationManager(minValue, maxValue); + AssertCommonValuesAreMoreLikely(maxValue / 2.0, mutationManager); + } + + [TestMethod] + public void IntGaussianMutationManager_AssertValuesAreScattered() + { + var minValue = -11; + var maxValue = 11; + var mutationManager = new IntGaussianMutationManager(minValue, maxValue); + AssertValuesAreScattered(mutationManager); + } + + [TestMethod] + public void DoubleGaussianMutationManager_MutationHappensWithRightProbability() + { + var mutationManager = new DoubleGaussianMutationManager(-100, 100); + CheckMutationsHappenWithRightProbability(mutationManager, g => g != 0); + } + + [TestMethod] + public void DoubleGaussianMutationManager_AllValuesWithinRange() + { + var minValue = -5; + var maxValue = 5; + var mutationManager = new DoubleGaussianMutationManager(minValue, maxValue); + AssertAllValuesAreWithinRange(mutationManager, maxValue, minValue); + } + + [TestMethod] + public void DoubleGaussianMutationManager_CommandValuesAreMoeLikely() + { + var minValue = -31; + var maxValue = 31; + var mutationManager = new DoubleGaussianMutationManager(minValue, maxValue); + AssertCommonValuesAreMoreLikely(maxValue / 2.0, mutationManager); + } + + [TestMethod] + public void DoubleGaussianMutationManager_AssertValuesAreScattered() + { + var minValue = -11; + var maxValue = 11; + var mutationManager = new DoubleGaussianMutationManager(minValue, maxValue); + AssertValuesAreScattered(mutationManager); + } + + [TestMethod] + public void DoubleShrinkMutationManager_MutationHappensWithRightProbability() + { + var mutationManager = new DoubleShrinkMutationManager(-100, 100); + CheckMutationsHappenWithRightProbability(mutationManager, g => g != 0); + } + + [TestMethod] + public void DoubleShrinkMutationManager_AllValuesWithinRange() + { + var minValue = -5; + var maxValue = 5; + var mutationManager = new DoubleShrinkMutationManager(minValue, maxValue); + AssertAllValuesAreWithinRange(mutationManager, maxValue, minValue); + } + + [TestMethod] + public void DoubleShrinkMutationManager_CommandValuesAreMoeLikely() + { + var minValue = -11; + var maxValue = 11; + var mutationManager = new DoubleShrinkMutationManager(minValue, maxValue); + AssertCommonValuesAreMoreLikely(maxValue / 2.0, mutationManager); + } + + [TestMethod] + public void DoubleGaussianMutationManager_DoubleShrinkMutationManager() + { + var minValue = -11; + var maxValue = 11; + var mutationManager = new DoubleShrinkMutationManager(minValue, maxValue); + AssertValuesAreScattered(mutationManager); + } + + [TestMethod] + public void IntShrinkMutationManager_MutationHappensWithRightProbability() + { + var mutationManager = new IntShrinkMutationManager(-100, 100); + CheckMutationsHappenWithRightProbability(mutationManager, g => g != 0); + } + + [TestMethod] + public void IntShrinkMutationManager_AllValuesWithinRange() + { + var minValue = -5; + var maxValue = 5; + var mutationManager = new IntShrinkMutationManager(minValue, maxValue); + AssertAllValuesAreWithinRange(mutationManager, maxValue, minValue); + } + + [TestMethod] + public void IntShrinkMutationManager_CommandValuesAreMoeLikely() + { + var minValue = -21; + var maxValue = 21; + var mutationManager = new IntShrinkMutationManager(minValue, maxValue); + AssertCommonValuesAreMoreLikely(maxValue / 2.0, mutationManager); + } + + [TestMethod] + public void IntGaussianMutationManager_DoubleShrinkMutationManager() + { + var minValue = -11; + var maxValue = 11; + var mutationManager = new IntShrinkMutationManager(minValue, maxValue); + AssertValuesAreScattered(mutationManager); + } + + [TestMethod] + public void BitStringMutationManager_MutationHappensWithRightProbability() + { + var mutationManager = new BitStringMutationManager(); + CheckMutationsHappenWithRightProbability(mutationManager, g => g); + } + + private static void CheckMutationsHappenWithRightProbability(IMutationManager mutationManager, Func isMutated) + { + var mutatedGenomes = 0; + for (int i = 0; i < attempts; i++) + { + var newChromosome = mutationManager.Mutate(new[] + {default(T), default(T), default(T), default(T), default(T)}); + mutatedGenomes += newChromosome.Count(isMutated); + } + + mutatedGenomes.AssertIsWithinRange(attempts, attempts * 0.1); + } + + private static void AssertAllValuesAreWithinRange(IMutationManager mutationManager, int maxValue, int minValue) + { + for (int i = 0; i < attempts; i++) + { + var value = mutationManager.Mutate(new[] { default(T) }).First().ToInt(); + Assert.IsTrue(value <= maxValue, $"{nameof(value)} ({value}) > {nameof(maxValue)} ({maxValue})"); + Assert.IsTrue(value >= minValue, $"{nameof(value)} ({value}) < {nameof(minValue)} ({minValue})"); + } + } + + private static void AssertAllValuesAreGenerated(int maxValue, int minValue, IMutationManager mutationManager) + { + var gottenValues = new HashSet(); + var runs = maxValue - minValue; + for (int i = 0; i < runs * 5; i++) + gottenValues.Add(mutationManager.Mutate(new[] { default(T) }).First().ToInt() + 5); + + for (int i = 1; i < runs; i++) + Assert.IsTrue(gottenValues.Contains(i), $"We didn't get {i}"); + } + + private static void AssertCommonValuesAreMoreLikely(double bourderValue, IMutationManager mutationManager) + { + var smallCount = 0; + var bigCount = 0; + for (int i = 0; i < 10; i++) + { + var value = Math.Abs(mutationManager.Mutate(new[] {default(T)}).First().ToInt()); + Console.WriteLine(value); + if (value >= bourderValue) bigCount++; + else smallCount++; + } + + Assert.IsTrue(smallCount > bigCount, "Got to many big genomes"); + } + + private static void AssertValuesAreScattered(IMutationManager mutationManager) + { + var values = new HashSet(); + for (int i = 0; i < 50; i++) + { + var value = mutationManager.Mutate(new[] { default(T) }).First().ToInt(); + values.Add(value.ToInt()); + } + + Assert.IsTrue(values.Count > 8, $"We only got {values.Count} diffrent values"); + } + } +} diff --git a/GeneticAlgorithm.UnitTests/Components/PopulationGeneratorsTests.cs b/GeneticAlgorithm.UnitTests/Components/PopulationGeneratorsTests.cs new file mode 100644 index 0000000..97368e0 --- /dev/null +++ b/GeneticAlgorithm.UnitTests/Components/PopulationGeneratorsTests.cs @@ -0,0 +1,127 @@ +using System.Collections.Generic; +using System.Linq; +using FakeItEasy; +using GeneticAlgorithm.Components.Chromosomes; +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Components.PopulationGenerators; +using GeneticAlgorithm.Exceptions; +using GeneticAlgorithm.Interfaces; +using GeneticAlgorithm.UnitTests.TestUtils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace GeneticAlgorithm.UnitTests.Components +{ + [TestClass] + public class PopulationGeneratorsTests + { + [TestMethod] + [DataRow(-1)] + [DataRow(0)] + [ExpectedException(typeof(GeneticAlgorithmException))] + public void IntVectorChromosomePopulationGenerator_BadChromosomeSize_ThrowException(int chromosomeSize) => + new IntVectorChromosomePopulationGenerator(chromosomeSize, 0, 10, A.Fake>(), + A.Fake()); + + [TestMethod] + [ExpectedException(typeof(GeneticAlgorithmException))] + public void IntVectorChromosomePopulationGenerator_MinGenomeSmallerThanMaxGenome_ThrowException() => + new IntVectorChromosomePopulationGenerator(10, 10, 0, A.Fake>(), + A.Fake()); + + [TestMethod] + [DataRow(1)] + [DataRow(10)] + public void IntVectorChromosomePopulationGenerator_CreatesChromosomeOfRightSize(int chromosomeSize) + { + var populationGenerator = + new IntVectorChromosomePopulationGenerator(chromosomeSize, 0, 10, A.Fake>(), A.Fake()); + var vector = (VectorChromosome) populationGenerator.GeneratePopulation(1).First(); + Assert.AreEqual(chromosomeSize, vector.GetVector().Length); + } + + [TestMethod] + [DataRow(-10, 10)] + public void IntVectorChromosomePopulationGenerator_CreatesChromosomeOfWithRightValues(int minGenome, int maxGenome) + { + var populationGenerator = + new IntVectorChromosomePopulationGenerator(10, minGenome, maxGenome, A.Fake>(), A.Fake()); + TestChromosomes(minGenome, maxGenome, populationGenerator); + } + + [TestMethod] + [DataRow(-1)] + [DataRow(0)] + [ExpectedException(typeof(GeneticAlgorithmException))] + public void DoubleVectorChromosomePopulationGenerator_BadChromosomeSize_ThrowException(int chromosomeSize) => + new DoubleVectorChromosomePopulationGenerator(chromosomeSize, 0, 10, A.Fake>(), + A.Fake()); + + [TestMethod] + [ExpectedException(typeof(GeneticAlgorithmException))] + public void DoubleVectorChromosomePopulationGenerator_MinGenomeSmallerThanMaxGenome_ThrowException() => + new DoubleVectorChromosomePopulationGenerator(10, 10, 0, A.Fake>(), + A.Fake()); + + [TestMethod] + [DataRow(1)] + [DataRow(10)] + public void DoubleVectorChromosomePopulationGenerator_CreatesChromosomeOfRightSize(int chromosomeSize) + { + var populationGenerator = + new DoubleVectorChromosomePopulationGenerator(chromosomeSize, 0, 10, A.Fake>(), A.Fake()); + var vector = (VectorChromosome)populationGenerator.GeneratePopulation(1).First(); + Assert.AreEqual(chromosomeSize, vector.GetVector().Length); + } + + [TestMethod] + [DataRow(-10, 10)] + public void DoubleVectorChromosomePopulationGenerator_CreatesChromosomeOfWithRightValues(int minGenome, int maxGenome) + { + var populationGenerator = + new DoubleVectorChromosomePopulationGenerator(10, minGenome, maxGenome, A.Fake>(), A.Fake()); + TestChromosomes(minGenome + 1, maxGenome - 1, populationGenerator); + } + + [TestMethod] + public void BinaryVectorChromosomePopulationGenerator_AllValuesCreated() + { + var populationGenerator = + new BinaryVectorChromosomePopulationGenerator(5, A.Fake>(), A.Fake()); + var vector = ((VectorChromosome) populationGenerator.GeneratePopulation(1).First()).GetVector(); + Assert.IsTrue(vector.Contains(true), "Vector dosn't contain true"); + Assert.IsTrue(vector.Contains(false), "Vector dosn't contain false"); + } + + [TestMethod] + public void BinaryVectorChromosomePopulationGenerator_VectorsAreRightSize() + { + var vecotrSize = 5; + var populationGenerator = + new BinaryVectorChromosomePopulationGenerator(vecotrSize, A.Fake>(), A.Fake()); + var vector = ((VectorChromosome)populationGenerator.GeneratePopulation(1).First()).GetVector(); + Assert.AreEqual(vecotrSize, vector.Length); + } + + /// + /// Test that the genomes in the chromosome are within range, and that they are dispersed. + /// + private static void TestChromosomes(int minGenome, int maxGenome, IPopulationGenerator populationGenerator) + { + var chromosomes = populationGenerator.GeneratePopulation(10); + var recivedGenomes = new HashSet(); + foreach (var chromosome in chromosomes) + { + var vectorChromosome = (VectorChromosome)chromosome; + foreach (var genome in vectorChromosome.GetVector()) + { + var intGenome = genome.ToInt(); + Assert.IsTrue(intGenome >= minGenome, $"Got a genome smaller than {minGenome} ({intGenome})"); + Assert.IsTrue(intGenome <= maxGenome, $"Got a genome bigger than {maxGenome} ({intGenome})"); + recivedGenomes.Add(intGenome); + } + } + for (int i = minGenome; i < maxGenome; i++) + Assert.IsTrue(recivedGenomes.Contains(i), $"Didn't get {i}"); + } + } +} diff --git a/GeneticAlgorithm.UnitTests/GeneticAlgorithm.UnitTests.csproj b/GeneticAlgorithm.UnitTests/GeneticAlgorithm.UnitTests.csproj index adcfe33..db3da38 100644 --- a/GeneticAlgorithm.UnitTests/GeneticAlgorithm.UnitTests.csproj +++ b/GeneticAlgorithm.UnitTests/GeneticAlgorithm.UnitTests.csproj @@ -15,7 +15,6 @@ - diff --git a/GeneticAlgorithm.UnitTests/MutationManagersTests.cs b/GeneticAlgorithm.UnitTests/MutationProbabilityManagersTests.cs similarity index 93% rename from GeneticAlgorithm.UnitTests/MutationManagersTests.cs rename to GeneticAlgorithm.UnitTests/MutationProbabilityManagersTests.cs index 5371f13..018f063 100644 --- a/GeneticAlgorithm.UnitTests/MutationManagersTests.cs +++ b/GeneticAlgorithm.UnitTests/MutationProbabilityManagersTests.cs @@ -5,12 +5,12 @@ namespace GeneticAlgorithm.UnitTests { [TestClass] - public class MutationManagersTests + public class MutationProbabilityManagersTests { [TestMethod] public void ConvergenceMutationManagerTest() { - var convergenceMutationManager = new ConvergenceMutationManager(); + var convergenceMutationManager = new ConvergenceMutationProbabilityManager(); var homogeneousPopulation = new double[] {2, 2, 2, 2, 2 }.ToPopulation().Evaluate(); var diversifiedPopulation = new double[] { 1, 2, 3, 4, 5 }.ToPopulation().Evaluate(); diff --git a/GeneticAlgorithm.UnitTests/NumberVectorTests.cs b/GeneticAlgorithm.UnitTests/NumberVectorTests.cs index 417fc72..57f998c 100644 --- a/GeneticAlgorithm.UnitTests/NumberVectorTests.cs +++ b/GeneticAlgorithm.UnitTests/NumberVectorTests.cs @@ -1,5 +1,9 @@ -using GeneticAlgorithm.UnitTests.TestUtils; -using GreatestVectorTests; +using GeneticAlgorithm.Components.CrossoverManagers; +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Components.MutationManagers; +using GeneticAlgorithm.Components.PopulationGenerators; +using GeneticAlgorithm.Interfaces; +using GeneticAlgorithm.UnitTests.TestUtils; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace GeneticAlgorithm.UnitTests @@ -7,38 +11,49 @@ namespace GeneticAlgorithm.UnitTests [TestClass] public class NumberVectorTests { + private const int VECTOR_SIZE = 10; private const int POPULATION_SIZE = 100; + private readonly IMutationManager mutationManager; + private readonly IEvaluator evaluator; + private readonly IPopulationGenerator populationGenerator; + private readonly ICrossoverManager crossoverManager; + + public NumberVectorTests() + { + mutationManager = new IntUniformMutationManager(0, 100); + evaluator = new BasicEvaluator(); + populationGenerator = new IntVectorChromosomePopulationGenerator(VECTOR_SIZE, 0, 1, mutationManager, evaluator); + crossoverManager = new SinglePointCrossoverManager(mutationManager, evaluator); + } [TestMethod] [DataRow(RunType.Run)] [DataRow(RunType.Next)] public void BassicTest(RunType runType) { - var searchEngine = new GeneticSearchEngineBuilder(POPULATION_SIZE, 50, new NumberVectorCrossoverManager(), - new NumberVectorPopulationGenerator()) - .SetSelectionStrategy(new AssertRequestedChromosomesIsRightSelectionWrapper()).Build(); + var searchEngine = + new GeneticSearchEngineBuilder(POPULATION_SIZE, 50, crossoverManager, populationGenerator) + .SetSelectionStrategy(new AssertRequestedChromosomesIsRightSelectionWrapper()).Build(); var result = searchEngine.Run(runType); - Assert.AreEqual(NumberVectorPopulationGenerator.VECTOR_SIZE, result.BestChromosome.Evaluate()); + Assert.AreEqual(VECTOR_SIZE, result.BestChromosome.Evaluate()); Assert.AreEqual(50, result.Generations, "Wrong number of generations"); } [TestMethod] [DataRow(RunType.Run)] - [DataRow(RunType.Next)] public void MutationTest(RunType runType) { - var searchEngine = new GeneticSearchEngineBuilder(POPULATION_SIZE, 50, new NumberVectorCrossoverManager(), - new NumberVectorPopulationGenerator()) - .SetSelectionStrategy(new AssertRequestedChromosomesIsRightSelectionWrapper()) - .SetMutationProbability(0.1) - .Build(); + var searchEngine = + new GeneticSearchEngineBuilder(POPULATION_SIZE, 50, crossoverManager, populationGenerator) + .SetSelectionStrategy(new AssertRequestedChromosomesIsRightSelectionWrapper()) + .SetMutationProbability(0.1).IncludeAllHistory().Build(); var result = searchEngine.Run(runType); - Assert.IsTrue(NumberVectorPopulationGenerator.VECTOR_SIZE < result.BestChromosome.Evaluate(), - $"best result ({result.BestChromosome.Evaluate()}) should have been greater than {NumberVectorPopulationGenerator.VECTOR_SIZE}"); + Assert.IsTrue(VECTOR_SIZE < result.BestChromosome.Evaluate(), + $"best result ({result.BestChromosome.Evaluate()}) should have been greater than {VECTOR_SIZE}. Chromosome is {result.BestChromosome}"); } [TestMethod] @@ -47,9 +62,10 @@ public void MutationTest(RunType runType) [DataRow(0.219)] public void RequestedChromosomesIsRightWithElite(double elite) { - var searchEngine = new GeneticSearchEngineBuilder(POPULATION_SIZE, 50, new NumberVectorCrossoverManager(), - new NumberVectorPopulationGenerator()).SetElitePercentage(elite) - .SetSelectionStrategy(new AssertRequestedChromosomesIsRightSelectionWrapper()).Build(); + var searchEngine = + new GeneticSearchEngineBuilder(POPULATION_SIZE, 50, crossoverManager, populationGenerator) + .SetElitePercentage(elite) + .SetSelectionStrategy(new AssertRequestedChromosomesIsRightSelectionWrapper()).Build(); searchEngine.Next(); } diff --git a/GeneticAlgorithm.UnitTests/ProbabilityUtilsTests.cs b/GeneticAlgorithm.UnitTests/ProbabilityUtilsTests.cs new file mode 100644 index 0000000..fbf7eb8 --- /dev/null +++ b/GeneticAlgorithm.UnitTests/ProbabilityUtilsTests.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using GeneticAlgorithm.Exceptions; +using GeneticAlgorithm.UnitTests.TestUtils; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; + +namespace GeneticAlgorithm.UnitTests +{ + [TestClass] + public class ProbabilityUtilsTests + { + [TestMethod] + [DataRow(0)] + [DataRow(1)] + [DataRow(0.2)] + [DataRow(0.5)] + public void P_Test(double probability) + { + var trueCount = 0; + var falseCount = 0; + var attempts = 1000; + var margernOnError = attempts * 0.1; + for (int i = 0; i < attempts; i++) + { + if (ProbabilityUtils.P(probability)) + trueCount++; + else + falseCount++; + } + + trueCount.AssertIsWithinRange(attempts * probability, margernOnError); + falseCount.AssertIsWithinRange(attempts * (1 - probability), margernOnError); + } + + [TestMethod] + [DataRow(-0.1)] + [DataRow(1.1)] + [ExpectedException(typeof(InternalSearchException), "code 1008")] + public void P_ExceptionsTest(double probability) => + ProbabilityUtils.P(probability); + + [TestMethod] + [DataRow(5, 5)] + [DataRow(10, 1)] + [DataRow(10, 3)] + public void SelectKRandomNumbers_KNumbersSelectedTillTill(int till, int k) + { + var numbers = ProbabilityUtils.SelectKRandomNumbers(till, k); + + Assert.AreEqual(k, numbers.Count, "Didn't get enough numbers"); + foreach (var number in numbers) + Assert.AreEqual(1, numbers.Count(n => n == number), "Found the same index more then once"); + } + + [TestMethod] + public void SelectKRandomNumbers_NumbersAreRandom() + { + var till = 20; + var allNumbers = new List(); + for (int i = 0; i < till * 5; i++) + allNumbers.AddRange(ProbabilityUtils.SelectKRandomNumbers(till, 2)); + + for (int i = 0; i < till; i++) + Assert.IsTrue(allNumbers.Contains(i), $"{nameof(allNumbers)} dosn't contain {i}"); + } + + [TestMethod] + [DataRow(0, 1)] + [DataRow(10, 5)] + [DataRow(40, 5)] + public void GaussianDistribution_CommonNumbersAreMoreLikely(double mean, double sd) + { + var smallCount = 0; + var bigCount = 0; + for (int i = 0; i < 100; i++) + { + var value = ProbabilityUtils.GaussianDistribution(sd, mean); + Console.WriteLine(value); + if (value >= mean + sd || value <= mean - sd) bigCount++; + else smallCount++; + } + + Assert.IsTrue(smallCount > bigCount, $"Got to many big genomes. Big numberes = {bigCount}; small numbers = {smallCount}"); + } + } +} diff --git a/GeneticAlgorithm.UnitTests/TestUtils/Assertions.cs b/GeneticAlgorithm.UnitTests/TestUtils/Assertions.cs index 3ff1ff9..c15db59 100644 --- a/GeneticAlgorithm.UnitTests/TestUtils/Assertions.cs +++ b/GeneticAlgorithm.UnitTests/TestUtils/Assertions.cs @@ -66,5 +66,11 @@ public static void AssertThrowAggretateExceptionOfType(Action action, Type excep Assert.AreEqual(exceptionType, e.InnerExceptions[0].GetType()); } } + + public static void AssertIsWithinRange(this int value, double expactedValue, double margenOfError) + { + Assert.IsTrue(value >= expactedValue - margenOfError, $"Value ({value}) is too small (expacted value = {expactedValue})"); + Assert.IsTrue(value <= expactedValue + margenOfError, $"Value ({value}) is too big (expacted value = {expactedValue})"); + } } } diff --git a/GeneticAlgorithm.UnitTests/TestUtils/BasicEvaluator.cs b/GeneticAlgorithm.UnitTests/TestUtils/BasicEvaluator.cs new file mode 100644 index 0000000..65fd538 --- /dev/null +++ b/GeneticAlgorithm.UnitTests/TestUtils/BasicEvaluator.cs @@ -0,0 +1,13 @@ +using System.Linq; +using GeneticAlgorithm.Components.Chromosomes; +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Interfaces; + +namespace GeneticAlgorithm.UnitTests.TestUtils +{ + class BasicEvaluator : IEvaluator + { + public double Evaluate(IChromosome chromosome) => + ((VectorChromosome)chromosome).GetVector().Sum(); + } +} diff --git a/GeneticAlgorithm.UnitTests/TestUtils/Utils.cs b/GeneticAlgorithm.UnitTests/TestUtils/Utils.cs index 05f8046..85c9ea0 100644 --- a/GeneticAlgorithm.UnitTests/TestUtils/Utils.cs +++ b/GeneticAlgorithm.UnitTests/TestUtils/Utils.cs @@ -30,5 +30,15 @@ public static GeneticSearchEngine GetBassicEngine() var engineBuilder = new TestGeneticSearchEngineBuilder(5, int.MaxValue, populationManager); return engineBuilder.Build(); } + + /// + /// Converts a value that we know is a double or an int to an int. + /// + public static int ToInt(this T value) + { + var intGenome = value as int?; + var doubleGenoe = value as double?; + return intGenome ?? (int) doubleGenoe.Value; + } } } diff --git a/GeneticAlgorithm.UnitTests/UpdatePopulationTests.cs b/GeneticAlgorithm.UnitTests/UpdatePopulationTests.cs index b65c72f..6113af2 100644 --- a/GeneticAlgorithm.UnitTests/UpdatePopulationTests.cs +++ b/GeneticAlgorithm.UnitTests/UpdatePopulationTests.cs @@ -168,12 +168,12 @@ private GeneticSearchEngineBuilder CreateEngineBuilder(TestPopulationManager pop var populationRenwalManager = A.Fake(); A.CallTo(() => populationRenwalManager.AddGeneration(A._)) .Invokes((Population p) => populationUpdatedForRenewalManager.Save(p)); - var mutationManager = A.Fake(); + var mutationManager = A.Fake(); A.CallTo(() => mutationManager.AddGeneration(A._)) .Invokes((Population p) => populationUpdatedForMutationManager.Save(p)); var builder = new TestGeneticSearchEngineBuilder(2, generations, populationManager) .AddStopManager(stopManager) - .AddPopulationRenwalManager(populationRenwalManager).SetCustomMutationManager(mutationManager) + .AddPopulationRenwalManager(populationRenwalManager).SetCustomMutationProbabilityManager(mutationManager) .AddPopulationConverter(populationConverter); return builder; } diff --git a/GeneticAlgorithm.sln b/GeneticAlgorithm.sln index d7ed8f2..00a5567 100644 --- a/GeneticAlgorithm.sln +++ b/GeneticAlgorithm.sln @@ -15,8 +15,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserControls", "UserControl EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvironmentGui", "EnvironmentGui\EnvironmentGui.csproj", "{B68B3A55-9825-410A-B892-D710481CE834}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NumberVector", "NumberVector\NumberVector.csproj", "{E84D6BD7-FF0D-4125-9E5C-36462247613A}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -47,10 +45,6 @@ Global {B68B3A55-9825-410A-B892-D710481CE834}.Debug|Any CPU.Build.0 = Debug|Any CPU {B68B3A55-9825-410A-B892-D710481CE834}.Release|Any CPU.ActiveCfg = Release|Any CPU {B68B3A55-9825-410A-B892-D710481CE834}.Release|Any CPU.Build.0 = Release|Any CPU - {E84D6BD7-FF0D-4125-9E5C-36462247613A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E84D6BD7-FF0D-4125-9E5C-36462247613A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E84D6BD7-FF0D-4125-9E5C-36462247613A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E84D6BD7-FF0D-4125-9E5C-36462247613A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GeneticAlgorithm/ChildrenGenerator.cs b/GeneticAlgorithm/ChildrenGenerator.cs index 9fdf9ca..36b2d84 100644 --- a/GeneticAlgorithm/ChildrenGenerator.cs +++ b/GeneticAlgorithm/ChildrenGenerator.cs @@ -17,12 +17,11 @@ public class ChildrenGenerator : IChildrenGenerator typeof(StochasticUniversalSampling), typeof(TournamentSelection) }; - private readonly Random random = new Random(); private readonly ICrossoverManager crossoverManager; - private readonly IMutationManager mutationManager; + private readonly IMutationProbabilityManager mutationManager; private readonly ISelectionStrategy selectionStrategy; - public ChildrenGenerator(ICrossoverManager crossoverManager, IMutationManager mutationManager, ISelectionStrategy selectionStrategy) + public ChildrenGenerator(ICrossoverManager crossoverManager, IMutationProbabilityManager mutationManager, ISelectionStrategy selectionStrategy) { this.crossoverManager = crossoverManager; this.mutationManager = mutationManager; @@ -46,7 +45,7 @@ public IChromosome[] GenerateChildren(Population population, int number, int gen var parent1 = AssertNotNull(selectionStrategy.SelectChromosome()); var parent2 = AssertNotNull(selectionStrategy.SelectChromosome()); var child = crossoverManager.Crossover(parent1, parent2); - if (random.NextDouble() < mutationProbability) + if (ProbabilityUtils.P(mutationProbability)) child.Mutate(); children.Add(child); }); @@ -61,7 +60,7 @@ private void CheckMuationProbability(double probability) { if (probability >= 0 && probability <= 1) return; - if (mutationManager.GetType() == typeof(BassicMutationManager)) + if (mutationManager.GetType() == typeof(BassicMutationProbabilityManager)) throw new InternalSearchException( $"Code 1004 (Bad mutation value for manager {mutationManager.GetType()})"); throw new BadMutationProbabilityException(probability); diff --git a/GeneticAlgorithm/Components/Chromosomes/VectorChromosome.cs b/GeneticAlgorithm/Components/Chromosomes/VectorChromosome.cs new file mode 100644 index 0000000..2dc229b --- /dev/null +++ b/GeneticAlgorithm/Components/Chromosomes/VectorChromosome.cs @@ -0,0 +1,37 @@ +using System.Text; +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Interfaces; + +namespace GeneticAlgorithm.Components.Chromosomes +{ + public class VectorChromosome : IChromosome + { + private T[] vector; + private readonly IMutationManager mutationManager; + private readonly IEvaluator evaluator; + + public VectorChromosome(T[] vector, IMutationManager mutationManager, IEvaluator evaluator) + { + this.vector = vector; + this.mutationManager = mutationManager; + this.evaluator = evaluator; + } + + public double Evaluate() => evaluator.Evaluate(this); + + public void Mutate() => vector = mutationManager.Mutate(vector); + + public T[] GetVector() => vector; + + public T this[int index] => vector[index]; + + public override string ToString() + { + var stringBuilder = new StringBuilder(); + foreach (var value in vector) + stringBuilder.Append(value + ", "); + + return stringBuilder.ToString(); + } + } +} diff --git a/GeneticAlgorithm/Components/CrossoverManagers/K_PointCrossoverManager.cs b/GeneticAlgorithm/Components/CrossoverManagers/K_PointCrossoverManager.cs new file mode 100644 index 0000000..1313bd0 --- /dev/null +++ b/GeneticAlgorithm/Components/CrossoverManagers/K_PointCrossoverManager.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using GeneticAlgorithm.Components.Chromosomes; +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Interfaces; + +namespace GeneticAlgorithm.Components.CrossoverManagers +{ + /// + /// K crossover points are picked randomly from the parent chromosomes. The bits in between these points are swapped between the parent organisms. + /// See: https://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)#Two-point_and_k-point_crossover + /// + public class K_PointCrossoverManager : ICrossoverManager + { + private readonly int k; + private readonly IMutationManager mutationManager; + private readonly IEvaluator evaluator; + + public K_PointCrossoverManager(int k, IMutationManager mutationManager, IEvaluator evaluator) + { + this.k = k; + this.mutationManager = mutationManager; + this.evaluator = evaluator; + } + + public IChromosome Crossover(IChromosome chromosome1, IChromosome chromosome2) + { + (var shortVectorChromosome, var longVectorChromosome) = + Utils.OrderChromosomes(chromosome1, chromosome2); + + var crossoverPionts = ChooseCrossoverPoints(k, shortVectorChromosome.GetVector().Length); + + var takingFromShortChromosome = true; + var newVector = new T[longVectorChromosome.GetVector().Length]; + var index = 0; + for (; index < shortVectorChromosome.GetVector().Length; index++) + { + newVector[index] = takingFromShortChromosome + ? shortVectorChromosome.GetVector()[index] + : longVectorChromosome.GetVector()[index]; + if (crossoverPionts.Contains(index)) + takingFromShortChromosome = !takingFromShortChromosome; + } + for (; index < longVectorChromosome.GetVector().Length; index++) + newVector[index] = longVectorChromosome.GetVector()[index]; + + return new VectorChromosome(newVector, mutationManager, evaluator); + } + + private List ChooseCrossoverPoints(int k, int length) + { + var crossoverPoints = k < length ? k : length - 1; + return ProbabilityUtils.SelectKRandomNumbers(length - 1, crossoverPoints); + } + } +} diff --git a/GeneticAlgorithm/Components/CrossoverManagers/SinglePointCrossoverManager.cs b/GeneticAlgorithm/Components/CrossoverManagers/SinglePointCrossoverManager.cs new file mode 100644 index 0000000..efe7a50 --- /dev/null +++ b/GeneticAlgorithm/Components/CrossoverManagers/SinglePointCrossoverManager.cs @@ -0,0 +1,22 @@ +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Interfaces; + +namespace GeneticAlgorithm.Components.CrossoverManagers +{ + /// + /// A point on both parents' chromosomes is picked randomly, and designated a 'crossover point'. Bits to the right of that point are swapped between the two parent chromosomes. + /// See: https://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)#Single-point_crossover + /// + public class SinglePointCrossoverManager : ICrossoverManager + { + private readonly K_PointCrossoverManager kPointCrossover; + + public SinglePointCrossoverManager(IMutationManager mutationManager, IEvaluator evaluator) + { + kPointCrossover = new K_PointCrossoverManager(1, mutationManager, evaluator); + } + + public IChromosome Crossover(IChromosome chromosome1, IChromosome chromosome2) => + kPointCrossover.Crossover(chromosome1, chromosome2); + } +} diff --git a/GeneticAlgorithm/Components/CrossoverManagers/UniformCrossoverManager.cs b/GeneticAlgorithm/Components/CrossoverManagers/UniformCrossoverManager.cs new file mode 100644 index 0000000..a70ba42 --- /dev/null +++ b/GeneticAlgorithm/Components/CrossoverManagers/UniformCrossoverManager.cs @@ -0,0 +1,40 @@ +using GeneticAlgorithm.Components.Chromosomes; +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Interfaces; + +namespace GeneticAlgorithm.Components.CrossoverManagers +{ + /// + /// In uniform crossover, each bit is chosen from either parent with equal probability. + /// + public class UniformCrossoverManager : ICrossoverManager + { + private readonly IMutationManager mutationManager; + private readonly IEvaluator evaluator; + + public UniformCrossoverManager(IMutationManager mutationManager, IEvaluator evaluator) + { + this.mutationManager = mutationManager; + this.evaluator = evaluator; + } + + public IChromosome Crossover(IChromosome chromosome1, IChromosome chromosome2) + { + (var shortVectorChromosome, var longVectorChromosome) = + Utils.OrderChromosomes(chromosome1, chromosome2); + + var newVector = new T[longVectorChromosome.GetVector().Length]; + var index = 0; + for (; index < shortVectorChromosome.GetVector().Length; index++) + { + newVector[index] = ProbabilityUtils.P(0.5) + ? shortVectorChromosome.GetVector()[index] + : longVectorChromosome.GetVector()[index]; + } + for (; index < longVectorChromosome.GetVector().Length; index++) + newVector[index] = longVectorChromosome.GetVector()[index]; + + return new VectorChromosome(newVector, mutationManager, evaluator); + } + } +} diff --git a/GeneticAlgorithm/Components/CrossoverManagers/Utils.cs b/GeneticAlgorithm/Components/CrossoverManagers/Utils.cs new file mode 100644 index 0000000..fc1e246 --- /dev/null +++ b/GeneticAlgorithm/Components/CrossoverManagers/Utils.cs @@ -0,0 +1,22 @@ +using GeneticAlgorithm.Components.Chromosomes; +using GeneticAlgorithm.Interfaces; + +namespace GeneticAlgorithm.Components.CrossoverManagers +{ + static class Utils + { + /// + /// Returns the shroter chromorose as the first, and the longer chromosome second + /// + public static (VectorChromosome, VectorChromosome) OrderChromosomes(IChromosome chromosome1, + IChromosome chromosome2) + { + var vectorChromosome1 = (VectorChromosome) chromosome1; + var vectorChromosome2 = (VectorChromosome) chromosome2; + return vectorChromosome1.GetVector().Length <= vectorChromosome2.GetVector().Length + ? (vectorChromosome1, vectorChromosome2) + : (vectorChromosome2, vectorChromosome1); + } + + } +} diff --git a/GeneticAlgorithm/Components/Interfaces/IEvaluator.cs b/GeneticAlgorithm/Components/Interfaces/IEvaluator.cs new file mode 100644 index 0000000..310e14b --- /dev/null +++ b/GeneticAlgorithm/Components/Interfaces/IEvaluator.cs @@ -0,0 +1,9 @@ +using GeneticAlgorithm.Interfaces; + +namespace GeneticAlgorithm.Components.Interfaces +{ + public interface IEvaluator + { + double Evaluate(IChromosome chromosome); + } +} diff --git a/GeneticAlgorithm/Components/Interfaces/IMutationManager.cs b/GeneticAlgorithm/Components/Interfaces/IMutationManager.cs new file mode 100644 index 0000000..eb1d63c --- /dev/null +++ b/GeneticAlgorithm/Components/Interfaces/IMutationManager.cs @@ -0,0 +1,7 @@ +namespace GeneticAlgorithm.Components.Interfaces +{ + public interface IMutationManager + { + T[] Mutate(T[] vector); + } +} diff --git a/GeneticAlgorithm/Components/MutationManagers/BitStringMutationManager.cs b/GeneticAlgorithm/Components/MutationManagers/BitStringMutationManager.cs new file mode 100644 index 0000000..ec98f92 --- /dev/null +++ b/GeneticAlgorithm/Components/MutationManagers/BitStringMutationManager.cs @@ -0,0 +1,22 @@ +using GeneticAlgorithm.Components.Interfaces; + +namespace GeneticAlgorithm.Components.MutationManagers +{ + /// + /// This mutation only works on binary chromosomes. It flips bits at random (that is replaces 1 with 0 and 0 with 1). + /// The probability of a bit being flipped is 1 / . + /// + public class BitStringMutationManager : IMutationManager + { + public bool[] Mutate(bool[] vector) + { + for (int i = 0; i < vector.Length; i++) + { + if (ProbabilityUtils.P(1.0 / vector.Length)) + vector[i] = !vector[i]; + } + + return vector; + } + } +} diff --git a/GeneticAlgorithm/Components/MutationManagers/DoubleBoundaryMutationManager.cs b/GeneticAlgorithm/Components/MutationManagers/DoubleBoundaryMutationManager.cs new file mode 100644 index 0000000..81c71cb --- /dev/null +++ b/GeneticAlgorithm/Components/MutationManagers/DoubleBoundaryMutationManager.cs @@ -0,0 +1,29 @@ +using GeneticAlgorithm.Components.Interfaces; + +namespace GeneticAlgorithm.Components.MutationManagers +{ + /// + /// This mutation operator replaces the genome with either lower or upper bound randomly. + /// The probability of a bit being replaced is 1 / . + /// + public class DoubleBoundaryMutationManager : IMutationManager + { + private readonly double minValue; + private readonly double maxValue; + + public DoubleBoundaryMutationManager(double minValue, double maxValue) + { + this.minValue = minValue; + this.maxValue = maxValue; + } + + public double[] Mutate(double[] vector) + { + for (int i = 0; i < vector.Length; i++) + if (ProbabilityUtils.P(1.0 / vector.Length)) + vector[i] = ProbabilityUtils.P(0.5) ? maxValue : minValue; + + return vector; + } + } +} diff --git a/GeneticAlgorithm/Components/MutationManagers/DoubleGaussianMutationManager.cs b/GeneticAlgorithm/Components/MutationManagers/DoubleGaussianMutationManager.cs new file mode 100644 index 0000000..b539e0c --- /dev/null +++ b/GeneticAlgorithm/Components/MutationManagers/DoubleGaussianMutationManager.cs @@ -0,0 +1,42 @@ +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Exceptions; + +namespace GeneticAlgorithm.Components.MutationManagers +{ + /// + /// This operator adds a unit Gaussian distributed random value to the chosen gene. + /// If it falls outside of the user-specified lower or upper bounds for that gene, the new gene value is clipped. + /// + public class DoubleGaussianMutationManager : IMutationManager + { + private readonly double minValue; + private readonly double maxValue; + private readonly double standardDeviation; + + public DoubleGaussianMutationManager(double minValue, double maxValue) + { + this.minValue = minValue; + this.maxValue = maxValue; + standardDeviation = (maxValue - minValue) / 4.0; + } + + public DoubleGaussianMutationManager(double minValue, double maxValue, double standardDeviation) + { + this.minValue = minValue; + this.maxValue = maxValue; + if (standardDeviation < 0) + throw new GeneticAlgorithmException($"{nameof(standardDeviation)} can't be nagitave"); + this.standardDeviation = standardDeviation; + } + + public double[] Mutate(double[] vector) + { + var mean = (maxValue + minValue) / 2.0; + for (int i = 0; i < vector.Length; i++) + if (ProbabilityUtils.P(1.0 / vector.Length)) + vector[i] = ProbabilityUtils.GaussianDistribution(standardDeviation, mean).Clip(minValue, maxValue); + + return vector; + } + } +} diff --git a/GeneticAlgorithm/Components/MutationManagers/DoubleShrinkMutationManager.cs b/GeneticAlgorithm/Components/MutationManagers/DoubleShrinkMutationManager.cs new file mode 100644 index 0000000..8d15bd3 --- /dev/null +++ b/GeneticAlgorithm/Components/MutationManagers/DoubleShrinkMutationManager.cs @@ -0,0 +1,40 @@ +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Exceptions; + +namespace GeneticAlgorithm.Components.MutationManagers +{ + /// + /// This operator adds a random number taken from a Gaussian distribution with mean equal to the original genome. + /// + public class DoubleShrinkMutationManager : IMutationManager + { + private readonly double minValue; + private readonly double maxValue; + private readonly double standardDeviation; + + public DoubleShrinkMutationManager(double minValue, double maxValue) + { + this.minValue = minValue; + this.maxValue = maxValue; + standardDeviation = (maxValue - minValue) / 4.0; + } + + public DoubleShrinkMutationManager(double minValue, double maxValue, double standardDeviation) + { + this.minValue = minValue; + this.maxValue = maxValue; + if (standardDeviation < 0) + throw new GeneticAlgorithmException($"{nameof(standardDeviation)} can't be nagitave"); + this.standardDeviation = standardDeviation; + } + + public double[] Mutate(double[] vector) + { + for (int i = 0; i < vector.Length; i++) + if (ProbabilityUtils.P(1.0 / vector.Length)) + vector[i] = ProbabilityUtils.GaussianDistribution(standardDeviation, vector[i]).Clip(minValue, maxValue); + + return vector; + } + } +} diff --git a/GeneticAlgorithm/Components/MutationManagers/DoubleUniformMutationManager.cs b/GeneticAlgorithm/Components/MutationManagers/DoubleUniformMutationManager.cs new file mode 100644 index 0000000..cff8ab2 --- /dev/null +++ b/GeneticAlgorithm/Components/MutationManagers/DoubleUniformMutationManager.cs @@ -0,0 +1,31 @@ +using System; +using GeneticAlgorithm.Components.Interfaces; + +namespace GeneticAlgorithm.Components.MutationManagers +{ + /// + /// This mutation operator replaces the genome with a random value between the lower and upper bound. + /// The probability of a bit being replaced is 1 / . + /// + public class DoubleUniformMutationManager : IMutationManager + { + private readonly double minValue; + private readonly double range; + private readonly Random random = new Random(); + + public DoubleUniformMutationManager(double minValue, double maxValue) + { + this.minValue = minValue; + range = maxValue - minValue; + } + + public double[] Mutate(double[] vector) + { + for (int i = 0; i < vector.Length; i++) + if (ProbabilityUtils.P(1.0 / vector.Length)) + vector[i] = minValue + random.NextDouble() * range; + + return vector; + } + } +} diff --git a/GeneticAlgorithm/Components/MutationManagers/IntBoundaryMutationManager.cs b/GeneticAlgorithm/Components/MutationManagers/IntBoundaryMutationManager.cs new file mode 100644 index 0000000..f21bf49 --- /dev/null +++ b/GeneticAlgorithm/Components/MutationManagers/IntBoundaryMutationManager.cs @@ -0,0 +1,29 @@ +using GeneticAlgorithm.Components.Interfaces; + +namespace GeneticAlgorithm.Components.MutationManagers +{ + /// + /// This mutation operator replaces the genome with either lower or upper bound randomly. + /// The probability of a bit being replaced is 1 / . + /// + public class IntBoundaryMutationManager : IMutationManager + { + private readonly int minValue; + private readonly int maxValue; + + public IntBoundaryMutationManager(int minValue, int maxValue) + { + this.minValue = minValue; + this.maxValue = maxValue; + } + + public int[] Mutate(int[] vector) + { + for (int i = 0; i < vector.Length; i++) + if (ProbabilityUtils.P(1.0 / vector.Length)) + vector[i] = ProbabilityUtils.P(0.5) ? maxValue : minValue; + + return vector; + } + } +} diff --git a/GeneticAlgorithm/Components/MutationManagers/IntGaussianMutationManager.cs b/GeneticAlgorithm/Components/MutationManagers/IntGaussianMutationManager.cs new file mode 100644 index 0000000..6049c59 --- /dev/null +++ b/GeneticAlgorithm/Components/MutationManagers/IntGaussianMutationManager.cs @@ -0,0 +1,42 @@ +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Exceptions; + +namespace GeneticAlgorithm.Components.MutationManagers +{ + /// + /// This operator adds a unit Gaussian distributed random value to the chosen gene. + /// If it falls outside of the user-specified lower or upper bounds for that gene, the new gene value is clipped. + /// + public class IntGaussianMutationManager : IMutationManager + { + private readonly int minValue; + private readonly int maxValue; + private readonly double standardDeviation; + + public IntGaussianMutationManager(int minValue, int maxValue) + { + this.minValue = minValue; + this.maxValue = maxValue; + standardDeviation = (maxValue - minValue) / 4.0; + } + + public IntGaussianMutationManager(int minValue, int maxValue, double standardDeviation) + { + this.minValue = minValue; + this.maxValue = maxValue; + if (standardDeviation < 0) + throw new GeneticAlgorithmException($"{nameof(standardDeviation)} can't be nagitave"); + this.standardDeviation = standardDeviation; + } + + public int[] Mutate(int[] vector) + { + var mean = (maxValue + minValue) / 2.0; + for (int i = 0; i < vector.Length; i++) + if (ProbabilityUtils.P(1.0 / vector.Length)) + vector[i] = (int) ProbabilityUtils.GaussianDistribution(standardDeviation, mean).Clip(minValue, maxValue); + + return vector; + } + } +} diff --git a/GeneticAlgorithm/Components/MutationManagers/IntShrinkMutationManager.cs b/GeneticAlgorithm/Components/MutationManagers/IntShrinkMutationManager.cs new file mode 100644 index 0000000..8952344 --- /dev/null +++ b/GeneticAlgorithm/Components/MutationManagers/IntShrinkMutationManager.cs @@ -0,0 +1,40 @@ +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Exceptions; + +namespace GeneticAlgorithm.Components.MutationManagers +{ + /// + /// This operator adds a random number taken from a Gaussian distribution with mean equal to the original genome. + /// + public class IntShrinkMutationManager : IMutationManager + { + private readonly double minValue; + private readonly double maxValue; + private readonly double standardDeviation; + + public IntShrinkMutationManager(double minValue, double maxValue) + { + this.minValue = minValue; + this.maxValue = maxValue; + standardDeviation = (maxValue - minValue) / 4.0; + } + + public IntShrinkMutationManager(double minValue, double maxValue, double standardDeviation) + { + this.minValue = minValue; + this.maxValue = maxValue; + if (standardDeviation < 0) + throw new GeneticAlgorithmException($"{nameof(standardDeviation)} can't be nagitave"); + this.standardDeviation = standardDeviation; + } + + public int[] Mutate(int[] vector) + { + for (int i = 0; i < vector.Length; i++) + if (ProbabilityUtils.P(1.0 / vector.Length)) + vector[i] = (int) ProbabilityUtils.GaussianDistribution(standardDeviation, vector[i]).Clip(minValue, maxValue); + + return vector; + } + } +} diff --git a/GeneticAlgorithm/Components/MutationManagers/IntUniformMutationManager.cs b/GeneticAlgorithm/Components/MutationManagers/IntUniformMutationManager.cs new file mode 100644 index 0000000..a673f1b --- /dev/null +++ b/GeneticAlgorithm/Components/MutationManagers/IntUniformMutationManager.cs @@ -0,0 +1,31 @@ +using System; +using GeneticAlgorithm.Components.Interfaces; + +namespace GeneticAlgorithm.Components.MutationManagers +{ + /// + /// This mutation operator replaces the genome with a random value between the lower and upper bound. + /// The probability of a bit being replaced is 1 / . + /// + public class IntUniformMutationManager : IMutationManager + { + private readonly int minValue; + private readonly int maxValue; + private readonly Random random = new Random(); + + public IntUniformMutationManager(int minValue, int maxValue) + { + this.minValue = minValue; + this.maxValue = maxValue; + } + + public int[] Mutate(int[] vector) + { + for (int i = 0; i < vector.Length; i++) + if (ProbabilityUtils.P(1.0 / vector.Length)) + vector[i] = random.Next(minValue, maxValue + 1); + + return vector; + } + } +} diff --git a/GeneticAlgorithm/Components/PopulationGenerators/BinaryVectorChromosomePopulationGenerator.cs b/GeneticAlgorithm/Components/PopulationGenerators/BinaryVectorChromosomePopulationGenerator.cs new file mode 100644 index 0000000..00aec1e --- /dev/null +++ b/GeneticAlgorithm/Components/PopulationGenerators/BinaryVectorChromosomePopulationGenerator.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using GeneticAlgorithm.Components.Chromosomes; +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Interfaces; + +namespace GeneticAlgorithm.Components.PopulationGenerators +{ + public class BinaryVectorChromosomePopulationGenerator : IPopulationGenerator + { + private readonly int vectorSize; + private readonly IMutationManager mutationManager; + private readonly IEvaluator evaluator; + + public BinaryVectorChromosomePopulationGenerator(int vectorSize, IMutationManager mutationManager, IEvaluator evaluator) + { + this.vectorSize = vectorSize; + this.mutationManager = mutationManager; + this.evaluator = evaluator; + } + + public IEnumerable GeneratePopulation(int size) + { + var population = new IChromosome[size]; + + for (int i = 0; i < size; i++) + population[i] = GetChromosome(); + + return population; + } + + private VectorChromosome GetChromosome() + { + var vector = new bool[vectorSize]; + for (int i = 0; i < vectorSize; i++) + vector[i] = ProbabilityUtils.P(0.5); + + return new VectorChromosome(vector, mutationManager, evaluator); + } + } +} diff --git a/GeneticAlgorithm/Components/PopulationGenerators/DoubleVectorChromosomePopulationGenerator.cs b/GeneticAlgorithm/Components/PopulationGenerators/DoubleVectorChromosomePopulationGenerator.cs new file mode 100644 index 0000000..c4afca7 --- /dev/null +++ b/GeneticAlgorithm/Components/PopulationGenerators/DoubleVectorChromosomePopulationGenerator.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using GeneticAlgorithm.Components.Chromosomes; +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Exceptions; +using GeneticAlgorithm.Interfaces; + +namespace GeneticAlgorithm.Components.PopulationGenerators +{ + public class DoubleVectorChromosomePopulationGenerator : IPopulationGenerator + { + private readonly Random random = new Random(); + private readonly int vectorSize; + private readonly double minGenome; + private readonly double range; + private readonly IMutationManager mutationManager; + private readonly IEvaluator evaluator; + + public DoubleVectorChromosomePopulationGenerator(int vectorSize, double minGenome, double maxGenome, IMutationManager mutationManager, IEvaluator evaluator) + { + if (vectorSize <= 0) + throw new GeneticAlgorithmException($"{nameof(vectorSize)} must be bigger than 0. It was {vectorSize}"); + if (maxGenome < minGenome) + throw new GeneticAlgorithmException($"{nameof(maxGenome)} ({maxGenome}) must be bigger than {nameof(minGenome)} ({minGenome})"); + + this.vectorSize = vectorSize; + this.minGenome = minGenome; + range = maxGenome - minGenome; + this.mutationManager = mutationManager; + this.evaluator = evaluator; + } + + public IEnumerable GeneratePopulation(int size) + { + var population = new IChromosome[size]; + + for (int i = 0; i < size; i++) + population[i] = GetChromosome(); + + return population; + } + + private VectorChromosome GetChromosome() + { + var vector = new double[vectorSize]; + for (int i = 0; i < vectorSize; i++) + vector[i] = minGenome + random.NextDouble() * range; + + return new VectorChromosome(vector, mutationManager, evaluator); + } + } +} diff --git a/GeneticAlgorithm/Components/PopulationGenerators/IntVectorChromosomePopulationGenerator.cs b/GeneticAlgorithm/Components/PopulationGenerators/IntVectorChromosomePopulationGenerator.cs new file mode 100644 index 0000000..eb7c5c2 --- /dev/null +++ b/GeneticAlgorithm/Components/PopulationGenerators/IntVectorChromosomePopulationGenerator.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using GeneticAlgorithm.Components.Chromosomes; +using GeneticAlgorithm.Components.Interfaces; +using GeneticAlgorithm.Exceptions; +using GeneticAlgorithm.Interfaces; + +namespace GeneticAlgorithm.Components.PopulationGenerators +{ + public class IntVectorChromosomePopulationGenerator : IPopulationGenerator + { + private readonly Random random = new Random(); + private readonly int vectorSize; + private readonly int minGenome; + private readonly int maxGenome; + private readonly IMutationManager mutationManager; + private readonly IEvaluator evaluator; + + public IntVectorChromosomePopulationGenerator(int vectorSize, int minGenome, int maxGenome, IMutationManager mutationManager, IEvaluator evaluator) + { + if (vectorSize <= 0) + throw new GeneticAlgorithmException($"{nameof(vectorSize)} must be bigger than 0. It was {vectorSize}"); + if (maxGenome < minGenome) + throw new GeneticAlgorithmException($"{nameof(maxGenome)} ({maxGenome}) must be bigger than {nameof(minGenome)} ({minGenome})"); + + this.vectorSize = vectorSize; + this.minGenome = minGenome; + this.maxGenome = maxGenome; + this.mutationManager = mutationManager; + this.evaluator = evaluator; + } + + public IEnumerable GeneratePopulation(int size) + { + var population = new IChromosome[size]; + + for (int i = 0; i < size; i++) + population[i] = GetChromosome(); + + return population; + } + + private VectorChromosome GetChromosome() + { + var vector = new int[vectorSize]; + for (int i = 0; i < vectorSize; i++) + vector[i] = random.Next(minGenome, maxGenome + 1); + + return new VectorChromosome(vector, mutationManager, evaluator); + } + } +} diff --git a/GeneticAlgorithm/GeneticSearchEngineBuilder.cs b/GeneticAlgorithm/GeneticSearchEngineBuilder.cs index 8af7984..f2d8403 100644 --- a/GeneticAlgorithm/GeneticSearchEngineBuilder.cs +++ b/GeneticAlgorithm/GeneticSearchEngineBuilder.cs @@ -12,7 +12,7 @@ public class GeneticSearchEngineBuilder protected readonly ICrossoverManager crossoverManager; protected readonly IPopulationGenerator populationGenerator; protected readonly int populationSize; - protected IMutationManager mutationManager = null; + protected IMutationProbabilityManager mutationManager = null; private double mutationProbability = 0; protected IChromosomeEvaluator chromosomeEvaluator = new BassicChromosomeEvaluator(); protected IEnvironment environment = null; @@ -46,7 +46,7 @@ public GeneticSearchEngineBuilder SetMutationProbability(double probability) /// A Custom Mutation Manager lets you dynamically determine the probability of a mutation based on the current population. /// For instance, you might want to set a high mutation probability for a few generations if the population is homogeneous, and lower it while the population is diversified. /// - public GeneticSearchEngineBuilder SetCustomMutationManager(IMutationManager manager) + public GeneticSearchEngineBuilder SetCustomMutationProbabilityManager(IMutationProbabilityManager manager) { mutationManager = manager; return this; @@ -175,7 +175,7 @@ protected void PreBuildActions() environment = new DefaultEnvironment(); if (mutationManager == null) - mutationManager = new BassicMutationManager(mutationProbability); + mutationManager = new BassicMutationProbabilityManager(mutationProbability); } public virtual GeneticSearchEngine Build() diff --git a/GeneticAlgorithm/GeneticSearchOptions.cs b/GeneticAlgorithm/GeneticSearchOptions.cs index ec1c0a6..955d785 100644 --- a/GeneticAlgorithm/GeneticSearchOptions.cs +++ b/GeneticAlgorithm/GeneticSearchOptions.cs @@ -7,7 +7,7 @@ namespace GeneticAlgorithm public class GeneticSearchOptions { public GeneticSearchOptions(int populationSize,List stopManagers, bool includeAllHistory, - List populationRenwalManagers, double elitePercentage, IMutationManager mutationManager, List populationConverters, IChromosomeEvaluator chromosomeEvaluator) + List populationRenwalManagers, double elitePercentage, IMutationProbabilityManager mutationManager, List populationConverters, IChromosomeEvaluator chromosomeEvaluator) { StopManagers = stopManagers; IncludeAllHistory = includeAllHistory; @@ -39,7 +39,7 @@ private void AssertIsBetweenZeroAndOne(double value, string name) public List PopulationRenwalManagers { get; } - public IMutationManager MutationManager { get; } + public IMutationProbabilityManager MutationManager { get; } public IChromosomeEvaluator ChromosomeEvaluator { get; } diff --git a/GeneticAlgorithm/Interfaces/IMutationManager.cs b/GeneticAlgorithm/Interfaces/IMutationProbabilityManager.cs similarity index 93% rename from GeneticAlgorithm/Interfaces/IMutationManager.cs rename to GeneticAlgorithm/Interfaces/IMutationProbabilityManager.cs index 25eda76..b59bb70 100644 --- a/GeneticAlgorithm/Interfaces/IMutationManager.cs +++ b/GeneticAlgorithm/Interfaces/IMutationProbabilityManager.cs @@ -3,7 +3,7 @@ /// /// IMutationManagers lets you dynamically determine the probability of a mutation based on the current population. /// - public interface IMutationManager + public interface IMutationProbabilityManager { /// /// This method is will be called once per generation (after the MutationProbability method for that generation), so you can use it to remember old data. diff --git a/GeneticAlgorithm/MutationManagers/BassicMutationManager.cs b/GeneticAlgorithm/MutationProbabilityManagers/BassicMutationProbabilityManager.cs similarity index 81% rename from GeneticAlgorithm/MutationManagers/BassicMutationManager.cs rename to GeneticAlgorithm/MutationProbabilityManagers/BassicMutationProbabilityManager.cs index 025f04e..3adbdd7 100644 --- a/GeneticAlgorithm/MutationManagers/BassicMutationManager.cs +++ b/GeneticAlgorithm/MutationProbabilityManagers/BassicMutationProbabilityManager.cs @@ -3,11 +3,11 @@ namespace GeneticAlgorithm.MutationManagers { - public class BassicMutationManager : IMutationManager + public class BassicMutationProbabilityManager : IMutationProbabilityManager { private readonly double mutation; - public BassicMutationManager(double mutation) + public BassicMutationProbabilityManager(double mutation) { if (mutation > 1 || mutation < 0) throw new GeneticAlgorithmException(nameof(mutation) + " must be between 0.0 to 1.0 (including)"); diff --git a/GeneticAlgorithm/MutationManagers/ConvergenceMutationManager.cs b/GeneticAlgorithm/MutationProbabilityManagers/ConvergenceMutationProbabilityManager.cs similarity index 95% rename from GeneticAlgorithm/MutationManagers/ConvergenceMutationManager.cs rename to GeneticAlgorithm/MutationProbabilityManagers/ConvergenceMutationProbabilityManager.cs index 6d8364f..a1253d7 100644 --- a/GeneticAlgorithm/MutationManagers/ConvergenceMutationManager.cs +++ b/GeneticAlgorithm/MutationProbabilityManagers/ConvergenceMutationProbabilityManager.cs @@ -8,7 +8,7 @@ namespace GeneticAlgorithm.MutationManagers /// A MutationManager that will return a mutation based on the population's convergence. /// The more homogeneous the population is, the higher the mutation probability will be. /// - public class ConvergenceMutationManager : IMutationManager + public class ConvergenceMutationProbabilityManager : IMutationProbabilityManager { private double previousConvergence; diff --git a/GeneticAlgorithm/ProbabilityUtils.cs b/GeneticAlgorithm/ProbabilityUtils.cs new file mode 100644 index 0000000..bc98a1d --- /dev/null +++ b/GeneticAlgorithm/ProbabilityUtils.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using GeneticAlgorithm.Exceptions; + +namespace GeneticAlgorithm +{ + public static class ProbabilityUtils + { + private static readonly Random random = new Random(); + + /// + /// Returns true with a probability of probability - where probability is between + /// + public static bool P(double probability) + { + if (probability > 1 || probability < 0) + throw new InternalSearchException($"Code 1008 (probability is {probability})"); + + return random.NextDouble() < probability; + } + + /// + /// Selects K random indexes between 0 and 'till' + /// + public static List SelectKRandomNumbers(int till, int k) + { + var selectedIndexes = new List(); + for (int i = 0; i < till; i++) + { + if (P(k / (double) (till - i))) + { + selectedIndexes.Add(i); + k--; + if (k == 0) break; + } + } + return selectedIndexes; + } + + /// + /// returns a random number with a Gaussian distribution. + /// + public static double GaussianDistribution(double sd, double mean) + { + return mean + sd * GetStandardDistribution(); + } + + /// + /// Returns random numbers with a standard distribution using Box-Muller Transformation. + /// + private static double GetStandardDistribution() + { + var u1 = 1.0 - random.NextDouble(); + var u2 = 1.0 - random.NextDouble(); + return Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); + } + } +} diff --git a/GeneticAlgorithm/SearchUtils.cs b/GeneticAlgorithm/SearchUtils.cs index 0ffe5f3..116b44d 100644 --- a/GeneticAlgorithm/SearchUtils.cs +++ b/GeneticAlgorithm/SearchUtils.cs @@ -43,5 +43,15 @@ public static Population Clone(this Population population) return newPopulation; } + + /// + /// Clips value if it is smaller than min or greater than max. + /// + public static double Clip(this double value, double min, double max) + { + if (value > max) return max; + if (value < min) return min; + return value; + } } } diff --git a/NumberVector/NumberVector.csproj b/NumberVector/NumberVector.csproj deleted file mode 100644 index 0ad7acf..0000000 --- a/NumberVector/NumberVector.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - netstandard2.0 - - false - - - - - - - diff --git a/NumberVector/NumberVectorChromosome.cs b/NumberVector/NumberVectorChromosome.cs deleted file mode 100644 index beb16d3..0000000 --- a/NumberVector/NumberVectorChromosome.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using GeneticAlgorithm.Interfaces; - -namespace GreatestVectorTests -{ - public class NumberVectorChromosome : IChromosome - { - private readonly int[] vector; - - public NumberVectorChromosome(int[] vector) - { - this.vector = vector; - } - - public double Evaluate() => vector.Sum(); - - public void Mutate() - { - var random = new Random(); - for (int i = 0; i < vector.Length; i++) - if (random.NextDouble() < 0.1) - vector[i] += random.Next(0, 10); - } - - public int this[int i] => vector[i]; - - public int VectorSize => vector.Length; - - public override string ToString() - { - var stringBuilder = new StringBuilder(); - foreach (var value in vector) - stringBuilder.Append(value + " "); - - return stringBuilder.ToString(); - } - } -} diff --git a/NumberVector/NumberVectorCrossoverManager.cs b/NumberVector/NumberVectorCrossoverManager.cs deleted file mode 100644 index 9090420..0000000 --- a/NumberVector/NumberVectorCrossoverManager.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using GeneticAlgorithm.Interfaces; - -namespace GreatestVectorTests -{ - public class NumberVectorCrossoverManager : ICrossoverManager - { - private readonly Random random = new Random(); - - public IChromosome Crossover(IChromosome chromosome1, IChromosome chromosome2) - { - var numberChromosome1 = (NumberVectorChromosome) chromosome1; - var numberChromosome2 = (NumberVectorChromosome) chromosome2; - - var splitLocation = random.Next(0, numberChromosome1.VectorSize + 1); - var newVector = new int[numberChromosome1.VectorSize]; - for (int i = 0; i < numberChromosome1.VectorSize; i++) - newVector[i] = i < splitLocation ? numberChromosome1[i] : numberChromosome2[i]; - - return new NumberVectorChromosome(newVector); - } - } -} diff --git a/NumberVector/NumberVectorPopulationGenerator.cs b/NumberVector/NumberVectorPopulationGenerator.cs deleted file mode 100644 index 18dfac5..0000000 --- a/NumberVector/NumberVectorPopulationGenerator.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using GeneticAlgorithm.Interfaces; - -namespace GreatestVectorTests -{ - /// - /// Returns 10 vectors, where each has 10 in one cell, and one in all the others - /// - public class NumberVectorPopulationGenerator : IPopulationGenerator - { - private readonly Random random = new Random(); - public const int VECTOR_SIZE = 10; - - public IEnumerable GeneratePopulation(int size) - { - var population = new IChromosome[size]; - - for (int i = 0; i < size; i++) - population[i] = GetChromosome(); - - return population; - } - - private NumberVectorChromosome GetChromosome() - { - var vector = new int[VECTOR_SIZE]; - for (int i = 0; i < VECTOR_SIZE; i++) - vector[i] = random.NextDouble() < 0.5 ? 0 : 1; - - return new NumberVectorChromosome(vector); - } - } -} diff --git a/README.md b/README.md index 62c2f9a..6a02ab9 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,17 @@ You can find the GeneticAlgorithmEngine library on nuget.org via package name Ge - [Elitism](#elitism) - [StopManagers](#istopmanagers) - [PopulationRenwalManagers](#ipopulationrenwalmanagers) - - [MutationManager](#imutationmanager) + - [MutationProbabilityManager](#imutationprobabilitymanager) - [PopulationConverters](#ipopulationconverter) - [SelectionStrategies](#iselectionstrategy) +- [Ready-Made Components](#ready-made-components) + - [Chromosomes](#chromosomes) + - [MutationManagers](#mutationmanagers) + - [CrossoverManagers](#crossovermanagers) + - [PopulationGenerators](#populationgenerators) + - [Example of Using Components](#example-of-using-components) + - [Using An Environment](#using-an-environment) - [IEnvironment](#ienvironment) - [ChromosomeEvaluator](#ichromosomeevaluator) @@ -50,7 +57,7 @@ Your chromosomes will need to implement the [IChromosome](/GeneticAlgorithm/Inte } ``` -You can find a sample Chromosome [here](/NumberVector/NumberVectorChromosome.cs). +You can find a sample Chromosome [here](/GeneticAlgorithm/Components/Chromosomes/VectorChromosome.cs). ### ICrossoverManager @@ -64,7 +71,7 @@ You can read more about crossovers [here](https://en.wikipedia.org/wiki/Crossove } ``` -You can find a sample CrossoverManager [here](/NumberVector/NumberVectorCrossoverManager.cs). +You can find some sample CrossoverManagers [here](/GeneticAlgorithm/Components/CrossoverManagers). ### IPopulationGenerator @@ -81,7 +88,7 @@ The PopulationGenerator will also renew the population when needed (see [IPopula } ``` -You can find a sample PopulationGenerator [here](/NumberVector/NumberVectorPopulationGenerator.cs). +You can find some sample PopulationGenerators [here](/GeneticAlgorithm/Components/PopulationGenerators). ## Creating an Instance of GeneticSearchEngine @@ -134,7 +141,7 @@ Let's see how we can configure our search engine to better match our needs. ### Mutations By default, the probability of mutations is 0. You can change this be using the GeneticSearchEngineBuilder.SetMutationProbability method. -Note that the mutation probability will be ignored if you set a [MutationManager](#imutationmanager). +Note that the mutation probability will be ignored if you set a [IMutationProbabilityManager](#imutationprobabilitymanager). ### CancellationToken You can use the GeneticSearchEngineBuilder.SetCancellationToken method to add cencellationTokens. @@ -190,17 +197,17 @@ var searchEngine = new GeneticSearchEngineBuilder(POPULATION_SIZE, MAX_GENERATIO var result = searchEngine.Run(); ``` -### IMutationManager +### IMutationProbabilityManager -The [IMutationManager](/GeneticAlgorithm/Interfaces/IMutationManager.cs) interface lets you dynamically determine the probability of a mutation based on the current population. +The [IMutationProbabilityManager](/GeneticAlgorithm/Interfaces/IMutationProbabilityManager.cs) interface lets you dynamically determine the probability of a mutation based on the current population. For instance, you might want to set a high mutation probability for a few generations if the population is homogeneous, and lower it while the population is diversified. -You can find an exsample of a custom MutationManager [here](/GeneticAlgorithm/MutationManagers/ConvergenceMutationManager.cs). +You can find an exsample of a custom MutationManager [here](/GeneticAlgorithm/MutationProbabilityManagers/ConvergenceMutationProbabilityManager.cs). Example: ```CSharp var searchEngine = new GeneticSearchEngineBuilder(POPULATION_SIZE, MAX_GENERATIONS, crossoverManager, populationGenerator) - .SetCustomMutationManager(new MyMutationManager()).Build(); + .SetCustomMutationProbabilityManager(new MyMutationProbabilityManager()).Build(); var result = searchEngine.Run(); ``` @@ -225,7 +232,7 @@ You can find an example of a custom PopulationConverter [here](/GeneticAlgorithm ### ISelectionStrategy -The [ISelectionStrategy](/GeneticAlgorithm/Interfaces/ISelectionStrategy.cs) tells the engine how to choose the chromosmes that will create the next generation. +The [ISelectionStrategy](/GeneticAlgorithm/Interfaces/ISelectionStrategy.cs) tells the engine how to choose the chromosomes that will create the next generation. You can create your own SelectionStrategy by implementing the ISelectionStrategy interface, or use one of the existing strategies. By default, the engine will use the RouletteWheelSelection, but you can changed that with the GeneticSearchEngineBuilder's SetSelectionStrategy method. @@ -244,6 +251,89 @@ var searchEngine = new GeneticSearchEngineBuilder(POPULATION_SIZE, MAX_GENERATIO var result = searchEngine.Run(); ``` +## Ready-Made Components + +Components are ready-made implementations of commonly used genetic components (such as CrossoverManagers, MutationManagers and chromosomes). + +### Chromosomes + +The [VectorChromosome](/GeneticAlgorithm/Components/Chromosomes/VectorChromosome.cs) is an implementation of the IChromosome interface. +It holds a generic vector, which is set in its constructor and can be retrieved via the GetVector method. + +VectorChromosome expects an [IMutationManager](#mutationmanagers) and [IEvaluator](/GeneticAlgorithm/Components/Interfaces/IEvaluator.cs) in its constructor, which tell it how to preform mutations and evaluate itself. + +```CSharp + class BasicEvaluator : IEvaluator + { + public double Evaluate(IChromosome chromosome) => + ((VectorChromosome) chromosome).GetVector().Sum(); + } + + class UseVectorChromosome + { + IMutationManager mutationManager = new UniformMutationManager(0, 10); + IEvaluator evaluator = new BasicEvaluator(); + int[] vector = new int[] {1, 3, 2, 8}; + VectorChromosome = new VectorChromosome(vector, mutationManager, evaluator); + } +``` + +### MutationManagers + +[IMutationManager](/GeneticAlgorithm/Components/Interfaces/IMutationManager.cs) tells the VectorChromosome how to preform mutations. +You can create your own MutationManager by implementing the IMutationManager interface, or use an existing managers. + +Existing managers: +- [BitStringMutationManager](/GeneticAlgorithm/Components/MutationManagers/BitStringMutationManager.cs): This mutation only works on binary chromosomes (represented as type VectorChromosome). It flips bits at random (that is replaces 1 with 0 and 0 with 1). The probability of a bit being flipped is 1 / \\. +- [IntBoundaryMutationManager](/GeneticAlgorithm/Components/MutationManagers/IntBoundaryMutationManager.cs): This mutation operator replaces the genome with either lower or upper bound randomly (works only on integer-vector chromosomes). The probability of a bit being replaced is 1 / \\. +- [DoubleBoundaryMutationManager](/GeneticAlgorithm/Components/MutationManagers/DoubleBoundaryMutationManager.cs): This mutation operator replaces the genome with either lower or upper bound randomly (works only on double-vector chromosomes). The probability of a bit being replaced is 1 / \\. +- [IntUniformMutationManager](/GeneticAlgorithm/Components/MutationManagers/IntUniformMutationManager.cs): This mutation operator replaces the genome with a random value between the lower and upper bound (works only on integer-vector chromosomes). The probability of a bit being replaced is 1 / \\. +- [DoubleUniformMutationManager](/GeneticAlgorithm/Components/MutationManagers/DoubleUniformMutationManager.cs): This mutation operator replaces the genome with a random value between the lower and upper bound (works only on double-vector chromosomes). The probability of a bit being replaced is 1 / \\. +- [IntGaussianMutationManager](/GeneticAlgorithm/Components/MutationManagers/IntGaussianMutationManager.cs): This operator adds a unit Gaussian distributed random value to the chosen gene (works only on integer-vector chromosomes). If it falls outside of the user-specified lower or upper bounds for that gene, the new gene value is clipped. +- [DoubleGaussianMutationManager](/GeneticAlgorithm/Components/MutationManagers/DoubleGaussianMutationManager.cs): This operator adds a unit Gaussian distributed random value to the chosen gene (works only on double-vector chromosomes). If it falls outside of the user-specified lower or upper bounds for that gene, the new gene value is clipped. +- [IntShrinkMutationManager](/GeneticAlgorithm/Components/MutationManagers/IntShrinkMutationManager.cs): This operator adds a random number taken from a Gaussian distribution with mean equal to the original genome (works only on integer-vector chromosomes). If it falls outside of the user-specified lower or upper bounds for that gene, the new gene value is clipped. +- [DoubleShrinkMutationManager](/GeneticAlgorithm/Components/MutationManagers/DoubleShrinkMutationManager.cs): This operator adds a random number taken from a Gaussian distribution with mean equal to the original genome (works only on double-vector chromosomes). If it falls outside of the user-specified lower or upper bounds for that gene, the new gene value is clipped. + +### CrossoverManagers + +The CrossoverManagers are implementations of the [ICrossoverManager](#icrossovermanager) interface. + +- [SinglePointCrossoverManager](/GeneticAlgorithm/Components/CrossoverManagers/SinglePointCrossoverManager.cs): A point on both parents' chromosomes is picked randomly, and designated a 'crossover point'. Bits to the right of that point are swapped between the two parent chromosomes. See [this](https://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)#Single-point_crossover) for more information. +- [K_PointCrossoverManager](/GeneticAlgorithm/Components/CrossoverManagers/K_PointCrossoverManager.cs): Similar to SinglePointCrossoverManager, only that K points are chosen instead of one. See [this](https://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)#Two-point_and_k-point_crossover) for more information. +- [UniformCrossoverManager](/GeneticAlgorithm/Components/CrossoverManagers/UniformCrossoverManager.cs): In uniform crossover, each bit is chosen from either parent with equal probability. + +### PopulationGenerators + +PopulationGenerators are implementations of the [IPopulationGenerator](#ipopulationgenerator) interface. + +- [IntVectorChromosomePopulationGenerator](/GeneticAlgorithm/Components/PopulationGenerators/IntVectorChromosomePopulationGenerator.cs): Generates a population of VectorChromosome within some min and max bounds. +- [DoubleVectorChromosomePopulationGenerator](/GeneticAlgorithm/Components/PopulationGenerators/DoubleVectorChromosomePopulationGenerator.cs): Generates a population of VectorChromosome within some min and max bounds. +- [BinaryVectorChromosomePopulationGenerator](/GeneticAlgorithm/Components/PopulationGenerators/BinaryVectorChromosomePopulationGenerator.cs): Generates binary chromosomes (chromosomes of type VectorChromosome). + +### Example of Using Components + +Following is an example of using components: + +```CSharp + class BasicEvaluator : IEvaluator + { + public double Evaluate(IChromosome chromosome) => + ((VectorChromosome) chromosome).GetVector().Sum(); + } + + class UseComponents + { + IMutationManager mutationManager = new UniformMutationManager(0, 10); + IEvaluator evaluator = new BasicEvaluator(); + IPopulationGenerator populationGenerator = + new IntVectorChromosomePopulationGenerator(VECTOR_SIZE, 0, 10, mutationManager, evaluator); + ICrossoverManager crossoverManager = new SinglePointCrossoverManager(mutationManager, evaluator); + GeneticSearchEngine engine = + new GeneticSearchEngineBuilder(POPULATION_SIZE, GENERATION, crossoverManager, populationGenerator).Build(); + SearchReasult result = engine.Run(); + } +``` + ## Using an Environment Sometimes, it's impossible to evaluate a chromosome without knowing information about it's surroundings, such as the rest of the population. (This, by the way, in the case in nature - where the fitness an individual depends on its environment and the way it interacts with the other individuals). @@ -267,7 +357,7 @@ Since the IChromosomeEvaluator's SetEnvierment is called before the evaluation b You can find an example of a custom ChromosomeEvaluator [here](/EnvironmentGui/ChromosomeEvaluator.cs). -### Example +### Example of Using an Environment ```CSharp var searchEngine = new GeneticSearchEngineBuilder(POPULATION_SIZE, MAX_GENERATIONS, crossoverManager, populationGenerator)