1  #region License Information


2  /* HeuristicLab


3  * Copyright (C) 20022018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)


4  *


5  * This file is part of HeuristicLab.


6  *


7  * HeuristicLab is free software: you can redistribute it and/or modify


8  * it under the terms of the GNU General Public License as published by


9  * the Free Software Foundation, either version 3 of the License, or


10  * (at your option) any later version.


11  *


12  * HeuristicLab is distributed in the hope that it will be useful,


13  * but WITHOUT ANY WARRANTY; without even the implied warranty of


14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the


15  * GNU General Public License for more details.


16  *


17  * You should have received a copy of the GNU General Public License


18  * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.


19  */


20  #endregion


21 


22  using System;


23  using System.Collections.Generic;


24  using System.Linq;


25  using System.Threading;


26  using HeuristicLab.Common;


27  using HeuristicLab.Core;


28  using HeuristicLab.Data;


29  using HeuristicLab.Optimization;


30  using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;


31  using HeuristicLab.Problems.DataAnalysis;


32  using HeuristicLab.Problems.DataAnalysis.Symbolic;


33  using HeuristicLab.Problems.DataAnalysis.Symbolic.Regression;


34 


35  namespace HeuristicLab.Algorithms.DataAnalysis {


36  /// <summary>


37  /// Linear regression data analysis algorithm.


38  /// </summary>


39  [Item("Linear Regression (LR)", "Linear regression data analysis algorithm (wrapper for ALGLIB).")]


40  [Creatable(CreatableAttribute.Categories.DataAnalysisRegression, Priority = 100)]


41  [StorableClass]


42  public sealed class LinearRegression : FixedDataAnalysisAlgorithm<IRegressionProblem> {


43  private const string SolutionResultName = "Linear regression solution";


44  private const string ConfidenceSolutionResultName = "Solution with prediction intervals";


45 


46  [StorableConstructor]


47  private LinearRegression(bool deserializing) : base(deserializing) { }


48  private LinearRegression(LinearRegression original, Cloner cloner)


49  : base(original, cloner) {


50  }


51  public LinearRegression()


52  : base() {


53  Problem = new RegressionProblem();


54  }


55  [StorableHook(HookType.AfterDeserialization)]


56  private void AfterDeserialization() { }


57 


58  public override IDeepCloneable Clone(Cloner cloner) {


59  return new LinearRegression(this, cloner);


60  }


61 


62  #region linear regression


63  protected override void Run(CancellationToken cancellationToken) {


64  double rmsError, cvRmsError;


65  // produce both solutions, to allow symbolic manipulation of LR solutions as well


66  // as the calculation of prediction intervals.


67  // There is no clean way to implement the new model class for LR as a symbolic model.


68  var solution = CreateSolution(Problem.ProblemData, out rmsError, out cvRmsError);


69  #pragma warning disable 168, 3021


70  var symbolicSolution = CreateLinearRegressionSolution(Problem.ProblemData, out rmsError, out cvRmsError);


71  #pragma warning restore 168, 3021


72  Results.Add(new Result(SolutionResultName, "The linear regression solution.", symbolicSolution));


73  Results.Add(new Result(ConfidenceSolutionResultName, "Linear regression solution with parameter covariance matrix " +


74  "and calculation of prediction intervals", solution));


75  Results.Add(new Result("Root mean square error", "The root of the mean of squared errors of the linear regression solution on the training set.", new DoubleValue(rmsError)));


76  Results.Add(new Result("Estimated root mean square error (crossvalidation)", "The estimated root of the mean of squared errors of the linear regression solution via cross validation.", new DoubleValue(cvRmsError)));


77  }


78 


79  [Obsolete("Use CreateSolution() instead")]


80  public static ISymbolicRegressionSolution CreateLinearRegressionSolution(IRegressionProblemData problemData, out double rmsError, out double cvRmsError) {


81  IEnumerable<string> doubleVariables;


82  IEnumerable<KeyValuePair<string, IEnumerable<string>>> factorVariables;


83  double[,] inputMatrix;


84  PrepareData(problemData, out inputMatrix, out doubleVariables, out factorVariables);


85 


86  alglib.linearmodel lm = new alglib.linearmodel();


87  alglib.lrreport ar = new alglib.lrreport();


88  int nRows = inputMatrix.GetLength(0);


89  int nFeatures = inputMatrix.GetLength(1)  1;


90 


91  int retVal = 1;


92  alglib.lrbuild(inputMatrix, nRows, nFeatures, out retVal, out lm, out ar);


93  if (retVal != 1) throw new ArgumentException("Error in calculation of linear regression solution");


94  rmsError = ar.rmserror;


95  cvRmsError = ar.cvrmserror;


96 


97  double[] coefficients = new double[nFeatures + 1]; // last coefficient is for the constant


98  alglib.lrunpack(lm, out coefficients, out nFeatures);


99 


100  int nFactorCoeff = factorVariables.Sum(kvp => kvp.Value.Count());


101  int nVarCoeff = doubleVariables.Count();


102  var tree = LinearModelToTreeConverter.CreateTree(factorVariables, coefficients.Take(nFactorCoeff).ToArray(),


103  doubleVariables.ToArray(), coefficients.Skip(nFactorCoeff).Take(nVarCoeff).ToArray(),


104  @const: coefficients[nFeatures]);


105 


106  SymbolicRegressionSolution solution = new SymbolicRegressionSolution(new SymbolicRegressionModel(problemData.TargetVariable, tree, new SymbolicDataAnalysisExpressionTreeLinearInterpreter()), (IRegressionProblemData)problemData.Clone());


107  solution.Model.Name = "Linear Regression Model";


108  solution.Name = "Linear Regression Solution";


109  return solution;


110  }


111 


112  public static IRegressionSolution CreateSolution(IRegressionProblemData problemData, out double rmsError, out double cvRmsError) {


113  IEnumerable<string> doubleVariables;


114  IEnumerable<KeyValuePair<string, IEnumerable<string>>> factorVariables;


115  double[,] inputMatrix;


116  PrepareData(problemData, out inputMatrix, out doubleVariables, out factorVariables);


117 


118  alglib.linearmodel lm = new alglib.linearmodel();


119  alglib.lrreport ar = new alglib.lrreport();


120  int nRows = inputMatrix.GetLength(0);


121  int nFeatures = inputMatrix.GetLength(1)  1;


122 


123  int retVal = 1;


124  alglib.lrbuild(inputMatrix, nRows, nFeatures, out retVal, out lm, out ar);


125  if (retVal != 1) throw new ArgumentException("Error in calculation of linear regression solution");


126  rmsError = ar.rmserror;


127  cvRmsError = ar.cvrmserror;


128 


129  // get parameters of the model


130  double[] w;


131  int nVars;


132  alglib.lrunpack(lm, out w, out nVars);


133 


134  // ar.c is the covariation matrix, array[0..NVars,0..NVars].


135  // C[i, j] = Cov(A[i], A[j])


136 


137  var solution = new LinearRegressionModel(w, ar.c, cvRmsError, problemData.TargetVariable, doubleVariables, factorVariables)


138  .CreateRegressionSolution((IRegressionProblemData)problemData.Clone());


139  solution.Name = "Linear Regression Solution";


140  return solution;


141  }


142 


143  private static void PrepareData(IRegressionProblemData problemData,


144  out double[,] inputMatrix,


145  out IEnumerable<string> doubleVariables,


146  out IEnumerable<KeyValuePair<string, IEnumerable<string>>> factorVariables) {


147  var dataset = problemData.Dataset;


148  string targetVariable = problemData.TargetVariable;


149  IEnumerable<string> allowedInputVariables = problemData.AllowedInputVariables;


150  IEnumerable<int> rows = problemData.TrainingIndices;


151  doubleVariables = allowedInputVariables.Where(dataset.VariableHasType<double>);


152  var factorVariableNames = allowedInputVariables.Where(dataset.VariableHasType<string>);


153  factorVariables = dataset.GetFactorVariableValues(factorVariableNames, rows);


154  double[,] binaryMatrix = dataset.ToArray(factorVariables, rows);


155  double[,] doubleVarMatrix = dataset.ToArray(doubleVariables.Concat(new string[] { targetVariable }), rows);


156  inputMatrix = binaryMatrix.HorzCat(doubleVarMatrix);


157 


158  if (inputMatrix.ContainsNanOrInfinity())


159  throw new NotSupportedException("Linear regression does not support NaN or infinity values in the input dataset.");


160  }


161  #endregion


162  }


163  }

