1 /*
2  * Copyright © 2004-2011 Ondra Kamenik
3  * Copyright © 2019 Dynare Team
4  *
5  * This file is part of Dynare.
6  *
7  * Dynare is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Dynare is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "BlockDiagonal.hh"
22 #include "int_power.hh"
23 
24 #include <iostream>
25 #include <utility>
26 
BlockDiagonal(ConstVector d,int d_size)27 BlockDiagonal::BlockDiagonal(ConstVector d, int d_size)
28   : QuasiTriangular(std::move(d), d_size),
29     row_len(d_size), col_len(d_size)
30 {
31   for (int i = 0; i < d_size; i++)
32     {
33       row_len[i] = d_size;
34       col_len[i] = 0;
35     }
36 }
37 
BlockDiagonal(const QuasiTriangular & t)38 BlockDiagonal::BlockDiagonal(const QuasiTriangular &t)
39   : QuasiTriangular(t),
40     row_len(t.nrows()), col_len(t.nrows())
41 {
42   for (int i = 0; i < t.nrows(); i++)
43     {
44       row_len[i] = t.nrows();
45       col_len[i] = 0;
46     }
47 }
48 
49 /* Put zeroes to right upper submatrix whose first column is defined
50    by ‘edge’ */
51 void
setZerosToRU(diag_iter edge)52 BlockDiagonal::setZerosToRU(diag_iter edge)
53 {
54   int iedge = edge->getIndex();
55   for (int i = 0; i < iedge; i++)
56     for (int j = iedge; j < ncols(); j++)
57       get(i, j) = 0.0;
58 }
59 
60 /* Updates row_len and col_len so that there are zeroes in upper right part, i.e.
61    ⎛T1  0⎞
62    ⎝ 0 T2⎠. The first column of T2 is given by diagonal iterator ‘edge’.
63 
64    Note the semantics of row_len and col_len. row_len[i] is distance
65    of the right-most non-zero element of i-th row from the left, and
66    col_len[j] is distance of top-most non-zero element of j-th column
67    to the top. (First element has distance 1).
68 */
69 void
setZeroBlockEdge(diag_iter edge)70 BlockDiagonal::setZeroBlockEdge(diag_iter edge)
71 {
72   setZerosToRU(edge);
73 
74   int iedge = edge->getIndex();
75   for (diag_iter run = diag_begin(); run != edge; ++run)
76     {
77       int ind = run->getIndex();
78       if (row_len[ind] > iedge)
79         {
80           row_len[ind] = iedge;
81           if (!run->isReal())
82             row_len[ind+1] = iedge;
83         }
84     }
85   for (diag_iter run = edge; run != diag_end(); ++run)
86     {
87       int ind = run->getIndex();
88       if (col_len[ind] < iedge)
89         {
90           col_len[ind] = iedge;
91           if (!run->isReal())
92             col_len[ind+1] = iedge;
93         }
94     }
95 }
96 
97 BlockDiagonal::const_col_iter
col_begin(const DiagonalBlock & b) const98 BlockDiagonal::col_begin(const DiagonalBlock &b) const
99 {
100   int jbar = b.getIndex();
101   int d_size = diagonal.getSize();
102   return const_col_iter(&getData()[jbar*d_size + col_len[jbar]], d_size,
103                         b.isReal(), col_len[jbar]);
104 }
105 
106 BlockDiagonal::col_iter
col_begin(const DiagonalBlock & b)107 BlockDiagonal::col_begin(const DiagonalBlock &b)
108 {
109   int jbar = b.getIndex();
110   int d_size = diagonal.getSize();
111   return col_iter(&getData()[jbar*d_size + col_len[jbar]], d_size,
112                   b.isReal(), col_len[jbar]);
113 }
114 
115 BlockDiagonal::const_row_iter
row_end(const DiagonalBlock & b) const116 BlockDiagonal::row_end(const DiagonalBlock &b) const
117 {
118   int jbar = b.getIndex();
119   int d_size = diagonal.getSize();
120   return const_row_iter(&getData()[d_size*row_len[jbar]+jbar], d_size,
121                         b.isReal(), row_len[jbar]);
122 }
123 
124 BlockDiagonal::row_iter
row_end(const DiagonalBlock & b)125 BlockDiagonal::row_end(const DiagonalBlock &b)
126 {
127   int jbar = b.getIndex();
128   int d_size = diagonal.getSize();
129   return row_iter(&getData()[d_size*row_len[jbar]+jbar], d_size,
130                   b.isReal(), row_len[jbar]);
131 }
132 
133 int
getNumZeros() const134 BlockDiagonal::getNumZeros() const
135 {
136   int sum = 0;
137   for (int i = 0; i < diagonal.getSize(); i++)
138     sum += diagonal.getSize() - row_len[i];
139   return sum;
140 }
141 
142 QuasiTriangular::const_diag_iter
findBlockStart(const_diag_iter from) const143 BlockDiagonal::findBlockStart(const_diag_iter from) const
144 {
145   if (from != diag_end())
146     {
147       ++from;
148       while (from != diag_end()
149              && col_len[from->getIndex()] != from->getIndex())
150         ++from;
151     }
152   return from;
153 }
154 
155 int
getLargestBlock() const156 BlockDiagonal::getLargestBlock() const
157 {
158   int largest = 0;
159   const_diag_iter start = diag_begin();
160   const_diag_iter end = findBlockStart(start);
161   while (start != diag_end())
162     {
163       int si = start->getIndex();
164       int ei = diagonal.getSize();
165       if (end != diag_end())
166         ei = end->getIndex();
167       largest = std::max(largest, ei-si);
168       start = end;
169       end = findBlockStart(start);
170     }
171   return largest;
172 }
173 
174 void
savePartOfX(int si,int ei,const KronVector & x,Vector & work)175 BlockDiagonal::savePartOfX(int si, int ei, const KronVector &x, Vector &work)
176 {
177   for (int i = si; i < ei; i++)
178     {
179       ConstKronVector xi(x, i);
180       Vector target(work, (i-si)*xi.length(), xi.length());
181       target = xi;
182     }
183 }
184 
185 void
multKronBlock(const_diag_iter start,const_diag_iter end,KronVector & x,Vector & work) const186 BlockDiagonal::multKronBlock(const_diag_iter start, const_diag_iter end,
187                              KronVector &x, Vector &work) const
188 {
189   int si = start->getIndex();
190   int ei = diagonal.getSize();
191   if (end != diag_end())
192     ei = end->getIndex();
193   savePartOfX(si, ei, x, work);
194 
195   for (const_diag_iter di = start; di != end; ++di)
196     {
197       int jbar = di->getIndex();
198       if (di->isReal())
199         {
200           KronVector xi(x, jbar);
201           xi.zeros();
202           Vector wi(work, (jbar-si)*xi.length(), xi.length());
203           xi.add(*(di->getAlpha()), wi);
204           for (const_row_iter ri = row_begin(*di); ri != row_end(*di); ++ri)
205             {
206               int col = ri.getCol();
207               Vector wj(work, (col-si)*xi.length(), xi.length());
208               xi.add(*ri, wj);
209             }
210         }
211       else
212         {
213           KronVector xi(x, jbar);
214           KronVector xii(x, jbar+1);
215           xi.zeros();
216           xii.zeros();
217           Vector wi(work, (jbar-si)*xi.length(), xi.length());
218           Vector wii(work, (jbar+1-si)*xi.length(), xi.length());
219           xi.add(*(di->getAlpha()), wi);
220           xi.add(di->getBeta1(), wii);
221           xii.add(di->getBeta2(), wi);
222           xii.add(*(di->getAlpha()), wii);
223           for (const_row_iter ri = row_begin(*di); ri != row_end(*di); ++ri)
224             {
225               int col = ri.getCol();
226               Vector wj(work, (col-si)*xi.length(), xi.length());
227               xi.add(ri.a(), wj);
228               xii.add(ri.b(), wj);
229             }
230         }
231     }
232 }
233 
234 void
multKronBlockTrans(const_diag_iter start,const_diag_iter end,KronVector & x,Vector & work) const235 BlockDiagonal::multKronBlockTrans(const_diag_iter start, const_diag_iter end,
236                                   KronVector &x, Vector &work) const
237 {
238   int si = start->getIndex();
239   int ei = diagonal.getSize();
240   if (end != diag_end())
241     ei = end->getIndex();
242   savePartOfX(si, ei, x, work);
243 
244   for (const_diag_iter di = start; di != end; ++di)
245     {
246       int jbar = di->getIndex();
247       if (di->isReal())
248         {
249           KronVector xi(x, jbar);
250           xi.zeros();
251           Vector wi(work, (jbar-si)*xi.length(), xi.length());
252           xi.add(*(di->getAlpha()), wi);
253           for (const_col_iter ci = col_begin(*di); ci != col_end(*di); ++ci)
254             {
255               int row = ci.getRow();
256               Vector wj(work, (row-si)*xi.length(), xi.length());
257               xi.add(*ci, wj);
258             }
259         }
260       else
261         {
262           KronVector xi(x, jbar);
263           KronVector xii(x, jbar+1);
264           xi.zeros();
265           xii.zeros();
266           Vector wi(work, (jbar-si)*xi.length(), xi.length());
267           Vector wii(work, (jbar+1-si)*xi.length(), xi.length());
268           xi.add(*(di->getAlpha()), wi);
269           xi.add(di->getBeta2(), wii);
270           xii.add(di->getBeta1(), wi);
271           xii.add(*(di->getAlpha()), wii);
272           for (const_col_iter ci = col_begin(*di); ci != col_end(*di); ++ci)
273             {
274               int row = ci.getRow();
275               Vector wj(work, (row-si)*xi.length(), xi.length());
276               xi.add(ci.a(), wj);
277               xii.add(ci.b(), wj);
278             }
279         }
280     }
281 }
282 
283 void
multKron(KronVector & x) const284 BlockDiagonal::multKron(KronVector &x) const
285 {
286   int largest = getLargestBlock();
287   Vector work(largest *x.getN()*power(x.getM(), x.getDepth()-1));
288   const_diag_iter start = diag_begin();
289   const_diag_iter end = findBlockStart(start);
290   while (start != diag_end())
291     {
292       multKronBlock(start, end, x, work);
293       start = end;
294       end = findBlockStart(start);
295     }
296 }
297 
298 void
multKronTrans(KronVector & x) const299 BlockDiagonal::multKronTrans(KronVector &x) const
300 {
301   int largest = getLargestBlock();
302   Vector work(largest *x.getN()*power(x.getM(), x.getDepth()-1));
303   const_diag_iter start = diag_begin();
304   const_diag_iter end = findBlockStart(start);
305   while (start != diag_end())
306     {
307       multKronBlockTrans(start, end, x, work);
308       start = end;
309       end = findBlockStart(start);
310     }
311 }
312 
313 void
printInfo() const314 BlockDiagonal::printInfo() const
315 {
316   std::cout << "Block sizes:";
317   int num_blocks = 0;
318   const_diag_iter start = diag_begin();
319   const_diag_iter end = findBlockStart(start);
320   while (start != diag_end())
321     {
322       int si = start->getIndex();
323       int ei = diagonal.getSize();
324       if (end != diag_end())
325         ei = end->getIndex();
326       std::cout << ' ' << ei-si;
327       num_blocks++;
328       start = end;
329       end = findBlockStart(start);
330     }
331   std::cout << std::endl
332             << "Num blocks: " << num_blocks << std::endl
333             << "There are " << getNumZeros() << " zeros out of " << getNumOffdiagonal() << std::endl;
334 }
335 
336 int
getNumBlocks() const337 BlockDiagonal::getNumBlocks() const
338 {
339   int num_blocks = 0;
340   const_diag_iter start = diag_begin();
341   const_diag_iter end = findBlockStart(start);
342   while (start != diag_end())
343     {
344       num_blocks++;
345       start = end;
346       end = findBlockStart(start);
347     }
348   return num_blocks;
349 }
350