aGrUM  0.20.3
a C++ library for (probabilistic) graphical models
multiDimCombinationDefault_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright (c) 2005-2021 by Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4  * info_at_agrum_dot_org
5  *
6  * This library is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 /**
23  * @file
24  * @brief A generic class to combine efficiently several MultiDim tables
25  *
26  * @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
27  */
28 
29 #ifndef DOXYGEN_SHOULD_SKIP_THIS
30 
31 # include <limits>
32 
33 # include <agrum/agrum.h>
34 
35 # include <agrum/tools/core/priorityQueue.h>
36 # include <agrum/tools/multidim/utils/operators/multiDimCombinationDefault.h>
37 
38 namespace gum {
39 
40  // constructor
41  template < typename GUM_SCALAR, template < typename > class TABLE >
42  MultiDimCombinationDefault< GUM_SCALAR, TABLE >::MultiDimCombinationDefault(
43  TABLE< GUM_SCALAR >* (*combine)(const TABLE< GUM_SCALAR >&, const TABLE< GUM_SCALAR >&)) :
44  MultiDimCombination< GUM_SCALAR, TABLE >(),
45  combine_(combine) {
46  GUM_CONSTRUCTOR(MultiDimCombinationDefault);
47  }
48 
49  // copy constructor
50  template < typename GUM_SCALAR, template < typename > class TABLE >
55  // for debugging purposes
57  }
58 
59  // destructor
60  template < typename GUM_SCALAR, template < typename > class TABLE >
62  // for debugging purposes
64  }
65 
66  // virtual constructor
67  template < typename GUM_SCALAR, template < typename > class TABLE >
71  }
72 
73  // changes the function used for combining two TABLES
74  template < typename GUM_SCALAR, template < typename > class TABLE >
76  TABLE< GUM_SCALAR >* (*combine)(const TABLE< GUM_SCALAR >&, const TABLE< GUM_SCALAR >&)) {
77  combine_ = combine;
78  }
79 
80  // returns the combination function currently used by the combinator
81  template < typename GUM_SCALAR, template < typename > class TABLE >
83  const TABLE< GUM_SCALAR >&,
84  const TABLE< GUM_SCALAR >&) {
85  return combine_;
86  }
87 
88  // returns the domain size of the Cartesian product of the union of all the
89  // variables in seq1 and seq2
90  template < typename GUM_SCALAR, template < typename > class TABLE >
92  const Sequence< const DiscreteVariable* >& seq1,
93  const Sequence< const DiscreteVariable* >& seq2) const {
94  if (seq1.empty() && seq2.empty()) return 0;
95 
96  Size size = 1;
97 
98  for (const auto ptrVar: seq1)
99  size *= ptrVar->domainSize();
100 
101  for (const auto ptrVar: seq2)
102  if (!seq1.exists(ptrVar)) size *= ptrVar->domainSize();
103 
104  return size;
105  }
106 
107  // returns the result of the combination
108  template < typename GUM_SCALAR, template < typename > class TABLE >
111  const Set< const TABLE< GUM_SCALAR >* >& set) {
112  TABLE< GUM_SCALAR >* res = combine(set);
113  container = std::move(*res);
114  delete (res);
115  }
116 
117  // returns the result of the combination
118  template < typename GUM_SCALAR, template < typename > class TABLE >
120  const Set< const TABLE< GUM_SCALAR >* >& set) {
121  // check if the set passed in argument is empty. If so, raise an exception
122  if (set.size() < 2) {
124  "the set passed to a MultiDimCombinationDefault"
125  " should at least contain two elements");
126  }
127 
128  // create a vector with all the tables to combine
129  std::vector< const TABLE< GUM_SCALAR >* > tables(set.size());
130  const Size tabsize = tables.size();
131 
132  {
133  Size i = Size(0);
134  for (auto iter = set.cbegin(); iter != set.cend(); ++iter, ++i) {
135  tables[i] = *iter;
136  }
137  }
138 
139  // create a vector indicating wether the elements in tables are freshly
140  // created TABLE<GUM_SCALAR>* due to the combination of some TABLEs or if
141  // they were added by the user into the combination container
142  std::vector< bool > is_t_new(tabsize, false);
143 
144  // for each pair of tables (i,j), compute the size of the table that would
145  // result from the addition of tables i and j and store the result into a
146  // priorityQueue
147  std::pair< Size, Size > pair;
149 
150  for (Size i = Size(0); i < tabsize; ++i) {
151  pair.first = i;
152  const Sequence< const DiscreteVariable* >& seq1 = tables[i]->variablesSequence();
153 
154  for (Size j = i + 1; j < tabsize; ++j) {
155  pair.second = j;
157  }
158  }
159 
160  // now parse the priority queue: the top element (i,j) gives the combination
161  // to perform. When the result R has been computed, substitute i by R,
162  // remove table j and recompute all the priorities of all the pairs (R,k)
163  // still available.
164  for (Size k = 1; k < tabsize; ++k) {
165  // get the combination to perform and do it
166  pair = queue.pop();
167  const Size ti = pair.first;
168  const Size tj = pair.second;
169 
170  TABLE< GUM_SCALAR >* result = combine_(*(tables[ti]), *(tables[tj]));
171 
172  // substitute tables[pair.first] by the result
173  if (tables[ti] && is_t_new[ti]) delete tables[ti];
174  if (tables[tj] && is_t_new[tj]) delete tables[tj];
175 
176  tables[ti] = result;
177  is_t_new[ti] = true;
178 
179  tables[tj] = nullptr;
180 
181  // remove all the pairs involving tj in the priority queue
182  for (Size ind = 0; ind < tj; ++ind) {
183  if (tables[ind] != nullptr) {
184  pair.first = ind;
185  queue.erase(pair);
186  }
187  }
188 
189  pair.first = tj;
190  for (Size ind = tj + 1; ind < tabsize; ++ind) {
191  if (tables[ind] != nullptr) {
192  pair.second = ind;
193  queue.erase(pair);
194  }
195  }
196 
197  // update the "combinated" size of all the pairs involving "result"
198  {
199  const Sequence< const DiscreteVariable* >& seq1 = tables[ti]->variablesSequence();
200  pair.second = ti;
201  Size newsize;
202 
203  for (Size ind = 0; ind < ti; ++ind) {
204  if (tables[ind] != nullptr) {
205  pair.first = ind;
208  }
209  }
210 
211  pair.first = ti;
212 
213  for (Size ind = ti + 1; ind < tabsize; ++ind) {
214  if (tables[ind] != nullptr) {
215  pair.second = ind;
218  }
219  }
220  }
221  }
222 
223  // here, there remains only one nonzero pointer in tables:
224  // the result of our combination
225  Size k = 0;
226  while (tables[k] == nullptr)
227  ++k;
228 
229  return const_cast< TABLE< GUM_SCALAR >* >(tables[k]);
230  }
231 
232  // returns the result of the combination
233  template < typename GUM_SCALAR, template < typename > class TABLE >
235  const Set< const Sequence< const DiscreteVariable* >* >& set) const {
236  // check if the set passed in argument is empty.
237  if (set.size() < 2) return 0.0f;
238 
239  float result = 0.0f;
240 
241  // create a vector with all the tables to combine
242  std::vector< const Sequence< const DiscreteVariable* >* > tables(set.size());
243  const Size tabsize = tables.size();
244 
245  {
246  Size i = Size(0);
247  for (auto iter = set.cbegin(); iter != set.cend(); ++iter, ++i) {
248  tables[i] = *iter;
249  }
250  }
251 
252  // create a vector indicating wether the elements in tables are freshly
253  // created Sequence<const DiscreteVariable *>* due to the combination of
254  // some TABLEs or if they were added by the user into the combination
255  // container
256  std::vector< bool > is_t_new(tabsize, false);
257 
258  // for each pair of tables (i,j), compute the size of the table that would
259  // result from the addition of tables i and j and store the result into a
260  // priorityQueue
261  std::pair< Size, Size > pair;
263 
264  for (Size i = Size(0); i < tabsize; ++i) {
265  pair.first = i;
266 
267  for (Size j = i + 1; j < tabsize; ++j) {
268  pair.second = j;
270  }
271  }
272 
273  // now parse the priority queue: the top element (i,j) gives the combination
274  // to perform. When the result R has been computed, substitute i by R,
275  // remove table j and recompute all the priorities of all the pairs (R,k)
276  // still available.
277  for (Size k = 1; k < tabsize; ++k) {
278  // get the combination to perform and do it
279  pair = queue.pop();
280  const Size ti = pair.first;
281  const Size tj = pair.second;
282 
283  // compute the result
284  Sequence< const DiscreteVariable* >* new_seq = new Sequence< const DiscreteVariable* >;
285  const Sequence< const DiscreteVariable* >& seq1 = *(tables[ti]);
286  const Sequence< const DiscreteVariable* >& seq2 = *(tables[tj]);
287 
288  Size new_size = 1;
289 
290  for (const auto ptrVar: seq1) {
291  new_size *= ptrVar->domainSize();
293  }
294 
295  for (const auto ptrVar: seq2) {
296  if (!seq1.exists(ptrVar)) {
297  new_size *= ptrVar->domainSize();
299  }
300  }
301 
302  result += new_size;
303 
304  // substitute tables[pair.first] by the result
305  if (tables[ti] && is_t_new[ti]) delete tables[ti];
306  if (tables[tj] && is_t_new[tj]) delete tables[tj];
307 
308  tables[ti] = new_seq;
309  is_t_new[ti] = true;
310 
311  tables[tj] = nullptr;
312 
313  // remove all the pairs involving tj in the priority queue
314  for (Size ind = 0; ind < tj; ++ind) {
315  if (tables[ind] != nullptr) {
316  pair.first = ind;
317  queue.erase(pair);
318  }
319  }
320 
321  pair.first = tj;
322 
323  for (Size ind = tj + 1; ind < tabsize; ++ind) {
324  if (tables[ind] != nullptr) {
325  pair.second = ind;
326  queue.erase(pair);
327  }
328  }
329 
330  // update the "combined" size of all the pairs involving "new_seq"
331  {
332  pair.second = ti;
333  Size newsize;
334 
335  for (Size ind = 0; ind < ti; ++ind) {
336  if (tables[ind] != nullptr) {
337  pair.first = ind;
340  }
341  }
342 
343  pair.first = ti;
344 
345  for (Size ind = ti + 1; ind < tabsize; ++ind) {
346  if (tables[ind] != nullptr) {
347  pair.second = ind;
350  }
351  }
352  }
353  }
354 
355  // here, there remains only one nonzero pointer in tables:
356  // the result of our combination
357  Size k = 0;
358  while (tables[k] == nullptr)
359  ++k;
360 
361  delete tables[k];
362 
363  return result;
364  }
365 
366  // returns the result of the combination
367  template < typename GUM_SCALAR, template < typename > class TABLE >
369  const Set< const TABLE< GUM_SCALAR >* >& set) const {
370  // check if the set passed in argument is empty.
371  if (set.size() < 2) return 0.0f;
372 
373  // create the set of sets of discrete variables involved in the tables
374  Set< const Sequence< const DiscreteVariable* >* > var_set(set.size());
375 
376  for (const auto ptrTab: set) {
378  }
379 
380  return nbOperations(var_set);
381  }
382 
383  // returns the memory consumption used during the combination
384  template < typename GUM_SCALAR, template < typename > class TABLE >
386  const Set< const Sequence< const DiscreteVariable* >* >& set) const {
387  // check if the set passed in argument is empty.
388  if (set.size() < 2) return std::pair< long, long >(0, 0);
389 
390  Size max_memory = 0;
391  Size current_memory = 0;
392 
393  // create a vector with all the tables to combine
394  std::vector< const Sequence< const DiscreteVariable* >* > tables(set.size());
395  const Size tabsize = tables.size();
396 
397  std::vector< Size > table_size(set.size());
398 
399  {
400  Size i = Size(0);
401 
402  for (auto iter = set.cbegin(); iter != set.cend(); ++iter, ++i) {
403  const Sequence< const DiscreteVariable* >* vars = *iter;
404  tables[i] = vars;
405 
406  Size size = Size(1);
407  for (const auto ptrVar: *vars) {
408  size *= ptrVar->domainSize();
409  }
410 
411  table_size[i] = size;
412  }
413  }
414 
415  // create a vector indicating wether the elements in tables are freshly
416  // created Sequence<const DiscreteVariable *>* due to the combination of
417  // some TABLEs or if they were added by the user into the combination
418  // container
419  std::vector< bool > is_t_new(tables.size(), false);
420 
421  // for each pair of tables (i,j), compute the size of the table that would
422  // result from the addition of tables i and j and store the result into a
423  // priorityQueue
424  std::pair< Size, Size > pair;
425 
427 
428  for (Size i = Size(0); i < tabsize; ++i) {
429  pair.first = i;
430 
431  for (Size j = i + 1; j < tabsize; ++j) {
432  pair.second = j;
434  }
435  }
436 
437  // now parse the priority queue: the top element (i,j) gives the combination
438  // to perform. When the result R has been computed, substitute i by R,
439  // remove table j and recompute all the priorities of all the pairs (R,k)
440  // still available.
441  for (Size k = 1; k < tabsize; ++k) {
442  // get the combination to perform and do it
443  pair = queue.pop();
444  const Size ti = pair.first;
445  const Size tj = pair.second;
446 
447  // compute the result
448  Sequence< const DiscreteVariable* >* new_seq = new Sequence< const DiscreteVariable* >;
449  const Sequence< const DiscreteVariable* >& seq1 = *(tables[ti]);
450  const Sequence< const DiscreteVariable* >& seq2 = *(tables[tj]);
451 
452  Size new_size = Size(1);
453 
454  for (const auto ptrVar: seq1) {
455  if (std::numeric_limits< Size >::max() / ptrVar->domainSize() < new_size) {
456  GUM_ERROR(OutOfBounds, "memory usage out of long int range")
457  }
458 
459  new_size *= ptrVar->domainSize();
460 
462  }
463 
464  for (const auto ptrVar: seq2) {
465  if (!seq1.exists(ptrVar)) {
466  if (std::numeric_limits< Size >::max() / ptrVar->domainSize() < new_size) {
467  GUM_ERROR(OutOfBounds, "memory usage out of long int range")
468  }
469 
470  new_size *= ptrVar->domainSize();
471 
473  }
474  }
475 
477  GUM_ERROR(OutOfBounds, "memory usage out of long int range")
478  }
479 
481 
483 
484  // substitute tables[pair.first] by the result
485  if (tables[ti] && is_t_new[ti]) {
486  delete tables[ti];
488  }
489 
490  if (tables[tj] && is_t_new[tj]) {
491  delete tables[tj];
493  }
494 
495  tables[ti] = new_seq;
497  is_t_new[ti] = true;
498 
499  tables[tj] = nullptr;
500 
501  // remove all the pairs involving tj in the priority queue
502  for (Size ind = 0; ind < tj; ++ind) {
503  if (tables[ind] != nullptr) {
504  pair.first = ind;
505  queue.erase(pair);
506  }
507  }
508 
509  pair.first = tj;
510 
511  for (Size ind = tj + 1; ind < tabsize; ++ind) {
512  if (tables[ind] != nullptr) {
513  pair.second = ind;
514  queue.erase(pair);
515  }
516  }
517 
518  // update the "combined" size of all the pairs involving "new_seq"
519  {
520  pair.second = ti;
521  Size newsize;
522 
523  for (Size ind = Size(0); ind < ti; ++ind) {
524  if (tables[ind] != nullptr) {
525  pair.first = ind;
528  }
529  }
530 
531  pair.first = ti;
532 
533  for (Size ind = ti + 1; ind < tabsize; ++ind) {
534  if (tables[ind] != nullptr) {
535  pair.second = ind;
538  }
539  }
540  }
541  }
542 
543  // here, there remains only one nonzero pointer in tables:
544  // the result of our combination
545  Size k = Size(0);
546  while (!tables[k])
547  ++k;
548 
549  delete tables[k];
550 
551  return std::pair< long, long >(long(max_memory), long(current_memory));
552  }
553 
554  // returns the memory consumption used during the combination
555  template < typename GUM_SCALAR, template < typename > class TABLE >
557  const Set< const TABLE< GUM_SCALAR >* >& set) const {
558  // check if the set passed in argument is empty.
559  if (set.size() < 2) return std::pair< long, long >(0, 0);
560 
561  // create the set of sets of discrete variables involved in the tables
562  Set< const Sequence< const DiscreteVariable* >* > var_set(set.size());
563 
564  for (const auto ptrTab: set) {
566  }
567 
568  return memoryUsage(var_set);
569  }
570 
571 } /* namespace gum */
572 
573 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:643