Automatic Differentiation
This example shows how GAUSS uses the symbolic procs created by Symbolic Tools to undertake automatic differentiation. The GAUSS code for the likelihood function is given in the proc fct; the equivalent code for creating the gradients and Hessian is given in the string txt. The code is effectively identical - the former deals with matrices, while the latter deals with symbols. Almost all GAUSS operations and functions are permitted when dealing with symbols. gradp and hessp are overloaded to permit symbol operation. NLP, like most optimization techniques in GAUSS, permits the user to specify procs for evaluating the gradient or Hessian - in this case by defining the globals _nlp_Gradproc and _nlp_Hessproc. In this example, analytic estimation of the gradient and Hessian is 8.5 times faster than using forward differencing.
/* Symbolic example: nlp.e proc boxcox(y,indx,sigma, lamda); y is the censored variable indx is the vector of the index sigma is the parameter for the standard error of the residual lamda is the parameter for the boxcox process This is an estimation of a BoxCox process, with 5000 observations and 10 parameters. In this example, artificial data is generated and the parameters are estimated using the NLP optimization package (part of FANPAC). The Newton-Raphson algorithm is used, with a step length of unity. NLP minimizes the function, so to maximize the llf, minimize -llf Pindyck and Rubinfeld, 4th ed, p 278 */ library fanpac, symbolic; nlpset; call symstate(reset); call symdebug(off); rndseed 12345; grad_mode = 1; // 0 - numeric; 1 - analytic display_mode = 0; // 0 - off; 1 - on nobs = 5000; // number of observations // create the data xmat = ones(nobs,1) ~rndu(nobs,7); x1= xmat[.,1]; x2= xmat[.,2]; x3= xmat[.,3]; x4= xmat[.,4]; x5= xmat[.,5]; x6= xmat[.,6]; x7= xmat[.,7]; x8= xmat[.,8]; let bhat = 1 2 3 4 5 6 7 8; lmda = 2; y = (lmda*xmat*bhat + 1)^(1/lmda) + .01*rndn(nobs,1); // Likelihood functionfor Box-Cox process proc fct(avec); local llf1, indx, bvec, sig,lam,cnst, llf; bvec = avec[1:8]; sig = avec[9]; lam = avec[10]; indx = xmat*bvec; cnst = ln(2*pi); llf = -.5*( cnst + ln(sig^2) + (((y^lam -1)/lam) - indx)^2/sig^2 ) + (lam-1)*ln(y); retp(-sumc(llf)); endp; // Gauss symbolic representation of Box-Cox as string txt = " slist = symset(avec,b,10); xlist = symlist(8,x); sig = b9; lam = b10; indx = xlist * slist[1:8]; cnst = ln(2*pi); llf = -sumc(-.5*( cnst + ln(sig^2) + (((y^lam -1)/lam) - indx)^2/sig^2 ) + (lam-1)*ln(y)); llfg = gradp(llf,slist); llfh = hessp(llf,slist); "; proc fgrad; endp; proc fhess; endp; cls; if grad_mode == 1 ; print /flush "Estimating BoxCox model using NLP, analytic grad and Hess"; call symproc("fgrad","avec","llfg",txt); call symproc("fhess","avec","llfh",txt); _nlp_Gradproc = &fgrad; _nlp_Hessproc = &fhess; else; print /flush "Estimating BoxCox model using NLP, numeric grad and Hess"; endif; if display_mode == 1; _nlp_Diagnostic = 1; // print out iterations endif; _nlp_GradMethod = 1; // forward difference _nlp_MaxIters = 1000; // max iterations _nlp_Switch = {.}; // not switch _nlp_LineSearch = 1; // step length = 1 _nlp_Algorithm = 3; // Newton-Raphson avec0 = 1*ones(10,1); // starting values d1 = date; {x, f, g, retcode} = nlp(&fct,avec0); time1 = ethsec(d1,date)/100; "\n ======================================== Estimation Results for Box-Cox process ========================================"; "\n LLF value: " -f; "\n Iterations: " _nlp_niter; "\n Coefficient vector " x; "\n Elapsed Time: " time1;
The output from this example is :
grad_mode = 0; (numeric gradients and hessian)======================================== Estimation Results for Box-Cox process ========================================grad_mode = 0; (numeric) grad_mode = 1; (analytic)LLF value: 15766.373 LLF value: 15766.374 Iterations: 42.000000 Iterations: 40.000000 Coefficient vector Coefficient vector 1.0038481 1.0033551 1.9995901 1.9998821 2.9934115 2.9938492 3.9981160 3.9987023 4.9974498 4.9981792 5.9960651 5.9969432 6.9976282 6.9986546 8.0011099 8.0022799 0.062763255 0.062768393 1.9998455 1.9999265 Elapsed Time :16.922000 Elapsed Time: 2.0630000
Notes:
MAXLIK and CML require two input arguments for the gradient and Hessian functions - a Kx1 vector of parameter values, and an NxM matrix of data as input, and a NxK matrix of gradients as an output. This is supported by Symbolic Tools.
call symproc("grdcode","bvec,dta","llfg",txt); __gdproc = &grdcode; {x,f,g,cov} = maxlik ...Alternatively one could run symproc and either retrieve the code from the string, or save it to file. The proc could then be incorporated into the optimization code in the normal way.
str = symproc("grdcode","bvec","llfg",txt); print str; call keyw; // display in GAUSS fname = "c:\\temp\\grdcode.txt"; call putf(fname,str,1,strlen(str),0,0); // write code to file