|
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 |
}
|