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 // Symmetry.
22 
23 /* Symmetry is an abstraction for a term of the form y³u². It manages only
24    indices, not the variable names. So if one uses this abstraction, it must
25    be kept in mind that y is the first and u is the second.
26 
27    In fact, the symmetry is a special case of equivalence, but its
28    implementation is much simpler. We do not need an abstraction for the
29    term “yyuyu” but due to Green theorem we can have term y³u². That
30    is why the equivalence is too general for our purposes.
31 
32    One of a main purposes of the tensor library is to calculate something like:
33 
34 35     [B_y²u³]_α₁α₂β₁β₂β₃ = [f_zˡ]_γ₁…γₗ    ∑     ∏  [g_{s^|cₘ|}]_cₘ(α,β)^γₘ
36                                        c∈ℳₗ,₅ ᵐ⁼¹
37 
38    We must be able to calculate a symmetry induced by symmetry y²u³ and by an
39    equivalence class from equivalence c. For equivalence class {0,4} the
40    induced symmetry is “yu”, since we pick first and fifth variable from y²u³.
41    For a given outer symmetry, the class InducedSymmetries does this for all
42    classes of a given equivalence.
43 
44    We need also to cycle through all possible symmetries yielding the
45    given dimension. For this purpose we define classes SymmetrySet and
46    symiterator.
47 
48    The symmetry is implemented as IntSequence, in fact, it inherits
49    from it. */
50 
51 #ifndef SYMMETRY_H
52 #define SYMMETRY_H
53 
54 #include "equivalence.hh"
55 #include "int_sequence.hh"
56 
57 #include <list>
58 #include <vector>
59 #include <initializer_list>
60 #include <utility>
61 #include <memory>
62 
63 /* Clear. The method isFull() returns true if and only if the symmetry
64    allows for any permutation of indices.
65 
66    WARNING: Symmetry(n) and Symmetry{n} are not the same. The former
67    initializes a symmetry of n elements, while the latter is a full symmetry of
68    order n. This is similar to the behaviour of std::vector. */
69 
70 class Symmetry : public IntSequence
71 {
72 public:
73   // Constructor allocating a given length of (zero-initialized) data
Symmetry(int len)74   explicit Symmetry(int len)
75     : IntSequence(len, 0)
76   {
77   }
78   /* Constructor using an initializer list, that gives the contents of the
79      Symmetry. Typically used for symmetries of the form yⁿ, yⁿuᵐ, yⁿuᵐσᵏ */
Symmetry(std::initializer_list<int> init)80   Symmetry(std::initializer_list<int> init)
81     : IntSequence(std::move(init))
82   {
83   }
84   // Constructor of implied symmetry for a symmetry and an equivalence class
85   Symmetry(const Symmetry &s, const OrdSequence &cl);
86   /* Subsymmetry, which takes the given length of symmetry from the end (shares
87      data pointer) */
Symmetry(Symmetry & s,int len)88   Symmetry(Symmetry &s, int len)
89     : IntSequence(s, s.size()-len, s.size())
90   {
91   }
92 
93   int
num() const94   num() const
95   {
96     return size();
97   }
98   int
dimen() const99   dimen() const
100   {
101     return sum();
102   }
103   int findClass(int i) const;
104   bool isFull() const;
105 };
106 
107 /* This is an iterator that iterates over all symmetries of given length and
108    dimension (see the SymmetrySet class for details).
109 
110    The beginning iterator is (0, …, 0, dim).
111    Increasing it gives (0, … , 1, dim−1)
112    The just-before-end iterator is (dim, 0, …, 0)
113    The past-the-end iterator is (dim+1, 0, …, 0)
114 
115    The constructor creates the iterator which starts from the given symmetry
116    symmetry (beginning). */
117 
118 class symiterator
119 {
120   const int dim;
121   Symmetry run;
122 public:
123   symiterator(int dim_arg, Symmetry run_arg);
124   ~symiterator() = default;
125   symiterator &operator++();
126   const Symmetry &
operator *() const127   operator*() const
128   {
129     return run;
130   }
131   bool
operator =(const symiterator & it)132   operator=(const symiterator &it)
133   {
134     return dim == it.dim && run == it.run;
135   }
136   bool
operator !=(const symiterator & it)137   operator!=(const symiterator &it)
138   {
139     return !operator=(it);
140   }
141 };
142 
143 /* The class SymmetrySet defines a set of symmetries of the given length
144    having given dimension (i.e. it represents all the lists of integers of
145    length ‘len’ and of sum equal to ‘dim’). It does not store all the
146    symmetries, it is just a convenience class for iteration.
147 
148    The typical usage of the abstractions for SymmetrySet and
149    symiterator is as follows:
150 
151      for (auto &si : SymmetrySet(6, 4))
152 
153    It goes through all symmetries of lenght 4 having dimension 6. One can use
154    ‘si’ as the symmetry in the body. */
155 
156 class SymmetrySet
157 {
158 public:
159   const int len;
160   const int dim;
SymmetrySet(int dim_arg,int len_arg)161   SymmetrySet(int dim_arg, int len_arg)
162     : len(len_arg), dim(dim_arg)
163   {
164   }
165   symiterator
begin() const166   begin() const
167   {
168     Symmetry run(len);
169     run[len-1] = dim;
170     return { dim, run };
171   }
172   symiterator
end() const173   end() const
174   {
175     Symmetry run(len);
176     run[0] = dim+1;
177     return { dim, run };
178   }
179 };
180 
181 /* This simple abstraction just constructs a vector of induced
182    symmetries from the given equivalence and outer symmetry. A
183    permutation might optionally permute the classes of the equivalence. */
184 
185 class InducedSymmetries : public std::vector<Symmetry>
186 {
187 public:
188   InducedSymmetries(const Equivalence &e, const Symmetry &s);
189   InducedSymmetries(const Equivalence &e, const Permutation &p, const Symmetry &s);
190   void print() const;
191 };
192 
193 #endif
194