aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
multiDimCombinationDefault_tpl.h
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2025 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2025 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40#pragma once
41
42
49
50#ifndef DOXYGEN_SHOULD_SKIP_THIS
51
52# include <limits>
53
54# include <agrum/agrum.h>
55
58
59namespace gum {
60
62 template < class TABLE >
64 const TABLE&)) :
65 MultiDimCombination< TABLE >(), _combine_(combine) {
66 GUM_CONSTRUCTOR(MultiDimCombinationDefault);
67 }
68
70 template < class TABLE >
71 MultiDimCombinationDefault< TABLE >::MultiDimCombinationDefault(
72 const MultiDimCombinationDefault< TABLE >& from) :
73 MultiDimCombination< TABLE >(), _combine_(from._combine_) {
74 // for debugging purposes
75 GUM_CONS_CPY(MultiDimCombinationDefault);
76 }
77
79 template < class TABLE >
80 MultiDimCombinationDefault< TABLE >::~MultiDimCombinationDefault() {
81 // for debugging purposes
82 GUM_DESTRUCTOR(MultiDimCombinationDefault);
83 }
84
86 template < class TABLE >
87 MultiDimCombinationDefault< TABLE >* MultiDimCombinationDefault< TABLE >::clone() const {
88 return new MultiDimCombinationDefault< TABLE >(_combine_);
89 }
90
92 template < class TABLE >
93 void MultiDimCombinationDefault< TABLE >::setCombinationFunction(TABLE (*combine)(const TABLE&,
94 const TABLE&)) {
95 _combine_ = combine;
96 }
97
99 template < class TABLE >
100 INLINE TABLE (*MultiDimCombinationDefault< TABLE >::combinationFunction())(const TABLE&,
101 const TABLE&) {
102 return _combine_;
103 }
104
106 template < class TABLE >
107 INLINE void MultiDimCombinationDefault< TABLE >::execute(TABLE& container,
108 const Set< const TABLE* >& set) const {
109 TABLE* res = execute(set);
110 container = std::move(*res);
111 delete (res);
112 }
113
115 template < class TABLE >
116 TABLE* MultiDimCombinationDefault< TABLE >::execute(const Set< const TABLE* >& set) const {
117 // check if the set passed in argument is empty. If so, raise an exception
118 if (set.size() < 2) {
120 "the set passed to a MultiDimCombinationDefault"
121 " should at least contain two elements");
122 }
123
124 // create a vector with all the tables stored as multidims
125 std::vector< const IScheduleMultiDim* > tables;
126 tables.reserve(set.size());
127 for (const auto table: set) {
128 tables.push_back(new ScheduleMultiDim< TABLE >(*table, false));
129 }
130
131 // get the set of operations to perform and execute them
132 auto ops_plus_res = operations(tables);
133 for (auto op: ops_plus_res.first) {
134 op->execute();
135 }
136
137 // get the schedule multidim of the last combination and save it
138 auto& schedule_result = const_cast< ScheduleMultiDim< TABLE >& >(
139 static_cast< const ScheduleMultiDim< TABLE >& >(*ops_plus_res.second));
140
141 // note that, as ScheduleCombinations always produce new freshly allocated
142 // tables, we can safely export the multiDims of their results
143 auto result = schedule_result.exportMultiDim();
144
145 // delete all the operations created as well as all the schedule tables
146 _freeData_(tables, ops_plus_res.first);
147
148 return result;
149 }
150
151 // returns the result of the combination
152 template < class TABLE >
153 double MultiDimCombinationDefault< TABLE >::nbOperations(
154 const Set< const Sequence< const DiscreteVariable* >* >& set) const {
155 // check if the set passed in argument is empty.
156 if (set.size() < 2) return 0.0;
157
158 // create a vector with all the tables stored as multidims
159 std::vector< const IScheduleMultiDim* > tables;
160 tables.reserve(set.size());
161 for (const auto ptrVars: set) {
162 tables.push_back(new ScheduleMultiDim< TABLE >(*ptrVars));
163 }
164
165 // get the set of operations to perform and compute their number of operations
166 auto ops_plus_res = operations(tables);
167 double nb_operations = 0.0;
168 for (const auto op: ops_plus_res.first) {
169 nb_operations += op->nbOperations();
170 }
171
172 // delete all the operations created as well as all the schedule tables
173 _freeData_(tables, ops_plus_res.first);
174
175 return nb_operations;
176 }
177
178 // returns the result of the combination
179 template < class TABLE >
180 double MultiDimCombinationDefault< TABLE >::nbOperations(const Set< const TABLE* >& set) const {
181 // check if the set passed in argument is empty.
182 if (set.size() < 2) return 0.0;
183
184 // create the set of sets of discrete variables involved in the tables
185 Set< const Sequence< const DiscreteVariable* >* > var_set(set.size());
186
187 for (const auto ptrTab: set) {
188 var_set << &(ptrTab->variablesSequence());
189 }
190
191 return nbOperations(var_set);
192 }
193
194 // returns the memory consumption used during the combination
195 template < class TABLE >
196 std::pair< double, double > MultiDimCombinationDefault< TABLE >::memoryUsage(
197 const Set< const Sequence< const DiscreteVariable* >* >& set) const {
198 // check if the set passed in argument is empty.
199 if (set.size() < 2) return {0.0, 0.0};
200
201 // create a vector with all the tables stored as multidims
202 std::vector< const IScheduleMultiDim* > tables;
203 tables.reserve(set.size());
204 for (const auto ptrVars: set) {
205 tables.push_back(new ScheduleMultiDim< TABLE >(*ptrVars));
206 }
207
208 // get the set of operations to perform and compute their memory consumption
209 auto ops_plus_res = operations(tables);
210
211 double max_memory = 0.0;
212 double end_memory = 0.0;
213
214 for (const auto op: ops_plus_res.first) {
215 const auto usage = op->memoryUsage();
216 if (end_memory + usage.first > max_memory) max_memory = end_memory + usage.first;
217 end_memory += usage.second;
218 }
219
220 // delete all the operations created as well as all the schedule tables
221 _freeData_(tables, ops_plus_res.first);
222
223 return {max_memory, end_memory};
224 }
225
226 // returns the memory consumption used during the combination
227 template < class TABLE >
228 std::pair< double, double >
229 MultiDimCombinationDefault< TABLE >::memoryUsage(const Set< const TABLE* >& set) const {
230 // check if the set passed in argument is empty.
231 if (set.size() < 2) return {0.0, 0.0};
232
233 // create the set of sets of discrete variables involved in the tables
234 Set< const Sequence< const DiscreteVariable* >* > var_set(set.size());
235
236 for (const auto ptrTab: set) {
237 var_set << &(ptrTab->variablesSequence());
238 }
239
240 return memoryUsage(var_set);
241 }
242
243 // returns the domain size of the Cartesian product of the union of all the
244 // variables in seq1 and seq2
245 template < class TABLE >
246 INLINE double
247 MultiDimCombinationDefault< TABLE >::_combinedSize_(const IScheduleMultiDim& table1,
248 const IScheduleMultiDim& table2) const {
249 auto size = double(table1.domainSize());
250 const auto& vars1 = table1.variablesSequence();
251 const auto& vars2 = table2.variablesSequence();
252 for (const auto ptrVar: vars2)
253 if (!vars1.exists(ptrVar)) size *= double(ptrVar->domainSize());
254
255 return size;
256 }
257
258 // returns the set of operations to perform to make the combination
259 template < class TABLE >
260 std::pair< std::vector< ScheduleOperator* >, const IScheduleMultiDim* >
261 MultiDimCombinationDefault< TABLE >::operations(
262 const std::vector< const IScheduleMultiDim* >& original_tables,
263 const bool is_result_persistent) const {
264 // check if the set passed in argument is empty.
265 const Size tabsize = original_tables.size();
266 if (tabsize < 2) return {};
267
268 // we copy the vector of tables to be combined because we will modify
269 // it during the combination process
270 std::vector< const IScheduleMultiDim* > tables = original_tables;
271
272 // create the resulting set of operations to execute to perform the combination
273 std::vector< ScheduleOperator* > operations;
274 operations.reserve(2 * tables.size());
275
276 // create a vector indicating whether the elements in Vector tables are
277 // freshly created ScheduleMultiDim* resulting from the combination of
278 // some tables or if they were added by the user into the set of tables
279 // to combine
280 std::vector< bool > is_t_new(tabsize, false);
281
282 // for each pair of tables (i,j), compute the size of the table that would
283 // operations from the combination of tables i and j and store the operations into a
284 // priorityQueue
285 std::pair< Size, Size > pair;
286 PriorityQueue< std::pair< Size, Size >, double > queue;
287
288 for (Size i = Size(0); i < tabsize; ++i) {
289 pair.first = i;
290
291 for (Size j = i + 1; j < tabsize; ++j) {
292 pair.second = j;
293 queue.insert(pair, _combinedSize_(*tables[i], *tables[j]));
294 }
295 }
296
297 // keep track of the result of the last combination performed as well as of
298 // the operation that created it
299 const IScheduleMultiDim* resulting_table = nullptr;
300 ScheduleOperator* resulting_op = nullptr;
301
302 // now parse the priority queue: the top element (i,j) gives the combination
303 // to perform. When the operations R has been computed,substitute i by R,
304 // remove table j and recompute all the priorities of all the pairs (R,k)
305 // still available.
306 for (Size k = 1; k < tabsize; ++k) {
307 // get the combination to perform and save it
308 pair = queue.pop();
309 const Size ti = pair.first;
310 const Size tj = pair.second;
311
312 // compute the operations and free the temporary tables
313 auto combination = new ScheduleBinaryCombination< TABLE, TABLE, TABLE >(
314 static_cast< const ScheduleMultiDim< TABLE >& >(*tables[ti]),
315 static_cast< const ScheduleMultiDim< TABLE >& >(*tables[tj]),
316 _combine_);
317 operations.push_back(combination);
318 resulting_table = &combination->result();
319 resulting_op = combination;
320
321 // add operations to remove the temporary tables
322 if (is_t_new[ti]) {
323 auto deletion = new ScheduleDeletion< TABLE >(
324 static_cast< const ScheduleMultiDim< TABLE >& >(*tables[ti]));
325 operations.push_back(deletion);
326 }
327 if (is_t_new[tj]) {
328 auto deletion = new ScheduleDeletion< TABLE >(
329 static_cast< const ScheduleMultiDim< TABLE >& >(*tables[tj]));
330 operations.push_back(deletion);
331 }
332
333 // substitute ti by result and remove tj
334 tables[ti] = resulting_table;
335 is_t_new[ti] = true;
336 tables[tj] = nullptr;
337
338 // remove all the pairs involving tj in the priority queue
339 for (Size ind = 0; ind < tj; ++ind) {
340 if (tables[ind] != nullptr) {
341 pair.first = ind;
342 queue.erase(pair);
343 }
344 }
345
346 pair.first = tj;
347 for (Size ind = tj + 1; ind < tabsize; ++ind) {
348 if (tables[ind] != nullptr) {
349 pair.second = ind;
350 queue.erase(pair);
351 }
352 }
353
354 // update the "combined" size of all the pairs involving ti (i.e., result)
355 {
356 pair.second = ti;
357 for (Size ind = 0; ind < ti; ++ind) {
358 if (tables[ind] != nullptr) {
359 pair.first = ind;
360 queue.setPriority(pair, _combinedSize_(*resulting_table, *(tables[ind])));
361 }
362 }
363
364 pair.first = ti;
365 for (Size ind = ti + 1; ind < tabsize; ++ind) {
366 if (tables[ind] != nullptr) {
367 pair.second = ind;
368 queue.setPriority(pair, _combinedSize_(*resulting_table, *(tables[ind])));
369 }
370 }
371 }
372 }
373
374 // if necessary, make the resulting table persistent
375 if (is_result_persistent) { resulting_op->makeResultsPersistent(true); }
376
377 return {operations, resulting_table};
378 }
379
381 template < class TABLE >
382 std::pair< std::vector< ScheduleOperator* >, const IScheduleMultiDim* >
383 MultiDimCombinationDefault< TABLE >::operations(const Set< const IScheduleMultiDim* >& set,
384 const bool is_result_persistent) const {
385 std::vector< const IScheduleMultiDim* > vect;
386 vect.reserve(set.size());
387 for (const auto elt: set) {
388 vect.push_back(elt);
389 }
390 return operations(vect, is_result_persistent);
391 }
392
394 template < class TABLE >
395 INLINE void MultiDimCombinationDefault< TABLE >::_freeData_(
396 std::vector< const IScheduleMultiDim* >& tables,
397 std::vector< ScheduleOperator* >& operations) const {
398 for (auto op: operations)
399 delete op;
400
401 for (auto table: tables)
402 delete table;
403 }
404
405} /* namespace gum */
406
407#endif /* DOXYGEN_SHOULD_SKIP_THIS */
Exception: the number of arguments passed to a function is not what was expected.
MultiDimCombinationDefault(TABLE(*combine)(const TABLE &, const TABLE &))
Default constructor.
A generic interface to combine efficiently several MultiDim tables.
#define GUM_ERROR(type, msg)
Definition exceptions.h:72
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
priority queues (in which an element cannot appear more than once)