@q $Id: korder.hweb 2332 2009-01-14 10:26:54Z kamenik $ @> @q Copyright 2004, Ondra Kamenik @> @*2 Higher order at deterministic steady. Start of {\tt korder.h} file. The main purpose of this file is to implement a perturbation method algorithm for an SDGE model for higher order approximations. The input of the algorithm are sparse tensors as derivatives of the dynamic system, then dimensions of vector variables, then the first order approximation to the decision rule and finally a covariance matrix of exogenous shocks. The output are higher order derivatives of decision rule $y_t=g(y^*_{t-1},u_t,\sigma)$. The class provides also a method for checking a size of residuals of the solved equations. The algorithm is implemented in |KOrder| class. The class contains both unfolded and folded containers to allow for switching (usually from unfold to fold) during the calculations. The algorithm is implemented in a few templated methods. To do this, we need some container type traits, which are in |ctraits| struct. Also, the |KOrder| class contains some information encapsulated in other classes, which are defined here. These include: |PartitionY|, |MatrixA|, |MatrixS| and |MatrixB|. @s KOrder int @s ctraits int @s PartitionY int @s MatrixA int @s MatrixB int @s MatrixS int @s PLUMatrix int @s FGSTensor int @s UGSTensor int @s FGSContainer int @s UGSContainer int @s FSSparseTensor int @s TensorContainer int @s UNormalMoments int @s FNormalMoments int @s FoldedZContainer int @s UnfoldedZContainer int @s FoldedGContainer int @s UnfoldedGContainer int @s FoldedZXContainer int @s UnfoldedZXContainer int @s FoldedGXContainer int @s UnfoldedGXContainer int @s TwoDMatrix int @s ConstTwoDMatrix int @s IntSequence int @s Symmetry int @s SymmetrySet int @s symiterator int @s TensorDimens int @s Vector int @s ConstVector int @s UTensorPolynomial int @s FTensorPolynomial int @s UFSTensor int @s FFSTensor int @s GeneralSylvester int @c #ifndef KORDER_H #define KORDER_H #include "int_sequence.h" #include "fs_tensor.h" #include "gs_tensor.h" #include "t_container.h" #include "stack_container.h" #include "normal_moments.h" #include "t_polynomial.h" #include "faa_di_bruno.h" #include "journal.h" #include "kord_exception.h" #include "GeneralSylvester.h" #include #include #define TYPENAME typename @<|ctraits| type traits declaration@>; @<|PartitionY| struct declaration@>; @<|PLUMatrix| class declaration@>; @<|MatrixA| class declaration@>; @<|MatrixS| class declaration@>; @<|MatrixB| class declaration@>; @<|KOrder| class declaration@>; #endif @ Here we use a classical IF template, and in |ctraits| we define a number of types. We have a type for tensor |Ttensor|, and types for each pair of folded/unfolded containers used as a member in |KOrder|. Note that we have enumeration |fold| and |unfold|. These must have the same value as the same enumeration in |KOrder|. @s IF int @s Then int @s Else int @s RET int @<|ctraits| type traits declaration@>= class FoldedZXContainer; class UnfoldedZXContainer; class FoldedGXContainer; class UnfoldedGXContainer; template struct IF { typedef Then RET; }; template struct IF { typedef Else RET; }; template class ctraits { public:@; enum {@+ fold, unfold@+}; typedef TYPENAME IF::RET Ttensor; typedef TYPENAME IF::RET Ttensym; typedef TYPENAME IF::RET Tg; typedef TYPENAME IF::RET Tgs; typedef TYPENAME IF::RET Tgss; typedef TYPENAME IF::RET TG; typedef TYPENAME IF::RET TZstack; typedef TYPENAME IF::RET TGstack; typedef TYPENAME IF::RET Tm; typedef TYPENAME IF::RET Tpol; typedef TYPENAME IF::RET TZXstack; typedef TYPENAME IF::RET TGXstack; }; @ The |PartitionY| class defines the partitioning of state variables $y$. The vector $y$, and subvector $y^*$, and $y^{**}$ are defined. $$y=\left[\matrix{\hbox{static}\cr\hbox{predeter}\cr\hbox{both}\cr \hbox{forward}}\right],\quad y^*=\left[\matrix{\hbox{predeter}\cr\hbox{both}}\right],\quad y^{**}=\left[\matrix{\hbox{both}\cr\hbox{forward}}\right],$$ where ``static'' means variables appearing only at time $t$, ``predeter'' means variables appearing at time $t-1$, but not at $t+1$, ``both'' means variables appearing both at $t-1$ and $t+1$ (regardless appearance at $t$), and ``forward'' means variables appearing at $t+1$, but not at $t-1$. The class maintains the four lengths, and returns the whole length, length of $y^s$, and length of $y^{**}$. @<|PartitionY| struct declaration@>= struct PartitionY { const int nstat; const int npred; const int nboth; const int nforw; PartitionY(int num_stat, int num_pred, int num_both, int num_forw) : nstat(num_stat), npred(num_pred), nboth(num_both), nforw(num_forw) {} int ny() const {@+ return nstat+npred+nboth+nforw;@+} int nys() const {@+ return npred+nboth;@+} int nyss() const {@+ return nboth+nforw;@+} }; @ This is an abstraction for a square matrix with attached PLU factorization. It can calculate the PLU factorization and apply the inverse with some given matrix. We use LAPACK $PLU$ decomposition for the inverse. We store the $L$ and $U$ in the |inv| array and |ipiv| is the permutation $P$. @<|PLUMatrix| class declaration@>= class PLUMatrix : public TwoDMatrix { public:@; PLUMatrix(int n) : TwoDMatrix(n,n), inv(nrows()*ncols()), ipiv(new lapack_int[nrows()]) {} PLUMatrix(const PLUMatrix& plu); virtual ~PLUMatrix() {delete [] ipiv;} void multInv(TwoDMatrix& m) const; private:@; Vector inv; lapack_int* ipiv; protected:@; void calcPLU(); }; @ The class |MatrixA| is used for matrix $\left[f_{y}\right]+ \left[0 \left[f_{y^{**}_+}\right]\cdot\left[g^{**}_{y^*}\right] 0\right]$, which is central for the perturbation method step. @<|MatrixA| class declaration@>= class MatrixA : public PLUMatrix { public:@; MatrixA(const FSSparseTensor& f, const IntSequence& ss, const TwoDMatrix& gy, const PartitionY& ypart); }; @ The class |MatrixS| slightly differs from |MatrixA|. It is used for matrix $$\left[f_{y}\right]+ \left[0 \quad\left[f_{y^{**}_+}\right]\cdot\left[g^{**}_{y^*}\right]\quad 0\right]+\left[0\quad 0\quad\left[f_{y^{**}_+}\right]\right]$$, which is needed when recovering $g_{\sigma^k}$. @<|MatrixS| class declaration@>= class MatrixS : public PLUMatrix { public:@; MatrixS(const FSSparseTensor& f, const IntSequence& ss, const TwoDMatrix& gy, const PartitionY& ypart); }; @ The $B$ matrix is equal to $\left[f_{y^{**}_+}\right]$. We have just a constructor. @<|MatrixB| class declaration@>= class MatrixB : public TwoDMatrix { public:@; MatrixB(const FSSparseTensor& f, const IntSequence& ss) : TwoDMatrix(FGSTensor(f, ss, IntSequence(1,0), TensorDimens(ss, IntSequence(1,0)))) {} }; @ Here we have the class for the higher order approximations. It contains the following data: \halign{\kern\parindent\vrule height12pt width0pt \vtop{\hsize=4cm\noindent\raggedright #}&\kern0.5cm\vtop{\hsize=10cm\noindent #}\cr variable sizes ypart& |PartitionY| struct maintaining partitions of $y$, see |@<|PartitionY| struct declaration@>|\cr tensor variable dimension |nvs|& variable sizes of all tensors in containers, sizes of $y^*$, $u$, $u'$@q'@> and $\sigma$\cr tensor containers & folded and unfolded containers for $g$, $g_{y^*}$, $g_{y^**}$ (the latter two collect appropriate subtensors of $g$, they do not allocate any new space), $G$, $G$ stack, $Z$ stack\cr dynamic model derivatives & just a reference to the container of sparse tensors of the system derivatives, lives outside the class\cr moments & both folded and unfolded normal moment containers, both are calculated at initialization\cr matrices & matrix $A$, matrix $S$, and matrix $B$, see |@<|MatrixA| class declaration@>| and |@<|MatrixB| class declaration@>|\cr } \kern 0.4cm The methods are the following: \halign{\kern\parindent\vrule height12pt width0pt \vtop{\hsize=4cm\noindent\raggedright #}&\kern0.5cm\vtop{\hsize=10cm\noindent #}\cr member access & we declare template methods for accessing containers depending on |fold| and |unfold| flag, we implement their specializations\cr |performStep| & this performs $k$-order step provided that $k=2$ or the $k-1$-th step has been run, this is the core method\cr |check| & this calculates residuals of all solved equations for $k$-order and reports their sizes, it is runnable after $k$-order |performStep| has been run\cr |insertDerivative| & inserts a $g$ derivative to the $g$ container and also creates subtensors and insert them to $g_{y^*}$ and $g_{y^{**}}$ containers\cr |sylvesterSolve| & solve the sylvester equation (templated fold, and unfold)\cr |faaDiBrunoZ| & calculates derivatives of $F$ by Faa Di Bruno for the sparse container of system derivatives and $Z$ stack container\cr |faaDiBrunoG| & calculates derivatives of $G$ by Faa Di Bruno for the dense container $g^{**}$ and $G$ stack\cr |recover_y| & recovers $g_{y^{*i}}$\cr |recover_yu| & recovers $g_{y^{*i}u^j}$\cr |recover_ys| & recovers $g_{y^{*i}\sigma^j}$\cr |recover_yus| & recovers $g_{y^{*i}u^j\sigma^k}$\cr |recover_s| & recovers $g_{\sigma^i}$\cr |fillG| & calculates specified derivatives of $G$ and inserts them to the container\cr |calcE_ijk|& calculates $E_{ijk}$\cr |calcD_ijk|& calculates $D_{ijk}$\cr } \kern 0.3cm Most of the code is templated, and template types are calculated in |ctraits|. So all templated methods get a template argument |T|, which can be either |fold|, or |unfold|. To shorten a reference to a type calculated by |ctraits| for a particular |t|, we define the following macros. @s _Ttensor int @s _Ttensym int @s _Tg int @s _Tgs int @s _Tgss int @s _TG int @s _TZstack int @s _TGstack int @s _TZXstack int @s _TGXstack int @s __Tm int @s _Tpol int @d _Ttensor TYPENAME ctraits::Ttensor @d _Ttensym TYPENAME ctraits::Ttensym @d _Tg TYPENAME ctraits::Tg @d _Tgs TYPENAME ctraits::Tgs @d _Tgss TYPENAME ctraits::Tgss @d _TG TYPENAME ctraits::TG @d _TZstack TYPENAME ctraits::TZstack @d _TGstack TYPENAME ctraits::TGstack @d _TZXstack TYPENAME ctraits::TZXstack @d _TGXstack TYPENAME ctraits::TGXstack @d __Tm TYPENAME ctraits::Tm @d _Tpol TYPENAME ctraits::Tpol @<|KOrder| class declaration@>= class KOrder { protected:@; const PartitionY ypart; const int ny; const int nu; const int maxk; IntSequence nvs; @<|KOrder| container members@>; const MatrixA matA; const MatrixS matS; const MatrixB matB; @<|KOrder| member access method declarations@>; Journal& journal; public:@; KOrder(int num_stat, int num_pred, int num_both, int num_forw, const TensorContainer& fcont, const TwoDMatrix& gy, const TwoDMatrix& gu, const TwoDMatrix& v, Journal& jr); enum {@+ fold, unfold@+ }; @<|KOrder::performStep| templated code@>; @<|KOrder::check| templated code@>; @<|KOrder::calcStochShift| templated code@>; void switchToFolded(); const PartitionY& getPartY() const {@+ return ypart;@+} const FGSContainer& getFoldDers() const {@+ return _fg;@+} const UGSContainer& getUnfoldDers() const {@+ return _ug;@+} static bool is_even(int i) {@+ return (i/2)*2 == i;@+} protected:@; @<|KOrder::insertDerivative| templated code@>; template void sylvesterSolve(_Ttensor& der) const; @<|KOrder::faaDiBrunoZ| templated code@>; @<|KOrder::faaDiBrunoG| templated code@>; @<|KOrder::recover_y| templated code@>; @<|KOrder::recover_yu| templated code@>; @<|KOrder::recover_ys| templated code@>; @<|KOrder::recover_yus| templated code@>; @<|KOrder::recover_s| templated code@>; @<|KOrder::fillG| templated code@>; @<|KOrder::calcD_ijk| templated code@>; @<|KOrder::calcD_ik| templated code@>; @<|KOrder::calcD_k| templated code@>; @<|KOrder::calcE_ijk| templated code@>; @<|KOrder::calcE_ik| templated code@>; @<|KOrder::calcE_k| templated code@>; }; @ Here we insert the result to the container. Along the insertion, we also create subtensors and insert as well. @<|KOrder::insertDerivative| templated code@>= template void insertDerivative(_Ttensor* der) { g().insert(der); gs().insert(new _Ttensor(ypart.nstat, ypart.nys(), *der)); gss().insert(new _Ttensor(ypart.nstat+ypart.npred, ypart.nyss(), *der)); } @ Here we implement Faa Di Bruno formula $$\sum_{l=1}^k\left[f_{z^l}\right]_{\gamma_1\ldots\gamma_l} \sum_{c\in M_{l,k}}\prod_{m=1}^l\left[z_{s(c_m)}\right]^{\gamma_m}, $$ where $s$ is a given outer symmetry and $k$ is the dimension of the symmetry. @<|KOrder::faaDiBrunoZ| templated code@>= template _Ttensor* faaDiBrunoZ(const Symmetry& sym) const { JournalRecordPair pa(journal); pa << "Faa Di Bruno Z container for " << sym << endrec; _Ttensor* res = new _Ttensor(ny, TensorDimens(sym, nvs)); FaaDiBruno bruno(journal); bruno.calculate(Zstack(), f, *res); return res; } @ The same as |@<|KOrder::faaDiBrunoZ| templated code@>|, but for $g^{**}$ and $G$ stack. @<|KOrder::faaDiBrunoG| templated code@>= template _Ttensor* faaDiBrunoG(const Symmetry& sym) const { JournalRecordPair pa(journal); pa << "Faa Di Bruno G container for " << sym << endrec; TensorDimens tdims(sym, nvs); _Ttensor* res = new _Ttensor(ypart.nyss(), tdims); FaaDiBruno bruno(journal); bruno.calculate(Gstack(), gss(), *res); return res; } @ Here we solve $\left[F_{y^i}\right]=0$. First we calculate conditional $G_{y^i}$ (it misses $l=1$ and $l=i$ since $g_{y^i}$ does not exist yet). Then calculate conditional $F_{y^i}$ and we have the right hand side of equation. Since we miss two orders, we solve by Sylvester, and insert the solution as the derivative $g_{y^i}$. Then we need to update $G_{y^i}$ running |multAndAdd| for both dimensions $1$ and $i$. {\bf Requires:} everything at order $\leq i-1$ {\bf Provides:} $g_{y^i}$, and $G_{y^i}$ @<|KOrder::recover_y| templated code@>= template void recover_y(int i) { Symmetry sym(i,0,0,0); JournalRecordPair pa(journal); pa << "Recovering symmetry " << sym << endrec; _Ttensor* G_yi = faaDiBrunoG(sym); G().insert(G_yi); _Ttensor* g_yi = faaDiBrunoZ(sym); g_yi->mult(-1.0); sylvesterSolve(*g_yi); insertDerivative(g_yi); _Ttensor* gss_y = gss().get(Symmetry(1,0,0,0)); gs().multAndAdd(*gss_y, *G_yi); _Ttensor* gss_yi = gss().get(sym); gs().multAndAdd(*gss_yi, *G_yi); } @ Here we solve $\left[F_{y^iu^j}\right]=0$ to obtain $g_{y^iu^j}$ for $j>0$. We calculate conditional $G_{y^iu^j}$ (this misses only $l=1$) and calculate conditional $F_{y^iu^j}$ and we have the right hand side. It is solved by multiplication of inversion of $A$. Then we insert the result, and update $G_{y^iu^j}$ by |multAndAdd| for $l=1$. {\bf Requires:} everything at order $\leq i+j-1$, $G_{y^{i+j}}$, and $g_{y^{i+j}}$. {\bf Provides:} $g_{y^iu^j}$, and $G_{y^iu^j}$ @<|KOrder::recover_yu| templated code@>= template void recover_yu(int i, int j) { Symmetry sym(i,j,0,0); JournalRecordPair pa(journal); pa << "Recovering symmetry " << sym << endrec; _Ttensor* G_yiuj = faaDiBrunoG(sym); G().insert(G_yiuj); _Ttensor* g_yiuj = faaDiBrunoZ(sym); g_yiuj->mult(-1.0); matA.multInv(*g_yiuj); insertDerivative(g_yiuj); gs().multAndAdd(*(gss().get(Symmetry(1,0,0,0))), *G_yiuj); } @ Here we solve $\left[F_{y^i\sigma^j}\right]+\left[D_{ij}\right]+\left[E_{ij}\right]=0$ to obtain $g_{y^i\sigma^j}$. We calculate conditional $G_{y^i\sigma^j}$ (missing dimensions $1$ and $i+j$), calculate conditional $F_{y^i\sigma^j}$. Before we can calculate $D_{ij}$ and $E_{ij}$, we have to calculate $G_{y^iu'^m\sigma^{j-m}}$ for $m=1,\ldots,j$. Then we add the $D_{ij}$ and $E_{ij}$ to obtain the right hand side. Then we solve the sylvester to obtain $g_{y^i\sigma^j}$. Then we update $G_{y^i\sigma^j}$ for $l=1$ and $l=i+j$. {\bf Requires:} everything at order $\leq i+j-1$, $g_{y^{i+j}}$, $G_{y^iu'^j}$ and $g_{y^iu^j}$ through $D_{ij}$, $g_{y^iu^m\sigma^{j-m}}$ for $m=1,\ldots,j-1$ through $E_{ij}$. {\bf Provides:} $g_{y^i\sigma^j}$ and $G_{y^i\sigma^j}$, and finally $G_{y^iu'^m\sigma^{j-m}}$ for $m=1,\ldots,j$. The latter is calculated by |fillG| before the actual calculation. @<|KOrder::recover_ys| templated code@>= template void recover_ys(int i, int j) { Symmetry sym(i,0,0,j); JournalRecordPair pa(journal); pa << "Recovering symmetry " << sym << endrec; fillG(i, 0, j); if (is_even(j)) { _Ttensor* G_yisj = faaDiBrunoG(sym); G().insert(G_yisj); _Ttensor* g_yisj = faaDiBrunoZ(sym); { _Ttensor* D_ij = calcD_ik(i, j); g_yisj->add(1.0, *D_ij); delete D_ij; } if (j >= 3) { _Ttensor* E_ij = calcE_ik(i, j); g_yisj->add(1.0, *E_ij); delete E_ij; } g_yisj->mult(-1.0); sylvesterSolve(*g_yisj); insertDerivative(g_yisj); Gstack().multAndAdd(1, gss(), *G_yisj); Gstack().multAndAdd(i+j, gss(), *G_yisj); } } @ Here we solve $\left[F_{y^iu^j\sigma^k}\right]+\left[D_{ijk}\right]+\left[E_{ijk}\right]=0$ to obtain $g_{y^iu^j\sigma^k}$. First we calculate conditional $G_{y^iu^j\sigma^k}$ (missing only for dimension $l=1$), then we evaluate conditional $F_{y^iu^j\sigma^k}$. Before we can calculate $D_{ijk}$, and $E_{ijk}$, we need to insert $G_{y^iu^ju'^m\sigma^{k-m}}$ for $m=1,\ldots, k$. This is done by |fillG|. Then we have right hand side and we multiply by $A^{-1}$ to obtain $g_{y^iu^j\sigma^k}$. Finally we have to update $G_{y^iu^j\sigma^k}$ by |multAndAdd| for dimension $l=1$. {\bf Requires:} everything at order $\leq i+j+k$, $g_{y^{i+j}\sigma^k}$ through $G_{y^iu^j\sigma^k}$ involved in right hand side, then $g_{y^iu^{j+k}}$ through $D_{ijk}$, and $g_{y^iu^{j+m}\sigma^{k-m}}$ for $m=1,\ldots,k-1$ through $E_{ijk}$. {\bf Provides:} $g_{y^iu^j\sigma^k}$, $G_{y^iu^j\sigma^k}$, and $G_{y^iu^ju'^m\sigma^{k-m}}$ for $m=1,\ldots, k$ @<|KOrder::recover_yus| templated code@>= template void recover_yus(int i, int j, int k) { Symmetry sym(i,j,0,k); JournalRecordPair pa(journal); pa << "Recovering symmetry " << sym << endrec; fillG(i, j, k); if (is_even(k)) { _Ttensor* G_yiujsk = faaDiBrunoG(sym); G().insert(G_yiujsk); _Ttensor* g_yiujsk = faaDiBrunoZ(sym); { _Ttensor* D_ijk = calcD_ijk(i,j,k); g_yiujsk->add(1.0, *D_ijk); delete D_ijk; } if (k >= 3) { _Ttensor* E_ijk = calcE_ijk(i,j,k); g_yiujsk->add(1.0, *E_ijk); delete E_ijk; } g_yiujsk->mult(-1.0); matA.multInv(*g_yiujsk); insertDerivative(g_yiujsk); Gstack().multAndAdd(1, gss(), *G_yiujsk); } } @ Here we solve $\left[F_{\sigma^i}\right]+\left[D_i\right]+\left[E_i\right]=0$ to recover $g_{\sigma^i}$. First we calculate conditional $G_{\sigma^i}$ (missing dimension $l=1$ and $l=i$), then we calculate conditional $F_{\sigma^i}$. Before we can calculate $D_i$ and $E_i$, we have to obtain $G_{u'm\sigma^{i-m}}$ for $m=1,\ldots,i$. Than adding $D_i$ and $E_i$ we have the right hand side. We solve by $S^{-1}$ multiplication and update $G_{\sigma^i}$ by calling |multAndAdd| for dimension $l=1$. Recall that the solved equation here is: $$ \left[f_y\right]\left[g_{\sigma^k}\right]+ \left[f_{y^{**}_+}\right]\left[g^{**}_{y^*}\right]\left[g^*_{\sigma^k}\right]+ \left[f_{y^{**}_+}\right]\left[g^{**}_{\sigma^k}\right]=\hbox{RHS} $$ This is a sort of deficient sylvester equation (sylvester equation for dimension=0), we solve it by $S^{-1}$. See |@<|MatrixS| constructor code@>| to see how $S$ looks like. {\bf Requires:} everything at order $\leq i-1$, $g_{y^i}$ and $g_{y^{i-j}\sigma^j}$, then $g_{u^k}$ through $F_{u'^k}$, and $g_{y^mu^j\sigma^k}$ for $j=1,\ldots,i-1$ and $m+j+k=i$ through $F_{u'j\sigma^{i-j}}$. {\bf Provides:} $g_{\sigma^i}$, $G_{\sigma^i}$, and $G_{u'^m\sigma^{i-m}}$ for $m=1,\ldots,i$ @<|KOrder::recover_s| templated code@>= template void recover_s(int i) { Symmetry sym(0,0,0,i); JournalRecordPair pa(journal); pa << "Recovering symmetry " << sym << endrec; fillG(0, 0, i); if (is_even(i)) { _Ttensor* G_si = faaDiBrunoG(sym); G().insert(G_si); _Ttensor* g_si = faaDiBrunoZ(sym); { _Ttensor* D_i = calcD_k(i); g_si->add(1.0, *D_i); delete D_i; } if (i >= 3) { _Ttensor* E_i = calcE_k(i); g_si->add(1.0, *E_i); delete E_i; } g_si->mult(-1.0); matS.multInv(*g_si); insertDerivative(g_si); Gstack().multAndAdd(1, gss(), *G_si); Gstack().multAndAdd(i, gss(), *G_si); } } @ Here we calculate and insert $G_{y^iu^ju'^m\sigma^{k-m}}$ for $m=1,\ldots, k$. The derivatives are inserted only for $k-m$ being even. @<|KOrder::fillG| templated code@>= template void fillG(int i, int j, int k) { for (int m = 1; m <= k; m++) { if (is_even(k-m)) { _Ttensor* G_yiujupms = faaDiBrunoG(Symmetry(i,j,m,k-m)); G().insert(G_yiujupms); } } } @ Here we calculate $$\left[D_{ijk}\right]_{\alpha_1\ldots\alpha_i\beta_1\ldots\beta_j}= \left[F_{y^iu^ju'^k}\right] _{\alpha_1\ldots\alpha_i\beta_1\ldots\beta_j\gamma_1\ldots\gamma_k} \left[\Sigma\right]^{\gamma_1\ldots\gamma_k}$$ So it is non zero only for even $k$. @<|KOrder::calcD_ijk| templated code@>= template _Ttensor* calcD_ijk(int i, int j, int k) const { _Ttensor* res = new _Ttensor(ny, TensorDimens(Symmetry(i,j,0,0), nvs)); res->zeros(); if (is_even(k)) { _Ttensor* tmp = faaDiBrunoZ(Symmetry(i,j,k,0)); tmp->contractAndAdd(2, *res, *(m().get(Symmetry(k)))); delete tmp; } return res; } @ Here we calculate $$\left[E_{ijk}\right]_{\alpha_1\ldots\alpha_i\beta_1\ldots\beta_j}= \sum_{m=1}^{k-1}\left(\matrix{k\cr m}\right)\left[F_{y^iu^ju'^m\sigma^{k-m}}\right] _{\alpha_1\ldots\alpha_i\beta_1\ldots\beta_j\gamma_1\ldots\gamma_m} \left[\Sigma\right]^{\gamma_1\ldots\gamma_m}$$ The sum can sum only for even $m$. @<|KOrder::calcE_ijk| templated code@>= template _Ttensor* calcE_ijk(int i, int j, int k) const { _Ttensor* res = new _Ttensor(ny, TensorDimens(Symmetry(i,j,0,0), nvs)); res->zeros(); for (int n = 2; n <= k-1; n+=2) { _Ttensor* tmp = faaDiBrunoZ(Symmetry(i,j,n,k-n)); tmp->mult((double)(Tensor::noverk(k,n))); tmp->contractAndAdd(2, *res, *(m().get(Symmetry(n)))); delete tmp; } return res; } @ @<|KOrder::calcD_ik| templated code@>= template _Ttensor* calcD_ik(int i, int k) const { return calcD_ijk(i, 0, k); } @ @<|KOrder::calcD_k| templated code@>= template _Ttensor* calcD_k(int k) const { return calcD_ijk(0, 0, k); } @ @<|KOrder::calcE_ik| templated code@>= template _Ttensor* calcE_ik(int i, int k) const { return calcE_ijk(i, 0, k); } @ @<|KOrder::calcE_k| templated code@>= template _Ttensor* calcE_k(int k) const { return calcE_ijk(0, 0, k); } @ Here is the core routine. It calls methods recovering derivatives in the right order. Recall, that the code, namely Faa Di Bruno's formula, is implemented as to be run conditionally on the current contents of containers. So, if some call of Faa Di Bruno evaluates derivatives, and some derivatives are not present in the container, then it is considered to be zero. So, we have to be very careful to put everything in the right order. The order here can be derived from dependencies, or it is in the paper. The method recovers all the derivatives of the given |order|. The precondition of the method is that all tensors of order |order-1|, which are not zero, exist (including $G$). The postcondition of of the method is derivatives of $g$ and $G$ of order |order| are calculated and stored in the containers. Responsibility of precondition lays upon the constructor (for |order==2|), or upon the previous call of |performStep|. From the code, it is clear, that all $g$ are calculated. If one goes through all the recovering methods, he should find out that also all $G$ are provided. @<|KOrder::performStep| templated code@>= template void performStep(int order) { KORD_RAISE_IF(order-1 != g().getMaxDim(), "Wrong order for KOrder::performStep"); JournalRecordPair pa(journal); pa << "Performing step for order = " << order << endrec; recover_y(order); for (int i = 0; i < order; i++) { recover_yu(i, order-i); } for (int j = 1; j < order; j++) { for (int i = j-1; i >= 1; i--) { recover_yus(order-j,i,j-i); } recover_ys(order-j, j); } for (int i = order-1; i >= 1; i--) { recover_yus(0, i, order-i); } recover_s(order); } @ Here we check for residuals of all the solved equations at the given order. The method returns the largest residual size. Each check simply evaluates the equation. @<|KOrder::check| templated code@>= template double check(int dim) const { KORD_RAISE_IF(dim > g().getMaxDim(), "Wrong dimension for KOrder::check"); JournalRecordPair pa(journal); pa << "Checking residuals for order = " << dim << endrec; double maxerror = 0.0; @; @;@q'@> @; return maxerror; } @ @= for (int i = 0; i <= dim; i++) { Symmetry sym(dim-i, i, 0, 0); _Ttensor* r = faaDiBrunoZ(sym); double err = r->getData().getMax(); JournalRecord(journal) << "\terror for symmetry " << sym << "\tis " << err << endrec; if (err > maxerror) maxerror = err; delete r; } @ @= SymmetrySet ss(dim, 3); for (symiterator si(ss); !si.isEnd(); ++si) { int i = (*si)[0]; int j = (*si)[1]; int k = (*si)[2]; if (i+j > 0 && k > 0) { Symmetry sym(i, j, 0, k); _Ttensor* r = faaDiBrunoZ(sym); _Ttensor* D_ijk = calcD_ijk(i,j,k); r->add(1.0, *D_ijk); delete D_ijk; _Ttensor* E_ijk = calcE_ijk(i,j,k); r->add(1.0, *E_ijk); delete E_ijk; double err = r->getData().getMax(); JournalRecord(journal) << "\terror for symmetry " << sym << "\tis " << err << endrec; delete r; } } @ @= _Ttensor* r = faaDiBrunoZ(Symmetry(0,0,0,dim)); _Ttensor* D_k = calcD_k(dim); r->add(1.0, *D_k); delete D_k; _Ttensor* E_k = calcE_k(dim); r->add(1.0, *E_k); delete E_k; double err = r->getData().getMax(); Symmetry sym(0,0,0,dim); JournalRecord(journal) << "\terror for symmetry " << sym << "\tis " << err << endrec; if (err > maxerror) maxerror = err; delete r; @ @<|KOrder::calcStochShift| templated code@>= template Vector* calcStochShift(int order, double sigma) const { Vector* res = new Vector(ny); res->zeros(); int jfac = 1; for (int j = 1; j <= order; j++, jfac *= j) if (is_even(j)) { _Ttensor* ten = calcD_k(j); res->add(std::pow(sigma, j)/jfac, ten->getData()); delete ten; } return res; } @ These are containers. The names are not important because they do not appear anywhere else since we access them by template functions. @<|KOrder| container members@>= UGSContainer _ug; FGSContainer _fg; UGSContainer _ugs; FGSContainer _fgs; UGSContainer _ugss; FGSContainer _fgss; UGSContainer _uG; FGSContainer _fG; UnfoldedZContainer _uZstack; FoldedZContainer _fZstack; UnfoldedGContainer _uGstack; FoldedGContainer _fGstack; UNormalMoments _um; FNormalMoments _fm; const TensorContainer& f; @ These are the declarations of the template functions accessing the containers. @<|KOrder| member access method declarations@>= template _Tg& g(); template const _Tg& g() const; template _Tgs& gs(); template const _Tgs& gs() const; template _Tgss& gss(); template const _Tgss& gss() const; template _TG& G(); template const _TG& G() const; template _TZstack& Zstack(); template const _TZstack& Zstack() const; template _TGstack& Gstack(); template const _TGstack& Gstack() const; template __Tm& m(); template const __Tm& m() const; @ End of {\tt korder.h} file.