-
Notifications
You must be signed in to change notification settings - Fork 104
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- 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
Showing
32 changed files
with
2,520 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.