aGrUM  0.14.2
operatorPattern4MultiDimArray.h
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005 by Pierre-Henri WUILLEMIN et Christophe GONZALES *
3  * {prenom.nom}_at_lip6.fr *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the *
17  * Free Software Foundation, Inc., *
18  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19  ***************************************************************************/
27 // check if we allowed these patterns to be used
28 #ifndef GUM_OPERATOR_PATTERN_ALLOWED
29 
30 // #warning To use operatorPattern4MultiDimArray.h, you must define
31 // GUM_OPERATOR_PATTERN_ALLOWED
32 
33 #else
34 
35 namespace gum {
36 
37  // a specialized function for combining two multiDimArrays
38 
39 # ifdef GUM_MULTI_DIM_OPERATOR_NAME
40 # define GUM_MULTI_DIM_OPERATOR_TYPE T
41  template < typename T >
42  MultiDimArray< T >* GUM_MULTI_DIM_OPERATOR_NAME(const MultiDimArray< T >* t1,
43  const MultiDimArray< T >* t2)
44 # endif
45 
46  // clang-format off
47 
48 #ifdef GUM_MULTI_DIM_OPERATOR_POINTER_NAME
49 #define GUM_MULTI_DIM_OPERATOR_TYPE T *
50  template <typename T>
51  MultiDimArray<T*>* GUM_MULTI_DIM_OPERATOR_POINTER_NAME(
52  const MultiDimArray<T*>* t1, const MultiDimArray<T*>* t2 )
53 #endif
54 
55 #ifdef GUM_MULTI_DIM_OPERATOR_NAME_F
56 #define GUM_MULTI_DIM_OPERATOR_TYPE T
57  template <typename T>
58  MultiDimArray<T>* GUM_MULTI_DIM_OPERATOR_NAME_F(
59  const MultiDimArray<T>* t1,
60  const MultiDimArray<T>* t2,
61  const T ( *f )( const T&, const T& ) )
62 #endif
63 
64 #ifdef GUM_MULTI_DIM_OPERATOR_POINTER_NAME_F
65 #define GUM_MULTI_DIM_OPERATOR_TYPE T *
66  template <typename T>
67  MultiDimArray<T*>* GUM_MULTI_DIM_OPERATOR_POINTER_NAME_F(
68  const MultiDimArray<T*>* t1,
69  const MultiDimArray<T*>* t2,
70  const T* ( *f )( const T*, const T* ) )
71 #endif
72 
73 #ifdef GUM_MULTI_DIM_OPERATOR_IMPL2ARRAY_NAME
74 #define GUM_MULTI_DIM_OPERATOR_TYPE T
75  template <typename T>
76  MultiDimImplementation<T>* GUM_MULTI_DIM_OPERATOR_IMPL2ARRAY_NAME(
77  const MultiDimImplementation<T>* tt1,
78  const MultiDimImplementation<T>* tt2 )
79 #endif
80 
81 #ifdef GUM_MULTI_DIM_OPERATOR_POINTER_IMPL2ARRAY_NAME
82 #define GUM_MULTI_DIM_OPERATOR_TYPE T *
83  template <typename T>
84  MultiDimImplementation<T*>*
85  GUM_MULTI_DIM_OPERATOR_POINTER_IMPL2ARRAY_NAME(
86  const MultiDimImplementation<T*>* tt1,
87  const MultiDimImplementation<T*>* tt2 )
88 #endif
89 
90  // clang-format on
91 
92  {
93 
94 # ifdef GUM_MULTI_DIM_OPERATOR_IMPL2ARRAY_NAME
95  const MultiDimArray< T >* t1 =
96  reinterpret_cast< const MultiDimArray< T >* >(tt1);
97  const MultiDimArray< T >* t2 =
98  reinterpret_cast< const MultiDimArray< T >* >(tt2);
99 # endif
100 
101 # ifdef GUM_MULTI_DIM_OPERATOR_POINTER_IMPL2ARRAY_NAME
102  const MultiDimArray< T* >* t1 =
103  reinterpret_cast< const MultiDimArray< T* >* >(tt1);
104  const MultiDimArray< T* >* t2 =
105  reinterpret_cast< const MultiDimArray< T* >* >(tt2);
106 # endif
107 
108  // get the variables of the tables
109  const Sequence< const DiscreteVariable* >& t1_vars = t1->variablesSequence();
110  const Sequence< const DiscreteVariable* >& t2_vars = t2->variablesSequence();
111 
112  // get the domain size of the tables' variables
113  HashTable< const DiscreteVariable*, Idx > t1_offsets;
114  {
115  Idx current_offset = 1;
116 
117  for (const auto var : t1_vars) {
118  t1_offsets.insert(var, current_offset);
119  current_offset *= var->domainSize();
120  }
121  }
122  HashTable< const DiscreteVariable*, Idx > t2_offsets;
123  {
124  Idx current_offset = 1;
125 
126  for (const auto var : t2_vars) {
127  t2_offsets.insert(var, current_offset);
128  current_offset *= var->domainSize();
129  }
130  }
131 
132  // we divide the variables of t1 and t2 into 3 separate sets: those that
133  // belong only to t1 (variables t1_alone_xxx), those that belong only to t2
134  // (variables t2_alone_xxx) and those that belong to both tables (variables
135  // t1_and_t2_xxx). For each set, we define how an increment of a given
136  // variable of the set changes the offset in the corresponding table
137  // (variable txxx_offset) and what is the domain size of the variable
138  // (txxx_domain). In addition, we compute the domain size of the Cartesian
139  // product of the variables in each of the 3 sets. Given these data, we
140  // will be able to parse both t1, t2 and the result table t1+t2 without
141  // resorting to instantiations.
142  std::vector< Idx > t1_alone_offset;
143  std::vector< Idx > t1_alone_domain;
144  Idx t1_alone_domain_size = 1;
145 
146  std::vector< Idx > t2_alone_offset;
147  std::vector< Idx > t2_alone_domain;
148  Idx t2_alone_domain_size = 1;
149 
150  std::vector< Idx > t1_and_t2_1_offset;
151  std::vector< Idx > t1_and_t2_2_offset;
152  std::vector< Idx > t1_and_t2_domain;
153  std::vector< const DiscreteVariable* > t1_and_t2_vars;
154  Idx t1_and_t2_domain_size = 1;
155 
156  {
157  for (const auto var : t1_vars)
158  if (t2_vars.exists(var)) {
159  t1_and_t2_domain.push_back(var->domainSize());
160  t1_and_t2_1_offset.push_back(t1_offsets[var]);
161  t1_and_t2_2_offset.push_back(t2_offsets[var]);
162  t1_and_t2_vars.push_back(var);
163  t1_and_t2_domain_size *= var->domainSize();
164  } else {
165  t1_alone_domain.push_back(var->domainSize());
166  t1_alone_offset.push_back(t1_offsets[var]);
167  t1_alone_domain_size *= var->domainSize();
168  }
169 
170  for (const auto var : t2_vars)
171  if (!t1_vars.exists(var)) {
172  t2_alone_domain.push_back(var->domainSize());
173  t2_alone_offset.push_back(t2_offsets[var]);
174  t2_alone_domain_size *= var->domainSize();
175  }
176  }
177 
178  // a Boolean indicating whether the variables that t1 and t2 have in common
179  // are the first variables and are in the same order. When this is true,
180  // computations can be performed faster
181  bool t1_and_t2_begin_vars = false;
182 
183  if (t1_and_t2_vars.size()) {
184  unsigned int nb_t1_t2_vars = 0;
185 
186  for (auto iter = t1_vars.begin(); nb_t1_t2_vars != t1_and_t2_vars.size();
187  ++iter, ++nb_t1_t2_vars)
188  if (*iter != t1_and_t2_vars[nb_t1_t2_vars]) break;
189 
190  if (nb_t1_t2_vars == t1_and_t2_vars.size()) {
191  nb_t1_t2_vars = 0;
192 
193  for (auto iter = t2_vars.begin(); nb_t1_t2_vars != t1_and_t2_vars.size();
194  ++iter, ++nb_t1_t2_vars)
195  if (*iter != t1_and_t2_vars[nb_t1_t2_vars]) break;
196 
197  if (nb_t1_t2_vars == t1_and_t2_vars.size()) {
198  t1_and_t2_begin_vars = true;
199  }
200  }
201  }
202 
203  // when we will parse t1 and t2 to fill the result table t1+t2, we will use
204  // variables txxx_value : at the beginning they are initialized to the
205  // domain size of the variables (which are, themselves initialized to 0).
206  // Each time we increment a variable (that is, we increase the offset of
207  // the table by txxx_offset), its corresponding txxx_value is decreased by
208  // 1. When the latter is equal to 0, this means that the variable itself
209  // should be reinitialized to 0 as well and that the next variable of the
210  // table should be increased (that is, this is similar to increasing 9 to
211  // 10). As such the offset of txxx should be decreased by txxx_offset * the
212  // domain size of the variable. This quantity is precisely what is stored
213  // into variables txxx_down.
214  std::vector< Idx > t1_and_t2_value = t1_and_t2_domain;
215  std::vector< Idx > t1_and_t2_1_down = t1_and_t2_1_offset;
216  std::vector< Idx > t1_and_t2_2_down = t1_and_t2_2_offset;
217 
218  for (unsigned int i = 0; i < t1_and_t2_domain.size(); ++i) {
219  t1_and_t2_1_down[i] *= (t1_and_t2_domain[i] - 1);
220  t1_and_t2_2_down[i] *= (t1_and_t2_domain[i] - 1);
221  }
222 
223  std::vector< Idx > t1_alone_value = t1_alone_domain;
224  std::vector< Idx > t1_alone_down = t1_alone_offset;
225 
226  for (unsigned int i = 0; i < t1_alone_domain.size(); ++i) {
227  t1_alone_down[i] *= (t1_alone_domain[i] - 1);
228  }
229 
230  std::vector< Idx > t2_alone_value = t2_alone_domain;
231  std::vector< Idx > t2_alone_down = t2_alone_offset;
232 
233  for (unsigned int i = 0; i < t2_alone_domain.size(); ++i) {
234  t2_alone_down[i] *= (t2_alone_domain[i] - 1);
235  }
236 
237  // create a table "result" containing all the variables: the first
238  // variables are those that belong to both t1 and t2. The next variables
239  // are those that belong to t2 but not to t1. Finally, the last variables
240  // are those that belong to t1 but not t2. This order will be used in the
241  // next for loops.
242  MultiDimArray< GUM_MULTI_DIM_OPERATOR_TYPE >* result =
243  new MultiDimArray< GUM_MULTI_DIM_OPERATOR_TYPE >;
244  result->beginMultipleChanges();
245 
246  for (const auto var : t1_vars)
247  if (t2_vars.exists(var)) *result << *var;
248 
249  for (const auto var : t2_vars)
250  if (!t1_vars.exists(var)) *result << *var;
251 
252  for (const auto var : t1_vars)
253  if (!t2_vars.exists(var)) *result << *var;
254 
255  result->endMultipleChanges();
256 
257  // here we fill result. The idea is to use 3 loops. The innermost loop
258  // corresponds to the variables that belongs both to t1 and t2. The middle
259  // loop to the variables that belong to t2 but not to t1. Finally, the
260  // outer loop corresponds to the variables that belong to t1 but not t2.
261  GUM_MULTI_DIM_OPERATOR_TYPE* pt1 =
262  const_cast< GUM_MULTI_DIM_OPERATOR_TYPE* >(&(t1->unsafeGet(0)));
263  GUM_MULTI_DIM_OPERATOR_TYPE* pt2 =
264  const_cast< GUM_MULTI_DIM_OPERATOR_TYPE* >(&(t2->unsafeGet(0)));
265  GUM_MULTI_DIM_OPERATOR_TYPE* pres =
266  const_cast< GUM_MULTI_DIM_OPERATOR_TYPE* >(&(result->unsafeGet(0)));
267  GUM_MULTI_DIM_OPERATOR_TYPE* pt2_deb = pt2;
268  GUM_MULTI_DIM_OPERATOR_TYPE* pt1_alone_begin;
269 
270  // test if all the variables in common in t1 and t2 are the first variables
271  // and are in the same order. In this case, we can speed-up the
272  // incrementation processes
273  if (t1_and_t2_begin_vars) {
274  for (Idx i = 0; i < t1_alone_domain_size; ++i) {
275  pt2 = pt2_deb;
276  pt1_alone_begin = pt1;
277 
278  for (Idx j = 0; j < t2_alone_domain_size; ++j) {
279  pt1 = pt1_alone_begin;
280 
281  for (Idx z = 0; z < t1_and_t2_domain_size; ++z) {
282  *pres = GUM_MULTI_DIM_OPERATOR(*pt1, *pt2);
283  ++pres;
284 
285  // update the offset of both t1 and t2
286  ++pt1;
287  ++pt2;
288  }
289  }
290  }
291  } else {
292  Idx t1_offset = 0;
293  Idx t2_offset = 0;
294  Idx t1_alone_begin_offset = 0;
295 
296  for (Idx i = 0; i < t1_alone_domain_size; ++i) {
297  t2_offset = 0;
298  t1_alone_begin_offset = t1_offset;
299 
300  for (Idx j = 0; j < t2_alone_domain_size; ++j) {
301  t1_offset = t1_alone_begin_offset;
302 
303  for (Idx z = 0; z < t1_and_t2_domain_size; ++z) {
304  *pres = GUM_MULTI_DIM_OPERATOR(pt1[t1_offset], pt2[t2_offset]);
305  ++pres;
306 
307  // update the offset of both t1 and t2
308  for (unsigned int k = 0; k < t1_and_t2_value.size(); ++k) {
309  if (--t1_and_t2_value[k]) {
310  t1_offset += t1_and_t2_1_offset[k];
311  t2_offset += t1_and_t2_2_offset[k];
312  break;
313  }
314 
315  t1_and_t2_value[k] = t1_and_t2_domain[k];
316  t1_offset -= t1_and_t2_1_down[k];
317  t2_offset -= t1_and_t2_2_down[k];
318  }
319  }
320 
321  // update the offset of t2 alone
322  for (unsigned int k = 0; k < t2_alone_value.size(); ++k) {
323  if (--t2_alone_value[k]) {
324  t2_offset += t2_alone_offset[k];
325  break;
326  }
327 
328  t2_alone_value[k] = t2_alone_domain[k];
329  t2_offset -= t2_alone_down[k];
330  }
331  }
332 
333  // update the offset of t1 alone
334  for (unsigned int k = 0; k < t1_alone_value.size(); ++k) {
335  if (--t1_alone_value[k]) {
336  t1_offset += t1_alone_offset[k];
337  break;
338  }
339 
340  t1_alone_value[k] = t1_alone_domain[k];
341  t1_offset -= t1_alone_down[k];
342  }
343  }
344  }
345 
346  return result;
347  }
348 
349 # undef GUM_MULTI_DIM_OPERATOR_TYPE
350 } // namespace gum
351 
352 #endif /* GUM_OPERATOR_PATTERN_ALLOWED */
gum is the global namespace for all aGrUM entities
Definition: agrum.h:25