File: fff_glm_twolevel.c

package info (click to toggle)
nipy 0.6.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 7,352 kB
  • sloc: python: 39,115; ansic: 30,931; makefile: 210; sh: 93
file content (153 lines) | stat: -rw-r--r-- 3,668 bytes parent folder | download
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "fff_glm_twolevel.h"
#include "fff_base.h"
#include "fff_blas.h"

#include <stdlib.h>
#include <math.h>
#include <stdlib.h>

/*
  b, s2 are initialized using the values passed to the function.

  The function requires the projected pseudo-inverse matrix PpiX to be
  pre-calculated externally. It is defined by:

  PpiX = P * (X'X)^-1 X'
  where:

  P = Ip - A C' (C A C')^-1 C  with A = (X'X)^-1

  is the appropriate projector onto the constaint space, Cb=0. P is,
  in fact, orthogonal for the dot product defined by X'X.

  PpiX is p x n. The equality PpiX*X=P is not checked.
*/


fff_glm_twolevel_EM* fff_glm_twolevel_EM_new(size_t n, size_t p)
{
  fff_glm_twolevel_EM* thisone;

  thisone = (fff_glm_twolevel_EM*)malloc(sizeof(fff_glm_twolevel_EM));

  if (thisone==NULL)
    return NULL;

  thisone->n = n;
  thisone->p = p;
  thisone->s2 = FFF_POSINF;

  thisone->b = fff_vector_new(p);
  thisone->z = fff_vector_new(n);
  thisone->vz = fff_vector_new(n);
  thisone->Qz = fff_vector_new(n);

  return thisone;
}

void fff_glm_twolevel_EM_delete(fff_glm_twolevel_EM* thisone)
{
  if (thisone==NULL)
    return;
  fff_vector_delete(thisone->b);
  fff_vector_delete(thisone->z);
  fff_vector_delete(thisone->vz);
  fff_vector_delete(thisone->Qz);
  free(thisone);
}


void fff_glm_twolevel_EM_init(fff_glm_twolevel_EM* em)
{
  fff_vector_set_all(em->b, 0.0);
  em->s2 = FFF_POSINF;
  return;
}


void fff_glm_twolevel_EM_run(fff_glm_twolevel_EM* em, const fff_vector* y, const fff_vector* vy,
			     const fff_matrix* X, const fff_matrix* PpiX, unsigned int niter)
{
  unsigned int iter = 0;
  size_t n=X->size1, i;
  double *yi, *zi, *vyi, *vzi;
  double w1, w2;
  double m = 0.0;


  while (iter < niter) {

    /*** E step ***/

    /* Compute current prediction estimate: z = X*b */
    fff_blas_dgemv(CblasNoTrans, 1.0, X, em->b, 0.0, em->z);

    /* Posterior mean and variance of each "true" effect:
         vz = 1/(1/vy + 1/s2)
	 z =  vz * (y/vy + X*b/s2) */
    w2 = FFF_ENSURE_POSITIVE(em->s2);
    w2 = 1/w2;
    for(i=0, yi=y->data, zi=em->z->data, vyi=vy->data, vzi=em->vz->data;
	 i<n;
	 i++, yi+=y->stride, zi+=em->z->stride, vyi+=vy->stride, vzi+=em->vz->stride) {
      w1 = FFF_ENSURE_POSITIVE(*vyi);
      w1 = 1/w1;
      *vzi = 1/(w1+w2);
      *zi = *vzi * (w1*(*yi) + w2*(*zi));
    }

    /*** M step ***/

    /* Update effect: b = PpiX * z */
    fff_blas_dgemv(CblasNoTrans, 1.0, PpiX, em->z, 0.0, em->b);

    /* Update variance: s2 = (1/n) [ sum((z-Xb).^2) + sum(vz) ] */
    fff_vector_memcpy(em->Qz, em->z);
    fff_blas_dgemv(CblasNoTrans, 1.0, X, em->b, -1.0, em->Qz); /* Qz= Xb-z = Proj_X(z) - z */
    em->s2 = (fff_vector_ssd(em->Qz, &m, 1) + fff_vector_sum(em->vz)) / (long double)n;

    /*** Increment iteration number ***/
    iter ++;
  }

  return;
}


/*
   Log-likelihood computation.

   ri = y - Xb
   -2 LL = n log(2pi) + \sum_i log (s^2 + si^2) + \sum_i ri^2/(s^2 + si^2)

   We omit the nlog(2pi) term as it is constant.
*/
double fff_glm_twolevel_log_likelihood(const fff_vector* y,
				       const fff_vector* vy,
				       const fff_matrix* X,
				       const fff_vector* b,
				       double s2,
				       fff_vector* tmp)
{
  double LL = 0.0, w;
  size_t n=X->size1, i;
  double *ri, *vyi;

  /* Compute residuals: tmp = y - X b */
  fff_vector_memcpy(tmp, y);
  fff_blas_dgemv(CblasNoTrans, -1.0, X, b, 1.0, tmp);

  /* Incremental computation */
  for(i=0, ri=tmp->data, vyi=vy->data; i<n; i++, ri+=tmp->stride, vyi+=vy->stride) {
    w = *vyi + s2;
    w = FFF_ENSURE_POSITIVE(w);
    LL += log(w);
    LL += FFF_SQR(*ri)/w;

  }

  /* Finalize computation */
  LL *= -0.5;

  return LL;
}