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