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