Skip to content

Commit

Permalink
Fixed issues with random not being thread-safe
Browse files Browse the repository at this point in the history
  • Loading branch information
ZviRosenfeld committed Jul 5, 2020
1 parent 93cfc4f commit a2214af
Show file tree
Hide file tree
Showing 49 changed files with 444 additions and 186 deletions.
6 changes: 3 additions & 3 deletions Banchmarking/Benchmarking.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

<ItemGroup>
<PackageReference Include="FakeItEasy" Version="5.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
<PackageReference Include="MSTest.TestFramework" Version="1.3.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
</ItemGroup>

<ItemGroup>
Expand Down
8 changes: 4 additions & 4 deletions GeneticAlgorithm.UnitTests/ChildrenGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public void TestMutationsHappen(double mutationProbability)
var crossoverManager = A.Fake<ICrossoverManager>();
A.CallTo(() => crossoverManager.Crossover(A<IChromosome>._, A<IChromosome>._))
.ReturnsLazily((IChromosome c1, IChromosome c2) => c1);
var childrenGenerator = new ChildrenGenerator(crossoverManager, new BassicMutationProbabilityManager(mutationProbability), new RouletteWheelSelection());
var childrenGenerator = new ChildrenGenerator(crossoverManager, new BasicMutationProbabilityManager(mutationProbability), new RouletteWheelSelection());
childrenGenerator.GenerateChildren(population, populationSize, 0, null);

const double errorMargin = 0.05;
Expand All @@ -44,7 +44,7 @@ public void RetusnRightNumberOfChromosomes(int childrenCount)
{
const int count = 1500;
var crossoverManager = A.Fake<ICrossoverManager>();
var childrenGenerator = new ChildrenGenerator(crossoverManager, new BassicMutationProbabilityManager(0), new RouletteWheelSelection());
var childrenGenerator = new ChildrenGenerator(crossoverManager, new BasicMutationProbabilityManager(0), new RouletteWheelSelection());
var children = childrenGenerator.GenerateChildren(GetPopulation(count), childrenCount, 0, null);
Assert.AreEqual(childrenCount, children.Length, "Didn't get enough children");
foreach (var chromosome in children)
Expand All @@ -71,7 +71,7 @@ public void BadMutationProbability_ThrowException(double probability)
[DataRow(-1)]
public void RequestBadNumberOfChildren_ThrowsException(int childrenCount)
{
var childrenGenerator = new ChildrenGenerator(A.Fake<ICrossoverManager>(), new BassicMutationProbabilityManager(0),
var childrenGenerator = new ChildrenGenerator(A.Fake<ICrossoverManager>(), new BasicMutationProbabilityManager(0),
new RouletteWheelSelection());
childrenGenerator.GenerateChildren(GetPopulation(1), childrenCount, 0, null);
}
Expand All @@ -83,7 +83,7 @@ public void SelectionStrategyReturnsNull_ThrowsException()
{
var selectionStrategy = A.Fake<ISelectionStrategy>();
A.CallTo(() => selectionStrategy.SelectChromosome()).Returns(null);
var childrenGenerator = new ChildrenGenerator(A.Fake<ICrossoverManager>(), new BassicMutationProbabilityManager(0),
var childrenGenerator = new ChildrenGenerator(A.Fake<ICrossoverManager>(), new BasicMutationProbabilityManager(0),
selectionStrategy);
childrenGenerator.GenerateChildren(GetPopulation(1), 1, 0, null);
}, typeof(GeneticAlgorithmException));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ public void GetNeigbor_GetRandomNeigbor(bool selectNeighborWithLeastNeighbors)
}

var excpectedToGetAtLeast = selectNeighborWithLeastNeighbors ? 35 : 25;
Assert.IsTrue(excpectedToGetAtLeast < got2);
Assert.IsTrue(excpectedToGetAtLeast < got5);
Assert.IsTrue(excpectedToGetAtLeast < got2, $"Got 2 only {got2} times");
Assert.IsTrue(excpectedToGetAtLeast < got5, $"Got 5 only {got5} times");
if (!selectNeighborWithLeastNeighbors)
Assert.IsTrue(excpectedToGetAtLeast < got4);
Assert.IsTrue(excpectedToGetAtLeast < got4, $"Got 4 only {got4} times");
}

[TestMethod]
Expand Down
91 changes: 79 additions & 12 deletions GeneticAlgorithm.UnitTests/ExceptionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using FakeItEasy;
using GeneticAlgorithm.Exceptions;
using GeneticAlgorithm.Interfaces;
using GeneticAlgorithm.PopulationRenwalManagers;
using GeneticAlgorithm.StopManagers;
using GeneticAlgorithm.UnitTests.TestUtils;
using Microsoft.VisualStudio.TestTools.UnitTesting;

Expand All @@ -21,7 +23,7 @@ public void SetNegativePopulationSize_ThrowsException(int size) =>
.Build();

[TestMethod]
[ExpectedException(typeof(GeneticAlgorithmException))]
[ExpectedException(typeof(GeneticAlgorithmArgumentException))]
[DataRow(1)]
[DataRow(-2)]
public void BadNumberOfGenerations_ThrowsException(int size) =>
Expand Down Expand Up @@ -107,30 +109,95 @@ public void GetCurrentPopulation_EngineRunning_ThrowException()
[ExpectedException(typeof(EngineAlreadyRunningException))]
public void SetCurrentPopulation_EngineRunning_ThrowException()
{
var engine =
new TestGeneticSearchEngineBuilder(2, int.MaxValue, new TestPopulationManager(new double[] { 2, 2 })).Build();
Utils.RunTimedTest(() =>
{
var engine =
new TestGeneticSearchEngineBuilder(2, int.MaxValue, new TestPopulationManager(new double[] { 2, 2 })).Build();

Task.Run(() => engine.Run());
while (!engine.IsRunning) ;
Task.Run(() => engine.Run());
while (!engine.IsRunning) ;

engine.SetCurrentPopulation(new double[] { 3 ,3 }.ToChromosomes("Converted"));
engine.SetCurrentPopulation(new double[] { 3, 3 }.ToChromosomes("Converted"));

Assert.Fail("Should have thrown an exception by now");
Assert.Fail("Should have thrown an exception by now");
});
}

[TestMethod]
[ExpectedException(typeof(GeneticAlgorithmException))]
public void SetCurrentPopulation_WrongNumberOfChromosomes_ThrowException()
{
var engine =
Utils.RunTimedTest(() =>
{
var engine =
new TestGeneticSearchEngineBuilder(2, int.MaxValue, new TestPopulationManager(new double[] { 2, 2 })).Build();

Task.Run(() => engine.Run());
while (!engine.IsRunning) ;
Task.Run(() => engine.Run());
while (!engine.IsRunning) ;

engine.SetCurrentPopulation(new double[] { 3, 3 ,3 }.ToChromosomes("Converted"));
engine.SetCurrentPopulation(new double[] { 3, 3, 3 }.ToChromosomes("Converted"));

Assert.Fail("Should have thrown an exception by now");
Assert.Fail("Should have thrown an exception by now");
});
}

[TestMethod]
[ExpectedException(typeof(GeneticAlgorithmArgumentException))]
public void StopAtConvergence_DiffNegative_ThrowException() =>
new StopAtConvergence(-0.1);

[TestMethod]
[ExpectedException(typeof(GeneticAlgorithmArgumentException))]
public void StopAtEvaluation_EvaluationNegative_ThrowException() =>
new StopAtEvaluation(-0.1);

[TestMethod]
[ExpectedException(typeof(GeneticAlgorithmArgumentException))]
public void StopAtGeneration_BadGeneration_ThrowException() =>
new StopAtGeneration(1);

[TestMethod]
[ExpectedException(typeof(GeneticAlgorithmArgumentException))]
public void StopIfNoImprovment_BadGenerationsToConsider_ThrowException() =>
new StopIfNoImprovment(0, 0.5);

[TestMethod]
[ExpectedException(typeof(GeneticAlgorithmArgumentException))]
public void RenewIfNoImprovment_BadGenerationsToConsider_ThrowException() =>
new RenewIfNoImprovment(0, 1, 0.5);

[TestMethod]
[DataRow(-0.1)]
[DataRow(0)]
[DataRow(1.1)]
[ExpectedException(typeof(GeneticAlgorithmArgumentException))]
public void RenewIfNoImprovment_BadPrecentageToRenew_ThrowException(double precentageToRenew) =>
new RenewIfNoImprovment(1, 0.5, precentageToRenew);

[TestMethod]
[ExpectedException(typeof(GeneticAlgorithmArgumentException))]
public void RenewAtConvergence_BadGenerationsToConsider_ThrowException() =>
new RenewAtConvergence(-0.1, 0.5);

[TestMethod]
[DataRow(-0.1)]
[DataRow(0)]
[DataRow(1.1)]
[ExpectedException(typeof(GeneticAlgorithmArgumentException))]
public void RenewAtConvergence_BadPrecentageToRenew_ThrowException(double precentageToRenew) =>
new RenewAtConvergence(1, precentageToRenew);

[TestMethod]
[ExpectedException(typeof(GeneticAlgorithmArgumentException))]
public void RenewAtDifferenceBetweenAverageAndMaximumFitness_NegativeDiff_ThrowException() =>
new RenewAtDifferenceBetweenAverageAndMaximumFitness(-0.1, 0.5);

[TestMethod]
[DataRow(-0.1)]
[DataRow(0)]
[DataRow(1.1)]
[ExpectedException(typeof(GeneticAlgorithmArgumentException))]
public void RenewAtDifferenceBetweenAverageAndMaximumFitness_BadPrecentageToRenew_ThrowException(double precentageToRenew) =>
new RenewAtDifferenceBetweenAverageAndMaximumFitness(1, precentageToRenew);
}
}
6 changes: 3 additions & 3 deletions GeneticAlgorithm.UnitTests/GeneticAlgorithm.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

<ItemGroup>
<PackageReference Include="FakeItEasy" Version="5.1.1" />
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion GeneticAlgorithm.UnitTests/NumberVectorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public NumberVectorTests()
[TestMethod]
[DataRow(RunType.Run)]
[DataRow(RunType.Next)]
public void BassicTest(RunType runType)
public void BasicTest(RunType runType)
{
var searchEngine =
new GeneticSearchEngineBuilder(POPULATION_SIZE, 50, crossoverManager, populationGenerator)
Expand Down
21 changes: 19 additions & 2 deletions GeneticAlgorithm.UnitTests/PopulationRenwalManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ public void RenewAtConvergenceTest(RunType runType)
{
var populationManager = new TestPopulationManager(new double[] {1, 1, 1});
populationManager.SetPopulationGenerated(new[]
{new double[] {2, 2, 2}, new double[] {3, 3, 3}, new double[] {4, 4, 4}});
{new double[] {2, 3, 2}, new double[] {4, 4, 4}, new double[] {5, 5, 5}});
var engine = new TestGeneticSearchEngineBuilder(POPULATION_SIZE, 4, populationManager)
.AddPopulationRenwalManager(new RenewAtConvergence(0.9, 1))
.IncludeAllHistory().Build();

var result = engine.Run(runType);

Assert.AreEqual(4, result.BestChromosome.Evaluate());
Assert.AreEqual(3, result.BestChromosome.Evaluate());
}

[TestMethod]
Expand Down Expand Up @@ -76,6 +76,23 @@ public void RenewIfNoImprovmentTest2(RunType runType)
Assert.AreEqual(2, result.BestChromosome.Evaluate());
}

[TestMethod]
[DataRow(RunType.Run)]
[DataRow(RunType.Next)]
public void RenewAtDifferenceBetweenAverageAndMaximumFitnessTest(RunType runType)
{
var populationManager = new TestPopulationManager(new double[] { 1, 2, 1 });
populationManager.SetPopulationGenerated(new[]
{new double[] {2, 5, 2}, new double[] {6, 6, 6}, new double[] {7, 7, 7}});
var engine = new TestGeneticSearchEngineBuilder(POPULATION_SIZE, 4, populationManager)
.AddPopulationRenwalManager(new RenewAtDifferenceBetweenAverageAndMaximumFitness(1, 1))
.IncludeAllHistory().Build();

var result = engine.Run(runType);

Assert.AreEqual(5, result.BestChromosome.Evaluate());
}

[TestMethod]
[DataRow(RunType.Run)]
[DataRow(RunType.Next)]
Expand Down
11 changes: 7 additions & 4 deletions GeneticAlgorithm.UnitTests/ProbabilityUtilsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void P_Test(double probability)
var trueCount = 0;
var falseCount = 0;
var attempts = 1000;
var margernOnError = attempts * 0.1;
var marginOfError = attempts * 0.1;
for (int i = 0; i < attempts; i++)
{
if (ProbabilityUtils.P(probability))
Expand All @@ -29,8 +29,8 @@ public void P_Test(double probability)
falseCount++;
}

trueCount.AssertIsWithinRange(attempts * probability, margernOnError);
falseCount.AssertIsWithinRange(attempts * (1 - probability), margernOnError);
trueCount.AssertIsWithinRange(attempts * probability, marginOfError);
falseCount.AssertIsWithinRange(attempts * (1 - probability), marginOfError);
}

[TestMethod]
Expand Down Expand Up @@ -103,7 +103,10 @@ public void SelectKRandomElements_ElementsAreRandom()

// No element is generated more than 20% of the time
foreach (var element in slelectedElements)
Assert.IsTrue(slelectedElements.Count(e => e.Equals(element)) < length * 5 * 2 * 0.2, $"{nameof(element)} {element} selected too many times");
{
var timesSelected = slelectedElements.Count(e => e.Equals(element));
Assert.IsTrue(timesSelected < length * 5 * 2 * 0.2, $"{nameof(element)} {element} selected too many times ({timesSelected}).");
}
}

[TestMethod]
Expand Down
27 changes: 19 additions & 8 deletions GeneticAlgorithm.UnitTests/SearchTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,24 +86,35 @@ public void BestChromosome()
[TestMethod]
public void SearchTimeWithNextTest()
{
var sleepTime = 10;
var generations = 50;
var populationManager = new TestPopulationManager(new double[] { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 });
var populationManager = new TestPopulationManager(new double[] { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 }, millisecondsPerGeneration: 10);
var engine = new TestGeneticSearchEngineBuilder(10, generations, populationManager).Build();

var stopwatch = new Stopwatch();
stopwatch.Start();
GeneticSearchResult result = null;
while (result == null || !result.IsCompleted)
{
result = engine.Next();
Thread.Sleep(sleepTime);
}

stopwatch.Stop();

Assert.IsTrue(stopwatch.Elapsed.TotalMilliseconds * 0.90 < stopwatch.Elapsed.TotalMilliseconds, $"time is too short. {nameof(stopwatch.Elapsed)} = {stopwatch.Elapsed}; {nameof(result.SearchTime)} = {result.SearchTime.TotalMilliseconds}");
Assert.IsTrue(stopwatch.Elapsed.TotalMilliseconds * 1.1 > stopwatch.Elapsed.TotalMilliseconds, $"time is too long. {nameof(stopwatch.Elapsed)} = {stopwatch.Elapsed}; {nameof(result.SearchTime)} = {result.SearchTime.TotalMilliseconds}");
}

[TestMethod]
public void SearchTimeRunTest()
{
var populationManager = new TestPopulationManager(new double[] { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 });
var engine = new TestGeneticSearchEngineBuilder(10, 50, populationManager).Build();

var stopwatch = new Stopwatch();
stopwatch.Start();
var result = engine.Run();
stopwatch.Stop();

var time = stopwatch.Elapsed.TotalMilliseconds - sleepTime * generations -
result.SearchTime.TotalMilliseconds;
Assert.IsTrue(time < 0.2 * stopwatch.Elapsed.TotalMilliseconds);
Assert.IsTrue(stopwatch.Elapsed.TotalMilliseconds * 0.90 < stopwatch.Elapsed.TotalMilliseconds, $"time is too short. {nameof(stopwatch.Elapsed)} = {stopwatch.Elapsed}; {nameof(result.SearchTime)} = {result.SearchTime.TotalMilliseconds}");
Assert.IsTrue(stopwatch.Elapsed.TotalMilliseconds * 1.1> stopwatch.Elapsed.TotalMilliseconds, $"time is too long. {nameof(stopwatch.Elapsed)} = {stopwatch.Elapsed}; {nameof(result.SearchTime)} = {result.SearchTime.TotalMilliseconds}");
}

[DataRow(false, RunType.Run)]
Expand Down
2 changes: 1 addition & 1 deletion GeneticAlgorithm.UnitTests/SearchUtilsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void CombineTest_BothArraysContainElements()
}

[TestMethod]
public void CombineTest_FirstArrayEmty()
public void CombineTest_FirstArrayEmpty()
{
var population1 = new IChromosome[] {};
var population2 = new[] { chromosome1, chromosome2 , chromosome3 };
Expand Down
40 changes: 25 additions & 15 deletions GeneticAlgorithm.UnitTests/SetAndGetPopulationTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using GeneticAlgorithm.Exceptions;
using GeneticAlgorithm.UnitTests.TestUtils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
Expand Down Expand Up @@ -26,30 +27,39 @@ public void GetCurrentPopulation_EngineRunning_ThrowException()
[ExpectedException(typeof(EngineAlreadyRunningException))]
public void SetCurrentPopulation_EngineRunning_ThrowException()
{
var engine =
new TestGeneticSearchEngineBuilder(2, int.MaxValue, new TestPopulationManager(new double[] { 2, 2 })).Build();

Task.Run(() => engine.Run());
while (!engine.IsRunning) ;

engine.SetCurrentPopulation(new double[] { 3, 3 }.ToChromosomes("Converted"));

Assert.Fail("Should have thrown an exception by now");
Utils.RunTimedTest(() =>
{
var engine =
new TestGeneticSearchEngineBuilder(2, int.MaxValue, new TestPopulationManager(new double[] { 2, 2 })).Build();

Task.Run(() => engine.Run());
while (!engine.IsRunning) ;

engine.SetCurrentPopulation(new double[] { 3, 3 }.ToChromosomes("Converted"));

if (engine.IsRunning)
Assert.Fail("Should have thrown an exception.");
else
Assert.Fail("For some reason, the engine is no longer running.");
});
}

[TestMethod]
[ExpectedException(typeof(GeneticAlgorithmException))]
public void SetCurrentPopulation_WrongNumberOfChromosomes_ThrowException()
{
var engine =
Utils.RunTimedTest(() =>
{
var engine =
new TestGeneticSearchEngineBuilder(2, int.MaxValue, new TestPopulationManager(new double[] { 2, 2 })).Build();

Task.Run(() => engine.Run());
while (!engine.IsRunning) ;
Task.Run(() => engine.Run());
while (!engine.IsRunning) ;

engine.SetCurrentPopulation(new double[] { 3, 3, 3 }.ToChromosomes("Converted"));
engine.SetCurrentPopulation(new double[] { 3, 3, 3 }.ToChromosomes("Converted"));

Assert.Fail("Should have thrown an exception by now");
Assert.Fail("Should have thrown an exception by now");
});
}

[TestMethod]
Expand Down
Loading

0 comments on commit a2214af

Please sign in to comment.