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