#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);
}
}
}