#region License Information
/* HeuristicLab
* Copyright (C) 2002-2014 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.Linq;
using HeuristicLab.Analysis;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Operators;
using HeuristicLab.Optimization;
using HeuristicLab.Optimization.Operators;
using HeuristicLab.Parameters;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using HeuristicLab.PluginInfrastructure;
namespace HeuristicLab.Algorithms.VOffspringSelectionGeneticAlgorithm {
[Item("WeightedParentsDiversityComparator", "Compares the similarity against that of its parents (assumes the parents are subscopes to the child scope). This operator works with any number of subscopes > 0.")]
[StorableClass]
public class WeightedParentsDiversityComparator : SingleSuccessorOperator, ISubScopesQualityComparatorOperator, ISimilarityBasedOperator {
[Storable]
public ISolutionSimilarityCalculator SimilarityCalculator { get; set; }
public IValueLookupParameter MaximizationParameter {
get { return (IValueLookupParameter)Parameters["Maximization"]; }
}
public ILookupParameter LeftSideParameter {
get { return (ILookupParameter)Parameters["LeftSide"]; }
}
public ILookupParameter> RightSideParameter {
get { return (ILookupParameter>)Parameters["RightSide"]; }
}
public ILookupParameter ResultParameter {
get { return (ILookupParameter)Parameters["Result"]; }
}
public ValueLookupParameter ComparisonFactorParameter {
get { return (ValueLookupParameter)Parameters["ComparisonFactor"]; }
}
public ValueLookupParameter DiversityComparisonFactorParameter {
get { return (ValueLookupParameter)Parameters["DiversityComparisonFactor"]; }
}
private ValueLookupParameter ComparisonFactorLowerBoundParameter {
get { return (ValueLookupParameter)Parameters["DiversityComparisonFactorLowerBound"]; }
}
private ValueLookupParameter ComparisonFactorUpperBoundParameter {
get { return (ValueLookupParameter)Parameters["DiversityComparisonFactorUpperBound"]; }
}
public IConstrainedValueParameter ComparisonFactorModifierParameter {
get { return (IConstrainedValueParameter)Parameters["ComparisonFactorModifier"]; }
}
public ValueLookupParameter ResultsParameter {
get { return (ValueLookupParameter)Parameters["Results"]; }
}
public ILookupParameter GenerationsParameter {
get { return (LookupParameter)Parameters["Generations"]; }
}
public ValueParameter EnableDivCriteriaParameter {
get { return (ValueParameter)Parameters["EnableDivCriteria"]; }
}
private const string spDetailsParameterName = "SPDetails";
private const string divDataRowName = "DiversitySuccessCount";
private const string qualityDataRowName = "QualitySuccessCount";
private const string divFailDataRowName = "DiversityFailCount";
private const string qualityFailDataRowName = "QualityFailCount";
private const string overallCountDataRowName = "OverallCount";
private const string successCountDataRowName = "SuccessCount";
[Storable]
private int currentGeneration;
[Storable]
private int divCount;
[Storable]
private int qualityCount;
[Storable]
private int badQualityCount;
[Storable]
private int badDivCount;
[Storable]
private int overallCount;
[Storable]
private int successCount;
[StorableConstructor]
protected WeightedParentsDiversityComparator(bool deserializing) : base(deserializing) { }
protected WeightedParentsDiversityComparator(WeightedParentsDiversityComparator original, Cloner cloner)
: base(original, cloner) {
SimilarityCalculator = cloner.Clone(original.SimilarityCalculator);
currentGeneration = original.currentGeneration;
divCount = original.divCount;
qualityCount = original.qualityCount;
overallCount = original.overallCount;
successCount = original.successCount;
badDivCount = original.badDivCount;
badQualityCount = original.badQualityCount;
}
public WeightedParentsDiversityComparator()
: base() {
Parameters.Add(new ValueLookupParameter("Maximization", "True if the problem is a maximization problem, false otherwise"));
Parameters.Add(new LookupParameter("LeftSide", "The quality of the child."));
Parameters.Add(new ScopeTreeLookupParameter("RightSide", "The qualities of the parents."));
Parameters.Add(new LookupParameter("Result", "The result of the comparison: True means Quality is better, False means it is worse than parents."));
Parameters.Add(new ValueLookupParameter("ComparisonFactor", "Determines if the quality should be compared to the better parent (1.0), to the worse (0.0) or to any linearly interpolated value between them."));
Parameters.Add(new ValueLookupParameter("DiversityComparisonFactor", "Determines if the quality should be compared to the better parent (1.0), to the worse (0.0) or to any linearly interpolated value between them.", new DoubleValue(0.0)));
Parameters.Add(new ValueLookupParameter("DiversityComparisonFactorLowerBound", "The lower bound of the comparison factor (start).", new DoubleValue(0.5)));
Parameters.Add(new ValueLookupParameter("DiversityComparisonFactorUpperBound", "The upper bound of the comparison factor (end).", new DoubleValue(1.0)));
Parameters.Add(new OptionalConstrainedValueParameter("ComparisonFactorModifier", "The operator used to modify the comparison factor.", new ItemSet(new IDiscreteDoubleValueModifier[] { new LinearDiscreteDoubleValueModifier() }), new LinearDiscreteDoubleValueModifier()));
Parameters.Add(new ValueLookupParameter("Results", "The result collection where the population diversity analysis results should be stored."));
Parameters.Add(new LookupParameter("Generations", "The current number of generations."));
Parameters.Add(new ValueParameter("EnableDivCriteria", "Use diversity as additional offspring selection criteria.", new BoolValue(true)));
foreach (IDiscreteDoubleValueModifier modifier in ApplicationManager.Manager.GetInstances().OrderBy(x => x.Name))
ComparisonFactorModifierParameter.ValidValues.Add(modifier);
IDiscreteDoubleValueModifier linearModifier = ComparisonFactorModifierParameter.ValidValues.FirstOrDefault(x => x.GetType().Name.Equals("LinearDiscreteDoubleValueModifier"));
if (linearModifier != null) ComparisonFactorModifierParameter.Value = linearModifier;
ParameterizeComparisonFactorModifiers();
}
public override IDeepCloneable Clone(Cloner cloner) {
return new WeightedParentsDiversityComparator(this, cloner);
}
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
if (!Parameters.ContainsKey("Generations"))
Parameters.Add(new LookupParameter("Generations", "The current number of generations."));
if (!Parameters.ContainsKey("Results"))
Parameters.Add(new ValueLookupParameter("Results", "The result collection where the population diversity analysis results should be stored."));
if (!Parameters.ContainsKey("EnableDivCriteria"))
Parameters.Add(new ValueParameter("EnableDivCriteria", "Use diversity as additional offspring selection criteria.", new BoolValue(true)));
}
private void ParameterizeComparisonFactorModifiers() {
//TODO: does not work if Generations parameter names are changed
foreach (IDiscreteDoubleValueModifier modifier in ComparisonFactorModifierParameter.ValidValues) {
modifier.IndexParameter.ActualName = "Generations";
modifier.EndIndexParameter.ActualName = "MaximumGenerations";
modifier.EndValueParameter.ActualName = ComparisonFactorUpperBoundParameter.Name;
modifier.StartIndexParameter.Value = new IntValue(0);
modifier.StartValueParameter.ActualName = ComparisonFactorLowerBoundParameter.Name;
modifier.ValueParameter.ActualName = "DiversityComparisonFactor";
}
}
public override IOperation Apply() {
ItemArray rightQualities = RightSideParameter.ActualValue;
if (rightQualities.Length < 1) throw new InvalidOperationException(Name + ": No subscopes found.");
double compFact = ComparisonFactorParameter.ActualValue.Value;
double diversityComFact = DiversityComparisonFactorParameter.ActualValue.Value;
bool maximization = MaximizationParameter.ActualValue.Value;
double leftQuality = LeftSideParameter.ActualValue.Value;
bool resultDiversity;
double threshold = 0;
DataTable spDetailsTable;
if (ResultsParameter.ActualValue.ContainsKey(spDetailsParameterName)) {
spDetailsTable = (DataTable)ResultsParameter.ActualValue[spDetailsParameterName].Value;
} else {
spDetailsTable = new DataTable(spDetailsParameterName);
spDetailsTable.Rows.Add(new DataRow(divDataRowName));
spDetailsTable.Rows.Add(new DataRow(qualityDataRowName));
spDetailsTable.Rows.Add(new DataRow(overallCountDataRowName));
spDetailsTable.Rows.Add(new DataRow(successCountDataRowName));
spDetailsTable.Rows.Add(new DataRow(divFailDataRowName));
spDetailsTable.Rows.Add(new DataRow(qualityFailDataRowName));
ResultsParameter.ActualValue.Add(new Result(spDetailsParameterName, spDetailsTable));
}
if (GenerationsParameter.ActualValue.Value != currentGeneration) {
spDetailsTable.Rows[divDataRowName].Values.Add(divCount);
divCount = 0;
spDetailsTable.Rows[qualityDataRowName].Values.Add(qualityCount);
qualityCount = 0;
spDetailsTable.Rows[overallCountDataRowName].Values.Add(overallCount);
overallCount = 0;
spDetailsTable.Rows[successCountDataRowName].Values.Add(successCount);
successCount = 0;
spDetailsTable.Rows[qualityFailDataRowName].Values.Add(badQualityCount);
badQualityCount = 0;
spDetailsTable.Rows[divFailDataRowName].Values.Add(badDivCount);
badDivCount = 0;
currentGeneration = GenerationsParameter.ActualValue.Value;
}
#region Calculate threshold
if (rightQualities.Length == 2) {
var sim1 = SimilarityCalculator.CalculateSolutionSimilarity(ExecutionContext.Scope, ExecutionContext.Scope.SubScopes[0]);
var sim2 = SimilarityCalculator.CalculateSolutionSimilarity(ExecutionContext.Scope, ExecutionContext.Scope.SubScopes[1]);
var simAvg = (sim1 + sim2) / 2.0;
//TODO: try out different things
//resultDiversity = simAvg < diversityComFact;
resultDiversity = sim1 < diversityComFact || sim2 < diversityComFact;
double minQuality = Math.Min(rightQualities[0].Value, rightQualities[1].Value);
double maxQuality = Math.Max(rightQualities[0].Value, rightQualities[1].Value);
if (maximization)
threshold = minQuality + (maxQuality - minQuality) * compFact;
else
threshold = maxQuality - (maxQuality - minQuality) * compFact;
} else {
throw new NotImplementedException("Only 2 parents are supported.");
}
#endregion
bool result = maximization && leftQuality > threshold || !maximization && leftQuality < threshold;
//collect statistics
if (result) {
qualityCount++;
} else {
badQualityCount++;
}
if (resultDiversity) {
divCount++;
} else {
badDivCount++;
}
if (result && resultDiversity) {
successCount++;
}
overallCount++;
//use diveristiy criteria or not
if (EnableDivCriteriaParameter.Value.Value) {
result = result && resultDiversity;
}
BoolValue resultValue = ResultParameter.ActualValue;
if (resultValue == null) {
ResultParameter.ActualValue = new BoolValue(result);
} else {
resultValue.Value = result;
}
//like the placeholder, though we create child operations
OperationCollection next = new OperationCollection(base.Apply());
IOperator op = ComparisonFactorModifierParameter.Value;
if (op != null)
next.Insert(0, ExecutionContext.CreateChildOperation(op));
return next;
}
}
}