2019-06-19 14:34:30 +02:00
|
|
|
|
/*
|
|
|
|
|
* Copyright © 2004-2011 Ondra Kamenik
|
2023-12-04 16:55:51 +01:00
|
|
|
|
* Copyright © 2019-2023 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-08 17:12:05 +01:00
|
|
|
|
|
|
|
|
|
#include "SimilarityDecomp.hh"
|
|
|
|
|
#include "SchurDecomp.hh"
|
|
|
|
|
#include "SchurDecompEig.hh"
|
|
|
|
|
#include "SylvException.hh"
|
|
|
|
|
|
|
|
|
|
#include <dynlapack.h>
|
|
|
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
2023-11-29 19:00:21 +01:00
|
|
|
|
SimilarityDecomp::SimilarityDecomp(const ConstVector& d, int d_size, double log10norm)
|
2019-01-08 17:12:05 +01:00
|
|
|
|
{
|
2023-11-29 19:00:21 +01:00
|
|
|
|
SchurDecomp sd(SqSylvMatrix(Vector {d}, d_size));
|
2019-01-15 18:55:09 +01:00
|
|
|
|
q = std::make_unique<SqSylvMatrix>(sd.getQ());
|
|
|
|
|
b = std::make_unique<BlockDiagonal>(sd.getT());
|
|
|
|
|
invq = std::make_unique<SqSylvMatrix>(d_size);
|
2019-01-08 17:12:05 +01:00
|
|
|
|
invq->setUnit();
|
|
|
|
|
invq->multLeftTrans(sd.getQ());
|
|
|
|
|
double norm = pow(10.0, log10norm);
|
|
|
|
|
diagonalize(norm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
SimilarityDecomp::getXDim(diag_iter start, diag_iter end, int& rows, int& cols) const
|
2019-01-08 17:12:05 +01:00
|
|
|
|
{
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int si = start->getIndex();
|
|
|
|
|
int ei = end->getIndex();
|
2019-04-16 12:40:50 +02:00
|
|
|
|
cols = b->nrows() - ei;
|
2019-01-08 17:12:05 +01:00
|
|
|
|
rows = ei - si;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
/* Find solution of X for diagonal block given by start(incl.) and
|
|
|
|
|
end(excl.). If the solution cannot be found, or it is greater than
|
|
|
|
|
norm, X is not changed and flase is returned.
|
2019-12-20 14:36:20 +01:00
|
|
|
|
*/
|
2019-01-08 17:12:05 +01:00
|
|
|
|
bool
|
2023-11-29 19:00:21 +01:00
|
|
|
|
SimilarityDecomp::solveX(diag_iter start, diag_iter end, GeneralMatrix& X, double norm) const
|
2019-01-08 17:12:05 +01:00
|
|
|
|
{
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int si = start->getIndex();
|
|
|
|
|
int ei = end->getIndex();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
|
2023-11-29 19:00:21 +01:00
|
|
|
|
SqSylvMatrix A(const_cast<const BlockDiagonal&>(*b), si, si, X.nrows());
|
|
|
|
|
SqSylvMatrix B(const_cast<const BlockDiagonal&>(*b), ei, ei, X.ncols());
|
|
|
|
|
GeneralMatrix C(const_cast<const BlockDiagonal&>(*b), si, ei, X.nrows(), X.ncols());
|
2019-01-08 17:12:05 +01:00
|
|
|
|
|
|
|
|
|
lapack_int isgn = -1;
|
2019-04-16 12:40:50 +02:00
|
|
|
|
lapack_int m = A.nrows();
|
|
|
|
|
lapack_int n = B.nrows();
|
2019-01-24 13:08:05 +01:00
|
|
|
|
lapack_int lda = A.getLD(), ldb = B.getLD();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
double scale;
|
|
|
|
|
lapack_int info;
|
2023-11-29 19:00:21 +01:00
|
|
|
|
dtrsyl("N", "N", &isgn, &m, &n, A.base(), &lda, B.base(), &ldb, C.base(), &m, &scale, &info);
|
2019-01-08 17:12:05 +01:00
|
|
|
|
if (info < -1)
|
|
|
|
|
throw SYLV_MES_EXCEPTION("Wrong parameter to LAPACK dtrsyl.");
|
|
|
|
|
|
|
|
|
|
if (info == 1 || scale < 1)
|
|
|
|
|
return false;
|
|
|
|
|
if (C.getData().getMax() > norm)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
X = C;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-28 18:56:46 +01:00
|
|
|
|
/* ⎛I −X⎞ ⎛I X⎞
|
|
|
|
|
Multiply Q and invQ with ⎝0 I⎠ and ⎝0 I⎠ respectively. This also sets X=−X. */
|
2019-01-08 17:12:05 +01:00
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
SimilarityDecomp::updateTransform(diag_iter start, diag_iter end, GeneralMatrix& X)
|
2019-01-08 17:12:05 +01:00
|
|
|
|
{
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int si = start->getIndex();
|
|
|
|
|
int ei = end->getIndex();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
|
2019-04-16 12:40:50 +02:00
|
|
|
|
SqSylvMatrix iX(q->nrows());
|
2019-01-08 17:12:05 +01:00
|
|
|
|
iX.setUnit();
|
|
|
|
|
iX.place(X, si, ei);
|
|
|
|
|
invq->GeneralMatrix::multLeft(iX);
|
|
|
|
|
|
|
|
|
|
iX.setUnit();
|
|
|
|
|
X.mult(-1.0);
|
|
|
|
|
iX.place(X, si, ei);
|
|
|
|
|
q->multRight(iX);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
SimilarityDecomp::bringGuiltyBlock(diag_iter start, diag_iter& end)
|
2019-01-08 17:12:05 +01:00
|
|
|
|
{
|
|
|
|
|
double av = b->getAverageDiagSize(start, end);
|
2023-12-04 16:55:51 +01:00
|
|
|
|
auto guilty = b->findClosestDiagBlock(end, b->diag_end(), av);
|
2023-11-29 19:00:21 +01:00
|
|
|
|
SchurDecompEig sd(*b); // works on b including diagonal structure
|
2019-01-08 17:12:05 +01:00
|
|
|
|
end = sd.bubbleEigen(guilty, end); // iterators are valid
|
|
|
|
|
++end;
|
|
|
|
|
q->multRight(sd.getQ());
|
|
|
|
|
invq->multLeftTrans(sd.getQ());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SimilarityDecomp::diagonalize(double norm)
|
|
|
|
|
{
|
2023-12-04 16:55:51 +01:00
|
|
|
|
auto start = b->diag_begin();
|
|
|
|
|
auto end = start;
|
2019-01-08 17:12:05 +01:00
|
|
|
|
++end;
|
|
|
|
|
|
|
|
|
|
while (end != b->diag_end())
|
|
|
|
|
{
|
|
|
|
|
int xrows;
|
|
|
|
|
int xcols;
|
|
|
|
|
getXDim(start, end, xrows, xcols);
|
|
|
|
|
GeneralMatrix X(xrows, xcols);
|
|
|
|
|
if (solveX(start, end, X, norm))
|
|
|
|
|
{
|
|
|
|
|
updateTransform(start, end, X);
|
|
|
|
|
b->setZeroBlockEdge(end);
|
|
|
|
|
start = end;
|
|
|
|
|
++end;
|
|
|
|
|
}
|
|
|
|
|
else
|
2019-01-25 15:27:20 +01:00
|
|
|
|
bringGuiltyBlock(start, end); // moves with end
|
2019-01-08 17:12:05 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
SimilarityDecomp::check(SylvParams& pars, const GeneralMatrix& m) const
|
2019-01-08 17:12:05 +01:00
|
|
|
|
{
|
2019-03-28 18:56:46 +01:00
|
|
|
|
// M − Q·B·Q⁻¹
|
2019-02-26 16:54:34 +01:00
|
|
|
|
SqSylvMatrix c(getQ() * getB());
|
2019-01-08 17:12:05 +01:00
|
|
|
|
c.multRight(getInvQ());
|
|
|
|
|
c.add(-1.0, m);
|
|
|
|
|
pars.f_err1 = c.getNorm1();
|
|
|
|
|
pars.f_errI = c.getNormInf();
|
|
|
|
|
|
2019-03-28 18:56:46 +01:00
|
|
|
|
// I − Q·Q⁻¹
|
2019-01-08 17:12:05 +01:00
|
|
|
|
c.setUnit();
|
|
|
|
|
c.mult(-1);
|
|
|
|
|
c.multAndAdd(getQ(), getInvQ());
|
|
|
|
|
pars.viv_err1 = c.getNorm1();
|
|
|
|
|
pars.viv_errI = c.getNormInf();
|
|
|
|
|
|
2019-03-28 18:56:46 +01:00
|
|
|
|
// I − Q⁻¹·Q
|
2019-01-08 17:12:05 +01:00
|
|
|
|
c.setUnit();
|
|
|
|
|
c.mult(-1);
|
|
|
|
|
c.multAndAdd(getInvQ(), getQ());
|
|
|
|
|
pars.ivv_err1 = c.getNorm1();
|
|
|
|
|
pars.ivv_errI = c.getNormInf();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
SimilarityDecomp::infoToPars(SylvParams& pars) const
|
2019-01-08 17:12:05 +01:00
|
|
|
|
{
|
|
|
|
|
pars.f_blocks = getB().getNumBlocks();
|
|
|
|
|
pars.f_largest = getB().getLargestBlock();
|
|
|
|
|
pars.f_zeros = getB().getNumZeros();
|
|
|
|
|
pars.f_offdiag = getB().getNumOffdiagonal();
|
|
|
|
|
}
|