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

Experimental code for upcoming genetic ai #34

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
115 changes: 115 additions & 0 deletions src/Genetics/Chromosomes/StringChromosome.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
namespace AiDotNet.Genetics.Chromosomes;

public class StringChromosome : IChromosome<string>
{
public double FitnessScore { get; }
public string Chromosome { get; private set; }
public int Size { get; }

private string Genes { get; }
private ChromosomeOptions<string> ChromosomeOptions { get; }

private StringChromosome(StringChromosome source)
{
Chromosome = (string)source.Chromosome.Clone();
FitnessScore = source.FitnessScore;
Genes = source.Genes;
Size = source.Size;
ChromosomeOptions = source.ChromosomeOptions;
}

public StringChromosome(ChromosomeOptions<string> chromosomeOptions,
string genes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890, .-;:_!\"#%&/()=?@${[]}")
{
ChromosomeOptions = chromosomeOptions;
Chromosome = Generate();
FitnessScore = CalculateFitnessScore();
Genes = genes;
}

public void Mutate()
{
var mutationGene = ChromosomeOptions.RandomGenerator.Next(Size);
var tempArray = Chromosome.ToCharArray();

if (ChromosomeOptions.RandomGenerator.NextDouble() < ChromosomeOptions.MutationBalancer)
{

tempArray[mutationGene] = Genes[ChromosomeOptions.MutationMultiplierGenerator.Next(0, 1)];
}
else
{
tempArray[mutationGene] = Genes[ChromosomeOptions.MutationAdditionGenerator.Next(-1, 1)];
}

Chromosome = tempArray.ToString() ?? Chromosome;
}

public string Generate()
{
var chromosome = string.Empty;
for (var i = 0; i < ChromosomeOptions.Target.Length; i++)
{
chromosome += CreateRandomGene();
}

return chromosome;
}

private char CreateRandomGene()
{
var index = ChromosomeOptions.RandomGenerator.Next(0, Genes.Length);

return Genes[index];
}

public IChromosome<string> Clone()
{
return new StringChromosome(this);
}

public IChromosome<string> CreateNew()
{
return new StringChromosome(ChromosomeOptions);
}

public double CalculateFitnessScore()
{
double fitnessScore = 0;
for (var i = 0; i < ChromosomeOptions.Target.Length; i++)
{
fitnessScore += ChromosomeOptions.Target[i] != Chromosome[i] ? 1 : 0;
}

return fitnessScore;
}

public string Crossover(IChromosome<string> chromosome)
{
var newChromosome = string.Empty;
for (var i = 0; i < Chromosome.Length; i++)
{
var probability = ChromosomeOptions.RandomGenerator.NextDouble();

if (probability < ChromosomeOptions.CrossoverBalancer)
{
newChromosome += Chromosome[i];
}
else if (probability > ChromosomeOptions.CrossoverBalancer)
{
newChromosome += chromosome.Chromosome[i];
}
else
{
newChromosome += CreateRandomGene();
}
}

return newChromosome;
}

public int CompareTo(IChromosome<string>? otherChromosome)
{
return FitnessScore.CompareTo(otherChromosome?.FitnessScore);
}
}
11 changes: 11 additions & 0 deletions src/Genetics/GeneticsAi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace AiDotNet.Genetics;

public class GeneticsAi<T>
{
public GeneticsAi(IChromosome<T> chromosome, GeneticAiOptions<T> geneticAiOptions)
{
var test = new GeneticsFacade<T>(chromosome, geneticAiOptions);

var test2 = new StringChromosome(new ChromosomeOptions<string>());
}
}
121 changes: 121 additions & 0 deletions src/Genetics/GeneticsFacade.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
namespace AiDotNet.Genetics;

internal class GeneticsFacade<T> : IGenetics<T>
{
private Random RandomGenerator { get; } = new();
public int PopulationSize { get; }
public List<IChromosome<T>> Population { get; }
public ISelectionMethod<T> SelectionMethod { get; }
public double RandomSelectionPortion { get; }
public bool AutoShuffle { get; }
public double CrossoverRate { get; }
public double MutationRate { get; }

public GeneticsFacade(IChromosome<T> chromosome, GeneticAiOptions<T> geneticAiOptions)
{
PopulationSize = geneticAiOptions.PopulationSize;
RandomSelectionPortion = geneticAiOptions.RandomSelectionPortion;
SelectionMethod = geneticAiOptions.SelectionMethod;
AutoShuffle = geneticAiOptions.AutoShuffle;
CrossoverRate = geneticAiOptions.CrossoverRate;
MutationRate = geneticAiOptions.MutationRate;
Population = new List<IChromosome<T>>(PopulationSize);

GeneratePopulation(chromosome);
RunGeneration();
}

public void GeneratePopulation(IChromosome<T> chromosome)
{
// add more chromosomes to the population
for (var i = 0; i < PopulationSize; i++)
{
// create new chromosome
var c = chromosome.CreateNew();
// calculate it's fitness
c.CalculateFitnessScore();
// add it to population
Population.Add(c);
}
}

public void RunGeneration()
{
// do crossover
Crossover();

// do mutation
Mutation();

// do selection
Selection();

// shuffle population
if (AutoShuffle) Population.Shuffle();
}

public void Mutation()
{
for (var i = 0; i < PopulationSize; i++)
{
// generate next random number and check if we need to do mutation
if (RandomGenerator.NextDouble() > MutationRate) continue;

// clone the chromosome
var c = Population[i].Clone();
// mutate it
c.Mutate();
// calculate fitness of the mutant
c.CalculateFitnessScore();
// add mutant to the population
Population.Add(c);
}
}

public void Crossover()
{
for (var i = 1; i < PopulationSize; i += 2)
{
// generate next random number and check if we need to do crossover
if (RandomGenerator.NextDouble() > CrossoverRate) continue;

// clone both ancestors
var c1 = Population[i - 1].Clone();
var c2 = Population[i].Clone();

// do crossover
c1.Crossover(c2);

// calculate fitness of these two offsprings
c1.CalculateFitnessScore();
c2.CalculateFitnessScore();

// add two new offsprings to the population
Population.Add(c1);
Population.Add(c2);
}
}

public void Selection()
{
// amount of random chromosomes in the new population
var randomAmount = (int)(RandomSelectionPortion * PopulationSize);

// do selection
SelectionMethod.ApplySelection(Population, PopulationSize - randomAmount);

// add random chromosomes
if (randomAmount <= 0) return;
var chromosome = Population[0];

for (var i = 0; i < randomAmount; i++)
{
// create new chromosome
var c = chromosome.CreateNew();
// calculate it's fitness
c.CalculateFitnessScore();
// add it to population
Population.Add(c);
}
}
}
13 changes: 13 additions & 0 deletions src/Genetics/SelectionMethods/EliteSelection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace AiDotNet.Genetics.SelectionMethods;

public class EliteSelection<T> : ISelectionMethod<T>
{
public void ApplySelection(List<IChromosome<T>> chromosomes, int size)
{
// sort chromosomes
chromosomes.Sort();

// remove bad chromosomes
chromosomes.RemoveRange(size, chromosomes.Count - size);
}
}
20 changes: 20 additions & 0 deletions src/Helpers/GeneticsHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace AiDotNet.Helpers;

internal static class GeneticsHelper
{
public static void Shuffle<T>(this IList<T> list)
{
var provider = RandomNumberGenerator.Create();
var n = list.Count;

while (n > 1)
{
var box = new byte[1];
do provider.GetBytes(box);
while (!(box[0] < n * (byte.MaxValue / n)));
var k = box[0] % n;
n--;
(list[n], list[k]) = (list[k], list[n]);
}
}
}
5 changes: 4 additions & 1 deletion src/Helpers/UsingsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
global using AiDotNet.Statistics;
global using AiDotNet.Enums;
global using MathNet.Numerics.LinearAlgebra;
global using MetricsHelper = AiDotNet.Helpers.MetricsHelper;
global using System.Security.Cryptography;
global using AiDotNet.Genetics.SelectionMethods;
global using AiDotNet.Genetics.Chromosomes;
global using AiDotNet.Quartile;
global using MetricsHelper = AiDotNet.Helpers.MetricsHelper;
20 changes: 20 additions & 0 deletions src/Interfaces/IChromosome.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace AiDotNet.Interfaces;

public interface IChromosome<T> : IComparable<IChromosome<T>>
{
double FitnessScore { get; }

T Chromosome { get; }

public void Mutate();

public T Crossover(IChromosome<T> chromosome);

public T Generate();

public IChromosome<T> Clone();

public IChromosome<T> CreateNew();

public double CalculateFitnessScore();
}
22 changes: 22 additions & 0 deletions src/Interfaces/IGenetics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace AiDotNet.Interfaces;

public interface IGenetics<T>
{
public int PopulationSize { get; }
public List<IChromosome<T>> Population { get; }
public ISelectionMethod<T> SelectionMethod { get; }
public double RandomSelectionPortion { get; }
public bool AutoShuffle { get; }
public double CrossoverRate { get; }
public double MutationRate { get; }

public void GeneratePopulation(IChromosome<T> chromosome);

public void RunGeneration();

public void Mutation();

public void Crossover();

public void Selection();
}
6 changes: 6 additions & 0 deletions src/Interfaces/ISelectionMethod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace AiDotNet.Interfaces;

public interface ISelectionMethod<T>
{
public void ApplySelection(List<IChromosome<T>> chromosomes, int size);
}
11 changes: 11 additions & 0 deletions src/Models/ChromosomeOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace AiDotNet.Models;

public class ChromosomeOptions<T>
{
public double CrossoverBalancer { get; set; } = 0.5;
public double MutationBalancer { get; set; } = 0.5;
public T Target { get; set; } = default!;
public Random MutationMultiplierGenerator { get; set; } = new();
public Random MutationAdditionGenerator { get; set; } = new();
public Random RandomGenerator { get; set; } = new();
}
11 changes: 11 additions & 0 deletions src/Models/GeneticAiOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace AiDotNet.Models;

public class GeneticAiOptions<T>
{
public int PopulationSize { get; set; } = 500;
public double RandomSelectionPortion { get; set; } = 0.1;
public bool AutoShuffle { get; set; } = false;
public double CrossoverRate { get; set; } = 0.75;
public double MutationRate { get; set; } = 0.1;
public ISelectionMethod<T> SelectionMethod { get; set; } = new EliteSelection<T>();
}
4 changes: 4 additions & 0 deletions testconsole/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
using AiDotNet.Normalization;
using AiDotNet.OutlierRemoval;
using AiDotNet.Regression;
using AiDotNet.Genetics;
using AiDotNet.Genetics.Chromosomes;
using AiDotNet.Genetics.SelectionMethods;

namespace AiDotNetTestConsole;

Expand Down Expand Up @@ -40,6 +43,7 @@ static void Main(string[] args)
int[] keySort = Enumerable.Range(0, arrayOne.Length).ToArray();
var stringOne = new string[] {"four", "one", "three", "two" };

var test = new GeneticsAi<string>(new StringChromosome(new ChromosomeOptions<string>()), new GeneticAiOptions<string>());
//Sort by Inputs
Array.Sort(arrayOne, keySort);
PrintToConsole(keySort);
Expand Down
Loading