Skip to content

Commit

Permalink
Fix #72 - GeneticAlgorithm.BestChromosome.Fitness decrease over time …
Browse files Browse the repository at this point in the history
…when using EliteSelection
  • Loading branch information
giacomelli committed Sep 8, 2022
1 parent ec0245d commit d0a8c60
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 45 deletions.
32 changes: 32 additions & 0 deletions src/GeneticSharp.Domain.UnitTests/Populations/PopulationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,38 @@ public void EndCurrentGeneration_BestChromosomeChanged_ChangeEventRaise()

Assert.IsTrue(eventRaise);
}

/// <summary>
/// https://github.com/giacomelli/GeneticSharp/issues/70
/// </summary>
[Test]
public void EndCurrentGeneration_Issue70_Solved()
{
var target = new Population(100, 100, new ChromosomeStub());

var eventRaise = false;
IChromosome lastChromosome = null;

target.BestChromosomeChanged += (e, a) =>
{
eventRaise = true;

if (lastChromosome != null && lastChromosome.Fitness == target.BestChromosome.Fitness)
Assert.Fail("BestChromosomeChanged should only be raised if fitness is different.");

lastChromosome = target.BestChromosome;
};

target.CreateInitialGeneration();
target.CurrentGeneration.Chromosomes.Each(c => c.Fitness = 0);
target.CurrentGeneration.Chromosomes[1].Fitness = 1;
target.EndCurrentGeneration();

target.CurrentGeneration.Chromosomes[0].Fitness = 1;
target.EndCurrentGeneration();

Assert.IsTrue(eventRaise);
}
}
}

43 changes: 41 additions & 2 deletions src/GeneticSharp.Domain.UnitTests/Selections/EliteSelectionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using NUnit.Framework;
using NSubstitute;
using System.Linq;

namespace GeneticSharp.Domain.UnitTests.Selections
{
Expand Down Expand Up @@ -72,8 +73,46 @@ public void SelectChromosomes_Generation_ChromosomesSelected()
actual = target.SelectChromosomes(3, generation);
Assert.AreEqual(3, actual.Count);
Assert.AreEqual(0.7, actual[0].Fitness);
Assert.AreEqual(0.5, actual[1].Fitness);
Assert.AreEqual(0.1, actual[2].Fitness);
Assert.AreEqual(0.7, actual[1].Fitness);
Assert.AreEqual(0.5, actual[2].Fitness);
}

/// <summary>
/// https://github.com/giacomelli/GeneticSharp/issues/72
/// </summary>
[Test()]
public void SelectChromosomes_Issue72_Solved()
{
var target = new EliteSelection();
var chromosomes = new IChromosome[10];

for (int i = 0; i < chromosomes.Length; i++)
{
var c = Substitute.ForPartsOf<ChromosomeBase>(2);
c.Fitness = i;
chromosomes[i] = c;
}

var generation3 = new Generation(3, chromosomes.Take(4).ToList());
var generation2 = new Generation(2, chromosomes.Skip(4).Take(3).ToList());
var generation1 = new Generation(1, chromosomes.Skip(7).Take(3).ToList());


var actual = target.SelectChromosomes(2, generation1);
Assert.AreEqual(2, actual.Count);
Assert.AreEqual(9, actual[0].Fitness);
Assert.AreEqual(8, actual[1].Fitness);

actual = target.SelectChromosomes(3, generation2);
Assert.AreEqual(3, actual.Count);
Assert.AreEqual(9, actual[0].Fitness);
Assert.AreEqual(6, actual[1].Fitness);
Assert.AreEqual(5, actual[2].Fitness);

actual = target.SelectChromosomes(2, generation3);
Assert.AreEqual(2, actual.Count);
Assert.AreEqual(9, actual[0].Fitness);
Assert.AreEqual(3, actual[1].Fitness);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,27 @@ public void GetSelectionTypes_NoArgs_AllAvailableSelections()
{
var actual = SelectionService.GetSelectionTypes();

Assert.AreEqual(5, actual.Count);
Assert.AreEqual(6, actual.Count);
Assert.AreEqual(typeof(EliteSelection), actual[0]);
Assert.AreEqual(typeof(RankSelection), actual[1]);
Assert.AreEqual(typeof(RouletteWheelSelection), actual[2]);
Assert.AreEqual(typeof(StochasticUniversalSamplingSelection), actual[3]);
Assert.AreEqual(typeof(TournamentSelection), actual[4]);

Assert.AreEqual(typeof(TruncationSelection), actual[5]);
}

[Test()]
public void GetSelectionNames_NoArgs_AllAvailableSelectionsNames()
{
var actual = SelectionService.GetSelectionNames();

Assert.AreEqual(5, actual.Count);
Assert.AreEqual(6, actual.Count);
Assert.AreEqual("Elite", actual[0]);
Assert.AreEqual("Rank", actual[1]);
Assert.AreEqual("Roulette Wheel", actual[2]);
Assert.AreEqual("Stochastic Universal Sampling", actual[3]);
Assert.AreEqual("Tournament", actual[4]);
Assert.AreEqual("Truncation", actual[5]);
}

[Test()]
Expand All @@ -48,7 +49,7 @@ public void CreateSelectionByName_ValidNameButInvalidConstructorArgs_Exception()
{
Assert.Catch<ArgumentException>(() =>
{
SelectionService.CreateSelectionByName("Elite", 1);
SelectionService.CreateSelectionByName("Elite", 1, 2);
}, "A ISelection's implementation with name 'Elite' was found, but seems the constructor args were invalid.");
}

Expand Down
32 changes: 11 additions & 21 deletions src/GeneticSharp.Domain/Populations/Population.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ namespace GeneticSharp
/// </summary>
public class Population : IPopulation
{
#region Constructors
/// <summary>
/// Occurs when best chromosome changed.
/// </summary>
public event EventHandler BestChromosomeChanged;

/// <summary>
/// Initializes a new instance of the <see cref="GeneticSharp.Domain.Populations.Population"/> class.
/// </summary>
Expand Down Expand Up @@ -36,16 +40,7 @@ public Population(int minSize, int maxSize, IChromosome adamChromosome)
Generations = new List<Generation>();
GenerationStrategy = new PerformanceGenerationStrategy(10);
}
#endregion

#region Events
/// <summary>
/// Occurs when best chromosome changed.
/// </summary>
public event EventHandler BestChromosomeChanged;
#endregion

#region Properties

/// <summary>
/// Gets or sets the creation date.
/// </summary>
Expand Down Expand Up @@ -101,10 +96,8 @@ public Population(int minSize, int maxSize, IChromosome adamChromosome)
/// Gets or sets the original chromosome of all population.
/// </summary>
/// <value>The adam chromosome.</value>
protected IChromosome AdamChromosome { get; set; }
#endregion

#region Public methods
protected IChromosome AdamChromosome { get; set; }

/// <summary>
/// Creates the initial generation.
/// </summary>
Expand Down Expand Up @@ -159,18 +152,15 @@ public virtual void EndCurrentGeneration()

OnBestChromosomeChanged(EventArgs.Empty);
}
}
#endregion

#region Protected methods
}

/// <summary>
/// Raises the best chromosome changed event.
/// </summary>
/// <param name="args">The event arguments.</param>
protected virtual void OnBestChromosomeChanged(EventArgs args)
{
BestChromosomeChanged?.Invoke(this, args);
}
#endregion
}
}
}
35 changes: 27 additions & 8 deletions src/GeneticSharp.Domain/Selections/EliteSelection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,35 @@
namespace GeneticSharp
{
/// <summary>
/// Selects the chromosomes with the best fitness.
/// Selects the chromosomes with the best fitness and a small portion of the best individuals
/// from the last generation is carried over (without any changes) to the next one.
/// </summary>
/// <remarks>
/// Also know as: Truncation Selection.
/// </remarks>
/// <see href="https://en.wikipedia.org/wiki/Selection_(genetic_algorithm)">Wikipedia</see>
/// </remarks>
[DisplayName("Elite")]
public sealed class EliteSelection : SelectionBase
{
#region Constructors
int _previousGenerationChromosomesNumber;
List<IChromosome> _previousGenerationChromosomes = new List<IChromosome>();

/// <summary>
/// Initializes a new instance of the <see cref="GeneticSharp.Domain.Selections.EliteSelection"/> class.
/// </summary>
public EliteSelection() : base(2)
public EliteSelection()
: this(1)
{
}
#endregion

/// <summary>
/// Initializes a new instance of the <see cref="GeneticSharp.Domain.Selections.EliteSelection"/> class.
/// </summary>
/// <param name="previousGenerationChromosomesNumber">The number of best chromosomes of the previous generation to carried over to the next one.</param>
public EliteSelection(int previousGenerationChromosomesNumber)
: base(2)
{
_previousGenerationChromosomesNumber = previousGenerationChromosomesNumber;
}

#region ISelection implementation
/// <summary>
Expand All @@ -31,8 +44,14 @@ public EliteSelection() : base(2)
/// <returns>The select chromosomes.</returns>
protected override IList<IChromosome> PerformSelectChromosomes(int number, Generation generation)
{
var ordered = generation.Chromosomes.OrderByDescending(c => c.Fitness);
return ordered.Take(number).ToList();
_previousGenerationChromosomes.AddRange(generation.Chromosomes);

var ordered = _previousGenerationChromosomes.OrderByDescending(c => c.Fitness);
var result = ordered.Take(number).ToList();

_previousGenerationChromosomes = result.Take(_previousGenerationChromosomesNumber).ToList();

return result;
}

#endregion
Expand Down
12 changes: 3 additions & 9 deletions src/GeneticSharp.Domain/Selections/SelectionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@ namespace GeneticSharp
/// </summary>
public abstract class SelectionBase : ISelection
{
#region Fields
private readonly int m_minNumberChromosomes;
#endregion
readonly int m_minNumberChromosomes;

#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="GeneticSharp.Domain.Selections.SelectionBase"/> class.
/// </summary>
Expand All @@ -22,9 +19,7 @@ protected SelectionBase(int minNumberChromosomes)
{
m_minNumberChromosomes = minNumberChromosomes;
}
#endregion

#region ISelection implementation

/// <summary>
/// Selects the number of chromosomes from the generation specified.
/// </summary>
Expand Down Expand Up @@ -56,7 +51,6 @@ public IList<IChromosome> SelectChromosomes(int number, Generation generation)
/// <returns>The selected chromosomes.</returns>
/// <param name="number">The number of chromosomes to select.</param>
/// <param name="generation">The generation where the selection will be made.</param>
protected abstract IList<IChromosome> PerformSelectChromosomes(int number, Generation generation);
#endregion
protected abstract IList<IChromosome> PerformSelectChromosomes(int number, Generation generation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class MultipleTest
public void Evolve_CompareToSingleChromosome_Evolved()
{
int numberOfCities = 30;
var selection = new EliteSelection();
var selection = new TruncationSelection();
var crossover = new UniformCrossover();
var mutation = new TworsMutation();

Expand Down

0 comments on commit d0a8c60

Please sign in to comment.