diff --git a/global.json b/global.json index a05a252d..6fd9083b 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "6.0.400" + "version": "8" } } \ No newline at end of file diff --git a/src/GeneticSharp.Domain.UnitTests/Chromosomes/SelfAdaptiveChromosomeTest.cs b/src/GeneticSharp.Domain.UnitTests/Chromosomes/SelfAdaptiveChromosomeTest.cs new file mode 100644 index 00000000..c44406c1 --- /dev/null +++ b/src/GeneticSharp.Domain.UnitTests/Chromosomes/SelfAdaptiveChromosomeTest.cs @@ -0,0 +1,62 @@ +using System; +using System.Linq; +using NUnit.Framework; + +namespace GeneticSharp.Domain.UnitTests.Chromosomes +{ + [TestFixture] + public class SelfAdaptiveChromosomeTest + { + [Test] + public void Constructor_ValidParameters_InitializesCorrectly() + { + int length = 10; + double minValue = 0.0; + double maxValue = 1.0; + double initMutationProvVal = 0.05; + + var chromosome = new SelfAdaptiveChromosome(length, minValue, maxValue, initMutationProvVal); + + Assert.AreEqual(length, chromosome.Length); + } + + [Test] + public void Clone_ValidChromosome_ClonesCorrectly() + { + int length = 10; + var chromosome = new SelfAdaptiveChromosome(length); + + var clone = chromosome.Clone() as SelfAdaptiveChromosome; + + Assert.IsNotNull(clone); + Assert.AreEqual(chromosome.Length, clone.Length); + } + + [Test] + public void CreateNew_ValidChromosome_CreatesNewInstance() + { + int length = 10; + var chromosome = new SelfAdaptiveChromosome(length); + + var newChromosome = chromosome.CreateNew() as SelfAdaptiveChromosome; + + Assert.IsNotNull(newChromosome); + Assert.AreEqual(length, newChromosome.Length); + } + + [Test] + public void GenerateGene_ValidIndex_GeneratesGeneWithinRange() + { + int length = 10; + double minValue = 0.0; + double maxValue = 1.0; + var chromosome = new SelfAdaptiveChromosome(length, minValue, maxValue); + + for (int i = 0; i < length; i++) + { + var gene = chromosome.GenerateGene(i); + Assert.IsTrue((double)gene.Value >= minValue && (double)gene.Value <= maxValue); + } + } + } +} diff --git a/src/GeneticSharp.Domain.UnitTests/Crossovers/CrossoverServiceTest.cs b/src/GeneticSharp.Domain.UnitTests/Crossovers/CrossoverServiceTest.cs index 41fa2ed4..11958ec3 100644 --- a/src/GeneticSharp.Domain.UnitTests/Crossovers/CrossoverServiceTest.cs +++ b/src/GeneticSharp.Domain.UnitTests/Crossovers/CrossoverServiceTest.cs @@ -12,7 +12,7 @@ public void GetCrossoverTypes_NoArgs_AllAvailableCrossovers() { var actual = CrossoverService.GetCrossoverTypes(); - Assert.AreEqual(12, actual.Count); + Assert.AreEqual(13, actual.Count); var index = -1; Assert.AreEqual(typeof(AlternatingPositionCrossover), actual[++index]); Assert.AreEqual(typeof(CutAndSpliceCrossover), actual[++index]); @@ -22,10 +22,12 @@ public void GetCrossoverTypes_NoArgs_AllAvailableCrossovers() Assert.AreEqual(typeof(OrderedCrossover), actual[++index]); Assert.AreEqual(typeof(PartiallyMappedCrossover), actual[++index]); Assert.AreEqual(typeof(PositionBasedCrossover), actual[++index]); + Assert.AreEqual(typeof(SelfAdaptiveCrossover), actual[++index]); Assert.AreEqual(typeof(ThreeParentCrossover), actual[++index]); Assert.AreEqual(typeof(TwoPointCrossover), actual[++index]); Assert.AreEqual(typeof(UniformCrossover), actual[++index]); Assert.AreEqual(typeof(VotingRecombinationCrossover), actual[++index]); + } [Test()] @@ -33,7 +35,7 @@ public void GetCrossoverNames_NoArgs_AllAvailableCrossoversNames() { var actual = CrossoverService.GetCrossoverNames(); - Assert.AreEqual(12, actual.Count); + Assert.AreEqual(13, actual.Count); var index = -1; Assert.AreEqual("Alternating-position (AP)", actual[++index]); Assert.AreEqual("Cut and Splice", actual[++index]); @@ -43,6 +45,7 @@ public void GetCrossoverNames_NoArgs_AllAvailableCrossoversNames() Assert.AreEqual("Ordered (OX1)", actual[++index]); Assert.AreEqual("Partially Mapped (PMX)", actual[++index]); Assert.AreEqual("Position-based (POS)", actual[++index]); + Assert.AreEqual("Self Adaptive Crossover", actual[++index]); Assert.AreEqual("Three Parent", actual[++index]); Assert.AreEqual("Two-Point", actual[++index]); Assert.AreEqual("Uniform", actual[++index]); diff --git a/src/GeneticSharp.Domain.UnitTests/GeneticAlgorithmTest.cs b/src/GeneticSharp.Domain.UnitTests/GeneticAlgorithmTest.cs index 8510393d..c4570328 100644 --- a/src/GeneticSharp.Domain.UnitTests/GeneticAlgorithmTest.cs +++ b/src/GeneticSharp.Domain.UnitTests/GeneticAlgorithmTest.cs @@ -493,8 +493,7 @@ public void Start_UsingAllConfigurationCombinationsAvailable_AllRun() var crossovers = CrossoverService.GetCrossoverNames(); var mutations = MutationService.GetMutationNames().Where(m => !m.Equals("Flip Bit")); var reinsertions = ReinsertionService.GetReinsertionNames(); - var chromosome = new OrderedChromosomeStub(); - + foreach (var s in selections) { foreach (var c in crossovers) @@ -508,6 +507,8 @@ public void Start_UsingAllConfigurationCombinationsAvailable_AllRun() var mutation = MutationService.CreateMutationByName(m); var reinsertion = ReinsertionService.CreateReinsertionByName(r); + IChromosome chromosome =(IChromosome) (((crossover is SelfAdaptiveCrossover) || (mutation is SelfAdaptiveMutation)) ? new SelfAdaptiveChromosome(6, 0,6) : new OrderedChromosomeStub()); + if (crossover.IsOrdered ^ mutation.IsOrdered) { continue; @@ -523,12 +524,12 @@ public void Start_UsingAllConfigurationCombinationsAvailable_AllRun() mutation = new UniformMutation(1); } + var population = new Population(50, 50, chromosome.Clone()){ GenerationStrategy = new TrackingGenerationStrategy() }; + var fitness = new FitnessStub() { SupportsParallel = false }; + var target = new GeneticAlgorithm( - new Population(50, 50, chromosome.Clone()) - { - GenerationStrategy = new TrackingGenerationStrategy() - }, - new FitnessStub() { SupportsParallel = false }, + population, + fitness, selection, crossover, mutation); diff --git a/src/GeneticSharp.Domain.UnitTests/Mutations/MutationServiceTest.cs b/src/GeneticSharp.Domain.UnitTests/Mutations/MutationServiceTest.cs index f400dd3f..c2050366 100644 --- a/src/GeneticSharp.Domain.UnitTests/Mutations/MutationServiceTest.cs +++ b/src/GeneticSharp.Domain.UnitTests/Mutations/MutationServiceTest.cs @@ -13,14 +13,15 @@ public void GetMutationTypes_NoArgs_AllAvailableMutations() { var actual = MutationService.GetMutationTypes(); - Assert.AreEqual(7, actual.Count); + Assert.AreEqual(8, actual.Count); Assert.AreEqual(typeof(DisplacementMutation), actual[0]); Assert.AreEqual(typeof(FlipBitMutation), actual[1]); Assert.AreEqual(typeof(InsertionMutation), actual[2]); Assert.AreEqual(typeof(PartialShuffleMutation), actual[3]); Assert.AreEqual(typeof(ReverseSequenceMutation), actual[4]); - Assert.AreEqual(typeof(TworsMutation), actual[5]); - Assert.AreEqual(typeof(UniformMutation), actual[6]); + Assert.AreEqual(typeof(SelfAdaptiveMutation), actual[5]); + Assert.AreEqual(typeof(TworsMutation), actual[6]); + Assert.AreEqual(typeof(UniformMutation), actual[7]); } [Test()] @@ -28,14 +29,15 @@ public void GetMutationNames_NoArgs_AllAvailableMutationsNames() { var actual = MutationService.GetMutationNames(); - Assert.AreEqual(7, actual.Count); + Assert.AreEqual(8, actual.Count); Assert.AreEqual("Displacement", actual[0]); Assert.AreEqual("Flip Bit", actual[1]); Assert.AreEqual("Insertion", actual[2]); Assert.AreEqual("Partial Shuffle (PSM)", actual[3]); Assert.AreEqual("Reverse Sequence (RSM)", actual[4]); - Assert.AreEqual("Twors", actual[5]); - Assert.AreEqual("Uniform", actual[6]); + Assert.AreEqual("Self Adaptive Mutation", actual[5]); + Assert.AreEqual("Twors", actual[6]); + Assert.AreEqual("Uniform", actual[7]); } [Test()] diff --git a/src/GeneticSharp.Domain.UnitTests/Mutations/SelfAdaptiveMutationTest.cs b/src/GeneticSharp.Domain.UnitTests/Mutations/SelfAdaptiveMutationTest.cs new file mode 100644 index 00000000..b23aea46 --- /dev/null +++ b/src/GeneticSharp.Domain.UnitTests/Mutations/SelfAdaptiveMutationTest.cs @@ -0,0 +1,50 @@ +using System; +using System.Linq; +using NUnit.Framework; + +namespace GeneticSharp.Domain.UnitTests.Mutations +{ + [TestFixture] + public class SelfAdaptiveMutationTest + { + [Test] + public void Constructor_ValidParameters_InitializesCorrectly() + { + double tau = 0.1; + double minMutationRate = 0.05; + double maxMutationRate = 0.9; + + var mutation = new SelfAdaptiveMutation(tau, minMutationRate, maxMutationRate); + } + + + + [Test] + public void PerformMutate_ValidChromosome_MutatesCorrectly() + { + int length = 100; + var chromosome = new SelfAdaptiveChromosome(length, 0,20); + var mutation = new SelfAdaptiveMutation(); + var random = new Random(); + + + + mutation.Mutate(chromosome, 1.0f); + + // Check if mutation probabilities are within the expected range + for (int i = 0; i < length; i++) + { + Assert.That(chromosome.GetMutationProbability(i), Is.InRange(mutation.MinMutationRate, mutation.MaxMutationRate)); + } + + // Check if genes have been mutated + var genes = chromosome.GetGenes(); + for (int i = 0; i < length; i++) + { + Assert.That((double)genes[i].Value, Is.InRange(chromosome._minValue, chromosome._maxValue)); + } + } + + + } +} diff --git a/src/GeneticSharp.Domain.UnitTests/Populations/FitnessStub.cs b/src/GeneticSharp.Domain.UnitTests/Populations/FitnessStub.cs index cee40e4d..b0032c8c 100644 --- a/src/GeneticSharp.Domain.UnitTests/Populations/FitnessStub.cs +++ b/src/GeneticSharp.Domain.UnitTests/Populations/FitnessStub.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Threading; @@ -21,7 +22,7 @@ public double Evaluate(IChromosome chromosome) } var genes = chromosome.GetGenes(); - double f = genes.Sum(g => (int)g.Value) / 20f; + double f = genes.Sum(g => Convert.ToInt32(g.Value)) / 20f; if (f > 1) { diff --git a/src/GeneticSharp.Domain/Chromosomes/SelfAdaptiveChromosome.cs b/src/GeneticSharp.Domain/Chromosomes/SelfAdaptiveChromosome.cs new file mode 100644 index 00000000..a63088ed --- /dev/null +++ b/src/GeneticSharp.Domain/Chromosomes/SelfAdaptiveChromosome.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace GeneticSharp +{ + public enum CrossoverType + { + OnePoint, TwoPoints, Uniform + } + + public class SelfAdaptiveChromosome : ChromosomeBase + { + Dictionary _mutationProbabilities; + public CrossoverType CrossoverType { get; set; } + int _length; + double _initMutationProvVal; + public readonly double _minValue, _maxValue; + + public SelfAdaptiveChromosome(int length, double minValue = double.MinValue, double maxValue = double.MaxValue, double initMutationProvVal = -1) + : base(length) + { + + _length = length; + _minValue = minValue; + _maxValue = maxValue; + _initMutationProvVal = (initMutationProvVal > 0) ? initMutationProvVal : 1.0 / Math.Sqrt(2 * length); + _mutationProbabilities = new Dictionary(); + + var random = RandomizationProvider.Current; + for (int i = 0; i < length; i++) + { + var g = new Gene(random.GetDouble(minValue, maxValue)); + base.ReplaceGene(i, g); + } + + var crossoverTypes = Enum.GetValues(typeof(CrossoverType)); + CrossoverType = (CrossoverType)crossoverTypes.GetValue(random.GetInt(0, crossoverTypes.Length)); + } + + public double GetMutationProbability(int index) + { + double d; + if (!_mutationProbabilities.TryGetValue(index, out d)) + return _initMutationProvVal; + return d; + } + + public void SetMutationProbability(int index, double prov) + { + _mutationProbabilities[index] = prov; + } + + public override IChromosome Clone() + { + SelfAdaptiveChromosome c = (SelfAdaptiveChromosome)base.Clone(); + c._mutationProbabilities = _mutationProbabilities.ToDictionary(r => r.Key, r => r.Value); + return c; + } + + + public override IChromosome CreateNew() + { + var e = new SelfAdaptiveChromosome(_length, _minValue, _maxValue, _initMutationProvVal); + return e; + } + + public override Gene GenerateGene(int geneIndex) + { + var random = RandomizationProvider.Current; + double value = random.GetDouble(_minValue, _maxValue); + var g = new Gene(value); + base.ReplaceGene(geneIndex, g); + return new Gene(value); + } + } +} \ No newline at end of file diff --git a/src/GeneticSharp.Domain/Crossovers/SelfAdaptiveCrossover.cs b/src/GeneticSharp.Domain/Crossovers/SelfAdaptiveCrossover.cs new file mode 100644 index 00000000..ec70e1de --- /dev/null +++ b/src/GeneticSharp.Domain/Crossovers/SelfAdaptiveCrossover.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using GeneticSharp; + +namespace GeneticSharp +{ + [DisplayName("Self Adaptive Crossover")] + public class SelfAdaptiveCrossover : CrossoverBase + { + public SelfAdaptiveCrossover() : base(2, 2) { } + + protected override IList PerformCross(IList parents) + { + var parent1 = parents[0] as SelfAdaptiveChromosome; + var parent2 = parents[1] as SelfAdaptiveChromosome; + + if (parent1 == null || parent2 == null) + { + throw new ArgumentException("Both parents must be of type SelfAdaptiveChromosome."); + } + + IList ret = UniformCrossover(parent1, parent2); + switch (parent1.CrossoverType) + { + case CrossoverType.Uniform: + ret = UniformCrossover(parent1, parent2); + break; + case CrossoverType.TwoPoints: + ret = TwoPointCrossover(parent1, parent2); + break; + case CrossoverType.OnePoint: + ret = OnePointCrossover(parent1, parent2); + break; + default: + ret = UniformCrossover(parent1, parent2); + break; + } + + foreach (var e in ret) + ((SelfAdaptiveChromosome)e).CrossoverType = RandomizationProvider.Current.GetDouble() > 0.5 ? parent1.CrossoverType : parent2.CrossoverType; + + return ret; + } + + private IList OnePointCrossover(SelfAdaptiveChromosome parent1, SelfAdaptiveChromosome parent2) + { + int length = parent1.Length; + int crossoverPoint = RandomizationProvider.Current.GetInt(0, length); + + SelfAdaptiveChromosome offspring1 = (SelfAdaptiveChromosome)parent1.Clone(); + SelfAdaptiveChromosome offspring2 = (SelfAdaptiveChromosome)parent2.Clone(); + + for (int i = 0; i < length; i++) + { + if (i >= crossoverPoint) + { + var v = offspring1.GetMutationProbability(i); + offspring1.SetMutationProbability(i, parent2.GetMutationProbability(i)); + offspring2.SetMutationProbability(i, parent1.GetMutationProbability(i)); + + var g = offspring1.GetGene(i); + offspring1.ReplaceGene(i, parent2.GetGene(i)); + offspring2.ReplaceGene(i, parent1.GetGene(i)); + } + } + + return new List() { offspring1, offspring2 }; + } + + private IList TwoPointCrossover(SelfAdaptiveChromosome parent1, SelfAdaptiveChromosome parent2) + { + int length = parent1.Length; + int crossoverPoint1 = RandomizationProvider.Current.GetInt(0, length); + int crossoverPoint2 = RandomizationProvider.Current.GetInt(crossoverPoint1, length); + + SelfAdaptiveChromosome offspring1 = (SelfAdaptiveChromosome)parent1.Clone(); + SelfAdaptiveChromosome offspring2 = (SelfAdaptiveChromosome)parent2.Clone(); + + for (int i = 0; i < length; i++) + { + if (i >= crossoverPoint1 && i <= crossoverPoint2) + { + var v = offspring1.GetMutationProbability(i); + offspring1.SetMutationProbability(i, parent2.GetMutationProbability(i)); + offspring2.SetMutationProbability(i, parent1.GetMutationProbability(i)); + + var g = offspring1.GetGene(i); + offspring1.ReplaceGene(i, parent2.GetGene(i)); + offspring2.ReplaceGene(i, parent1.GetGene(i)); + } + } + + return new List() { offspring1, offspring2 }; + } + + private IList UniformCrossover(SelfAdaptiveChromosome parent1, SelfAdaptiveChromosome parent2) + { + int length = parent1.Length; + + + SelfAdaptiveChromosome offspring1 = (SelfAdaptiveChromosome)parent1.Clone(); + SelfAdaptiveChromosome offspring2 = (SelfAdaptiveChromosome)parent2.Clone(); + + for (int i = 0; i < length; i++) + { + if (RandomizationProvider.Current.GetDouble() < 0.5) + { + var v = offspring1.GetMutationProbability(i); + offspring1.SetMutationProbability(i,offspring2.GetMutationProbability(i)); + offspring2.SetMutationProbability(i, v); + } + + + if (RandomizationProvider.Current.GetDouble() < 0.5) + { + var g = offspring1.GetGene(i); + offspring1.ReplaceGene(i, offspring2.GetGene(i)); + offspring2.ReplaceGene(i, g); + } + + } + + return new List() { offspring1, offspring2 }; + } + } +} \ No newline at end of file diff --git a/src/GeneticSharp.Domain/Mutations/SelfAdaptiveMutation.cs b/src/GeneticSharp.Domain/Mutations/SelfAdaptiveMutation.cs new file mode 100644 index 00000000..7cebde2b --- /dev/null +++ b/src/GeneticSharp.Domain/Mutations/SelfAdaptiveMutation.cs @@ -0,0 +1,69 @@ +using System; +using System.ComponentModel; +using System.Linq; +using GeneticSharp; + +namespace GeneticSharp +{ + [DisplayName("Self Adaptive Mutation")] + public class SelfAdaptiveMutation : MutationBase + { + public readonly double Tau = 0.1; + public readonly double MinMutationRate = 0.05; + public readonly double MaxMutationRate = 0.9; + + + public SelfAdaptiveMutation() + { + } + + public SelfAdaptiveMutation(double tau = 0.1, double minMutationRate = 0.05, double maxMutationRate = 0.9) + { + this.Tau = tau; + this.MinMutationRate = minMutationRate; + this.MaxMutationRate = maxMutationRate; + } + + protected override void PerformMutate(IChromosome chromosome, float probability) + { + if (chromosome is not SelfAdaptiveChromosome adaptiveChromosome) + { + throw new ArgumentException("The chromosome must be of type SelfAdaptiveChromosome.", nameof(chromosome)); + } + + var random = RandomizationProvider.Current; + + for (int i = 0; i < adaptiveChromosome.Length; i++) + { + if (random.GetDouble() < adaptiveChromosome.GetMutationProbability(i)) + { + double normal = NextGaussian(0, 1); + double p = adaptiveChromosome.GetMutationProbability(i) * Math.Exp(Tau * normal); + p = Math.Clamp(p, MinMutationRate, MaxMutationRate); + adaptiveChromosome.SetMutationProbability(i,p); + } + + if (random.GetDouble() < adaptiveChromosome.GetMutationProbability(i)) + { + var g = chromosome.GenerateGene(i); + adaptiveChromosome.ReplaceGene(i, g); + } + } + + if(random.GetDouble() < probability) + { + var crossoverTypes = Enum.GetValues(typeof(CrossoverType)); + adaptiveChromosome.CrossoverType = (CrossoverType)crossoverTypes.GetValue(random.GetInt(0, crossoverTypes.Length)); + } + } + + private double NextGaussian(double mean, double stdDev) + { + var random = RandomizationProvider.Current; + double u1 = random.GetDouble(); + double u2 = random.GetDouble(); + double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Cos(2.0 * Math.PI * u2); + return mean + stdDev * randStdNormal; + } + } +} \ No newline at end of file diff --git a/src/GeneticSharp.Domain/Selections/TournamentSelection.cs b/src/GeneticSharp.Domain/Selections/TournamentSelection.cs index 58c531b5..9367fdcd 100644 --- a/src/GeneticSharp.Domain/Selections/TournamentSelection.cs +++ b/src/GeneticSharp.Domain/Selections/TournamentSelection.cs @@ -85,7 +85,7 @@ protected override IList PerformSelectChromosomes(int number, Gener var candidates = generation.Chromosomes.ToList(); var selected = new List(); - while (selected.Count < number) + while (selected.Count < number && Size <= candidates.Count) { var randomIndexes = RandomizationProvider.Current.GetUniqueInts(Size, 0, candidates.Count); var tournamentWinner = candidates.Where((c, i) => randomIndexes.Contains(i)).OrderByDescending(c => c.Fitness).First(); @@ -98,7 +98,14 @@ protected override IList PerformSelectChromosomes(int number, Gener } } - return selected; + while(selected.Count < number && candidates.Any()) + { + var canditate = candidates.First().Clone(); + selected.Add(canditate); + candidates.Remove(canditate); + } + + return selected; } } }