#region License Information
/* HeuristicLab
* Copyright (C) 2002-2010 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.Core;
using HeuristicLab.Common;
using HeuristicLab.Data;
using HeuristicLab.Encodings.PermutationEncoding;
using HeuristicLab.Operators;
using HeuristicLab.Optimization;
using HeuristicLab.Parameters;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using System.Data;
namespace HeuristicLab.Problems.TravelingSalesman {
///
/// An operator for analyzing the diversity of a population of solutions for a Traveling Salesman Problems given in path representation using city coordinates.
///
[Item("TSPPopulationDiversityAnalyzer", "An operator for analyzing the diversity of a population of solutions for a Traveling Salesman Problems given in path representation using city coordinates.")]
[StorableClass]
public sealed class TSPPopulationDiversityAnalyzer : SingleSuccessorOperator, IAnalyzer {
// TODO:
// - iterations sampling
// - view
// - extract population diversity basic behavior into separate project
// - analyze variation of values
public const string PermutationKey = "Permutation";
public ScopeTreeLookupParameter PermutationParameter {
get { return (ScopeTreeLookupParameter)Parameters[PermutationKey]; }
}
public const string QualityKey = "Quality";
public ScopeTreeLookupParameter QualityParameter {
get { return (ScopeTreeLookupParameter)Parameters[QualityKey]; }
}
public const string StoreCompleteHistoryKey = "StoreCompleteHistory";
public ValueParameter StoreCompleteHistoryParameter {
get { return (ValueParameter)Parameters[StoreCompleteHistoryKey]; }
}
public const string CurrentSimilaritiesKey = "Current Solution Similarities";
public const string CurrentAverageSimilarityKey = "Current Average Population Similarity";
public const string AverageSimilarityProgressKey = "Average Population Similarity Progress";
public const string CurrentAverageMaximumSimilarityKey = "Current Average Maximum Population Similarity";
public const string AverageMaximumSimilarityProgressKey = "Average Maximum Population Similarity Progress";
public const string PopulationDiversityAnalysisResultsDetailsKey = "Population Diversity Analysis Results Details";
public const string ResultsKey = "Results";
public ValueLookupParameter ResultsParameter {
get { return (ValueLookupParameter)Parameters[ResultsKey]; }
}
public TSPPopulationDiversityAnalyzer()
: base() {
Parameters.Add(new ScopeTreeLookupParameter(PermutationKey, "The TSP solutions given in path representation from which the best solution should be analyzed."));
Parameters.Add(new ScopeTreeLookupParameter(QualityKey, "The qualities of the TSP solutions which should be analyzed."));
Parameters.Add(new ValueParameter(StoreCompleteHistoryKey, "Flag that denotes whether the complete history of similarity values shall be stored.", new BoolValue(true)));
Parameters.Add(new ValueLookupParameter("Results", "The results collection in which the population diversity analysis results should be stored."));
}
public override IOperation Apply() {
ItemArray permutations = PermutationParameter.ActualValue;
ItemArray qualities = QualityParameter.ActualValue;
Permutation[] permutationsArray = permutations.ToArray();
DoubleValue[] qualitiesArray = qualities.ToArray();
int cities = permutationsArray.Length;
ResultCollection results = ResultsParameter.ActualValue;
#region sort permutations array
for (int i = 0; i < cities; i++) {
int minIndex = i;
for (int j = i + 1; j < cities; j++) {
if (qualitiesArray[j].Value < qualitiesArray[minIndex].Value)
minIndex = j;
}
if (minIndex != i) {
Permutation p = permutationsArray[i];
permutationsArray[i] = permutationsArray[minIndex];
permutationsArray[minIndex] = p;
DoubleValue d = qualitiesArray[i];
qualitiesArray[i] = qualitiesArray[minIndex];
qualitiesArray[minIndex] = d;
}
}
#endregion
int[][] edges = new int[cities][];
for (int i = 0; i < cities; i++)
edges[i] = CalculateEdgesVector(permutationsArray[i]);
DoubleMatrix similarities = new DoubleMatrix(cities, cities);
DoubleArray maxSimilarities = new DoubleArray(cities);
double avgSimilarity = 0;
int n = 0;
for (int i = 0; i < cities; i++) {
similarities[i, i] = 1;
for (int j = (i + 1); j < cities; j++) {
double similarity = CalculateSimilarity(edges[i], edges[j]);
avgSimilarity += similarity;
n++;
similarities[i, j] = similarity;
similarities[j, i] = similarity;
if (maxSimilarities[i] < similarity)
maxSimilarities[i] = similarity;
if (maxSimilarities[j] < similarity)
maxSimilarities[j] = similarity;
}
}
DoubleValue averageMaximumSimilarity = new DoubleValue(maxSimilarities.Average());
DoubleValue averageSimilarity = new DoubleValue(avgSimilarity / n);
#region Store current solution similarities
if (results.ContainsKey(CurrentAverageSimilarityKey))
results[CurrentSimilaritiesKey].Value = similarities;
else
results.Add(new Result(CurrentSimilaritiesKey, similarities));
#endregion
#region Store average similarity values
if (results.ContainsKey(CurrentAverageSimilarityKey))
results[CurrentAverageSimilarityKey].Value = averageSimilarity;
else
results.Add(new Result(CurrentAverageSimilarityKey, averageSimilarity));
if (!results.ContainsKey(AverageSimilarityProgressKey))
results.Add(new Result(AverageSimilarityProgressKey, new Analysis.DataTable(AverageSimilarityProgressKey)));
Analysis.DataTable averageSimilarityProgressDataTable = (Analysis.DataTable)(results[AverageSimilarityProgressKey].Value);
if (averageSimilarityProgressDataTable.Rows.Count == 0)
averageSimilarityProgressDataTable.Rows.Add(new Analysis.DataRow(AverageSimilarityProgressKey));
averageSimilarityProgressDataTable.Rows[AverageSimilarityProgressKey].Values.Add(averageSimilarity.Value);
#endregion
#region Store average maximum similarity values
if (results.ContainsKey(CurrentAverageMaximumSimilarityKey))
results[CurrentAverageMaximumSimilarityKey].Value = averageMaximumSimilarity;
else
results.Add(new Result(CurrentAverageMaximumSimilarityKey, averageMaximumSimilarity));
if (!results.ContainsKey(AverageMaximumSimilarityProgressKey))
results.Add(new Result(AverageMaximumSimilarityProgressKey, new Analysis.DataTable(AverageMaximumSimilarityProgressKey)));
Analysis.DataTable averageMaximumSimilarityProgressDataTable = (Analysis.DataTable)(results[AverageMaximumSimilarityProgressKey].Value);
if (averageMaximumSimilarityProgressDataTable.Rows.Count == 0)
averageMaximumSimilarityProgressDataTable.Rows.Add(new Analysis.DataRow(AverageMaximumSimilarityProgressKey));
averageMaximumSimilarityProgressDataTable.Rows[AverageMaximumSimilarityProgressKey].Values.Add(averageMaximumSimilarity.Value);
#endregion
#region Store details
TSPPopulationDiversityAnalysisDetails details;
if (!results.ContainsKey(PopulationDiversityAnalysisResultsDetailsKey)) {
details = new TSPPopulationDiversityAnalysisDetails();
results.Add(new Result(PopulationDiversityAnalysisResultsDetailsKey, details));
} else {
details = (TSPPopulationDiversityAnalysisDetails)(results[PopulationDiversityAnalysisResultsDetailsKey].Value);
}
details.AverageSimilarities.Add(averageSimilarity);
details.AverageMaximumSimilarities.Add(averageMaximumSimilarity);
details.Similarities.Add(similarities);
details.MaximumSimilarities.Add(maxSimilarities);
if (!StoreCompleteHistoryParameter.Value.Value && details.Similarities.Count > 1) {
details.Similarities[details.Similarities.Count - 1] = null;
details.MaximumSimilarities[details.MaximumSimilarities.Count - 1] = null;
}
#endregion
return base.Apply();
}
private static int[] CalculateEdgesVector(Permutation permutation) {
int cities = permutation.Length;
int[] edgesVector = new int[cities];
for (int i = 0; i < (cities - 1); i++)
edgesVector[permutation[i]] = permutation[i + 1];
edgesVector[permutation[cities - 1]] = permutation[0];
return edgesVector;
}
private double CalculateSimilarity(int[] edgesA, int[] edgesB) {
if (edgesA.Length != edgesB.Length)
throw new InvalidOperationException("ERROR in " + Name + ": Similarity can only be calculated between instances of an equal number of cities");
int cities = edgesA.Length;
int similarEdges = 0;
for (int i = 0; i < edgesA.Length; i++) {
if (edgesA[i] == edgesB[i] || edgesA[edgesB[i]] == i)
similarEdges++;
}
return (double)(similarEdges) / cities;
}
}
}