#region License Information /* HeuristicLab * Copyright (C) 2002-2018 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.ConstantsOptimization { public class LMConstantsOptimizer { private LMConstantsOptimizer() { } /// /// Method to determine whether the numeric constants of the tree can be optimized. This depends primarily on the symbols occuring in the tree. /// /// The tree that should be analyzed /// A flag indicating whether the numeric constants of the tree can be optimized public static bool CanOptimizeConstants(ISymbolicExpressionTree tree) { return AutoDiffConverter.IsCompatible(tree); } /// /// Optimizes the numeric constants in a symbolic expression tree in place. /// /// The tree for which the constants should be optimized /// The dataset containing the data. /// The target variable name. /// The rows for which the data should be extracted. /// A flag to determine whether linear scaling should be applied during the optimization /// The maximum number of iterations of the Levenberg-Marquard algorithm. /// public static double OptimizeConstants(ISymbolicExpressionTree tree, IDataset dataset, string targetVariable, IEnumerable rows, bool applyLinearScaling, int maxIterations = 10) { if (tree == null) throw new ArgumentNullException("tree"); if (dataset == null) throw new ArgumentNullException("dataset"); if (!dataset.ContainsVariable(targetVariable)) throw new ArgumentException("The dataset does not contain the provided target variable."); var allVariables = Util.ExtractVariables(tree); var numericNodes = Util.ExtractNumericNodes(tree); AutoDiff.IParametricCompiledTerm term; if (!AutoDiffConverter.TryConvertToAutoDiff(tree, applyLinearScaling, numericNodes, allVariables, out term)) throw new NotSupportedException("Could not convert symbolic expression tree to an AutoDiff term due to not supported symbols used in the tree."); //Variables of the symbolic expression tree correspond to parameters in the term //Hence if no parameters are present no variables occur in the tree and the R² = 0 if (term.Parameters.Count == 0) return 0.0; var initialConstants = Util.ExtractConstants(numericNodes, applyLinearScaling); double[] constants; double[,] x = Util.ExtractData(dataset, allVariables, rows); double[] y = dataset.GetDoubleValues(targetVariable, rows).ToArray(); var result = OptimizeConstants(term, initialConstants, x, y, maxIterations, out constants); if (result > 0.0 && constants.Length != 0) Util.UpdateConstants(numericNodes, constants); return result; } /// /// Optimizes the numeric coefficents of an AutoDiff Term using the Levenberg-Marquard algorithm. /// /// The AutoDiff term for which the numeric coefficients should be optimized. /// The starting values for the numeric coefficients. /// The input data for the optimization. /// The target values for the optimization. /// The maximum number of iterations of the Levenberg-Marquard /// The opitmized constants. /// An optional callback for detailed analysis that is called in each algorithm iteration. /// The R² of the term evaluated on the input data x and the target data y using the optimized constants public static double OptimizeConstants(AutoDiff.IParametricCompiledTerm term, double[] initialConstants, double[,] x, double[] y, int maxIterations, out double[] constants, Action LM_IterationCallback = null) { if (term.Parameters.Count == 0) { constants = new double[0]; return 0.0; } var optimizedConstants = (double[])initialConstants.Clone(); int numberOfRows = x.GetLength(0); int numberOfColumns = x.GetLength(1); int numberOfConstants = optimizedConstants.Length; alglib.lsfitstate state; alglib.lsfitreport rep; alglib.ndimensional_rep xrep = (p, f, obj) => LM_IterationCallback(p, f, obj); int retVal; try { alglib.lsfitcreatefg(x, y, optimizedConstants, numberOfRows, numberOfColumns, numberOfConstants, cheapfg: false, state: out state); alglib.lsfitsetcond(state, 0.0, 0.0, maxIterations); alglib.lsfitsetxrep(state, LM_IterationCallback != null); alglib.lsfitfit(state, Evaluate, EvaluateGradient, xrep, term); alglib.lsfitresults(state, out retVal, out optimizedConstants, out rep); } catch (ArithmeticException) { constants = new double[0]; return double.NaN; } catch (alglib.alglibexception) { constants = new double[0]; return double.NaN; } constants = optimizedConstants; return rep.r2; } private static void Evaluate(double[] c, double[] x, ref double fx, object o) { AutoDiff.IParametricCompiledTerm term = (AutoDiff.IParametricCompiledTerm)o; fx = term.Evaluate(c, x); } private static void EvaluateGradient(double[] c, double[] x, ref double fx, double[] grad, object o) { AutoDiff.IParametricCompiledTerm term = (AutoDiff.IParametricCompiledTerm)o; Tuple result = term.Differentiate(c, x); fx = result.Item2; Array.Copy(result.Item1, grad, grad.Length); } } }