#region License Information
/* HeuristicLab
* Copyright (C) 2002-2019 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 HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Operators;
using HeuristicLab.Optimization;
using HeuristicLab.Parameters;
using HEAL.Attic;
namespace HeuristicLab.Analysis {
///
/// An operator for analyzing the solution similarity in a population.
///
[Item("PopulationSimilarityAnalyzer", "An operator for analyzing the solution similarity in a population.")]
[StorableType("56A495B0-573F-421A-99E2-24D67D8DB686")]
public class PopulationSimilarityAnalyzer : SingleSuccessorOperator, IAnalyzer, ISimilarityBasedOperator {
private const string DiversityResultNameParameterName = "DiversityResultsName";
private const string ExecuteInParallelParameterName = "ExecuteInParallel";
private const string MaxDegreeOfParallelismParameterName = "MaxDegreeOfParallelism";
#region Backwards compatible code, remove with 3.4
private ISolutionSimilarityCalculator oldSimilarityCalculator;
[Storable(OldName = "SimilarityCalculator")]
[Obsolete]
private ISolutionSimilarityCalculator SimilarityCalculator { set { oldSimilarityCalculator = value; } }
#endregion
public virtual bool EnabledByDefault {
get { return false; }
}
public ScopeParameter CurrentScopeParameter {
get { return (ScopeParameter)Parameters["CurrentScope"]; }
}
public IValueLookupParameter ResultsParameter {
get { return (IValueLookupParameter)Parameters["Results"]; }
}
public IConstrainedValueParameter SimilarityCalculatorParameter {
get { return (IConstrainedValueParameter)Parameters["SimilarityCalculator"]; }
}
public IValueParameter StoreHistoryParameter {
get { return (IValueParameter)Parameters["StoreHistory"]; }
}
public IValueParameter UpdateIntervalParameter {
get { return (IValueParameter)Parameters["UpdateInterval"]; }
}
public ILookupParameter UpdateCounterParameter {
get { return (ILookupParameter)Parameters["UpdateCounter"]; }
}
public IFixedValueParameter DiversityResultNameParameter {
get { return (FixedValueParameter)Parameters[DiversityResultNameParameterName]; }
}
public IFixedValueParameter ExecuteInParallelParameter {
get { return (IFixedValueParameter)Parameters[ExecuteInParallelParameterName]; }
}
public IFixedValueParameter MaxDegreeOfParallelismParameter {
get { return (IFixedValueParameter)Parameters[MaxDegreeOfParallelismParameterName]; }
}
public string DiversityResultName {
get { return DiversityResultNameParameter.Value.Value; }
set { DiversityResultNameParameter.Value.Value = value; }
}
public bool ExecuteInParallel {
get { return ExecuteInParallelParameter.Value.Value; }
set { ExecuteInParallelParameter.Value.Value = value; }
}
public int MaxDegreeOfParallelism {
get { return MaxDegreeOfParallelismParameter.Value.Value; }
set { MaxDegreeOfParallelismParameter.Value.Value = value; }
}
[StorableConstructor]
protected PopulationSimilarityAnalyzer(StorableConstructorFlag _) : base(_) { }
protected PopulationSimilarityAnalyzer(PopulationSimilarityAnalyzer original, Cloner cloner)
: base(original, cloner) {
RegisterParametersEventHandlers();
}
public PopulationSimilarityAnalyzer(IEnumerable validSimilarityCalculators)
: base() {
Parameters.Add(new ScopeParameter("CurrentScope", "The current scope that contains the solutions which should be analyzed."));
Parameters.Add(new ValueLookupParameter("Results", "The result collection where the population diversity analysis results should be stored."));
Parameters.Add(new ConstrainedValueParameter("SimilarityCalculator", "The similarity calculator that should be used to calculate population similarity."));
Parameters.Add(new ValueParameter("StoreHistory", "True if the history of the population diversity analysis should be stored.", new BoolValue(false)));
Parameters.Add(new ValueParameter("UpdateInterval", "The interval in which the population diversity analysis should be applied.", new IntValue(1)));
Parameters.Add(new LookupParameter("UpdateCounter", "The value which counts how many times the operator was called since the last update.", "PopulationDiversityAnalyzerUpdateCounter"));
Parameters.Add(new FixedValueParameter(DiversityResultNameParameterName, "Specifies how the diversity results should be named.", new StringValue("PopulationDiversity")));
Parameters.Add(new FixedValueParameter(ExecuteInParallelParameterName, "Specifies whether similarity calculations should be parallelized.", new BoolValue(false)));
Parameters.Add(new FixedValueParameter(MaxDegreeOfParallelismParameterName, "Specifies the maximum number of threads when calculating similarities in parallel.", new IntValue(-1)));
var similarityCalculators = SimilarityCalculatorParameter.ValidValues;
foreach (var sc in validSimilarityCalculators) {
similarityCalculators.Add(sc);
}
ResultsParameter.Hidden = true;
UpdateCounterParameter.Hidden = true;
ExecuteInParallelParameter.Hidden = true;
MaxDegreeOfParallelismParameter.Hidden = true;
RegisterParametersEventHandlers();
}
private void RegisterParametersEventHandlers() {
ExecuteInParallelParameter.Value.ValueChanged += Value_ValueChanged;
MaxDegreeOfParallelismParameter.Value.ValueChanged += Value_ValueChanged;
}
private void Value_ValueChanged(object sender, EventArgs e) {
var similarityCalculators = SimilarityCalculatorParameter.ValidValues;
foreach (var similarityCalculator in similarityCalculators) {
similarityCalculator.ExecuteInParallel = ExecuteInParallel;
similarityCalculator.MaxDegreeOfParallelism = MaxDegreeOfParallelism;
}
}
public override IDeepCloneable Clone(Cloner cloner) {
return new PopulationSimilarityAnalyzer(this, cloner);
}
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
// BackwardsCompatibility3.3
#region Backwards compatible code, remove with 3.4
if (!Parameters.ContainsKey(DiversityResultNameParameterName))
Parameters.Add(new FixedValueParameter(DiversityResultNameParameterName, "Specifies how the diversity results should be named.", new StringValue("PopulationDiversity")));
if (!Parameters.ContainsKey("SimilarityCalculator"))
Parameters.Add(new ConstrainedValueParameter("SimilarityCalculator", "The similarity calculator that should be used to calculate solution similarity."));
if (oldSimilarityCalculator != null)
SimilarityCalculatorParameter.ValidValues.Add(oldSimilarityCalculator);
if (!Parameters.ContainsKey(ExecuteInParallelParameterName)) {
Parameters.Add(new FixedValueParameter(ExecuteInParallelParameterName,
"Specifies whether similarity calculations should be parallelized.", new BoolValue(false)));
ExecuteInParallelParameter.Hidden = true;
}
if (!Parameters.ContainsKey(MaxDegreeOfParallelismParameterName)) {
Parameters.Add(new FixedValueParameter(MaxDegreeOfParallelismParameterName,
"Specifies the maximum number of threads when calculating similarities in parallel.", new IntValue(-1)));
MaxDegreeOfParallelismParameter.Hidden = true;
}
RegisterParametersEventHandlers();
#endregion
}
public override IOperation Apply() {
int updateInterval = UpdateIntervalParameter.Value.Value;
IntValue updateCounter = UpdateCounterParameter.ActualValue;
// if counter does not yet exist then initialize it with update interval
// to make sure the solutions are analyzed on the first application of this operator
if (updateCounter == null) {
updateCounter = new IntValue(updateInterval);
UpdateCounterParameter.ActualValue = updateCounter;
}
//analyze solutions only every 'updateInterval' times
if (updateCounter.Value != updateInterval) {
updateCounter.Value++;
return base.Apply();
}
updateCounter.Value = 1;
bool storeHistory = StoreHistoryParameter.Value.Value;
int count = CurrentScopeParameter.ActualValue.SubScopes.Count;
if (count > 1) {
var similarityCalculator = SimilarityCalculatorParameter.Value;
// calculate solution similarities
var similarityMatrix = similarityCalculator.CalculateSolutionCrowdSimilarity(CurrentScopeParameter.ActualValue);
double[,] similarities = new double[similarityMatrix.Length, similarityMatrix[0].Length];
for (int i = 0; i < similarityMatrix.Length; i++)
for (int j = 0; j < similarityMatrix[0].Length; j++)
similarities[i, j] = similarityMatrix[i][j];
// calculate minimum, average and maximum similarities
double similarity;
double[] minSimilarities = new double[count];
double[] avgSimilarities = new double[count];
double[] maxSimilarities = new double[count];
for (int i = 0; i < count; i++) {
minSimilarities[i] = 1;
avgSimilarities[i] = 0;
maxSimilarities[i] = 0;
for (int j = 0; j < count; j++) {
if (i != j) {
similarity = similarities[i, j];
if ((similarity < 0) || (similarity > 1))
throw new InvalidOperationException("Solution similarities have to be in the interval [0;1].");
if (minSimilarities[i] > similarity) minSimilarities[i] = similarity;
avgSimilarities[i] += similarity;
if (maxSimilarities[i] < similarity) maxSimilarities[i] = similarity;
}
}
avgSimilarities[i] = avgSimilarities[i] / (count - 1);
}
double avgMinSimilarity = minSimilarities.Average();
double avgAvgSimilarity = avgSimilarities.Average();
double avgMaxSimilarity = maxSimilarities.Average();
// fetch results collection
ResultCollection results;
if (!ResultsParameter.ActualValue.ContainsKey(DiversityResultName)) {
results = new ResultCollection();
ResultsParameter.ActualValue.Add(new Result(DiversityResultName, results));
} else {
results = (ResultCollection)ResultsParameter.ActualValue[DiversityResultName].Value;
}
// store similarities
HeatMap similaritiesHeatMap = new HeatMap(similarities, "Solution Similarities", 0.0, 1.0);
if (!results.ContainsKey("Solution Similarities"))
results.Add(new Result("Solution Similarities", similaritiesHeatMap));
else
results["Solution Similarities"].Value = similaritiesHeatMap;
// store similarities history
if (storeHistory) {
if (!results.ContainsKey("Solution Similarities History")) {
HeatMapHistory history = new HeatMapHistory();
history.Add(similaritiesHeatMap);
results.Add(new Result("Solution Similarities History", history));
} else {
((HeatMapHistory)results["Solution Similarities History"].Value).Add(similaritiesHeatMap);
}
}
// store average minimum, average and maximum similarity
if (!results.ContainsKey("Average Minimum Solution Similarity"))
results.Add(new Result("Average Minimum Solution Similarity", new DoubleValue(avgMinSimilarity)));
else
((DoubleValue)results["Average Minimum Solution Similarity"].Value).Value = avgMinSimilarity;
if (!results.ContainsKey("Average Average Solution Similarity"))
results.Add(new Result("Average Average Solution Similarity", new DoubleValue(avgAvgSimilarity)));
else
((DoubleValue)results["Average Average Solution Similarity"].Value).Value = avgAvgSimilarity;
if (!results.ContainsKey("Average Maximum Solution Similarity"))
results.Add(new Result("Average Maximum Solution Similarity", new DoubleValue(avgMaxSimilarity)));
else
((DoubleValue)results["Average Maximum Solution Similarity"].Value).Value = avgMaxSimilarity;
// store average minimum, average and maximum solution similarity data table
DataTable minAvgMaxSimilarityDataTable;
if (!results.ContainsKey("Average Minimum/Average/Maximum Solution Similarity")) {
minAvgMaxSimilarityDataTable = new DataTable("Average Minimum/Average/Maximum Solution Similarity");
minAvgMaxSimilarityDataTable.VisualProperties.XAxisTitle = "Iteration";
minAvgMaxSimilarityDataTable.VisualProperties.YAxisTitle = "Solution Similarity";
minAvgMaxSimilarityDataTable.Rows.Add(new DataRow("Average Minimum Solution Similarity", null));
minAvgMaxSimilarityDataTable.Rows["Average Minimum Solution Similarity"].VisualProperties.StartIndexZero = true;
minAvgMaxSimilarityDataTable.Rows.Add(new DataRow("Average Average Solution Similarity", null));
minAvgMaxSimilarityDataTable.Rows["Average Average Solution Similarity"].VisualProperties.StartIndexZero = true;
minAvgMaxSimilarityDataTable.Rows.Add(new DataRow("Average Maximum Solution Similarity", null));
minAvgMaxSimilarityDataTable.Rows["Average Maximum Solution Similarity"].VisualProperties.StartIndexZero = true;
results.Add(new Result("Average Minimum/Average/Maximum Solution Similarity", minAvgMaxSimilarityDataTable));
} else {
minAvgMaxSimilarityDataTable = (DataTable)results["Average Minimum/Average/Maximum Solution Similarity"].Value;
}
minAvgMaxSimilarityDataTable.Rows["Average Minimum Solution Similarity"].Values.Add(avgMinSimilarity);
minAvgMaxSimilarityDataTable.Rows["Average Average Solution Similarity"].Values.Add(avgAvgSimilarity);
minAvgMaxSimilarityDataTable.Rows["Average Maximum Solution Similarity"].Values.Add(avgMaxSimilarity);
// store minimum, average, maximum similarities data table
DataTable minAvgMaxSimilaritiesDataTable = new DataTable("Minimum/Average/Maximum Solution Similarities");
minAvgMaxSimilaritiesDataTable.VisualProperties.XAxisTitle = "Solution Index";
minAvgMaxSimilaritiesDataTable.VisualProperties.YAxisTitle = "Solution Similarity";
minAvgMaxSimilaritiesDataTable.Rows.Add(new DataRow("Minimum Solution Similarity", null, minSimilarities));
minAvgMaxSimilaritiesDataTable.Rows["Minimum Solution Similarity"].VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
minAvgMaxSimilaritiesDataTable.Rows.Add(new DataRow("Average Solution Similarity", null, avgSimilarities));
minAvgMaxSimilaritiesDataTable.Rows["Average Solution Similarity"].VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
minAvgMaxSimilaritiesDataTable.Rows.Add(new DataRow("Maximum Solution Similarity", null, maxSimilarities));
minAvgMaxSimilaritiesDataTable.Rows["Maximum Solution Similarity"].VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
if (!results.ContainsKey("Minimum/Average/Maximum Solution Similarities")) {
results.Add(new Result("Minimum/Average/Maximum Solution Similarities", minAvgMaxSimilaritiesDataTable));
} else {
results["Minimum/Average/Maximum Solution Similarities"].Value = minAvgMaxSimilaritiesDataTable;
}
// store minimum, average, maximum similarities history
if (storeHistory) {
if (!results.ContainsKey("Minimum/Average/Maximum Solution Similarities History")) {
DataTableHistory history = new DataTableHistory();
history.Add(minAvgMaxSimilaritiesDataTable);
results.Add(new Result("Minimum/Average/Maximum Solution Similarities History", history));
} else {
((DataTableHistory)results["Minimum/Average/Maximum Solution Similarities History"].Value).Add(minAvgMaxSimilaritiesDataTable);
}
}
}
return base.Apply();
}
}
}