1 /*
2  *  gretl -- Gnu Regression, Econometrics and Time-series Library
3  *  Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include "libgretl.h"
21 #include "libset.h"
22 #include "bhhh_max.h"
23 
24 #define BHHH_DEBUG 0
25 
26 /* just experimental at this point: call @func to compute the
27    per-observation contributions to the log-likelihood in @L,
28    and use that info to compute @G numerically.
29 */
30 
bhhh_numeric(double * b,int k,gretl_matrix * L,gretl_matrix * G,BHHH_FUNC func,void * data,int * err)31 static double bhhh_numeric (double *b, int k,
32 			    gretl_matrix *L,
33 			    gretl_matrix *G,
34 			    BHHH_FUNC func, void *data,
35 			    int *err)
36 {
37     double * restrict Lval = L->val;
38     const double h = 1.0e-8;
39     double gti, ret;
40     int i, t, T = G->rows;
41 
42     /* loglik at current evaluation point */
43     ret = func(b, L, data, 0, err);
44 
45     for (i=0; i<k && !*err; i++) {
46 	double ll, bi0 = b[i];
47 
48 	b[i] = bi0 - h;
49 	ll = func(b, L, data, 0, err);
50 
51 	if (na(ll)) {
52 	    ret = NADBL;
53 	    break;
54 	}
55 
56 	for (t=0; t<T; t++) {
57 	    gretl_matrix_set(G, t, i, Lval[t]);
58 	}
59 
60 	b[i] = bi0 + h;
61 	ll = func(b, L, data, 0, err);
62 
63 	b[i] = bi0;
64 
65 	if (na(ll)) {
66 	    ret = NADBL;
67 	    break;
68 	}
69 
70 	for (t=0; t<T; t++) {
71 	    gti = gretl_matrix_get(G, t, i);
72 	    gti = (Lval[t] - gti) / (2.0 * h);
73 	    gretl_matrix_set(G, t, i, gti);
74 	}
75     }
76 
77     return ret;
78 }
79 
80 /* in case we want to compute the score numerically */
81 
make_score_matrix(gretl_matrix * L,int k,int * err)82 static gretl_matrix *make_score_matrix (gretl_matrix *L, int k, int *err)
83 {
84     int T = gretl_matrix_rows(L);
85     gretl_matrix *G = NULL;
86 
87     if (T == 0) {
88 	*err = E_DATA;
89     } else if (T <= k) {
90 	*err = E_DF;
91     } else {
92 	G = gretl_zero_matrix_new(T, k);
93 	if (G == NULL) {
94 	    *err = E_ALLOC;
95 	}
96     }
97 
98     return G;
99 }
100 
101 /**
102  * bhhh_max:
103  * @theta: array of adjustable coefficients.
104  * @k: number of elements in @theta.
105  * @M: T x @k matrix to hold the gradient.
106  * @callback: pointer to function for calculating log-likelihood and
107  * score.
108  * @toler: tolerance for convergence.
109  * @fncount: location to receive count of function evaluations.
110  * @grcount: location to receive count of gradient evaluations.
111  * @data: pointer to be passed to @loglik.
112  * @V: matrix in which to store covariance, or %NULL.
113  * @opt: can include %OPT_V for verbose output.
114  * @prn: printing struct for iteration info (or %NULL).
115  *
116  * Maximize likelihood using the BHHH method, implemented via
117  * iteration of the Outer Product of the Gradient (OPG) regression
118  * with line search.
119  *
120  * @callback is called to calculate the loglikelihood for the model
121  * in question.  The parameters passed to this function are:
122  * (1) the current array of estimated coefficients; (2) @G;
123  * (3) the @data pointer; (4) an integer indicator that is 1 if
124  * the gradient should be calculated in @G, 0 if only the
125  * loglikelihood is needed; and (5) an int pointer to receive
126  * an error code. The return value from @callback should be the
127  * loglikelihood (or #NADBL on error).
128  *
129  * For an example of the use of such a function, see arma.c in the
130  * %plugin directory of the gretl source.
131  *
132  * Returns: 0 on successful completion, non-zero error code otherwise.
133  */
134 
bhhh_max(double * theta,int k,gretl_matrix * M,BHHH_FUNC callback,double toler,int * fncount,int * grcount,void * data,gretl_matrix * V,gretlopt opt,PRN * prn)135 int bhhh_max (double *theta, int k,
136 	      gretl_matrix *M,
137 	      BHHH_FUNC callback,
138 	      double toler,
139 	      int *fncount, int *grcount,
140 	      void *data,
141 	      gretl_matrix *V,
142 	      gretlopt opt,
143 	      PRN *prn)
144 {
145     gretl_matrix *c = NULL;
146     gretl_matrix *g = NULL;
147     gretl_matrix *G = NULL;
148     double * restrict delta = NULL;
149     double * restrict ctemp = NULL;
150     double * restrict grad;
151     int itermax, iter = 0;
152     int fcount = 0;
153     int gcount = 0;
154     double minstep = 1.0e-06;
155     double crit = 1.0;
156     double stepsize = 0.25;
157     double shrink = 0.5;
158     double ll2, ll = 0.0;
159     int numeric = 0;
160     int i, T, err = 0;
161 
162     if (opt & OPT_N) {
163 	/* numerical score (not ready) */
164 	G = make_score_matrix(M, k, &err);
165 	if (err) {
166 	    return err;
167 	}
168 	numeric = 1;
169     } else {
170 	/* analytical score */
171 	if (gretl_matrix_cols(M) != k) {
172 	    return E_NONCONF;
173 	} else {
174 	    G = M;
175 	}
176     }
177 
178     T = gretl_matrix_rows(G);
179     c = gretl_unit_matrix_new(T, 1);
180     g = gretl_column_vector_alloc(k);
181     delta = malloc(k * sizeof *delta);
182     ctemp = malloc(k * sizeof *ctemp);
183 
184     if (c == NULL || g == NULL || delta == NULL || ctemp == NULL) {
185 	err = E_ALLOC;
186 	goto bailout;
187     }
188 
189     if (opt & OPT_I) {
190 	/* using BHHH for initialization of exact ML */
191 	minstep = 1.0e-03;
192 	shrink = 0.4;
193     }
194 
195     itermax = libset_get_int(BHHH_MAXITER);
196     grad = g->val;
197 
198     while (crit > toler && iter++ < itermax) {
199 	/* compute loglikelihood and score */
200 	if (numeric) {
201 	    ll = bhhh_numeric(theta, k, M, G, callback, data, &err);
202 	} else {
203 	    ll = callback(theta, G, data, 1, &err);
204 	}
205 	fcount++;
206 	gcount++;
207 
208 	if (err) {
209 	    pputs(prn, "Error calculating log-likelihood\n");
210 	    break;
211 	}
212 
213 #if BHHH_DEBUG > 1
214 	gretl_matrix_print(G, "RHS in OPG regression");
215 #endif
216 	/* BHHH via OPG regression */
217 	err = gretl_matrix_ols(c, G, g, NULL, NULL, NULL);
218 	if (err) {
219 	    fprintf(stderr, "BHHH OLS error code = %d\n", err);
220 	    break;
221 	}
222 	for (i=0; i<k; i++) {
223 	    delta[i] = grad[i] * stepsize;
224 	    ctemp[i] = theta[i] + delta[i];
225 	}
226 
227 	/* see if we've gone up...  (0 means don't compute score) */
228 	ll2 = callback(ctemp, G, data, 0, &err);
229 	fcount++;
230 #if BHHH_DEBUG
231 	fprintf(stderr, "bhhh loop: initial ll2 = %#.14g\n", ll2);
232 #endif
233 	while (err || ll2 < ll) {
234 	    /* ... or if not, halve the steplength */
235 	    stepsize *= shrink;
236 	    if (stepsize < minstep) {
237 		fprintf(stderr, "BHHH: hit minimum step size %g\n", minstep);
238 		err = E_NOCONV;
239 		break;
240 	    }
241 	    for (i=0; i<k; i++) {
242 		delta[i] *= shrink;
243 		ctemp[i] = theta[i] + delta[i];
244 	    }
245 	    ll2 = callback(ctemp, G, data, 0, &err);
246 	    fcount++;
247 #if BHHH_DEBUG
248 	    fprintf(stderr, "bhhh loop: modified ll2 = %#.14g\n", ll2);
249 #endif
250 	}
251 
252 	if (err) break;
253 
254 	/* print iteration info, if wanted */
255 	if (opt & OPT_V) {
256 	    print_iter_info(iter, ll, C_LOGLIK, k, theta, grad,
257 			    stepsize, prn);
258 	}
259 
260 	/* actually update parameter estimates */
261 	for (i=0; i<k; i++) {
262 	    theta[i] = ctemp[i];
263 	}
264 	/* double the steplength? (was < 4.0 below) */
265 	if (stepsize < 1.0) {
266 	    stepsize *= 2.0;
267 	}
268 	crit = ll2 - ll;
269     }
270 
271     *fncount = fcount;
272     *grcount = gcount;
273 
274     if ((opt & OPT_V) && !(opt & OPT_I)) {
275 	print_iter_info(-1, ll, C_LOGLIK, k, theta, grad,
276 			stepsize, prn);
277     }
278 
279     if (crit > toler && !err) {
280 	err = E_NOCONV;
281     }
282 
283     if (err) {
284 	fprintf(stderr, "bhhh_max: iters = %d, crit = %g, tol = %g, err = %d\n",
285 		iter, crit, toler, err);
286     } else if (V != NULL) {
287 	/* run OPG once more, to get VCV */
288 	double s2 = 0.0;
289 
290 	err = gretl_matrix_ols(c, G, g, V, NULL, &s2);
291 	if (!err) {
292 	    /* undo the default df correction */
293 	    gretl_matrix_multiply_by_scalar(V, (T-k) / (double) T);
294 	}
295     }
296 
297  bailout:
298 
299     gretl_matrix_free(c);
300     gretl_matrix_free(g);
301     if (G != M) {
302 	gretl_matrix_free(G);
303     }
304     free(delta);
305     free(ctemp);
306 
307     return err;
308 }
309