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