root/projects/AsynCluster/trunk/svpmc/model.c

Revision 207, 18.1 kB (checked in by edsuom, 6 months ago)

Got Rao-Blackwellized version working locally & remotely, with better but still disappointing degeneracy

Line 
1 /* svPMC: Stochastic Volatility Inference via Population Monte Carlo
2   
3    Copyright (C) 2007-2008 by Edwin A. Suominen, http://www.eepatents.com
4   
5    This program is free software; you can redistribute it and/or modify it under
6    the terms of the GNU General Public License as published by the Free Software
7    Foundation; either version 2 of the License, or (at your option) any later
8    version.
9   
10    This program is distributed in the hope that it will be useful, but WITHOUT
11    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12    FOR A PARTICULAR PURPOSE.  See the file COPYING for more details.
13   
14    You should have received a copy of the GNU General Public License along with
15    this program; if not, write to the Free Software Foundation, Inc., 51
16    Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17
18 */
19
20
21 // VAR.reverse
22 //
23 // Supplied variables
24 // ----------------------------------------------------------------------------
25 // v    Innovation values (initially empty), [pn] array
26 // y    Observation values, [pn] array
27 // a    Drift, [p] vector
28 // b    Lag-1 cross-correlation [pp] array
29
30 int i, j, k;
31 double sum;
32
33 // The first innovation for each time series is just the first observation
34 // minus the drift
35 for(i=0; i<Ny[0]; i++)
36   V2(i,0) = Y2(i,0) - A1(i);
37 // For each observation after the first...
38 for(j=1; j<Ny[1]; j++) {
39   // For each time series...
40   for(i=0; i<Ny[0]; i++) {
41     // The current observation minus the drift...
42     sum = Y2(i,j) - A1(i);
43     // ...minus the dot product for the VAR(1) term...
44     for(k=0; k<Ny[0]; k++)
45       sum -= B2(i,k) * Y2(k,j-1);
46     // ...is the innovation that yielded this observation
47     V2(i,j) = sum;
48   }
49 }
50
51
52
53 // VAR.forward
54 //
55 // Supplied variables
56 // ----------------------------------------------------------------------------
57 // y    Modeled values (initially empty), [pn] array
58 // v    Innovation values, [pn] array
59 // a    Drift, [p] vector
60 // b    Lag-1 cross-correlation [pp] array
61
62 int i, j, k;
63 double sum;
64
65 // The first modeled value for each time series is just the first innovation
66 // plus the drift
67 for(i=0; i<Nv[0]; i++)
68   Y2(i,0) = V2(i,0) + A1(i);
69 // For each innovation after the first...
70 for(j=1; j<Nv[1]; j++) {
71   // For each time series...
72   for(i=0; i<Nv[0]; i++) {
73     // The drift plus the current innovation...
74     sum = A1(i) + V2(i,j);
75     // ...plus the dot product for the VAR(1) term...
76     for(k=0; k<Nv[0]; k++)
77       sum += B2(i,k) * Y2(k,j-1);
78     // ...is the modeled output
79     Y2(i,j) = sum;
80   }
81 }
82
83
84
85 // Model.support
86 // ----------------------------------------------------------------------------
87
88
89 // Model parameters
90 struct parameters
91 {
92   //--- Model parameters, direct ---
93   PyArrayObject* d;  // Multivariate log-volatility offset
94   PyArrayObject* e;  // VAR(1) coefficient, volatility shock to log-volatility
95   PyArrayObject* g;  // Correlation, multivariate normal z vs. multivariate
96                      // normal for innovation shocks
97
98   //--- Model parameters, derivations ---
99   PyArrayObject* rz; // Cross-correlation matrix, multivariate normal z to
100                      // volatility shock v
101   PyArrayObject* ri; // Inverted cross-correlation matrix, innovation shocks
102   PyArrayObject* sc; // Scaling constants for multivariate normal pdf, given g
103 };
104
105
106 // An evaluation sample
107 struct sample
108 {
109   // Log-likelihood of the time-series sample in the current evaluation, given
110   // h at this point
111   double Lxk;
112   // Log-likelihood of the innovations up to the current one, given the history
113   // of h to this point
114   double Lx;
115   // Multivariate normal proposal, which is just z for the current evaluation,
116   // and for all time-series samples after the first in each log-volatility
117   // computation
118   double *z;
119   // Multivariate log-volatility value based on the current evaluation, or the
120   // previous proposal to use this struct if the current proposal hasn't been
121   // crunched yet
122   double *h;
123   // Scratchpad 1-D arrays of the same length as z, h
124   double *x0, *x1;
125 };
126
127
128 // Fast approximate exponent function EXP
129 //
130 // From N. Schraudolph, "A Fast, Compact Approximation of the Exponential
131 // Function," Neural Computation 11, 853-862 (1999)
132 static union {
133   double d;
134   struct {
135 #ifdef LITTLE_ENDIAN
136     int j, i;
137 #else
138     int i, j;
139 #endif
140   } n;
141 } _eco;
142 #define EXP_A (1048576/M_LN2)
143 #define EXP_C 60801
144
145 #define EXP(y) (_eco.n.i = EXP_A*(y) + (1072693248 - EXP_C), _eco.d)
146  
147
148 // Uniformly distributed random variate in [a,b)
149 double uniform(double a, double b)
150 {
151   // Seed the PRNG on the first run only
152   static int seeded = 0;
153   if (!seeded) {
154     int k;
155     unsigned short seed[3];
156     long int timeval = time(0);
157     for(k=0; k<3; k++) {
158       seed[k] = (unsigned short) timeval;
159       timeval = timeval >> 16;
160     }
161     seed48(seed);
162     seeded = 1;
163   }
164   // Compute and return a variate from U(a,b)
165   return (b - a) * drand48() + a;
166 }
167
168
169 // Log probability density of the unit normal distribution
170 double normLogDensity(double x)
171 {
172   static double logC = -log(2 * M_PI);
173   return -0.5*x*x + logC;
174 }
175
176
177 // Random variate from a truncated normal distribution
178 double truncnorm(double a, double b)
179 {
180   int k;
181   register double x;
182   double c, cp;
183   const double diff = b-a;
184   // Compute the scale value c as the maximum possible pdf (regular normal
185   // distribution) within the truncated range
186   if(a < 0) {
187     if(b > 0) {
188       // Center is within the range, so use the maximum pdf at center
189       c = 1.02; // Higher than 1.0 due to max EXP +error of 1.02
190     } else {
191       // Range is below center, so use maximum pdf at its right edge
192       c = 1.0625 * EXP(-0.5*b*b); // Scaled up by 1.02 / 0.96
193     }
194   } else {
195     // Range is above center, so use pdf at its left edge
196     c = 1.0625 * EXP(-0.5*a*a); // Scaled up by 1.02 / 0.96
197   }
198   // Accept-reject sampling for the truncated normal distribution
199   do {
200     // Generate a proposal within the truncated range
201     x = a + diff*drand48();
202     // Compute the pdf of the proposal, using the regular normal distribution
203     cp = EXP(-0.5*x*x);
204     // Accept (and exit the loop) when the scaled uniform variate is less than
205     // the proposal's pdf
206   } while(c*drand48() > cp);
207   return x;
208 }
209
210
211 // Matrix multiplication x*y -> z, where the square matrix 'x' is a
212 // PyArrayObject and 'y' and 'z' are 1-D arrays.
213 void matrixMultiply(PyArrayObject* x, double *y, double *z)
214 {
215   int kr, km;
216   register double sum;
217   const int rows = (int) x->dimensions[0];
218   // Compute the dot product for each row of x
219   for(kr=0; kr<rows; kr++) {
220     // For this row of y and z...
221     sum = 0;
222     // Matrix multiplication dot product
223     for(km=0; km<rows; km++)
224       sum += PP2(x, kr, km) * PA1(y, km);
225     PA1(z, kr) = sum;
226   }
227 }
228
229
230 // Computes the log-volatility, given a proposal on z, the previously computed
231 // log-volatility value previous to the sample, and the model parameters.
232 void logVol(struct sample *S, struct parameters *P)
233 {
234   int k;
235   const int N = (int) P->d->dimensions[0];
236
237   // Run the VAR(1) process to get the current multivariate log-volatility
238   // value
239
240   // Start with the matrix product for the VAR(1) term...
241   matrixMultiply(P->e, S->h, S->x0);
242   // ...then compute the multivariate volatility shock given the proposed
243   // normal variate...
244   matrixMultiply(P->rz, S->z, S->x1);
245   // ...then add them, plus the multivariate volatility offset, to be the new h
246   for(k=0; k<N; k++)
247     SPA1(S, h, k) = SPA1(S, x0, k) + SPA1(S, x1, k) + SPP1(P, d, k);
248 }
249
250
251 // Computes the log-likelihood of a multivariate innovation x given a 1-D array
252 // containing a multivariate log-volatility.
253 void logLikelihood(struct sample *S, struct parameters *P, double *x)
254 {
255   register int k;
256   double val;
257   const int N = (int) P->g->dimensions[0];
258  
259   // Inverse-scale the innovation by the square root of the volatilities in
260   // preparation for decorrelation
261
262   for(k=0; k<N; k++)
263     // We could use a stripped-down version of exp here for speed; doing it
264     // with the stdlib exp function takes about 70 CPU cycles (20 nS) on an
265     // Intel QX9760 running at 3.8 GHz, though some of that is the unavoidable
266     // array lookups and products in the equation.
267     //
268     SPA1(S, x0, k) = exp(-0.5 * SPA1(S, h, k)) * PA1(x, k);
269     // Using EXP is very fast, but has error of about +/-6%. In a multivariate
270     // test, the resulting error was -1112.29 vs an expected -1135.15.
271     //SPA1(S, x0, k) = EXP(-0.5 * SPA1(S, h, k)) * PA1(x, k);
272
273   // Now decorrelate the equi-variance innovations in preparation for the
274   // log-likelihood computation for this multivariate log-volatility proposal
275   matrixMultiply(P->ri, S->x0, S->x1);
276  
277   // Now compute log Pr(x[t]|h[t]), the log-likelihood of the decorrelated
278   // multivariate innovation for this sample, conditional on the sample's
279   // log-volatility.
280
281   S->Lxk = 0;
282
283   // For each time series...
284   for(k=0; k<N; k++) {
285     // Offset the decorrelated innovation by the mean, which is the correlation
286     // between the normal variates for innovation and proposed volatility
287     // shocks, times the value of the normal variate value underlying the
288     // volatility shock for this sample
289     SPA1(S, x0, k) = SPA1(S, x1, k) - SPP1(P, g, k) * SPA1(S, z, k);
290     // The log-likelihood for this sample is simply the argument of the
291     // exponential of the normal pdf, with the reciprocal scaling of the
292     // exponential being done in log space by subtraction of the log of the
293     // scaling coefficient
294     val = SPP2(P, sc, k, 0) * SPA1(S, x0, k);
295     // Fast squaring instead of pow()
296     S->Lxk -= 0.5 * val*val + SPP2(P, sc, k, 1);
297     // Since the innovation has been inverse-scaled by the square root of the
298     // proposed volatility, the probability density needs to be scaled as
299     // well. In log space, this is a subtraction of half the log-volatility.
300     S->Lxk -= 0.5 * SPA1(S, h, k);
301   }
302   // Add the current log-likelihood to the accumulated log-likelihoods
303   S->Lx += S->Lxk;
304 }
305
306
307
308 // Model.hybridGibbs
309 //
310 // Supplied variables
311 // ----------------------------------------------------------------------------
312
313 //--- Supplied variables ---
314 // z    Independent normal variates, [pn] array (updated)
315 // x    Innovation values, [pn] array
316 // w    Wiggle value for +/- proposals (float)
317 // ni   Number of hybrid-Gibbs iterations (int)
318
319 //--- Model parameters, direct ---
320 // d    Volatility offset, [p] vector
321 // e    Lag-1 cross-correlations for VAR of volatilities, [pp] array
322 // g    Volatility/innovation shock correlations, [p] vector
323
324 //--- Model parameters, derivations ---
325 // rz   Concurrent xcorr, innovation-volatility normals, [pp]
326 // ri   Inverse concurrent xcorr, innovation shocks, [pp]
327 // sc   Constants for multivariate normal PDF
328
329
330 // Run evaluations until odds of error in selection between current and
331 // proposed z will be less than 1/100.
332 #define MIN_DIFF 0.01
333
334 // The number of time series making up a single multivariate sample
335 const int Nt = Nx[0];
336 // The number of multivariate samples
337 const int Ns = Nx[1];
338
339 int ki, ke;
340 int k0, k1, k2, k3;
341 double val;
342
343 // Multivariate innovation for one current time-series sample
344 double *xk;
345 // The multivariate proposal on z
346 double zp[Nt];
347 // The multivariate value of h for current and proposal evaluations, at the
348 // first time-series sample of the evaluation interval
349 double he[2][Nt];
350
351 // A set of evaluation sample structs
352 struct sample se[2];
353 // A struct for the model parameters
354 struct parameters P;
355
356
357 // Generate parameter struct
358 P.d = d_array; P.e = e_array; P.g = g_array;
359 P.rz = rz_array; P.ri = ri_array; P.sc = sc_array;
360
361 // Initialize various arrays
362 xk = (double *) malloc(sizeof(double) * Nt);
363 for(k0=0; k0<2; k0++) {
364   se[k0].z = (double *) malloc(sizeof(double) * Nt);
365   se[k0].h = (double *) malloc(sizeof(double) * Nt);
366   se[k0].x0 = (double *) malloc(sizeof(double) * Nt);
367   se[k0].x1 = (double *) malloc(sizeof(double) * Nt);
368 }
369
370 // For each iteration...
371 for(ki=0; ki<ni; ki++) {
372
373   // The "previous" log-volatility of the first time-series sample is set to
374   // the log-volatility offset plus a simulated, latent-parameter value.
375   // TODO
376   for(k0=0; k0<2; k0++) {
377     for(k1=0; k1<Nt; k1++)
378       PA1(se[k0].h, k1) = D1(k1);
379   }
380
381   // For each time-series sample...
382   for(k0=0; k0<Ns; k0++) {
383    
384     // Simulate two sets of h samples from the current z and a proposal on z,
385     // with a likelihood evaluation that starts from the current time-series
386     // sample to some subsequent time-series sample at which the log-likelihood
387     // of the innovation for that sample becomes substantially the same for the
388     // current and proposal value of z.
389  
390     k1 = k0;
391  
392     // Clear the log-likelihood and normal log-density accumulators of each
393     // evaluation struct for the next pair of evaluations.
394     for(k2=0; k2<2; k2++)
395       se[k2].Lx = 0;
396    
397     do {
398  
399       // Prepare current-innovation vector xk
400       for(k2=0; k2<Nt; k2++)
401         PA1(xk, k2) = X2(k2, k1);
402      
403       // For each evaluation...
404       for(k2=0; k2<2; k2++) {
405  
406         // Set z...
407         for(k3=0; k3<Nt; k3++) {
408           val = Z2(k3, k1);
409           if(k1==k0) {
410             if(k2==1) {
411               // First time-series sample for the second evaluation, use a
412               // proposal on z instead of z itself
413               val = truncnorm(val-w, val+w);
414               zp[k3] = val;
415             }
416           }
417           PA1(se[k2].z, k3) = val;
418         }
419         // ...compute the multivariate log-volatility
420         logVol(&se[k2], &P);
421         // ...and the log-likelihood of the innovation for this time-series
422         // sample, given the log-volatility
423         logLikelihood(&se[k2], &P, xk);
424        
425         // Save the first log-volatility
426         if(k1==k0) {
427           for(k3=0; k3<Nt; k3++)
428             he[k2][k3] = PA1(se[k2].h, k3);
429         }
430       }
431  
432       // Ready for the next time series sample, assuming there is one
433       k1++;
434
435       // Continue only if difference in log-likelihood of the current
436       // time-series sample of the evaluations between current and proposed z
437       // is great enough to warrant doing so.
438       val = se[0].Lxk - se[1].Lxk;
439    
440     } while (k1<Ns && (val > MIN_DIFF || val < -MIN_DIFF));
441
442     // Metropolis-hastings selection of current or proposal
443     val = se[1].Lx  -  se[0].Lx;
444     if(val > 0 || log(uniform(0, 1)) < val) {
445       // Proposal accepted
446       ke = 1;
447       for(k2=0; k2<Nt; k2++)
448         Z2(k2, k0) = zp[k2];
449     } else {
450       // Proposal rejected
451       ke = 0;
452     }
453    
454     // Set the initial log-volatility of the evaluations for the next
455     // time-series sample to the log-volatility of the selected evaluation for
456     // this time-series sample.
457     for(k2=0; k2<Nt; k2++) {
458       val = he[ke][k2];
459       for(k3=0; k3<2; k3++)
460         PA1(se[k3].h, k2) = val;
461     }
462  
463     // Done with log-volatility draw for this time-series sample
464   }
465
466   // Done with iterations
467 }
468
469 // Free array memory
470 free(xk);
471 for(k0=0; k0<2; k0++) {
472     free(se[k0].z);
473     free(se[k0].h);
474     free(se[k0].x0);
475     free(se[k0].x1);
476 }
477
478
479
480 // Model.likelihood
481 //
482 // Supplied variables
483 // ----------------------------------------------------------------------------
484
485 //--- Supplied variables ---
486 // z    Independent normal variates, m previously simulated sets, [mpn] array
487 // x    Innovation values, [pn] array
488
489 //--- Model parameters, direct ---
490 // d    Volatility offset, [p] vector
491 // e    Lag-1 cross-correlations for VAR of volatilities, [pp] array
492 // g    Volatility/innovation shock correlations, [p] vector
493
494 //--- Model parameters, derivations ---
495 // rz   Concurrent xcorr, innovation-volatility normals, [pp]
496 // ri   Inverse concurrent xcorr, innovation shocks, [pp]
497 // sc   Constants for multivariate normal PDF
498
499 //--- Return value (double float) ---
500 // The linear likelihood P(x|w) of the innovations given the parameters,
501 // integrated over the log-volatility simulations along the first axis of z
502
503
504 // The number of time series making up a single multivariate sample
505 const int Nt = Nx[0];
506 // The number of multivariate samples
507 const int Ns = Nx[1];
508
509 int ki;
510 int k0, k1;
511 double val, maxVal;
512
513 // Multivariate innovation for one current time-series sample
514 double *xk;
515 // Log-likelihood of the innovation for the time-series sample in the current
516 // evalution, accumulated for each iteration
517 double Lx[Nz[0]];
518
519 // A single evaluation sample struct
520 struct sample se;
521 // A struct for the model parameters
522 struct parameters P;
523
524
525 // Generate parameter struct
526 P.d = d_array; P.e = e_array; P.g = g_array;
527 P.rz = rz_array; P.ri = ri_array; P.sc = sc_array;
528
529 // Initialize various arrays
530 xk = (double *) malloc(sizeof(double) * Nt);
531 se.z = (double *) malloc(sizeof(double) * Nt);
532 se.h = (double *) malloc(sizeof(double) * Nt);
533 se.x0 = (double *) malloc(sizeof(double) * Nt);
534 se.x1 = (double *) malloc(sizeof(double) * Nt);
535
536
537 // Iterate over each set of simulated z values...
538 for(ki=0; ki<Nz[0]; ki++) {
539
540   // Initialize this iteration's accumulator
541   Lx[ki] = 0;
542
543   // The "previous" log-volatility of the first time-series sample is set to
544   // the log-volatility offset plus a simulated, latent-parameter value.
545   // TODO
546   for(k1=0; k1<Nt; k1++)
547     PA1(se.h, k1) = D1(k1);
548
549   // For each time-series sample...
550   for(k0=0; k0<Ns; k0++) {
551    
552     // For each time series...
553     for(k1=0; k1<Nt; k1++) {
554       // Prepare current-innovation vector xk...
555       PA1(xk, k1) = X2(k1, k0);
556       // ...and set z
557       PA1(se.z, k1) = Z3(ki, k1, k0);
558     }
559    
560     // Compute the multivariate log-volatility...
561     logVol(&se, &P);
562     // ...and the log-likelihood of the innovation for this time-series
563     // sample, given the log-volatility
564     logLikelihood(&se, &P, xk);
565     // Accumulate the log-likelihood
566     Lx[ki] += se.Lxk;
567  
568     // Done with log-likelihood evaluation for this time-series sample
569   }
570
571   // Done with iterations
572 }
573
574 // Free array memory
575 free(xk);
576 free(se.z);
577 free(se.h);
578 free(se.x0);
579 free(se.x1);
580
581
582 // Compute the result, the integrated P(x|w) over all the sets of simulated z
583 // values
584
585 // Compute the maximum log density and save it to subtract from everybody so
586 // that the maximum log density is normalized to zero
587 maxVal = -HUGE_VAL;
588 for(ki=0; ki<Nz[0]; ki++) {
589   if(Lx[ki] > maxVal)
590     maxVal = Lx[ki];
591 }
592 // Accumulate the linearized normalized densities
593 val = 0;
594 for(ki=0; ki<Nz[0]; ki++)
595   val += exp(Lx[ki] - maxVal);
596 // ...and return the denormalized, log mean of the linear densities...
597 return_val = log(val) + maxVal - log(Nz[0]);
Note: See TracBrowser for help on using the browser.