File: atom_substitutions.cc

package info (click to toggle)
dynare 5.3-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 77,852 kB
  • sloc: cpp: 94,481; ansic: 28,551; pascal: 14,532; sh: 5,453; objc: 4,671; yacc: 4,442; makefile: 2,923; lex: 1,612; python: 677; ruby: 469; lisp: 156; xml: 22
file content (275 lines) | stat: -rw-r--r-- 8,378 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
 * Copyright © 2006 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
 * along with Dynare.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "atom_substitutions.hh"
#include "utils/cc/exception.hh"

using namespace ogp;

AtomSubstitutions::AtomSubstitutions(const AtomSubstitutions &as, const FineAtoms &oa,
                                     FineAtoms &na)
  : new2old(as.new2old), old2new(as.old2new), old_atoms(oa), new_atoms(na)
{
}

void
AtomSubstitutions::add_substitution(string newname, string oldname, int tshift)
{
  // insert to new2old map
  new2old.emplace(newname, Tshiftname(oldname, tshift));
  // insert to old2new map
  auto it = old2new.find(oldname);
  if (it != old2new.end())
    it->second.emplace(std::move(newname), -tshift);
  else
    {
      Tshiftnameset snset;
      snset.emplace(std::move(newname), -tshift);
      old2new.emplace(std::move(oldname), snset);
    }

  // put to info
  info.num_substs++;
}

void
AtomSubstitutions::substitutions_finished(VarOrdering::ord_type ot)
{
  // create an external ordering of new_atoms from old_atoms
  const vector<string> &oa_ext = old_atoms.get_allvar();
  vector<string> na_ext;
  for (const auto &oname : oa_ext)
    {
      // add the old name itself
      na_ext.push_back(oname);
      // add all new names derived from the old name
      auto it = old2new.find(oname);
      if (it != old2new.end())
        for (const auto &itt : it->second)
          na_ext.push_back(itt.first);
    }

  // call parsing finished for the new_atoms
  new_atoms.parsing_finished(ot, na_ext);
}

string
AtomSubstitutions::get_new4old(const string &oldname, int tshift) const
{
  auto it = old2new.find(oldname);
  if (it != old2new.end())
    {
      const Tshiftnameset &sset = it->second;
      for (const auto &itt : sset)
        if (itt.second == -tshift)
          return itt.first;
    }
  return "";
}

void
AtomSubstitutions::print() const
{
  std::cout << u8"Atom Substitutions:\nOld ⇒ New:\n";
  for (const auto &it : old2new)
    for (const auto &itt : it.second)
      std::cout << "    " << it.first << u8" ⇒ [" << itt.first << ", " << itt.second << "]\n";

  std::cout << u8"Old ⇐ New:\n";
  for (const auto &it : new2old)
    std::cout << "    [" << it.second.first << ", " << it.second.second << "] ⇐ " << it.first << '\n';
}

void
SAtoms::substituteAllLagsAndLeads(FormulaParser &fp, AtomSubstitutions &as)
{
  string name;

  int mlead, mlag;
  endovarspan(mlead, mlag);

  // substitute all endo lagged more than 1
  while (!(name = findEndoWithLeadInInterval(mlag, -2)).empty())
    makeAuxVariables(name, -1, -2, mlag, fp, as);
  // substitute all endo leaded more than 1
  while (!(name = findEndoWithLeadInInterval(2, mlead)).empty())
    makeAuxVariables(name, 1, 2, mlead, fp, as);

  exovarspan(mlead, mlag);

  // substitute all lagged exo
  while (!(name = findExoWithLeadInInterval(mlag, -1)).empty())
    makeAuxVariables(name, -1, -1, mlag, fp, as);
  // substitute all leaded exo
  while (!(name = findExoWithLeadInInterval(1, mlead)).empty())
    makeAuxVariables(name, 1, 1, mlead, fp, as);

  // notify that substitution have been finished
  as.substitutions_finished(order_type);
}

void
SAtoms::substituteAllLagsAndExo1Leads(FormulaParser &fp, AtomSubstitutions &as)
{
  string name;

  int mlead, mlag;
  endovarspan(mlead, mlag);

  // substitute all endo lagged more than 1
  while (!(name = findEndoWithLeadInInterval(mlag, -2)).empty())
    makeAuxVariables(name, -1, -2, mlag, fp, as);

  exovarspan(mlead, mlag);

  // substitute all lagged exo
  while (!(name = findExoWithLeadInInterval(mlag, -1)).empty())
    makeAuxVariables(name, -1, -1, mlag, fp, as);
  // substitute all leaded exo by 1
  while (!(name = findExoWithLeadInInterval(1, 1)).empty())
    makeAuxVariables(name, 1, 1, 1, fp, as);

  // notify that substitution have been finished
  as.substitutions_finished(order_type);
}

string
SAtoms::findNameWithLeadInInterval(const vector<string> &names,
                                   int ll1, int ll2) const
{
  for (auto name : names)
    {
      auto it = vars.find(name);
      if (it != vars.end())
        {
          const DynamicAtoms::Tlagmap &lmap = it->second;
          for (auto itt : lmap)
            if (itt.first >= ll1 && itt.first <= ll2)
              return name;
        }
    }

  // nothing found
  return "";
}

void
SAtoms::attemptAuxName(const string &str, int ll, string &out) const
{
  char c = (ll >= 0) ? ((ll == 0) ? 'e' : 'p') : 'm';
  string absll = std::to_string(std::abs(ll));
  int iter = 1;
  do
    {
      out = str + '_';
      for (int i = 0; i < iter; i++)
        out += c;
      if (ll != 0)
        out += absll;
      iter++;
    }
  while (varnames.query(out));
}

void
SAtoms::makeAuxVariables(const string &name, int step, int start, int limit_lead,
                         FormulaParser &fp, AtomSubstitutions &as)
{
  if (!(step == 1 || step == -1))
    throw ogu::Exception(__FILE__, __LINE__,
                         "Wrong value of step in SAtoms::makeAuxVariables");
  if (step*start > step*limit_lead)
    throw ogu::Exception(__FILE__, __LINE__,
                         "Wrong value of start in SAtoms::makeAuxVariables");

  // make sure that we do not go further than necessary, this is
  // that the limit lead is not behind maxlead or minlag
  int mlead, mlag;
  varspan(name, mlead, mlag);
  if (step == -1)
    limit_lead = std::max(limit_lead, mlag);
  else
    limit_lead = std::min(limit_lead, mlead);

  // Comment to comments: name="a"; start=-3; step=-1;

  // recover tree index of a previous atom, i.e. set tprev to a tree
  // index of atom "a(-2)"
  int tprev = index(name, start-step);
  if (tprev == -1)
    tprev = fp.add_nulary(name + '(' + std::to_string(start-step) + ')');

  int ll = start;
  do
    {
      // either create atom "a_m2(0)" with tree index taux and add
      // equation "a_m2(0)=a(-2)"
      // or
      // check if "a_m2(0)" has not been already created (with
      // different step), in this case do not add equation "a_m2(0)
      // = a(-2)"
      string newname, newname_str;
      int taux;
      if ((newname = as.get_new4old(name, ll-step)).empty())
        {
          attemptAuxName(name, ll-step, newname_str);
          newname = newname_str;
          register_uniq_endo(newname);
          taux = fp.add_nulary(newname + "(0)");
          // add to substitutions
          as.add_substitution(newname, name, ll-step);

          // add equation "a_m2(0) = a(-2)", this is taux = tprev
          fp.add_formula(fp.add_binary(code_t::MINUS, taux, tprev));
        }
      else
        {
          // example: exogenous EPS and occurrence at both EPS(-1)
          // EPS(+1)
          // first call makeAuxVariables("EPS",1,1,...) will make endo EPS_p0 = EPS
          // second call makeAuxVariables("EPS",-1,-1,...) will use this EPS_p0
          //             to substitute for EPS(-1)
          taux = index(newname, 0);
          if (taux < 0)
            throw ogu::Exception(__FILE__, __LINE__,
                                 "Couldn't find tree index of previously substituted variable");
        }

      // create atom "a_m2(-1)" or turn "a(-3)" if any to "a_m2(-1)"; tree index t
      int t = index(name, ll);
      if (t == -1)
        {
          // no "a(-3)", make t <-> a_m2(-1)
          t = fp.add_nulary(newname + '(' + std::to_string(step) + ')');
        }
      else
        {
          // turn a(-3) to a_m2(-1)
          unassign_variable(name, ll, t);
          assign_variable(newname, step, t);
        }

      // next iteration starts with tprev <-> "a_m2(-1)" (this will be made equal to "a_m3(0)")
      tprev = t;

      ll += step;
    }
  while (step*ll <= step*limit_lead);
}