Print["This is Perturbation AIM Full Version 2.4.1ets"] ; (* *) (* Credits: original algorithm designed by Gary Anderson, 2002-3; *) (* corrected, optimized, and recoded by Eric Swanson, 2004. *) (* Code is currently maintained by Eric Swanson, see *) (* http://www.ericswanson.pro for most recent version and for *) (* background, instructions, capabilities, examples, and tips. *) (* This code will only work with Mathematica version 5. While it is not *) (* too hard to modify the code to work in earlier versions of *) (* Mathematica, those earlier versions are *much* slower at solving *) (* linear systems of equations, and a few of the function calls are also *) (* a bit more awkward. *) (* This is the "advanced" or "Full" version of the perturbationAIM code. *) (* It is generally faster and more immune to potential numerical *) (* problems than the "simple" version, but it is also longer and less *) (* transparent. If you want to understand the underlying algorithm *) (* and the implementation of the algorithm, you will find it much *) (* easier to work through the "simple" version of the code, *) (* perturbationAIMsimple.m, first, and then go through the code below. *) (* The "simple" version is available for download from the web site *) (* above. *) (* Both the "Full" and "simple" versions should always yield identical *) (* solutions to a given model, up to issues of machine precision and *) (* numerical accuracy. The "simple" version should be completely *) (* adequate for most purposes, however, and will be much easier for the *) (* user to understand and even tailor to his/her individual needs. *) (* No matter which version you are using, you should compute your final *) (* set of results using the arbitrary precision capabilities of the *) (* code, as these are *much* more likely to be free of numerical *) (* problems. *) (* *) SetOptions["stdout",PageWidth->79] ; <j,Infinity]],-1}]&, AIMVarNames[eqns]]] ; AIMMaxLag[eqns_]:= AIMMaxLag[eqns] = -Min[Cases[eqns,x_Symbol[t+i_.]->i,Infinity]] ; AIMMaxLead[eqns_]:= AIMMaxLead[eqns] = Max[Cases[eqns,x_Symbol[t+i_.]->i,Infinity]] ; AIMNArgs[eqns_]:= AIMNArgs[eqns] = Length[AIMStateVars[eqns]] ; AIMNEqns[eqns_]:= AIMNEqns[eqns] = Length[Flatten[{eqns}]] ; AIMNVars[eqns_]:= AIMNVars[eqns] = Length[AIMVarNames[eqns]] ; AIMShocks[eqns_]:= AIMShocks[eqns] = Union[Cases[eqns,eps[_][t],Infinity]] ; AIMSSSubs={Sigma->0, eps[_][_]->0, x_[t+_.]:>Symbol[SymbolName[x]<>"AIMSS"]} ; AIMSSVars[eqns_]:= AIMSSVars[eqns] = Map[Symbol[SymbolName[#]<>"AIMSS"]&, AIMVarNames[eqns]] ; AIMStateVars[eqns_]:= AIMStateVars[eqns] = Join[AIMLagVars[eqns], AIMShocks[eqns], {Sigma}] ; AIMVarNames[eqns_]:= AIMVarNames[eqns] = Union[Cases[eqns,x_Symbol[t+i_.]->x,Infinity]] ; (* Here is the AIMBackLookingVars routine. It first finds all equations *) (* that have no leads. For those equations that have only one variable *) (* dated t, it is then obvious that the solution to this variable is *) (* entirely a function of the lagged variables and shocks in that *) (* particular equation. This particular variable can then be solved *) (* in terms of a reduced state vector, so that its particular bFunc has *) (* fewer arguments, hence fewer derivatives and fewer undetermined *) (* coefficients that must be solved. *) (* The routine then iterates, so that a variable is also counted as *) (* clearly backward-looking if the only other variables in its equation *) (* are lagged variables, date t shocks, and other variables dated t *) (* that have already been shown to be clearly backward-looking. *) AIMBackLookingVars[eqns_]:= AIMBackLookingVars[eqns] = Module[{eqnsnoleads,symbols,nsv,nsvargs,possiblensv,nextpos}, eqnsnoleads = Select[Flatten[{eqns}],Cases[#,_[t+i_/;i>0],Infinity] =={}&] ; symbols = Map[Join[AIMLagVars[#],Union[Cases[#,_[t],Infinity]]]&, eqnsnoleads] ; {nsv,nsvargs} = FixedPoint[ Function[nsv, symbols = symbols /.Thread[nsv[[1]]->nsv[[2]]] ; possiblensv = Map[Cases[#,_Symbol[t]]&, symbols] ; nextpos = Flatten[Position[Map[Length,possiblensv], 1]] ; {Join[nsv[[1]], Flatten[possiblensv[[nextpos]]]], Join[nsv[[2]], DeleteCases[symbols[[nextpos]],_Symbol[t],Infinity]]} ], {{},{}}] ; {nsv /.x_[t]:>x, Map[Union[Flatten[#]]&,nsvargs]} ] ; AIMBLVPos[eqns_]:= AIMBLVPos[eqns] = Flatten[Map[Position[AIMVarNames[eqns],#]&, AIMBackLookingVars[eqns][[1]]]] ; (* Calculate the steady state *) AIMSS[eqns_,___,opts___Rule]:= AIMSS[eqns,AIMZeroTol] = Module[{ssguess,precision,sseqns,symbols,ss}, ssguess = AIMSSGuess /.{opts} /.AIMSSGuess->Table[0,{AIMNVars[eqns]}] ; precision = AIMPrecision /.{opts} /.AIMPrecision->Precision[eqns]-2 ; sseqns = Thread[(eqns /.Equal->Subtract /.AIMSSSubs)==0] ; AIMModelDiagnostics[eqns] ; Print["Finding steady state, AIMSSGuess->",InputForm[ssguess]] ; symbols = Complement[Union[Cases[sseqns,_Symbol,Infinity]], Join[AIMSSVars[eqns],{E}]] ; If[Length[symbols]>0,Print["Warning: found symbols ",symbols," in equations"]]; ss = Chop[FindRoot[sseqns, Transpose[{AIMSSVars[eqns],ssguess}], MaxIterations->5000, WorkingPrecision->precision], AIMZeroTol] ; If[Head[ss]===FindRoot, $Failed, ss] ] ; (* Front end that does error-checking and formats the output *) AIMSeries[eqns_,deg_Integer]:= Module[{soln,const,argsubs}, If[AIMSS[eqns,AIMZeroTol] === $Failed || (soln=AIMSoln[eqns,deg,AIMZeroTol]) === $Failed, $Failed, Print["Formatting output, time is ", Date[]] ; const = AIMSSVars[eqns] /.AIMSS[eqns,AIMZeroTol] ; argsubs = Thread[AIMGenericArgs[eqns]->AIMStateVars[eqns]] ; Thread[Through[AIMVarNames[eqns][t]] == const + (soln /.argsubs)] ]] ; (* Front end for Linear AIM *) AIMSoln[eqns_,1,zerotol_]:= AIMSoln[eqns,1,zerotol] = Module[{eqnseq0,allvars,hmat,epsmat,soln,cofb,s0inv,alllagvars}, eqnseq0 = Flatten[{eqns}] /.Equal->Subtract ; allvars = Flatten[Map[Through[AIMVarNames[eqns] [t+#]]&, Range[-Max[AIMMaxLag[eqns],1], AIMMaxLead[eqns]]]] ; hmat = Outer[D,eqnseq0,allvars] /.AIMSSSubs /.AIMSS[eqns,zerotol] ; epsmat = Outer[D,eqnseq0,AIMShocks[eqns]] /.AIMSSSubs /.AIMSS[eqns,zerotol] ; If[(soln=AIMLinearSoln[hmat,AIMMaxLead[eqns]]) === $Failed, $Failed, {cofb,s0inv} = soln ; alllagvars = allvars[[Range[Max[AIMMaxLag[eqns],1]*AIMNVars[eqns]]]] ; Chop[cofb .alllagvars - s0inv .epsmat .AIMShocks[eqns] /. Thread[AIMStateVars[eqns]->AIMGenericArgs[eqns]], zerotol] ]] ; (* This is the heart of the program. Derivatives are evaluated at steady *) (* state, the expectation is taken, and coefficients are solved using *) (* the method of undetermined coefficients. *) (* As in the simple version, we solve a certainty-equivalent version of *) (* the problem first. In addition, for the clearly backward-looking *) (* variables, we delete terms and coefficients that are clearly zero, *) (* and thus solve only those coefficients that are in general nonzero. *) AIMSoln[eqns_,deg_Integer,zerotol_]:= AIMSoln[eqns,deg,zerotol] = Module[{args,blvpos,bfuncargs,drvindxs,cedrvindxs,stdrvindxs,cecoeffs, stcoeffs,nextceTerms,nextstTerms,cesubs,cesystem,cesoln,dum,stsubs, stsystem,stsoln}, args = AIMGenericArgs[eqns] ; blvpos = AIMBLVPos[eqns] ; bfuncargs = Table[args,{AIMNVars[eqns]}] ; bfuncargs[[blvpos]] = AIMBackLookingVars[eqns][[2]] /. Thread[AIMStateVars[eqns]->AIMGenericArgs[eqns]] ; drvindxs = Compositions[deg,AIMNArgs[eqns]] ; cedrvindxs = Select[drvindxs, #[[-1]]==0 &] ; stdrvindxs = Select[drvindxs, #[[-1]] >1 &] ; cecoeffs = Table[Unique[],{AIMNVars[eqns]},{Length[cedrvindxs]}] ; stcoeffs = Table[Unique[],{AIMNVars[eqns]},{Length[stdrvindxs]}] ; nextceTerms = cecoeffs .Map[Apply[Times,Power[args,#]]&, cedrvindxs] ; nextstTerms = stcoeffs .Map[Apply[Times,Power[args,#]]&, stdrvindxs] ; nextceTerms[[blvpos]] = Map[ nextceTerms[[#]] /. Thread[Complement[args,bfuncargs[[#]]]->0]&, blvpos] ; nextstTerms[[blvpos]] = 0 ; cecoeffs[[blvpos]] = Map[Complement[Cases[nextceTerms[[#]],_Symbol,Infinity], bfuncargs[[#]]]&, blvpos] ; stcoeffs[[blvpos]] = {} ; cecoeffs = Flatten[cecoeffs] ; stcoeffs = Flatten[stcoeffs] ; cesubs = Thread[Map[bFunc,AIMVarNames[eqns]] -> MapThread[Apply[Function,{#1,#2}]&, {bfuncargs, AIMSoln[eqns,deg-1,zerotol] + nextceTerms}]] ; Print["Calculating CE Derivatives, time is ", Date[]] ; Print["Undetermined CE coefficients to solve: ",Length[cecoeffs]] ; cesoln = If[AIMNArgs[eqns]===1, PrependTo[args,dum]; nextceTerms, cesystem = Chop[Flatten[CoefficientArrays[AIMCEDerivatives[eqns,deg][0] /.cesubs, Drop[args,-2]]], zerotol] ; Print["Calculating CE solution, time is ", Date[]] ; nextceTerms /.Chop[Flatten[NSolve[cesystem, cecoeffs]], zerotol]] ; If[stcoeffs==={}, stsoln=0, stsubs = Thread[Map[bFunc,AIMVarNames[eqns]] -> MapThread[Apply[Function,{#1,#2}]&, {bfuncargs, AIMSoln[eqns,deg-1,zerotol] + cesoln + nextstTerms}]] ; Print["Calculating Stoch Derivatives, time is ", Date[]] ; stsystem = Chop[Expand[Flatten[CoefficientArrays[Chop[ AIMStDerivatives[eqns,deg][0] /.stsubs, zerotol],Drop[args,-1]]]], zerotol] /.eps[x_][_]^n_->mom[x,n] /.eps[_][_]->0 ; Print["Undetermined Stoch coefficients to solve: ",Length[stcoeffs]] ; Print["Calculating Stoch solution, time is ", Date[]] ; stsoln = nextstTerms /.Chop[Flatten[NSolve[stsystem, stcoeffs]], zerotol] ; ] ; AIMSoln[eqns,deg-1,zerotol] + cesoln + stsoln ] ; (* That's essentially it. The following routine calculates derivatives *) (* of the equations composed with the (unknown) solution functions b. *) (* Like the simple version, we use univariate differentiation to compute *) (* the multivariate derivatives of the function Fob. Unlike the simple *) (* version, we compute the certainty-equivalent derivatives (those not *) (* involving Sigma) first, and the stochastic derivatives (those involving *) (* Sigma) second. This allows us to avoid ever computing the first *) (* derivatives of Fob with respect to Sigma, which we know from theory *) (* must be zero. *) AIMCEDerivatives[eqns_,2]:= AIMCEDerivatives[eqns,2] = Derivative[2][Function[tAIM, Evaluate[AIMSubBFuncsIntoEqns[eqns] /. Thread[AIMStateVars[eqns]->tAIM*Join[Drop[AIMGenericArgs[eqns],-2],{1,0}]]]]] ; AIMCEDerivatives[eqns_,deg_Integer]:= AIMCEDerivatives[eqns,deg] = AIMCEDerivatives[eqns,deg-1]' ; AIMStDerivatives[eqns_,2]:= AIMStDerivatives[eqns,2] = Function[tAIM, Evaluate[Derivative[0,2] [Function[{tAIM1,tAIM2}, Evaluate[AIMSubBFuncsIntoEqns[eqns] /.Thread[AIMStateVars[eqns]-> Append[tAIM1*Drop[AIMGenericArgs[eqns],-1],tAIM2]]]]] [tAIM,tAIM]]] ; AIMStDerivatives[eqns_,deg_Integer]:= AIMStDerivatives[eqns,deg] = AIMStDerivatives[eqns,deg-1]' ; AIMSubBFuncsIntoEqns[eqns_]:= AIMSubBFuncsIntoEqns[eqns] = With[{deveqns = eqns /.x_Symbol[t+i_.]:>Symbol[SymbolName[x]<>"AIMSS"] +x[t+i] /.Equal->Subtract /.AIMSS[eqns,AIMZeroTol]}, AIMSubOutLeadVars[eqns,deveqns,AIMMaxLead[eqns]] ] ; AIMBFuncs[eqns_]:= AIMBFuncs[eqns] = Module[{bfuncs}, bfuncs = Map[Apply[bFunc[#],AIMStateVars[eqns]]&, AIMVarNames[eqns]] ; bfuncs[[AIMBLVPos[eqns]]] = MapThread[Apply[bFunc[#1],#2]&, AIMBackLookingVars[eqns]] ; bfuncs ] ; AIMSubOutLeadVars[origeqns_,eqnssofar_,0]:= eqnssofar /.Thread[Through[AIMVarNames[origeqns][t]]->AIMBFuncs[origeqns]] /. eps[x_][t+i_]->Sigma*eps[x][t+i] ; AIMSubOutLeadVars[origeqns_,eqnssofar_,lead_Integer]:= With[{reducedeqns=eqnssofar /.Thread[Through[AIMVarNames[origeqns][t+lead]]-> (AIMBFuncs[origeqns]/.t->t+lead)]}, AIMSubOutLeadVars[origeqns, reducedeqns, lead-1] ] ; (* Print out model diagnostics (obviously) *) AIMModelDiagnostics[eqns_] := ( Print["\nModel Diagnostics:"] ; Print["Number of equations: ",AIMNEqns[eqns]] ; Print["Number of variables: ",AIMNVars[eqns]] ; Print["Number of shocks: ",Length[AIMShocks[eqns]]] ; Print["Maximum lag: ",AIMMaxLag[eqns]] ; Print["Maximum lead: ",AIMMaxLead[eqns]] ; Print["Lagged variables: ",AIMLagVars[eqns]] ; Print["Shocks: ",AIMShocks[eqns]] ; Print[" together with Sigma, these yield ",AIMNArgs[eqns]," state variables\n"]; Print["Variables ",AIMBackLookingVars[eqns][[1]]," are clearly backward-looking"]; Print[" using the following reduced state variable sets for these variables:"] ; Print[AIMBackLookingVars[eqns][[2]]] ; Print["List of all variables: ",AIMVarNames[eqns]] ; Print["Treating steady state and final coeff values < ", N[AIMZeroTol], " as zero (AIMZeroTol)\n"] ; ) ; (* Everything that follows is Linear AIM. See Anderson and Moore (1985) *) (* for a description of the algorithm. *) (* In contrast to the simple version of the code, here we perform "exact" *) (* ShiftRights first (i.e., shift equations forward that have a complete *) (* row of zeros in the lead matrix, which reduces the number of calls to *) (* the singular value decomposition and thus reduces potential numerical *) (* problems.) *) (* Also, we compute the stability conditions of the model using the Schur *) (* decomposition rather than eigenvectors. This should be more *) (* numerically robust than eigenvectors, and the Schur decomposition is *) (* guaranteed to exist for all models, while linearly independent *) (* eigenvectors are not. The disadvantage of using Schur vectors is that *) (* computing them is slow: first, the computation is inherently slower, *) (* and second, the fact that the code must swap Schur blocks using a *) (* routine that I (ets) wrote, rather than internal optimized machine *) (* code, slows it down quite a bit. *) AIMLinearSoln[hmat_,nleads_Integer] := Module[{hrows,hcols,shiftedh,qmat,stabconds,bmat,smat}, {hrows,hcols} = Dimensions[hmat] ; {shiftedh,qmat} = NestWhile[AIMExactShiftRight, {hmat,{}}, Min[Map[Max,Abs[AIMLastBlock[#[[1]]]]]] ==0 &, 1, nleads] ; {shiftedh,qmat} = NestWhile[AIMShiftRight, {shiftedh,qmat}, MatrixRank[AIMLastBlock[#[[1]]]] hrows*nleads || MatrixRank[AIMLastBlock[qmat]]1+AIMZeroTol]] ; swaplist = Flatten[Map[Range[bigrtpos[[#]]-1,#,-1]&, Range[Length[bigrtpos]]]]; {q,t,blockpos} = Fold[AIMSwapSchurBlocks, {Transpose[q],t,blockpos}, swaplist]; Take[q, blockpos[[Length[bigrtpos]+1]] -1] ] ; AIMSwapSchurBlocks[{q_,t_,blockpos_},swappos_]:= Module[{index1,index2,n1,n2,a11,a12,a22,xmat,ans,xq,index12,qswpd,tprime,tswpd, blockposswpd}, index1 = Range[blockpos[[swappos]], blockpos[[swappos+1]]-1] ; index2 = Range[blockpos[[swappos+1]], blockpos[[swappos+2]]-1] ; n1 = Length[index1] ; n2 = Length[index2] ; a11 = Take[t, index1, index1] ; a12 = Take[t, index1, index2] ; a22 = Take[t, index2, index2] ; xmat = Table[Unique[x],{n1},{n2}] ; ans = Flatten[NSolve[Flatten[a11.xmat-xmat.a22]-Flatten[a12], Flatten[xmat]]]; xq = First[QRDecomposition[ BlockMatrix[{{-xmat /.ans, IdentityMatrix[n1]}, {IdentityMatrix[n2], ZeroMatrix[n2,n1]}}] ]] ; index12 = Join[index1,index2] ; qswpd = q ; (* premultiply q by xq *) qswpd[[index12]] = xq .qswpd[[index12]] ; tprime = Transpose[t] ; (* transform t to xq .t .xq' *) tprime[[index12]] = xq .tprime[[index12]] ; tswpd = Transpose[tprime] ; tswpd[[index12]] = xq .tswpd[[index12]] ; blockposswpd = ReplacePart[blockpos, blockpos[[swappos+1]] +n2-n1, swappos+1] ; Chop[{qswpd,tswpd,blockposswpd}, AIMZeroTol] ] ;