using System; using System.Collections.Generic; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; namespace HeuristicLab.Problems.DataAnalysis.Symbolic { public sealed class IntervalEvaluator : Interpreter { [ThreadStatic] private IDictionary intervals; public Interval Evaluate(ISymbolicExpressionTree tree, IDictionary intervals) { this.intervals = intervals; var code = Compile(tree); Evaluate(code); if (code[0].value.LowerBound.Value.Value > code[0].value.UpperBound.Value.Value) throw new InvalidProgramException($"lower: {code[0].value.LowerBound.Value.Value} > upper: {code[0].value.UpperBound.Value.Value}"); return new Interval(code[0].value.LowerBound.Value.Value, code[0].value.UpperBound.Value.Value); } public Interval Evaluate(ISymbolicExpressionTree tree, IDictionary intervals, ISymbolicExpressionTreeNode[] paramNodes, out double[] lowerGradient, out double[] upperGradient) { this.intervals = intervals; var code = Compile(tree); Evaluate(code); lowerGradient = new double[paramNodes.Length]; upperGradient = new double[paramNodes.Length]; var l = code[0].value.LowerBound; var u = code[0].value.UpperBound; for (int i = 0; i < paramNodes.Length; ++i) { if (paramNodes[i] == null) continue; if (l.Gradient.Elements.TryGetValue(paramNodes[i], out AlgebraicDouble value)) lowerGradient[i] = value; if (u.Gradient.Elements.TryGetValue(paramNodes[i], out value)) upperGradient[i] = value; } return new Interval(code[0].value.LowerBound.Value.Value, code[0].value.UpperBound.Value.Value); } protected override void InitializeInternalInstruction(ref Instruction instruction, ISymbolicExpressionTreeNode node) { instruction.value = new AlgebraicInterval(0, 0); } protected override void InitializeTerminalInstruction(ref Instruction instruction, ConstantTreeNode constant) { instruction.dblVal = constant.Value; instruction.value = new AlgebraicInterval( new MultivariateDual(instruction.dblVal, constant, 1.0), new MultivariateDual(instruction.dblVal, constant, 1.0) // use node as key ); } protected override void InitializeTerminalInstruction(ref Instruction instruction, VariableTreeNode variable) { instruction.dblVal = variable.Weight; var v1 = instruction.dblVal * intervals[variable.VariableName].LowerBound; var v2 = instruction.dblVal * intervals[variable.VariableName].UpperBound; var lower = Math.Min(v1, v2); var upper = Math.Max(v1, v2); // we assume that the for variable nodes ( v(x,w) = w * x ) the gradient is returned for parameter w instruction.value = new AlgebraicInterval( low: new MultivariateDual(v: lower, key: variable, dv: intervals[variable.VariableName].LowerBound), high: new MultivariateDual(v: upper, key: variable, dv: intervals[variable.VariableName].UpperBound) ); } protected override void LoadVariable(Instruction a) { // nothing to do } } }