aGrUM  0.16.0
projectionPattern4MultiDimArray.h
Go to the documentation of this file.
1 
31 // check if we allowed these patterns to be used
32 #ifndef GUM_PROJECTION_PATTERN_ALLOWED
33 
34 // #warning To use projectionPattern, you must define
35 // GUM_PROJECTION_PATTERN_ALLOWED
36 
37 #else
38 namespace gum {
39 
40  // a specialized function for projecting a multiDimArray over a subset of its
41  // vars
42 
43 # ifdef GUM_MULTI_DIM_PROJECTION_NAME
44 # define GUM_MULTI_DIM_PROJECTION_TYPE GUM_SCALAR
45  template < typename GUM_SCALAR >
46  MultiDimArray< GUM_SCALAR >* GUM_MULTI_DIM_PROJECTION_NAME(
47  const MultiDimArray< GUM_SCALAR >* table,
48  const Set< const DiscreteVariable* >& del_vars)
49 # endif
50 
51  // clang-format off
52 
53 #ifdef GUM_MULTI_DIM_PROJECTION_POINTER_NAME
54 #define GUM_MULTI_DIM_PROJECTION_TYPE GUM_SCALAR *
55 #define GUM_MULTI_DIM_PROJECTION_POINTER
56  template <typename GUM_SCALAR>
57  MultiDimArray<GUM_SCALAR*>* GUM_MULTI_DIM_PROJECTION_POINTER_NAME(
58  const MultiDimArray<GUM_SCALAR*>* table,
59  const Set<const DiscreteVariable*>& del_vars )
60 #endif
61 
62 #ifdef GUM_MULTI_DIM_PROJECTION_NAME_F
63 #define GUM_MULTI_DIM_PROJECTION_TYPE GUM_SCALAR
64  template <typename GUM_SCALAR>
65  MultiDimArray<GUM_SCALAR>* GUM_MULTI_DIM_PROJECTION_NAME_F(
66  const MultiDimArray<GUM_SCALAR>* table,
67  const Set<const DiscreteVariable*>& del_vars,
68  GUM_SCALAR ( *f )( const GUM_SCALAR&, const GUM_SCALAR& ) )
69 #endif
70 
71 #ifdef GUM_MULTI_DIM_PROJECTION_POINTER_NAME_F
72 #define GUM_MULTI_DIM_PROJECTION_TYPE GUM_SCALAR *
73 #define GUM_MULTI_DIM_PROJECTION_POINTER
74  template <typename GUM_SCALAR>
75  MultiDimArray<GUM_SCALAR*>* GUM_MULTI_DIM_PROJECTION_POINTER_NAME_F(
76  const MultiDimArray<GUM_SCALAR*>* table,
77  const Set<const DiscreteVariable*>& del_vars,
78  GUM_SCALAR* ( *f )( const GUM_SCALAR const*,
79  const GUM_SCALAR const* ) )
80 #endif
81 
82 #ifdef GUM_MULTI_DIM_PROJECTION_IMPL2ARRAY_NAME
83 #define GUM_MULTI_DIM_PROJECTION_TYPE GUM_SCALAR
84  template <typename GUM_SCALAR>
85  MultiDimImplementation<GUM_SCALAR>*
86  GUM_MULTI_DIM_PROJECTION_IMPL2ARRAY_NAME(
87  const MultiDimImplementation<GUM_SCALAR>* ttable,
88  const Set<const DiscreteVariable*>& del_vars )
89 #endif
90 
91 #ifdef GUM_MULTI_DIM_PROJECTION_POINTER_IMPL2ARRAY_NAME
92 #define GUM_MULTI_DIM_PROJECTION_TYPE GUM_SCALAR *
93 #define GUM_MULTI_DIM_PROJECTION_POINTER
94  template <typename GUM_SCALAR>
95  MultiDimImplementation<GUM_SCALAR*>*
96  GUM_MULTI_DIM_PROJECTION_POINTER_IMPL2ARRAY_NAME(
97  const MultiDimImplementation<GUM_SCALAR*>* ttable,
98  const Set<const DiscreteVariable*>& del_vars )
99 #endif
100 
101  // clang-format on
102 
103  {
104 
105 # ifdef GUM_MULTI_DIM_PROJECTION_IMPL2ARRAY_NAME
106  const MultiDimArray< GUM_SCALAR >* table =
107  reinterpret_cast< const MultiDimArray< GUM_SCALAR >* >(ttable);
108 # endif
109 
110 # ifdef GUM_MULTI_DIM_PROJECTION_POINTER_IMPL2ARRAY_NAME
111  const MultiDimArray< GUM_SCALAR* >* table =
112  reinterpret_cast< const MultiDimArray< GUM_SCALAR* >* >(ttable);
113 # endif
114 
115  // create the neutral element used to fill the result upon its
116  // creation
117  const GUM_SCALAR neutral_element = GUM_MULTI_DIM_PROJECTION_NEUTRAL;
118 
119  // first, compute whether we should loop over table or over the projected
120  // table first to get a faster algorithm.
121  const Sequence< const DiscreteVariable* >& table_vars =
122  table->variablesSequence();
123  bool need_swapping = table_vars.size() >= 2 * del_vars.size();
124 
125  if (!need_swapping) {
126  // Compute the variables that belong to both the projection set and
127  // table. Store the domain size of the Cartesian product of these
128  // variables (result_domain_size) as well as the domain size of the
129  // Cartesian product of the variables of table that do not belong to
130  // projection set, i.e., those variables that belong to table but not to
131  // del_vars (table_alone_domain_size). In addition, store the number of
132  // increments in the computation loops at the end of the function before
133  // which the variables of the projection set need be incremented (vector
134  // before incr).
135  std::vector< Idx > table_and_result_offset;
136  std::vector< Idx > table_and_result_domain;
137  std::vector< Idx > before_incr;
138  unsigned int nb_positive_before_incr = 0;
139  Idx table_alone_domain_size = 1;
140  Idx result_domain_size = 1;
141  Idx table_domain_size = 1;
142  Sequence< const DiscreteVariable* > result_varSeq;
143  {
144  Idx tmp_before_incr = 1;
145  bool has_before_incr = false;
146 
147  for (const auto var : table_vars) {
148  table_domain_size *= var->domainSize();
149 
150  if (!del_vars.exists(var)) {
151  if (has_before_incr) {
152  before_incr.push_back(tmp_before_incr - 1);
153  has_before_incr = false;
154  ++nb_positive_before_incr;
155  } else {
156  before_incr.push_back(0);
157  }
158 
159  table_and_result_domain.push_back(var->domainSize());
160  table_and_result_offset.push_back(result_domain_size);
161  result_domain_size *= var->domainSize();
162  tmp_before_incr = 1;
163  result_varSeq << var;
164  } else {
165  tmp_before_incr *= var->domainSize();
166  has_before_incr = true;
167  table_alone_domain_size *= var->domainSize();
168  }
169  }
170  }
171  std::vector< Idx > table_and_result_value = table_and_result_domain;
172  std::vector< Idx > current_incr = before_incr;
173  std::vector< Idx > table_and_result_down = table_and_result_offset;
174 
175  for (unsigned int i = 0; i < table_and_result_down.size(); ++i) {
176  table_and_result_down[i] *= (table_and_result_domain[i] - 1);
177  }
178 
179  // create a table "result" containing only the variables of the
180  // projection: the variables are stored in the order in which they appear
181  // in "table" Hence, ++ operations on an instantiation on table will more
182  // or less correspond to a ++ operation on an instantiation on result
183  MultiDimArray< GUM_MULTI_DIM_PROJECTION_TYPE >* result =
184  new MultiDimArray< GUM_MULTI_DIM_PROJECTION_TYPE >;
185 
186  if (!result_varSeq.size()) { return result; }
187 
188  result->beginMultipleChanges();
189 
190  for (const auto var : result_varSeq)
191  *result << *var;
192 
193 // fill the matrix with the neutral element
194 # ifdef GUM_MULTI_DIM_PROJECTION_POINTER
195  result->endMultipleChanges();
196 
197  for (Idx i = 0; i < result_domain_size; ++i) {
198  result->unsafeSet(i, new GUM_SCALAR(neutral_element));
199  }
200 
201 # else
202  result->endMultipleChanges(neutral_element);
203 # endif
204 
205  // compute the projection: first loop over the variables X's in table
206  // that do not belong to result and, for each value of these X's, loop
207  // over the variables in both table and result. As such, in the internal
208  // loop, the offsets of "result" need only be incremented as usually to
209  // parse appropriately this table. For result, the problem is slightly
210  // more complicated: in the outer for loop, we shall always reset
211  // resul_offset to 0. For the inner loop, result_offset should be
212  // incremented (++) only when t1 before_incr[xxx] steps in the loop have
213  // already been made.
214 
215  // but before doing so, check whether there exist positive_before_incr.
216  // If this is not the case, optimize by not using before_incr at all
217  GUM_MULTI_DIM_PROJECTION_TYPE* pt =
218  const_cast< GUM_MULTI_DIM_PROJECTION_TYPE* >(&(table->unsafeGet(0)));
219  GUM_MULTI_DIM_PROJECTION_TYPE* pres =
220  const_cast< GUM_MULTI_DIM_PROJECTION_TYPE* >(&(result->unsafeGet(0)));
221  GUM_MULTI_DIM_PROJECTION_TYPE* pres_deb = pres;
222 
223  if (!nb_positive_before_incr) {
224  for (Idx i = 0; i < table_alone_domain_size; ++i) {
225  for (Idx j = 0; j < result_domain_size; ++j) {
226 # ifdef GUM_MULTI_DIM_PROJECTION_EFFECTIVE_TYPE
227  GUM_MULTI_DIM_PROJECTION(*pres, *pt);
228 # else
229 # ifdef GUM_MULTI_DIM_PROJECTION_POINTER
230  **pres = GUM_MULTI_DIM_PROJECTION(*pres, *pt);
231 # else
232  *pres = GUM_MULTI_DIM_PROJECTION(*pres, *pt);
233 # endif
234 # endif
235 
236  // update the offset of table and result
237  ++pt;
238  ++pres;
239  }
240 
241  // update the offset of result
242  pres = pres_deb;
243  }
244  } else {
245  // here there are positive before_incr and we should use them to know
246  // when result_offset needs be changed
247  Idx result_offset = 0;
248 
249  for (Idx i = 0; i < table_domain_size; ++i) {
250 # ifdef GUM_MULTI_DIM_PROJECTION_EFFECTIVE_TYPE
251  GUM_MULTI_DIM_PROJECTION(pres[result_offset], *pt);
252 # else
253 # ifdef GUM_MULTI_DIM_PROJECTION_POINTER
254  *(pres[result_offset]) =
255  GUM_MULTI_DIM_PROJECTION(pres[result_offset], *pt);
256 # else
257  pres[result_offset] = GUM_MULTI_DIM_PROJECTION(pres[result_offset], *pt);
258 # endif
259 # endif
260 
261  // update the offset of table
262  ++pt;
263 
264  // update the offset of result
265  for (unsigned int k = 0; k < current_incr.size(); ++k) {
266  // check if we need modify result_offset
267  if (current_incr[k]) {
268  --current_incr[k];
269  break;
270  }
271 
272  current_incr[k] = before_incr[k];
273 
274  // here we shall modify result_offset
275  --table_and_result_value[k];
276 
277  if (table_and_result_value[k]) {
278  result_offset += table_and_result_offset[k];
279  break;
280  }
281 
282  table_and_result_value[k] = table_and_result_domain[k];
283  result_offset -= table_and_result_down[k];
284  }
285  }
286  }
287 
288  return result;
289  } else { // need_swapping = true
290 
291  // Compute the variables that are in t1 but not in t2. For these
292  // variables, get the increment in the offset of t1 that would result
293  // from an increment in one of these variables (vector t1_alone_offset).
294  // Also store the domain size of these variables (vector t1_alone_domain)
295  // and, for the computation loops at the end of this function, |variable|
296  // - the current values of these variables (vector t1_alone_value). All
297  // these vectors reference the variables of t1 \ t2 in the order in which
298  // they appear in seq1. Keep as well the size of the Cartesian product of
299  // these variables.
300  std::vector< Idx > table_alone_offset;
301  std::vector< Idx > table_alone_domain;
302  Idx offset = 1;
303  Idx table_alone_domain_size = 1;
304  HashTable< const DiscreteVariable*, Idx > var1offset(table_vars.size());
305 
306  for (const auto var : table_vars) {
307  if (del_vars.exists(var)) {
308  table_alone_domain.push_back(var->domainSize());
309  table_alone_offset.push_back(offset);
310  table_alone_domain_size *= var->domainSize();
311  }
312 
313  var1offset.insert(var, offset);
314  offset *= var->domainSize();
315  }
316 
317  std::vector< Idx > table_alone_value = table_alone_domain;
318  std::vector< Idx > table_alone_down = table_alone_offset;
319 
320  for (unsigned int i = 0; i < table_alone_down.size(); ++i)
321  table_alone_down[i] *= (table_alone_domain[i] - 1);
322 
323  // Compute the same vectors for the variables that belong to both t1 and
324  // t2. In this case, All these vectors reference the variables in the
325  // order in which they appear in seq2. In addition, store the number of
326  // increments in the computation loops at the end of the function before
327  // which the variables of t1 cap t2 need be incremented (vector
328  // t1_and_t2_before incr). Similarly, store the number of such
329  // increments currently still needed before the next incrementation of
330  // the variables of t1 cap t2. Keep as well the size of the Cartesian
331  // product of these variables.
332  Sequence< const DiscreteVariable* > result_varSeq;
333  std::vector< Idx > table_and_result_offset;
334  std::vector< Idx > table_and_result_domain;
335  Idx result_domain_size = 1;
336  bool has_before_incr = false;
337  bool found_proj_var = false;
338 
339  for (const auto var : table_vars) {
340  if (!del_vars.exists(var)) {
341  table_and_result_domain.push_back(var->domainSize());
342  table_and_result_offset.push_back(var1offset[var]);
343  found_proj_var = true;
344  result_domain_size *= var->domainSize();
345  result_varSeq << var;
346  } else {
347  if (found_proj_var) has_before_incr = true;
348  }
349  }
350 
351  std::vector< Idx > table_and_result_value = table_and_result_domain;
352  std::vector< Idx > table_and_result_down = table_and_result_offset;
353 
354  for (unsigned int i = 0; i < table_and_result_down.size(); ++i) {
355  table_and_result_down[i] *= (table_and_result_domain[i] - 1);
356  }
357 
358  // create a table "result" containing only the variables of the
359  // projection: the variables are stored in the order in which they appear
360  // in "table" Hence, ++ operations on an instantiation on table will more
361  // or less correspond to a ++ operation on an instantiation on result
362  MultiDimArray< GUM_MULTI_DIM_PROJECTION_TYPE >* result =
363  new MultiDimArray< GUM_MULTI_DIM_PROJECTION_TYPE >;
364  result->beginMultipleChanges();
365 
366  for (const auto var : result_varSeq)
367  *result << *var;
368 
369 # ifdef GUM_MULTI_DIM_PROJECTION_POINTER
370  result->endMultipleChanges();
371 
372  // fill the matrix with the neutral element
373  for (Idx i = 0; i < result_domain_size; ++i) {
374  result->unsafeSet(i, new GUM_SCALAR(neutral_element));
375  }
376 
377 # else
378  result->endMultipleChanges(neutral_element);
379 # endif
380 
381  // compute the sum: first loop over the variables X's both in table and
382  // in result and, for each value of these X's, loop over the variables
383  // that are in table but not result. As such, in the internal loop, there
384  // is no increment in the offset of "result", and in the outer loop, this
385  // offset is incremented using a simple ++ operator. For table, the
386  // problem is slightly more complicated: in the outer for loop, we shall
387  // increment the variables of table cap result according to vectors
388  // table_and_result_xxx. Each time a variable of these vectors has been
389  // incremented up to its max, we shall put it down to 0 and increment the
390  // next one, and so on. For the inner loop, this is similar except that
391  // we shall do these operations only when before_incr[xxx] steps in the
392  // loop have already been made.
393  GUM_MULTI_DIM_PROJECTION_TYPE* pt =
394  const_cast< GUM_MULTI_DIM_PROJECTION_TYPE* >(&(table->unsafeGet(0)));
395  GUM_MULTI_DIM_PROJECTION_TYPE* pres =
396  const_cast< GUM_MULTI_DIM_PROJECTION_TYPE* >(&(result->unsafeGet(0)));
397 
398  // but before doing so, check whether there exist positive_before_incr.
399  // If this is not the case, optimize by not using before_incr at all
400  if (!has_before_incr) {
401  for (Idx i = 0; i < result_domain_size; ++i) {
402  for (Idx j = 0; j < table_alone_domain_size; ++j) {
403 # ifdef GUM_MULTI_DIM_PROJECTION_EFFECTIVE_TYPE
404  GUM_MULTI_DIM_PROJECTION(*pres, *pt);
405 # else
406 # ifdef GUM_MULTI_DIM_PROJECTION_POINTER
407  **pres = GUM_MULTI_DIM_PROJECTION(*pres, *pt);
408 # else
409  *pres = GUM_MULTI_DIM_PROJECTION(*pres, *pt);
410 # endif
411 # endif
412 
413  // update the offset of table
414  ++pt;
415  }
416 
417  // update the offset of result
418  ++pres;
419  }
420  } else {
421  // here there are positive before_incr and we should use them to know
422  // when result_offset needs be changed
423  Idx table_offset = 0;
424 
425  for (Idx j = 0; j < result_domain_size; ++j) {
426  for (Idx i = 0; i < table_alone_domain_size; ++i) {
427 # ifdef GUM_MULTI_DIM_PROJECTION_EFFECTIVE_TYPE
428  GUM_MULTI_DIM_PROJECTION(*pres, pt[table_offset]);
429 # else
430 # ifdef GUM_MULTI_DIM_PROJECTION_POINTER
431  **pres = GUM_MULTI_DIM_PROJECTION(*pres, pt[table_offset]);
432 # else
433  *pres = GUM_MULTI_DIM_PROJECTION(*pres, pt[table_offset]);
434 # endif
435 # endif
436 
437  // update the increment of table for the inner loop
438  for (unsigned int k = 0; k < table_alone_value.size(); ++k) {
439  --table_alone_value[k];
440 
441  if (table_alone_value[k]) {
442  table_offset += table_alone_offset[k];
443  break;
444  }
445 
446  table_alone_value[k] = table_alone_domain[k];
447  table_offset -= table_alone_down[k];
448  }
449  }
450 
451  // update the offset of table for the outer loop
452  for (unsigned int k = 0; k < table_and_result_value.size(); ++k) {
453  --table_and_result_value[k];
454 
455  if (table_and_result_value[k]) {
456  table_offset += table_and_result_offset[k];
457  break;
458  }
459 
460  table_and_result_value[k] = table_and_result_domain[k];
461  table_offset -= table_and_result_down[k];
462  }
463 
464  // update the offset of result for the outer loop
465  //++result_offset;
466  ++pres;
467  }
468  }
469 
470  return result;
471  }
472  }
473 
474 # undef GUM_MULTI_DIM_PROJECTION_TYPE
475 
476 # ifdef GUM_MULTI_DIM_PROJECTION_POINTER
477 # undef GUM_MULTI_DIM_PROJECTION_POINTER
478 # endif
479 } // namespace gum
480 #endif /* GUM_PROJECTION_PATTERN_ALLOWED */
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Definition: agrum.h:25