#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 HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; namespace HeuristicLab.Problems.DataAnalysis.Symbolic { public static class IntervalUtil { private static void Prepare( ShapeConstraint constraint, IBoundsEstimator estimator, IntervalCollection variableRanges, ISymbolicExpressionTree tree, out ISymbolicExpressionTree preparedTree, out IntervalCollection preparedRanges) { var varRanges = variableRanges.GetReadonlyDictionary(); if (!string.IsNullOrEmpty(constraint.Variable) && !varRanges.ContainsKey(constraint.Variable)) { throw new ArgumentException( $"No variable range found for variable {constraint.Variable} used in the constraints.", nameof(constraint)); } // Create new variable ranges for defined regions var regionRanges = new IntervalCollection(); foreach (var kvp in varRanges) { if (constraint.Regions.GetReadonlyDictionary().TryGetValue(kvp.Key, out var val)) { regionRanges.AddInterval(kvp.Key, val); } else { regionRanges.AddInterval(kvp.Key, kvp.Value); } } if (constraint.IsDerivative) { for (var i = 0; i < constraint.NumberOfDerivations; ++i) { if (!estimator.IsCompatible(tree) || !DerivativeCalculator.IsCompatible(tree)) throw new ArgumentException("The tree contains an unsupported symbol."); tree = DerivativeCalculator.Derive(tree, constraint.Variable); } } preparedTree = tree; preparedRanges = regionRanges; } public static IEnumerable GetConstraintViolations( IEnumerable constraints, IBoundsEstimator estimator, IntervalCollection intervalCollection, ISymbolicExpressionTree solution) { return constraints.Select(constraint => GetConstraintViolation(constraint, estimator, intervalCollection, solution)).ToList(); } public static double GetConstraintViolation( ShapeConstraint constraint, IBoundsEstimator estimator, IntervalCollection variableRanges, ISymbolicExpressionTree tree) { Prepare(constraint, estimator, variableRanges, tree, out ISymbolicExpressionTree preparedTree, out IntervalCollection preparedRanges); var bounds = estimator.GetModelBound(preparedTree, preparedRanges); return GetIntervalError(constraint.Interval, bounds, constraint.Threshold); //return estimator.GetConstraintViolation(preparedTree, preparedRanges, constraint); } public static double GetIntervalError(Interval target, Interval estimation, Interval? threshold = null) { if(threshold == null) threshold = new Interval(0, 0); var error = 0d; if (!target.Contains(estimation.LowerBound)) { var lbError = Math.Abs(estimation.LowerBound - target.LowerBound); error += CalcBoundViolation(lbError, threshold.LowerBound); } if (!target.Contains(estimation.UpperBound)) { var ubError = Math.Abs(estimation.UpperBound - target.UpperBound); error += CalcBoundViolation(ubError, threshold.UpperBound); } return error == 0 ? 0 : error / 2.0; } private static double CalcBoundViolation(double error, double threshold) { threshold = Math.Abs(threshold); if (double.IsNaN(error)) return 1.0; if (double.IsInfinity(error) && !double.IsInfinity(threshold)) return 1.0; if (double.IsInfinity(threshold)) return 0; if (error <= 0) return 0; if (error > threshold) return 1.0; if (threshold > 0) return Math.Min(1.0, error / threshold); return 1.0; } } }