#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 HEAL.Attic; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Regression.MultiObjective { [Item("Multi Soft Constraints Evaluator", "Calculates the Person R² and the constraints violations of a symbolic regression solution.")] [StorableType("8E9D76B7-ED9C-43E7-9898-01FBD3633880")] public class SymbolicRegressionMultiObjectiveMultiSoftConstraintEvaluator : SymbolicRegressionMultiObjectiveSplittingEvaluator { #region Constructors public SymbolicRegressionMultiObjectiveMultiSoftConstraintEvaluator() { } [StorableConstructor] protected SymbolicRegressionMultiObjectiveMultiSoftConstraintEvaluator(StorableConstructorFlag _) : base(_) { } protected SymbolicRegressionMultiObjectiveMultiSoftConstraintEvaluator( SymbolicRegressionMultiObjectiveMultiSoftConstraintEvaluator original, Cloner cloner) : base(original, cloner) { } #endregion public override IDeepCloneable Clone(Cloner cloner) { return new SymbolicRegressionMultiObjectiveMultiSoftConstraintEvaluator(this, cloner); } public override IOperation InstrumentedApply() { var rows = GenerateRowsToEvaluate(); var solution = SymbolicExpressionTreeParameter.ActualValue; var problemData = ProblemDataParameter.ActualValue; var interpreter = SymbolicDataAnalysisTreeInterpreterParameter.ActualValue; var estimationLimits = EstimationLimitsParameter.ActualValue; var minIntervalWidth = MinSplittingWidth; var maxIntervalSplitDepth = MaxSplittingDepth; var applyLinearScaling = ApplyLinearScalingParameter.ActualValue.Value; if (applyLinearScaling) { //Check for interval arithmetic grammar //remove scaling nodes for linear scaling evaluation var rootNode = new ProgramRootSymbol().CreateTreeNode(); var startNode = new StartSymbol().CreateTreeNode(); SymbolicExpressionTree newTree = null; foreach (var node in solution.IterateNodesPrefix()) if (node.Symbol.Name == "Scaling") { for (var i = 0; i < node.SubtreeCount; ++i) startNode.AddSubtree(node.GetSubtree(i)); rootNode.AddSubtree(startNode); newTree = new SymbolicExpressionTree(rootNode); break; } //calculate alpha and beta for scaling var estimatedValues = interpreter.GetSymbolicExpressionTreeValues(newTree, problemData.Dataset, rows); var targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows); OnlineLinearScalingParameterCalculator.Calculate(estimatedValues, targetValues, out var alpha, out var beta, out var errorState); //Set alpha and beta to the scaling nodes from ia grammar foreach (var node in solution.IterateNodesPrefix()) if (node.Symbol.Name == "Offset") { node.RemoveSubtree(1); var alphaNode = new ConstantTreeNode(new Constant()) {Value = alpha}; node.AddSubtree(alphaNode); } else if (node.Symbol.Name == "Scaling") { node.RemoveSubtree(1); var betaNode = new ConstantTreeNode(new Constant()) {Value = beta}; node.AddSubtree(betaNode); } } if (UseConstantOptimization) SymbolicRegressionConstantOptimizationEvaluator.OptimizeConstants(interpreter, solution, problemData, rows, false, ConstantOptimizationIterations, ConstantOptimizationUpdateVariableWeights, estimationLimits.Lower, estimationLimits.Upper); var qualities = Calculate(interpreter, solution, estimationLimits.Lower, estimationLimits.Upper, problemData, rows, DecimalPlaces, minIntervalWidth, maxIntervalSplitDepth); QualitiesParameter.ActualValue = new DoubleArray(qualities); return base.InstrumentedApply(); } public override double[] Evaluate(IExecutionContext context, ISymbolicExpressionTree tree, IRegressionProblemData problemData, IEnumerable rows) { SymbolicDataAnalysisTreeInterpreterParameter.ExecutionContext = context; EstimationLimitsParameter.ExecutionContext = context; ApplyLinearScalingParameter.ExecutionContext = context; var quality = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, tree, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, problemData, rows, DecimalPlaces, MinSplittingWidth, MaxSplittingDepth); SymbolicDataAnalysisTreeInterpreterParameter.ExecutionContext = null; EstimationLimitsParameter.ExecutionContext = null; ApplyLinearScalingParameter.ExecutionContext = null; return quality; } public static double[] Calculate(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, ISymbolicExpressionTree solution, double lowerEstimationLimit, double upperEstimationLimit, IRegressionProblemData problemData, IEnumerable rows, int decimalPlaces, double minIntervalSplitWidth, int maxIntervalSplitDepth) { OnlineCalculatorError errorState; var estimatedValues = interpreter.GetSymbolicExpressionTreeValues(solution, problemData.Dataset, rows); var targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows); double nmse; var boundedEstimatedValues = estimatedValues.LimitToRange(lowerEstimationLimit, upperEstimationLimit); nmse = OnlineNormalizedMeanSquaredErrorCalculator.Calculate(targetValues, boundedEstimatedValues, out errorState); if (errorState != OnlineCalculatorError.None) nmse = 1.0; if (nmse > 1) nmse = 1.0; var constraints = problemData.IntervalConstraints.Constraints.Where(c => c.Enabled); var variableRanges = problemData.VariableRanges.GetReadonlyDictionary(); var objectives = new List {nmse}; var intervalInterpreter = new IntervalInterpreter(); /*{MinIntervalSplitWidth = minIntervalSplitWidth, MaxIntervalSplitDepth = maxIntervalSplitDetph};*/ var constraintObjectives = new List(); foreach (var c in constraints) { var penalty = ConstraintExceeded(c, intervalInterpreter, variableRanges, solution /*, problemData.IntervalSplitting*/); var maxP = 0.1; if (double.IsNaN(penalty) || double.IsInfinity(penalty) || penalty > maxP) penalty = maxP; constraintObjectives.Add(penalty); } objectives.AddRange(constraintObjectives); return objectives.ToArray(); } public static double ConstraintExceeded(IntervalConstraint constraint, IntervalInterpreter intervalInterpreter, IReadOnlyDictionary variableRanges, ISymbolicExpressionTree solution /*, bool splitting*/) { if (constraint.Variable != null && !variableRanges.ContainsKey(constraint.Variable)) throw new ArgumentException( $"The given variable {constraint.Variable} in the constraint does not exists in the model.", nameof(IntervalConstraintsParser)); Interval resultInterval; if (!constraint.IsDerivative) { resultInterval = intervalInterpreter.GetSymbolicExpressionTreeInterval(solution, variableRanges /*, splitting:splitting*/); } else { var tree = solution; for (var i = 0; i < constraint.NumberOfDerivations; ++i) tree = DerivativeCalculator.Derive(tree, constraint.Variable); resultInterval = intervalInterpreter.GetSymbolicExpressionTreeInterval(tree, variableRanges /*, splitting: splitting*/); } //Calculate soft-constraints for intervals if (constraint.Interval.Contains(resultInterval)) return 0; var pLower = 0.0; var pUpper = 0.0; if (constraint.Interval.Contains(resultInterval.LowerBound)) pLower = 0; else pLower = constraint.Interval.LowerBound - resultInterval.LowerBound; if (constraint.Interval.Contains(resultInterval.UpperBound)) pUpper = 0; else pUpper = resultInterval.UpperBound - constraint.Interval.UpperBound; var penalty = Math.Abs(pLower) + Math.Abs(pUpper); return penalty; } /* * First objective is to maximize the Pearson R² value * All following objectives have to be minimized ==> Constraints */ public override IEnumerable Maximization { get { var objectives = new List {false}; //First NMSE ==> min objectives.AddRange(Enumerable.Repeat(false, 2)); //Constraints ==> min return objectives; } } } }