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