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 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
|
/*
* Copyright (C) 1996 Christopher Sims
* Copyright (C) 2003 Karibzhanov, Waggoner, and Zha
*
* This free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* If you did not received a copy of the GNU General Public License
* with this software, see <http://www.gnu.org/licenses/>.
*/
//======= Revisions by T. Zha.
//======= Fixing bugs: convering all if-else loop. 02/24/05
/*=========================================================
* csminwel.c
*
* Unconstrained minimization. Uses a quasi-Newton method with BFGS update of
* the estimated inverse hessian. It is robust against certain pathologies
* common on likelihood functions. It attempts to be robust against "cliffs",
* i.e. hyperplane discontinuities, though it is not really clear whether what
* it does in such cases succeeds reliably.
*
* function [fhat,xhat,ghat,Hhat,itct,fcount,retcodehat] = csminwelmex(fcn,x0,H0,grad,crit,nit,varargin)
* fcn: string naming the objective function to be minimized
* x0: initial value of the parameter vector
* H0: initial value for the inverse Hessian. Must be positive definite.
* grad: Either a string naming a function that calculates the gradient, or the null matrix.
* If it's null, the program calculates a numerical gradient. In this case fcn must
* be written so that it can take a matrix argument and produce a row vector of values.
* crit: Convergence criterion. Iteration will cease when it proves impossible to improve the
* function value by more than crit.
* nit: Maximum number of iterations.
* varargin: A list of optional length of additional parameters that get handed off to fcn each
* time it is called.
* Note that if the program ends abnormally, it is possible to retrieve the current x,
* f, and H from the files g1.mat and H.mat that are written at each iteration and at each
* hessian update, respectively. (When the routine hits certain kinds of difficulty, it
* write g2.mat and g3.mat as well. If all were written at about the same time, any of them
* may be a decent starting point. One can also start from the one with best function value.)
*
* retcodes: 0, normal step (converged). 1, zero gradient (converged).
* 4,2, back and forth adjustment of stepsize didn't finish.
* 3, smallest stepsize still improves too slow. 5, largest step still improves too fast.
* 6, no improvement found.
*---------------------
* Fixed 7/17/93 to use inverse-hessian instead of hessian itself in bfgs update.
* Fixed 7/19/93 to flip eigenvalues of H to get better performance when it's not psd.
*
* Note: to set the level of display output, change preprocessor definitions at the beginning of this file.
* to display all output, uncomment both VERBOSE_WARNINGS and VERBOSE_DETOUTPUT
* to display only warnings without output, uncomment VERBOSE_WARNINGS
* to display no ouput, comment both VERBOSE_DETOUTPUT and VERBOSE_WARNINGS
*
* MATLAB algorithm by Christopher Sims
* C implementation by Iskander Karibzhanov
* Modified by Dan Waggoner and Tao Zha
*
* Copyright(c) 1996 Christopher Sims
* Copyright(c) 2003 Karibzhanov, Waggoner, and Zha
*=======================================================
* Revision history by T. Zha:
*
* 10/3/2002 - 1. corrected problem with memory corruption in C-MEX-file (csminwelmex.c)
* (needed to switch fcnRhs[0] back to x[0] before destroying it.
* If we don't do this, we will later clear previously destroyed array
* (one of x[1], x[2] or x[3]) which causes memory fault.
* The reason why fcnRhs[0] pointed to array other than x[0] is
* because we use mxSetPr in feval and gfeval.
* This was not a problem in C-file (csminwel.c).
*
* 10/11/2002 - 1. changed csminit function to avoid using fPeak without first initializing it
* 2. added two switches in csminit function to assign retcode to 7 for lambda>=4
* 3. added one more verbose level to display only warnings or all output *
*
* 07/13/2005 - Change #define GRADSTPS_CSMINWEL to double GRADSTPS_CSMINWEL in the .h file so the user can change the value.
* INDXNUMGRAD_CSMINWEL (gradient method) is now defined in optpackage.h.
*
* 03/10/2006 - Iskander's use of randmax=1/RAND_MAX is incorrect. Changed to randmax=1.0/RAND_MAX. Note rand() is in stdlib.h and time() is in time.h.
* - Fatal BUG by Iskander to have eye(n) instead of eye(nn). Corrected by TZ.
*
* 02/07/2011 - Modified code and .h so not to use Zha's matrix and optimization packages.
========================================================*/
#include "dw_csminwel.h"
#include "dw_error.h"
#include "dw_std.h"
#include "dw_rand.h"
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include <float.h>
#define VERBOSE_WARNINGS //Display warnings.
#define VERBOSE_DETOUTPUT //Display detailed output.
#define STRLEN 192
double GRADSTPS_CSMINWEL = 1.0e-04; //Default value. Will be overwritten by the data in the input file if it exists.
//1.0e-04 (for monthly TBVAR)
//Step size for numerical gradient only when the value of x is less than 1.0 in absolute value.
//If abs(x)>1.0, the step size is GRADSTPS_CSMINWEL*x.
// Added by DW to eliminate the need for tzmatlab.
#define TZ_TRUE 1
#define TZ_FALSE 0
#define tzMalloc(n,type) dw_malloc(n*sizeof(type))
#define tzDestroy(buffer) {if (buffer) { dw_free(buffer); (buffer) = NULL; }}
#define tzFclose(file_ptr) {if (file_ptr) { fclose(file_ptr); (file_ptr) = (FILE *)NULL; }}
#define NEARINFINITY 1.0E+300
static double GLB_sclForHess;
static int numgrad(double *g, double *x, int n,
double (*fcn)(double *x, int n, double **args, int *dims),
double **args, int *dims);
static void csminit(double *fhat, double *xhat, int *fcount, int *retcode,
double *x0, double f0, double *g, int badg, double *H0, int n,
double (*fcn)(double *x, int n, double **args, int *dims),
double **args, int *dims);
static void bfgsi(double *H, double *dg, double *dx, int n, int nn);
static int peakwall(double *g, int retcode, double *x, int n,
int (*gfcn)(double *x, int n, double *g, double **args, int *dims),
double (*fcn)(double *x, int n, double **args, int *dims),
double **args, int *dims);
static double times(double *x, double *y, int n);
static double *mtimes(double *x, double *y, int n, int nn);
static double *mminus(double *x, double *y, int n);
static FILE *fptr_interesults = (FILE *)NULL; //Printing intermediate results to a file.
static char filename_sp2vecs[STRLEN]; //Two vectors. 1st row: numerical gradient; 2nd row: vectorized parameters.
#define MAX_NUM_POSSIBLE_BADCASES 25 //After this number, reset inverse hessian (especially dealing with the case for "Correct for low angle").
#define EPS (1.0e-10) //Small number to rectify special case of converging to exactly zero function value.
#define TERMINATEVALUE (1.0e+300) //If the value of the objective function at the intial value is greater than this, terminates the program.
void csminwel(double (*fcn)(double *x, int n, double **args, int *dims),
double *xh, int n, double *H, double *gh,
int (*gfcn)(double *x, int n, double *g, double **args, int *dims),
double *fh, double crit, int *itct, int nit,
int *fcount, int *retcodeh, double **args, int *dims)
{
//If gfcn is passed as NULL, numerical gradient is automatically computed.
int done=0, badg[4], badgh, nogh=1, stuck=0;
double *x[4], *g[4], f[4], *dg, *dx;
int retcode[3], fc=0, ih, nn, i;
int i_dw, verbose_err, terminal_err; // needed by DW changes
char *err_msg, *err_fmt; // needed by DW changes
int cnt_n_badcases = 0; //Must set to 0. Counts the number of bad cases before restarting with the initial diagonal (inverse of) Hessian. Added by TZ.
// TSdmatrix *H_dm = tzMalloc(1, TSdmatrix); //H_dm wil point to the same location as H. Removed by DW - only used to reset to diagonal matrix.
#ifdef VERBOSE_DETOUTPUT
time_t begtime, currentime;
#endif
GLB_sclForHess = H[0]; //The scale factor for the initial (inverse of) Hessian, which was supposed to be **diagonal**. Added by TZ.
nn = n*n; /* n: dimension size of x or xh */
*itct = -1; /* itct: number of actual iterations */
*fcount = -1; /* fcount: number of evaluations of the function */
for (i=0; i<4; i++)
x[i] = tzMalloc(n, double); //x[i] = calloc(n, sizeof(double)); Commented out by TZ.
memcpy(x[0],xh,n*sizeof(double));
for (i=0; i<4; i++)
g[i] = tzMalloc(n, double); //calloc(n, sizeof(double)); Commented out by TZ.
f[0] = fcn(x[0],n,args,dims);
if (f[0] > TERMINATEVALUE) {
printf("Bad initial parameter. Minimization is terminated without any returned value!\n");
return;
}
if (gfcn)
/* if grad is a string, compute it */
badg[0] = gfcn(x[0],n,g[0],args,dims);
else
/* if grad is not string, compute it */
badg[0] = numgrad(g[0],x[0],n,fcn,args,dims);
retcode[2] = 101;
/* iterate until done is false */
while (!done) {
#ifdef OCTAVE_MEX_FILE
OCTAVE_QUIT;
#elif defined(MATLAB_MEX_FILE)
if (utIsInterruptPending())
{
mexPrintf("\n\n** Ctrl-C detected, quitting...\n");
dw_exit(0);
}
#endif
#ifdef VERBOSE_DETOUTPUT
time(&begtime);
#endif
for (i=0; i<n; i++)
g[1][i] = g[2][i] = g[3][i] = 0;
// #ifdef VERBOSE_DETOUTPUT
// printf("-----------------\n-----------------\n");
// printf("f at the beginning of new iteration, %.10f\nx = ",f[0]);
// for (i=0; i<n; i++) {
// printf("%15.8g ",x[0][i]);
// if (i%4==3) printf("\n");
// }
// if (i%4>0) printf("\n");
// #endif
(*itct)++;
csminit(&f[1],x[1],&fc,&retcode[0],x[0],f[0],g[0],badg[0],H,n,fcn,args,dims);
*fcount += fc;
/* if retcode1=1 gradient is zero and you are at the peak */
if (retcode[0]!=1) {
badg[1] = peakwall(g[1],retcode[0],x[1],n,gfcn,fcn,args,dims);
/* Bad gradient or back and forth on step length.
Possibly at cliff edge. Try perturbing search direction. */
if (badg[1]) {
double *Hcliff = tzMalloc(nn, double); //calloc(nn,sizeof(double)); Commented out by TZ.
// double randmax=1.0/(double)RAND_MAX; //03/10/2006, changed from 1/ to 1.0/ to make randmax a legal double.
/* if stuck, give it another try by perturbing Hessian */
memcpy(Hcliff,H,nn*sizeof(double));
for (i=0; i<nn; i+=n+1)
Hcliff[i] *= 1+dw_uniform_rnd(); //rand()*randmax; //Random search. Hcliff[i] *= 1+0.5;
#ifdef VERBOSE_WARNINGS
printf("======= Random search takes place now. =======\n");
printf("Cliff. Perturbing search direction.\n");
#endif
csminit(&f[2],x[2],&fc,&retcode[1],x[0],f[0],g[0],badg[0],Hcliff,n,fcn,args,dims);
*fcount += fc;
if (f[2] < f[0]) {
badg[2] = peakwall(g[2],retcode[1],x[2],n,gfcn,fcn,args,dims);
if (badg[2]) {
double *xx = tzMalloc(n, double), nx; //calloc(n,sizeof(double)), nx; Commented out by TZ.
#ifdef VERBOSE_WARNINGS
printf("Cliff again. Try traversing.\n");
#endif
for (i=0; i<n; i++)
xx[i] = x[2][i]-x[1][i];
nx = times(xx,xx,n);
if (sqrt(nx) < 1e-13) {
f[3] = f[0];
memcpy(x[3],x[0],n*sizeof(double));
badg[3] = 1;
retcode[2] = 101;
} else {
double *gcliff = tzMalloc(n, double), //calloc(n,sizeof(double)), Commented out by TZ.
*eye = tzMalloc(nn, double); //calloc(n,sizeof(double)); Bugs of Iskander. Changed from n to nn. 03/10/06.
double dfnx = (f[2]-f[1])/nx;
for (i=nn-1; i >= 0; i--) eye[i]=0.0; // We need eye to be the identity matrix and so have to zero it. DW - 06/20/2011
for (i=0; i<n; i++) {
gcliff[i] = dfnx*xx[i];
eye[i*(n+1)] = 1;
}
csminit(&f[3],x[3],&fc,&retcode[2],x[0],f[0],gcliff,0,eye,n,fcn,args,dims);
*fcount += fc;
badg[3] = peakwall(g[3],retcode[2],x[3],n,gfcn,fcn,args,dims);
tzDestroy(eye);
tzDestroy(gcliff);
}
tzDestroy(xx);
} else {
f[3] = f[0];
memcpy(x[3],x[0],n*sizeof(double));
badg[3] = 1;
retcode[2] = 101;
}
} else {
f[3] = f[0];
memcpy(x[3],x[0],n*sizeof(double));
badg[3] = 1;
retcode[2] = 101;
}
tzDestroy(Hcliff);
} else {
/* normal iteration, no walls, or else we're finished here. */
f[2] = f[0];
f[3] = f[0];
badg[2] = 1;
badg[3] = 1;
retcode[1] = 101;
retcode[2] = 101;
}
}
else //Bugs fixed by T. Zha -- 02/24/05.
{
f[1] = f[0];
f[2] = f[0];
f[3] = f[0];
retcode[1] = retcode[0];
retcode[2] = retcode[0];
}
// % normal iteration, no walls, or else we're finished here.
// f2=f; f3=f; badg2=1; badg3=1; retcode2=101; retcode3=101;
// f1=f; f2=f; f3=f; retcode2=retcode1; retcode3=retcode1;
/* how to pick gh and xh */
if (f[3]<f[0] && badg[3]==0) {
/* if 3 (transversing) was needed, it improved and gradient is good, take that */
ih = 3;
*fh = f[3];
memcpy(xh,x[3],n*sizeof(double));
memcpy(gh,g[3],n*sizeof(double));
badgh = badg[3];
*retcodeh = retcode[2];
}
else if (f[2]<f[0] && badg[2]==0) {
/* if 2 (perturbig) was needed, it improved and gradient is good, take that */
ih = 2;
*fh = f[2];
memcpy(xh,x[2],n*sizeof(double));
memcpy(gh,g[2],n*sizeof(double));
badgh = badg[2];
*retcodeh = retcode[1];
}
else if (f[1]<f[0] && badg[1]==0) {
/* if first try went fine, take that */
ih = 1;
*fh = f[1];
memcpy(xh,x[1],n*sizeof(double));
memcpy(gh,g[1],n*sizeof(double));
badgh = badg[1];
*retcodeh = retcode[0];
}
else {
/* if nothing worked, just take the min of your attempts and compute the gradient */
if (f[1] <= f[2])
if (f[1] <= f[3]) ih = 1;
else ih = 3;
else
if (f[2] <= f[3]) ih = 2;
else ih = 3;
*fh = f[ih];
memcpy(xh,x[ih],n*sizeof(double));
*retcodeh = retcode[ih-1];
if (nogh) {
nogh = 0;
if (gfcn)
badgh = gfcn(xh,n,gh,args,dims);
else
badgh = numgrad(gh,xh,n,fcn,args,dims);
}
badgh = 1;
}
/* end of picking */
stuck = fabs(*fh-f[0]) < crit; // Used if fh>0. TZ, 9/03.
//stuck = (2.0*fabs(*fh-f[0]) <= crit*(fabs(*fh)+fabs(f[0])+EPS)); //Used if fh<0. Added by TZ, 9/03.
/* if nothing REALLY worked, too bad, you're stuck */
if (!badg[0] && !badgh && !stuck) {
/* if you are not stuck, update H0 matrix */
dg = mminus(gh,g[0],n);
dx = mminus(xh,x[0],n);
bfgsi(H,dg,dx,n,nn);
tzDestroy(dx);
tzDestroy(dg);
}
#ifdef VERBOSE_DETOUTPUT
//=== Prints out intermediate results.
printf("========================================\n");
printf(" (1) New value of the obj. func. on iteration %d: %.9f\n (2) Old value: %.9f\n (3) Downhill improvement: %.9f\n",
(int)*itct, *fh, f[0], f[0]-(*fh));
time(¤time);
//=== Times the iterative progress.
printf(" (4) Seconds to complete one iteration: %0.4f\n (5) Current time of day: %s\n\n", difftime(currentime, begtime), ctime(¤time));
fflush(stdout); // Flush the buffer to get out this message without delay.
#endif
//--------- Prints outputs to a file. ---------
if ( !(fptr_interesults = fopen(filename_sp2vecs,"wt")) ) {
printf("\n\nUnable to create the starting point data file %s in csminwel.c!\n", filename_sp2vecs);
dw_exit(EXIT_FAILURE);
}
fprintf(fptr_interesults, "========= Only one block at a time if more-than-one blocks are used. ========== \n");
fprintf(fptr_interesults, "--------Numerical gradient---------\n");
for (i=0; i<n; i++) fprintf(fptr_interesults, " %0.16e ", gh[i]);
fprintf(fptr_interesults, "\n");
fprintf(fptr_interesults, "--------Restarting point---------\n");
for (i=0; i<n; i++) fprintf(fptr_interesults, " %0.16e ", xh[i]);
fprintf(fptr_interesults, "\n\n");
fflush(fptr_interesults);
tzFclose(fptr_interesults);
if ((int)*itct > nit) {
#ifdef VERBOSE_WARNINGS
// Changed by DW to avoid tzGetDebugFile() call
terminal_err=dw_SetTerminalErrors(0);
verbose_err=dw_SetVerboseErrors(USER_ERR);
err_fmt="Warning: Termination as the maximum number of iterations %d is reached with return code %d.";
sprintf(err_msg=dw_malloc(strlen(err_fmt)+21),err_fmt,nit,*retcodeh); // max of 24 digits total in nit and *retcodeh
dw_UserError(err_msg);
dw_free(err_msg);
dw_SetTerminalErrors(terminal_err);
dw_SetVerboseErrors(verbose_err);
/* printf("\nWarning: Termination as the maximum number of iterations %d is reached with return code %d.\n", nit, *retcodeh); */
/* fprintf(tzGetDebugFile(), "\nWarning: Termination as the maximum number of iterations %d is reached with return code %d.\n", nit, *retcodeh); */
#endif
done = 1;
}
else if (stuck) //stuck = 1 means fabs(*fh-f[0]) < crit, which means converged. why do we use the word "stuck" is beyond me.
{
#ifdef VERBOSE_DETOUTPUT
// Changed by DW to avoid tzGetDebugFile() call
terminal_err=dw_SetTerminalErrors(0);
verbose_err=dw_SetVerboseErrors(USER_ERR);
err_fmt="Convergence (improvement < crit %.4e) with return code %d.";
sprintf(err_msg=dw_malloc(strlen(err_fmt)+21),err_fmt,crit,*retcodeh); // max of 24 characters total in crit and *retcodeh
dw_UserError(err_msg);
dw_free(err_msg);
dw_SetTerminalErrors(terminal_err);
dw_SetVerboseErrors(verbose_err);
//printf("\nConvergence (improvement < crit %.4e) with return code %d.\n", crit, *retcodeh);
//fprintf(tzGetDebugFile(), "\nConvergence (improvement < crit %.4e) with return code %d.\n", crit, *retcodeh);
#endif
done = 1;
}
#ifdef VERBOSE_WARNINGS
switch (*retcodeh) {
case 1:
printf("\nConverged: Zero gradient.\n"); break;
case 2:
printf("\nWarning: Back adjustment of stepsize didn't finish.\n"); break;
case 3:
printf("\nWarning: Smallest stepsize still improving too slow.\n"); break;
case 4:
printf("\nWarning: Forth adjustment of stepsize didn't finish.\n"); break;
case 6:
printf("\nWarning: Smallest step still improving too slow, reversed gradient.\n"); break;
case 5:
printf("\nWarning: Largest stepsize still improving too fast.\n"); break;
case 7:
printf("\nWarning: Possible inaccuracy in Hessian matrix.\n"); break;
}
#endif
//=== Restarts from the initial (inverse of) Hessian when stuck for a while in bad cases. This can happen even *retcodeh=0 while stuck=0.
//if (*retcodeh && *retcodeh != 1)
if (!done)
if (cnt_n_badcases++ >= MAX_NUM_POSSIBLE_BADCASES)
{
// changed by DW to initialize H to diagonal matrix
for (i_dw=nn-1; i_dw >= 0; i_dw--) H[i_dw]=0.0;
for (i_dw=nn-1; i_dw >= 0; i_dw-=n+1) H[i_dw]=GLB_sclForHess;
/* H_dm->M = H; */
/* H_dm->nrows = H_dm->ncols = n; */
/* InitializeDiagonalMatrix_lf(H_dm, GLB_sclForHess); */
/* //H_dm->flag = M_GE | M_SU | M_SL; //Hessian is symmetric. */
cnt_n_badcases = 0; //Reset after we restart wtih the initial Hessian.
#ifdef VERBOSE_WARNINGS
// Changed by DW to avoid tzGetDebugFile() call
terminal_err=dw_SetTerminalErrors(0);
verbose_err=dw_SetVerboseErrors(USER_ERR);
err_fmt="Hessian is reset to the initial value because the maximum number of possibly bad cases, %d, is reached with return code %d!";
// max of 24 digits total in MAX_NUM_POSSIBLE_BADCASES and *retcodeh
sprintf(err_msg=dw_malloc(strlen(err_fmt)+21),err_fmt,MAX_NUM_POSSIBLE_BADCASES,*retcodeh);
dw_UserError(err_msg);
dw_free(err_msg);
dw_SetTerminalErrors(terminal_err);
dw_SetVerboseErrors(verbose_err);
/* printf("Hessian is reset to the initial value because the maximum number of possibly bad cases, %d, is reached with return code %d!\n", MAX_NUM_POSSIBLE_BADCASES, *retcodeh); */
/* fprintf(tzGetDebugFile(), "Hessian is reset to the initial value because the maximum number of possibly bad cases, %d, is reached with return code %d!\n", MAX_NUM_POSSIBLE_BADCASES, *retcodeh); */
#endif
}
f[0] = *fh;
memcpy(x[0],xh,n*sizeof(double));
memcpy(g[0],gh,n*sizeof(double));
badg[0] = badgh;
}
//--------- Prints outputs to a file. ---------
if ( !(fptr_interesults = fopen(filename_sp2vecs,"wt")) ) {
printf("\n\nUnable to create the starting point data file %s in csminwel.c!\n", filename_sp2vecs);
dw_exit(EXIT_FAILURE);
}
fprintf(fptr_interesults, "========= Only a block at a time if more-than-one blocks are used. ========== \n");
fprintf(fptr_interesults, "--------Numerical gradient---------\n");
for (i=0; i<n; i++) fprintf(fptr_interesults, " %0.16e ", g[0][i]);
fprintf(fptr_interesults, "\n");
fprintf(fptr_interesults, "--------Restarting point---------\n");
for (i=0; i<n; i++) fprintf(fptr_interesults, " %0.16e ", x[0][i]);
fprintf(fptr_interesults, "\n\n");
fflush(fptr_interesults);
tzFclose(fptr_interesults);
//=== Frees memory.
for (i=0; i<4; i++) {
tzDestroy(g[i]);
tzDestroy(x[i]);
}
// tzDestroy(H_dm); deleted by DW
}
#undef MAX_NUM_POSSIBLE_BADCASES
#undef EPS
#undef TERMINATEVALUE
#if INDXNUMGRAD_CSMINWEL == 1
#define SCALE 1.0
static int numgrad(double *g, double *x, int n,
double (*fcn)(double *x, int n, double **args, int *dims),
double **args, int *dims) {
//Forward difference gradient method.
double delta, deltai;
double f0, g0, ff, tmp, *xp;
int i;
int badg;
f0 = fcn(x,n,args,dims);
badg = 0;
for (i=0, xp=x; i<n; i++, xp++, g++) {
delta=SCALE*GRADSTPS_CSMINWEL, deltai=1.0/delta; //e+5/SCALE;
tmp = *xp;
*xp += delta;
delta = *xp - tmp; // This increases the precision slightly. Added by TZ.
if ( (ff=fcn(x,n,args,dims)) < NEARINFINITY ) g0 = (ff-f0)*deltai; //Not over the boundary.
else {
//Switches to the other side of the boundary.
*xp = tmp - delta;
g0 = (f0-fcn(x,n,args,dims))*deltai;
}
*xp = tmp; //Puts back to the original place. TZ, 9/03.
if (fabs(g0)<1.0e+15)
*g = g0;
else {
#ifdef VERBOSE_WARNINGS
printf("Bad gradient.\n");
#endif
*g = 0;
badg = 1;
}
}
return badg;
}
//#elif INDXNUMGRAD_CSMINWEL == 2
#else
//#define STPS 1.0e-04 // 6.0554544523933391e-6 step size = pow(DBL_EPSILON,1.0/3)
static int numgrad(double *g, double *x, int n,
double (*fcn)(double *x, int n, double **args, int *dims),
double **args, int *dims) {
//Central difference gradient method. Added by TZ.
double dh;
double f0, fp, fm, tmp, *xp;
int i;
int badg;
badg = 0;
for (i=0, xp=x; i<n; i++, xp++, g++) {
dh = fabs(*xp)<=1 ? GRADSTPS_CSMINWEL : GRADSTPS_CSMINWEL*(*xp);
tmp = *xp;
*xp += dh;
dh = *xp - tmp; // This increases the precision slightly.
fp = fcn(x,n,args,dims);
*xp = tmp - dh;
fm = fcn(x,n,args,dims);
//=== Checking the boundary condition for the minimization problem.
if (fp >= NEARINFINITY) {
*xp = tmp; //Puts back to the original place. TZ, 9/03.
f0 = fcn(x,n,args,dims);
*g = (f0-fm)/dh;
}
else if (fm >= NEARINFINITY) {
//Switches to the other side of the boundary.
*xp = tmp; //Puts back to the original place. TZ, 9/03.
f0 = fcn(x,n,args,dims);
*g = (fp-f0)/dh;
}
else {
*g = (fp-fm)/(2.0*dh);
*xp = tmp; //Puts back to the original place. TZ, 9/03.
}
if (fabs(*g)>1.0e+15) {
#ifdef VERBOSE_WARNINGS
printf("Bad gradient.\n");
#endif
*g = 0.0;
badg = 1;
}
}
return badg;
}
#endif
////#undef INDXNUMGRAD_CSMINWEL
////#undef GRADSTPS_CSMINWEL
#define ANGLE 0.01 //When output of this variable becomes negative, we have a wrong analytical graident.
//.005 works for identified VARs and OLS.
//.005 implies 89.71 degrees (acrcos(ANGLE)*180/pi).
//.01 implies 89.43 degrees (acrcos(ANGLE)*180/pi).
//.05 implies 87.13 degrees (acrcos(ANGLE)*180/pi).
//.1 implies 84.26 degrees (acrcos(ANGLE)*180/pi).
#define THETA .4 //(0<THETA<.5) THETA near .5 makes long line searches, possibly fewer iterations.
//.1 works for OLS or other nonlinear functions.
//.3 works for identified VARs.
#define FCHANGE 1000
#define MINLAMB 1e-9
#define MINDFAC .01
static void csminit(double *fhat, double *xhat, int *fcount, int *retcode,
double *x0, double f0, double *g, int badg, double *H0, int n,
double (*fcn)(double *x, int n, double **args, int *dims),
double **args, int *dims) {
double lambda=1, gnorm=0, dxnorm=0, factor=3, lambdaPeak=0;
double f, dfhat, a, tmp, fPeak=f0, lambdaMax=DBL_MAX;
double *dx, *dxtest;
int done=0, shrink=1, shrinkSignal, growSignal;
int i;
memcpy(xhat, x0, n*sizeof(double)); //Iskander's original code does not have this line, which is a major bug. Corrected by TZ.
*fhat = f0;
*fcount = 0;
*retcode = 0;
gnorm = sqrt(times(g,g,n));
if ((gnorm < 1.e-12) && !badg)
*retcode = 1; /* gradient convergence */
else {
/* with badg 1, we don't try to match rate of improvement to directional
derivative. We're satisfied just to get some improvement in f. */
dx = tzMalloc(n, double); //dx = calloc(n, sizeof(double)); Commented out by TZ.
//if (!dx) printf("Dynamic memory allocation error.\n"); Commnted out by TZ.
for (i=0; i<n; i++)
dx[i] = -times(&H0[i*n],g,n);
dxnorm = sqrt(times(dx,dx,n));
if (dxnorm > 1e12) {
#ifdef VERBOSE_WARNINGS
printf("Near-singular H problem.\n");
#endif
for (i=0; i<n; i++)
dx[i] *= FCHANGE/dxnorm;
}
dfhat = times(dx,g,n);
if (!badg) {
/* If the gradient is good, test for alignment of dx with gradient and fix if necessary */
if ((a=-dfhat/(gnorm*dxnorm))<ANGLE) {
tmp = (ANGLE*dxnorm+dfhat/gnorm)/gnorm;
for (i=0; i<n; i++)
dx[i] -= tmp*g[i];
dfhat = times(dx,g,n);
dxnorm = sqrt(times(dx,dx,n));
#ifdef VERBOSE_DETOUTPUT
printf("Correct for low angle: %g\n",a);
#endif
}
}
#ifdef VERBOSE_DETOUTPUT
printf("Predicted improvement: %18.9f, Norm of gradient: %18.9f\n", -dfhat*0.5, gnorm);
#endif
dxtest = tzMalloc(n, double); //calloc(n, sizeof(double)); Commented out by TZ.
while (!done) {
for (i=0; i<n; i++)
dxtest[i] = x0[i]+dx[i]*lambda;
f = fcn(dxtest,n,args,dims);
#ifdef VERBOSE_DETOUTPUT
printf("lambda = %10.5g; f = %20.7e\n",lambda,f);
#endif
if (f<*fhat) {
*fhat = f;
memcpy(xhat,dxtest,n*sizeof(double));
}
(*fcount)++;
tmp = -THETA*dfhat*lambda;
/* the optimal lambda should be such that f0-f > -THETA*dfhat*lambda (see Berndt et al.)
If that's not the case and grad is good, OR
if grad is bad and f is not going down, shrinkSignal = 1 */
shrinkSignal = ( !badg && (f0-f <= (tmp>0?tmp:0)) ) ||
( badg && (f0-f < 0 ) );
/* the optimal lambda should also be such that f0-f<-(1-THETA)*dfhat*lambda
If that's not the case with lambda>0, AND grad is good, growthSignal = 1 */
growSignal = !badg && ( (lambda > 0) && (f0-f >= -(1-THETA)*dfhat*lambda) );
/* If shrinkSignal=1 AND ( lambda>lambdaPeak or lambda negative )
(note when lambdaPeak=0 the second part only excludes lambda=0)
try shrinking lambda */
if ( shrinkSignal && ( (lambda>lambdaPeak) || (lambda<0) ) ) {
/* if shrink=0 OR lambda/factor is already smaller than lambdaPeak, increase factor */
if ( (lambda>0) && ((!shrink) || (lambda/factor <= lambdaPeak)) ) {
shrink = 1;
factor = pow(factor,.6);
while (lambda/factor <= lambdaPeak)
factor = pow(factor,.6);
if (fabs(factor-1)<MINDFAC) {
if (fabs(lambda) < 4)
*retcode = 2;
else
*retcode = 7;
done = 1;
}
}
if ((lambda<lambdaMax) && (lambda>lambdaPeak))
lambdaMax=lambda;
/* shrink lambda */
lambda /= factor;
/* if lambda has already been shrunk as much as possible */
if (fabs(lambda) < MINLAMB)
/* if lambda is positive AND you have not made any improvement
try going against gradient, which may be inaccurate */
if ((lambda > 0) && (f0 <= *fhat))
lambda = -lambda*pow(factor,6);
else {
/* if lambda is negative: let it be known and quit trying */
if (lambda < 0)
*retcode = 6;
/* if you have not made any imporvement:
let it be known and quit trying */
else
*retcode = 3;
done = 1;
}
}
/* If growSignal=1 and lambda positive OR ( lambda>lambdaPeak or lambda negative )
(note when lambdaPeak=0 the second part only excludes lambda=0)
try increase lambda */
else
if ( (growSignal && (lambda > 0) ) ||
( shrinkSignal && (lambda <= lambdaPeak) && (lambda > 0) ) ) {
if (shrink) {
shrink = 0;
factor = pow(factor,.6);
if (fabs(factor-1) < MINDFAC) {
if (fabs(lambda) < 4)
*retcode = 4;
else
*retcode = 7;
done = 1;
}
}
if ( (f<fPeak) && (lambda>0) ) {
fPeak = f;
lambdaPeak = lambda;
if (lambdaMax <= lambdaPeak)
lambdaMax = lambdaPeak*factor*factor;
}
/* increase lambda (up to 1e20) */
lambda *= factor;
/* if lambda has been increased up to the limit and
you have not made any imporvement:
let it be known and quit trying */
if (fabs(lambda) > 1e20) {
*retcode = 5;
done = 1;
}
}
/* If growthSignal=shrinkSignal=0 you found a good lambda, you are done */
else {
done = 1;
*retcode = factor<1.2 ? 7 : 0;
}
}
tzDestroy(dxtest);
tzDestroy(dx);
}
#ifdef VERBOSE_DETOUTPUT
printf("Norm of dx %10.5g\n", dxnorm);
#endif
}
#undef ANGLE
#undef THETA
#undef FCHANGE
#undef MINLAMB
#undef MINDFAC
static double times(double *x, double *y, int n) {
double z = 0;
int i;
for (i=0; i<n; i++, x++, y++)
z += (*x)*(*y);
return z;
}
static int peakwall(double *g, int retcode, double *x, int n,
int (*gfcn)(double *x, int n, double *g, double **args, int *dims),
double (*fcn)(double *x, int n, double **args, int *dims),
double **args, int *dims) {
/* if retcode=2 or 4 you have shrunk or increased lambda as much as you could:
exhausted search possibilities the csminit step has failed */
if (retcode==2 || retcode==4)
return 1;
else
/* if you are not at the peak but the csminit has improved,
compute the gradient again to update H0 */
if (gfcn)
return gfcn(x,n,g,args,dims);
else
return numgrad(g,x,n,fcn,args,dims);
}
static void bfgsi(double *H, double *dg, double *dx, int n, int nn) {
double *Hdg, *dxdx, *dxHdg, *Hdgdx;
double dgdx, m;
int i;
// TSdmatrix *H_dm = NULL; changed by DW
Hdg = tzMalloc(n, double); //calloc(n, sizeof(double)); Commented out by TZ.
//if (!Hdg) printf("Dynamic memory allocation error.\n"); Commented out by TZ.
/* Hdg = H0*dg; */
for (i=0; i<n; i++)
Hdg[i] = times(&H[i*n],dg,n);
/* dgdx = dg'*dx; */
dgdx = 1/times(dg,dx,n);
if (fabs(dgdx)<1e12) {
dxdx = mtimes(dx,dx,n,nn);
dxHdg = mtimes(dx,Hdg,n,nn);
Hdgdx = mtimes(Hdg,dx,n,nn);
m = 1+times(dg,Hdg,n)*dgdx;
for (i=0; i<nn; i++, H++, dxdx++, dxHdg++, Hdgdx++)
*H += (m*(*dxdx)-(*dxHdg)-(*Hdgdx))*dgdx;
dw_free(Hdgdx-nn);
Hdgdx=NULL;
dw_free(dxHdg-nn);
dxHdg = NULL;
dw_free(dxdx-nn);
dxdx = NULL;
}
else {
//=== Restarting the inverse of Hessian at its initial value. Added by TZ.
// changed by DW to eliminate matrix references.
for (i=nn-1; i >= 0; i--) H[i]=0.0;
for (i=nn-1; i >= 0; i-=n+1) H[i]=GLB_sclForHess;
/* H_dm = tzMalloc(1, TSdmatrix); //H_dm wil point to the same location as H. */
/* H_dm->M = H; */
/* H_dm->nrows = H_dm->ncols = n; */
/* InitializeDiagonalMatrix_lf(H_dm, GLB_sclForHess); */
/* //H_dm->flag = M_GE | M_SU | M_SL; //Hessian is symmetric. */
/* tzDestroy(H_dm); */
#ifdef VERBOSE_WARNINGS
printf("BFGS update failed.\n");
printf("|dg| = %f |dx| = %f\n",sqrt(times(dg,dg,n)),sqrt(times(dx,dx,n)));
printf("dg\'*dx = %f\n",dgdx);
printf("|H*dg| = %f\n",sqrt(times(Hdg,Hdg,n)));
#endif
}
tzDestroy(Hdg);
}
static double *mtimes(double *x, double *y, int n, int nn) {
double *x0;
double *z;
int i, j;
z = tzMalloc(nn, double); //calloc(nn, sizeof(double)); Commented out by TZ.
for (i=0, x0=x; i<n; i++, y++)
for (j=0, x=x0; j<n; j++, x++, z++)
*z = (*x)*(*y);
return z-nn;
}
static double *mminus(double *x, double *y, int n) {
double *z;
int i;
z = tzMalloc(n, double); //calloc(n, sizeof(double)); Commented out by TZ.
for (i=0; i<n; i++, x++, y++, z++)
*z = (*x)-(*y);
return z-n;
}
//=== The following two extern functions to be accessed by other C files.
void csminwel_SetPrintFile(char *filename) {
if (!filename) sprintf(filename_sp2vecs, "outdata5csminwel.prn"); //Default filename.
else if (STRLEN-1 < strlen(filename))
{
dw_SetTerminalErrors(USER_ERR);
dw_SetVerboseErrors(USER_ERR);
dw_UserError(".../csminwel.c: the allocated length STRLEN for filename_sp2vecs is too short. Must increase the string length");
}
else strcpy(filename_sp2vecs, filename);
}
#undef STRLEN
|