#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.Linq;
using HEAL.Attic;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
using HeuristicLab.Optimization;
using HeuristicLab.Parameters;
using HeuristicLab.PluginInfrastructure;
using HeuristicLab.Problems.Instances;
using HeuristicLab.Problems.Instances.DataAnalysis;
namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Regression {
[StorableType("7464E84B-65CC-440A-91F0-9FA920D730F9")]
[Item(Name = "Structured Symbolic Regression Single Objective Problem (single-objective)", Description = "A problem with a structural definition and unfixed subfunctions.")]
[Creatable(CreatableAttribute.Categories.GeneticProgrammingProblems, Priority = 150)]
public class StructuredSymbolicRegressionSingleObjectiveProblem : SingleObjectiveBasicProblem, IRegressionProblem, IProblemInstanceConsumer {
#region Constants
private const string TreeEvaluatorParameterName = "TreeEvaluator";
private const string ProblemDataParameterName = "ProblemData";
private const string StructureTemplateParameterName = "Structure Template";
private const string InterpreterParameterName = "Interpreter";
private const string EstimationLimitsParameterName = "EstimationLimits";
private const string BestTrainingSolutionParameterName = "Best Training Solution";
private const string ApplyLinearScalingParameterName = "Apply Linear Scaling";
private const string OptimizeParametersParameterName = "Optimize Parameters";
private const string SymbolicExpressionTreeName = "SymbolicExpressionTree";
private const string StructureTemplateDescriptionText =
"Enter your expression as string in infix format into the empty input field.\n" +
"By checking the \"Apply Linear Scaling\" checkbox you can add the relevant scaling terms to your expression.\n" +
"After entering the expression click parse to build the tree.\n" +
"To edit the defined sub-functions, click on the corresponding-colored node in the tree view.\n" +
"Check the info box besides the input field for more information.";
#endregion
#region Parameters
public IConstrainedValueParameter TreeEvaluatorParameter => (IConstrainedValueParameter)Parameters[TreeEvaluatorParameterName];
public IValueParameter ProblemDataParameter => (IValueParameter)Parameters[ProblemDataParameterName];
public IFixedValueParameter StructureTemplateParameter => (IFixedValueParameter)Parameters[StructureTemplateParameterName];
public IValueParameter InterpreterParameter => (IValueParameter)Parameters[InterpreterParameterName];
public IFixedValueParameter EstimationLimitsParameter => (IFixedValueParameter)Parameters[EstimationLimitsParameterName];
public IResultParameter BestTrainingSolutionParameter => (IResultParameter)Parameters[BestTrainingSolutionParameterName];
public IFixedValueParameter ApplyLinearScalingParameter => (IFixedValueParameter)Parameters[ApplyLinearScalingParameterName];
public IFixedValueParameter OptimizeParametersParameter => (IFixedValueParameter)Parameters[OptimizeParametersParameterName];
#endregion
#region Properties
public IRegressionProblemData ProblemData {
get => ProblemDataParameter.Value;
set {
ProblemDataParameter.Value = value;
ProblemDataChanged?.Invoke(this, EventArgs.Empty);
}
}
public SymbolicRegressionSingleObjectiveEvaluator TreeEvaluator => TreeEvaluatorParameter.Value;
public StructureTemplate StructureTemplate => StructureTemplateParameter.Value;
public ISymbolicDataAnalysisExpressionTreeInterpreter Interpreter => InterpreterParameter.Value;
IParameter IDataAnalysisProblem.ProblemDataParameter => ProblemDataParameter;
IDataAnalysisProblemData IDataAnalysisProblem.ProblemData => ProblemData;
public DoubleLimit EstimationLimits => EstimationLimitsParameter.Value;
public bool ApplyLinearScaling {
get => ApplyLinearScalingParameter.Value.Value;
set => ApplyLinearScalingParameter.Value.Value = value;
}
public bool OptimizeParameters {
get => OptimizeParametersParameter.Value.Value;
set => OptimizeParametersParameter.Value.Value = value;
}
public override bool Maximization => false;
#endregion
#region EventHandlers
public event EventHandler ProblemDataChanged;
#endregion
#region Constructors & Cloning
public StructuredSymbolicRegressionSingleObjectiveProblem() {
var provider = new PhysicsInstanceProvider();
var descriptor = new SheetBendingProcess();
var problemData = provider.LoadData(descriptor);
var shapeConstraintProblemData = new ShapeConstrainedRegressionProblemData(problemData);
var structureTemplate = new StructureTemplate();
var evaluators = new ItemSet(
ApplicationManager.Manager.GetInstances()
.Where(x => x.Maximization == Maximization));
Parameters.Add(new ConstrainedValueParameter(
TreeEvaluatorParameterName,
evaluators,
evaluators.First()));
Parameters.Add(new ValueParameter(
ProblemDataParameterName,
shapeConstraintProblemData));
Parameters.Add(new FixedValueParameter(
StructureTemplateParameterName,
StructureTemplateDescriptionText,
structureTemplate));
Parameters.Add(new FixedValueParameter(
ApplyLinearScalingParameterName, new BoolValue(true)
));
Parameters.Add(new FixedValueParameter(
OptimizeParametersParameterName, new BoolValue(true)
));
Parameters.Add(new ValueParameter(
InterpreterParameterName,
new SymbolicDataAnalysisExpressionTreeBatchInterpreter()) { Hidden = true });
Parameters.Add(new FixedValueParameter(
EstimationLimitsParameterName,
new DoubleLimit(double.NegativeInfinity, double.PositiveInfinity)) { Hidden = true });
Parameters.Add(new ResultParameter(BestTrainingSolutionParameterName, "") { Hidden = true });
this.EvaluatorParameter.Hidden = true;
Operators.Add(new SymbolicDataAnalysisVariableFrequencyAnalyzer());
Operators.Add(new MinAverageMaxSymbolicExpressionTreeLengthAnalyzer());
Operators.Add(new SymbolicExpressionSymbolFrequencyAnalyzer());
RegisterEventHandlers();
StructureTemplate.ApplyLinearScaling = ApplyLinearScaling;
StructureTemplate.Template =
"(" +
"(210000 / (210000 + h)) * ((sigma_y * t * t) / (wR * Rt * t)) + " +
"PlasticHardening(_) - Elasticity(_)" +
")" +
" * C(_)";
}
public StructuredSymbolicRegressionSingleObjectiveProblem(StructuredSymbolicRegressionSingleObjectiveProblem original, Cloner cloner) : base(original, cloner) {
RegisterEventHandlers();
}
public override IDeepCloneable Clone(Cloner cloner) =>
new StructuredSymbolicRegressionSingleObjectiveProblem(this, cloner);
[StorableConstructor]
protected StructuredSymbolicRegressionSingleObjectiveProblem(StorableConstructorFlag _) : base(_) { }
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
if (!Parameters.ContainsKey(ApplyLinearScalingParameterName)) {
Parameters.Add(new FixedValueParameter(ApplyLinearScalingParameterName, new BoolValue(StructureTemplate.ApplyLinearScaling)));
}
if (!Parameters.ContainsKey(OptimizeParametersParameterName)) {
Parameters.Add(new FixedValueParameter(OptimizeParametersParameterName, new BoolValue(false)));
}
RegisterEventHandlers();
}
#endregion
private void RegisterEventHandlers() {
if (StructureTemplate != null) {
StructureTemplate.Changed += OnTemplateChanged;
}
ProblemDataParameter.ValueChanged += ProblemDataParameterValueChanged;
ApplyLinearScalingParameter.Value.ValueChanged += (o, e) => StructureTemplate.ApplyLinearScaling = ApplyLinearScaling;
OptimizeParametersParameter.Value.ValueChanged += (o, e) => {
if (OptimizeParameters) ApplyLinearScaling = true;
};
}
private void ProblemDataParameterValueChanged(object sender, EventArgs e) {
StructureTemplate.Reset();
// InfoBox for Reset?
}
private void OnTemplateChanged(object sender, EventArgs args) {
ApplyLinearScaling = StructureTemplate.ApplyLinearScaling;
SetupEncoding();
}
private void SetupEncoding() {
foreach (var e in Encoding.Encodings.ToArray())
Encoding.Remove(e);
foreach (var subFunction in StructureTemplate.SubFunctions) {
subFunction.SetupVariables(ProblemData.AllowedInputVariables);
// prevent the same encoding twice
if (Encoding.Encodings.Any(x => x.Name == subFunction.Name)) continue;
var encoding = new SymbolicExpressionTreeEncoding(
subFunction.Name,
subFunction.Grammar,
subFunction.MaximumSymbolicExpressionTreeLength,
subFunction.MaximumSymbolicExpressionTreeDepth);
Encoding.Add(encoding);
}
//set multi manipulator as default manipulator for all encoding parts
var manipulator = (IParameterizedItem)Encoding.Operators.OfType().FirstOrDefault();
if (manipulator != null) {
foreach (var param in manipulator.Parameters.OfType>()) {
var m = param.ValidValues.OfType().FirstOrDefault();
param.Value = m == null ? param.ValidValues.First() : m;
}
}
}
public override void Analyze(Individual[] individuals, double[] qualities, ResultCollection results, IRandom random) {
base.Analyze(individuals, qualities, results, random);
var best = GetBestIndividual(individuals, qualities).Item1;
if (!results.ContainsKey(BestTrainingSolutionParameter.ActualName)) {
results.Add(new Result(BestTrainingSolutionParameter.ActualName, typeof(SymbolicRegressionSolution)));
}
var tree = (ISymbolicExpressionTree)best[SymbolicExpressionTreeName];
var model = new SymbolicRegressionModel(ProblemData.TargetVariable, tree, Interpreter);
var solution = model.CreateRegressionSolution(ProblemData);
results[BestTrainingSolutionParameter.ActualName].Value = solution;
}
public override double Evaluate(Individual individual, IRandom random) {
var templateTree = StructureTemplate.Tree;
if (templateTree == null)
throw new ArgumentException("No structure template defined!");
var tree = BuildTree(templateTree, individual);
// NMSEConstraintsEvaluator sets linear scaling terms itself
if (StructureTemplate.ApplyLinearScaling && !(TreeEvaluator is NMSESingleObjectiveConstraintsEvaluator)) {
AdjustLinearScalingParams(ProblemData, tree, Interpreter);
}
individual[SymbolicExpressionTreeName] = tree;
return TreeEvaluator.Evaluate(
tree, ProblemData,
ProblemData.TrainingIndices,
Interpreter,
StructureTemplate.ApplyLinearScaling,
EstimationLimits.Lower,
EstimationLimits.Upper);
}
private static ISymbolicExpressionTree BuildTree(ISymbolicExpressionTree template, Individual individual) {
var resolvedTree = (ISymbolicExpressionTree)template.Clone();
// build main tree
foreach (var subFunctionTreeNode in resolvedTree.IterateNodesPrefix().OfType()) {
var subFunctionTree = individual.SymbolicExpressionTree(subFunctionTreeNode.Name);
// extract function tree
var subTree = subFunctionTree.Root.GetSubtree(0) // StartSymbol
.GetSubtree(0); // First Symbol
subTree = (ISymbolicExpressionTreeNode)subTree.Clone();
subFunctionTreeNode.AddSubtree(subTree);
}
return resolvedTree;
}
private static void AdjustLinearScalingParams(IRegressionProblemData problemData, ISymbolicExpressionTree tree, ISymbolicDataAnalysisExpressionTreeInterpreter interpreter) {
var offsetNode = tree.Root.GetSubtree(0).GetSubtree(0);
var scalingNode = offsetNode.Subtrees.Where(x => !(x is NumberTreeNode)).First();
var offsetNumberNode = (NumberTreeNode)offsetNode.Subtrees.Where(x => x is NumberTreeNode).First();
var scalingNumberNode = (NumberTreeNode)scalingNode.Subtrees.Where(x => x is NumberTreeNode).First();
var estimatedValues = interpreter.GetSymbolicExpressionTreeValues(tree, problemData.Dataset, problemData.TrainingIndices);
var targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices);
OnlineLinearScalingParameterCalculator.Calculate(estimatedValues, targetValues, out double a, out double b, out OnlineCalculatorError error);
if (error == OnlineCalculatorError.None) {
offsetNumberNode.Value = a;
scalingNumberNode.Value = b;
}
}
public void Load(IRegressionProblemData data) {
ProblemData = data;
}
}
}