#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 System.Runtime.InteropServices;
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("ToeplitzGaussianProcessModel", "Gaussian process model implemented using ILNumerics.")]
public sealed class ToeplitzGaussianProcessModel : NamedItem, IGaussianProcessModel {
[DllImport("toeplitz.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void r4to_sl_(ref int n, float[] a, float[] x, float[] b, ref int job);
[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;
[Storable]
private float[] alpha;
[Storable]
private double[,] x;
[Storable]
private Scaling inputScaling;
[StorableConstructor]
private ToeplitzGaussianProcessModel(bool deserializing) : base(deserializing) { }
private ToeplitzGaussianProcessModel(ToeplitzGaussianProcessModel original, Cloner cloner)
: base(original, cloner) {
this.meanFunction = cloner.Clone(original.meanFunction);
this.covarianceFunction = cloner.Clone(original.covarianceFunction);
this.inputScaling = cloner.Clone(original.inputScaling);
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;
this.alpha = original.alpha;
this.x = original.x;
}
public ToeplitzGaussianProcessModel(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) {
inputScaling = new Scaling(ds, allowedInputVariables, rows);
x = AlglibUtil.PrepareAndScaleInputMatrix(ds, allowedInputVariables, rows, inputScaling);
var y = ds.GetDoubleValues(targetVariable, rows);
int n = x.GetLength(0);
var l = new float[2 * n - 1];
// 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)));
var l0 = (float)(cov.Covariance(x, 0, 0) / sqrSigmaNoise + 1.0);
l[0] = 1.0f;
for (int i = 1; i < n; i++) {
l[i] = (float)((cov.Covariance(x, 0, i) / sqrSigmaNoise)/ l0);
}
for (int i = n; i < 2 * n - 1; i++) {
l[i] = (float)((cov.Covariance(x, i - n + 1, 0) / sqrSigmaNoise) / l0);
}
int info = 0;
alpha = new float[n];
// solve for alpha
float[] ym = y.Zip(Enumerable.Range(0, x.GetLength(0)).Select(r => mean.Mean(x, r)), (a, b) => a - b)
.Select(e => (float)e)
.ToArray();
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);
// }
//}
int job = 0;
r4to_sl_(ref n, l, ym, alpha, ref job);
alpha = alpha.Select(a => (float)(a / sqrSigmaNoise / l0)).ToArray();
var yDotAlpha = ym.Zip(alpha, (f, f1) => f * f1).Sum();
var lambda = toeplitz_cholesky_diag(n, l);
var logDetK = 2 * lambda.Sum(f => Math.Log(f * Math.Sqrt(l0)));
this.negativeLogLikelihood = 0.5 * yDotAlpha + 0.5 * logDetK + n / 2.0 * Math.Log(2.0 * Math.PI * sqrSigmaNoise);
//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] = -meanGrad.Zip(alpha, (d1, f) => d1 * f).Sum();
//}
//
//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 =
Enumerable.Repeat(0.0,
meanFunction.GetNumberOfParameters(nAllowedVariables) +
covarianceFunction.GetNumberOfParameters(nAllowedVariables) + 1).ToArray();
//hyperparameterGradients =
// meanGradients
// .Concat(covGradients)
// .Concat(new double[] { noiseGradient }).ToArray();
}
public static double[] toeplitz_cholesky_diag(int n, float[] a) {
double div;
double[] g;
double g1j;
double g2j;
int i;
int j;
double[] l;
double rho;
l = new double[n];
g = new double[2 * n];
for (j = 0; j < n; j++) {
g[0 + j * 2] = a[j];
}
g[1 + 0 * 2] = 0.0;
for (j = 1; j < n; j++) {
g[1 + j * 2] = a[j + n - 1];
}
l[0] = g[0];
//for (i = 0; i < n; i++) {
// l[i, 0] = g[0 + i * 2];
//}
for (j = n - 1; 1 <= j; j--) {
g[0 + j * 2] = g[0 + (j - 1) * 2];
}
g[0 + 0 * 2] = 0.0;
for (i = 1; i < n; i++) {
rho = -g[1 + i * 2] / g[0 + i * 2];
div = Math.Sqrt((1.0 - rho) * (1.0 + rho));
for (j = i; j < n; j++) {
g1j = g[0 + j * 2];
g2j = g[1 + j * 2];
g[0 + j * 2] = (g1j + rho * g2j) / div;
g[1 + j * 2] = (rho * g1j + g2j) / div;
}
l[i] = g[i * 2];
//for (j = i; j < n; j++) {
// l[j, i] = g[0 + j * 2];
//}
for (j = n - 1; i < j; j--) {
g[0 + j * 2] = g[0 + (j - 1) * 2];
}
g[0 + i * 2] = 0.0;
}
return l;
}
public override IDeepCloneable Clone(Cloner cloner) {
return new ToeplitzGaussianProcessModel(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) {
var newX = AlglibUtil.PrepareAndScaleInputMatrix(dataset, allowedInputVariables, rows, inputScaling);
int newN = newX.GetLength(0);
int n = x.GetLength(0);
var Ks = new double[newN, n];
var mean = meanFunction.GetParameterizedMeanFunction(meanParameter, Enumerable.Range(0, newX.GetLength(1)));
var ms = Enumerable.Range(0, newX.GetLength(0))
.Select(r => mean.Mean(newX, r))
.ToArray();
var cov = covarianceFunction.GetParameterizedCovarianceFunction(covarianceParameter, Enumerable.Range(0, x.GetLength(1)));
for (int i = 0; i < newN; i++) {
for (int j = 0; j < n; j++) {
Ks[i, j] = cov.CrossCovariance(x, newX, j, i);
}
}
return Enumerable.Range(0, newN)
.Select(i => ms[i] + Util.ScalarProd(Util.GetRow(Ks, i), alpha.Select(a=>(double)a)));
}
public IEnumerable GetEstimatedVariance(Dataset dataset, IEnumerable rows) {
return rows.Select(r => sqrSigmaNoise);
}
}
}