#region License Information
/* HeuristicLab
* Copyright (C) 2002-2016 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;
namespace HeuristicLab.Analysis.FitnessLandscape {
public class InformationAnalysis {
public class Peak {
public double QualityDelta { get; private set; }
public double Value { get; private set; }
public Peak(double qualityDelta, double value) {
QualityDelta = qualityDelta;
Value = value;
}
}
public List InformationContent { get; private set; }
public List PartialInformationContent { get; private set; }
public List DensityBasinInformation { get; private set; }
public List TotalEntropy { get; private set; }
public List QualityDelta { get; private set; }
public double InformationStability { get; private set; }
public int Regularity { get; private set; }
public int Diversity { get; private set; }
public Peak PeakInformationContent { get; private set; }
public Peak PeakPartialInformationContent { get; private set; }
public Peak PeakDensityBasinInformation { get; private set; }
public Peak PeakTotalEntropy { get; private set; }
public InformationAnalysis(IList qualities, int nQuantiles, int shapeSize) {
InformationContent = new List();
PartialInformationContent = new List();
DensityBasinInformation = new List();
TotalEntropy = new List();
QualityDelta = new List();
if (shapeSize < 1)
throw new ArgumentException("Shape size must be at least 1 (better 2)");
if (qualities.Count > 1)
PerformAnalysis(qualities, nQuantiles, shapeSize);
}
private void PerformAnalysis(IList qualities, int nQuantiles, int shapeSize) {
var differences = Differences(qualities).ToList();
InformationStability = differences.Select(Math.Abs).Max();
Regularity = new HashSet(differences).Count;
Diversity = new HashSet(qualities).Count;
var absDifferences = differences.Select(Math.Abs).OrderBy(d => d).ToList();
nQuantiles = Math.Min(nQuantiles, absDifferences.Count);
var thresholds = (nQuantiles == 0
? absDifferences
: UniqueThresholdCalculator.DetermineThresholds(differences, nQuantiles)).ToList();
foreach (var eps in thresholds) {
if (QualityDelta.Count > 0 && QualityDelta.Last() == eps) {
QualityDelta.DuplicateLast();
InformationContent.DuplicateLast();
DensityBasinInformation.DuplicateLast();
TotalEntropy.DuplicateLast();
PartialInformationContent.DuplicateLast();
} else {
var slopes = Slopes(eps, differences).ToList();
var shapes = Shapes(shapeSize, slopes).ToList();
var shapeCounts = CountShapes(shapes);
QualityDelta.Add(eps);
InformationContent.Add(CalculateEntropy(Shape.GetAll(shapeSize, Shape.Form.NonUni), shapeCounts, shapes.Count));
DensityBasinInformation.Add(CalculateEntropy(Shape.GetAll(shapeSize, Shape.Form.Uni), shapeCounts, shapes.Count));
TotalEntropy.Add(CalculateEntropy(Shape.GetAll(shapeSize, Shape.Form.Any), shapeCounts, shapes.Count));
PartialInformationContent.Add(CalculatePartialInformationContent(eps, differences));
}
}
PeakInformationContent = GetPeak(QualityDelta, InformationContent);
PeakDensityBasinInformation = GetPeak(QualityDelta, DensityBasinInformation);
PeakPartialInformationContent = GetPeak(QualityDelta, PartialInformationContent);
PeakTotalEntropy = GetPeak(QualityDelta, TotalEntropy);
}
public static Peak GetPeak(IEnumerable indexes, IEnumerable values) {
var max = indexes.Zip(values, (i, v) => new { i, v }).OrderByDescending(p => p.v).First();
return new Peak(max.i, max.v);
}
public enum Slope {
Up = 1,
Flat = 0,
Down = -1
}
public class Shape : IEnumerable, IComparable {
#region types, fields and properties
public enum Form { Uni, NonUni, Any }
private readonly Slope[] slopes;
private static readonly Dictionary, IList> SHAPES =
new Dictionary, IList>();
#endregion
public Shape(IEnumerable slopes) {
this.slopes = slopes.ToArray();
}
#region static methods
public static Shape Get(params Slope[] slopes) {
return new Shape(slopes);
}
public static IList GetAll(int size, Form type) {
var key = Tuple.Create(type, size);
IList allShapes;
if (!SHAPES.TryGetValue(key, out allShapes)) {
allShapes = CreateAll(size, type).ToList();
SHAPES[key] = allShapes;
}
return allShapes;
}
private static IEnumerable CreateAll(int size, Form type) {
if (size == 0) {
yield return Get();
} else {
foreach (var s in CreateAll(size - 1, type)) {
foreach (var s2 in s.ExtendAll(type))
yield return s2;
}
}
}
#endregion
private Shape Extend(Slope s) {
return new Shape(slopes.Concat(new[] { s }));
}
private IEnumerable ExtendAll(Form t) {
if (Length == 0 || t == Form.Any) {
yield return Extend(Slope.Up);
yield return Extend(Slope.Flat);
yield return Extend(Slope.Down);
} else if (t == Form.Uni) {
yield return Extend(slopes[0]);
} else if (t == Form.NonUni) {
if (slopes.Last() != Slope.Up) yield return Extend(Slope.Up);
if (slopes.Last() != Slope.Flat) yield return Extend(Slope.Flat);
if (slopes.Last() != Slope.Down) yield return Extend(Slope.Down);
}
}
public int Length {
get { return slopes.Length; }
}
public Slope this[int i] {
get { return slopes[i]; }
}
#region IEnumerable Members
public IEnumerator GetEnumerator() {
return (IEnumerator)slopes.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return GetEnumerator();
}
#endregion
#region IComparable Members
public int CompareTo(Shape other) {
if (other.Length < Length)
return -1;
if (other.Length > Length)
return 1;
for (var i = 0; i < Length; i++) {
var d = slopes[i].CompareTo(other.slopes[i]);
if (d != 0)
return d;
}
return 0;
}
public override bool Equals(object obj) {
var s = obj as Shape;
if (s == null) return false;
return CompareTo(s) == 0;
}
public override int GetHashCode() {
return slopes.Aggregate(0, (a, s) => a * 3 + ((int)s + 1)).GetHashCode();
}
#endregion
private string asString;
public override string ToString() {
return asString ?? (asString = string.Join("", slopes.Select(s => (s == Slope.Down ? "v" : (s == Slope.Up ? "^" : "-")))));
}
}
private static IEnumerable Slopes(double eps, IEnumerable differences) {
return differences.Select(d => (d > eps ? Slope.Up : (d < -eps ? Slope.Down : 0)));
}
private static IEnumerable Shapes(int size, IEnumerable slopes) {
var q = new Queue();
foreach (var s in slopes) {
q.Enqueue(s);
if (q.Count < size) continue;
yield return new Shape(q);
q.Dequeue();
}
}
private static double CalculateEntropy(IList shapes, Dictionary shapeCounts, int totalNShapes) {
return shapes.Aggregate(0.0, (current, s) => current - Entropy(shapeCounts.GetValueOrDefault(s, 0), totalNShapes, shapes.Count));
}
private static double CalculatePartialInformationContent(double eps, ICollection differences) {
int slope = 0;
int nPeaks = 0;
foreach (var d in differences) {
if (d > eps) {
if (slope < 0) nPeaks++;
slope = +1;
} else if (d < -eps) {
if (slope > 0) nPeaks++;
slope = -1;
}
}
return 1.0 * nPeaks / differences.Count;
}
private static Dictionary CountShapes(IEnumerable shapes) {
var shapeCounts = new Dictionary();
foreach (var group in shapes.GroupBy(s => s))
shapeCounts[group.Key] = group.Count();
return shapeCounts;
}
private static double Entropy(int count, int total, int nCases) {
if (count == 0)
return 0;
double freq = 1.0 * count / total;
return freq * Math.Log(freq, nCases);
}
private static IEnumerable Differences(IEnumerable values) {
return values.Delta((x, y) => y - x);
}
}
}