Skip to content

Commit

Permalink
CK 170510
Browse files Browse the repository at this point in the history
 - New infrastructure for genetic algorithms including; selection, mutation and crossover with default implementations.
 -- Added random & tounament selection methods
 -- Added point crossover method
 -- Added random, flip & gaussian mutation methods
 - Added new Filters framework along with Roulette Wheel sampling, for sampling objects according to a probability distribution
 - Added extension methods to ease common functions
 - Added new collection (NSortedList) to overcome SortedList limitations for maintaining sorted collections in ascending and descending order.
Adds: #35 (MVP), Tests Passing.
  • Loading branch information
bdschrisk committed Jul 5, 2017
1 parent f7bf398 commit 324d9e2
Show file tree
Hide file tree
Showing 32 changed files with 2,520 additions and 6 deletions.
209 changes: 209 additions & 0 deletions Src/numl.Tests/CollectionTests/CollectionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;

using numl.AI.Collections;

using Xunit;

using MATH = System.Math;
using System.Diagnostics;

namespace numl.Tests.CollectionTests
{
[Trait("CollectionTests", "CollectionTests")]
public class CollectionsTests
{
[Fact]
public void NSortedList_Sort_Test()
{
for (int run = 0; run < 2; run++)
{
bool reverse = (run != 0);

var sorted = new NSortedList<int>(-1, reverse);

int length = Math.Probability.Sampling.GetUniform(10, 1024);

for (int i = 0; i < length; i++)
{
int val = Math.Probability.Sampling.GetUniform(0, length - 1);
sorted.Add(val);
}

int remove = (int) MATH.Ceiling(length / 2.0);
for (int i = 0; i < remove; i++)
{
int index = Math.Probability.Sampling.GetUniform(0, sorted.Count - 1);
sorted.Remove(index);
}

bool result = false;
for (int i = 1; i < (length - remove); i++)
{
if (reverse)
{
result = sorted[i - 1] >= sorted[i];
Assert.True(result, $"Item at {i - 1} was larger than expected");
}
else
{
result = sorted[i - 1] <= sorted[i];
Assert.True(result, $"Item at {i - 1} was smaller than expected");
}
}
}
}

[Fact]
public void NSortedList_Limit_Reverse_Test()
{
for (int run = 0; run < 2; run++)
{
int length = Math.Probability.Sampling.GetUniform(10, 1024);
bool reverse = (run != 0);

var sorted = new NSortedList<int>(length, reverse);

for (int i = 0; i < length; i++)
{
int val = Math.Probability.Sampling.GetUniform(0, length - 1);
sorted.Add(val);
}

length = (length / 2);

sorted.Limit = length;

Assert.True(sorted.Count == length, "Length exceeds specified limit");

for (int i = 0; i < length / 2; i++)
{
int val = Math.Probability.Sampling.GetUniform(0, length - 1);
sorted.Add(val);
}

Assert.True(sorted.Count == length, "Length exceeds specified limit after additions");

bool result = false;
for (int i = 1; i < sorted.Count; i++)
{
if (reverse)
{
result = sorted[i - 1] >= sorted[i];
Assert.True(result, $"Item at {i - 1} was larger than expected");
}
else
{
result = sorted[i - 1] <= sorted[i];
Assert.True(result, $"Item at {i - 1} was smaller than expected");
}
}
}
}

[Fact]
public void NSortedList_Size_Test()
{
var sorted = new NSortedList<int>();

int length = Math.Probability.Sampling.GetUniform(10, 1024);

var dict = new Dictionary<int, int>();

for (int i = 0; i < length; i++)
{
int val = Math.Probability.Sampling.GetUniform(0, length - 1);
sorted.Add(val);

dict[val] = (dict.ContainsKey(val) ? dict[val] + 1 : 1);
}

int remove = (int) MATH.Ceiling(length / 2.0);
for (int i = 0; i < remove; i++)
{
int val = Math.Probability.Sampling.GetUniform(0, sorted.Count - 1);

if (dict.ContainsKey(val))
{
sorted.Remove(val);

dict[val] = 0;

Assert.False(sorted.Contains(val));
}
}

var contains = (dict.Where(w => w.Value == 0 && sorted.Contains(w.Key)));

bool result = (contains.Count() == 0);

Assert.True(result, "Length was not equal to the elements in the collection");

bool lresult = (sorted.Select(s => s).Count() == sorted.Count);

Assert.True(lresult, "Enumerator extends beyond number of accessible items in collection");
}

[Fact]
public void NSortedList_Speed_Test()
{
int epochs = 10000;

Stopwatch timer = new Stopwatch();

timer.Start();

var nsorted = new NSortedList<Foo>();

for (int i = 0; i < epochs; i++)
{
var foo = new Foo();
nsorted.Add(foo);
}

timer.Stop();

long ts1 = timer.ElapsedMilliseconds;

timer.Stop();
Foo._Id = 0;

timer.Start();

var sorted = new SortedList<int, Foo>();

for (int i = 0; i < epochs; i++)
{
var foo = new Foo();
sorted.Add(foo.Weight, foo);
}

timer.Stop();

long ts2 = timer.ElapsedMilliseconds;

Assert.True(ts1 < ts2);
}

public class Foo : IComparable
{
internal static int _Id = 0;

public int Weight { get; set; }

public Foo()
{
this.Weight = (++_Id);
}

public int CompareTo(object obj)
{
var other = (Foo) obj;

return this.Weight.CompareTo(other.Weight);
}
}
}
}
64 changes: 64 additions & 0 deletions Src/numl.Tests/GeneticTests/BaseGenetic.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Text;

using Xunit;

using numl.Math.Probability;
using numl.Math.LinearAlgebra;
using numl.Genetic;
using numl.Genetic.Functions.Crossover;
using numl.Genetic.Functions.Mutation;

namespace numl.Tests.GeneticTests
{
public abstract class BaseGenetic
{

public void Test_Mutation<T>(Func<T> factory, bool binary, int length, Func<IChromosome, IChromosome, bool> fnTest,
Func<int, double> fnSequenceInitializer = null)
where T : IMutationFunction
{
var s = (fnSequenceInitializer == null ?
Vector.Create(length, i => (binary ? (i % 2 == 0 ? 1 : 0) : (i + 1)))
: Vector.Create(length, fnSequenceInitializer));

var parent = new Chromosome()
{
Sequence = s.Copy()
};

var mutator = factory();

var clone = mutator.Mutate(parent);

bool result = fnTest(parent, clone);

Assert.True(result);
}

public void Test_Crossover<T>(Func<T> factory, int length, Func<IChromosome, IChromosome, IChromosome, bool> fnTest)
where T : ICrossoverFunction
{
ICrossoverFunction crossover = factory();

var s1 = Vector.Create(length, i => (i + 1) * 100);
var parent1 = new Chromosome()
{
Sequence = s1.Copy()
};

var s2 = Vector.Create(length, i => (i + 1));
var parent2 = new Chromosome()
{
Sequence = s2.Copy()
};

var child = crossover.Crossover(parent1, parent2);

bool result = fnTest(parent1, parent2, child);

Assert.True(result);
}
}
}
85 changes: 85 additions & 0 deletions Src/numl.Tests/GeneticTests/CrossoverTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Xunit;

using numl.Genetic;
using numl.Genetic.Functions.Crossover;

using MATH = System.Math;

namespace numl.Tests.GeneticTests
{
[Trait("Category", "Genetic")]
public class CrossoverTests : BaseGenetic
{
[Fact]
public void Test_Point_Crossover()
{
double prob = 0.25;
int points = 5;
int length = 50;

while (prob <= 1.0)
{
var crossover = new PointCrossover(points) { Probability = prob };

this.Test_Crossover(() => crossover, length,
(p1, p2, child) =>
{
bool result = false;

var sequence = child.Sequence;

double count1 = p1.Sequence.Intersect(sequence).Count();
double count2 = p2.Sequence.Intersect(sequence).Count();

double p = (MATH.Pow(prob, points)) / 2;

double c1 = 0, c2 = 0;

int i; double diff; double t1 = 0, t2 = 0;
for (i = 0; i < crossover.Points.Length - 1; i++)
{
diff = (crossover.Points[i + 1] - crossover.Points[i]);

if (i % 2 == 0)
{
c1 += diff;
t1 += (diff / length) * (1.0 - p);
}
else
{
c2 += diff;
t2 += (diff / length) * p;
}
}

diff = length - crossover.Points[i];

if (i % 2 == 0)
{
c1 += diff;
t1 += (diff / length) * (1.0 - p);
}
else
{
c2 += diff;
t2 += (diff / length) * p;
}

t1 = t1 / (t1 + t2);
t2 = t2 / (t1 + t2);

result = (t1 >= (1.0 - p * 2.0) && t2 <= p * 2.0);

return result;
});

prob += 0.25;
}
}
}
}
Loading

0 comments on commit 324d9e2

Please sign in to comment.