838 lines
18 KiB
C
838 lines
18 KiB
C
|
/*
|
||
|
* hmm.c
|
||
|
*
|
||
|
* Created by Mark Levy on 12/02/2006.
|
||
|
* Copyright 2006 Centre for Digital Music, Queen Mary, University of London.
|
||
|
|
||
|
This program 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 2 of the
|
||
|
License, or (at your option) any later version. See the file
|
||
|
COPYING included with this distribution for more information.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <math.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <float.h>
|
||
|
#include <time.h> /* to seed random number generator */
|
||
|
|
||
|
#include <clapack.h> /* LAPACK for matrix inversion */
|
||
|
|
||
|
#include "maths/nan-inf.h"
|
||
|
|
||
|
#ifdef ATLAS_ORDER
|
||
|
#define HAVE_ATLAS 1
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_ATLAS
|
||
|
// Using ATLAS C interface to LAPACK
|
||
|
#define dgetrf_(m, n, a, lda, ipiv, info) \
|
||
|
clapack_dgetrf(CblasColMajor, *m, *n, a, *lda, ipiv)
|
||
|
#define dgetri_(n, a, lda, ipiv, work, lwork, info) \
|
||
|
clapack_dgetri(CblasColMajor, *n, a, *lda, ipiv)
|
||
|
#endif
|
||
|
|
||
|
#ifdef _MAC_OS_X
|
||
|
#include <vecLib/cblas.h>
|
||
|
#else
|
||
|
#include <cblas.h> /* BLAS for matrix multiplication */
|
||
|
#endif
|
||
|
|
||
|
#include "hmm.h"
|
||
|
|
||
|
model_t* hmm_init(double** x, int T, int L, int N)
|
||
|
{
|
||
|
int i, j, d, e, t;
|
||
|
double s, ss;
|
||
|
|
||
|
model_t* model;
|
||
|
model = (model_t*) malloc(sizeof(model_t));
|
||
|
model->N = N;
|
||
|
model->L = L;
|
||
|
model->p0 = (double*) malloc(N*sizeof(double));
|
||
|
model->a = (double**) malloc(N*sizeof(double*));
|
||
|
model->mu = (double**) malloc(N*sizeof(double*));
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
model->a[i] = (double*) malloc(N*sizeof(double));
|
||
|
model->mu[i] = (double*) malloc(L*sizeof(double));
|
||
|
}
|
||
|
model->cov = (double**) malloc(L*sizeof(double*));
|
||
|
for (i = 0; i < L; i++)
|
||
|
model->cov[i] = (double*) malloc(L*sizeof(double));
|
||
|
|
||
|
srand(time(0));
|
||
|
double* global_mean = (double*) malloc(L*sizeof(double));
|
||
|
|
||
|
/* find global mean */
|
||
|
for (d = 0; d < L; d++)
|
||
|
{
|
||
|
global_mean[d] = 0;
|
||
|
for (t = 0; t < T; t++)
|
||
|
global_mean[d] += x[t][d];
|
||
|
global_mean[d] /= T;
|
||
|
}
|
||
|
|
||
|
/* calculate global diagonal covariance */
|
||
|
for (d = 0; d < L; d++)
|
||
|
{
|
||
|
for (e = 0; e < L; e++)
|
||
|
model->cov[d][e] = 0;
|
||
|
for (t = 0; t < T; t++)
|
||
|
model->cov[d][d] += (x[t][d] - global_mean[d]) * (x[t][d] - global_mean[d]);
|
||
|
model->cov[d][d] /= T-1;
|
||
|
}
|
||
|
|
||
|
/* set all means close to global mean */
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
for (d = 0; d < L; d++)
|
||
|
{
|
||
|
/* add some random noise related to covariance */
|
||
|
/* ideally the random number would be Gaussian(0,1), as a hack we make it uniform on [-0.25,0.25] */
|
||
|
model->mu[i][d] = global_mean[d] + (0.5 * rand() / (double) RAND_MAX - 0.25) * sqrt(model->cov[d][d]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* random intial and transition probs */
|
||
|
s = 0;
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
model->p0[i] = 1 + rand() / (double) RAND_MAX;
|
||
|
s += model->p0[i];
|
||
|
ss = 0;
|
||
|
for (j = 0; j < N; j++)
|
||
|
{
|
||
|
model->a[i][j] = 1 + rand() / (double) RAND_MAX;
|
||
|
ss += model->a[i][j];
|
||
|
}
|
||
|
for (j = 0; j < N; j++)
|
||
|
{
|
||
|
model->a[i][j] /= ss;
|
||
|
}
|
||
|
}
|
||
|
for (i = 0; i < N; i++)
|
||
|
model->p0[i] /= s;
|
||
|
|
||
|
free(global_mean);
|
||
|
|
||
|
return model;
|
||
|
}
|
||
|
|
||
|
void hmm_close(model_t* model)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < model->N; i++)
|
||
|
{
|
||
|
free(model->a[i]);
|
||
|
free(model->mu[i]);
|
||
|
}
|
||
|
free(model->a);
|
||
|
free(model->mu);
|
||
|
for (i = 0; i < model->L; i++)
|
||
|
free(model->cov[i]);
|
||
|
free(model->cov);
|
||
|
free(model);
|
||
|
}
|
||
|
|
||
|
void hmm_train(double** x, int T, model_t* model)
|
||
|
{
|
||
|
int i, t;
|
||
|
double loglik; /* overall log-likelihood at each iteration */
|
||
|
|
||
|
int N = model->N;
|
||
|
int L = model->L;
|
||
|
double* p0 = model->p0;
|
||
|
double** a = model->a;
|
||
|
double** mu = model->mu;
|
||
|
double** cov = model->cov;
|
||
|
|
||
|
/* allocate memory */
|
||
|
double** gamma = (double**) malloc(T*sizeof(double*));
|
||
|
double*** xi = (double***) malloc(T*sizeof(double**));
|
||
|
for (t = 0; t < T; t++)
|
||
|
{
|
||
|
gamma[t] = (double*) malloc(N*sizeof(double));
|
||
|
xi[t] = (double**) malloc(N*sizeof(double*));
|
||
|
for (i = 0; i < N; i++)
|
||
|
xi[t][i] = (double*) malloc(N*sizeof(double));
|
||
|
}
|
||
|
|
||
|
/* temporary memory */
|
||
|
double* gauss_y = (double*) malloc(L*sizeof(double));
|
||
|
double* gauss_z = (double*) malloc(L*sizeof(double));
|
||
|
|
||
|
/* obs probs P(j|{x}) */
|
||
|
double** b = (double**) malloc(T*sizeof(double*));
|
||
|
for (t = 0; t < T; t++)
|
||
|
b[t] = (double*) malloc(N*sizeof(double));
|
||
|
|
||
|
/* inverse covariance and its determinant */
|
||
|
double** icov = (double**) malloc(L*sizeof(double*));
|
||
|
for (i = 0; i < L; i++)
|
||
|
icov[i] = (double*) malloc(L*sizeof(double));
|
||
|
double detcov;
|
||
|
|
||
|
double thresh = 0.0001;
|
||
|
int niter = 50;
|
||
|
int iter = 0;
|
||
|
double loglik1, loglik2;
|
||
|
int foundnan = 0;
|
||
|
|
||
|
while (iter < niter && !foundnan && !(iter > 1 && (loglik - loglik1) < thresh * (loglik1 - loglik2)))
|
||
|
{
|
||
|
++iter;
|
||
|
/*
|
||
|
fprintf(stderr, "calculating obsprobs...\n");
|
||
|
fflush(stderr);
|
||
|
*/
|
||
|
/* precalculate obs probs */
|
||
|
invert(cov, L, icov, &detcov);
|
||
|
|
||
|
for (t = 0; t < T; t++)
|
||
|
{
|
||
|
//int allzero = 1;
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
b[t][i] = exp(loggauss(x[t], L, mu[i], icov, detcov, gauss_y, gauss_z));
|
||
|
|
||
|
//if (b[t][i] != 0)
|
||
|
// allzero = 0;
|
||
|
}
|
||
|
/*
|
||
|
if (allzero)
|
||
|
{
|
||
|
printf("all the b[t][i] were zero for t = %d, correcting...\n", t);
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
b[t][i] = 0.00001;
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
}
|
||
|
/*
|
||
|
fprintf(stderr, "forwards-backwards...\n");
|
||
|
fflush(stderr);
|
||
|
*/
|
||
|
forward_backwards(xi, gamma, &loglik, &loglik1, &loglik2, iter, N, T, p0, a, b);
|
||
|
/*
|
||
|
fprintf(stderr, "iteration %d: loglik = %f\n", iter, loglik);
|
||
|
fprintf(stderr, "re-estimation...\n");
|
||
|
fflush(stderr);
|
||
|
*/
|
||
|
if (ISNAN(loglik)) {
|
||
|
foundnan = 1;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
baum_welch(p0, a, mu, cov, N, T, L, x, xi, gamma);
|
||
|
|
||
|
/*
|
||
|
printf("a:\n");
|
||
|
for (i = 0; i < model->N; i++)
|
||
|
{
|
||
|
for (j = 0; j < model->N; j++)
|
||
|
printf("%f ", model->a[i][j]);
|
||
|
printf("\n");
|
||
|
}
|
||
|
printf("\n\n");
|
||
|
*/
|
||
|
//hmm_print(model);
|
||
|
}
|
||
|
|
||
|
/* deallocate memory */
|
||
|
for (t = 0; t < T; t++)
|
||
|
{
|
||
|
free(gamma[t]);
|
||
|
free(b[t]);
|
||
|
for (i = 0; i < N; i++)
|
||
|
free(xi[t][i]);
|
||
|
free(xi[t]);
|
||
|
}
|
||
|
free(gamma);
|
||
|
free(xi);
|
||
|
free(b);
|
||
|
|
||
|
for (i = 0; i < L; i++)
|
||
|
free(icov[i]);
|
||
|
free(icov);
|
||
|
|
||
|
free(gauss_y);
|
||
|
free(gauss_z);
|
||
|
}
|
||
|
|
||
|
void mlss_reestimate(double* p0, double** a, double** mu, double** cov, int N, int T, int L, int* q, double** x)
|
||
|
{
|
||
|
/* fit a single Gaussian to observations in each state */
|
||
|
|
||
|
/* calculate the mean observation in each state */
|
||
|
|
||
|
/* calculate the overall covariance */
|
||
|
|
||
|
/* count transitions */
|
||
|
|
||
|
/* estimate initial probs from transitions (???) */
|
||
|
}
|
||
|
|
||
|
void baum_welch(double* p0, double** a, double** mu, double** cov, int N, int T, int L, double** x, double*** xi, double** gamma)
|
||
|
{
|
||
|
int i, j, t;
|
||
|
|
||
|
double* sum_gamma = (double*) malloc(N*sizeof(double));
|
||
|
|
||
|
/* temporary memory */
|
||
|
double* u = (double*) malloc(L*L*sizeof(double));
|
||
|
double* yy = (double*) malloc(T*L*sizeof(double));
|
||
|
double* yy2 = (double*) malloc(T*L*sizeof(double));
|
||
|
|
||
|
/* re-estimate transition probs */
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
sum_gamma[i] = 0;
|
||
|
for (t = 0; t < T-1; t++)
|
||
|
sum_gamma[i] += gamma[t][i];
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
if (sum_gamma[i] == 0)
|
||
|
{
|
||
|
/* fprintf(stderr, "sum_gamma[%d] was zero...\n", i); */
|
||
|
}
|
||
|
//double s = 0;
|
||
|
for (j = 0; j < N; j++)
|
||
|
{
|
||
|
a[i][j] = 0;
|
||
|
if (sum_gamma[i] == 0.) continue;
|
||
|
for (t = 0; t < T-1; t++)
|
||
|
a[i][j] += xi[t][i][j];
|
||
|
//s += a[i][j];
|
||
|
a[i][j] /= sum_gamma[i];
|
||
|
}
|
||
|
/*
|
||
|
for (j = 0; j < N; j++)
|
||
|
{
|
||
|
a[i][j] /= s;
|
||
|
}
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
/* NB: now we need to sum gamma over all t */
|
||
|
for (i = 0; i < N; i++)
|
||
|
sum_gamma[i] += gamma[T-1][i];
|
||
|
|
||
|
/* re-estimate initial probs */
|
||
|
for (i = 0; i < N; i++)
|
||
|
p0[i] = gamma[0][i];
|
||
|
|
||
|
/* re-estimate covariance */
|
||
|
int d, e;
|
||
|
double sum_sum_gamma = 0;
|
||
|
for (i = 0; i < N; i++)
|
||
|
sum_sum_gamma += sum_gamma[i];
|
||
|
|
||
|
/*
|
||
|
for (d = 0; d < L; d++)
|
||
|
{
|
||
|
for (e = d; e < L; e++)
|
||
|
{
|
||
|
cov[d][e] = 0;
|
||
|
for (t = 0; t < T; t++)
|
||
|
for (j = 0; j < N; j++)
|
||
|
cov[d][e] += gamma[t][j] * (x[t][d] - mu[j][d]) * (x[t][e] - mu[j][e]);
|
||
|
|
||
|
cov[d][e] /= sum_sum_gamma;
|
||
|
|
||
|
if (ISNAN(cov[d][e]))
|
||
|
{
|
||
|
printf("cov[%d][%d] was nan\n", d, e);
|
||
|
for (j = 0; j < N; j++)
|
||
|
for (i = 0; i < L; i++)
|
||
|
if (ISNAN(mu[j][i]))
|
||
|
printf("mu[%d][%d] was nan\n", j, i);
|
||
|
for (t = 0; t < T; t++)
|
||
|
for (j = 0; j < N; j++)
|
||
|
if (ISNAN(gamma[t][j]))
|
||
|
printf("gamma[%d][%d] was nan\n", t, j);
|
||
|
exit(-1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for (d = 0; d < L; d++)
|
||
|
for (e = 0; e < d; e++)
|
||
|
cov[d][e] = cov[e][d];
|
||
|
*/
|
||
|
|
||
|
/* using BLAS */
|
||
|
for (d = 0; d < L; d++)
|
||
|
for (e = 0; e < L; e++)
|
||
|
cov[d][e] = 0;
|
||
|
|
||
|
for (j = 0; j < N; j++)
|
||
|
{
|
||
|
for (d = 0; d < L; d++)
|
||
|
for (t = 0; t < T; t++)
|
||
|
{
|
||
|
yy[d*T+t] = x[t][d] - mu[j][d];
|
||
|
yy2[d*T+t] = gamma[t][j] * (x[t][d] - mu[j][d]);
|
||
|
}
|
||
|
|
||
|
cblas_dgemm(CblasColMajor, CblasTrans, CblasNoTrans, L, L, T, 1.0, yy, T, yy2, T, 0, u, L);
|
||
|
|
||
|
for (e = 0; e < L; e++)
|
||
|
for (d = 0; d < L; d++)
|
||
|
cov[d][e] += u[e*L+d];
|
||
|
}
|
||
|
|
||
|
for (d = 0; d < L; d++)
|
||
|
for (e = 0; e < L; e++)
|
||
|
cov[d][e] /= T; /* sum_sum_gamma; */
|
||
|
|
||
|
//printf("sum_sum_gamma = %f\n", sum_sum_gamma); /* fine, = T IS THIS ALWAYS TRUE with pooled cov?? */
|
||
|
|
||
|
/* re-estimate means */
|
||
|
for (j = 0; j < N; j++)
|
||
|
{
|
||
|
for (d = 0; d < L; d++)
|
||
|
{
|
||
|
mu[j][d] = 0;
|
||
|
for (t = 0; t < T; t++)
|
||
|
mu[j][d] += gamma[t][j] * x[t][d];
|
||
|
mu[j][d] /= sum_gamma[j];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* deallocate memory */
|
||
|
free(sum_gamma);
|
||
|
free(yy);
|
||
|
free(yy2);
|
||
|
free(u);
|
||
|
}
|
||
|
|
||
|
void forward_backwards(double*** xi, double** gamma, double* loglik, double* loglik1, double* loglik2, int iter, int N, int T, double* p0, double** a, double** b)
|
||
|
{
|
||
|
/* forwards-backwards with scaling */
|
||
|
int i, j, t;
|
||
|
|
||
|
double** alpha = (double**) malloc(T*sizeof(double*));
|
||
|
double** beta = (double**) malloc(T*sizeof(double*));
|
||
|
for (t = 0; t < T; t++)
|
||
|
{
|
||
|
alpha[t] = (double*) malloc(N*sizeof(double));
|
||
|
beta[t] = (double*) malloc(N*sizeof(double));
|
||
|
}
|
||
|
|
||
|
/* scaling coefficients */
|
||
|
double* c = (double*) malloc(T*sizeof(double));
|
||
|
|
||
|
/* calculate forward probs and scale coefficients */
|
||
|
c[0] = 0;
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
alpha[0][i] = p0[i] * b[0][i];
|
||
|
c[0] += alpha[0][i];
|
||
|
|
||
|
//printf("p0[%d] = %f, b[0][%d] = %f\n", i, p0[i], i, b[0][i]);
|
||
|
}
|
||
|
c[0] = 1 / c[0];
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
alpha[0][i] *= c[0];
|
||
|
|
||
|
//printf("alpha[0][%d] = %f\n", i, alpha[0][i]); /* OK agrees with Matlab */
|
||
|
}
|
||
|
|
||
|
*loglik1 = *loglik;
|
||
|
*loglik = -log(c[0]);
|
||
|
if (iter == 2)
|
||
|
*loglik2 = *loglik;
|
||
|
|
||
|
for (t = 1; t < T; t++)
|
||
|
{
|
||
|
c[t] = 0;
|
||
|
for (j = 0; j < N; j++)
|
||
|
{
|
||
|
alpha[t][j] = 0;
|
||
|
for (i = 0; i < N; i++)
|
||
|
alpha[t][j] += alpha[t-1][i] * a[i][j];
|
||
|
alpha[t][j] *= b[t][j];
|
||
|
|
||
|
c[t] += alpha[t][j];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
if (c[t] == 0)
|
||
|
{
|
||
|
printf("c[%d] = 0, going to blow up so exiting\n", t);
|
||
|
for (i = 0; i < N; i++)
|
||
|
if (b[t][i] == 0)
|
||
|
fprintf(stderr, "b[%d][%d] was zero\n", t, i);
|
||
|
fprintf(stderr, "x[t] was \n");
|
||
|
for (i = 0; i < L; i++)
|
||
|
fprintf(stderr, "%f ", x[t][i]);
|
||
|
fprintf(stderr, "\n\n");
|
||
|
exit(-1);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
c[t] = 1 / c[t];
|
||
|
for (j = 0; j < N; j++)
|
||
|
alpha[t][j] *= c[t];
|
||
|
|
||
|
//printf("c[%d] = %e\n", t, c[t]);
|
||
|
|
||
|
*loglik -= log(c[t]);
|
||
|
}
|
||
|
|
||
|
/* calculate backwards probs using same coefficients */
|
||
|
for (i = 0; i < N; i++)
|
||
|
beta[T-1][i] = 1;
|
||
|
t = T - 1;
|
||
|
while (1)
|
||
|
{
|
||
|
for (i = 0; i < N; i++)
|
||
|
beta[t][i] *= c[t];
|
||
|
|
||
|
if (t == 0)
|
||
|
break;
|
||
|
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
beta[t-1][i] = 0;
|
||
|
for (j = 0; j < N; j++)
|
||
|
beta[t-1][i] += a[i][j] * b[t][j] * beta[t][j];
|
||
|
}
|
||
|
|
||
|
t--;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
printf("alpha:\n");
|
||
|
for (t = 0; t < T; t++)
|
||
|
{
|
||
|
for (i = 0; i < N; i++)
|
||
|
printf("%4.4e\t\t", alpha[t][i]);
|
||
|
printf("\n");
|
||
|
}
|
||
|
printf("\n\n");printf("beta:\n");
|
||
|
for (t = 0; t < T; t++)
|
||
|
{
|
||
|
for (i = 0; i < N; i++)
|
||
|
printf("%4.4e\t\t", beta[t][i]);
|
||
|
printf("\n");
|
||
|
}
|
||
|
printf("\n\n");
|
||
|
*/
|
||
|
|
||
|
/* calculate posterior probs */
|
||
|
double tot;
|
||
|
for (t = 0; t < T; t++)
|
||
|
{
|
||
|
tot = 0;
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
gamma[t][i] = alpha[t][i] * beta[t][i];
|
||
|
tot += gamma[t][i];
|
||
|
}
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
gamma[t][i] /= tot;
|
||
|
|
||
|
//printf("gamma[%d][%d] = %f\n", t, i, gamma[t][i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (t = 0; t < T-1; t++)
|
||
|
{
|
||
|
tot = 0;
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
for (j = 0; j < N; j++)
|
||
|
{
|
||
|
xi[t][i][j] = alpha[t][i] * a[i][j] * b[t+1][j] * beta[t+1][j];
|
||
|
tot += xi[t][i][j];
|
||
|
}
|
||
|
}
|
||
|
for (i = 0; i < N; i++)
|
||
|
for (j = 0; j < N; j++)
|
||
|
xi[t][i][j] /= tot;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
// CHECK - fine
|
||
|
// gamma[t][i] = \sum_j{xi[t][i][j]}
|
||
|
tot = 0;
|
||
|
for (j = 0; j < N; j++)
|
||
|
tot += xi[3][1][j];
|
||
|
printf("gamma[3][1] = %f, sum_j(xi[3][1][j]) = %f\n", gamma[3][1], tot);
|
||
|
*/
|
||
|
|
||
|
for (t = 0; t < T; t++)
|
||
|
{
|
||
|
free(alpha[t]);
|
||
|
free(beta[t]);
|
||
|
}
|
||
|
free(alpha);
|
||
|
free(beta);
|
||
|
free(c);
|
||
|
}
|
||
|
|
||
|
void viterbi_decode(double** x, int T, model_t* model, int* q)
|
||
|
{
|
||
|
int i, j, t;
|
||
|
double max;
|
||
|
int havemax;
|
||
|
|
||
|
int N = model->N;
|
||
|
int L = model->L;
|
||
|
double* p0 = model->p0;
|
||
|
double** a = model->a;
|
||
|
double** mu = model->mu;
|
||
|
double** cov = model->cov;
|
||
|
|
||
|
/* inverse covariance and its determinant */
|
||
|
double** icov = (double**) malloc(L*sizeof(double*));
|
||
|
for (i = 0; i < L; i++)
|
||
|
icov[i] = (double*) malloc(L*sizeof(double));
|
||
|
double detcov;
|
||
|
|
||
|
double** logb = (double**) malloc(T*sizeof(double*));
|
||
|
double** phi = (double**) malloc(T*sizeof(double*));
|
||
|
int** psi = (int**) malloc(T*sizeof(int*));
|
||
|
for (t = 0; t < T; t++)
|
||
|
{
|
||
|
logb[t] = (double*) malloc(N*sizeof(double));
|
||
|
phi[t] = (double*) malloc(N*sizeof(double));
|
||
|
psi[t] = (int*) malloc(N*sizeof(int));
|
||
|
}
|
||
|
|
||
|
/* temporary memory */
|
||
|
double* gauss_y = (double*) malloc(L*sizeof(double));
|
||
|
double* gauss_z = (double*) malloc(L*sizeof(double));
|
||
|
|
||
|
/* calculate observation logprobs */
|
||
|
invert(cov, L, icov, &detcov);
|
||
|
for (t = 0; t < T; t++)
|
||
|
for (i = 0; i < N; i++)
|
||
|
logb[t][i] = loggauss(x[t], L, mu[i], icov, detcov, gauss_y, gauss_z);
|
||
|
|
||
|
/* initialise */
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
phi[0][i] = log(p0[i]) + logb[0][i];
|
||
|
psi[0][i] = 0;
|
||
|
}
|
||
|
|
||
|
for (t = 1; t < T; t++)
|
||
|
{
|
||
|
for (j = 0; j < N; j++)
|
||
|
{
|
||
|
max = -1000000;
|
||
|
havemax = 0;
|
||
|
|
||
|
psi[t][j] = 0;
|
||
|
for (i = 0; i < N; i++)
|
||
|
{
|
||
|
if (phi[t-1][i] + log(a[i][j]) > max || !havemax)
|
||
|
{
|
||
|
max = phi[t-1][i] + log(a[i][j]);
|
||
|
phi[t][j] = max + logb[t][j];
|
||
|
psi[t][j] = i;
|
||
|
havemax = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* find maximising state at time T-1 */
|
||
|
max = phi[T-1][0];
|
||
|
q[T-1] = 0;
|
||
|
for (i = 1; i < N; i++)
|
||
|
{
|
||
|
if (phi[T-1][i] > max)
|
||
|
{
|
||
|
max = phi[T-1][i];
|
||
|
q[T-1] = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* track back */
|
||
|
t = T - 2;
|
||
|
while (t >= 0)
|
||
|
{
|
||
|
q[t] = psi[t+1][q[t+1]];
|
||
|
t--;
|
||
|
}
|
||
|
|
||
|
/* de-allocate memory */
|
||
|
for (i = 0; i < L; i++)
|
||
|
free(icov[i]);
|
||
|
free(icov);
|
||
|
for (t = 0; t < T; t++)
|
||
|
{
|
||
|
free(logb[t]);
|
||
|
free(phi[t]);
|
||
|
free(psi[t]);
|
||
|
}
|
||
|
free(logb);
|
||
|
free(phi);
|
||
|
free(psi);
|
||
|
|
||
|
free(gauss_y);
|
||
|
free(gauss_z);
|
||
|
}
|
||
|
|
||
|
/* invert matrix and calculate determinant using LAPACK */
|
||
|
void invert(double** cov, int L, double** icov, double* detcov)
|
||
|
{
|
||
|
/* copy square matrix into a vector in column-major order */
|
||
|
double* a = (double*) malloc(L*L*sizeof(double));
|
||
|
int i, j;
|
||
|
for(j=0; j < L; j++)
|
||
|
for (i=0; i < L; i++)
|
||
|
a[j*L+i] = cov[i][j];
|
||
|
|
||
|
int M = (int) L;
|
||
|
int* ipiv = (int *) malloc(L*L*sizeof(int));
|
||
|
int ret;
|
||
|
|
||
|
/* LU decomposition */
|
||
|
ret = dgetrf_(&M, &M, a, &M, ipiv, &ret); /* ret should be zero, negative if cov is singular */
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
fprintf(stderr, "Covariance matrix was singular, couldn't invert\n");
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
/* find determinant */
|
||
|
double det = 1;
|
||
|
for(i = 0; i < L; i++)
|
||
|
det *= a[i*L+i];
|
||
|
// TODO: get this to work!!! If detcov < 0 then cov is bad anyway...
|
||
|
/*
|
||
|
int sign = 1;
|
||
|
for (i = 0; i < L; i++)
|
||
|
if (ipiv[i] != i)
|
||
|
sign = -sign;
|
||
|
det *= sign;
|
||
|
*/
|
||
|
if (det < 0)
|
||
|
det = -det;
|
||
|
*detcov = det;
|
||
|
|
||
|
/* allocate required working storage */
|
||
|
#ifndef HAVE_ATLAS
|
||
|
int lwork = -1;
|
||
|
double lwbest = 0.0;
|
||
|
dgetri_(&M, a, &M, ipiv, &lwbest, &lwork, &ret);
|
||
|
lwork = (int) lwbest;
|
||
|
double* work = (double*) malloc(lwork*sizeof(double));
|
||
|
#endif
|
||
|
|
||
|
/* find inverse */
|
||
|
dgetri_(&M, a, &M, ipiv, work, &lwork, &ret);
|
||
|
|
||
|
for(j=0; j < L; j++)
|
||
|
for (i=0; i < L; i++)
|
||
|
icov[i][j] = a[j*L+i];
|
||
|
|
||
|
#ifndef HAVE_ATLAS
|
||
|
free(work);
|
||
|
#endif
|
||
|
free(a);
|
||
|
}
|
||
|
|
||
|
/* probability of multivariate Gaussian given mean, inverse and determinant of covariance */
|
||
|
double gauss(double* x, int L, double* mu, double** icov, double detcov, double* y, double* z)
|
||
|
{
|
||
|
int i, j;
|
||
|
double s = 0;
|
||
|
for (i = 0; i < L; i++)
|
||
|
y[i] = x[i] - mu[i];
|
||
|
for (i = 0; i < L; i++)
|
||
|
{
|
||
|
//z[i] = 0;
|
||
|
//for (j = 0; j < L; j++)
|
||
|
// z[i] += icov[i][j] * y[j];
|
||
|
z[i] = cblas_ddot(L, &icov[i][0], 1, y, 1);
|
||
|
}
|
||
|
s = cblas_ddot(L, z, 1, y, 1);
|
||
|
//for (i = 0; i < L; i++)
|
||
|
// s += z[i] * y[i];
|
||
|
|
||
|
return exp(-s/2.0) / (pow(2*PI, L/2.0) * sqrt(detcov));
|
||
|
}
|
||
|
|
||
|
/* log probability of multivariate Gaussian given mean, inverse and determinant of covariance */
|
||
|
double loggauss(double* x, int L, double* mu, double** icov, double detcov, double* y, double* z)
|
||
|
{
|
||
|
int i, j;
|
||
|
double s = 0;
|
||
|
double ret;
|
||
|
for (i = 0; i < L; i++)
|
||
|
y[i] = x[i] - mu[i];
|
||
|
for (i = 0; i < L; i++)
|
||
|
{
|
||
|
//z[i] = 0;
|
||
|
//for (j = 0; j < L; j++)
|
||
|
// z[i] += icov[i][j] * y[j];
|
||
|
z[i] = cblas_ddot(L, &icov[i][0], 1, y, 1);
|
||
|
}
|
||
|
s = cblas_ddot(L, z, 1, y, 1);
|
||
|
//for (i = 0; i < L; i++)
|
||
|
// s += z[i] * y[i];
|
||
|
|
||
|
ret = -0.5 * (s + L * log(2*PI) + log(detcov));
|
||
|
|
||
|
/*
|
||
|
// TEST
|
||
|
if (ISINF(ret) > 0)
|
||
|
printf("loggauss returning infinity\n");
|
||
|
if (ISINF(ret) < 0)
|
||
|
printf("loggauss returning -infinity\n");
|
||
|
if (ISNAN(ret))
|
||
|
printf("loggauss returning nan\n");
|
||
|
*/
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void hmm_print(model_t* model)
|
||
|
{
|
||
|
int i, j;
|
||
|
printf("p0:\n");
|
||
|
for (i = 0; i < model->N; i++)
|
||
|
printf("%f ", model->p0[i]);
|
||
|
printf("\n\n");
|
||
|
printf("a:\n");
|
||
|
for (i = 0; i < model->N; i++)
|
||
|
{
|
||
|
for (j = 0; j < model->N; j++)
|
||
|
printf("%f ", model->a[i][j]);
|
||
|
printf("\n");
|
||
|
}
|
||
|
printf("\n\n");
|
||
|
printf("mu:\n");
|
||
|
for (i = 0; i < model->N; i++)
|
||
|
{
|
||
|
for (j = 0; j < model->L; j++)
|
||
|
printf("%f ", model->mu[i][j]);
|
||
|
printf("\n");
|
||
|
}
|
||
|
printf("\n\n");
|
||
|
printf("cov:\n");
|
||
|
for (i = 0; i < model->L; i++)
|
||
|
{
|
||
|
for (j = 0; j < model->L; j++)
|
||
|
printf("%f ", model->cov[i][j]);
|
||
|
printf("\n");
|
||
|
}
|
||
|
printf("\n\n");
|
||
|
}
|
||
|
|
||
|
|