#region License Information
/* HeuristicLab
* Copyright (C) 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.Linq.Expressions;
using HEAL.Attic;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Encodings.IntegerVectorEncoding;
using HeuristicLab.Optimization;
using HeuristicLab.Parameters;
using HeuristicLab.PluginInfrastructure;
using HeuristicLab.Problems.Instances;
using HeuristicLab.Problems.Instances.Types;
using DoubleVector = MathNet.Numerics.LinearAlgebra.Vector;
namespace HeuristicLab.Problems.DataAnalysis.Symbolic.SegmentOptimization {
[Item("Segment Optimization Problem (SOP)", "")]
[Creatable(CreatableAttribute.Categories.CombinatorialProblems, Priority = 1200)]
[StorableType("64107939-34A7-4530-BFAB-8EA1C321BF6F")]
public class SegmentOptimizationProblem : SingleObjectiveBasicProblem, IProblemInstanceConsumer {
[StorableType("63243591-5A56-41A6-B079-122B83583993")]
public enum Aggregation {
Sum,
Mean,
StandardDeviation
}
public override bool Maximization => false;
[Storable]
private IValueParameter dataParameter;
public IValueParameter DataParameter {
get { return dataParameter; }
}
[Storable]
private IValueParameter knownBoundsParameter;
public IValueParameter KnownBoundsParameter {
get { return knownBoundsParameter; }
}
[Storable]
private IValueParameter> aggregationParameter;
public IValueParameter> AggregationParameter {
get { return aggregationParameter; }
}
public SegmentOptimizationProblem() {
Encoding = new IntegerVectorEncoding("bounds");
Parameters.Add(dataParameter = new ValueParameter("Data", ""));
Parameters.Add(knownBoundsParameter = new ValueParameter("Known Bounds", ""));
Parameters.Add(aggregationParameter = new ValueParameter>("Aggregation Function", ""));
RegisterEventHandlers();
#region Default Instance
Load(new SOPData() {
Data = ToNdimArray(Enumerable.Range(1, 50).Select(x => (double)x * x).ToArray()),
Lower = 20, Upper = 30,
Aggregation = "mean"
});
#endregion
var optMutators = ApplicationManager.Manager.GetInstances();
Encoding.ConfigureOperators(optMutators);
Operators.AddRange(optMutators);
}
private SegmentOptimizationProblem(SegmentOptimizationProblem original, Cloner cloner)
: base(original, cloner) {
dataParameter = cloner.Clone(original.dataParameter);
knownBoundsParameter = cloner.Clone(original.knownBoundsParameter);
aggregationParameter = cloner.Clone(original.aggregationParameter);
RegisterEventHandlers();
}
public override IDeepCloneable Clone(Cloner cloner) {
return new SegmentOptimizationProblem(this, cloner);
}
[StorableConstructor]
private SegmentOptimizationProblem(StorableConstructorFlag _) : base(_) { }
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
if (Parameters.ContainsKey("Data Vector") && Parameters["Data Vector"] is ValueParameter arrayParameter) {
Parameters.Remove(arrayParameter);
var array = arrayParameter.Value;
var matrix = new DoubleMatrix(1, array.Length);
for (int i = 0; i < array.Length; i++) matrix[0, i] = array[i];
Parameters.Add(dataParameter = new ValueParameter("Data", "", matrix));
}
RegisterEventHandlers();
}
private void RegisterEventHandlers() {
dataParameter.ValueChanged += DataChanged;
knownBoundsParameter.ValueChanged += KnownBoundsChanged;
aggregationParameter.Value.ValueChanged += AggregationFunctionChanged;
}
private void DataChanged(object sender, EventArgs eventArgs) {
Encoding.Bounds = new IntMatrix(new[,] { { 0, DataParameter.Value.Columns } });
}
private void KnownBoundsChanged(object sender, EventArgs e) {
}
private void AggregationFunctionChanged(object sender, EventArgs eventArgs) {
}
public override double Evaluate(Individual individual, IRandom random) {
var data = DataParameter.Value;
var knownBounds = KnownBoundsParameter.Value;
var aggregation = aggregationParameter.Value.Value;
var solution = individual.IntegerVector(Encoding.Name);
return Evaluate(solution, data, knownBounds, aggregation);
}
public static double Evaluate(IntegerVector solution, DoubleMatrix data, IntRange knownBounds, Aggregation aggregation) {
var bounds = new IntRange(solution.Min(), solution.Max());
double target = BoundedAggregation(data, knownBounds, aggregation);
double prediction = BoundedAggregation(data, bounds, aggregation);
return Math.Pow(target - prediction, 2);
}
public override void Analyze(Individual[] individuals, double[] qualities, ResultCollection results, IRandom random) {
var orderedIndividuals = individuals.Zip(qualities, (i, q) => new { Individual = i, Quality = q }).OrderBy(z => z.Quality);
var best = Maximization ? orderedIndividuals.Last().Individual.IntegerVector(Encoding.Name) : orderedIndividuals.First().Individual.IntegerVector(Encoding.Name);
var bounds = new IntRange(best.Min(), best.Max());
var data = DataParameter.Value;
var knownBounds = KnownBoundsParameter.Value;
var aggregation = aggregationParameter.Value.Value;
double target = BoundedAggregation(data, knownBounds, aggregation);
double prediction = BoundedAggregation(data, bounds, aggregation);
double diff = target - prediction;
if (results.TryGetValue("AggValue Diff", out var oldDiffResult)) {
var oldDiff = (DoubleValue)oldDiffResult.Value;
if (Math.Abs(oldDiff.Value) < Math.Abs(diff)) return;
}
results.AddOrUpdateResult("Bounds", bounds);
results.AddOrUpdateResult("AggValue Diff", new DoubleValue(diff));
results.AddOrUpdateResult("AggValue Squared Diff", new DoubleValue(Math.Pow(diff, 2)));
results.AddOrUpdateResult("Lower Diff", new IntValue(knownBounds.Start - bounds.Start));
results.AddOrUpdateResult("Upper Diff", new IntValue(knownBounds.End - bounds.End));
results.AddOrUpdateResult("Length Diff", new IntValue(knownBounds.Size - bounds.Size));
}
//private static double BoundedAggregation(DoubleArray data, IntRange bounds, Aggregation aggregation) {
// var matrix = new DoubleMatrix(1, data.Length);
// for (int i = 0; i < data.Length; i++) matrix[0, i] = data[i];
// return BoundedAggregation(matrix, bounds, aggregation);
//}
private static double BoundedAggregation(DoubleMatrix data, IntRange bounds, Aggregation aggregation) {
//if (bounds.Size == 0) {
// return 0;
//}
var resultValues = new double[data.Rows];
for (int row = 0; row < data.Rows; row++) {
var vector = data.GetRow(row);
var segment = vector.Skip(bounds.Start).Take(bounds.Size + 1); // exclusive end
switch (aggregation) {
case Aggregation.Sum:
resultValues[row] = segment.Sum();
break;
case Aggregation.Mean:
resultValues[row] = segment.Average();
break;
case Aggregation.StandardDeviation:
resultValues[row] = segment.StandardDeviationPop();
break;
default:
throw new NotImplementedException();
}
}
return resultValues.Average();
}
public void Load(SOPData data) {
DataParameter.Value = new DoubleMatrix(data.Data);
KnownBoundsParameter.Value = new IntRange(data.Lower, data.Upper);
switch (data.Aggregation.ToLower()) {
case "sum":
AggregationParameter.Value.Value = Aggregation.Sum;
break;
case "mean":
case "avg":
AggregationParameter.Value.Value = Aggregation.Mean;
break;
case "standarddeviation":
case "std":
case "sd":
AggregationParameter.Value.Value = Aggregation.StandardDeviation;
break;
default:
throw new NotSupportedException();
}
Encoding.Length = 2;
Encoding.Bounds = new IntMatrix(new[,] { { 0, DataParameter.Value.Columns } });
BestKnownQuality = 0;
Name = data.Name;
Description = data.Description;
}
public static T[,] ToNdimArray(T[] array) {
var matrix = new T[1, array.Length];
for (int i = 0; i < array.Length; i++)
matrix[0, i] = array[i];
return matrix;
}
private class DoubleArrayComparer : IEqualityComparer {
public bool Equals(double[,] x, double[,] y) {
if (ReferenceEquals(x, y)) return true;
if (x.Length != y.Length) return false;
if (x.GetLength(0) != y.GetLength(0)) return false;
if (x.GetLength(1) != y.GetLength(1)) return false;
int rows = x.GetLength(0), cols = x.GetLength(1);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (x[i, j] != y[i, j])
return false;
}
}
return true;
}
public int GetHashCode(double[,] obj) {
return GetSequenceHashCode(obj.Cast())/*gives matrix enumerated*/;
}
//https://stackoverflow.com/questions/7278136/create-hash-value-on-a-list
public static int GetSequenceHashCode(IEnumerable sequence) {
const int seed = 487;
const int modifier = 31;
unchecked {
return sequence.Aggregate(seed, (current, item) => (current * modifier) + item.GetHashCode());
}
}
}
private static readonly Action setValues;
private static readonly Func getValues;
static SegmentOptimizationProblem() {
var dataset = Expression.Parameter(typeof(DoubleMatrix));
var variableValues = Expression.Parameter(typeof(double[,]));
var valuesExpression = Expression.Field(dataset, "matrix");
var assignExpression = Expression.Assign(valuesExpression, variableValues);
var variableValuesSetExpression = Expression.Lambda>(assignExpression, dataset, variableValues);
setValues = variableValuesSetExpression.Compile();
var variableValuesGetExpression = Expression.Lambda>(valuesExpression, dataset);
getValues = variableValuesGetExpression.Compile();
}
public static Tuple RemoveDuplicateMatrices(IContent content) {
int overallTests = 0, removedDuplicated = 0;
var mappings = new Dictionary(new DoubleArrayComparer());
foreach (var parameter in content.GetObjectGraphObjects(excludeStaticMembers: true).OfType()) {
var originalValue = getValues(parameter);
overallTests++;
if (mappings.TryGetValue(originalValue, out var mappedValue)) {
setValues(parameter, mappedValue);
removedDuplicated++;
} else {
mappings.Add(originalValue, originalValue);
}
}
int removedQualities = 0;
foreach (var run in content.GetObjectGraphObjects(excludeStaticMembers: true).OfType()) {
if (run.Results.ContainsKey("Qualities")) {
run.Results.Remove("Qualities");
removedQualities++;
}
}
return Tuple.Create(overallTests, removedDuplicated, removedQualities);
}
}
}