/* * Copyright © 2004-2011 Ondra Kamenik * Copyright © 2019-2023 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 . */ #include "TriangularSylvester.hh" #include "BlockDiagonal.hh" #include "KronUtils.hh" #include "QuasiTriangularZero.hh" #include #include 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 (auto 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 { auto di = matrixF->diag_begin(); auto 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(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(dj)); KronVector y2(const_cast(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(dj)); KronUtils::multKron(*matrixF, *matrixK, y1); y1.mult(2 * alpha); // y11 KronVector y11(const_cast(djj)); KronUtils::multKron(*matrixF, *matrixK, y11); y11.mult(2 * alpha); // y2 KronVector y2(const_cast(dj)); KronUtils::multKron(*matrixFF, *matrixKK, y2); y2.mult(aspbs); // y22 KronVector y22(const_cast(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); } } }