1 /*
2 * Copyright © 2005 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 "utils/cc/exception.hh"
22 #include "dynamic_atoms.hh"
23
24 using namespace ogp;
25
26 void
insert(string name)27 NameStorage::insert(string name)
28 {
29 if (!query(name))
30 {
31 name_store.push_back(name);
32 name_set.insert(std::move(name));
33 }
34 }
35
36 void
print() const37 NameStorage::print() const
38 {
39 for (auto i : name_store)
40 std::cout << i << '\n';
41 }
42
43 void
import_constants(const Constants & c,OperationTree & otree,Tintintmap & tmap)44 Constants::import_constants(const Constants &c, OperationTree &otree, Tintintmap &tmap)
45 {
46 for (auto it : c.cmap)
47 {
48 int told = it.first;
49 int tnew = otree.add_nulary();
50 tmap.emplace(told, tnew);
51 add_constant(tnew, it.second);
52 }
53 }
54
55 void
setValues(EvalTree & et) const56 Constants::setValues(EvalTree &et) const
57 {
58 for (const auto &it : cmap)
59 et.set_nulary(it.first, it.second);
60 }
61
62 void
add_constant(int t,double val)63 Constants::add_constant(int t, double val)
64 {
65 cmap.emplace(t, val);
66 cinvmap.emplace(val, t);
67 }
68
69 bool
is_constant(int t) const70 Constants::is_constant(int t) const
71 {
72 if (t < OperationTree::num_constants)
73 return true;
74 auto it = cmap.find(t);
75 return (it != cmap.end());
76 }
77
78 double
get_constant_value(int t) const79 Constants::get_constant_value(int t) const
80 {
81 auto it = cmap.find(t);
82 if (it != cmap.end())
83 return it->second;
84 else
85 throw ogu::Exception(__FILE__, __LINE__,
86 "Tree index is not constant in Constants::get_constant_value");
87 }
88
89 int
check(const string & str) const90 Constants::check(const string &str) const
91 {
92 double d = std::stod(str);
93 auto it = cinvmap.find(d);
94 if (it != cinvmap.end())
95 return it->second;
96 else
97 return -1;
98 }
99
100 void
print() const101 Constants::print() const
102 {
103 for (const auto &it : cmap)
104 std::cout << "$" << it.first << ": " << it.second << "\n";
105 }
106
107 int
check(const string & name) const108 DynamicAtoms::check(const string &name) const
109 {
110 if (is_string_constant(name))
111 return Constants::check(name);
112
113 return check_variable(name);
114 }
115
116 int
check_variable(const string & name) const117 DynamicAtoms::check_variable(const string &name) const
118 {
119 string str;
120 int ll;
121 parse_variable(name, str, ll);
122 auto it = vars.find(str);
123
124 if (it != vars.end())
125 {
126 const Tlagmap &lmap = it->second;
127 auto itt = lmap.find(ll);
128 if (itt != lmap.end())
129 return itt->second;
130 }
131 return -1;
132 }
133
134 void
assign(const string & name,int t)135 DynamicAtoms::assign(const string &name, int t)
136 {
137 if (is_string_constant(name))
138 assign_constant(name, t);
139 else
140 assign_variable(name, t);
141 }
142
143 void
assign_constant(const string & name,int t)144 DynamicAtoms::assign_constant(const string &name, int t)
145 {
146 double val = std::stod(name);
147 add_constant(t, val);
148 }
149
150 // parse the name and then call assing_variable(varname, ll, t)
151
152 void
assign_variable(const string & name,int t)153 DynamicAtoms::assign_variable(const string &name, int t)
154 {
155 int ll;
156 string str;
157 parse_variable(name, str, ll);
158 // here str is just name without lead/lag
159 varnames.insert(str);
160
161 assign_variable(str, ll, t);
162 }
163
164 void
assign_variable(const string & varname,int ll,int t)165 DynamicAtoms::assign_variable(const string &varname, int ll, int t)
166 {
167 if (indices.end() != indices.find(t))
168 throw ogu::Exception(__FILE__, __LINE__,
169 "Attempt to assign already allocated tree index");
170
171 auto it = vars.find(varname);
172 if (it != vars.end())
173 {
174 Tlagmap &lmap = it->second;
175 if (lmap.end() != lmap.find(ll))
176 throw ogu::Exception(__FILE__, __LINE__,
177 "Attempt to assign already allocated variable");
178 lmap.emplace(ll, t);
179 }
180 else
181 {
182 Tlagmap lmap;
183 lmap.emplace(ll, t);
184 vars.emplace(varname, lmap);
185 }
186 indices.emplace(t, varname);
187
188 nv++;
189 minlag = std::min(ll, minlag);
190 maxlead = std::max(ll, maxlead);
191 }
192
193 void
unassign_variable(const string & varname,int ll,int t)194 DynamicAtoms::unassign_variable(const string &varname, int ll, int t)
195 {
196 auto it = vars.find(varname);
197 if (it != vars.end())
198 {
199 Tlagmap &lmap = it->second;
200 auto itt = lmap.find(ll);
201 if (itt != lmap.end())
202 {
203 if (itt->second == t)
204 {
205 // erase it from the lagmap; if it becomes empty,
206 // erase the lagmap from varmap
207 lmap.erase(itt);
208 if (lmap.size() == 0)
209 vars.erase(it);
210 // erase it from the indices
211 auto ittt = indices.find(t);
212 if (ittt != indices.end())
213 indices.erase(ittt);
214
215 nv--;
216 if (ll == minlag || ll == maxlead)
217 update_minmaxll();
218 }
219 else
220 throw ogu::Exception(__FILE__, __LINE__,
221 "Tree index inconsistent in DynamicAtoms::unassign_variable");
222 }
223 else
224 throw ogu::Exception(__FILE__, __LINE__,
225 "Lead/lag of the variable not found in DynamicAtoms::unassign_variable");
226 }
227 else
228 throw ogu::Exception(__FILE__, __LINE__,
229 "Variable not found in DynamicAtoms::unassign_variable");
230 }
231
232 void
update_minmaxll()233 DynamicAtoms::update_minmaxll()
234 {
235 minlag = std::numeric_limits<int>::max();
236 maxlead = std::numeric_limits<int>::min();
237 for (const auto &it : vars)
238 {
239 const Tlagmap &lmap = it.second;
240 for (auto itt : lmap)
241 {
242 int ll = itt.first;
243 minlag = std::min(ll, minlag);
244 maxlead = std::max(ll, maxlead);
245 }
246 }
247 }
248
249 vector<int>
variables() const250 DynamicAtoms::variables() const
251 {
252 vector<int> res;
253 for (const auto &var : vars)
254 {
255 const Tlagmap &lmap = var.second;
256 for (auto itt : lmap)
257 res.push_back(itt.second);
258 }
259 return res;
260 }
261
262 void
varspan(int t,int & mlead,int & mlag) const263 DynamicAtoms::varspan(int t, int &mlead, int &mlag) const
264 {
265 auto it = indices.find(t);
266 if (indices.end() == it)
267 {
268 mlead = std::numeric_limits<int>::min();
269 mlag = std::numeric_limits<int>::max();
270 return;
271 }
272 varspan(it->second, mlead, mlag);
273 }
274
275 void
varspan(const string & name,int & mlead,int & mlag) const276 DynamicAtoms::varspan(const string &name, int &mlead, int &mlag) const
277 {
278 auto it = vars.find(name);
279 if (vars.end() == it)
280 {
281 mlead = std::numeric_limits<int>::min();
282 mlag = std::numeric_limits<int>::max();
283 return;
284 }
285 const Tlagmap &lmap = it->second;
286 auto beg = lmap.begin();
287 auto end = lmap.rbegin();
288 mlag = beg->first;
289 mlead = end->first;
290 }
291
292 void
varspan(const vector<string> & names,int & mlead,int & mlag) const293 DynamicAtoms::varspan(const vector<string> &names, int &mlead, int &mlag) const
294 {
295 mlead = std::numeric_limits<int>::min();
296 mlag = std::numeric_limits<int>::max();
297 for (const auto &name : names)
298 {
299 int lag, lead;
300 varspan(name, lead, lag);
301 mlead = std::max(lead, mlead);
302 mlag = std::min(lag, mlag);
303 }
304 }
305
306 bool
is_named_atom(int t) const307 DynamicAtoms::is_named_atom(int t) const
308 {
309 return indices.end() != indices.find(t);
310 }
311
312 int
index(const string & name,int ll) const313 DynamicAtoms::index(const string &name, int ll) const
314 {
315 auto it = vars.find(name);
316 if (vars.end() != it)
317 {
318 const Tlagmap &lmap = it->second;
319 auto itt = lmap.find(ll);
320 if (lmap.end() != itt)
321 return itt->second;
322 }
323 return -1;
324 }
325
326 bool
is_referenced(const string & name) const327 DynamicAtoms::is_referenced(const string &name) const
328 {
329 return vars.find(name) != vars.end();
330 }
331
332 const DynamicAtoms::Tlagmap &
lagmap(const string & name) const333 DynamicAtoms::lagmap(const string &name) const
334 {
335 auto it = vars.find(name);
336 if (vars.end() == it)
337 throw ogu::Exception(__FILE__, __LINE__,
338 std::string("Couldn't find the name ")
339 + name + " in DynamicAtoms::lagmap");
340 return it->second;
341 }
342
343 const string &
name(int t) const344 DynamicAtoms::name(int t) const
345 {
346 auto it = indices.find(t);
347 if (indices.end() == it)
348 throw ogu::Exception(__FILE__, __LINE__,
349 "Couldn't find tree index in DynamicAtoms::name");
350 return it->second;
351 }
352
353 int
lead(int t) const354 DynamicAtoms::lead(int t) const
355 {
356 const string &nam = name(t);
357 const Tlagmap &lmap = lagmap(nam);
358 auto it = lmap.begin();
359 while (it != lmap.end() && it->second != t)
360 ++it;
361 if (lmap.end() == it)
362 throw ogu::Exception(__FILE__, __LINE__,
363 "Couldn't find the three index in DynamicAtoms::lead");
364 return it->first;
365 }
366
367 void
print() const368 DynamicAtoms::print() const
369 {
370 std::cout << "names:\n";
371 varnames.print();
372 std::cout << "constants:\n";
373 Constants::print();
374 std::cout << "variables:\n";
375 for (const auto &var : vars)
376 {
377 const Tlagmap &lmap = var.second;
378 for (auto itt : lmap)
379 std::cout << "$" << itt.second << ": " << var.first << "(" << itt.first << ")\n";
380 }
381 std::cout << "indices:\n";
382 for (auto indice : indices)
383 std::cout << "t=" << indice.first << u8" ⇒ " << indice.second << "\n";
384 }
385
386 /** Note that the str has been parsed by the lexicographic
387 * analyzer. It can be either a variable or a double. So it is easy to
388 * recognize it by the first character. */
389 bool
is_string_constant(const string & str)390 DynamicAtoms::is_string_constant(const string &str)
391 {
392 return str[0] == '.' || str[0] == '-' || (str[0] >= '0' && str[0] <= '9');
393 }
394
VarOrdering(const VarOrdering & vo,const vector<string> & vnames,const DynamicAtoms & a)395 VarOrdering::VarOrdering(const VarOrdering &vo, const vector<string> &vnames,
396 const DynamicAtoms &a)
397 : n_stat(vo.n_stat), n_pred(vo.n_pred), n_both(vo.n_both), n_forw(vo.n_forw),
398 der_atoms(vo.der_atoms), positions(vo.positions),
399 outer2y(vo.outer2y), y2outer(vo.y2outer), varnames(vnames), atoms(a)
400 {
401 }
402
403 bool
check(int t) const404 VarOrdering::check(int t) const
405 {
406 return positions.find(t) != positions.end();
407 }
408
409 int
get_pos_of(int t) const410 VarOrdering::get_pos_of(int t) const
411 {
412 auto it = positions.find(t);
413 if (it != positions.end())
414 return it->second;
415 else
416 throw ogu::Exception(__FILE__, __LINE__,
417 "Couldn't find the tree index in VarOrdering::get_pos_of");
418 }
419
420 void
do_general(ord_type ordering)421 VarOrdering::do_general(ord_type ordering)
422 {
423 // auxiliary vectors for setting der_atoms and map
424 vector<int> pred_minus;
425 vector<int> both_minus;
426 vector<int> stat;
427 vector<int> pred_pad;
428 vector<int> both_pad;
429 vector<int> forw_pad;
430 vector<int> both_plus;
431 vector<int> forw_plus;
432
433 // auxiliary vectors for setting y2outer and outer2y
434 vector<int> y2o_stat;
435 vector<int> y2o_pred;
436 vector<int> y2o_both;
437 vector<int> y2o_forw;
438
439 for (unsigned int i = 0; i < varnames.size(); i++)
440 {
441 const string &ss = varnames[i];
442 int lead;
443 int lag;
444 atoms.varspan(ss, lead, lag);
445 if (lag == 0 && lead == 0)
446 {
447 stat.push_back(atoms.index(ss, 0));
448 y2o_stat.push_back(i);
449 }
450 else if (lag == -1 && lead < 1)
451 {
452 pred_minus.push_back(atoms.index(ss, -1));
453 pred_pad.push_back(atoms.index(ss, 0));
454 y2o_pred.push_back(i);
455 }
456 else if (lag > -1 && lead == 1)
457 {
458 forw_pad.push_back(atoms.index(ss, 0));
459 forw_plus.push_back(atoms.index(ss, 1));
460 y2o_forw.push_back(i);
461 }
462 else if (lag == -1 && lead == 1)
463 {
464 both_minus.push_back(atoms.index(ss, -1));
465 both_pad.push_back(atoms.index(ss, 0));
466 both_plus.push_back(atoms.index(ss, 1));
467 y2o_both.push_back(i);
468 }
469 else
470 throw ogu::Exception(__FILE__, __LINE__,
471 "A wrong lag/lead of a variable in VarOrdering::do_pbspbfbf");
472 }
473
474 // here we fill ords according to ordering
475 vector<int> *ords[8];
476 if (ordering == pbspbfbf)
477 {
478 ords[0] = &pred_minus;
479 ords[1] = &both_minus;
480 ords[2] = &stat;
481 ords[3] = &pred_pad;
482 ords[4] = &both_pad;
483 ords[5] = &forw_pad;
484 ords[6] = &both_plus;
485 ords[7] = &forw_plus;
486 }
487 else if (ordering == bfspbfpb)
488 {
489 ords[0] = &both_plus;
490 ords[1] = &forw_plus;
491 ords[2] = &stat;
492 ords[3] = &pred_pad;
493 ords[4] = &both_pad;
494 ords[5] = &forw_pad;
495 ords[6] = &pred_minus;
496 ords[7] = &both_minus;
497 }
498 else // BEWARE: when implementing a new ordering, check also the code below setting y2outer
499 throw ogu::Exception(__FILE__, __LINE__,
500 "Ordering not implemented in VarOrdering::do_general");
501
502 // make der_atoms and positions
503 int off = 0;
504 for (auto &ord : ords)
505 for (unsigned int j = 0; j < ord->size(); j++, off++)
506 if ((*ord)[j] != -1)
507 {
508 der_atoms.push_back((*ord)[j]);
509 positions.emplace((*ord)[j], off);
510 }
511
512 // set integer constants
513 n_stat = stat.size();
514 n_pred = pred_pad.size();
515 n_both = both_pad.size();
516 n_forw = forw_pad.size();
517
518 // make y2outer mapping
519 y2outer.insert(y2outer.end(), y2o_stat.begin(), y2o_stat.end());
520 y2outer.insert(y2outer.end(), y2o_pred.begin(), y2o_pred.end());
521 y2outer.insert(y2outer.end(), y2o_both.begin(), y2o_both.end());
522 y2outer.insert(y2outer.end(), y2o_forw.begin(), y2o_forw.end());
523 // make outer2y mapping
524 outer2y.resize(y2outer.size(), -1);
525 for (unsigned int i = 0; i < y2outer.size(); i++)
526 outer2y[y2outer[i]] = i;
527 }
528
529 void
do_increasing_time()530 VarOrdering::do_increasing_time()
531 {
532 // get maxlead and minlag of the variables
533 int mlag, mlead;
534 atoms.varspan(varnames, mlead, mlag);
535 // setup the matrix of tree indices, if there is no occurrence,
536 // the index is set to -1
537 vector<int> ll_init(varnames.size(), -1);
538 vector<vector<int>> tree_ind(mlead-mlag+1, ll_init);
539 for (unsigned int iv = 0; iv < varnames.size(); iv++)
540 {
541 try
542 {
543 const DynamicAtoms::Tlagmap &lmap = atoms.lagmap(varnames[iv]);
544 for (auto it : lmap)
545 {
546 int ll = it.first;
547 int t = it.second;
548 tree_ind[ll-mlag][iv] = t;
549 }
550 }
551 catch (const ogu::Exception &e)
552 {
553 // ignore the error of not found variable in the tree
554 }
555 }
556
557 // setup der_atoms and positions
558 for (int ll = mlag; ll <= mlead; ll++)
559 for (unsigned int iv = 0; iv < varnames.size(); iv++)
560 {
561 int t = tree_ind[ll-mlag][iv];
562 if (t != -1)
563 {
564 der_atoms.push_back(t);
565 int pos = (ll-mlag)*varnames.size() + iv;
566 positions.emplace(t, pos);
567 }
568 }
569
570 // set outer2y and y2outer to identities
571 for (unsigned int iv = 0; iv < varnames.size(); iv++)
572 {
573 outer2y.push_back(iv);
574 y2outer.push_back(iv);
575 }
576
577 // set n_stat, n_pred, n_both, and n_forw
578 for (auto varname : varnames)
579 {
580 int mmlag, mmlead;
581 atoms.varspan(varname, mmlead, mmlag);
582 if (mmlead == 0 && mmlag == 0)
583 n_stat++;
584 else if (mmlead <= 0 && mmlag < 0)
585 n_pred++;
586 else if (mmlead > 0 && mmlag >= 0)
587 n_forw++;
588 else if (mmlead > 0 && mmlag < 0)
589 n_both++;
590 else if (mmlead < mmlag)
591 // variable does not occur in the tree, cound as static
592 n_stat++;
593 else
594 throw ogu::Exception(__FILE__, __LINE__,
595 "A wrong lag/lead of a variable in VarOrdering::do_increasing_time");
596 }
597 }
598
599 void
print() const600 VarOrdering::print() const
601 {
602 std::cout << "nstat=" << n_stat << ", npred=" << n_pred << ", nboth=" << n_both
603 << ", nforw=" << n_forw << "\n"
604 << "der_atoms:\n";
605 for (int der_atom : der_atoms)
606 std::cout << " " << der_atom;
607 std::cout << "\nmap:\n";
608 for (auto position : positions)
609 std::cout << " [" << position.first << u8"→" << position.second << "]";
610 std::cout << "\ny2outer:\n";
611 for (int i : y2outer)
612 std::cout << " " << i;
613 std::cout << "\nouter2y:\n";
614 for (int i : outer2y)
615 std::cout << " " << i;
616 std::cout << "\n";
617 }
618