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