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