2019-06-19 14:34:30 +02:00
|
|
|
|
/*
|
|
|
|
|
* Copyright © 2004 Ondra Kamenik
|
|
|
|
|
* Copyright © 2019 Dynare Team
|
|
|
|
|
*
|
|
|
|
|
* This file is part of Dynare.
|
|
|
|
|
*
|
|
|
|
|
* Dynare is 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.
|
|
|
|
|
*
|
|
|
|
|
* Dynare 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.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2021-06-09 17:33:48 +02:00
|
|
|
|
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
|
2019-06-19 14:34:30 +02:00
|
|
|
|
*/
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
// Higher order at deterministic steady
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* The main purpose of this file is to implement a perturbation method
|
|
|
|
|
algorithm for an DSGE 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ₜ=g(y*ₜ₋₁,uₜ,σ). 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.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef KORDER_H
|
|
|
|
|
#define KORDER_H
|
|
|
|
|
|
2019-01-08 16:09:25 +01:00
|
|
|
|
#include "int_sequence.hh"
|
|
|
|
|
#include "fs_tensor.hh"
|
|
|
|
|
#include "gs_tensor.hh"
|
|
|
|
|
#include "t_container.hh"
|
|
|
|
|
#include "stack_container.hh"
|
|
|
|
|
#include "normal_moments.hh"
|
|
|
|
|
#include "t_polynomial.hh"
|
2019-01-04 16:29:57 +01:00
|
|
|
|
#include "faa_di_bruno.hh"
|
|
|
|
|
#include "journal.hh"
|
2019-02-19 15:57:19 +01:00
|
|
|
|
#include "pascal_triangle.hh"
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
#include "kord_exception.hh"
|
2019-01-08 17:12:05 +01:00
|
|
|
|
#include "GeneralSylvester.hh"
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
#include <dynlapack.h>
|
|
|
|
|
|
|
|
|
|
#include <cmath>
|
2019-01-09 17:26:28 +01:00
|
|
|
|
#include <type_traits>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-03-06 18:40:19 +01:00
|
|
|
|
// The enum class passed as template parameter for many data structures
|
|
|
|
|
enum class Storage { fold, unfold };
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* 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. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
class FoldedZXContainer;
|
|
|
|
|
class UnfoldedZXContainer;
|
|
|
|
|
class FoldedGXContainer;
|
|
|
|
|
class UnfoldedGXContainer;
|
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage type>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
class ctraits
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-03-06 18:40:19 +01:00
|
|
|
|
using Ttensor = std::conditional_t<type == Storage::fold, FGSTensor, UGSTensor>;
|
|
|
|
|
using Ttensym = std::conditional_t<type == Storage::fold, FFSTensor, UFSTensor>;
|
|
|
|
|
using Tg = std::conditional_t<type == Storage::fold, FGSContainer, UGSContainer>;
|
|
|
|
|
using Tgs = std::conditional_t<type == Storage::fold, FGSContainer, UGSContainer>;
|
|
|
|
|
using Tgss = std::conditional_t<type == Storage::fold, FGSContainer, UGSContainer>;
|
|
|
|
|
using TG = std::conditional_t<type == Storage::fold, FGSContainer, UGSContainer>;
|
|
|
|
|
using TZstack = std::conditional_t<type == Storage::fold, FoldedZContainer, UnfoldedZContainer>;
|
|
|
|
|
using TGstack = std::conditional_t<type == Storage::fold, FoldedGContainer, UnfoldedGContainer>;
|
|
|
|
|
using Tm = std::conditional_t<type == Storage::fold, FNormalMoments, UNormalMoments>;
|
|
|
|
|
using Tpol = std::conditional_t<type == Storage::fold, FTensorPolynomial, UTensorPolynomial>;
|
|
|
|
|
using TZXstack = std::conditional_t<type == Storage::fold, FoldedZXContainer, UnfoldedZXContainer>;
|
|
|
|
|
using TGXstack = std::conditional_t<type == Storage::fold, FoldedGXContainer, UnfoldedGXContainer>;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
};
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* The PartitionY class defines the partitioning of state variables y. The
|
|
|
|
|
vector y, and subvectors y* and y** are defined as:
|
2019-03-07 18:17:43 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
⎡ static⎤
|
|
|
|
|
⎢ pred ⎥
|
|
|
|
|
y = ⎢ both ⎥ ⎡pred⎤ ⎡ both ⎤
|
|
|
|
|
⎣forward⎦ y* = ⎣both⎦ y** = ⎣forward⎦
|
2019-03-07 18:17:43 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
where ‘static’ means variables appearing only at time t, ‘pred’ means
|
|
|
|
|
variables appearing at time t−1, but not at t+1, ‘both’ means variables
|
|
|
|
|
appearing both at t−1 and t+1 (regardless of appearance at t), and ‘forward’
|
|
|
|
|
means variables appearing at t+1 but not at t−1.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
The class maintains the four lengths, and returns the whole length, length
|
|
|
|
|
of y*, and length of y**$.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* 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.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
We use LAPACK PLU decomposition for the inverse. We store the L and U in the
|
|
|
|
|
‘inv’ array and ‘ipiv’ is the permutation P. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
class PLUMatrix : public TwoDMatrix
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
PLUMatrix(int n)
|
|
|
|
|
: TwoDMatrix(n, n),
|
|
|
|
|
inv(nrows()*ncols()),
|
2019-03-06 18:40:19 +01:00
|
|
|
|
ipiv(nrows())
|
2019-01-04 16:29:57 +01:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
void multInv(TwoDMatrix &m) const;
|
|
|
|
|
private:
|
|
|
|
|
Vector inv;
|
2019-03-06 18:40:19 +01:00
|
|
|
|
std::vector<lapack_int> ipiv;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
protected:
|
|
|
|
|
void calcPLU();
|
|
|
|
|
};
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* The class MatrixA is used for matrix [f_y] + [0 [f_y**₊]·[g**_y*] 0]
|
2019-01-04 16:29:57 +01:00
|
|
|
|
which is central for the perturbation method step. */
|
|
|
|
|
|
|
|
|
|
class MatrixA : public PLUMatrix
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
MatrixA(const FSSparseTensor &f, const IntSequence &ss,
|
|
|
|
|
const TwoDMatrix &gy, const PartitionY &ypart);
|
|
|
|
|
};
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* The class MatrixS slightly differs from MatrixA. It is used for
|
|
|
|
|
[f_y] + [0 [f_y**₊]·[g**_y*] 0] + [0 0 [f_y**₊]]
|
|
|
|
|
which is needed when recovering g_σᵏ. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
class MatrixS : public PLUMatrix
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
MatrixS(const FSSparseTensor &f, const IntSequence &ss,
|
|
|
|
|
const TwoDMatrix &gy, const PartitionY &ypart);
|
|
|
|
|
};
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* The B matrix is equal to [f_y**₊]. We have just a constructor. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
class MatrixB : public TwoDMatrix
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
MatrixB(const FSSparseTensor &f, const IntSequence &ss)
|
|
|
|
|
: TwoDMatrix(FGSTensor(f, ss, IntSequence(1, 0),
|
|
|
|
|
TensorDimens(ss, IntSequence(1, 0))))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Class for the higher order approximations.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
Most of the code is templated, and template types are calculated in
|
2019-06-11 16:45:09 +02:00
|
|
|
|
‘ctraits’. So all templated methods get a template argument ‘T’, which
|
|
|
|
|
can be either ‘Storage::fold’, or ‘Storage::unfold’.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
class KOrder
|
|
|
|
|
{
|
|
|
|
|
protected:
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Variable sizes: PartitionY struct maintaining partitions of y, see
|
|
|
|
|
PartitionY struct declaration */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
const PartitionY ypart;
|
2019-06-11 16:45:09 +02:00
|
|
|
|
|
2019-01-04 16:29:57 +01:00
|
|
|
|
const int ny;
|
|
|
|
|
const int nu;
|
|
|
|
|
const int maxk;
|
2019-06-11 16:45:09 +02:00
|
|
|
|
|
|
|
|
|
/* Tensor variable dimension: variable sizes of all tensors in
|
|
|
|
|
containers, sizes of y*, u, u′ and σ */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
IntSequence nvs;
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* 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
|
|
|
|
|
|
|
|
|
|
The names are not important because they do not appear anywhere else since
|
|
|
|
|
we access them by template functions. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
UGSContainer _ug;
|
|
|
|
|
FGSContainer _fg;
|
|
|
|
|
UGSContainer _ugs;
|
|
|
|
|
FGSContainer _fgs;
|
|
|
|
|
UGSContainer _ugss;
|
|
|
|
|
FGSContainer _fgss;
|
|
|
|
|
UGSContainer _uG;
|
|
|
|
|
FGSContainer _fG;
|
|
|
|
|
UnfoldedZContainer _uZstack;
|
|
|
|
|
FoldedZContainer _fZstack;
|
|
|
|
|
UnfoldedGContainer _uGstack;
|
|
|
|
|
FoldedGContainer _fGstack;
|
2019-06-11 16:45:09 +02:00
|
|
|
|
|
|
|
|
|
/* Moments: both folded and unfolded normal moment containers, both are
|
|
|
|
|
calculated at initialization */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
UNormalMoments _um;
|
|
|
|
|
FNormalMoments _fm;
|
2019-06-11 16:45:09 +02:00
|
|
|
|
|
|
|
|
|
/* Dynamic model derivatives: just a reference to the container of sparse
|
|
|
|
|
tensors of the system derivatives, lives outside the class */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
const TensorContainer<FSSparseTensor> &f;
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Matrices: matrix A, matrix S, and matrix B, see MatrixA and MatrixB class
|
|
|
|
|
declarations */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
const MatrixA matA;
|
|
|
|
|
const MatrixS matS;
|
|
|
|
|
const MatrixB matB;
|
|
|
|
|
|
|
|
|
|
/* These are the declarations of the template functions accessing the
|
2019-06-11 16:45:09 +02:00
|
|
|
|
containers. We declare template methods for accessing containers depending
|
|
|
|
|
on ‘fold’ and ‘unfold’ flag, we implement their specializations*/
|
2019-03-06 18:40:19 +01:00
|
|
|
|
template<Storage t>
|
|
|
|
|
typename ctraits<t>::Tg &g();
|
|
|
|
|
template<Storage t>
|
|
|
|
|
const typename ctraits<t>::Tg &g() const;
|
|
|
|
|
|
|
|
|
|
template<Storage t>
|
|
|
|
|
typename ctraits<t>::Tgs &gs();
|
|
|
|
|
template<Storage t>
|
|
|
|
|
const typename ctraits<t>::Tgs &gs() const;
|
|
|
|
|
|
|
|
|
|
template<Storage t>
|
|
|
|
|
typename ctraits<t>::Tgss &gss();
|
|
|
|
|
template<Storage t>
|
|
|
|
|
const typename ctraits<t>::Tgss &gss() const;
|
|
|
|
|
|
|
|
|
|
template<Storage t>
|
|
|
|
|
typename ctraits<t>::TG &G();
|
|
|
|
|
template<Storage t>
|
|
|
|
|
const typename ctraits<t>::TG &G() const;
|
|
|
|
|
|
|
|
|
|
template<Storage t>
|
|
|
|
|
typename ctraits<t>::TZstack &Zstack();
|
|
|
|
|
template<Storage t>
|
|
|
|
|
const typename ctraits<t>::TZstack &Zstack() const;
|
|
|
|
|
|
|
|
|
|
template<Storage t>
|
|
|
|
|
typename ctraits<t>::TGstack &Gstack();
|
|
|
|
|
template<Storage t>
|
|
|
|
|
const typename ctraits<t>::TGstack &Gstack() const;
|
|
|
|
|
|
|
|
|
|
template<Storage t>
|
|
|
|
|
typename ctraits<t>::Tm &m();
|
|
|
|
|
template<Storage t>
|
|
|
|
|
const typename ctraits<t>::Tm &m() const;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
Journal &journal;
|
|
|
|
|
public:
|
|
|
|
|
KOrder(int num_stat, int num_pred, int num_both, int num_forw,
|
|
|
|
|
const TensorContainer<FSSparseTensor> &fcont,
|
|
|
|
|
const TwoDMatrix &gy, const TwoDMatrix &gu, const TwoDMatrix &v,
|
|
|
|
|
Journal &jr);
|
2019-06-11 16:45:09 +02:00
|
|
|
|
|
|
|
|
|
/* Performs k-order step provided that k=2 or the k−1-th step has been
|
|
|
|
|
run, this is the core method */
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void performStep(int order);
|
2019-06-11 16:45:09 +02:00
|
|
|
|
|
|
|
|
|
/* Calculates residuals of all solved equations for k-order and reports their
|
|
|
|
|
sizes, it is runnable after k-order performStep() has been run */
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
double check(int dim) const;
|
2019-06-11 16:45:09 +02:00
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
Vector calcStochShift(int order, double sigma) const;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
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)
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
return i % 2 == 0;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
protected:
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Inserts a g derivative to the g container and also creates subtensors and
|
|
|
|
|
insert them to g_y* and g_y** containers */
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
void insertDerivative(std::unique_ptr<typename ctraits<t>::Ttensor> der);
|
2019-06-11 16:45:09 +02:00
|
|
|
|
|
|
|
|
|
/* Solves the sylvester equation (templated fold, and unfold) */
|
2019-03-06 18:40:19 +01:00
|
|
|
|
template<Storage t>
|
|
|
|
|
void sylvesterSolve(typename ctraits<t>::Ttensor &der) const;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Calculates derivatives of F by Faà Di Bruno for the sparse container of
|
|
|
|
|
system derivatives and Z stack container */
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
std::unique_ptr<typename ctraits<t>::Ttensor> faaDiBrunoZ(const Symmetry &sym) const;
|
2019-06-11 16:45:09 +02:00
|
|
|
|
|
|
|
|
|
/* Calculates derivatives of G by Faà Di Bruno for the dense container g**
|
|
|
|
|
and G stack */
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
std::unique_ptr<typename ctraits<t>::Ttensor> faaDiBrunoG(const Symmetry &sym) const;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Recovers g_y*ⁱ
|
2019-03-06 18:40:19 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void recover_y(int i);
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Recovers g_y*ⁱuʲ
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void recover_yu(int i, int j);
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Recovers g_y*ⁱσʲ
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void recover_ys(int i, int j);
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Recovers g_y*ⁱuʲσᵏ
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void recover_yus(int i, int j, int k);
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Recovers g_σⁱ
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void recover_s(int i);
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Calculates specified derivatives of G and inserts them to the container
|
2019-03-06 18:40:19 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void fillG(int i, int j, int k);
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Calculates Dᵢⱼₖ
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor calcD_ijk(int i, int j, int k) const;
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor calcD_ik(int i, int k) const;
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor calcD_k(int k) const;
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Calculates Eᵢⱼₖ
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor calcE_ijk(int i, int j, int k) const;
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor calcE_ik(int i, int k) const;
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor calcE_k(int k) const;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Here we insert the result to the container. Along the insertion, we
|
|
|
|
|
also create subtensors and insert as well. */
|
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void
|
2019-03-06 18:40:19 +01:00
|
|
|
|
KOrder::insertDerivative(std::unique_ptr<typename ctraits<t>::Ttensor> der)
|
2019-01-04 16:29:57 +01:00
|
|
|
|
{
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto der_ptr = der.get();
|
|
|
|
|
g<t>().insert(std::move(der));
|
2019-03-06 18:40:19 +01:00
|
|
|
|
gs<t>().insert(std::make_unique<typename ctraits<t>::Ttensor>(ypart.nstat, ypart.nys(), *der_ptr));
|
|
|
|
|
gss<t>().insert(std::make_unique<typename ctraits<t>::Ttensor>(ypart.nstat+ypart.npred,
|
|
|
|
|
ypart.nyss(), *der_ptr));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
/* Here we implement Faà Di Bruno formula
|
2019-06-11 16:45:09 +02:00
|
|
|
|
|
|
|
|
|
ₖ ₗ
|
|
|
|
|
∑ [f_zˡ]_γ₁…γₗ ∑ ∏ [z_{s^|cₘ|}]^γₘ
|
|
|
|
|
ˡ⁼¹ c∈ℳₗ,ₖ ᵐ⁼¹
|
|
|
|
|
|
|
|
|
|
where s is a given outer symmetry and k is the dimension of the symmetry. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
std::unique_ptr<typename ctraits<t>::Ttensor>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
KOrder::faaDiBrunoZ(const Symmetry &sym) const
|
|
|
|
|
{
|
|
|
|
|
JournalRecordPair pa(journal);
|
2019-03-28 18:56:46 +01:00
|
|
|
|
pa << u8"Faà Di Bruno Z container for " << sym << endrec;
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto res = std::make_unique<typename ctraits<t>::Ttensor>(ny, TensorDimens(sym, nvs));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
FaaDiBruno bruno(journal);
|
|
|
|
|
bruno.calculate(Zstack<t>(), f, *res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* The same as KOrder::faaDiBrunoZ(), but for g** and G stack. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
std::unique_ptr<typename ctraits<t>::Ttensor>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
KOrder::faaDiBrunoG(const Symmetry &sym) const
|
|
|
|
|
{
|
|
|
|
|
JournalRecordPair pa(journal);
|
2019-03-28 18:56:46 +01:00
|
|
|
|
pa << u8"Faà Di Bruno G container for " << sym << endrec;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
TensorDimens tdims(sym, nvs);
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto res = std::make_unique<typename ctraits<t>::Ttensor>(ypart.nyss(), tdims);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
FaaDiBruno bruno(journal);
|
|
|
|
|
bruno.calculate(Gstack<t>(), gss<t>(), *res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Here we solve [F_yⁱ]=0. First we calculate conditional G_yⁱ (it misses l=1
|
|
|
|
|
and l=i since g_yⁱ does not exist yet). Then calculate conditional F_yⁱ 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ⁱ. Then we need
|
|
|
|
|
to update G_yⁱ running multAndAdd() for both dimensions 1 and i.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
Requires: everything at order ≤ i−1
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
Provides: g_yⁱ and G_yⁱ
|
2019-12-20 14:36:20 +01:00
|
|
|
|
*/
|
2019-03-06 18:40:19 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void
|
|
|
|
|
KOrder::recover_y(int i)
|
|
|
|
|
{
|
2019-02-08 18:38:05 +01:00
|
|
|
|
Symmetry sym{i, 0, 0, 0};
|
2019-01-04 16:29:57 +01:00
|
|
|
|
JournalRecordPair pa(journal);
|
|
|
|
|
pa << "Recovering symmetry " << sym << endrec;
|
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto G_yi = faaDiBrunoG<t>(sym);
|
|
|
|
|
auto G_yi_ptr = G_yi.get();
|
|
|
|
|
G<t>().insert(std::move(G_yi));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto g_yi = faaDiBrunoZ<t>(sym);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
g_yi->mult(-1.0);
|
|
|
|
|
|
|
|
|
|
sylvesterSolve<t>(*g_yi);
|
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
insertDerivative<t>(std::move(g_yi));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto &gss_y = gss<t>().get(Symmetry{1, 0, 0, 0});
|
2019-02-20 17:51:05 +01:00
|
|
|
|
gs<t>().multAndAdd(gss_y, *G_yi_ptr);
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto &gss_yi = gss<t>().get(sym);
|
2019-02-20 17:51:05 +01:00
|
|
|
|
gs<t>().multAndAdd(gss_yi, *G_yi_ptr);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Here we solve [F_yⁱuʲ]=0 to obtain g_yⁱuʲ for j>0. We calculate conditional
|
|
|
|
|
G_yⁱuʲ (this misses only l=1) and calculate conditional F_yⁱuʲ 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ⁱuʲ by multAndAdd() for l=1.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
Requires: everything at order ≤ i+j−1, G_yⁱ⁺ʲ and g_yⁱ⁺ʲ.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
Provides: g_yⁱuʲ and G_yⁱuʲ
|
2019-12-20 14:36:20 +01:00
|
|
|
|
*/
|
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void
|
|
|
|
|
KOrder::recover_yu(int i, int j)
|
|
|
|
|
{
|
2019-02-08 18:38:05 +01:00
|
|
|
|
Symmetry sym{i, j, 0, 0};
|
2019-01-04 16:29:57 +01:00
|
|
|
|
JournalRecordPair pa(journal);
|
|
|
|
|
pa << "Recovering symmetry " << sym << endrec;
|
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto G_yiuj = faaDiBrunoG<t>(sym);
|
|
|
|
|
auto G_yiuj_ptr = G_yiuj.get();
|
|
|
|
|
G<t>().insert(std::move(G_yiuj));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto g_yiuj = faaDiBrunoZ<t>(sym);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
g_yiuj->mult(-1.0);
|
|
|
|
|
matA.multInv(*g_yiuj);
|
2019-02-20 16:50:33 +01:00
|
|
|
|
insertDerivative<t>(std::move(g_yiuj));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-02-20 17:51:05 +01:00
|
|
|
|
gs<t>().multAndAdd(gss<t>().get(Symmetry{1, 0, 0, 0}), *G_yiuj_ptr);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Here we solve [F_yⁱσʲ]+[Dᵢⱼ]+[Eᵢⱼ]=0 to obtain g_yⁱσʲ. We calculate
|
|
|
|
|
conditional G_yⁱσʲ (missing dimensions 1 and i+j), calculate conditional
|
|
|
|
|
F_yⁱσʲ. Before we can calculate Dᵢⱼ and Eᵢⱼ, we have to calculate
|
|
|
|
|
G_yⁱu′ᵐσʲ⁻ᵐ for m=1,…,j. Then we add the Dᵢⱼ and Eᵢⱼ to obtain the right
|
|
|
|
|
hand side. Then we solve the sylvester to obtain g_yⁱσʲ. Then we update
|
|
|
|
|
G_yⁱσʲ for l=1 and l=i+j.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
Requires: everything at order ≤ i+j−1, g_yⁱ⁺ʲ, G_yⁱu′ʲ and g_yⁱuʲ through
|
|
|
|
|
Dᵢⱼ, g_yⁱuᵐσʲ⁻ᵐ for m=1,…,j−1 through Eᵢⱼ.
|
|
|
|
|
|
|
|
|
|
Provides: g_yⁱσʲ and G_yⁱσʲ, and finally G_yⁱu′ᵐσʲ⁻ᵐ for m=1,…,j. The latter
|
|
|
|
|
is calculated by fillG() before the actual calculation.
|
2019-12-20 14:36:20 +01:00
|
|
|
|
*/
|
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void
|
|
|
|
|
KOrder::recover_ys(int i, int j)
|
|
|
|
|
{
|
2019-02-08 18:38:05 +01:00
|
|
|
|
Symmetry sym{i, 0, 0, j};
|
2019-01-04 16:29:57 +01:00
|
|
|
|
JournalRecordPair pa(journal);
|
|
|
|
|
pa << "Recovering symmetry " << sym << endrec;
|
|
|
|
|
|
|
|
|
|
fillG<t>(i, 0, j);
|
|
|
|
|
|
|
|
|
|
if (is_even(j))
|
|
|
|
|
{
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto G_yisj = faaDiBrunoG<t>(sym);
|
|
|
|
|
auto G_yisj_ptr = G_yisj.get();
|
|
|
|
|
G<t>().insert(std::move(G_yisj));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto g_yisj = faaDiBrunoZ<t>(sym);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto D_ij = calcD_ik<t>(i, j);
|
|
|
|
|
g_yisj->add(1.0, D_ij);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (j >= 3)
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto E_ij = calcE_ik<t>(i, j);
|
|
|
|
|
g_yisj->add(1.0, E_ij);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_yisj->mult(-1.0);
|
|
|
|
|
|
|
|
|
|
sylvesterSolve<t>(*g_yisj);
|
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
insertDerivative<t>(std::move(g_yisj));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
Gstack<t>().multAndAdd(1, gss<t>(), *G_yisj_ptr);
|
|
|
|
|
Gstack<t>().multAndAdd(i+j, gss<t>(), *G_yisj_ptr);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Here we solve [F_yⁱuʲσᵏ]+[Dᵢⱼₖ]+[Eᵢⱼₖ]=0 to obtain g_yⁱuʲσᵏ. First we
|
|
|
|
|
calculate conditional G_yⁱuʲσᵏ (missing only for dimension l=1), then we
|
|
|
|
|
evaluate conditional F_yⁱuʲσᵏ. Before we can calculate Dᵢⱼₖ, and Eᵢⱼₖ, we
|
|
|
|
|
need to insert G_yⁱuʲu′ᵐσᵏ⁻ᵐ for m=1,…,k. This is done by fillG(). Then we
|
|
|
|
|
have right hand side and we multiply by A⁻¹ to obtain g_yⁱuʲσᵏ. Finally we
|
|
|
|
|
have to update G_yⁱuʲσᵏ by multAndAdd() for dimension l=1.
|
|
|
|
|
|
|
|
|
|
Requires: everything at order ≤ i+j+k, g_yⁱ⁺ʲσᵏ through G_yⁱuʲσᵏ involved in
|
|
|
|
|
right hand side, then g_yⁱuʲ⁺ᵏ through Dᵢⱼₖ, and g_yⁱuʲ⁺ᵐσᵏ⁻ᵐ for m=1,…,k−1
|
|
|
|
|
through Eᵢⱼₖ.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
Provides: g_yⁱuʲσᵏ, G_yⁱuʲσᵏ, and G_yⁱuʲu′ᵐσᵏ⁻ᵐ for m=1,…,k
|
2019-12-20 14:36:20 +01:00
|
|
|
|
*/
|
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void
|
|
|
|
|
KOrder::recover_yus(int i, int j, int k)
|
|
|
|
|
{
|
2019-02-08 18:38:05 +01:00
|
|
|
|
Symmetry sym{i, j, 0, k};
|
2019-01-04 16:29:57 +01:00
|
|
|
|
JournalRecordPair pa(journal);
|
|
|
|
|
pa << "Recovering symmetry " << sym << endrec;
|
|
|
|
|
|
|
|
|
|
fillG<t>(i, j, k);
|
|
|
|
|
|
|
|
|
|
if (is_even(k))
|
|
|
|
|
{
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto G_yiujsk = faaDiBrunoG<t>(sym);
|
|
|
|
|
auto G_yiujsk_ptr = G_yiujsk.get();
|
|
|
|
|
G<t>().insert(std::move(G_yiujsk));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto g_yiujsk = faaDiBrunoZ<t>(sym);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto D_ijk = calcD_ijk<t>(i, j, k);
|
|
|
|
|
g_yiujsk->add(1.0, D_ijk);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (k >= 3)
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto E_ijk = calcE_ijk<t>(i, j, k);
|
|
|
|
|
g_yiujsk->add(1.0, E_ijk);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_yiujsk->mult(-1.0);
|
|
|
|
|
|
|
|
|
|
matA.multInv(*g_yiujsk);
|
2019-02-20 16:50:33 +01:00
|
|
|
|
insertDerivative<t>(std::move(g_yiujsk));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
Gstack<t>().multAndAdd(1, gss<t>(), *G_yiujsk_ptr);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Here we solve [F_{σⁱ}]+[Dᵢ]+[Eᵢ]=0 to recover g_σⁱ. First we calculate
|
|
|
|
|
conditional G_σⁱ (missing dimension l=1 and l=i), then we calculate
|
|
|
|
|
conditional F_σⁱ. Before we can calculate Dᵢ and Eᵢ, we have to obtain
|
|
|
|
|
G_u′ᵐσⁱ⁻ᵐ for m=1,…,i. Then adding Dᵢ and Eᵢ we have the right hand side. We
|
|
|
|
|
solve by S⁻¹ multiplication and update G_σⁱ by calling multAndAdd() for
|
|
|
|
|
dimension l=1.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
Recall that the solved equation here is:
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
[f_y][g_σᵏ]+[f_y**₊][g**_y*][g*_σᵏ]+[f_y**₊][g**_σᵏ] = RHS
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
This is a sort of deficient sylvester equation (sylvester equation for
|
|
|
|
|
dimension=0), we solve it by S⁻¹. See the constructor of MatrixS to see how
|
|
|
|
|
S looks like.
|
|
|
|
|
|
|
|
|
|
Requires: everything at order ≤ i−1, g_yⁱ and g_yⁱ⁻ʲσʲ, then g_uᵏ through
|
|
|
|
|
F_u′ᵏ, and g_yᵐuʲσᵏ for j=1,…,i−1 and m+j+k=i through F_u′ʲσⁱ⁻ʲ.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
Provides: g_σⁱ, G_σⁱ, and G_u′ᵐσⁱ⁻ᵐ for m=1,…,i
|
2019-12-20 14:36:20 +01:00
|
|
|
|
*/
|
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void
|
|
|
|
|
KOrder::recover_s(int i)
|
|
|
|
|
{
|
2019-02-08 18:38:05 +01:00
|
|
|
|
Symmetry sym{0, 0, 0, i};
|
2019-01-04 16:29:57 +01:00
|
|
|
|
JournalRecordPair pa(journal);
|
|
|
|
|
pa << "Recovering symmetry " << sym << endrec;
|
|
|
|
|
|
|
|
|
|
fillG<t>(0, 0, i);
|
|
|
|
|
|
|
|
|
|
if (is_even(i))
|
|
|
|
|
{
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto G_si = faaDiBrunoG<t>(sym);
|
|
|
|
|
auto G_si_ptr = G_si.get();
|
|
|
|
|
G<t>().insert(std::move(G_si));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto g_si = faaDiBrunoZ<t>(sym);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto D_i = calcD_k<t>(i);
|
|
|
|
|
g_si->add(1.0, D_i);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i >= 3)
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto E_i = calcE_k<t>(i);
|
|
|
|
|
g_si->add(1.0, E_i);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_si->mult(-1.0);
|
|
|
|
|
|
|
|
|
|
matS.multInv(*g_si);
|
2019-02-20 16:50:33 +01:00
|
|
|
|
insertDerivative<t>(std::move(g_si));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
Gstack<t>().multAndAdd(1, gss<t>(), *G_si_ptr);
|
|
|
|
|
Gstack<t>().multAndAdd(i, gss<t>(), *G_si_ptr);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Here we calculate and insert G_yⁱuʲu′ᵐσᵏ⁻ᵐ for m=1,…,k. The derivatives are
|
|
|
|
|
inserted only for k−m being even. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-03-06 18:40:19 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void
|
|
|
|
|
KOrder::fillG(int i, int j, int k)
|
|
|
|
|
{
|
|
|
|
|
for (int m = 1; m <= k; m++)
|
2019-03-06 18:40:19 +01:00
|
|
|
|
if (is_even(k-m))
|
|
|
|
|
{
|
|
|
|
|
auto G_yiujupms = faaDiBrunoG<t>(Symmetry{i, j, m, k-m});
|
|
|
|
|
G<t>().insert(std::move(G_yiujupms));
|
|
|
|
|
}
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Here we calculate:
|
|
|
|
|
|
|
|
|
|
[Dᵢⱼₖ]_α₁…αᵢβ₁…βⱼ = [F_yⁱuʲu′ᵏ]_α₁…αᵢβ₁…βⱼγ₁…γₖ [Σ]^γ₁…γₖ
|
|
|
|
|
|
|
|
|
|
So it is non zero only for even k. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor
|
2019-01-04 16:29:57 +01:00
|
|
|
|
KOrder::calcD_ijk(int i, int j, int k) const
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor res(ny, TensorDimens(Symmetry{i, j, 0, 0}, nvs));
|
|
|
|
|
res.zeros();
|
2019-01-04 16:29:57 +01:00
|
|
|
|
if (is_even(k))
|
|
|
|
|
{
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto tmp = faaDiBrunoZ<t>(Symmetry{i, j, k, 0});
|
2019-03-06 18:40:19 +01:00
|
|
|
|
tmp->contractAndAdd(2, res, m<t>().get(Symmetry{k}));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Here we calculate
|
2019-06-11 16:45:09 +02:00
|
|
|
|
ₖ₋₁ ⎛k⎞
|
|
|
|
|
[Eᵢⱼₖ]_α₁…αᵢβ₁…βⱼ = ∑ ⎝m⎠ [F_yⁱuʲu′ᵐσᵏ⁻ᵐ]_α₁…αᵢβ₁…βⱼγ₁…γₘ [Σ]^γ₁…γₘ
|
|
|
|
|
ᵐ⁼¹
|
|
|
|
|
The sum can sum only for even m. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor
|
2019-01-04 16:29:57 +01:00
|
|
|
|
KOrder::calcE_ijk(int i, int j, int k) const
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor res(ny, TensorDimens(Symmetry{i, j, 0, 0}, nvs));
|
|
|
|
|
res.zeros();
|
2019-01-04 16:29:57 +01:00
|
|
|
|
for (int n = 2; n <= k-1; n += 2)
|
|
|
|
|
{
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto tmp = faaDiBrunoZ<t>(Symmetry{i, j, n, k-n});
|
2019-03-06 18:40:19 +01:00
|
|
|
|
tmp->mult(static_cast<double>(PascalTriangle::noverk(k, n)));
|
|
|
|
|
tmp->contractAndAdd(2, res, m<t>().get(Symmetry{n}));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor
|
2019-01-04 16:29:57 +01:00
|
|
|
|
KOrder::calcD_ik(int i, int k) const
|
|
|
|
|
{
|
|
|
|
|
return calcD_ijk<t>(i, 0, k);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor
|
2019-01-04 16:29:57 +01:00
|
|
|
|
KOrder::calcD_k(int k) const
|
|
|
|
|
{
|
|
|
|
|
return calcD_ijk<t>(0, 0, k);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor
|
2019-01-04 16:29:57 +01:00
|
|
|
|
KOrder::calcE_ik(int i, int k) const
|
|
|
|
|
{
|
|
|
|
|
return calcE_ijk<t>(i, 0, k);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor
|
2019-01-04 16:29:57 +01:00
|
|
|
|
KOrder::calcE_k(int k) const
|
|
|
|
|
{
|
|
|
|
|
return calcE_ijk<t>(0, 0, k);
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* Here is the core routine. It calls methods recovering derivatives in the
|
|
|
|
|
right order. Recall, that the code, namely Faà Di Bruno’s formula, is
|
|
|
|
|
implemented as to be run conditionally on the current contents of
|
|
|
|
|
containers. So, if some call of Faà 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.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
The method recovers all the derivatives of the given ‘order’.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
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().
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
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. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void
|
|
|
|
|
KOrder::performStep(int order)
|
|
|
|
|
{
|
|
|
|
|
KORD_RAISE_IF(order-1 != g<t>().getMaxDim(),
|
|
|
|
|
"Wrong order for KOrder::performStep");
|
|
|
|
|
JournalRecordPair pa(journal);
|
|
|
|
|
pa << "Performing step for order = " << order << endrec;
|
|
|
|
|
|
|
|
|
|
recover_y<t>(order);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < order; i++)
|
2019-03-06 18:40:19 +01:00
|
|
|
|
recover_yu<t>(i, order-i);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
for (int j = 1; j < order; j++)
|
|
|
|
|
{
|
|
|
|
|
for (int i = j-1; i >= 1; i--)
|
2019-03-06 18:40:19 +01:00
|
|
|
|
recover_yus<t>(order-j, i, j-i);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
recover_ys<t>(order-j, j);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = order-1; i >= 1; i--)
|
2019-03-06 18:40:19 +01:00
|
|
|
|
recover_yus<t>(0, i, order-i);
|
|
|
|
|
|
2019-01-04 16:29:57 +01:00
|
|
|
|
recover_s<t>(order);
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* 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. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
double
|
|
|
|
|
KOrder::check(int dim) const
|
|
|
|
|
{
|
|
|
|
|
KORD_RAISE_IF(dim > g<t>().getMaxDim(),
|
|
|
|
|
"Wrong dimension for KOrder::check");
|
|
|
|
|
JournalRecordPair pa(journal);
|
|
|
|
|
pa << "Checking residuals for order = " << dim << endrec;
|
|
|
|
|
|
|
|
|
|
double maxerror = 0.0;
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Check for F_yⁱuʲ=0
|
2019-01-04 16:29:57 +01:00
|
|
|
|
for (int i = 0; i <= dim; i++)
|
|
|
|
|
{
|
2019-12-20 14:36:20 +01:00
|
|
|
|
Symmetry sym{dim-i, i, 0, 0};
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto r = faaDiBrunoZ<t>(sym);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
double err = r->getData().getMax();
|
|
|
|
|
JournalRecord(journal) << "\terror for symmetry " << sym << "\tis " << err << endrec;
|
2019-03-07 18:17:43 +01:00
|
|
|
|
maxerror = std::max(err, maxerror);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Check for F_yⁱuʲu′ᵏ+Dᵢⱼₖ+Eᵢⱼₖ=0
|
2019-02-12 12:15:56 +01:00
|
|
|
|
for (auto &si : SymmetrySet(dim, 3))
|
2019-01-04 16:29:57 +01:00
|
|
|
|
{
|
2019-02-12 12:15:56 +01:00
|
|
|
|
int i = si[0];
|
|
|
|
|
int j = si[1];
|
|
|
|
|
int k = si[2];
|
2019-01-04 16:29:57 +01:00
|
|
|
|
if (i+j > 0 && k > 0)
|
|
|
|
|
{
|
2019-02-08 18:38:05 +01:00
|
|
|
|
Symmetry sym{i, j, 0, k};
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto r = faaDiBrunoZ<t>(sym);
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto D_ijk = calcD_ijk<t>(i, j, k);
|
|
|
|
|
r->add(1.0, D_ijk);
|
|
|
|
|
auto E_ijk = calcE_ijk<t>(i, j, k);
|
|
|
|
|
r->add(1.0, E_ijk);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
double err = r->getData().getMax();
|
|
|
|
|
JournalRecord(journal) << "\terror for symmetry " << sym << "\tis " << err << endrec;
|
2019-03-07 10:39:33 +01:00
|
|
|
|
maxerror = std::max(err, maxerror);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Check for F_σⁱ+Dᵢ+Eᵢ=0
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto r = faaDiBrunoZ<t>(Symmetry{0, 0, 0, dim});
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto D_k = calcD_k<t>(dim);
|
|
|
|
|
r->add(1.0, D_k);
|
|
|
|
|
auto E_k = calcE_k<t>(dim);
|
|
|
|
|
r->add(1.0, E_k);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
double err = r->getData().getMax();
|
2019-02-08 18:38:05 +01:00
|
|
|
|
Symmetry sym{0, 0, 0, dim};
|
2019-01-04 16:29:57 +01:00
|
|
|
|
JournalRecord(journal) << "\terror for symmetry " << sym << "\tis " << err << endrec;
|
2019-03-07 18:17:43 +01:00
|
|
|
|
maxerror = std::max(err, maxerror);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
return maxerror;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
Vector
|
2019-01-04 16:29:57 +01:00
|
|
|
|
KOrder::calcStochShift(int order, double sigma) const
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
Vector res(ny);
|
|
|
|
|
res.zeros();
|
2019-01-04 16:29:57 +01:00
|
|
|
|
int jfac = 1;
|
|
|
|
|
for (int j = 1; j <= order; j++, jfac *= j)
|
|
|
|
|
if (is_even(j))
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto ten = calcD_k<t>(j);
|
|
|
|
|
res.add(std::pow(sigma, j)/jfac, ten.getData());
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|