#region License Information
/* HeuristicLab
* Copyright (C) 2002-2018 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.Optimization;
using HeuristicLab.Parameters;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Regression {
///
/// An operator that optimizes the constants for the best symbolic expression tress in the current generation.
///
[Item("ConstantOptimizationAnalyzer", "An operator that performs a constant optimization on the best symbolic expression trees.")]
[StorableClass]
public sealed class ConstantOptimizationAnalyzer : SymbolicDataAnalysisSingleObjectiveAnalyzer, IStatefulItem {
private const string PercentageOfBestSolutionsParameterName = "PercentageOfBestSolutions";
private const string ConstantOptimizationEvaluatorParameterName = "ConstantOptimizationOperator";
private const string DataTableNameConstantOptimizationImprovement = "Constant Optimization Improvement";
private const string DataRowNameMinimumImprovement = "Minimum improvement";
private const string DataRowNameMedianImprovement = "Median improvement";
private const string DataRowNameAverageImprovement = "Average improvement";
private const string DataRowNameMaximumImprovement = "Maximum improvement";
#region parameter properties
public IFixedValueParameter PercentageOfBestSolutionsParameter {
get { return (IFixedValueParameter)Parameters[PercentageOfBestSolutionsParameterName]; }
}
public IFixedValueParameter ConstantOptimizationEvaluatorParameter {
get { return (IFixedValueParameter)Parameters[ConstantOptimizationEvaluatorParameterName]; }
}
#endregion
#region properties
public SymbolicRegressionConstantOptimizationEvaluator ConstantOptimizationEvaluator {
get { return ConstantOptimizationEvaluatorParameter.Value; }
}
public double PercentageOfBestSolutions {
get { return PercentageOfBestSolutionsParameter.Value.Value; }
}
private DataTable ConstantOptimizationImprovementDataTable {
get {
IResult result;
ResultCollection.TryGetValue(DataTableNameConstantOptimizationImprovement, out result);
if (result == null) return null;
return (DataTable)result.Value;
}
}
private DataRow MinimumImprovement {
get { return ConstantOptimizationImprovementDataTable.Rows[DataRowNameMinimumImprovement]; }
}
private DataRow MedianImprovement {
get { return ConstantOptimizationImprovementDataTable.Rows[DataRowNameMedianImprovement]; }
}
private DataRow AverageImprovement {
get { return ConstantOptimizationImprovementDataTable.Rows[DataRowNameAverageImprovement]; }
}
private DataRow MaximumImprovement {
get { return ConstantOptimizationImprovementDataTable.Rows[DataRowNameMaximumImprovement]; }
}
#endregion
[StorableConstructor]
private ConstantOptimizationAnalyzer(bool deserializing) : base(deserializing) { }
private ConstantOptimizationAnalyzer(ConstantOptimizationAnalyzer original, Cloner cloner) : base(original, cloner) { }
public override IDeepCloneable Clone(Cloner cloner) { return new ConstantOptimizationAnalyzer(this, cloner); }
public ConstantOptimizationAnalyzer()
: base() {
Parameters.Add(new FixedValueParameter(PercentageOfBestSolutionsParameterName, "The percentage of the top solutions which should be analyzed.", new PercentValue(0.1)));
Parameters.Add(new FixedValueParameter(ConstantOptimizationEvaluatorParameterName, "The operator used to perform the constant optimization"));
//Changed the ActualName of the EvaluationPartitionParameter so that it matches the parameter name of symbolic regression problems.
ConstantOptimizationEvaluator.EvaluationPartitionParameter.ActualName = "FitnessCalculationPartition";
}
private double[] qualitiesBeforeCoOp = null;
private int[] scopeIndexes = null;
void IStatefulItem.InitializeState() {
qualitiesBeforeCoOp = null;
scopeIndexes = null;
}
void IStatefulItem.ClearState() {
qualitiesBeforeCoOp = null;
scopeIndexes = null;
}
public override IOperation Apply() {
//code executed in the first call of analyzer
if (qualitiesBeforeCoOp == null) {
double[] trainingQuality;
// sort is ascending and we take the first n% => order so that best solutions are smallest
// sort order is determined by maximization parameter
if (Maximization.Value) {
// largest values must be sorted first
trainingQuality = Quality.Select(x => -x.Value).ToArray();
} else {
// smallest values must be sorted first
trainingQuality = Quality.Select(x => x.Value).ToArray();
}
// sort trees by training qualities
int topN = (int)Math.Max(trainingQuality.Length * PercentageOfBestSolutions, 1);
scopeIndexes = Enumerable.Range(0, trainingQuality.Length).ToArray();
Array.Sort(trainingQuality, scopeIndexes);
scopeIndexes = scopeIndexes.Take(topN).ToArray();
qualitiesBeforeCoOp = scopeIndexes.Select(x => Quality[x].Value).ToArray();
OperationCollection operationCollection = new OperationCollection();
operationCollection.Parallel = true;
foreach (var scopeIndex in scopeIndexes) {
var childOperation = ExecutionContext.CreateChildOperation(ConstantOptimizationEvaluator, ExecutionContext.Scope.SubScopes[scopeIndex]);
operationCollection.Add(childOperation);
}
return new OperationCollection { operationCollection, ExecutionContext.CreateOperation(this) };
}
//code executed to analyze results of constant optimization
double[] qualitiesAfterCoOp = scopeIndexes.Select(x => Quality[x].Value).ToArray();
var qualityImprovement = qualitiesBeforeCoOp.Zip(qualitiesAfterCoOp, (b, a) => a - b).ToArray();
if (!ResultCollection.ContainsKey(DataTableNameConstantOptimizationImprovement)) {
var dataTable = new DataTable(DataTableNameConstantOptimizationImprovement);
ResultCollection.Add(new Result(DataTableNameConstantOptimizationImprovement, dataTable));
dataTable.VisualProperties.YAxisTitle = "Rē";
dataTable.Rows.Add(new DataRow(DataRowNameMinimumImprovement));
MinimumImprovement.VisualProperties.StartIndexZero = true;
dataTable.Rows.Add(new DataRow(DataRowNameMedianImprovement));
MedianImprovement.VisualProperties.StartIndexZero = true;
dataTable.Rows.Add(new DataRow(DataRowNameAverageImprovement));
AverageImprovement.VisualProperties.StartIndexZero = true;
dataTable.Rows.Add(new DataRow(DataRowNameMaximumImprovement));
MaximumImprovement.VisualProperties.StartIndexZero = true;
}
MinimumImprovement.Values.Add(qualityImprovement.Min());
MedianImprovement.Values.Add(qualityImprovement.Median());
AverageImprovement.Values.Add(qualityImprovement.Average());
MaximumImprovement.Values.Add(qualityImprovement.Max());
qualitiesBeforeCoOp = null;
scopeIndexes = null;
return base.Apply();
}
}
}