dynare/dynare++/sylv/cc/TriangularSylvester.cc

410 lines
12 KiB
C++

/*
* Copyright © 2004-2011 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
* along with Dynare. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TriangularSylvester.hh"
#include "QuasiTriangularZero.hh"
#include "KronUtils.hh"
#include "BlockDiagonal.hh"
#include <iostream>
#include <cmath>
TriangularSylvester::TriangularSylvester(const QuasiTriangular &k,
const QuasiTriangular &f)
: SylvesterSolver(k, f),
matrixKK{matrixK->square()},
matrixFF{matrixF->square()}
{
}
TriangularSylvester::TriangularSylvester(const SchurDecompZero &kdecomp,
const SchurDecomp &fdecomp)
: SylvesterSolver(kdecomp, fdecomp),
matrixKK{matrixK->square()},
matrixFF{matrixF->square()}
{
}
TriangularSylvester::TriangularSylvester(const SchurDecompZero &kdecomp,
const SimilarityDecomp &fdecomp)
: SylvesterSolver(kdecomp, fdecomp),
matrixKK{matrixK->square()},
matrixFF{matrixF->square()}
{
}
void
TriangularSylvester::print() const
{
std::cout << "matrix K (" << matrixK->getDiagonal().getSize() << "):" << std::endl;
matrixK->print();
std::cout << "matrix F (" << matrixF->getDiagonal().getSize() << "):" << std::endl;
matrixF->print();
}
void
TriangularSylvester::solve(SylvParams &pars, KronVector &d) const
{
double eig_min = 1e30;
solvi(1., d, eig_min);
pars.eig_min = std::sqrt(eig_min);
}
void
TriangularSylvester::solvi(double r, KronVector &d, double &eig_min) const
{
if (d.getDepth() == 0)
{
auto t = matrixK->scale(r);
t->solvePre(d, eig_min);
}
else
{
for (const_diag_iter di = matrixF->diag_begin();
di != matrixF->diag_end();
++di)
if (di->isReal())
solviRealAndEliminate(r, di, d, eig_min);
else
solviComplexAndEliminate(r, di, d, eig_min);
}
}
void
TriangularSylvester::solvii(double alpha, double beta1, double beta2,
KronVector &d1, KronVector &d2,
double &eig_min) const
{
KronVector d1tmp(d1);
KronVector d2tmp(d2);
linEval(alpha, beta1, beta2, d1, d2, d1tmp, d2tmp);
solviip(alpha, beta1*beta2, d1, eig_min);
solviip(alpha, beta1*beta2, d2, eig_min);
}
void
TriangularSylvester::solviip(double alpha, double betas,
KronVector &d, double &eig_min) const
{
// quick exit to solvi if betas is small
if (betas < diag_zero_sq)
{
solvi(alpha, d, eig_min);
solvi(alpha, d, eig_min);
return;
}
if (d.getDepth() == 0)
{
double aspbs = alpha*alpha+betas;
auto t = matrixK->linearlyCombine(2*alpha, aspbs, *matrixKK);
t->solvePre(d, eig_min);
}
else
{
const_diag_iter di = matrixF->diag_begin();
const_diag_iter dsi = matrixFF->diag_begin();
for (; di != matrixF->diag_end(); ++di, ++dsi)
if (di->isReal())
solviipRealAndEliminate(alpha, betas, di, dsi, d, eig_min);
else
solviipComplexAndEliminate(alpha, betas, di, dsi, d, eig_min);
}
}
void
TriangularSylvester::solviRealAndEliminate(double r, const_diag_iter di,
KronVector &d, double &eig_min) const
{
// di is real
int jbar = di->getIndex();
double f = *(di->getAlpha());
KronVector dj(d, jbar);
// solve system
if (std::abs(r*f) > diag_zero)
solvi(r*f, dj, eig_min);
// calculate y
KronVector y(const_cast<const KronVector &>(dj));
KronUtils::multKron(*matrixF, *matrixK, y);
y.mult(r);
double divisor = 1.0;
solviEliminateReal(di, d, y, divisor);
}
void
TriangularSylvester::solviEliminateReal(const_diag_iter di, KronVector &d,
const KronVector &y, double divisor) const
{
for (const_row_iter ri = matrixF->row_begin(*di);
ri != matrixF->row_end(*di); ++ri)
{
KronVector dk(d, ri.getCol());
dk.add(-(*ri)/divisor, y);
}
}
void
TriangularSylvester::solviComplexAndEliminate(double r, const_diag_iter di,
KronVector &d, double &eig_min) const
{
// di is complex
int jbar = di->getIndex();
// pick data
double alpha = *di->getAlpha();
double beta1 = di->getBeta2();
double beta2 = -di->getBeta1();
double aspbs = di->getDeterminant();
KronVector dj(d, jbar);
KronVector djj(d, jbar+1);
// solve
if (r*r*aspbs > diag_zero_sq)
solvii(r*alpha, r*beta1, r*beta2, dj, djj, eig_min);
KronVector y1(dj);
KronVector y2(djj);
KronUtils::multKron(*matrixF, *matrixK, y1);
KronUtils::multKron(*matrixF, *matrixK, y2);
y1.mult(r);
y2.mult(r);
double divisor = 1.0;
solviEliminateComplex(di, d, y1, y2, divisor);
}
void
TriangularSylvester::solviEliminateComplex(const_diag_iter di, KronVector &d,
const KronVector &y1, const KronVector &y2,
double divisor) const
{
for (const_row_iter ri = matrixF->row_begin(*di);
ri != matrixF->row_end(*di); ++ri)
{
KronVector dk(d, ri.getCol());
dk.add(-ri.a()/divisor, y1);
dk.add(-ri.b()/divisor, y2);
}
}
void
TriangularSylvester::solviipRealAndEliminate(double alpha, double betas,
const_diag_iter di, const_diag_iter dsi,
KronVector &d, double &eig_min) const
{
// di, and dsi are real
int jbar = di->getIndex();
double aspbs = alpha*alpha+betas;
// pick data
double f = *(di->getAlpha());
double fs = f*f;
KronVector dj(d, jbar);
// solve
if (fs*aspbs > diag_zero_sq)
solviip(f*alpha, fs*betas, dj, eig_min);
KronVector y1(const_cast<const KronVector &>(dj));
KronVector y2(const_cast<const KronVector &>(dj));
KronUtils::multKron(*matrixF, *matrixK, y1);
y1.mult(2*alpha);
KronUtils::multKron(*matrixFF, *matrixKK, y2);
y2.mult(aspbs);
double divisor = 1.0;
double divisor2 = 1.0;
solviipEliminateReal(di, dsi, d, y1, y2, divisor, divisor2);
}
void
TriangularSylvester::solviipEliminateReal(const_diag_iter di, const_diag_iter dsi,
KronVector &d,
const KronVector &y1, const KronVector &y2,
double divisor, double divisor2) const
{
const_row_iter ri = matrixF->row_begin(*di);
const_row_iter rsi = matrixFF->row_begin(*dsi);
for (; ri != matrixF->row_end(*di); ++ri, ++rsi)
{
KronVector dk(d, ri.getCol());
dk.add(-(*ri)/divisor, y1);
dk.add(-(*rsi)/divisor2, y2);
}
}
void
TriangularSylvester::solviipComplexAndEliminate(double alpha, double betas,
const_diag_iter di, const_diag_iter dsi,
KronVector &d, double &eig_min) const
{
// di, and dsi are complex
int jbar = di->getIndex();
double aspbs = alpha*alpha+betas;
// pick data
double gamma = *(di->getAlpha());
double delta1 = di->getBeta2(); // swap because of transpose
double delta2 = -di->getBeta1();
double gspds = di->getDeterminant();
KronVector dj(d, jbar);
KronVector djj(d, jbar+1);
if (gspds*aspbs > diag_zero_sq)
solviipComplex(alpha, betas, gamma, delta1, delta2, dj, djj, eig_min);
// here dj, djj is solution, set y1, y2, y11, y22
// y1
KronVector y1(const_cast<const KronVector &>(dj));
KronUtils::multKron(*matrixF, *matrixK, y1);
y1.mult(2*alpha);
// y11
KronVector y11(const_cast<const KronVector &>(djj));
KronUtils::multKron(*matrixF, *matrixK, y11);
y11.mult(2*alpha);
// y2
KronVector y2(const_cast<const KronVector &>(dj));
KronUtils::multKron(*matrixFF, *matrixKK, y2);
y2.mult(aspbs);
// y22
KronVector y22(const_cast<const KronVector &>(djj));
KronUtils::multKron(*matrixFF, *matrixKK, y22);
y22.mult(aspbs);
double divisor = 1.0;
solviipEliminateComplex(di, dsi, d, y1, y11, y2, y22, divisor);
}
void
TriangularSylvester::solviipComplex(double alpha, double betas, double gamma,
double delta1, double delta2,
KronVector &d1, KronVector &d2,
double &eig_min) const
{
KronVector d1tmp(d1);
KronVector d2tmp(d2);
quaEval(alpha, betas, gamma, delta1, delta2,
d1, d2, d1tmp, d2tmp);
double delta = std::sqrt(delta1*delta2);
double beta = std::sqrt(betas);
double a1 = alpha*gamma - beta*delta;
double b1 = alpha*delta + gamma*beta;
double a2 = alpha*gamma + beta*delta;
double b2 = alpha*delta - gamma*beta;
solviip(a2, b2*b2, d1, eig_min);
solviip(a1, b1*b1, d1, eig_min);
solviip(a2, b2*b2, d2, eig_min);
solviip(a1, b1*b1, d2, eig_min);
}
void
TriangularSylvester::solviipEliminateComplex(const_diag_iter di, const_diag_iter dsi,
KronVector &d,
const KronVector &y1, const KronVector &y11,
const KronVector &y2, const KronVector &y22,
double divisor) const
{
const_row_iter ri = matrixF->row_begin(*di);
const_row_iter rsi = matrixFF->row_begin(*dsi);
for (; ri != matrixF->row_end(*di); ++ri, ++rsi)
{
KronVector dk(d, ri.getCol());
dk.add(-ri.a()/divisor, y1);
dk.add(-ri.b()/divisor, y11);
dk.add(-rsi.a()/divisor, y2);
dk.add(-rsi.b()/divisor, y22);
}
}
void
TriangularSylvester::linEval(double alpha, double beta1, double beta2,
KronVector &x1, KronVector &x2,
const ConstKronVector &d1, const ConstKronVector &d2) const
{
KronVector d1tmp(d1); // make copy
KronVector d2tmp(d2); // make copy
KronUtils::multKron(*matrixF, *matrixK, d1tmp);
KronUtils::multKron(*matrixF, *matrixK, d2tmp);
x1 = d1;
x2 = d2;
Vector::mult2a(alpha, beta1, -beta2, x1, x2, d1tmp, d2tmp);
}
void
TriangularSylvester::quaEval(double alpha, double betas,
double gamma, double delta1, double delta2,
KronVector &x1, KronVector &x2,
const ConstKronVector &d1, const ConstKronVector &d2) const
{
KronVector d1tmp(d1); // make copy
KronVector d2tmp(d2); // make copy
KronUtils::multKron(*matrixF, *matrixK, d1tmp);
KronUtils::multKron(*matrixF, *matrixK, d2tmp);
x1 = d1;
x2 = d2;
Vector::mult2a(2*alpha*gamma, 2*alpha*delta1, -2*alpha*delta2,
x1, x2, d1tmp, d2tmp);
d1tmp = d1; // restore to d1
d2tmp = d2; // restore to d2
KronUtils::multKron(*matrixFF, *matrixKK, d1tmp);
KronUtils::multKron(*matrixFF, *matrixKK, d2tmp);
double aspbs = alpha*alpha + betas;
double gspds = gamma*gamma - delta1*delta2;
Vector::mult2a(aspbs*gspds, 2*aspbs*gamma*delta1, -2*aspbs*gamma*delta2,
x1, x2, d1tmp, d2tmp);
}
double
TriangularSylvester::getEigSep(int depth) const
{
int f_size = matrixF->getDiagonal().getSize();
Vector feig(2*f_size);
matrixF->getDiagonal().getEigenValues(feig);
int k_size = matrixK->getDiagonal().getSize();
Vector keig(2*k_size);
matrixK->getDiagonal().getEigenValues(keig);
KronVector eig(f_size, 2*k_size, depth);
multEigVector(eig, feig, keig);
double min = 1.0e20;
for (int i = 0; i < eig.length()/2; i++)
{
double alpha = eig[2*i];
double beta = eig[2*i+1];
double ss = (alpha+1)*(alpha+1)+beta*beta;
min = std::min(min, ss);
}
return min;
}
void
TriangularSylvester::multEigVector(KronVector &eig, const Vector &feig,
const Vector &keig)
{
int depth = eig.getDepth();
int m = eig.getM();
int n = eig.getN();
if (depth == 0)
eig = keig;
else
{
KronVector aux(m, n, depth-1);
multEigVector(aux, feig, keig);
for (int i = 0; i < m; i++)
{
KronVector eigi(eig, i);
eigi.zeros();
eigi.addComplex({ feig[2*i], feig[2*i+1] }, aux);
}
}
}