Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Sudoku extension and GTK# sample #43

Merged
merged 18 commits into from
Nov 3, 2018
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 11 additions & 18 deletions src/GeneticSharp.Domain/GeneticAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using GeneticSharp.Domain.Chromosomes;
using GeneticSharp.Domain.Crossovers;
Expand Down Expand Up @@ -79,6 +80,7 @@ public sealed class GeneticAlgorithm : IGeneticAlgorithm
private bool m_stopRequested;
private readonly object m_lock = new object();
private GeneticAlgorithmState m_state;
private Stopwatch m_stopwatch;
#endregion

#region Constructors
Expand Down Expand Up @@ -231,11 +233,7 @@ private set

if (shouldStop)
{
var handler = Stopped;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
Stopped?.Invoke(this, EventArgs.Empty);
}
}
}
Expand Down Expand Up @@ -268,9 +266,10 @@ public void Start()
lock (m_lock)
{
State = GeneticAlgorithmState.Started;
var startDateTime = DateTime.Now;
m_stopwatch = Stopwatch.StartNew();
Population.CreateInitialGeneration();
TimeEvolving = DateTime.Now - startDateTime;
m_stopwatch.Stop();
TimeEvolving = m_stopwatch.Elapsed;
}

Resume();
Expand Down Expand Up @@ -312,7 +311,6 @@ public void Resume()
}

bool terminationConditionReached = false;
DateTime startDateTime;

do
{
Expand All @@ -321,9 +319,10 @@ public void Resume()
break;
}

startDateTime = DateTime.Now;
m_stopwatch.Restart();
terminationConditionReached = EvolveOneGeneration();
TimeEvolving += DateTime.Now - startDateTime;
m_stopwatch.Stop();
TimeEvolving += m_stopwatch.Elapsed;
}
while (!terminationConditionReached);
}
Expand Down Expand Up @@ -374,20 +373,14 @@ private bool EndCurrentGeneration()
Population.EndCurrentGeneration();

var handler = GenerationRan;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
handler?.Invoke(this, EventArgs.Empty);

if (Termination.HasReached(this))
{
State = GeneticAlgorithmState.TerminationReached;

handler = TerminationReached;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
handler?.Invoke(this, EventArgs.Empty);

return true;
}
Expand Down
71 changes: 71 additions & 0 deletions src/GeneticSharp.Extensions.UnitTests/Multiple/MultipleTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GeneticSharp.Domain;
using GeneticSharp.Domain.Chromosomes;
using GeneticSharp.Domain.Crossovers;
using GeneticSharp.Domain.Fitnesses;
using GeneticSharp.Domain.Mutations;
using GeneticSharp.Domain.Populations;
using GeneticSharp.Domain.Selections;
using GeneticSharp.Domain.Terminations;
using GeneticSharp.Extensions.Multiple;
using GeneticSharp.Extensions.Tsp;
using NUnit.Framework;

namespace GeneticSharp.Extensions.UnitTests.Multiple
{


[TestFixture]
[Category("Extensions")]
class MultipleTest
{

[Test()]
public void Evolve_ManyGenerations_Fast()
{
int numberOfCities = 30;
var selection = new EliteSelection();
var crossover = new UniformCrossover();
var mutation = new TworsMutation();


// Given enough generations, the Multiple Chromosome should start exhibiting convergence
// we compare TSP /25 gen with 3*TSP / 500 gen

IChromosome chromosome = new TspChromosome(numberOfCities);
IFitness fitness = new TspFitness(numberOfCities, 0, 1000, 0, 1000);
var population = new Population(30, 30, chromosome);
var ga = new GeneticAlgorithm(population, fitness, selection, crossover, mutation)
{
Termination = new GenerationNumberTermination(26)
};
ga.Start();
var simpleChromosomeDistance = ((TspChromosome)ga.Population.BestChromosome).Distance;

chromosome = new MultipleChromosome((i) => new TspChromosome(numberOfCities), 3);
//MultiChromosome should create 3 TSP chromosomes and store them in the corresponding property
Assert.AreEqual(((MultipleChromosome)chromosome).Chromosomes.Count, 3);
var tempMultiFitness = ((MultipleChromosome)chromosome).Chromosomes.Sum(c => fitness.Evaluate(c));
fitness = new MultipleFitness(fitness);
//Multi fitness should sum over the fitnesses of individual chromosomes
Assert.AreEqual(tempMultiFitness, fitness.Evaluate(chromosome));
population = new Population(30, 30, chromosome);
ga = new GeneticAlgorithm(population, fitness, selection, crossover, mutation)
{
Termination = new GenerationNumberTermination(501)
};
ga.Start();
var bestTSPChromosome = (TspChromosome)((MultipleChromosome)ga.Population.BestChromosome).Chromosomes
.OrderByDescending(c => c.Fitness).First();
var multiChromosomesDistance = bestTSPChromosome.Distance;

Assert.Less(multiChromosomesDistance, simpleChromosomeDistance);
}



}
}
122 changes: 122 additions & 0 deletions src/GeneticSharp.Extensions.UnitTests/Sudoku/SudokuTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GeneticSharp.Domain;
using GeneticSharp.Domain.Chromosomes;
using GeneticSharp.Domain.Crossovers;
using GeneticSharp.Domain.Mutations;
using GeneticSharp.Domain.Populations;
using GeneticSharp.Domain.Selections;
using GeneticSharp.Domain.Terminations;
using GeneticSharp.Extensions.Sudoku;
using NUnit.Framework;

namespace GeneticSharp.Extensions.UnitTests.Sudoku
{


[TestFixture()]
[Category("Extensions")]
public class SudokuTest
{

private readonly string _easySudokuString = "9.2..54.31...63.255.84.7.6..263.9..1.57.1.29..9.67.53.24.53.6..7.52..3.4.8..4195.";

/// <summary>
/// The sample sudoku string should parse properly into corresponding cells
/// </summary>
[Test()]
public void ParseSudoku()
{

var sudoku = Extensions.Sudoku.SudokuBoard.Parse(_easySudokuString);

Assert.AreEqual(sudoku.CellsList[0], 9);
Assert.AreEqual(sudoku.CellsList[1], 0);
Assert.AreEqual(sudoku.CellsList[2], 2);
Assert.AreEqual(sudoku.CellsList[sudoku.CellsList.Count - 2], 5);
Assert.AreEqual(sudoku.CellsList[sudoku.CellsList.Count - 1], 0);

}


/// <summary>
/// The permutation chromosome should always solve the sudoku in a reasonable time with 1000 chromosomes
/// </summary>
[Test()]
public void Solve_sudoku_with_permutations()
{
var sudoku = Extensions.Sudoku.SudokuBoard.Parse(_easySudokuString);

IChromosome chromosome = new SudokuPermutationsChromosome(sudoku);
var fitness = EvaluatesSudokuChromosome(chromosome, sudoku, 1000, 0, 50);
Assert.AreEqual(fitness, 0);

}

/// <summary>
/// The cells chromosome might need more individuals, so in order to keep execution time low, we only expect near completion
/// </summary>
[Test()]
public void Nearly_solve_sudoku_with_Cells()
{
var sudoku = Extensions.Sudoku.SudokuBoard.Parse(_easySudokuString);

//the cells chromosome should solve the sudoku or nearly in less than 50 generations with 500 chromosomes
var chromosome = new SudokuCellsChromosome(sudoku);
var fitness = EvaluatesSudokuChromosome(chromosome, sudoku, 500, -20, 30);
Assert.GreaterOrEqual(fitness, -20);

}

/// <summary>
/// The random permutations chromosome require more individuals and generations, so we only test for significant progresses
/// </summary>
[Test()]
public void Make_Progresses_with_random_permutations()
{
var sudoku = Extensions.Sudoku.SudokuBoard.Parse(_easySudokuString);


//the Random permutations chromosome should make significant progresses over 3 generations with 5 individuals

var chromosome = new SudokuRandomPermutationsChromosome(sudoku, 2, 3);
var fitness1 = new SudokuFitness(sudoku).Evaluate((ISudokuChromosome)chromosome);
var fitness2 = EvaluatesSudokuChromosome(chromosome, sudoku, 5, fitness1 + 20, 3);
Assert.GreaterOrEqual(fitness2, fitness1 + 20);

}



private double EvaluatesSudokuChromosome(IChromosome sudokuChromosome, Extensions.Sudoku.SudokuBoard sudokuBoard, int populationSize, double fitnessThreshold, int generationNb)
{
var fitness = new SudokuFitness(sudokuBoard);
var selection = new EliteSelection();
var crossover = new UniformCrossover();
var mutation = new UniformMutation();

var population = new Population(populationSize, populationSize, sudokuChromosome);
var ga = new GeneticAlgorithm(population, fitness, selection, crossover, mutation)
{
Termination = new OrTermination(new ITermination[]
{
new FitnessThresholdTermination(fitnessThreshold),
new GenerationNumberTermination(generationNb)
})
};

ga.Start();

var bestIndividual = ((ISudokuChromosome)ga.Population.BestChromosome);
var solutions = bestIndividual.GetSudokus();
return solutions.Max(solutionSudoku => fitness.Evaluate(solutionSudoku));
}

}




}
2 changes: 1 addition & 1 deletion src/GeneticSharp.Extensions/GeneticSharp.Extensions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@

<ItemGroup>
<ProjectReference Include="..\GeneticSharp.Domain\GeneticSharp.Domain.csproj" />
<ProjectReference Include="..\GeneticSharp.Infrastructure.Framework\GeneticSharp.Infrastructure.Framework.csproj" PrivateAssets="all"/>
<ProjectReference Include="..\GeneticSharp.Infrastructure.Framework\GeneticSharp.Infrastructure.Framework.csproj" PrivateAssets="all" />
</ItemGroup>
</Project>
75 changes: 75 additions & 0 deletions src/GeneticSharp.Extensions/Multiple/MultipleChromosome.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GeneticSharp.Domain.Chromosomes;

namespace GeneticSharp.Extensions.Multiple
{

/// <summary>
/// Compound chromosome to artificially increase genetics diversity by evolving a list of chromosome instead of just one.
/// Sub-genes are inlined into a single compound list of genes
/// </summary>
public class MultipleChromosome : ChromosomeBase
{

/// <summary>
/// Child chromosomes are stored in a list
/// </summary>
public IList<IChromosome> Chromosomes { get; set; }

/// <summary>
/// Constructor accepting a delegate to create chromosomes
/// </summary>
/// <param name="createFunc">a function that create child chromosomes from index</param>
/// <param name="nbChromosomes">Number of child chromosomes to use</param>
public MultipleChromosome(Func<int, IChromosome> createFunc, int nbChromosomes) : this(new int[nbChromosomes].Select(createFunc).ToList())
{
}

/// <summary>
/// Constructor accepting a list of child chromosomes
/// </summary>
/// <param name="chromosomes"></param>
public MultipleChromosome(IList<IChromosome> chromosomes) : base(chromosomes.Count * chromosomes[0].Length)
{
Chromosomes = chromosomes;
for (int i = 0; i < Length; i++)
{
ReplaceGene(i, GenerateGene(i));
}

UpdateSubGenes();
}

/// <summary>
/// Generates the parent genes by inlining child genes
/// </summary>
/// <param name="geneIndex">the gene index in parent chromosome</param>
/// <returns></returns>
public override Gene GenerateGene(int geneIndex)
{
return Chromosomes[geneIndex / Chromosomes[0].Length]
.GenerateGene(geneIndex - ((geneIndex / Chromosomes[0].Length) * Chromosomes[0].Length));
}


public override IChromosome CreateNew()
{
return new MultipleChromosome(Chromosomes.Select(c => c.CreateNew()).ToList());
}

/// <summary>
/// Since the ReplaceGene is not virtual, a call to this method is required at evaluation time
/// </summary>
public void UpdateSubGenes()
{
for (int i = 0; i < Length; i++)
{
Chromosomes[i / Chromosomes[0].Length].ReplaceGene(i - ((i / Chromosomes[0].Length) * Chromosomes[0].Length), GetGene(i));
}

}
}
}
Loading