1 /*
2  * Copyright © 2004 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 "equivalence.hh"
22 #include "permutation.hh"
23 #include "tl_exception.hh"
24 
25 #include <iostream>
26 
27 int
operator [](int i) const28 OrdSequence::operator[](int i) const
29 {
30   TL_RAISE_IF((i < 0 || i >= length()),
31               "Index out of range in OrdSequence::operator[]");
32   return data[i];
33 }
34 
35 /* Here we implement the ordering. It can be changed, or various
36    orderings can be used for different problem sizes. We order them
37    according to the average, and then according to the first item. */
38 
39 bool
operator <(const OrdSequence & s) const40 OrdSequence::operator<(const OrdSequence &s) const
41 {
42   double ta = average();
43   double sa = s.average();
44   return (ta < sa || ((ta == sa) && (operator[](0) > s[0])));
45 }
46 
47 bool
operator ==(const OrdSequence & s) const48 OrdSequence::operator==(const OrdSequence &s) const
49 {
50   if (length() != s.length())
51     return false;
52 
53   int i = 0;
54   while (i < length() && operator[](i) == s[i])
55     i++;
56 
57   return (i == length());
58 }
59 
60 /* The first add() adds a given integer to the class, the second
61    iterates through a given sequence and adds everything found in the
62    given class. */
63 
64 void
add(int i)65 OrdSequence::add(int i)
66 {
67   auto vit = data.begin();
68   while (vit != data.end() && *vit < i)
69     ++vit;
70   if (vit != data.end() && *vit == i)
71     return;
72   data.insert(vit, i);
73 }
74 
75 void
add(const OrdSequence & s)76 OrdSequence::add(const OrdSequence &s)
77 {
78   auto vit = s.data.begin();
79   while (vit != s.data.end())
80     {
81       add(*vit);
82       ++vit;
83     }
84 }
85 
86 /* Answers true if a given number is in the class. */
87 bool
has(int i) const88 OrdSequence::has(int i) const
89 {
90   auto vit = data.begin();
91   while (vit != data.end())
92     {
93       if (*vit == i)
94         return true;
95       ++vit;
96     }
97   return false;
98 }
99 
100 /* Return an average of the class. */
101 double
average() const102 OrdSequence::average() const
103 {
104   double res = 0;
105   for (int i : data)
106     res += i;
107   TL_RAISE_IF(data.size() == 0,
108               "Attempt to take average of empty class in OrdSequence::average");
109   return res/data.size();
110 }
111 
112 /* Debug print. */
113 void
print(const std::string & prefix) const114 OrdSequence::print(const std::string &prefix) const
115 {
116   std::cout << prefix;
117   for (int i : data)
118     std::cout << i << ' ';
119   std::cout << '\n';
120 }
121 
Equivalence(int num)122 Equivalence::Equivalence(int num)
123   : n(num)
124 {
125   for (int i = 0; i < num; i++)
126     {
127       OrdSequence s;
128       s.add(i);
129       classes.push_back(s);
130     }
131 }
132 
Equivalence(int num,const std::string & dummy)133 Equivalence::Equivalence(int num, const std::string &dummy)
134   : n(num)
135 {
136   OrdSequence s;
137   for (int i = 0; i < num; i++)
138     s.add(i);
139   classes.push_back(s);
140 }
141 
142 /* Copy constructor that also glues a given couple. */
143 
Equivalence(const Equivalence & e,int i1,int i2)144 Equivalence::Equivalence(const Equivalence &e, int i1, int i2)
145   : n(e.n),
146     classes(e.classes)
147 {
148   auto s1 = find(i1);
149   auto s2 = find(i2);
150   if (s1 != s2)
151     {
152       OrdSequence ns(*s1);
153       ns.add(*s2);
154       classes.erase(s1);
155       classes.erase(s2);
156       insert(ns);
157     }
158 }
159 
160 bool
operator ==(const Equivalence & e) const161 Equivalence::operator==(const Equivalence &e) const
162 {
163   if (!std::operator==(classes, e.classes))
164     return false;
165 
166   if (n != e.n)
167     return false;
168 
169   return true;
170 }
171 
172 /* Return an iterator pointing to a class having a given integer. */
173 
174 Equivalence::const_seqit
findHaving(int i) const175 Equivalence::findHaving(int i) const
176 {
177   auto si = classes.begin();
178   while (si != classes.end())
179     {
180       if (si->has(i))
181         return si;
182       ++si;
183     }
184   TL_RAISE_IF(si == classes.end(),
185               "Couldn't find equivalence class in Equivalence::findHaving");
186   return si;
187 }
188 
189 Equivalence::seqit
findHaving(int i)190 Equivalence::findHaving(int i)
191 {
192   auto si = classes.begin();
193   while (si != classes.end())
194     {
195       if (si->has(i))
196         return si;
197       ++si;
198     }
199   TL_RAISE_IF(si == classes.end(),
200               "Couldn't find equivalence class in Equivalence::findHaving");
201   return si;
202 }
203 
204 /* Find j-th class for a given j. */
205 
206 Equivalence::const_seqit
find(int j) const207 Equivalence::find(int j) const
208 {
209   auto si = classes.begin();
210   int i = 0;
211   while (si != classes.end() && i < j)
212     {
213       ++si;
214       i++;
215     }
216   TL_RAISE_IF(si == classes.end(),
217               "Couldn't find equivalence class in Equivalence::find");
218   return si;
219 }
220 
221 Equivalence::seqit
find(int j)222 Equivalence::find(int j)
223 {
224   auto si = classes.begin();
225   int i = 0;
226   while (si != classes.end() && i < j)
227     {
228       ++si;
229       i++;
230     }
231   TL_RAISE_IF(si == classes.end(),
232               "Couldn't find equivalence class in Equivalence::find");
233   return si;
234 }
235 
236 /* Insert a new class yielding the ordering. */
237 void
insert(const OrdSequence & s)238 Equivalence::insert(const OrdSequence &s)
239 {
240   auto si = classes.begin();
241   while (si != classes.end() && *si < s)
242     ++si;
243   classes.insert(si, s);
244 }
245 
246 /* Trace the equivalence into the integer sequence. The classes are in
247    some order (described earlier), and items within classes are ordered,
248    so this implies, that the data can be linearized. This method
249    “prints” them to the sequence. We allow for tracing only a given
250    number of classes from the beginning. */
251 
252 void
trace(IntSequence & out,int num) const253 Equivalence::trace(IntSequence &out, int num) const
254 {
255   int i = 0;
256   int nc = 0;
257   for (auto it = begin(); it != end() && nc < num; ++it, ++nc)
258     for (int j = 0; j < it->length(); j++, i++)
259       {
260         TL_RAISE_IF(i >= out.size(),
261                     "Wrong size of output sequence in Equivalence::trace");
262         out[i] = (*it)[j];
263       }
264 }
265 
266 void
trace(IntSequence & out,const Permutation & per) const267 Equivalence::trace(IntSequence &out, const Permutation &per) const
268 {
269   TL_RAISE_IF(out.size() != n,
270               "Wrong size of output sequence in Equivalence::trace");
271   TL_RAISE_IF(per.size() != numClasses(),
272               "Wrong permutation for permuted Equivalence::trace");
273   int i = 0;
274   for (int iclass = 0; iclass < numClasses(); iclass++)
275     {
276       auto itper = find(per.getMap()[iclass]);
277       for (int j = 0; j < itper->length(); j++, i++)
278         out[i] = (*itper)[j];
279     }
280 }
281 
282 /* Debug print. */
283 void
print(const std::string & prefix) const284 Equivalence::print(const std::string &prefix) const
285 {
286   int i = 0;
287   for (auto it = classes.begin();
288        it != classes.end();
289        ++it, i++)
290     {
291       std::cout << prefix << "class " << i << ": ";
292       it->print("");
293     }
294 }
295 
296 /* Here we construct a set of all equivalences over n-element set. The
297    construction proceeds as follows. We maintain a list of added equivalences.
298    At each iteration we pop front of the list, try to add all parents of the
299    popped equivalence. This action adds new equivalences to the object and also
300    to the added list. We finish the iterations when the added list is empty.
301 
302    In the beginning we start with { {0}, {1}, …, {n-1} }. Adding of parents is
303    an action which for a given equivalence tries to glue all possible couples
304    and checks whether a new equivalence is already in the equivalence set. This
305    is not effective, but we will do the construction only ones.
306 
307    In this way we breath-first search a lattice of all equivalences. Note
308    that the lattice is modular, that is why the result of a construction
309    is a list with a property that between two equivalences with the same
310    number of classes there are only equivalences with that number of
311    classes. Obviously, the list is decreasing in a number of classes
312    (since it is constructed by gluing attempts). */
313 
EquivalenceSet(int num)314 EquivalenceSet::EquivalenceSet(int num)
315   : n(num)
316 {
317   std::list<Equivalence> added;
318   Equivalence first(n);
319   equis.push_back(first);
320   addParents(first, added);
321   while (!added.empty())
322     {
323       addParents(added.front(), added);
324       added.pop_front();
325     }
326   if (n > 1)
327     equis.emplace_back(n, "");
328 }
329 
330 /* This method is used in addParents() and returns true if the object
331    already has that equivalence. We trace list of equivalences in reverse
332    order since equivalences are ordered in the list from the most
333    primitive (nothing equivalent) to maximal (all is equivalent). Since
334    we will have much more results of has() method as true, and
335    operator==() between equivalences is quick if number of classes
336    differ, and in time we will compare with equivalences with less
337    classes, then it is more efficient to trace the equivalences from less
338    classes to more classes. hence the reverse order. */
339 
340 bool
has(const Equivalence & e) const341 EquivalenceSet::has(const Equivalence &e) const
342 {
343   auto rit = equis.rbegin();
344   while (rit != equis.rend() && *rit != e)
345     ++rit;
346   if (rit != equis.rend())
347     return true;
348   return false;
349 }
350 
351 /* Responsibility of this methods is to try to glue all possible
352    couples within a given equivalence and add those which are not in the
353    list yet. These are added also to the ‘added’ list.
354 
355    If number of classes is 2 or 1, we exit, because there is nothing to
356    be added. */
357 
358 void
addParents(const Equivalence & e,std::list<Equivalence> & added)359 EquivalenceSet::addParents(const Equivalence &e,
360                            std::list<Equivalence> &added)
361 {
362   if (e.numClasses() == 2 || e.numClasses() == 1)
363     return;
364 
365   for (int i1 = 0; i1 < e.numClasses(); i1++)
366     for (int i2 = i1+1; i2 < e.numClasses(); i2++)
367       {
368         Equivalence ns(e, i1, i2);
369         if (!has(ns))
370           {
371             added.push_back(ns);
372             equis.push_back(std::move(ns));
373           }
374       }
375 }
376 
377 /* Debug print. */
378 void
print(const std::string & prefix) const379 EquivalenceSet::print(const std::string &prefix) const
380 {
381   int i = 0;
382   for (auto it = equis.begin();
383        it != equis.end();
384        ++it, i++)
385     {
386       std::cout << prefix << "equivalence " << i << ":(classes "
387                 << it->numClasses() << ")\n";
388       it->print(prefix + "    ");
389     }
390 }
391 
392 /* Construct the bundle. nmax is a maximum size of underlying set. */
EquivalenceBundle(int nmax)393 EquivalenceBundle::EquivalenceBundle(int nmax)
394 {
395   nmax = std::max(nmax, 1);
396   generateUpTo(nmax);
397 }
398 
399 /* Remember, that the first item is EquivalenceSet(1). */
400 const EquivalenceSet &
get(int n) const401 EquivalenceBundle::get(int n) const
402 {
403   TL_RAISE_IF(n > static_cast<int>(bundle.size()) || n < 1,
404               "Equivalence set not found in EquivalenceBundle::get");
405   return bundle[n-1];
406 }
407 
408 /* Get ‘curmax’ which is a maximum size in the bundle, and generate for
409    all sizes from curmax+1 up to nmax. */
410 
411 void
generateUpTo(int nmax)412 EquivalenceBundle::generateUpTo(int nmax)
413 {
414   int curmax = bundle.size();
415   for (int i = curmax+1; i <= nmax; i++)
416     bundle.emplace_back(i);
417 }
418