2019-06-19 14:34:30 +02:00
|
|
|
|
/*
|
|
|
|
|
* Copyright © 2005 Ondra Kamenik
|
2022-05-04 18:26:37 +02:00
|
|
|
|
* Copyright © 2019-2022 Dynare Team
|
2019-06-19 14:34:30 +02:00
|
|
|
|
*
|
|
|
|
|
* 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 stochastic steady
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* This file defines a number of classes of which KOrderStoch is the main
|
|
|
|
|
purpose. Basically, KOrderStoch calculates first and higher order Taylor
|
|
|
|
|
expansion of a policy rule at σ>0 with explicit forward g**. More formally,
|
|
|
|
|
we have to solve a policy rule g from
|
|
|
|
|
|
|
|
|
|
𝔼ₜ[f(g**(g*(y*ₜ,uₜ,σ),uₜ₊₁,σ),g(y*,uₜ,σ),y*,uₜ)]
|
|
|
|
|
|
|
|
|
|
As the introduction in approximation.hh argues, g** at tine t+1 must be
|
|
|
|
|
given from outside. Let the explicit 𝔼ₜ(g**(y*,uₜ₊₁,σ) be equal to h(y*,σ).
|
|
|
|
|
Then we have to solve f(h(g*(y*,u,σ),σ),g(y,u,σ),y,u), which is much easier
|
|
|
|
|
than fully implicit system for σ=0.
|
|
|
|
|
|
|
|
|
|
Besides the class KOrderStoch, we declare here also classes for the new
|
|
|
|
|
containers corresponding to f(h(g*(y*,u,σ),σ),g(y,u,σ),y,u). Further, we
|
|
|
|
|
declare IntegDerivs and StochForwardDerivs classes which basically calculate
|
|
|
|
|
h as an extrapolation based on an approximation to g at lower σ.
|
2019-12-20 14:36:20 +01:00
|
|
|
|
*/
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
#include <memory>
|
|
|
|
|
|
2019-01-04 16:29:57 +01:00
|
|
|
|
#include "korder.hh"
|
|
|
|
|
#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
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* This class is a container, which has a specialized constructor integrating
|
|
|
|
|
the policy rule at given σ. */
|
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
|
|
|
|
class IntegDerivs : public ctraits<t>::Tgss
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-03-06 18:40:19 +01:00
|
|
|
|
IntegDerivs(int r, const IntSequence &nvs, const typename ctraits<t>::Tgss &g,
|
|
|
|
|
const typename ctraits<t>::Tm &mom, double at_sigma);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
};
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* This constructor integrates a rule (namely its g** part) with respect to
|
|
|
|
|
u=σ~·η, and stores to the object the derivatives of this integral h at
|
|
|
|
|
(y*,u,σ)=(ỹ*,0,σ~). The original container of g**, the moments of the
|
|
|
|
|
stochastic shocks ‘mom’ and the σ~ are input.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
The code follows the following derivation
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
h(y,σ) = 𝔼ₜ[g(y,u′,σ)]
|
|
|
|
|
|
|
|
|
|
1 ⎛ d ⎞
|
|
|
|
|
= ỹ + ∑ ── ∑ ⎝i,j,k⎠ [g_yⁱuʲσᵏ] (y*-ỹ*)ⁱ σʲ Σʲ (σ-σ~)ᵏ
|
|
|
|
|
ᵈ⁼¹ d! ⁱ⁺ʲ⁺ᵏ⁼ᵈ
|
|
|
|
|
|
|
|
|
|
1 ⎛ d ⎞ ⎛m+n⎞
|
|
|
|
|
= ỹ + ∑ ── ∑ ⎝i,m+n,k⎠ [g_yⁱuᵐ⁺ⁿσᵏ] ŷ*ⁱ Σᵐ⁺ⁿ ⎝m,n⎠ σ~ᵐ σ^ᵏ⁺ⁿ
|
|
|
|
|
ᵈ⁼¹ d! ⁱ⁺ᵐ⁺ⁿ⁺ᵏ⁼ᵈ
|
|
|
|
|
|
|
|
|
|
1 ⎛ d ⎞
|
|
|
|
|
= ỹ + ∑ ── ∑ ⎝i,m,n,k⎠ [g_yⁱuᵐ⁺ⁿσᵏ] Σᵐ⁺ⁿ σ~ᵐ ŷ*ⁱ σ^ᵏ⁺ⁿ
|
|
|
|
|
ᵈ⁼¹ d! ⁱ⁺ᵐ⁺ⁿ⁺ᵏ⁼ᵈ
|
|
|
|
|
|
|
|
|
|
1 ⎛ d ⎞
|
|
|
|
|
= ỹ + ∑ ── ∑ ∑ ⎝i,m,n,k⎠ [g_yⁱuᵐ⁺ⁿσᵏ] Σᵐ⁺ⁿ σ~ᵐ ŷ*ⁱ σ^ᵏ⁺ⁿ
|
|
|
|
|
ᵈ⁼¹ d! ⁱ⁺ᵖ⁼ᵈ ᵐ⁼⁰
|
|
|
|
|
ⁿ⁺ᵏ⁼ᵖ
|
|
|
|
|
|
|
|
|
|
1 ⎛ d ⎞ ⎡ ⎛ p ⎞ 1 ⎤
|
|
|
|
|
= ỹ + ∑ ── ∑ ⎝i,p⎠ ⎢ ∑ ⎝n,k⎠ ── [g_yⁱuᵐ⁺ⁿσᵏ] Σᵐ⁺ⁿ σ~ᵐ⎥ ŷ*ⁱ σ^ᵏ⁺ⁿ
|
|
|
|
|
ᵈ⁼¹ d! ⁱ⁺ᵖ⁼ᵈ ⎢ ᵐ⁼⁰ m! ⎥
|
|
|
|
|
⎣ⁿ⁺ᵏ⁼ᵖ ⎦
|
|
|
|
|
|
|
|
|
|
⎛ a ⎞
|
|
|
|
|
where ⎝b₁,…,bₙ⎠ is a generalized combination number, p=k+n, σ^=σ-σ~,
|
|
|
|
|
ŷ*=y*-ỹ, and we gave up writing the multidimensional indexes in Einstein
|
|
|
|
|
summation.
|
|
|
|
|
|
|
|
|
|
This implies that:
|
|
|
|
|
|
|
|
|
|
⎛ p ⎞ 1
|
|
|
|
|
h_yⁱσᵖ = ∑ ⎝n,k⎠ ── [g_yⁱuᵐ⁺ⁿσᵏ] Σᵐ⁺ⁿ σ~ᵐ
|
|
|
|
|
ᵐ⁼⁰ m!
|
|
|
|
|
ⁿ⁺ᵏ⁼ᵖ
|
|
|
|
|
|
|
|
|
|
and this is exactly what the code does.
|
2019-12-20 14:36:20 +01:00
|
|
|
|
*/
|
|
|
|
|
template<Storage t>
|
2019-03-06 18:40:19 +01:00
|
|
|
|
IntegDerivs<t>::IntegDerivs(int r, const IntSequence &nvs, const typename ctraits<t>::Tgss &g,
|
|
|
|
|
const typename ctraits<t>::Tm &mom, double at_sigma)
|
2019-01-04 16:29:57 +01:00
|
|
|
|
: ctraits<t>::Tgss(4)
|
|
|
|
|
{
|
|
|
|
|
int maxd = g.getMaxDim();
|
|
|
|
|
for (int d = 1; d <= maxd; d++)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i <= d; i++)
|
|
|
|
|
{
|
|
|
|
|
int p = d-i;
|
2019-02-08 18:38:05 +01:00
|
|
|
|
Symmetry sym{i, 0, 0, p};
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto ten = std::make_unique<typename ctraits<t>::Ttensor>(r, TensorDimens(sym, nvs));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Calculate derivative h_yⁱσᵖ
|
|
|
|
|
/* This code calculates:
|
|
|
|
|
|
|
|
|
|
⎛ p ⎞ 1
|
|
|
|
|
h_yⁱσᵖ = ∑ ⎝n,k⎠ ── [g_yⁱuᵐ⁺ⁿσᵏ] Σᵐ⁺ⁿ σ~ᵐ
|
|
|
|
|
ᵐ⁼⁰ m!
|
|
|
|
|
ⁿ⁺ᵏ⁼ᵖ
|
|
|
|
|
|
|
|
|
|
and stores it in ‘ten’. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
ten->zeros();
|
|
|
|
|
for (int n = 0; n <= p; n++)
|
|
|
|
|
{
|
|
|
|
|
int k = p-n;
|
2019-02-19 15:57:19 +01:00
|
|
|
|
int povern = PascalTriangle::noverk(p, n);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
int mfac = 1;
|
|
|
|
|
for (int m = 0; i+m+n+k <= maxd; m++, mfac *= m)
|
|
|
|
|
{
|
|
|
|
|
double mult = (pow(at_sigma, m)*povern)/mfac;
|
2019-02-08 18:38:05 +01:00
|
|
|
|
Symmetry sym_mn{i, m+n, 0, k};
|
2019-01-04 16:29:57 +01:00
|
|
|
|
if (m+n == 0 && g.check(sym_mn))
|
2019-02-20 17:51:05 +01:00
|
|
|
|
ten->add(mult, g.get(sym_mn));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
if (m+n > 0 && KOrder::is_even(m+n) && g.check(sym_mn))
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Ttensor gtmp(g.get(sym_mn));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
gtmp.mult(mult);
|
2019-02-20 17:51:05 +01:00
|
|
|
|
gtmp.contractAndAdd(1, *ten, mom.get(Symmetry{m+n}));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-20 16:50:33 +01:00
|
|
|
|
this->insert(std::move(ten));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This class calculates an extrapolation of expectation of forward
|
2019-06-11 16:45:09 +02:00
|
|
|
|
derivatives. It is a container, all calculations are done in a constructor.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
The class calculates derivatives of E[g(y*,u,σ)] at (ȳ*,σ¯). The derivatives
|
|
|
|
|
are extrapolated based on derivatives at (ỹ*,σ~). */
|
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
|
|
|
|
class StochForwardDerivs : public ctraits<t>::Tgss
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
StochForwardDerivs(const PartitionY &ypart, int nu,
|
2019-03-06 18:40:19 +01:00
|
|
|
|
const typename ctraits<t>::Tgss &g, const typename ctraits<t>::Tm &m,
|
2019-01-04 16:29:57 +01:00
|
|
|
|
const Vector &ydelta, double sdelta,
|
|
|
|
|
double at_sigma);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* This is the constructor which performs the integration and the
|
2019-06-11 16:45:09 +02:00
|
|
|
|
extrapolation. Its parameters are: ‘g’ is the container of derivatives at
|
|
|
|
|
(ỹ,σ~); ‘m’ are the moments of stochastic shocks; ‘ydelta’ is a difference
|
|
|
|
|
of the steady states ȳ−ỹ; ‘sdelta’ is the difference between new sigma and
|
|
|
|
|
old sigma σ¯−σ~, and ‘at_sigma’ is σ~. There is no need of inputing the ỹ.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
We do the operation in four steps:
|
2019-06-11 16:45:09 +02:00
|
|
|
|
— Integrate g**, the derivatives are at (ỹ,σ~)
|
|
|
|
|
— Form the (full symmetric) polynomial from the derivatives stacking
|
|
|
|
|
⎡y*⎤
|
|
|
|
|
⎣σ ⎦
|
|
|
|
|
— Centralize this polynomial about (ȳ,σ¯)
|
|
|
|
|
— Recover general symmetry tensors from the (full symmetric) polynomial
|
|
|
|
|
*/
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
StochForwardDerivs<t>::StochForwardDerivs(const PartitionY &ypart, int nu,
|
2019-03-06 18:40:19 +01:00
|
|
|
|
const typename ctraits<t>::Tgss &g,
|
|
|
|
|
const typename ctraits<t>::Tm &m,
|
2019-01-04 16:29:57 +01:00
|
|
|
|
const Vector &ydelta, double sdelta,
|
|
|
|
|
double at_sigma)
|
|
|
|
|
: ctraits<t>::Tgss(4)
|
|
|
|
|
{
|
|
|
|
|
int maxd = g.getMaxDim();
|
|
|
|
|
int r = ypart.nyss();
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Make ‘g_int’ be integral of g** at (ỹ,σ~)
|
|
|
|
|
/* This simply constructs IntegDerivs class. Note that the ‘nvs’ of
|
2019-01-04 16:29:57 +01:00
|
|
|
|
the tensors has zero dimensions for shocks, this is because we need to
|
2019-06-11 16:45:09 +02:00
|
|
|
|
⎡y*⎤
|
|
|
|
|
make easily stacks of the form ⎣σ ⎦ in the next step. */
|
2019-02-20 17:00:16 +01:00
|
|
|
|
IntSequence nvs{ypart.nys(), 0, 0, 1};
|
2019-01-04 16:29:57 +01:00
|
|
|
|
IntegDerivs<t> g_int(r, nvs, g, m, at_sigma);
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Make ‘g_int_sym’ be full symmetric polynomial from ‘g_int’
|
2019-01-04 16:29:57 +01:00
|
|
|
|
/* Here we just form a polynomial whose unique variable corresponds to
|
2019-06-11 16:45:09 +02:00
|
|
|
|
⎡y*⎤
|
|
|
|
|
⎣σ ⎦ stack. */
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Tpol g_int_sym(r, ypart.nys()+1);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
for (int d = 1; d <= maxd; d++)
|
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto ten = std::make_unique<typename ctraits<t>::Ttensym>(r, ypart.nys()+1, d);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
ten->zeros();
|
|
|
|
|
for (int i = 0; i <= d; i++)
|
|
|
|
|
{
|
|
|
|
|
int k = d-i;
|
2019-02-08 18:38:05 +01:00
|
|
|
|
if (g_int.check(Symmetry{i, 0, 0, k}))
|
2019-02-20 17:51:05 +01:00
|
|
|
|
ten->addSubTensor(g_int.get(Symmetry{i, 0, 0, k}));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
2019-02-20 16:50:33 +01:00
|
|
|
|
g_int_sym.insert(std::move(ten));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Make ‘g_int_cent’ the centralized polynomial about (ȳ,σ¯)
|
|
|
|
|
/* Here we centralize the polynomial to (ȳ,σ¯) knowing that the polynomial
|
|
|
|
|
was centralized about (ỹ,σ~). This is done by derivating and evaluating
|
|
|
|
|
the derivated polynomial at (ȳ−ỹ,σ¯-σ~). The stack of this vector is
|
|
|
|
|
‘delta’ in the code. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
Vector delta(ypart.nys()+1);
|
|
|
|
|
Vector dy(delta, 0, ypart.nys());
|
|
|
|
|
ConstVector dy_in(ydelta, ypart.nstat, ypart.nys());
|
|
|
|
|
dy = dy_in;
|
|
|
|
|
delta[ypart.nys()] = sdelta;
|
2019-03-06 18:40:19 +01:00
|
|
|
|
typename ctraits<t>::Tpol g_int_cent(r, ypart.nys()+1);
|
2019-01-04 16:29:57 +01:00
|
|
|
|
for (int d = 1; d <= maxd; d++)
|
|
|
|
|
{
|
|
|
|
|
g_int_sym.derivative(d-1);
|
2019-02-20 16:50:33 +01:00
|
|
|
|
auto der = g_int_sym.evalPartially(d, delta);
|
|
|
|
|
g_int_cent.insert(std::move(der));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
// Pull out general symmetry tensors from ‘g_int_cent’
|
2019-01-04 16:29:57 +01:00
|
|
|
|
/* Here we only recover the general symmetry derivatives from the full
|
2019-06-11 16:45:09 +02:00
|
|
|
|
symmetric polynomial. Note that the derivative get the true ‘nvs’. */
|
2019-02-20 17:00:16 +01:00
|
|
|
|
IntSequence ss{ypart.nys(), 0, 0, 1};
|
|
|
|
|
IntSequence pp{0, 1, 2, 3};
|
2019-01-04 16:29:57 +01:00
|
|
|
|
IntSequence true_nvs(nvs);
|
2019-02-20 17:00:16 +01:00
|
|
|
|
true_nvs[1] = nu;
|
|
|
|
|
true_nvs[2] = nu;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
for (int d = 1; d <= maxd; d++)
|
2019-03-06 18:40:19 +01:00
|
|
|
|
if (g_int_cent.check(Symmetry{d}))
|
|
|
|
|
for (int i = 0; i <= d; i++)
|
2019-01-04 16:29:57 +01:00
|
|
|
|
{
|
2019-03-06 18:40:19 +01:00
|
|
|
|
Symmetry sym{i, 0, 0, d-i};
|
|
|
|
|
IntSequence coor(pp.unfold(sym));
|
|
|
|
|
auto ten = std::make_unique<typename ctraits<t>::Ttensor>(g_int_cent.get(Symmetry{d}),
|
|
|
|
|
ss, coor,
|
|
|
|
|
TensorDimens(sym, true_nvs));
|
|
|
|
|
this->insert(std::move(ten));
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* This container corresponds to h(g*(y,u,σ),σ). Note that in our application,
|
|
|
|
|
the σ as a second argument to h will be its fourth variable in symmetry, so
|
|
|
|
|
we have to do four member stack having the second and third stack dummy. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<class _Ttype>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
class GXContainer : public GContainer<_Ttype>
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-01-09 17:21:14 +01:00
|
|
|
|
using _Stype = StackContainerInterface<_Ttype>;
|
|
|
|
|
using _Ctype = typename StackContainer<_Ttype>::_Ctype;
|
|
|
|
|
using itype = typename StackContainer<_Ttype>::itype;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
GXContainer(const _Ctype *gs, int ngs, int nu)
|
|
|
|
|
: GContainer<_Ttype>(gs, ngs, nu)
|
|
|
|
|
{
|
|
|
|
|
}
|
2019-01-09 16:26:42 +01:00
|
|
|
|
itype getType(int i, const Symmetry &s) const override;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* This routine corresponds to this stack:
|
2019-06-11 16:45:09 +02:00
|
|
|
|
⎡ g*(y,u,σ) ⎤
|
|
|
|
|
⎢ dummy ⎥
|
|
|
|
|
⎢ dummy ⎥
|
|
|
|
|
⎣ σ ⎦
|
2019-12-20 14:36:20 +01:00
|
|
|
|
*/
|
|
|
|
|
template<class _Ttype>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
typename GXContainer<_Ttype>::itype
|
|
|
|
|
GXContainer<_Ttype>::getType(int i, const Symmetry &s) const
|
|
|
|
|
{
|
|
|
|
|
if (i == 0)
|
|
|
|
|
if (s[2] > 0)
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::zero;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
else
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::matrix;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
if (i == 1)
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::zero;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
if (i == 2)
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::zero;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
if (i == 3)
|
2019-02-08 18:38:05 +01:00
|
|
|
|
if (s == Symmetry{0, 0, 0, 1})
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::unit;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
else
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::zero;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
KORD_RAISE("Wrong stack index in GXContainer::getType");
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* This container corresponds to f(H(y,u,σ),g(y,u,sigma),y,u), where the H has
|
|
|
|
|
the size (number of rows) as g**. Since it is very simmilar to ZContainer,
|
|
|
|
|
we inherit form it and override only getType() method. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<class _Ttype>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
class ZXContainer : public ZContainer<_Ttype>
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-01-09 17:21:14 +01:00
|
|
|
|
using _Stype = StackContainerInterface<_Ttype>;
|
|
|
|
|
using _Ctype = typename StackContainer<_Ttype>::_Ctype;
|
|
|
|
|
using itype = typename StackContainer<_Ttype>::itype;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
ZXContainer(const _Ctype *gss, int ngss, const _Ctype *g, int ng, int ny, int nu)
|
|
|
|
|
: ZContainer<_Ttype>(gss, ngss, g, ng, ny, nu)
|
|
|
|
|
{
|
|
|
|
|
}
|
2019-01-09 16:26:42 +01:00
|
|
|
|
itype getType(int i, const Symmetry &s) const override;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
};
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* This getType() method corresponds to this stack:
|
|
|
|
|
⎡ H(y,u,σ) ⎤
|
|
|
|
|
⎢ g(y,u,σ) ⎥
|
|
|
|
|
⎢ y ⎥
|
|
|
|
|
⎣ u ⎦
|
|
|
|
|
*/
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<class _Ttype>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
typename ZXContainer<_Ttype>::itype
|
|
|
|
|
ZXContainer<_Ttype>::getType(int i, const Symmetry &s) const
|
|
|
|
|
{
|
|
|
|
|
if (i == 0)
|
|
|
|
|
if (s[2] > 0)
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::zero;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
else
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::matrix;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
if (i == 1)
|
|
|
|
|
if (s[2] > 0)
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::zero;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
else
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::matrix;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
if (i == 2)
|
2019-02-08 18:38:05 +01:00
|
|
|
|
if (s == Symmetry{1, 0, 0, 0})
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::unit;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
else
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::zero;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
if (i == 3)
|
2019-02-08 18:38:05 +01:00
|
|
|
|
if (s == Symmetry{0, 1, 0, 0})
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::unit;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
else
|
2019-02-26 18:57:36 +01:00
|
|
|
|
return itype::zero;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
KORD_RAISE("Wrong stack index in ZXContainer::getType");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class UnfoldedGXContainer : public GXContainer<UGSTensor>, public UnfoldedStackContainer
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-01-09 17:21:14 +01:00
|
|
|
|
using _Ctype = TensorContainer<UGSTensor>;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
UnfoldedGXContainer(const _Ctype *gs, int ngs, int nu)
|
|
|
|
|
: GXContainer<UGSTensor>(gs, ngs, nu)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class FoldedGXContainer : public GXContainer<FGSTensor>, public FoldedStackContainer
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-01-09 17:21:14 +01:00
|
|
|
|
using _Ctype = TensorContainer<FGSTensor>;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
FoldedGXContainer(const _Ctype *gs, int ngs, int nu)
|
|
|
|
|
: GXContainer<FGSTensor>(gs, ngs, nu)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class UnfoldedZXContainer : public ZXContainer<UGSTensor>, public UnfoldedStackContainer
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-01-09 17:21:14 +01:00
|
|
|
|
using _Ctype = TensorContainer<UGSTensor>;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
UnfoldedZXContainer(const _Ctype *gss, int ngss, const _Ctype *g, int ng, int ny, int nu)
|
|
|
|
|
: ZXContainer<UGSTensor>(gss, ngss, g, ng, ny, nu)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class FoldedZXContainer : public ZXContainer<FGSTensor>, public FoldedStackContainer
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-01-09 17:21:14 +01:00
|
|
|
|
using _Ctype = TensorContainer<FGSTensor>;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
FoldedZXContainer(const _Ctype *gss, int ngss, const _Ctype *g, int ng, int ny, int nu)
|
|
|
|
|
: ZXContainer<FGSTensor>(gss, ngss, g, ng, ny, nu)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* This matrix corresponds to
|
2019-06-11 16:45:09 +02:00
|
|
|
|
|
|
|
|
|
[f_{y}]+ [0 [f_y**₊]·[h**_y*] 0]
|
|
|
|
|
|
|
|
|
|
This is almost the same as MatrixA, the only difference that the MatrixA is
|
|
|
|
|
constructed from whole h_y*, not only from h**_y*, hence the new
|
|
|
|
|
abstraction. */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
class MatrixAA : public PLUMatrix
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
MatrixAA(const FSSparseTensor &f, const IntSequence &ss,
|
|
|
|
|
const TwoDMatrix &gyss, const PartitionY &ypart);
|
|
|
|
|
};
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* This class calculates derivatives of g given implicitly by
|
|
|
|
|
f(h(g*(y,u,σ),σ),g(y,u,σ),y,u), where h(y,σ) is given from outside.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
Structurally, the class is very similar to KOrder, but calculations are much
|
|
|
|
|
easier. The two constructors construct an object from sparse derivatives of
|
|
|
|
|
f, and derivatives of h. The caller must ensure that the both derivatives
|
|
|
|
|
are done at the same point.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
The calculation for order k (including k=1) is done by a call
|
|
|
|
|
performStep(k). The derivatives can be retrived by getFoldDers() or
|
|
|
|
|
getUnfoldDers(). */
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
|
|
|
|
class KOrderStoch
|
|
|
|
|
{
|
|
|
|
|
protected:
|
|
|
|
|
IntSequence nvs;
|
|
|
|
|
PartitionY ypart;
|
|
|
|
|
Journal &journal;
|
|
|
|
|
UGSContainer _ug;
|
|
|
|
|
FGSContainer _fg;
|
|
|
|
|
UGSContainer _ugs;
|
|
|
|
|
FGSContainer _fgs;
|
|
|
|
|
UGSContainer _uG;
|
|
|
|
|
FGSContainer _fG;
|
|
|
|
|
const UGSContainer *_uh;
|
|
|
|
|
const FGSContainer *_fh;
|
|
|
|
|
UnfoldedZXContainer _uZstack;
|
|
|
|
|
FoldedZXContainer _fZstack;
|
|
|
|
|
UnfoldedGXContainer _uGstack;
|
|
|
|
|
FoldedGXContainer _fGstack;
|
|
|
|
|
const TensorContainer<FSSparseTensor> &f;
|
|
|
|
|
MatrixAA matA;
|
|
|
|
|
public:
|
|
|
|
|
KOrderStoch(const PartitionY &ypart, int nu, const TensorContainer<FSSparseTensor> &fcont,
|
|
|
|
|
const FGSContainer &hh, Journal &jr);
|
|
|
|
|
KOrderStoch(const PartitionY &ypart, int nu, const TensorContainer<FSSparseTensor> &fcont,
|
|
|
|
|
const UGSContainer &hh, Journal &jr);
|
2019-12-20 14:36:20 +01:00
|
|
|
|
template<Storage t>
|
2019-01-04 16:29:57 +01:00
|
|
|
|
void performStep(int order);
|
|
|
|
|
const FGSContainer &
|
|
|
|
|
getFoldDers() const
|
|
|
|
|
{
|
|
|
|
|
return _fg;
|
|
|
|
|
}
|
|
|
|
|
const UGSContainer &
|
|
|
|
|
getUnfoldDers() const
|
|
|
|
|
{
|
|
|
|
|
return _ug;
|
|
|
|
|
}
|
2021-06-15 14:30:32 +02:00
|
|
|
|
const FGSContainer &
|
|
|
|
|
getFoldDersS() const
|
|
|
|
|
{
|
|
|
|
|
return _fgs;
|
|
|
|
|
}
|
2019-01-04 16:29:57 +01:00
|
|
|
|
protected:
|
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-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
|
|
|
|
// Convenience access methods
|
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>
|
|
|
|
|
const typename ctraits<t>::Tgss &h() 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>::TZXstack &Zstack();
|
|
|
|
|
template<Storage t>
|
|
|
|
|
const typename ctraits<t>::TZXstack &Zstack() const;
|
|
|
|
|
|
|
|
|
|
template<Storage t>
|
|
|
|
|
typename ctraits<t>::TGXstack &Gstack();
|
|
|
|
|
template<Storage t>
|
|
|
|
|
const typename ctraits<t>::TGXstack &Gstack() const;
|
2019-01-04 16:29:57 +01:00
|
|
|
|
};
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* This calculates a derivative of f(G(y,u,σ),g(y,u,σ),y,u) of a given
|
|
|
|
|
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
|
|
|
|
KOrderStoch::faaDiBrunoZ(const Symmetry &sym) const
|
|
|
|
|
{
|
|
|
|
|
JournalRecordPair pa(journal);
|
2022-05-04 18:26:37 +02:00
|
|
|
|
pa << "Faà Di Bruno ZX container for " << sym << endrec;
|
2019-03-06 18:40:19 +01:00
|
|
|
|
auto res = std::make_unique<typename ctraits<t>::Ttensor>(ypart.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
|
|
|
|
/* This calculates a derivative of G(y,u,σ)=h(g*(y,u,σ),σ) of a given
|
|
|
|
|
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
|
|
|
|
KOrderStoch::faaDiBrunoG(const Symmetry &sym) const
|
|
|
|
|
{
|
|
|
|
|
JournalRecordPair pa(journal);
|
2022-05-04 18:26:37 +02:00
|
|
|
|
pa << "Faà Di Bruno GX 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>(), h<t>(), *res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
/* This retrieves all g derivatives of a given dimension from implicit
|
|
|
|
|
f(h(g*(y,u,σ),σ),g(y,u,σ),y,u). It supposes that all derivatives of smaller
|
|
|
|
|
dimensions have been retrieved.
|
2019-01-04 16:29:57 +01:00
|
|
|
|
|
2019-06-11 16:45:09 +02:00
|
|
|
|
So, we go through all symmetries s, calculate Gₛ conditional on gₛ=0, insert
|
|
|
|
|
the derivative to the G container, then calculate Fₛ conditional on gₛ=0.
|
|
|
|
|
This is a righthand side. The left hand side is matA·gₛ. The gₛ is retrieved
|
|
|
|
|
as gₛ=-matA⁻¹·RHS. Finally we have to update Gₛ by calling
|
|
|
|
|
Gstack<t>().multAndAdd(1, h<t>(), *G_sym_ptr). */
|
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
|
|
|
|
|
KOrderStoch::performStep(int order)
|
|
|
|
|
{
|
|
|
|
|
int maxd = g<t>().getMaxDim();
|
|
|
|
|
KORD_RAISE_IF(order-1 != maxd && (order != 1 || maxd != -1),
|
|
|
|
|
"Wrong order for KOrderStoch::performStep");
|
2019-02-12 12:15:56 +01:00
|
|
|
|
for (auto &si : SymmetrySet(order, 4))
|
2019-03-06 18:40:19 +01:00
|
|
|
|
if (si[2] == 0)
|
|
|
|
|
{
|
|
|
|
|
JournalRecordPair pa(journal);
|
|
|
|
|
pa << "Recovering symmetry " << si << endrec;
|
|
|
|
|
|
|
|
|
|
auto G_sym = faaDiBrunoG<t>(si);
|
|
|
|
|
auto G_sym_ptr = G_sym.get();
|
|
|
|
|
G<t>().insert(std::move(G_sym));
|
|
|
|
|
|
|
|
|
|
auto g_sym = faaDiBrunoZ<t>(si);
|
|
|
|
|
auto g_sym_ptr = g_sym.get();
|
|
|
|
|
g_sym->mult(-1.0);
|
|
|
|
|
matA.multInv(*g_sym);
|
|
|
|
|
g<t>().insert(std::move(g_sym));
|
|
|
|
|
gs<t>().insert(std::make_unique<typename ctraits<t>::Ttensor>(ypart.nstat, ypart.nys(),
|
|
|
|
|
*g_sym_ptr));
|
|
|
|
|
|
|
|
|
|
Gstack<t>().multAndAdd(1, h<t>(), *G_sym_ptr);
|
|
|
|
|
}
|
2019-01-04 16:29:57 +01:00
|
|
|
|
}
|