Blame randist/mvgauss.c

Packit 67cb25
/* randist/mvgauss.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2016 Timothée Flutre, Patrick Alken
Packit 67cb25
 * 
Packit 67cb25
 * This program is free software; you can redistribute it and/or modify
Packit 67cb25
 * it under the terms of the GNU General Public License as published by
Packit 67cb25
 * the Free Software Foundation; either version 3 of the License, or (at
Packit 67cb25
 * your option) any later version.
Packit 67cb25
 * 
Packit 67cb25
 * This program is distributed in the hope that it will be useful, but
Packit 67cb25
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 67cb25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 67cb25
 * General Public License for more details.
Packit 67cb25
 * 
Packit 67cb25
 * You should have received a copy of the GNU General Public License
Packit 67cb25
 * along with this program; if not, write to the Free Software
Packit 67cb25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
#include <config.h>
Packit 67cb25
#include <math.h>
Packit 67cb25
#include <gsl/gsl_math.h>
Packit 67cb25
#include <gsl/gsl_rng.h>
Packit 67cb25
#include <gsl/gsl_randist.h>
Packit 67cb25
#include <gsl/gsl_vector.h>
Packit 67cb25
#include <gsl/gsl_matrix.h>
Packit 67cb25
#include <gsl/gsl_blas.h>
Packit 67cb25
#include <gsl/gsl_linalg.h>
Packit 67cb25
#include <gsl/gsl_statistics.h>
Packit 67cb25
Packit 67cb25
static int multivar_vcov (const double data[], size_t d, size_t tda, size_t n,
Packit 67cb25
                          double vcov[], size_t tda2);
Packit 67cb25
Packit 67cb25
/* Generate a random vector from a multivariate Gaussian distribution using
Packit 67cb25
 * the Cholesky decomposition of the variance-covariance matrix, following
Packit 67cb25
 * "Computational Statistics" from Gentle (2009), section 7.4.
Packit 67cb25
 *
Packit 67cb25
 * mu      mean vector (dimension d)
Packit 67cb25
 * L       matrix resulting from the Cholesky decomposition of
Packit 67cb25
 *         variance-covariance matrix Sigma = L L^T (dimension d x d)
Packit 67cb25
 * result  output vector (dimension d)
Packit 67cb25
 */
Packit 67cb25
int
Packit 67cb25
gsl_ran_multivariate_gaussian (const gsl_rng * r,
Packit 67cb25
                               const gsl_vector * mu,
Packit 67cb25
                               const gsl_matrix * L,
Packit 67cb25
                               gsl_vector * result)
Packit 67cb25
{
Packit 67cb25
  const size_t M = L->size1;
Packit 67cb25
  const size_t N = L->size2;
Packit 67cb25
Packit 67cb25
  if (M != N)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("requires square matrix", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (mu->size != M)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("incompatible dimension of mean vector with variance-covariance matrix", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (result->size != M)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("incompatible dimension of result vector", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      size_t i;
Packit 67cb25
Packit 67cb25
      for (i = 0; i < M; ++i)
Packit 67cb25
        gsl_vector_set(result, i, gsl_ran_ugaussian(r));
Packit 67cb25
Packit 67cb25
      gsl_blas_dtrmv(CblasLower, CblasNoTrans, CblasNonUnit, L, result);
Packit 67cb25
      gsl_vector_add(result, mu);
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/* Compute the log of the probability density function at a given quantile
Packit 67cb25
 * vector for a multivariate Gaussian distribution using the Cholesky
Packit 67cb25
 * decomposition of the variance-covariance matrix.
Packit 67cb25
 *
Packit 67cb25
 * x       vector of quantiles (dimension d)
Packit 67cb25
 * mu      mean vector (dimension d)
Packit 67cb25
 * L       matrix resulting from the Cholesky decomposition of
Packit 67cb25
 *         variance-covariance matrix Sigma = L L^T (dimension d x d)
Packit 67cb25
 * result  output of the density (dimension 1)
Packit 67cb25
 * work    vector used for intermediate computations (dimension d)
Packit 67cb25
 */
Packit 67cb25
int
Packit 67cb25
gsl_ran_multivariate_gaussian_log_pdf (const gsl_vector * x,
Packit 67cb25
                                       const gsl_vector * mu,
Packit 67cb25
                                       const gsl_matrix * L,
Packit 67cb25
                                       double * result,
Packit 67cb25
                                       gsl_vector * work)
Packit 67cb25
{
Packit 67cb25
  const size_t M = L->size1;
Packit 67cb25
  const size_t N = L->size2;
Packit 67cb25
Packit 67cb25
  if (M != N)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("requires square matrix", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (mu->size != M)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("incompatible dimension of mean vector with variance-covariance matrix", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (x->size != M)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("incompatible dimension of quantile vector", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (work->size != M)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("incompatible dimension of work vector", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      size_t i;
Packit 67cb25
      double quadForm;        /* (x - mu)' Sigma^{-1} (x - mu) */
Packit 67cb25
      double logSqrtDetSigma; /* log [ sqrt(|Sigma|) ] */
Packit 67cb25
Packit 67cb25
      /* compute: work = x - mu */
Packit 67cb25
      for (i = 0; i < M; ++i)
Packit 67cb25
        {
Packit 67cb25
          double xi = gsl_vector_get(x, i);
Packit 67cb25
          double mui = gsl_vector_get(mu, i);
Packit 67cb25
          gsl_vector_set(work, i, xi - mui);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* compute: work = L^{-1} * (x - mu) */
Packit 67cb25
      gsl_blas_dtrsv(CblasLower, CblasNoTrans, CblasNonUnit, L, work);
Packit 67cb25
Packit 67cb25
      /* compute: quadForm = (x - mu)' Sigma^{-1} (x - mu) */
Packit 67cb25
      gsl_blas_ddot(work, work, &quadForm);
Packit 67cb25
Packit 67cb25
      /* compute: log [ sqrt(|Sigma|) ] = sum_i log L_{ii} */
Packit 67cb25
      logSqrtDetSigma = 0.0;
Packit 67cb25
      for (i = 0; i < M; ++i)
Packit 67cb25
        {
Packit 67cb25
          double Lii = gsl_matrix_get(L, i, i);
Packit 67cb25
          logSqrtDetSigma += log(Lii);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      *result = -0.5*quadForm - logSqrtDetSigma - 0.5*M*log(2.0*M_PI);
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_ran_multivariate_gaussian_pdf (const gsl_vector * x,
Packit 67cb25
                                   const gsl_vector * mu,
Packit 67cb25
                                   const gsl_matrix * L,
Packit 67cb25
                                   double * result,
Packit 67cb25
                                   gsl_vector * work)
Packit 67cb25
{
Packit 67cb25
  double logpdf;
Packit 67cb25
  int status = gsl_ran_multivariate_gaussian_log_pdf(x, mu, L, &logpdf, work);
Packit 67cb25
Packit 67cb25
  if (status == GSL_SUCCESS)
Packit 67cb25
    *result = exp(logpdf);
Packit 67cb25
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/* Compute the maximum-likelihood estimate of the mean vector of samples
Packit 67cb25
 * from a multivariate Gaussian distribution.
Packit 67cb25
 *
Packit 67cb25
 * Example from R (GPL): http://www.r-project.org/
Packit 67cb25
 * (samples <- matrix(c(4.348817, 2.995049, -3.793431, 4.711934, 1.190864, -1.357363), nrow=3, ncol=2))
Packit 67cb25
 * colMeans(samples) # 1.183478 1.515145
Packit 67cb25
 */
Packit 67cb25
int
Packit 67cb25
gsl_ran_multivariate_gaussian_mean (const gsl_matrix * X, gsl_vector * mu_hat)
Packit 67cb25
{
Packit 67cb25
  const size_t M = X->size1;
Packit 67cb25
  const size_t N = X->size2;
Packit 67cb25
Packit 67cb25
  if (N != mu_hat->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("mu_hat vector has wrong size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      size_t j;
Packit 67cb25
Packit 67cb25
      for (j = 0; j < N; ++j)
Packit 67cb25
        {
Packit 67cb25
          gsl_vector_const_view c = gsl_matrix_const_column(X, j);
Packit 67cb25
          double mean = gsl_stats_mean(c.vector.data, c.vector.stride, M);
Packit 67cb25
          gsl_vector_set(mu_hat, j, mean);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/* Compute the maximum-likelihood estimate of the variance-covariance matrix
Packit 67cb25
 * of samples from a multivariate Gaussian distribution.
Packit 67cb25
 */
Packit 67cb25
int
Packit 67cb25
gsl_ran_multivariate_gaussian_vcov (const gsl_matrix * X, gsl_matrix * sigma_hat)
Packit 67cb25
{
Packit 67cb25
  const size_t M = X->size1;
Packit 67cb25
  const size_t N = X->size2;
Packit 67cb25
Packit 67cb25
  if (sigma_hat->size1 != sigma_hat->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("sigma_hat must be a square matrix", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (N != sigma_hat->size1)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("sigma_hat does not match X matrix dimensions", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      return multivar_vcov (X->data, N, X->tda, M, sigma_hat->data, sigma_hat->tda);
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/* Example from R (GPL): http://www.r-project.org/
Packit 67cb25
 * (samples <- matrix(c(4.348817, 2.995049, -3.793431, 4.711934, 1.190864, -1.357363), nrow=3, ncol=2))
Packit 67cb25
 * cov(samples) # 19.03539 11.91384 \n 11.91384  9.28796
Packit 67cb25
 */
Packit 67cb25
static int
Packit 67cb25
multivar_vcov (const double data[], size_t d, size_t tda, size_t n,
Packit 67cb25
               double vcov[], size_t tda2)
Packit 67cb25
{
Packit 67cb25
  size_t j1 = 0, j2 = 0;
Packit 67cb25
Packit 67cb25
  for (j1 = 0; j1 < d; ++j1)
Packit 67cb25
    {
Packit 67cb25
      vcov[j1 * tda2 + j1] = gsl_stats_variance(&(data[j1]), tda, n);
Packit 67cb25
      for (j2 = j1 + 1; j2 < d; ++j2)
Packit 67cb25
        {
Packit 67cb25
          vcov[j1 * tda2 + j2] = gsl_stats_covariance(&(data[j1]), tda,
Packit 67cb25
                                                      &(data[j2]), tda, n);
Packit 67cb25
          vcov[j2 * tda2 + j1] = vcov[j1 * tda2 + j2];
Packit 67cb25
        }
Packit 67cb25
      }
Packit 67cb25
  
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}