aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
multiDimCombinationDefault_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright 2005-2020 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 >&,
44  const TABLE< GUM_SCALAR >&)) :
45  MultiDimCombination< GUM_SCALAR, TABLE >(),
46  combine_(combine) {
47  // for debugging purposes
48  GUM_CONSTRUCTOR(MultiDimCombinationDefault);
49  }
50 
51  // copy constructor
52  template < typename GUM_SCALAR, template < typename > class TABLE >
57  // for debugging purposes
59  }
60 
61  // destructor
62  template < typename GUM_SCALAR, template < typename > class TABLE >
64  // for debugging purposes
66  }
67 
68  // virtual constructor
69  template < typename GUM_SCALAR, template < typename > class TABLE >
73  }
74 
75  // changes the function used for combining two TABLES
76  template < typename GUM_SCALAR, template < typename > class TABLE >
78  TABLE< GUM_SCALAR >* (*combine)(const TABLE< GUM_SCALAR >&,
79  const TABLE< GUM_SCALAR >&)) {
80  combine_ = combine;
81  }
82 
83  // returns the combination function currently used by the combinator
84  template < typename GUM_SCALAR, template < typename > class TABLE >
85  INLINE TABLE< GUM_SCALAR >* (
87  const TABLE< GUM_SCALAR >&,
88  const TABLE< GUM_SCALAR >&) {
89  return combine_;
90  }
91 
92  // returns the domain size of the Cartesian product of the union of all the
93  // variables in seq1 and seq2
94  template < typename GUM_SCALAR, template < typename > class TABLE >
96  const Sequence< const DiscreteVariable* >& seq1,
97  const Sequence< const DiscreteVariable* >& seq2) const {
98  if (seq1.empty() && seq2.empty()) return 0;
99 
100  Size size = 1;
101 
102  for (const auto ptrVar: seq1)
103  size *= ptrVar->domainSize();
104 
105  for (const auto ptrVar: seq2)
106  if (!seq1.exists(ptrVar)) size *= ptrVar->domainSize();
107 
108  return size;
109  }
110 
111  // returns the result of the combination
112  template < typename GUM_SCALAR, template < typename > class TABLE >
115  const Set< const TABLE< GUM_SCALAR >* >& set) {
116  TABLE< GUM_SCALAR >* res = combine(set);
117  container = std::move(*res);
118  delete (res);
119  }
120 
121  // returns the result of the combination
122  template < typename GUM_SCALAR, template < typename > class TABLE >
124  const Set< const TABLE< GUM_SCALAR >* >& set) {
125  // check if the set passed in argument is empty. If so, raise an exception
126  if (set.size() < 2) {
128  "the set passed to a MultiDimCombinationDefault"
129  " should at least contain two elements");
130  }
131 
132  // create a vector with all the tables to combine
133  std::vector< const TABLE< GUM_SCALAR >* > tables(set.size());
134  const Size tabsize = tables.size();
135 
136  {
137  Size i = Size(0);
138  for (auto iter = set.cbegin(); iter != set.cend(); ++iter, ++i) {
139  tables[i] = *iter;
140  }
141  }
142 
143  // create a vector indicating wether the elements in tables are freshly
144  // created TABLE<GUM_SCALAR>* due to the combination of some TABLEs or if
145  // they were added by the user into the combination container
146  std::vector< bool > is_t_new(tabsize, false);
147 
148  // for each pair of tables (i,j), compute the size of the table that would
149  // result from the addition of tables i and j and store the result into a
150  // priorityQueue
151  std::pair< Size, Size > pair;
153 
154  for (Size i = Size(0); i < tabsize; ++i) {
155  pair.first = i;
156  const Sequence< const DiscreteVariable* >& seq1
157  = tables[i]->variablesSequence();
158 
159  for (Size j = i + 1; j < tabsize; ++j) {
160  pair.second = j;
162  }
163  }
164 
165  // now parse the priority queue: the top element (i,j) gives the combination
166  // to perform. When the result R has been computed, substitute i by R,
167  // remove table j and recompute all the priorities of all the pairs (R,k)
168  // still available.
169  for (Size k = 1; k < tabsize; ++k) {
170  // get the combination to perform and do it
171  pair = queue.pop();
172  const Size ti = pair.first;
173  const Size tj = pair.second;
174 
175  TABLE< GUM_SCALAR >* result = combine_(*(tables[ti]), *(tables[tj]));
176 
177  // substitute tables[pair.first] by the result
178  if (tables[ti] && is_t_new[ti]) delete tables[ti];
179  if (tables[tj] && is_t_new[tj]) delete tables[tj];
180 
181  tables[ti] = result;
182  is_t_new[ti] = true;
183 
184  tables[tj] = nullptr;
185 
186  // remove all the pairs involving tj in the priority queue
187  for (Size ind = 0; ind < tj; ++ind) {
188  if (tables[ind] != nullptr) {
189  pair.first = ind;
190  queue.erase(pair);
191  }
192  }
193 
194  pair.first = tj;
195  for (Size ind = tj + 1; ind < tabsize; ++ind) {
196  if (tables[ind] != nullptr) {
197  pair.second = ind;
198  queue.erase(pair);
199  }
200  }
201 
202  // update the "combinated" size of all the pairs involving "result"
203  {
204  const Sequence< const DiscreteVariable* >& seq1
206  pair.second = ti;
207  Size newsize;
208 
209  for (Size ind = 0; ind < ti; ++ind) {
210  if (tables[ind] != nullptr) {
211  pair.first = ind;
214  }
215  }
216 
217  pair.first = ti;
218 
219  for (Size ind = ti + 1; ind < tabsize; ++ind) {
220  if (tables[ind] != nullptr) {
221  pair.second = ind;
224  }
225  }
226  }
227  }
228 
229  // here, there remains only one nonzero pointer in tables:
230  // the result of our combination
231  Size k = 0;
232  while (tables[k] == nullptr)
233  ++k;
234 
235  return const_cast< TABLE< GUM_SCALAR >* >(tables[k]);
236  }
237 
238  // returns the result of the combination
239  template < typename GUM_SCALAR, template < typename > class TABLE >
241  const Set< const Sequence< const DiscreteVariable* >* >& set) const {
242  // check if the set passed in argument is empty.
243  if (set.size() < 2) return 0.0f;
244 
245  float result = 0.0f;
246 
247  // create a vector with all the tables to combine
248  std::vector< const Sequence< const DiscreteVariable* >* > tables(set.size());
249  const Size tabsize = tables.size();
250 
251  {
252  Size i = Size(0);
253  for (auto iter = set.cbegin(); iter != set.cend(); ++iter, ++i) {
254  tables[i] = *iter;
255  }
256  }
257 
258  // create a vector indicating wether the elements in tables are freshly
259  // created Sequence<const DiscreteVariable *>* due to the combination of
260  // some TABLEs or if they were added by the user into the combination
261  // container
262  std::vector< bool > is_t_new(tabsize, false);
263 
264  // for each pair of tables (i,j), compute the size of the table that would
265  // result from the addition of tables i and j and store the result into a
266  // priorityQueue
267  std::pair< Size, Size > pair;
269 
270  for (Size i = Size(0); i < tabsize; ++i) {
271  pair.first = i;
272 
273  for (Size j = i + 1; j < tabsize; ++j) {
274  pair.second = j;
276  }
277  }
278 
279  // now parse the priority queue: the top element (i,j) gives the combination
280  // to perform. When the result R has been computed, substitute i by R,
281  // remove table j and recompute all the priorities of all the pairs (R,k)
282  // still available.
283  for (Size k = 1; k < tabsize; ++k) {
284  // get the combination to perform and do it
285  pair = queue.pop();
286  const Size ti = pair.first;
287  const Size tj = pair.second;
288 
289  // compute the result
291  = new Sequence< const DiscreteVariable* >;
292  const Sequence< const DiscreteVariable* >& seq1 = *(tables[ti]);
293  const Sequence< const DiscreteVariable* >& seq2 = *(tables[tj]);
294 
295  Size new_size = 1;
296 
297  for (const auto ptrVar: seq1) {
298  new_size *= ptrVar->domainSize();
300  }
301 
302  for (const auto ptrVar: seq2) {
303  if (!seq1.exists(ptrVar)) {
304  new_size *= ptrVar->domainSize();
306  }
307  }
308 
309  result += new_size;
310 
311  // substitute tables[pair.first] by the result
312  if (tables[ti] && is_t_new[ti]) delete tables[ti];
313  if (tables[tj] && is_t_new[tj]) delete tables[tj];
314 
315  tables[ti] = new_seq;
316  is_t_new[ti] = true;
317 
318  tables[tj] = nullptr;
319 
320  // remove all the pairs involving tj in the priority queue
321  for (Size ind = 0; ind < tj; ++ind) {
322  if (tables[ind] != nullptr) {
323  pair.first = ind;
324  queue.erase(pair);
325  }
326  }
327 
328  pair.first = tj;
329 
330  for (Size ind = tj + 1; ind < tabsize; ++ind) {
331  if (tables[ind] != nullptr) {
332  pair.second = ind;
333  queue.erase(pair);
334  }
335  }
336 
337  // update the "combined" size of all the pairs involving "new_seq"
338  {
339  pair.second = ti;
340  Size newsize;
341 
342  for (Size ind = 0; ind < ti; ++ind) {
343  if (tables[ind] != nullptr) {
344  pair.first = ind;
347  }
348  }
349 
350  pair.first = ti;
351 
352  for (Size ind = ti + 1; ind < tabsize; ++ind) {
353  if (tables[ind] != nullptr) {
354  pair.second = ind;
357  }
358  }
359  }
360  }
361 
362  // here, there remains only one nonzero pointer in tables:
363  // the result of our combination
364  Size k = 0;
365  while (tables[k] == nullptr)
366  ++k;
367 
368  delete tables[k];
369 
370  return result;
371  }
372 
373  // returns the result of the combination
374  template < typename GUM_SCALAR, template < typename > class TABLE >
376  const Set< const TABLE< GUM_SCALAR >* >& set) const {
377  // check if the set passed in argument is empty.
378  if (set.size() < 2) return 0.0f;
379 
380  // create the set of sets of discrete variables involved in the tables
381  Set< const Sequence< const DiscreteVariable* >* > var_set(set.size());
382 
383  for (const auto ptrTab: set) {
385  }
386 
387  return nbOperations(var_set);
388  }
389 
390  // returns the memory consumption used during the combination
391  template < typename GUM_SCALAR, template < typename > class TABLE >
392  std::pair< long, long >
394  const Set< const Sequence< const DiscreteVariable* >* >& set) const {
395  // check if the set passed in argument is empty.
396  if (set.size() < 2) return std::pair< long, long >(0, 0);
397 
398  Size max_memory = 0;
399  Size current_memory = 0;
400 
401  // create a vector with all the tables to combine
402  std::vector< const Sequence< const DiscreteVariable* >* > tables(set.size());
403  const Size tabsize = tables.size();
404 
405  std::vector< Size > table_size(set.size());
406 
407  {
408  Size i = Size(0);
409 
410  for (auto iter = set.cbegin(); iter != set.cend(); ++iter, ++i) {
411  const Sequence< const DiscreteVariable* >* vars = *iter;
412  tables[i] = vars;
413 
414  Size size = Size(1);
415  for (const auto ptrVar: *vars) {
416  size *= ptrVar->domainSize();
417  }
418 
419  table_size[i] = size;
420  }
421  }
422 
423  // create a vector indicating wether the elements in tables are freshly
424  // created Sequence<const DiscreteVariable *>* due to the combination of
425  // some TABLEs or if they were added by the user into the combination
426  // container
427  std::vector< bool > is_t_new(tables.size(), false);
428 
429  // for each pair of tables (i,j), compute the size of the table that would
430  // result from the addition of tables i and j and store the result into a
431  // priorityQueue
432  std::pair< Size, Size > pair;
433 
435 
436  for (Size i = Size(0); i < tabsize; ++i) {
437  pair.first = i;
438 
439  for (Size j = i + 1; j < tabsize; ++j) {
440  pair.second = j;
442  }
443  }
444 
445  // now parse the priority queue: the top element (i,j) gives the combination
446  // to perform. When the result R has been computed, substitute i by R,
447  // remove table j and recompute all the priorities of all the pairs (R,k)
448  // still available.
449  for (Size k = 1; k < tabsize; ++k) {
450  // get the combination to perform and do it
451  pair = queue.pop();
452  const Size ti = pair.first;
453  const Size tj = pair.second;
454 
455  // compute the result
457  = new Sequence< const DiscreteVariable* >;
458  const Sequence< const DiscreteVariable* >& seq1 = *(tables[ti]);
459  const Sequence< const DiscreteVariable* >& seq2 = *(tables[tj]);
460 
461  Size new_size = Size(1);
462 
463  for (const auto ptrVar: seq1) {
464  if (std::numeric_limits< Size >::max() / ptrVar->domainSize() < new_size) {
465  GUM_ERROR(OutOfBounds, "memory usage out of long int range");
466  }
467 
468  new_size *= ptrVar->domainSize();
469 
471  }
472 
473  for (const auto ptrVar: seq2) {
474  if (!seq1.exists(ptrVar)) {
475  if (std::numeric_limits< Size >::max() / ptrVar->domainSize()
476  < new_size) {
477  GUM_ERROR(OutOfBounds, "memory usage out of long int range");
478  }
479 
480  new_size *= ptrVar->domainSize();
481 
483  }
484  }
485 
487  GUM_ERROR(OutOfBounds, "memory usage out of long int range");
488  }
489 
491 
493 
494  // substitute tables[pair.first] by the result
495  if (tables[ti] && is_t_new[ti]) {
496  delete tables[ti];
498  }
499 
500  if (tables[tj] && is_t_new[tj]) {
501  delete tables[tj];
503  }
504 
505  tables[ti] = new_seq;
507  is_t_new[ti] = true;
508 
509  tables[tj] = nullptr;
510 
511  // remove all the pairs involving tj in the priority queue
512  for (Size ind = 0; ind < tj; ++ind) {
513  if (tables[ind] != nullptr) {
514  pair.first = ind;
515  queue.erase(pair);
516  }
517  }
518 
519  pair.first = tj;
520 
521  for (Size ind = tj + 1; ind < tabsize; ++ind) {
522  if (tables[ind] != nullptr) {
523  pair.second = ind;
524  queue.erase(pair);
525  }
526  }
527 
528  // update the "combined" size of all the pairs involving "new_seq"
529  {
530  pair.second = ti;
531  Size newsize;
532 
533  for (Size ind = Size(0); ind < ti; ++ind) {
534  if (tables[ind] != nullptr) {
535  pair.first = ind;
538  }
539  }
540 
541  pair.first = ti;
542 
543  for (Size ind = ti + 1; ind < tabsize; ++ind) {
544  if (tables[ind] != nullptr) {
545  pair.second = ind;
548  }
549  }
550  }
551  }
552 
553  // here, there remains only one nonzero pointer in tables:
554  // the result of our combination
555  Size k = Size(0);
556  while (!tables[k])
557  ++k;
558 
559  delete tables[k];
560 
561  return std::pair< long, long >(long(max_memory), long(current_memory));
562  }
563 
564  // returns the memory consumption used during the combination
565  template < typename GUM_SCALAR, template < typename > class TABLE >
566  std::pair< long, long >
568  const Set< const TABLE< GUM_SCALAR >* >& set) const {
569  // check if the set passed in argument is empty.
570  if (set.size() < 2) return std::pair< long, long >(0, 0);
571 
572  // create the set of sets of discrete variables involved in the tables
573  Set< const Sequence< const DiscreteVariable* >* > var_set(set.size());
574 
575  for (const auto ptrTab: set) {
577  }
578 
579  return memoryUsage(var_set);
580  }
581 
582 } /* namespace gum */
583 
584 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:669