Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3138_Shape_Constraints_Transformations/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SingleObjective/Evaluators/NMSESingleObjectiveConstraintsEvaluator.cs @ 18213

Last change on this file since 18213 was 18213, checked in by dpiringe, 2 years ago

#3138

  • reimplemented extended constraints with an interface and an implementation, because of project reference troubles
  • moved the extended shape constraints into IShapeConstrainedRegressionProblemData
  • added thresholds for shape constraints -> the error is linear between 0 and 1, error caps at 1 when error >= (intervalbound + thresholdbound)
    • adapted ShapeConstraint and ShapeConstraintsParser to identify and store thresholds
  • adapted IntervalUtil to work with thresholds
  • adapted NMSESingleObjectiveConstraintsEvaluator and ShapeConstraintsAnalyzer to work with extended constraints
  • added a new chart in ShapeConstraintsAnalyzer to show the average constraint violation
File size: 14.3 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections;
24using System.Collections.Generic;
25using System.Linq;
26using HEAL.Attic;
27using HeuristicLab.Common;
28using HeuristicLab.Core;
29using HeuristicLab.Data;
30using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
31using HeuristicLab.Parameters;
32using HeuristicLab.Random;
33
34namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Regression {
35  [Item("NMSE Evaluator with shape-constraints (single-objective)", "Calculates NMSE of a symbolic regression solution and checks constraints. The fitness is a combination of NMSE and constraint violations.")]
36  [StorableType("27473973-DD8D-4375-997D-942E2280AE8E")]
37  public class NMSESingleObjectiveConstraintsEvaluator : SymbolicRegressionSingleObjectiveEvaluator {
38    #region Parameter/Properties
39
40    private const string OptimizeParametersParameterName = "OptimizeParameters";
41    private const string ParameterOptimizationIterationsParameterName = "ParameterOptimizationIterations";
42    private const string UseSoftConstraintsParameterName = "UseSoftConstraintsEvaluation";
43    private const string BoundsEstimatorParameterName = "BoundsEstimator";
44    private const string PenaltyFactorParameterName = "PenaltyFactor";
45
46
47    public IFixedValueParameter<BoolValue> OptimizerParametersParameter =>
48      (IFixedValueParameter<BoolValue>)Parameters[OptimizeParametersParameterName];
49
50    public IFixedValueParameter<IntValue> ParameterOptimizationIterationsParameter =>
51      (IFixedValueParameter<IntValue>)Parameters[ParameterOptimizationIterationsParameterName];
52
53    public IFixedValueParameter<BoolValue> UseSoftConstraintsParameter =>
54      (IFixedValueParameter<BoolValue>)Parameters[UseSoftConstraintsParameterName];
55
56    public IValueParameter<IBoundsEstimator> BoundsEstimatorParameter =>
57      (IValueParameter<IBoundsEstimator>)Parameters[BoundsEstimatorParameterName];
58    public IFixedValueParameter<DoubleValue> PenaltyFactorParameter =>
59      (IFixedValueParameter<DoubleValue>)Parameters[PenaltyFactorParameterName];
60
61
62
63    public bool OptimizeParameters {
64      get => OptimizerParametersParameter.Value.Value;
65      set => OptimizerParametersParameter.Value.Value = value;
66    }
67
68    public int ParameterOptimizationIterations {
69      get => ParameterOptimizationIterationsParameter.Value.Value;
70      set => ParameterOptimizationIterationsParameter.Value.Value = value;
71    }
72
73    public bool UseSoftConstraints {
74      get => UseSoftConstraintsParameter.Value.Value;
75      set => UseSoftConstraintsParameter.Value.Value = value;
76    }
77
78    public IBoundsEstimator BoundsEstimator {
79      get => BoundsEstimatorParameter.Value;
80      set => BoundsEstimatorParameter.Value = value;
81    }
82
83    public double PenalityFactor {
84      get => PenaltyFactorParameter.Value.Value;
85      set => PenaltyFactorParameter.Value.Value = value;
86    }
87
88    public override bool Maximization => false; // NMSE is minimized
89
90    #endregion
91
92    #region Constructors/Cloning
93
94    [StorableConstructor]
95    protected NMSESingleObjectiveConstraintsEvaluator(StorableConstructorFlag _) : base(_) { }
96
97    protected NMSESingleObjectiveConstraintsEvaluator(
98      NMSESingleObjectiveConstraintsEvaluator original, Cloner cloner) : base(original, cloner) { }
99
100    public NMSESingleObjectiveConstraintsEvaluator() {
101      Parameters.Add(new FixedValueParameter<BoolValue>(OptimizeParametersParameterName,
102        "Define whether optimization of parameters is active or not (default: false).", new BoolValue(false)));
103      Parameters.Add(new FixedValueParameter<IntValue>(ParameterOptimizationIterationsParameterName,
104        "Define how many parameter optimization steps should be performed (default: 10).", new IntValue(10)));
105      Parameters.Add(new FixedValueParameter<BoolValue>(UseSoftConstraintsParameterName,
106        "Define whether the constraints are penalized by soft or hard constraints (default: false).", new BoolValue(false)));
107      Parameters.Add(new ValueParameter<IBoundsEstimator>(BoundsEstimatorParameterName,
108        "The estimator which is used to estimate output ranges of models (default: interval arithmetic).", new IntervalArithBoundsEstimator()));
109      Parameters.Add(new FixedValueParameter<DoubleValue>(PenaltyFactorParameterName,
110        "Punishment factor for constraint violations for soft constraint handling (fitness = NMSE + penaltyFactor * avg(violations)) (default: 1.0)", new DoubleValue(1.0)));
111    }
112
113    [StorableHook(HookType.AfterDeserialization)]
114    private void AfterDeserialization() { }
115
116    public override IDeepCloneable Clone(Cloner cloner) {
117      return new NMSESingleObjectiveConstraintsEvaluator(this, cloner);
118    }
119
120    #endregion
121
122    public override IOperation InstrumentedApply() {
123      var rows = GenerateRowsToEvaluate();
124      var tree = SymbolicExpressionTreeParameter.ActualValue;
125      var problemData = ProblemDataParameter.ActualValue;
126      var interpreter = SymbolicDataAnalysisTreeInterpreterParameter.ActualValue;
127      var estimationLimits = EstimationLimitsParameter.ActualValue;
128      var applyLinearScaling = ApplyLinearScalingParameter.ActualValue.Value;
129      var random = RandomParameter.ActualValue;
130
131      if (OptimizeParameters) {
132        SymbolicRegressionParameterOptimizationEvaluator.OptimizeParameters(interpreter, tree, problemData, rows,
133          false, ParameterOptimizationIterations, true,
134          estimationLimits.Lower, estimationLimits.Upper);
135      } else {
136        if (applyLinearScaling) {
137          var rootNode = new ProgramRootSymbol().CreateTreeNode();
138          var startNode = new StartSymbol().CreateTreeNode();
139          var offset = tree.Root.GetSubtree(0) //Start
140                                .GetSubtree(0); //Offset
141          var scaling = offset.GetSubtree(0);
142
143          //Check if tree contains offset and scaling nodes
144          if (!(offset.Symbol is Addition) || !(scaling.Symbol is Multiplication))
145            throw new ArgumentException($"{ItemName} can only be used with LinearScalingGrammar.");
146
147          var t = (ISymbolicExpressionTreeNode)scaling.GetSubtree(0).Clone();
148          rootNode.AddSubtree(startNode);
149          startNode.AddSubtree(t);
150          var newTree = new SymbolicExpressionTree(rootNode);
151
152          //calculate alpha and beta for scaling
153          var estimatedValues = interpreter.GetSymbolicExpressionTreeValues(newTree, problemData.Dataset, rows);
154
155          var targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows);
156          OnlineLinearScalingParameterCalculator.Calculate(estimatedValues, targetValues, out var alpha, out var beta,
157            out var errorState);
158
159          if (errorState == OnlineCalculatorError.None) {
160            //Set alpha and beta to the scaling nodes from ia grammar
161            var offsetParameter = offset.GetSubtree(1) as NumberTreeNode;
162            offsetParameter.Value = alpha;
163            var scalingParameter = scaling.GetSubtree(1) as NumberTreeNode;
164            scalingParameter.Value = beta;
165          }
166        } // else: alpha and beta are evolved
167      }
168
169      var quality = Calculate(interpreter, tree, estimationLimits.Lower, estimationLimits.Upper, problemData, rows,
170        BoundsEstimator, random, UseSoftConstraints, PenalityFactor);
171      QualityParameter.ActualValue = new DoubleValue(quality);
172
173      return base.InstrumentedApply();
174    }
175
176    public static double Calculate(
177      ISymbolicDataAnalysisExpressionTreeInterpreter interpreter,
178      ISymbolicExpressionTree tree,
179      double lowerEstimationLimit, double upperEstimationLimit,
180      IRegressionProblemData problemData, IEnumerable<int> rows,
181      IBoundsEstimator estimator, IRandom random,
182      bool useSoftConstraints = false, double penaltyFactor = 1.0) {
183
184      var trainingEstimatedValues = interpreter.GetSymbolicExpressionTreeValues(tree, problemData.Dataset, rows);
185      var targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows);
186
187      var trainingBoundedEstimatedValues = trainingEstimatedValues.LimitToRange(lowerEstimationLimit, upperEstimationLimit);
188      var nmse = OnlineNormalizedMeanSquaredErrorCalculator.Calculate(targetValues, trainingBoundedEstimatedValues,
189        out var errorState);
190
191      if (errorState != OnlineCalculatorError.None)
192        return double.MaxValue;
193
194      var violations = Enumerable.Empty<double>();
195      if (problemData is ShapeConstrainedRegressionProblemData scProbData) {
196        violations = CalculateShapeConstraintsViolations(scProbData, tree, interpreter, estimator, random).Select(x => x.Item2);
197      }
198
199      if (violations.Any(x => double.IsNaN(x) || double.IsInfinity(x)))
200        return double.MaxValue;
201
202      if (useSoftConstraints) {
203        if (penaltyFactor < 0.0)
204          throw new ArgumentException("The parameter has to be >= 0.0.", nameof(penaltyFactor));
205
206        return nmse + penaltyFactor * violations.Average();
207      }
208      return violations.Any(x => x > 0.0) ? 1.0 : nmse;
209    }
210
211    public static IEnumerable<Tuple<ShapeConstraint, double>> CalculateShapeConstraintsViolations(
212      IShapeConstrainedRegressionProblemData problemData, ISymbolicExpressionTree tree,
213      ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IBoundsEstimator estimator,
214      IRandom random) {
215      IList<Tuple<ShapeConstraint, double>> violations = new List<Tuple<ShapeConstraint, double>>();
216     
217      var baseConstraints = problemData.ShapeConstraints.EnabledConstraints;
218      var intervalCollection = problemData.VariableRanges;
219      var extendedShapeConstraints = problemData.CheckedExtendedConstraints;
220      var allEstimatedValues = interpreter.GetSymbolicExpressionTreeValues(tree, problemData.Dataset, problemData.AllIndices);
221
222      foreach (var constraint in baseConstraints)
223        violations.Add(Tuple.Create(constraint, IntervalUtil.GetConstraintViolation(constraint, estimator, intervalCollection, tree) * constraint.Weight));
224
225      IDictionary<string, IList> dict = new Dictionary<string, IList>();
226      foreach (var varName in problemData.Dataset.VariableNames) {
227        if (varName != problemData.TargetVariable)
228          dict.Add(varName, problemData.Dataset.GetDoubleValues(varName).ToList());
229        else dict.Add(varName, allEstimatedValues.ToList());
230      }
231      var tmpDataset = new Dataset(dict.Keys, dict.Values);
232
233      foreach (var extendedConstraint in extendedShapeConstraints) {
234        var enabledConstraints = extendedConstraint.ShapeConstraints.EnabledConstraints;
235        if (enabledConstraints.Any()) {
236          var extendedConstraintExprValues = interpreter.GetSymbolicExpressionTreeValues(extendedConstraint.Tree, tmpDataset, problemData.AllIndices);
237          var extendedConstraintExprInterval = new Interval(extendedConstraintExprValues.Min(), extendedConstraintExprValues.Max());
238
239          foreach (var constraint in enabledConstraints) {
240            if (constraint.Regions.Count > 0) {
241              // adapt dataset
242              foreach (var kvp in constraint.Regions.GetReadonlyDictionary()) {
243                var lb = double.IsNegativeInfinity(kvp.Value.LowerBound) ? double.MinValue : kvp.Value.LowerBound;
244                var ub = double.IsPositiveInfinity(kvp.Value.UpperBound) ? double.MaxValue : kvp.Value.UpperBound;
245
246                var vals = Enumerable.Range(0, dict[kvp.Key].Count - 2)
247                                     .Select(x => UniformDistributedRandom.NextDouble(random, lb, ub))
248                                     .ToList();
249                vals.Add(lb);
250                vals.Add(ub);
251                vals.Sort();
252                dict[kvp.Key] = vals;
253              }
254              // calc again with new regions
255              tmpDataset = new Dataset(dict.Keys, dict.Values);
256              // calc target again
257              allEstimatedValues = interpreter.GetSymbolicExpressionTreeValues(tree, tmpDataset, problemData.AllIndices);
258              dict[problemData.TargetVariable] = allEstimatedValues.ToList();
259              tmpDataset = new Dataset(dict.Keys, dict.Values);
260              extendedConstraintExprValues = interpreter.GetSymbolicExpressionTreeValues(extendedConstraint.Tree, tmpDataset, problemData.AllIndices);
261              extendedConstraintExprInterval = new Interval(extendedConstraintExprValues.Min(), extendedConstraintExprValues.Max());
262            }
263            violations.Add(Tuple.Create(constraint, IntervalUtil.GetIntervalError(constraint.Interval, extendedConstraintExprInterval, constraint.Threshold) * constraint.Weight));
264          }
265        }
266      }
267      return violations;
268    }
269
270    public override double Evaluate(
271      IExecutionContext context, ISymbolicExpressionTree tree, IRegressionProblemData problemData,
272      IEnumerable<int> rows) {
273      SymbolicDataAnalysisTreeInterpreterParameter.ExecutionContext = context;
274      EstimationLimitsParameter.ExecutionContext = context;
275      ApplyLinearScalingParameter.ExecutionContext = context;
276      RandomParameter.ExecutionContext = context;
277
278      var nmse = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, tree,
279        EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper,
280        problemData, rows, BoundsEstimator, RandomParameter.Value, UseSoftConstraints, PenalityFactor);
281
282      SymbolicDataAnalysisTreeInterpreterParameter.ExecutionContext = null;
283      EstimationLimitsParameter.ExecutionContext = null;
284      ApplyLinearScalingParameter.ExecutionContext = null;
285      RandomParameter.ExecutionContext = null;
286
287      return nmse;
288    }
289  }
290}
Note: See TracBrowser for help on using the repository browser.