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