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