#region License Information
/* HeuristicLab
* Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Encodings.BinaryVectorEncoding;
using HeuristicLab.Encodings.PermutationEncoding;
using HeuristicLab.Optimization;
using HeuristicLab.Parameters;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using HeuristicLab.Random;
namespace HeuristicLab.Problems.Scheduling.CFSAP {
public enum OptimalAssignmentType { None, Polynomial, OneOpt, Both };
[Item("Genetic Algorithm (CFSAP)", "")]
[StorableClass]
public class GeneticAlgorithm : BasicAlgorithm {
public override bool SupportsPause { get { return true; } }
public override Type ProblemType {
get { return typeof(CFSAP); }
}
public new CFSAP Problem {
get { return (CFSAP)base.Problem; }
set { base.Problem = value; }
}
[Storable]
private IFixedValueParameter populationSizeParameter;
public IFixedValueParameter PopulationSizeParameter {
get { return populationSizeParameter; }
}
[Storable]
private IFixedValueParameter elitesParameter;
public IFixedValueParameter ElitesParameter {
get { return elitesParameter; }
}
[Storable]
private IFixedValueParameter mutationProbabilityParameter;
public IFixedValueParameter MutationProbabilityParameter {
get { return mutationProbabilityParameter; }
}
[Storable]
private IFixedValueParameter maximumGenerationsParameter;
public IFixedValueParameter MaximumGenerationsParameter {
get { return maximumGenerationsParameter; }
}
[Storable]
private IFixedValueParameter maximumEvaluatedSolutionsParameter;
public IFixedValueParameter MaximumEvaluatedSolutionsParameter {
get { return maximumEvaluatedSolutionsParameter; }
}
[Storable]
private IFixedValueParameter pauseAfterGenerationParameter;
public IFixedValueParameter PauseAfterGenerationParameter {
get { return pauseAfterGenerationParameter; }
}
[Storable]
private IFixedValueParameter> optimalAssignmentParameter;
public IFixedValueParameter> OptimalAssignmentParameter {
get { return optimalAssignmentParameter; }
}
public int PopulationSize {
get { return populationSizeParameter.Value.Value; }
set { populationSizeParameter.Value.Value = value; }
}
public int Elites {
get { return elitesParameter.Value.Value; }
set { elitesParameter.Value.Value = value; }
}
public double MutationProbability {
get { return mutationProbabilityParameter.Value.Value; }
set { mutationProbabilityParameter.Value.Value = value; }
}
public int MaximumGenerations {
get { return maximumGenerationsParameter.Value.Value; }
set { maximumGenerationsParameter.Value.Value = value; }
}
public int MaximumEvaluatedSolutions {
get { return maximumEvaluatedSolutionsParameter.Value.Value; }
set { maximumEvaluatedSolutionsParameter.Value.Value = value; }
}
public bool PauseAfterGeneration {
get { return pauseAfterGenerationParameter.Value.Value; }
set { pauseAfterGenerationParameter.Value.Value = value; }
}
public OptimalAssignmentType OptimalAssignment {
get { return optimalAssignmentParameter.Value.Value; }
set { optimalAssignmentParameter.Value.Value = value; }
}
[StorableConstructor]
protected GeneticAlgorithm(bool deserializing) : base(deserializing) { }
protected GeneticAlgorithm(GeneticAlgorithm original, Cloner cloner)
: base(original, cloner) {
populationSizeParameter = cloner.Clone(original.populationSizeParameter);
elitesParameter = cloner.Clone(original.elitesParameter);
mutationProbabilityParameter = cloner.Clone(original.mutationProbabilityParameter);
maximumGenerationsParameter = cloner.Clone(original.maximumGenerationsParameter);
maximumEvaluatedSolutionsParameter = cloner.Clone(original.maximumEvaluatedSolutionsParameter);
pauseAfterGenerationParameter = cloner.Clone(original.pauseAfterGenerationParameter);
optimalAssignmentParameter = cloner.Clone(original.optimalAssignmentParameter);
generation = original.generation;
evaluatedSolutions = original.evaluatedSolutions;
bestQuality = original.bestQuality;
if (original.population != null)
population = original.population.Select(cloner.Clone).ToArray();
if (original.nextGeneration != null)
nextGeneration = original.nextGeneration.Select(cloner.Clone).ToArray();
if (original.optimalSequences != null)
optimalSequences = new HashSet(original.optimalSequences, new PermutationEqualityComparer());
}
public GeneticAlgorithm() {
Parameters.Add(populationSizeParameter = new FixedValueParameter("PopulationSize", "The size of the population, each individual of the population is a solution with a permutation and a binary vector.", new IntValue(500)));
Parameters.Add(elitesParameter = new FixedValueParameter("Elites", "The number of best individuals from the previous population that will continue to the next generation.", new IntValue(1)));
Parameters.Add(mutationProbabilityParameter = new FixedValueParameter("MutationProbability", "The probability that an individual should be mutated after it has been created through crossover.", new PercentValue(0.05)));
Parameters.Add(maximumGenerationsParameter = new FixedValueParameter("MaximumGenerations", "The number of generations that the algorithm may run for.", new IntValue(1000000)));
Parameters.Add(maximumEvaluatedSolutionsParameter = new FixedValueParameter("MaximumEvaluatedSolutions", "The number of evaluated solutions before the algorithm terminates.", new IntValue(100000000)));
Parameters.Add(pauseAfterGenerationParameter = new FixedValueParameter("PauseAfterGeneration", "Whether the algorithm should pause after each generation.", new BoolValue(true)));
Parameters.Add(optimalAssignmentParameter = new FixedValueParameter>("OptimalAssignment", "Which optimal assignment strategy should be used.", new EnumValue(OptimalAssignmentType.Polynomial)));
}
public override IDeepCloneable Clone(Cloner cloner) {
return new GeneticAlgorithm(this, cloner);
}
protected override void OnProblemChanged() {
base.OnProblemChanged();
}
[Storable]
private IRandom random;
[Storable]
private int generation;
[Storable]
private int evaluatedSolutions;
[Storable]
private double bestQuality;
[Storable]
private EncodedSolution[] population;
[Storable]
private EncodedSolution[] nextGeneration;
[Storable]
private HashSet optimalSequences;
protected override void Initialize(CancellationToken cancellationToken) {
base.Initialize(cancellationToken);
random = new MersenneTwister();
optimalSequences = new HashSet(new PermutationEqualityComparer());
generation = 0;
evaluatedSolutions = 0;
population = new EncodedSolution[PopulationSize];
nextGeneration = new EncodedSolution[PopulationSize - Elites];
bestQuality = double.MaxValue;
for (var p = 0; p < PopulationSize; p++) {
population[p] = new EncodedSolution() {
Sequence = new Permutation(PermutationTypes.RelativeDirected, Problem.ProcessingTimes.Columns, random),
Assignment = new BinaryVector(Problem.ProcessingTimes.Columns, random)
};
population[p].Quality = Problem.Evaluate(population[p].Sequence, population[p].Assignment);
evaluatedSolutions++;
if (population[p].Quality < bestQuality) bestQuality = population[p].Quality;
}
Array.Sort(population);
Results.Add(new Result("BestQuality", new DoubleValue(bestQuality)));
Results.Add(new Result("EvaluatedSolutions", new IntValue(evaluatedSolutions)));
Results.Add(new Result("Generation", new IntValue(generation)));
if (PauseAfterGeneration) Pause();
}
protected override void Run(CancellationToken cancellationToken) {
if (cancellationToken.IsCancellationRequested) return;
while (generation < MaximumGenerations) {
if (evaluatedSolutions > MaximumEvaluatedSolutions) return;
for (var p = 0; p < PopulationSize - Elites; p++) {
var parent1 = TournamentSelect((int)Math.Round(Math.Max(PopulationSize / 71.0, 2)));
var parent2 = TournamentSelect((int)Math.Round(Math.Max(PopulationSize / 71.0, 2)));
nextGeneration[p] = new EncodedSolution() {
Sequence = CrossSequence(parent1.Sequence, parent2.Sequence),
Assignment = CrossAssignment(parent1.Assignment, parent2.Assignment)
};
var mutProb = random.NextDouble();
if (mutProb < MutationProbability) {
MutateSequence(nextGeneration[p].Sequence);
MutateAssignment(nextGeneration[p].Assignment);
}
nextGeneration[p].Quality = Problem.Evaluate(nextGeneration[p].Sequence, nextGeneration[p].Assignment);
evaluatedSolutions++;
if (nextGeneration[p].Quality <= bestQuality) {
switch (OptimalAssignment) {
case OptimalAssignmentType.None:
break;
case OptimalAssignmentType.Polynomial:
OptimalPolynomialAssignment(nextGeneration[p]);
break;
case OptimalAssignmentType.OneOpt:
OneOptAssignment(nextGeneration[p]);
break;
case OptimalAssignmentType.Both:
HybridAssignment(nextGeneration[p]);
break;
default:
throw new InvalidOperationException("Optimal assignment strategy not defined.");
}
if (nextGeneration[p].Quality < bestQuality) {
bestQuality = nextGeneration[p].Quality;
((DoubleValue)Results["BestQuality"].Value).Value = bestQuality;
}
}
}
for (var p = Elites; p < PopulationSize; p++) {
population[p] = nextGeneration[p - Elites];
}
Array.Sort(population);
generation++;
((IntValue)Results["EvaluatedSolutions"].Value).Value = evaluatedSolutions;
((IntValue)Results["Generation"].Value).Value = generation;
if (PauseAfterGeneration || cancellationToken.IsCancellationRequested) {
if (!cancellationToken.IsCancellationRequested) Pause();
break;
}
}
}
private void OptimalPolynomialAssignment(EncodedSolution generation) {
if (!optimalSequences.Contains(generation.Sequence)) {
var assignment = Scheduling.CFSAP.OptimalPolynomialAssignment.MakeAssignment(generation.Sequence, Problem.ProcessingTimes, Problem.SetupTimes, out var cycleTime);
evaluatedSolutions++;
generation.Assignment = new BinaryVector(assignment.Select(x => x == 1).ToArray());
generation.Quality = cycleTime;
optimalSequences.Add(generation.Sequence);
}
}
private void OneOptAssignment(EncodedSolution generation) {
var assignment = Scheduling.CFSAP.OneOptAssignment.MakeAssignment(generation.Sequence, generation.Assignment, Problem.ProcessingTimes, Problem.SetupTimes, out var cycleTime);
evaluatedSolutions++;
generation.Assignment = assignment;
generation.Quality = cycleTime;
}
private void HybridAssignment(EncodedSolution generation) {
var a = random.Next(2);
switch (a) {
case 0: OptimalPolynomialAssignment(generation); break;
case 1: OneOptAssignment(generation); break;
default: throw new InvalidOperationException("Assignment not defined.");
}
}
private EncodedSolution TournamentSelect(int groupSize) {
var selected = population[random.Next(population.Length)];
for (var i = 1; i < groupSize; i++) {
var competitor = population[random.Next(population.Length)];
if (selected.Quality > competitor.Quality) {
selected = competitor;
}
}
return selected;
}
private Permutation CrossSequence(Permutation sequence1, Permutation sequence2) {
var cx = random.Next(3);
switch (cx) {
case 0: return OrderCrossover.Apply(random, sequence1, sequence2);
case 1: return OrderCrossover2.Apply(random, sequence1, sequence2);
case 2: return MaximalPreservativeCrossover.Apply(random, sequence1, sequence2);
default: throw new InvalidOperationException("Crossover not defined.");
}
}
private void MutateSequence(Permutation sequence) {
var m = random.Next(7);
switch (m) {
case 0: InversionManipulator.Apply(random, sequence); break;
case 1: InsertionManipulator.Apply(random, sequence); break;
case 2: Swap2Manipulator.Apply(random, sequence); break;
case 3: Swap3Manipulator.Apply(random, sequence); break;
case 4: TranslocationManipulator.Apply(random, sequence); break;
case 5: TranslocationInversionManipulator.Apply(random, sequence); break;
case 6: ScrambleManipulator.Apply(random, sequence); break;
default: throw new InvalidOperationException("Manipulator not defined.");
}
}
private BinaryVector CrossAssignment(BinaryVector assign1, BinaryVector assign2) {
var cx = random.Next(3);
switch (cx) {
case 0: return UniformCrossover.Apply(random, assign1, assign2);
case 1: return NPointCrossover.Apply(random, assign1, assign2, new IntValue(1));
case 2: return NPointCrossover.Apply(random, assign1, assign2, new IntValue(2));
default: throw new InvalidOperationException("Crossover not defined.");
}
}
private void MutateAssignment(BinaryVector assignment) {
SomePositionsBitflipManipulator.Apply(random, assignment, new DoubleValue(0.2));
}
[StorableClass]
private class EncodedSolution : DeepCloneable, IComparable {
[Storable]
public Permutation Sequence { get; set; }
[Storable]
public BinaryVector Assignment { get; set; }
[Storable]
public double Quality { get; set; }
[StorableConstructor]
private EncodedSolution(bool deserializing) { }
private EncodedSolution(EncodedSolution original, Cloner cloner) : base(original, cloner) {
Sequence = cloner.Clone(original.Sequence);
Assignment = cloner.Clone(original.Assignment);
Quality = original.Quality;
}
public EncodedSolution() { }
public override IDeepCloneable Clone(Cloner cloner) {
return new EncodedSolution(this, cloner);
}
public int CompareTo(EncodedSolution other) {
return Quality.CompareTo(other.Quality);
}
}
}
}