aGrUM 3.1.1
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-2026 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-2026 *
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
41#pragma once
42
43
50
52#ifndef DOXYGEN_SHOULD_SKIP_THIS
53
54# include <limits>
55
56# include <agrum/agrum.h>
57
60
61namespace gum {
62
64 template < class TABLE >
66 const TABLE&)) :
67 MultiDimCombination< TABLE >(), _combine_(combine) {
68 GUM_CONSTRUCTOR(MultiDimCombinationDefault);
69 }
70
72 template < class TABLE >
73 MultiDimCombinationDefault< TABLE >::MultiDimCombinationDefault(
74 const MultiDimCombinationDefault< TABLE >& from) :
75 MultiDimCombination< TABLE >(), _combine_(from._combine_) {
76 // for debugging purposes
77 GUM_CONS_CPY(MultiDimCombinationDefault);
78 }
79
81 template < class TABLE >
82 MultiDimCombinationDefault< TABLE >::~MultiDimCombinationDefault() {
83 // for debugging purposes
84 GUM_DESTRUCTOR(MultiDimCombinationDefault);
85 }
86
88 template < class TABLE >
89 MultiDimCombinationDefault< TABLE >* MultiDimCombinationDefault< TABLE >::clone() const {
90 return new MultiDimCombinationDefault< TABLE >(_combine_);
91 }
92
94 template < class TABLE >
95 void MultiDimCombinationDefault< TABLE >::setCombinationFunction(TABLE (*combine)(const TABLE&,
96 const TABLE&)) {
97 _combine_ = combine;
98 }
99
101 template < class TABLE >
102 TABLE (*MultiDimCombinationDefault< TABLE >::combinationFunction())(const TABLE&, const TABLE&) {
103 return _combine_;
104 }
105
107 template < class TABLE >
108 void MultiDimCombinationDefault< TABLE >::execute(TABLE& container,
109 const Set< const TABLE* >& set) const {
110 TABLE* res = execute(set);
111 container = std::move(*res);
112 delete (res);
113 }
114
116 template < class TABLE >
117 TABLE* MultiDimCombinationDefault< TABLE >::execute(const Set< const TABLE* >& set) const {
118 // check if the set passed in argument is empty. If so, raise an exception
119 if (set.size() < 2) {
121 "the set passed to a MultiDimCombinationDefault"
122 " should at least contain two elements");
123 }
124
125 // create a vector with all the tables stored as multidims
126 std::vector< const IScheduleMultiDim* > tables;
127 tables.reserve(set.size());
128 for (const auto table: set) {
129 tables.push_back(new ScheduleMultiDim< TABLE >(*table, false));
130 }
131
132 // get the set of operations to perform and execute them
133 auto ops_plus_res = operations(tables);
134 for (auto op: ops_plus_res.first) {
135 op->execute();
136 }
137
138 // get the schedule multidim of the last combination and save it
139 auto& schedule_result = const_cast< ScheduleMultiDim< TABLE >& >(
140 static_cast< const ScheduleMultiDim< TABLE >& >(*ops_plus_res.second));
141
142 // note that, as ScheduleCombinations always produce new freshly allocated
143 // tables, we can safely export the multiDims of their results
144 auto result = schedule_result.exportMultiDim();
145
146 // delete all the operations created as well as all the schedule tables
147 _freeData_(tables, ops_plus_res.first);
148
149 return result;
150 }
151
152 // returns the result of the combination
153 template < class TABLE >
154 double MultiDimCombinationDefault< TABLE >::nbOperations(
155 const Set< const Sequence< const DiscreteVariable* >* >& set) const {
156 // check if the set passed in argument is empty.
157 if (set.size() < 2) return 0.0;
158
159 // create a vector with all the tables stored as multidims
160 std::vector< const IScheduleMultiDim* > tables;
161 tables.reserve(set.size());
162 for (const auto ptrVars: set) {
163 tables.push_back(new ScheduleMultiDim< TABLE >(*ptrVars));
164 }
165
166 // get the set of operations to perform and compute their number of operations
167 auto ops_plus_res = operations(tables);
168 double nb_operations = 0.0;
169 for (const auto op: ops_plus_res.first) {
170 nb_operations += op->nbOperations();
171 }
172
173 // delete all the operations created as well as all the schedule tables
174 _freeData_(tables, ops_plus_res.first);
175
176 return nb_operations;
177 }
178
179 // returns the result of the combination
180 template < class TABLE >
181 double MultiDimCombinationDefault< TABLE >::nbOperations(const Set< const TABLE* >& set) const {
182 // check if the set passed in argument is empty.
183 if (set.size() < 2) return 0.0;
184
185 // create the set of sets of discrete variables involved in the tables
186 Set< const Sequence< const DiscreteVariable* >* > var_set(set.size());
187
188 for (const auto ptrTab: set) {
189 var_set << &(ptrTab->variablesSequence());
190 }
191
192 return nbOperations(var_set);
193 }
194
195 // returns the memory consumption used during the combination
196 template < class TABLE >
197 std::pair< double, double > MultiDimCombinationDefault< TABLE >::memoryUsage(
198 const Set< const Sequence< const DiscreteVariable* >* >& set) const {
199 // check if the set passed in argument is empty.
200 if (set.size() < 2) return {0.0, 0.0};
201
202 // create a vector with all the tables stored as multidims
203 std::vector< const IScheduleMultiDim* > tables;
204 tables.reserve(set.size());
205 for (const auto ptrVars: set) {
206 tables.push_back(new ScheduleMultiDim< TABLE >(*ptrVars));
207 }
208
209 // get the set of operations to perform and compute their memory consumption
210 auto ops_plus_res = operations(tables);
211
212 double max_memory = 0.0;
213 double end_memory = 0.0;
214
215 for (const auto op: ops_plus_res.first) {
216 const auto usage = op->memoryUsage();
217 if (end_memory + usage.first > max_memory) max_memory = end_memory + usage.first;
218 end_memory += usage.second;
219 }
220
221 // delete all the operations created as well as all the schedule tables
222 _freeData_(tables, ops_plus_res.first);
223
224 return {max_memory, end_memory};
225 }
226
227 // returns the memory consumption used during the combination
228 template < class TABLE >
229 std::pair< double, double >
230 MultiDimCombinationDefault< TABLE >::memoryUsage(const Set< const TABLE* >& set) const {
231 // check if the set passed in argument is empty.
232 if (set.size() < 2) return {0.0, 0.0};
233
234 // create the set of sets of discrete variables involved in the tables
235 Set< const Sequence< const DiscreteVariable* >* > var_set(set.size());
236
237 for (const auto ptrTab: set) {
238 var_set << &(ptrTab->variablesSequence());
239 }
240
241 return memoryUsage(var_set);
242 }
243
244 // returns the domain size of the Cartesian product of the union of all the
245 // variables in seq1 and seq2
246 template < class TABLE >
247 double
248 MultiDimCombinationDefault< TABLE >::_combinedSize_(const IScheduleMultiDim& table1,
249 const IScheduleMultiDim& table2) const {
250 auto size = double(table1.domainSize());
251 const auto& vars1 = table1.variablesSequence();
252 const auto& vars2 = table2.variablesSequence();
253 for (const auto ptrVar: vars2)
254 if (!vars1.exists(ptrVar)) size *= double(ptrVar->domainSize());
255
256 return size;
257 }
258
259 // returns the set of operations to perform to make the combination
260 template < class TABLE >
261 std::pair< std::vector< ScheduleOperator* >, const IScheduleMultiDim* >
262 MultiDimCombinationDefault< TABLE >::operations(
263 const std::vector< const IScheduleMultiDim* >& original_tables,
264 const bool is_result_persistent) const {
265 // check if the set passed in argument is empty.
266 const Size tabsize = original_tables.size();
267 if (tabsize < 2) return {};
268
269 // we copy the vector of tables to be combined because we will modify
270 // it during the combination process
271 std::vector< const IScheduleMultiDim* > tables = original_tables;
272
273 // create the resulting set of operations to execute to perform the combination
274 std::vector< ScheduleOperator* > operations;
275 operations.reserve(2 * tables.size());
276
277 // create a vector indicating whether the elements in Vector tables are
278 // freshly created ScheduleMultiDim* resulting from the combination of
279 // some tables or if they were added by the user into the set of tables
280 // to combine
281 std::vector< bool > is_t_new(tabsize, false);
282
283 // for each pair of tables (i,j), compute the size of the table that would
284 // operations from the combination of tables i and j and store the operations into a
285 // priorityQueue
286 std::pair< Size, Size > pair;
287 PriorityQueue< std::pair< Size, Size >, double > queue;
288
289 for (Size i = Size(0); i < tabsize; ++i) {
290 pair.first = i;
291
292 for (Size j = i + 1; j < tabsize; ++j) {
293 pair.second = j;
294 queue.insert(pair, _combinedSize_(*tables[i], *tables[j]));
295 }
296 }
297
298 // keep track of the result of the last combination performed as well as of
299 // the operation that created it
300 const IScheduleMultiDim* resulting_table = nullptr;
301 ScheduleOperator* resulting_op = nullptr;
302
303 // now parse the priority queue: the top element (i,j) gives the combination
304 // to perform. When the operations R has been computed,substitute i by R,
305 // remove table j and recompute all the priorities of all the pairs (R,k)
306 // still available.
307 for (Size k = 1; k < tabsize; ++k) {
308 // get the combination to perform and save it
309 pair = queue.pop();
310 const Size ti = pair.first;
311 const Size tj = pair.second;
312
313 // compute the operations and free the temporary tables
314 auto combination = new ScheduleBinaryCombination< TABLE, TABLE, TABLE >(
315 static_cast< const ScheduleMultiDim< TABLE >& >(*tables[ti]),
316 static_cast< const ScheduleMultiDim< TABLE >& >(*tables[tj]),
317 _combine_);
318 operations.push_back(combination);
319 resulting_table = &combination->result();
320 resulting_op = combination;
321
322 // add operations to remove the temporary tables
323 if (is_t_new[ti]) {
324 auto deletion = new ScheduleDeletion< TABLE >(
325 static_cast< const ScheduleMultiDim< TABLE >& >(*tables[ti]));
326 operations.push_back(deletion);
327 }
328 if (is_t_new[tj]) {
329 auto deletion = new ScheduleDeletion< TABLE >(
330 static_cast< const ScheduleMultiDim< TABLE >& >(*tables[tj]));
331 operations.push_back(deletion);
332 }
333
334 // substitute ti by result and remove tj
335 tables[ti] = resulting_table;
336 is_t_new[ti] = true;
337 tables[tj] = nullptr;
338
339 // remove all the pairs involving tj in the priority queue
340 for (Size ind = 0; ind < tj; ++ind) {
341 if (tables[ind] != nullptr) {
342 pair.first = ind;
343 queue.erase(pair);
344 }
345 }
346
347 pair.first = tj;
348 for (Size ind = tj + 1; ind < tabsize; ++ind) {
349 if (tables[ind] != nullptr) {
350 pair.second = ind;
351 queue.erase(pair);
352 }
353 }
354
355 // update the "combined" size of all the pairs involving ti (i.e., result)
356 {
357 pair.second = ti;
358 for (Size ind = 0; ind < ti; ++ind) {
359 if (tables[ind] != nullptr) {
360 pair.first = ind;
361 queue.setPriority(pair, _combinedSize_(*resulting_table, *(tables[ind])));
362 }
363 }
364
365 pair.first = ti;
366 for (Size ind = ti + 1; ind < tabsize; ++ind) {
367 if (tables[ind] != nullptr) {
368 pair.second = ind;
369 queue.setPriority(pair, _combinedSize_(*resulting_table, *(tables[ind])));
370 }
371 }
372 }
373 }
374
375 // if necessary, make the resulting table persistent
376 if (is_result_persistent) { resulting_op->makeResultsPersistent(true); }
377
378 return {operations, resulting_table};
379 }
380
382 template < class TABLE >
383 std::pair< std::vector< ScheduleOperator* >, const IScheduleMultiDim* >
384 MultiDimCombinationDefault< TABLE >::operations(const Set< const IScheduleMultiDim* >& set,
385 const bool is_result_persistent) const {
386 std::vector< const IScheduleMultiDim* > vect;
387 vect.reserve(set.size());
388 for (const auto elt: set) {
389 vect.push_back(elt);
390 }
391 return operations(vect, is_result_persistent);
392 }
393
395 template < class TABLE >
396 void MultiDimCombinationDefault< TABLE >::_freeData_(
397 std::vector< const IScheduleMultiDim* >& tables,
398 std::vector< ScheduleOperator* >& operations) const {
399 for (auto op: operations)
400 delete op;
401
402 for (auto table: tables)
403 delete table;
404 }
405
406} /* namespace gum */
407
408#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:76
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
priority queues (in which an element cannot appear more than once)