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