-
-
Notifications
You must be signed in to change notification settings - Fork 335
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
Bug/Error when using ParallelTaskExecutor #40
Comments
Yes, this is an expected behavior. The ParallTaskExecutor will run the fitness evaluation in parallel, but the call of GeneticAlgorithm.Start wait until the GeneticAlgorithm.Termination is reached. One solution, if you want to run GeneticAlgorithm.Start in parallel of other operations in your application is running it on a separated thread. Where are you using GeneticSharp? Console application? Unity3d? |
That is not the behavior I am seeing. In all cases I run
geneticalgorithm.start within its own thread/task. When I don't configure
taskexecutor then the main thread is not blocked. But when I configure task
executor the main thread completely blocks. For clarity purpose, with main
thread I do not mean the thread on which geneticalgorithm.start is running.
This should never be the case. Imagine you run the algorithm from a UI
thread then the UI thread blocks which is in all cases highly undesirable.
I ran the same tests from within a console and from wpf both with the same
observation.
…On Wed, Oct 3, 2018, 23:34 Diego Giacomelli ***@***.***> wrote:
Yes, this is an expected behavior. The ParallTaskExecutor will run the
fitness evaluation in parallel, but the call of GeneticAlgorithm.Start wait
until the GeneticAlgorithm.Termination is reached.
One solution, if you want to run GeneticAlgorithm.Start in parallel of
other operations in your application is running it on a separated thread.
Where are you using GeneticSharp? Console application? Unity3d?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#40 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/APznJvH8DGmkj-VywPQceZCpbv1dRCjOks5uhNkPgaJpZM4XGK71>
.
|
Ok, now I'm understanding better your problem. Can you provide a simple running sample with this problem? |
Yes, will send one in a few hours as I am not in front of my machine right
now. Thanks for looking into this.
…On Wed, Oct 3, 2018, 23:47 Diego Giacomelli ***@***.***> wrote:
Ok, now I'm understanding better your problem. Can you provide a simple
running sample with this problem?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#40 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/APznJtfC-qwfAn3ccFFaIURDDAk8iJJIks5uhNwWgaJpZM4XGK71>
.
|
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic), Serializable]
public class GeneticOptimizerVariable
{
public string VariableName { get; set; }
public int NumberDigitsPrecision { get; set; }
public double MinimumValue { get; set; }
public double MaximumValue { get; set; }
public GeneticOptimizerVariable()
{ }
public GeneticOptimizerVariable(string variableName, int numberDigitsPrecision, double minimumValue, double maximumValue)
{
VariableName = variableName;
NumberDigitsPrecision = numberDigitsPrecision;
MinimumValue = minimumValue;
MaximumValue = maximumValue;
}
}
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic), Serializable]
public class GeneticOptimizerConfiguration
{
public int NumberThreadsToUse { get; set; }
public string OptimizeVariableName { get; set; }
public List<GeneticOptimizerVariable> Variables { get; set; }
public GeneticOptimizerConfiguration()
{ }
public GeneticOptimizerConfiguration(string optimizeVariableName, List<GeneticOptimizerVariable> variables, int numberThreadsToUse)
{
OptimizeVariableName = optimizeVariableName;
Variables = variables;
NumberThreadsToUse = numberThreadsToUse;
}
}
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic), Serializable]
public class GeneticOptimizerResult
{
public string OutputVariableName { get; set; }
public List<string> InputVariableNames { get; set; }
public List<double> BestFitInputs { get; set; }
public double BestFitOutput { get; set; }
public List<string> IterationArray { get; set; } //comma delimited -> values 1,...,n-1 = input values, n = output value
public GeneticOptimizerResult()
{
InputVariableNames = new List<string>();
BestFitInputs = new List<double>();
IterationArray = new List<string>();
}
public GeneticOptimizerResult(string optimizationVariableName, List<string> variableNames)
{
OutputVariableName = optimizationVariableName;
InputVariableNames = variableNames;
BestFitInputs = new List<double>();
IterationArray = new List<string>();
}
}
public class GeneticOptimizer
{
private const int MinimumNumberPopulation = 50;
private const int MaximumNumberPopulation = 100;
private readonly GeneticOptimizerConfiguration _configuration;
private readonly Action<string> _generationRanCallback;
private readonly GeneticAlgorithm _algorithm;
private GeneticOptimizerResult _result;
public GeneticOptimizer(GeneticOptimizerConfiguration configuration, Func<double[], double> objectiveFunction, Action<string> generationRanCallback = null)
{
//store configuration
_configuration = configuration;
_generationRanCallback = generationRanCallback;
//set min/max/precision of input variables
var minValues = new double[_configuration.Variables.Count];
var maxValues = new double[_configuration.Variables.Count];
var fractionDigits = new int[_configuration.Variables.Count];
for (int index = 0; index < _configuration.Variables.Count; index++)
{
minValues[index] = _configuration.Variables[index].MinimumValue;
maxValues[index] = _configuration.Variables[index].MaximumValue;
fractionDigits[index] = _configuration.Variables[index].NumberDigitsPrecision;
}
//total bits
var totalBits = new int[] { 64 };
//chromosome
var chromosome = new FloatingPointChromosome(minValues, maxValues, totalBits, fractionDigits);
//population
var population = new Population(MinimumNumberPopulation, MaximumNumberPopulation, chromosome);
//set fitness function
var fitnessFunction = new FuncFitness(c =>
{
var fc = c as FloatingPointChromosome;
var inputs = fc.ToFloatingPoints();
var result = objectiveFunction(inputs);
//add to results
if (!Double.IsNaN(result))
{
var list = inputs.ToList();
list.Add(result);
_result.IterationArray.Add(string.Join(",", list));
}
return result;
});
//other variables
var selection = new EliteSelection();
var crossover = new UniformCrossover(0.5f);
var mutation = new FlipBitMutation();
var termination = new FitnessThresholdTermination();
_algorithm = new GeneticAlgorithm(population, fitnessFunction, selection, crossover, mutation)
{
Termination = termination,
};
//task parallelism
var taskExecutor = new ParallelTaskExecutor();
taskExecutor.MinThreads = 1;
taskExecutor.MaxThreads = _configuration.NumberThreadsToUse;
_algorithm.TaskExecutor = taskExecutor;
//if (_configuration.NumberThreadsToUse > 1)
//{
// var taskExecutor = new ParallelTaskExecutor();
// taskExecutor.MinThreads = 1;
// taskExecutor.MaxThreads = _configuration.NumberThreadsToUse;
// _algorithm.TaskExecutor = taskExecutor;
//}
//register generation ran callback
_algorithm.GenerationRan += AlgorithmOnGenerationRan;
}
public void Start()
{
//define result
_result = new GeneticOptimizerResult(_configuration.OptimizeVariableName, _configuration.Variables.Select(x => x.VariableName).ToList());
//start optimizer
_algorithm.Start();
}
public void Stop()
{
_algorithm.Stop();
}
public GeneticOptimizerResult GetResults()
{
return _result;
}
private void AlgorithmOnGenerationRan(object sender, EventArgs e)
{
var bestChromosome = _algorithm.BestChromosome as FloatingPointChromosome;
if (bestChromosome == null || bestChromosome.Fitness == null)
return;
var phenotype = bestChromosome.ToFloatingPoints();
//update results with best fit
_result.BestFitInputs = phenotype.ToList();
_result.BestFitOutput = bestChromosome.Fitness.Value;
//invoke callback to update
if (_generationRanCallback != null)
{
var variables = string.Join(" - ", _configuration.Variables.Select((item, index) => $"{item.VariableName} = {phenotype[index]}"));
var updateString = $"Optimizer Generation: {_algorithm.GenerationsNumber} - Fitness: {bestChromosome.Fitness.Value} - Variables: {variables}";
_generationRanCallback(updateString);
}
}
} Then running the following code in a Console Application: class Program
{
static void Main(string[] args)
{
var progressTimer = new System.Timers.Timer(1000);
progressTimer.AutoReset = true;
progressTimer.Elapsed += (sender, arg) =>
{
//do something
Console.WriteLine("Hello from progress timer");
};
//start timer
progressTimer.Start();
Task.Run(() =>
{
RunGeneticOptimizer();
}).Wait();
Console.WriteLine("All tasks inside actionblock completed");
Console.WriteLine($"Press Key to quit");
Console.ReadLine();
}
private static void GenerationRanCallback(string obj)
{
Console.WriteLine(obj);
}
private static void RunGeneticOptimizer()
{
//optimizer variables
var variables = new List<GeneticOptimizerVariable>()
{
new GeneticOptimizerVariable("x", 4, -10, 10)
};
//optimizer configuration
var configuration = new GeneticOptimizerConfiguration("y", variables, 1);
//objective function
var objectiveFunction = new Func<double[], double>(inputs =>
{
Thread.Sleep(1000);
var objectiveFunctionResult = Math.Pow(inputs[0], 3) / Math.Exp(Math.Pow(inputs[0], 0.8));
return objectiveFunctionResult;
});
//optimizer
var optimizer = new GeneticOptimizer(configuration, objectiveFunction, GenerationRanCallback);
var watch = new Stopwatch();
watch.Start();
optimizer.Start();
watch.Stop();
Console.WriteLine($"Number milliseconds: {watch.ElapsedMilliseconds}");
Console.WriteLine($"Press Key to quit");
Console.ReadLine();
}
} You will notice that the timer thread is blocked. The reason is that the GeneticAlgorithm is configured with taskExecutor = new ParallelTaskExecutor() where the MaxThreads is set to 1 (as specified in the console app). Even when requesting the algorithm to run with >1 threads initially blocks the timer thread. |
Thanks, I will investigate that. |
The behavior you described (and I saw with your sample code) was caused by those lines on ParallelTaskExecutor's Start method: ThreadPool.GetMinThreads(out int minWorker, out int minIOC);
ThreadPool.SetMinThreads(MinThreads, minIOC);
ThreadPool.GetMaxThreads(out int maxWorker, out int maxIOC);
ThreadPool.SetMaxThreads(MaxThreads, maxIOC); When the MaxThreads is set to just 1, there are no others threads to attend the To solve this, I changed that lines to: // Do not change values if the new values to min and max threads are lower than already configured on ThreadPool.
ThreadPool.GetMinThreads(out minWorker, out minIOC);
if (MinThreads > minWorker)
{
ThreadPool.SetMinThreads(MinThreads, minIOC);
}
ThreadPool.GetMaxThreads(out maxWorker, out maxIOC);
if (MaxThreads > maxWorker)
{
ThreadPool.SetMaxThreads(MaxThreads, maxIOC);
} I added your sample to this branch issue-40, could you please checkout it and run the |
I checked out the branch and verified that the issue is resolved. Thanks |
I just discovered that when running the optimizer within a Task/Tread/TPL Dataflow block with TaskExecutor set to ParallelTaskExecuter when instantiating GeneticAlgorithm, it blocks all other outside operations during the lifetime of the optimizer run. This does not happen when not setting the TaskExecutor option.
Most likely this is a bug? Is there a workaround? At the moment I can hence not run the objective function in parallel.
Can someone please help?
The text was updated successfully, but these errors were encountered: