blob: d38ab39bbfaa6d1b3bbd9d4f9d546b644f4c514b [file]
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
// The log-likelihood of observations y under a Gaussian process with k Fourier
// features on a fixed frequency grid and noise variance s^2,
// log N(y | 0, S) = -(y^T S^{-1} y + log det S + n log(2 pi)) / 2,
// S = s^2 I + U U^T, U = (n x k feature matrix),
// costs O(n k^2) with DiagonalPlusLowRank: the Woodbury identity for the solve
// and the matrix determinant lemma for the determinant, instead of the O(n^3)
// Cholesky factorization of the dense covariance.
#include <contrib/Eigen/StructuredMatrices>
#include <cmath>
#include <iostream>
using namespace Eigen;
int main() {
const double pi = std::acos(-1.0), noise = 1.0;
const int n = 1000, k = 20;
VectorXd t(n), y(n);
for (int i = 0; i < n; ++i) {
t[i] = 10.0 * i / n;
y[i] = std::sin(t[i]) + 0.3 * std::cos(3 * t[i]);
}
// Features sqrt(2/k) cos(omega_j t + phase_j) on a fixed frequency grid.
MatrixXd U(n, k);
for (int j = 0; j < k; ++j)
for (int i = 0; i < n; ++i) U(i, j) = std::sqrt(2.0 / k) * std::cos(0.25 * j * t[i] + 0.5 * j);
const auto S = makeDiagonalPlusLowRank(VectorXd::Constant(n, noise * noise), U, U);
const double logLikelihood = -(y.dot(S.solve(y)) + std::log(S.determinant()) + n * std::log(2 * pi)) / 2;
const MatrixXd dense = noise * noise * MatrixXd::Identity(n, n) + U * U.transpose();
const LLT<MatrixXd> llt(dense);
const double logDet = 2 * llt.matrixL().toDenseMatrix().diagonal().array().log().sum();
const double reference = -(y.dot(llt.solve(y)) + logDet + n * std::log(2 * pi)) / 2;
std::cout << "log-likelihood, Woodbury: " << logLikelihood << "\n";
std::cout << "log-likelihood, dense: " << reference << "\n";
}