2019-06-19 14:34:30 +02:00
|
|
|
|
/*
|
|
|
|
|
* 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
|
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 "BlockDiagonal.hh"
|
2019-02-19 14:50:00 +01:00
|
|
|
|
#include "int_power.hh"
|
2019-01-08 17:12:05 +01:00
|
|
|
|
|
2019-01-16 17:52:16 +01:00
|
|
|
|
#include <iostream>
|
Dynare++ / sylvester equation solver: refactor Vector and ConstVector classes
- these classes now encapsulate a std::shared_ptr<{const, }double>, so that
they do not perform memory management, and several {Const,}Vector instances
can transparently share the same underlying data
- make converting constructor from ConstVector to Vector explicit, since that
entails memory allocation (but the reverse conversion is almost costless, so
keep it implicit); do the same for GeneralMatrix/ConstGeneralMatrix,
TwoDMatrix/ConstTwoDMatrix
- remove the constructors that were extracting a row/column from a matrix, and
replace them by getRow() and getCol() methods on {Const,}GeneralMatrix
- rename and change the API of the complex version Vector::add(), so that it is
explicit that it deals with complex numbers
- add constructors that take a MATLAB mxArray
2019-01-22 16:07:44 +01:00
|
|
|
|
#include <utility>
|
2019-01-08 17:12:05 +01:00
|
|
|
|
|
Dynare++ / sylvester equation solver: refactor Vector and ConstVector classes
- these classes now encapsulate a std::shared_ptr<{const, }double>, so that
they do not perform memory management, and several {Const,}Vector instances
can transparently share the same underlying data
- make converting constructor from ConstVector to Vector explicit, since that
entails memory allocation (but the reverse conversion is almost costless, so
keep it implicit); do the same for GeneralMatrix/ConstGeneralMatrix,
TwoDMatrix/ConstTwoDMatrix
- remove the constructors that were extracting a row/column from a matrix, and
replace them by getRow() and getCol() methods on {Const,}GeneralMatrix
- rename and change the API of the complex version Vector::add(), so that it is
explicit that it deals with complex numbers
- add constructors that take a MATLAB mxArray
2019-01-22 16:07:44 +01:00
|
|
|
|
BlockDiagonal::BlockDiagonal(ConstVector d, int d_size)
|
|
|
|
|
: QuasiTriangular(std::move(d), d_size),
|
2019-01-25 15:27:20 +01:00
|
|
|
|
row_len(d_size), col_len(d_size)
|
2019-01-08 17:12:05 +01:00
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < d_size; i++)
|
|
|
|
|
{
|
|
|
|
|
row_len[i] = d_size;
|
|
|
|
|
col_len[i] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BlockDiagonal::BlockDiagonal(const QuasiTriangular &t)
|
|
|
|
|
: QuasiTriangular(t),
|
2019-04-16 12:40:50 +02:00
|
|
|
|
row_len(t.nrows()), col_len(t.nrows())
|
2019-01-08 17:12:05 +01:00
|
|
|
|
{
|
2019-04-16 12:40:50 +02:00
|
|
|
|
for (int i = 0; i < t.nrows(); i++)
|
2019-01-08 17:12:05 +01:00
|
|
|
|
{
|
2019-04-16 12:40:50 +02:00
|
|
|
|
row_len[i] = t.nrows();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
col_len[i] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
/* Put zeroes to right upper submatrix whose first column is defined
|
|
|
|
|
by ‘edge’ */
|
2019-01-08 17:12:05 +01:00
|
|
|
|
void
|
|
|
|
|
BlockDiagonal::setZerosToRU(diag_iter edge)
|
|
|
|
|
{
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int iedge = edge->getIndex();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
for (int i = 0; i < iedge; i++)
|
2019-04-16 12:40:50 +02:00
|
|
|
|
for (int j = iedge; j < ncols(); j++)
|
2019-01-08 17:12:05 +01:00
|
|
|
|
get(i, j) = 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
/* Updates row_len and col_len so that there are zeroes in upper right part, i.e.
|
2019-03-28 18:56:46 +01:00
|
|
|
|
⎛T1 0⎞
|
|
|
|
|
⎝ 0 T2⎠. The first column of T2 is given by diagonal iterator ‘edge’.
|
2019-01-08 17:12:05 +01:00
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
Note the semantics of row_len and col_len. row_len[i] is distance
|
|
|
|
|
of the right-most non-zero element of i-th row from the left, and
|
|
|
|
|
col_len[j] is distance of top-most non-zero element of j-th column
|
|
|
|
|
to the top. (First element has distance 1).
|
2019-12-20 14:36:20 +01:00
|
|
|
|
*/
|
2019-01-08 17:12:05 +01:00
|
|
|
|
void
|
|
|
|
|
BlockDiagonal::setZeroBlockEdge(diag_iter edge)
|
|
|
|
|
{
|
|
|
|
|
setZerosToRU(edge);
|
|
|
|
|
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int iedge = edge->getIndex();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
for (diag_iter run = diag_begin(); run != edge; ++run)
|
|
|
|
|
{
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int ind = run->getIndex();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
if (row_len[ind] > iedge)
|
|
|
|
|
{
|
|
|
|
|
row_len[ind] = iedge;
|
2019-03-08 15:32:13 +01:00
|
|
|
|
if (!run->isReal())
|
2019-01-08 17:12:05 +01:00
|
|
|
|
row_len[ind+1] = iedge;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (diag_iter run = edge; run != diag_end(); ++run)
|
|
|
|
|
{
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int ind = run->getIndex();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
if (col_len[ind] < iedge)
|
|
|
|
|
{
|
|
|
|
|
col_len[ind] = iedge;
|
2019-03-08 15:32:13 +01:00
|
|
|
|
if (!run->isReal())
|
2019-01-08 17:12:05 +01:00
|
|
|
|
col_len[ind+1] = iedge;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BlockDiagonal::const_col_iter
|
|
|
|
|
BlockDiagonal::col_begin(const DiagonalBlock &b) const
|
|
|
|
|
{
|
|
|
|
|
int jbar = b.getIndex();
|
|
|
|
|
int d_size = diagonal.getSize();
|
|
|
|
|
return const_col_iter(&getData()[jbar*d_size + col_len[jbar]], d_size,
|
|
|
|
|
b.isReal(), col_len[jbar]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BlockDiagonal::col_iter
|
|
|
|
|
BlockDiagonal::col_begin(const DiagonalBlock &b)
|
|
|
|
|
{
|
|
|
|
|
int jbar = b.getIndex();
|
|
|
|
|
int d_size = diagonal.getSize();
|
|
|
|
|
return col_iter(&getData()[jbar*d_size + col_len[jbar]], d_size,
|
|
|
|
|
b.isReal(), col_len[jbar]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BlockDiagonal::const_row_iter
|
|
|
|
|
BlockDiagonal::row_end(const DiagonalBlock &b) const
|
|
|
|
|
{
|
|
|
|
|
int jbar = b.getIndex();
|
|
|
|
|
int d_size = diagonal.getSize();
|
|
|
|
|
return const_row_iter(&getData()[d_size*row_len[jbar]+jbar], d_size,
|
|
|
|
|
b.isReal(), row_len[jbar]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BlockDiagonal::row_iter
|
|
|
|
|
BlockDiagonal::row_end(const DiagonalBlock &b)
|
|
|
|
|
{
|
|
|
|
|
int jbar = b.getIndex();
|
|
|
|
|
int d_size = diagonal.getSize();
|
|
|
|
|
return row_iter(&getData()[d_size*row_len[jbar]+jbar], d_size,
|
|
|
|
|
b.isReal(), row_len[jbar]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
BlockDiagonal::getNumZeros() const
|
|
|
|
|
{
|
|
|
|
|
int sum = 0;
|
|
|
|
|
for (int i = 0; i < diagonal.getSize(); i++)
|
2019-01-25 15:27:20 +01:00
|
|
|
|
sum += diagonal.getSize() - row_len[i];
|
2019-01-08 17:12:05 +01:00
|
|
|
|
return sum;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QuasiTriangular::const_diag_iter
|
|
|
|
|
BlockDiagonal::findBlockStart(const_diag_iter from) const
|
|
|
|
|
{
|
|
|
|
|
if (from != diag_end())
|
|
|
|
|
{
|
|
|
|
|
++from;
|
|
|
|
|
while (from != diag_end()
|
2019-03-08 15:32:13 +01:00
|
|
|
|
&& col_len[from->getIndex()] != from->getIndex())
|
2019-01-08 17:12:05 +01:00
|
|
|
|
++from;
|
|
|
|
|
}
|
|
|
|
|
return from;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
BlockDiagonal::getLargestBlock() const
|
|
|
|
|
{
|
|
|
|
|
int largest = 0;
|
|
|
|
|
const_diag_iter start = diag_begin();
|
|
|
|
|
const_diag_iter end = findBlockStart(start);
|
|
|
|
|
while (start != diag_end())
|
|
|
|
|
{
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int si = start->getIndex();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
int ei = diagonal.getSize();
|
|
|
|
|
if (end != diag_end())
|
2019-03-08 15:32:13 +01:00
|
|
|
|
ei = end->getIndex();
|
|
|
|
|
largest = std::max(largest, ei-si);
|
2019-01-08 17:12:05 +01:00
|
|
|
|
start = end;
|
|
|
|
|
end = findBlockStart(start);
|
|
|
|
|
}
|
|
|
|
|
return largest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
BlockDiagonal::savePartOfX(int si, int ei, const KronVector &x, Vector &work)
|
|
|
|
|
{
|
|
|
|
|
for (int i = si; i < ei; i++)
|
|
|
|
|
{
|
|
|
|
|
ConstKronVector xi(x, i);
|
|
|
|
|
Vector target(work, (i-si)*xi.length(), xi.length());
|
|
|
|
|
target = xi;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
BlockDiagonal::multKronBlock(const_diag_iter start, const_diag_iter end,
|
|
|
|
|
KronVector &x, Vector &work) const
|
|
|
|
|
{
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int si = start->getIndex();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
int ei = diagonal.getSize();
|
|
|
|
|
if (end != diag_end())
|
2019-03-08 15:32:13 +01:00
|
|
|
|
ei = end->getIndex();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
savePartOfX(si, ei, x, work);
|
|
|
|
|
|
|
|
|
|
for (const_diag_iter di = start; di != end; ++di)
|
|
|
|
|
{
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int jbar = di->getIndex();
|
|
|
|
|
if (di->isReal())
|
2019-01-08 17:12:05 +01:00
|
|
|
|
{
|
|
|
|
|
KronVector xi(x, jbar);
|
|
|
|
|
xi.zeros();
|
|
|
|
|
Vector wi(work, (jbar-si)*xi.length(), xi.length());
|
2019-03-08 15:32:13 +01:00
|
|
|
|
xi.add(*(di->getAlpha()), wi);
|
2019-01-08 17:12:05 +01:00
|
|
|
|
for (const_row_iter ri = row_begin(*di); ri != row_end(*di); ++ri)
|
|
|
|
|
{
|
|
|
|
|
int col = ri.getCol();
|
|
|
|
|
Vector wj(work, (col-si)*xi.length(), xi.length());
|
|
|
|
|
xi.add(*ri, wj);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
KronVector xi(x, jbar);
|
|
|
|
|
KronVector xii(x, jbar+1);
|
|
|
|
|
xi.zeros();
|
|
|
|
|
xii.zeros();
|
|
|
|
|
Vector wi(work, (jbar-si)*xi.length(), xi.length());
|
|
|
|
|
Vector wii(work, (jbar+1-si)*xi.length(), xi.length());
|
2019-03-08 15:32:13 +01:00
|
|
|
|
xi.add(*(di->getAlpha()), wi);
|
|
|
|
|
xi.add(di->getBeta1(), wii);
|
|
|
|
|
xii.add(di->getBeta2(), wi);
|
|
|
|
|
xii.add(*(di->getAlpha()), wii);
|
2019-01-08 17:12:05 +01:00
|
|
|
|
for (const_row_iter ri = row_begin(*di); ri != row_end(*di); ++ri)
|
|
|
|
|
{
|
|
|
|
|
int col = ri.getCol();
|
|
|
|
|
Vector wj(work, (col-si)*xi.length(), xi.length());
|
|
|
|
|
xi.add(ri.a(), wj);
|
|
|
|
|
xii.add(ri.b(), wj);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
BlockDiagonal::multKronBlockTrans(const_diag_iter start, const_diag_iter end,
|
|
|
|
|
KronVector &x, Vector &work) const
|
|
|
|
|
{
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int si = start->getIndex();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
int ei = diagonal.getSize();
|
|
|
|
|
if (end != diag_end())
|
2019-03-08 15:32:13 +01:00
|
|
|
|
ei = end->getIndex();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
savePartOfX(si, ei, x, work);
|
|
|
|
|
|
|
|
|
|
for (const_diag_iter di = start; di != end; ++di)
|
|
|
|
|
{
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int jbar = di->getIndex();
|
|
|
|
|
if (di->isReal())
|
2019-01-08 17:12:05 +01:00
|
|
|
|
{
|
|
|
|
|
KronVector xi(x, jbar);
|
|
|
|
|
xi.zeros();
|
|
|
|
|
Vector wi(work, (jbar-si)*xi.length(), xi.length());
|
2019-03-08 15:32:13 +01:00
|
|
|
|
xi.add(*(di->getAlpha()), wi);
|
2019-01-08 17:12:05 +01:00
|
|
|
|
for (const_col_iter ci = col_begin(*di); ci != col_end(*di); ++ci)
|
|
|
|
|
{
|
|
|
|
|
int row = ci.getRow();
|
|
|
|
|
Vector wj(work, (row-si)*xi.length(), xi.length());
|
|
|
|
|
xi.add(*ci, wj);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
KronVector xi(x, jbar);
|
|
|
|
|
KronVector xii(x, jbar+1);
|
|
|
|
|
xi.zeros();
|
|
|
|
|
xii.zeros();
|
|
|
|
|
Vector wi(work, (jbar-si)*xi.length(), xi.length());
|
|
|
|
|
Vector wii(work, (jbar+1-si)*xi.length(), xi.length());
|
2019-03-08 15:32:13 +01:00
|
|
|
|
xi.add(*(di->getAlpha()), wi);
|
|
|
|
|
xi.add(di->getBeta2(), wii);
|
|
|
|
|
xii.add(di->getBeta1(), wi);
|
|
|
|
|
xii.add(*(di->getAlpha()), wii);
|
2019-01-08 17:12:05 +01:00
|
|
|
|
for (const_col_iter ci = col_begin(*di); ci != col_end(*di); ++ci)
|
|
|
|
|
{
|
|
|
|
|
int row = ci.getRow();
|
|
|
|
|
Vector wj(work, (row-si)*xi.length(), xi.length());
|
|
|
|
|
xi.add(ci.a(), wj);
|
|
|
|
|
xii.add(ci.b(), wj);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
BlockDiagonal::multKron(KronVector &x) const
|
|
|
|
|
{
|
|
|
|
|
int largest = getLargestBlock();
|
|
|
|
|
Vector work(largest *x.getN()*power(x.getM(), x.getDepth()-1));
|
|
|
|
|
const_diag_iter start = diag_begin();
|
|
|
|
|
const_diag_iter end = findBlockStart(start);
|
|
|
|
|
while (start != diag_end())
|
|
|
|
|
{
|
|
|
|
|
multKronBlock(start, end, x, work);
|
|
|
|
|
start = end;
|
|
|
|
|
end = findBlockStart(start);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
BlockDiagonal::multKronTrans(KronVector &x) const
|
|
|
|
|
{
|
|
|
|
|
int largest = getLargestBlock();
|
|
|
|
|
Vector work(largest *x.getN()*power(x.getM(), x.getDepth()-1));
|
|
|
|
|
const_diag_iter start = diag_begin();
|
|
|
|
|
const_diag_iter end = findBlockStart(start);
|
|
|
|
|
while (start != diag_end())
|
|
|
|
|
{
|
|
|
|
|
multKronBlockTrans(start, end, x, work);
|
|
|
|
|
start = end;
|
|
|
|
|
end = findBlockStart(start);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
BlockDiagonal::printInfo() const
|
|
|
|
|
{
|
2019-01-16 17:52:16 +01:00
|
|
|
|
std::cout << "Block sizes:";
|
2019-01-08 17:12:05 +01:00
|
|
|
|
int num_blocks = 0;
|
|
|
|
|
const_diag_iter start = diag_begin();
|
|
|
|
|
const_diag_iter end = findBlockStart(start);
|
|
|
|
|
while (start != diag_end())
|
|
|
|
|
{
|
2019-03-08 15:32:13 +01:00
|
|
|
|
int si = start->getIndex();
|
2019-01-08 17:12:05 +01:00
|
|
|
|
int ei = diagonal.getSize();
|
|
|
|
|
if (end != diag_end())
|
2019-03-08 15:32:13 +01:00
|
|
|
|
ei = end->getIndex();
|
2019-01-16 17:52:16 +01:00
|
|
|
|
std::cout << ' ' << ei-si;
|
2019-01-08 17:12:05 +01:00
|
|
|
|
num_blocks++;
|
|
|
|
|
start = end;
|
|
|
|
|
end = findBlockStart(start);
|
|
|
|
|
}
|
2019-01-16 17:52:16 +01:00
|
|
|
|
std::cout << std::endl
|
|
|
|
|
<< "Num blocks: " << num_blocks << std::endl
|
|
|
|
|
<< "There are " << getNumZeros() << " zeros out of " << getNumOffdiagonal() << std::endl;
|
2019-01-08 17:12:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
BlockDiagonal::getNumBlocks() const
|
|
|
|
|
{
|
|
|
|
|
int num_blocks = 0;
|
|
|
|
|
const_diag_iter start = diag_begin();
|
|
|
|
|
const_diag_iter end = findBlockStart(start);
|
|
|
|
|
while (start != diag_end())
|
|
|
|
|
{
|
|
|
|
|
num_blocks++;
|
|
|
|
|
start = end;
|
|
|
|
|
end = findBlockStart(start);
|
|
|
|
|
}
|
|
|
|
|
return num_blocks;
|
|
|
|
|
}
|