forked from blackstonep/Numerical-Recipes
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhmm.h
executable file
·116 lines (115 loc) · 3.21 KB
/
hmm.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
struct HMM {
MatDoub a, b;
VecInt obs;
Int fbdone;
Int mstat, nobs, ksym;
Int lrnrm;
MatDoub alpha, beta, pstate;
VecInt arnrm, brnrm;
Doub BIG, BIGI, lhood;
HMM(MatDoub_I &aa, MatDoub_I &bb, VecInt_I &obs);
void forwardbackward();
void baumwelch();
Doub loglikelihood() {return log(lhood)+lrnrm*log(BIGI);}
};
HMM::HMM(MatDoub_I &aa, MatDoub_I &bb, VecInt_I &obss) :
a(aa), b(bb), obs(obss), fbdone(0),
mstat(a.nrows()), nobs(obs.size()), ksym(b.ncols()),
alpha(nobs,mstat), beta(nobs,mstat), pstate(nobs,mstat),
arnrm(nobs), brnrm(nobs), BIG(1.e20), BIGI(1./BIG) {
Int i,j,k;
Doub sum;
if (a.ncols() != mstat) throw("transition matrix not square");
if (b.nrows() != mstat) throw("symbol prob matrix wrong size");
for (i=0; i<nobs; i++) {
if (obs[i] < 0 || obs[i] >= ksym) throw("bad data in obs");
}
for (i=0; i<mstat; i++) {
sum = 0.;
for (j=0; j<mstat; j++) sum += a[i][j];
if (abs(sum - 1.) > 0.01) throw("transition matrix not normalized");
for (j=0; j<mstat; j++) a[i][j] /= sum;
}
for (i=0; i<mstat; i++) {
sum = 0.;
for (k=0; k<ksym; k++) sum += b[i][k];
if (abs(sum - 1.) > 0.01) throw("symbol prob matrix not normalized");
for (k=0; k<ksym; k++) b[i][k] /= sum;
}
}
void HMM::forwardbackward() {
Int i,j,t;
Doub sum,asum,bsum;
for (i=0; i<mstat; i++) alpha[0][i] = b[i][obs[0]];
arnrm[0] = 0;
for (t=1; t<nobs; t++) {
asum = 0;
for (j=0; j<mstat; j++) {
sum = 0.;
for (i=0; i<mstat; i++) sum += alpha[t-1][i]*a[i][j]*b[j][obs[t]];
alpha[t][j] = sum;
asum += sum;
}
arnrm[t] = arnrm[t-1];
if (asum < BIGI) {
++arnrm[t];
for (j=0; j<mstat; j++) alpha[t][j] *= BIG;
}
}
for (i=0; i<mstat; i++) beta[nobs-1][i] = 1.;
brnrm[nobs-1] = 0;
for (t=nobs-2; t>=0; t--) {
bsum = 0.;
for (i=0; i<mstat; i++) {
sum = 0.;
for (j=0; j<mstat; j++) sum += a[i][j]*b[j][obs[t+1]]*beta[t+1][j];
beta[t][i] = sum;
bsum += sum;
}
brnrm[t] = brnrm[t+1];
if (bsum < BIGI) {
++brnrm[t];
for (j=0; j<mstat; j++) beta[t][j] *= BIG;
}
}
lhood = 0.;
for (i=0; i<mstat; i++) lhood += alpha[0][i]*beta[0][i];
lrnrm = arnrm[0] + brnrm[0];
while (lhood < BIGI) {lhood *= BIG; lrnrm++;}
for (t=0; t<nobs; t++) {
sum = 0.;
for (i=0; i<mstat; i++) sum += (pstate[t][i] = alpha[t][i]*beta[t][i]);
// sum = lhood*pow(BIGI, lrnrm - arnrm[t] - brnrm[t]);
for (i=0; i<mstat; i++) pstate[t][i] /= sum;
}
fbdone = 1;
}
void HMM::baumwelch() {
Int i,j,k,t;
Doub num,denom,term;
MatDoub bnew(mstat,ksym);
Doub powtab[10];
for (i=0; i<10; i++) powtab[i] = pow(BIGI,i-6);
if (fbdone != 1) throw("must do forwardbackward first");
for (i=0; i<mstat; i++) {
denom = 0.;
for (k=0; k<ksym; k++) bnew[i][k] = 0.;
for (t=0; t<nobs-1; t++) {
term = (alpha[t][i]*beta[t][i]/lhood)
* powtab[arnrm[t] + brnrm[t] - lrnrm + 6];
denom += term;
bnew[i][obs[t]] += term;
}
for (j=0; j<mstat; j++) {
num = 0.;
for (t=0; t<nobs-1; t++) {
num += alpha[t][i]*b[j][obs[t+1]]*beta[t+1][j]
* powtab[arnrm[t] + brnrm[t+1] - lrnrm + 6]/lhood;
}
a[i][j] *= (num/denom);
}
for (k=0; k<ksym; k++) bnew[i][k] /= denom;
}
b = bnew;
fbdone = 0;
}