using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using HEAL.Attic; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; using HeuristicLab.Parameters; using HeuristicLab.Problems.DataAnalysis.Symbolic.Regression; namespace HeuristicLab.Problems.DataAnalysis.Symbolic { [Item("Constraints Evaluator", "Calculates NMSE of a symbolic regression solution with respect to constraints.")] [StorableType("27473973-DD8D-4375-997D-942E2280AE8E")] class SymbolicRegressionSingleObjectiveConstraintEvaluator : SymbolicRegressionSingleObjectiveEvaluator { #region Parameter/Properties private const string UseConstantOptimizationParameterName = "Use Constant Optimization"; private const string ConstantOptimizationIterationsParameterName = "Constant Optimization Iterations"; private const string UseSoftConstraintsParameterName = "Use Soft Constraints Evaluation"; private const string PenaltyMultiplierParameterName = "Constraints Penalty Multiplier"; public IFixedValueParameter UseConstantOptimizationParameter => (IFixedValueParameter) Parameters[UseConstantOptimizationParameterName]; public IFixedValueParameter ConstantOptimizationIterationsParameter => (IFixedValueParameter) Parameters[ConstantOptimizationIterationsParameterName]; public IFixedValueParameter UseSoftConstraintsParameter => (IFixedValueParameter) Parameters[UseSoftConstraintsParameterName]; public IFixedValueParameter PenaltyMultiplierParameter => (IFixedValueParameter) Parameters[PenaltyMultiplierParameterName]; public bool UseConstantOptimization { get => UseConstantOptimizationParameter.Value.Value; set => UseConstantOptimizationParameter.Value.Value = value; } public int ConstantOptimizationIterations { get => ConstantOptimizationIterationsParameter.Value.Value; set => ConstantOptimizationIterationsParameter.Value.Value = value; } public bool UseSoftConstraints { get => UseSoftConstraintsParameter.Value.Value; set => UseSoftConstraintsParameter.Value.Value = value; } public double PenaltyMultiplier { get => PenaltyMultiplierParameter.Value.Value; set => PenaltyMultiplierParameter.Value.Value = value; } //Use false for maximization, because we try to minimize the NMSE public override bool Maximization => false; #endregion #region Constructors/Cloning [StorableConstructor] protected SymbolicRegressionSingleObjectiveConstraintEvaluator(StorableConstructorFlag _) : base(_) { } protected SymbolicRegressionSingleObjectiveConstraintEvaluator( SymbolicRegressionSingleObjectiveConstraintEvaluator original, Cloner cloner) : base(original, cloner) { } public SymbolicRegressionSingleObjectiveConstraintEvaluator() { Parameters.Add(new FixedValueParameter(UseConstantOptimizationParameterName, "Define whether constant optimization is active or not.", new BoolValue(false))); Parameters.Add(new FixedValueParameter(ConstantOptimizationIterationsParameterName, "Define how many constant optimization steps should be performed.", new IntValue(10))); Parameters.Add(new FixedValueParameter(UseSoftConstraintsParameterName, "Define whether the constraints are penalized by soft or hard constraints.", new BoolValue(false))); Parameters.Add(new FixedValueParameter(PenaltyMultiplierParameterName, "Specify how hard constraints violations should be punished", new DoubleValue(1.0))); } [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { if (!Parameters.ContainsKey(UseConstantOptimizationParameterName)) { Parameters.Add(new FixedValueParameter(UseConstantOptimizationParameterName, "Define whether constant optimization is active or not.", new BoolValue(false))); } if (!Parameters.ContainsKey(ConstantOptimizationIterationsParameterName)) { Parameters.Add(new FixedValueParameter(ConstantOptimizationIterationsParameterName, "Define how many constant optimization steps should be performed.", new IntValue(10))); } if (!Parameters.ContainsKey(UseSoftConstraintsParameterName)) { Parameters.Add(new FixedValueParameter(UseSoftConstraintsParameterName, "Define whether the constraints are penalized by soft or hard constraints.", new BoolValue(false))); } if (!Parameters.ContainsKey(PenaltyMultiplierParameterName)) { Parameters.Add(new FixedValueParameter(PenaltyMultiplierParameterName, "Specify how hard constraints violations should be punished", new DoubleValue(1.0))); } } public override IDeepCloneable Clone(Cloner cloner) { return new SymbolicRegressionSingleObjectiveConstraintEvaluator(this, cloner); } #endregion public override IOperation InstrumentedApply() { var rows = GenerateRowsToEvaluate(); var solution = SymbolicExpressionTreeParameter.ActualValue; var problemData = ProblemDataParameter.ActualValue; var interpreter = SymbolicDataAnalysisTreeInterpreterParameter.ActualValue; var estimationLimits = EstimationLimitsParameter.ActualValue; 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, true, estimationLimits.Lower, estimationLimits.Upper); } var quality = Calculate(interpreter, solution, estimationLimits.Lower, estimationLimits.Upper, problemData, rows, UseSoftConstraints, PenaltyMultiplier); QualityParameter.ActualValue = new DoubleValue(quality); return base.InstrumentedApply(); } public static double Calculate( ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, ISymbolicExpressionTree solution, double lowerEstimationLimit, double upperEstimationLimit, IRegressionProblemData problemData, IEnumerable rows, bool useSoftConstraints, double penaltyMultiplier) { var estimatedValues = interpreter.GetSymbolicExpressionTreeValues(solution, problemData.Dataset, rows); var targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows); var constraints = problemData.IntervalConstraints.EnabledConstraints; var variableRanges = problemData.VariableRanges.GetReadonlyDictionary(); var boundedEstimatedValues = estimatedValues.LimitToRange(lowerEstimationLimit, upperEstimationLimit); var nmse = OnlineNormalizedMeanSquaredErrorCalculator.Calculate(targetValues, boundedEstimatedValues, out var errorState); if (!SymbolicRegressionConstraintAnalyzer.ConstraintsSatisfied(constraints, variableRanges, solution, out var error)) { if (useSoftConstraints) { if (double.IsNaN(error) || double.IsInfinity(error)) { nmse += penaltyMultiplier * 1.0; } else { nmse += penaltyMultiplier * error; } nmse = Math.Min(1.0, nmse); } else { nmse = 1.0; } } if (errorState != OnlineCalculatorError.None) { nmse = 1.0; } return nmse; } public override double Evaluate( IExecutionContext context, ISymbolicExpressionTree tree, IRegressionProblemData problemData, IEnumerable rows) { SymbolicDataAnalysisTreeInterpreterParameter.ExecutionContext = context; EstimationLimitsParameter.ExecutionContext = context; ApplyLinearScalingParameter.ExecutionContext = context; var nmse = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, tree, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, problemData, rows, UseSoftConstraints, PenaltyMultiplier); SymbolicDataAnalysisTreeInterpreterParameter.ExecutionContext = null; EstimationLimitsParameter.ExecutionContext = null; ApplyLinearScalingParameter.ExecutionContext = null; return nmse; } } }