#region License Information /* HeuristicLab * Copyright (C) 2002-2012 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.Common; using HeuristicLab.Core; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; using HeuristicLab.Problems.DataAnalysis; using HeuristicLabEigen; using ILNumerics; namespace HeuristicLab.Algorithms.DataAnalysis { /// /// Represents a Gaussian process model. /// [StorableClass] [Item("EigenGaussianProcessModel", "Gaussian process model implemented using ILNumerics.")] public sealed class EigenGaussianProcessModel : NamedItem, IGaussianProcessModel { [Storable] private double negativeLogLikelihood; public double NegativeLogLikelihood { get { return negativeLogLikelihood; } } [Storable] private double[] hyperparameterGradients; public double[] HyperparameterGradients { get { var copy = new double[hyperparameterGradients.Length]; Array.Copy(hyperparameterGradients, copy, copy.Length); return copy; } } [Storable] private ICovarianceFunction covarianceFunction; public ICovarianceFunction CovarianceFunction { get { return covarianceFunction; } } [Storable] private IMeanFunction meanFunction; public IMeanFunction MeanFunction { get { return meanFunction; } } [Storable] private string targetVariable; public string TargetVariable { get { return targetVariable; } } [Storable] private string[] allowedInputVariables; public string[] AllowedInputVariables { get { return allowedInputVariables; } } [Storable] private double sqrSigmaNoise; public double SigmaNoise { get { return Math.Sqrt(sqrSigmaNoise); } } [Storable] private double[] meanParameter; [Storable] private double[] covarianceParameter; [StorableConstructor] private EigenGaussianProcessModel(bool deserializing) : base(deserializing) { } private EigenGaussianProcessModel(EigenGaussianProcessModel original, Cloner cloner) : base(original, cloner) { this.meanFunction = cloner.Clone(original.meanFunction); this.covarianceFunction = cloner.Clone(original.covarianceFunction); this.negativeLogLikelihood = original.negativeLogLikelihood; this.targetVariable = original.targetVariable; this.sqrSigmaNoise = original.sqrSigmaNoise; if (original.meanParameter != null) { this.meanParameter = (double[])original.meanParameter.Clone(); } if (original.covarianceParameter != null) { this.covarianceParameter = (double[])original.covarianceParameter.Clone(); } // shallow copies of arrays because they cannot be modified this.allowedInputVariables = original.allowedInputVariables; } public EigenGaussianProcessModel(Dataset ds, string targetVariable, IEnumerable allowedInputVariables, IEnumerable rows, IEnumerable hyp, IMeanFunction meanFunction, ICovarianceFunction covarianceFunction) : base() { this.name = ItemName; this.description = ItemDescription; this.meanFunction = (IMeanFunction)meanFunction.Clone(); this.covarianceFunction = (ICovarianceFunction)covarianceFunction.Clone(); this.targetVariable = targetVariable; this.allowedInputVariables = allowedInputVariables.ToArray(); int nVariables = this.allowedInputVariables.Length; meanParameter = hyp .Take(this.meanFunction.GetNumberOfParameters(nVariables)) .ToArray(); covarianceParameter = hyp.Skip(this.meanFunction.GetNumberOfParameters(nVariables)) .Take(this.covarianceFunction.GetNumberOfParameters(nVariables)) .ToArray(); sqrSigmaNoise = Math.Exp(2.0 * hyp.Last()); CalculateModel(ds, rows); } private void CalculateModel(Dataset ds, IEnumerable rows) { var inputScaling = new Scaling(ds, allowedInputVariables, rows); var x = AlglibUtil.PrepareAndScaleInputMatrix(ds, allowedInputVariables, rows, inputScaling); var y = ds.GetDoubleValues(targetVariable, rows); int n = x.GetLength(0); var l = new double[n * n]; // calculate means and covariances var mean = meanFunction.GetParameterizedMeanFunction(meanParameter, Enumerable.Range(0, x.GetLength(1))); var cov = covarianceFunction.GetParameterizedCovarianceFunction(covarianceParameter, Enumerable.Range(0, x.GetLength(1))); for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { l[j + i * n] = cov.Covariance(x, i, j) / sqrSigmaNoise; if (j == i) l[j + i * n] += 1.0; } } var myEigen = new MyEigen(); int info = 0; var alpha = new double[n]; // solve for alpha double[] ym = y.Zip(Enumerable.Range(0, x.GetLength(0)).Select(r => mean.Mean(x, r)), (a, b) => a - b).ToArray(); double[] invL = new double[n * n]; double nll; unsafe { fixed (double* ap = &alpha[0]) fixed (double* ymp = &ym[0]) fixed (double* invlP = &invL[0]) fixed (double* lp = &l[0]) { myEigen.Solve(lp, ymp, ap, invlP, sqrSigmaNoise, n, &nll, &info); } } if (info != 0) throw new ArgumentException("Matrix is not positive semidefinite"); this.negativeLogLikelihood = nll; double noiseGradient = sqrSigmaNoise * Enumerable.Range(0, n).Select(i => invL[i + i * n]).Sum(); // derivatives int nAllowedVariables = x.GetLength(1); double[] meanGradients = new double[meanFunction.GetNumberOfParameters(nAllowedVariables)]; for (int k = 0; k < meanGradients.Length; k++) { var meanGrad = Enumerable.Range(0, alpha.Length) .Select(r => mean.Gradient(x, r, k)); meanGradients[k] = -Util.ScalarProd(meanGrad, alpha); } double[] covGradients = new double[covarianceFunction.GetNumberOfParameters(nAllowedVariables)]; if (covGradients.Length > 0) { for (int i = 0; i < n; i++) { for (int j = 0; j < i; j++) { var g = cov.CovarianceGradient(x, i, j).ToArray(); for (int k = 0; k < covGradients.Length; k++) { covGradients[k] += invL[j + i * n] * g[k]; } } var gDiag = cov.CovarianceGradient(x, i, i).ToArray(); for (int k = 0; k < covGradients.Length; k++) { // diag covGradients[k] += 0.5 * invL[i + i * n] * gDiag[k]; } } } hyperparameterGradients = meanGradients .Concat(covGradients) .Concat(new double[] { noiseGradient }).ToArray(); } public override IDeepCloneable Clone(Cloner cloner) { return new EigenGaussianProcessModel(this, cloner); } // is called by the solution creator to set all parameter values of the covariance and mean function // to the optimized values (necessary to make the values visible in the GUI) public void FixParameters() { covarianceFunction.SetParameter(covarianceParameter); meanFunction.SetParameter(meanParameter); covarianceParameter = new double[0]; meanParameter = new double[0]; } #region IRegressionModel Members public IEnumerable GetEstimatedValues(Dataset dataset, IEnumerable rows) { return GetEstimatedValuesHelper(dataset, rows); } public GaussianProcessRegressionSolution CreateRegressionSolution(IRegressionProblemData problemData) { return new GaussianProcessRegressionSolution(this, new RegressionProblemData(problemData)); } IRegressionSolution IRegressionModel.CreateRegressionSolution(IRegressionProblemData problemData) { return CreateRegressionSolution(problemData); } #endregion private IEnumerable GetEstimatedValuesHelper(Dataset dataset, IEnumerable rows) { return rows.Select(r => 0.0); } public IEnumerable GetEstimatedVariance(Dataset dataset, IEnumerable rows) { return rows.Select(r => sqrSigmaNoise); } } }