aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
projectionPattern4MultiDimArray.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
41
49
50// check if we allowed these patterns to be used
51#ifndef GUM_PROJECTION_PATTERN_ALLOWED
52
53// #warning To use projectionPattern, you must define
54// GUM_PROJECTION_PATTERN_ALLOWED
55
56#else
57namespace gum {
58
59 // a specialized function for projecting a multiDimArray over a subset of its
60 // vars
61
62# ifdef GUM_MULTI_DIM_PROJECTION_NAME
63# define GUM_MULTI_DIM_PROJECTION_TYPE GUM_SCALAR
64
65 template < typename GUM_SCALAR >
66 MultiDimArray< GUM_SCALAR >* GUM_MULTI_DIM_PROJECTION_NAME(
67 const MultiDimArray< GUM_SCALAR >* table,
68 const gum::VariableSet& del_vars)
69# endif
70
71 // clang-format off
72
73#ifdef GUM_MULTI_DIM_PROJECTION_POINTER_NAME
74#define GUM_MULTI_DIM_PROJECTION_TYPE GUM_SCALAR *
75#define GUM_MULTI_DIM_PROJECTION_POINTER
76 template <typename GUM_SCALAR>
77 MultiDimArray<GUM_SCALAR*>* GUM_MULTI_DIM_PROJECTION_POINTER_NAME(
78 const MultiDimArray<GUM_SCALAR*>* table,
79 const VariableSet& del_vars )
80#endif
81
82#ifdef GUM_MULTI_DIM_PROJECTION_NAME_F
83#define GUM_MULTI_DIM_PROJECTION_TYPE GUM_SCALAR
84 template <typename GUM_SCALAR>
85 MultiDimArray<GUM_SCALAR>* GUM_MULTI_DIM_PROJECTION_NAME_F(
86 const MultiDimArray<GUM_SCALAR>* table,
87 const VariableSet& del_vars,
88 GUM_SCALAR ( *f )( const GUM_SCALAR&, const GUM_SCALAR& ) )
89#endif
90
91#ifdef GUM_MULTI_DIM_PROJECTION_POINTER_NAME_F
92#define GUM_MULTI_DIM_PROJECTION_TYPE GUM_SCALAR *
93#define GUM_MULTI_DIM_PROJECTION_POINTER
94 template <typename GUM_SCALAR>
95 MultiDimArray<GUM_SCALAR*>* GUM_MULTI_DIM_PROJECTION_POINTER_NAME_F(
96 const MultiDimArray<GUM_SCALAR*>* table,
97 const VariableSet& del_vars,
98 GUM_SCALAR* ( *f )( const GUM_SCALAR const*,
99 const GUM_SCALAR const* ) )
100#endif
101
102#ifdef GUM_MULTI_DIM_PROJECTION_IMPL2ARRAY_NAME
103#define GUM_MULTI_DIM_PROJECTION_TYPE GUM_SCALAR
104 template <typename GUM_SCALAR>
106 GUM_MULTI_DIM_PROJECTION_IMPL2ARRAY_NAME(
108 const VariableSet& del_vars )
109#endif
110
111#ifdef GUM_MULTI_DIM_PROJECTION_POINTER_IMPL2ARRAY_NAME
112#define GUM_MULTI_DIM_PROJECTION_TYPE GUM_SCALAR *
113#define GUM_MULTI_DIM_PROJECTION_POINTER
114 template <typename GUM_SCALAR>
116 GUM_MULTI_DIM_PROJECTION_POINTER_IMPL2ARRAY_NAME(
118 const VariableSet& del_vars )
119#endif
120
121 // clang-format on
122
123 {
124
125# ifdef GUM_MULTI_DIM_PROJECTION_IMPL2ARRAY_NAME
126 const MultiDimArray< GUM_SCALAR >* table
127 = reinterpret_cast< const MultiDimArray< GUM_SCALAR >* >(ttable);
128# endif
129
130# ifdef GUM_MULTI_DIM_PROJECTION_POINTER_IMPL2ARRAY_NAME
132 = reinterpret_cast< const MultiDimArray< GUM_SCALAR* >* >(ttable);
133# endif
134
135 // create the neutral element used to fill the result upon its
136 // creation
137 const GUM_SCALAR neutral_element = GUM_MULTI_DIM_PROJECTION_NEUTRAL;
138
139 // first, compute whether we should loop over table or over the projected
140 // table first to get a faster algorithm.
141 const Sequence< const DiscreteVariable* >& table_vars = table->variablesSequence();
142 bool need_swapping = table_vars.size() >= 2 * del_vars.size();
143
144 if (!need_swapping) {
145 // Compute the variables that belong to both the projection set and
146 // table. Store the domain size of the Cartesian product of these
147 // variables (result_domain_size) as well as the domain size of the
148 // Cartesian product of the variables of table that do not belong to
149 // projection set, i.e., those variables that belong to table but not to
150 // del_vars (table_alone_domain_size). In addition, store the number of
151 // increments in the computation loops at the end of the function before
152 // which the variables of the projection set need be incremented (vector
153 // before incr).
154 std::vector< Idx > table_and_result_offset;
155 std::vector< Idx > table_and_result_domain;
156 std::vector< Idx > before_incr;
157 unsigned int nb_positive_before_incr = 0;
158 Idx table_alone_domain_size = 1;
159 Idx result_domain_size = 1;
160 Idx table_domain_size = 1;
162 {
163 Idx tmp_before_incr = 1;
164 bool has_before_incr = false;
165
166 for (const auto var: table_vars) {
167 table_domain_size *= var->domainSize();
168
169 if (!del_vars.exists(var)) {
170 if (has_before_incr) {
171 before_incr.push_back(tmp_before_incr - 1);
172 has_before_incr = false;
173 ++nb_positive_before_incr;
174 } else {
175 before_incr.push_back(0);
176 }
177
178 table_and_result_domain.push_back(var->domainSize());
179 table_and_result_offset.push_back(result_domain_size);
180 result_domain_size *= var->domainSize();
181 tmp_before_incr = 1;
182 result_varSeq << var;
183 } else {
184 tmp_before_incr *= var->domainSize();
185 has_before_incr = true;
186 table_alone_domain_size *= var->domainSize();
187 }
188 }
189 }
190 std::vector< Idx > table_and_result_value = table_and_result_domain;
191 std::vector< Idx > current_incr = before_incr;
192 std::vector< Idx > table_and_result_down = table_and_result_offset;
193
194 for (unsigned int i = 0; i < table_and_result_down.size(); ++i) {
195 table_and_result_down[i] *= (table_and_result_domain[i] - 1);
196 }
197
198 // create a table "result" containing only the variables of the
199 // projection: the variables are stored in the order in which they appear
200 // in "table" Hence, ++ operations on an instantiation on table will more
201 // or less correspond to a ++ operation on an instantiation on result
204
205 if (!result_varSeq.size()) { return result; }
206
207 result->beginMultipleChanges();
208
209 for (const auto var: result_varSeq)
210 *result << *var;
211
212// fill the matrix with the neutral element
213# ifdef GUM_MULTI_DIM_PROJECTION_POINTER
214 result->endMultipleChanges();
215
216 for (Idx i = 0; i < result_domain_size; ++i) {
217 result->unsafeSet(i, new GUM_SCALAR(neutral_element));
218 }
219
220# else
221 result->endMultipleChanges(neutral_element);
222# endif
223
224 // compute the projection: first loop over the variables X's in table
225 // that do not belong to result and, for each value of these X's, loop
226 // over the variables in both table and result. As such, in the internal
227 // loop, the offsets of "result" need only be incremented as usually to
228 // parse appropriately this table. For result, the problem is slightly
229 // more complicated: in the outer for loop, we shall always reset
230 // resul_offset to 0. For the inner loop, result_offset should be
231 // incremented (++) only when t1 before_incr[xxx] steps in the loop have
232 // already been made.
233
234 // but before doing so, check whether there exist positive_before_incr.
235 // If this is not the case, optimize by not using before_incr at all
236 GUM_MULTI_DIM_PROJECTION_TYPE* pt
237 = const_cast< GUM_MULTI_DIM_PROJECTION_TYPE* >(&(table->unsafeGet(0)));
238 GUM_MULTI_DIM_PROJECTION_TYPE* pres
239 = const_cast< GUM_MULTI_DIM_PROJECTION_TYPE* >(&(result->unsafeGet(0)));
240 GUM_MULTI_DIM_PROJECTION_TYPE* pres_deb = pres;
241
242 if (!nb_positive_before_incr) {
243 for (Idx i = 0; i < table_alone_domain_size; ++i) {
244 for (Idx j = 0; j < result_domain_size; ++j) {
245# ifdef GUM_MULTI_DIM_PROJECTION_EFFECTIVE_TYPE
246 GUM_MULTI_DIM_PROJECTION(*pres, *pt);
247# else
248# ifdef GUM_MULTI_DIM_PROJECTION_POINTER
249 **pres = GUM_MULTI_DIM_PROJECTION(*pres, *pt);
250# else
251 *pres = GUM_MULTI_DIM_PROJECTION(*pres, *pt);
252# endif
253# endif
254
255 // update the offset of table and result
256 ++pt;
257 ++pres;
258 }
259
260 // update the offset of result
261 pres = pres_deb;
262 }
263 } else {
264 // here there are positive before_incr and we should use them to know
265 // when result_offset needs be changed
266 Idx result_offset = 0;
267
268 for (Idx i = 0; i < table_domain_size; ++i) {
269# ifdef GUM_MULTI_DIM_PROJECTION_EFFECTIVE_TYPE
270 GUM_MULTI_DIM_PROJECTION(pres[result_offset], *pt);
271# else
272# ifdef GUM_MULTI_DIM_PROJECTION_POINTER
273 *(pres[result_offset]) = GUM_MULTI_DIM_PROJECTION(pres[result_offset], *pt);
274# else
275 pres[result_offset] = GUM_MULTI_DIM_PROJECTION(pres[result_offset], *pt);
276# endif
277# endif
278
279 // update the offset of table
280 ++pt;
281
282 // update the offset of result
283 for (unsigned int k = 0; k < current_incr.size(); ++k) {
284 // check if we need modify result_offset
285 if (current_incr[k]) {
286 --current_incr[k];
287 break;
288 }
289
290 current_incr[k] = before_incr[k];
291
292 // here we shall modify result_offset
293 --table_and_result_value[k];
294
295 if (table_and_result_value[k]) {
296 result_offset += table_and_result_offset[k];
297 break;
298 }
299
300 table_and_result_value[k] = table_and_result_domain[k];
301 result_offset -= table_and_result_down[k];
302 }
303 }
304 }
305
306 return result;
307 } else { // need_swapping = true
308
309 // Compute the variables that are in t1 but not in t2. For these
310 // variables, get the increment in the offset of t1 that would result
311 // from an increment in one of these variables (vector t1_alone_offset).
312 // Also store the domain size of these variables (vector t1_alone_domain)
313 // and, for the computation loops at the end of this function, |variable|
314 // - the current values of these variables (vector t1_alone_value). All
315 // these vectors reference the variables of t1 \ t2 in the order in which
316 // they appear in seq1. Keep as well the size of the Cartesian product of
317 // these variables.
318 std::vector< Idx > table_alone_offset;
319 std::vector< Idx > table_alone_domain;
320 Idx offset = 1;
321 Idx table_alone_domain_size = 1;
322 HashTable< const DiscreteVariable*, Idx > var1offset(table_vars.size());
323
324 for (const auto var: table_vars) {
325 if (del_vars.exists(var)) {
326 table_alone_domain.push_back(var->domainSize());
327 table_alone_offset.push_back(offset);
328 table_alone_domain_size *= var->domainSize();
329 }
330
331 var1offset.insert(var, offset);
332 offset *= var->domainSize();
333 }
334
335 std::vector< Idx > table_alone_value = table_alone_domain;
336 std::vector< Idx > table_alone_down = table_alone_offset;
337
338 for (unsigned int i = 0; i < table_alone_down.size(); ++i)
339 table_alone_down[i] *= (table_alone_domain[i] - 1);
340
341 // Compute the same vectors for the variables that belong to both t1 and
342 // t2. In this case, All these vectors reference the variables in the
343 // order in which they appear in seq2. In addition, store the number of
344 // increments in the computation loops at the end of the function before
345 // which the variables of t1 cap t2 need be incremented (vector
346 // t1_and_t2_before incr). Similarly, store the number of such
347 // increments currently still needed before the next incrementation of
348 // the variables of t1 cap t2. Keep as well the size of the Cartesian
349 // product of these variables.
351 std::vector< Idx > table_and_result_offset;
352 std::vector< Idx > table_and_result_domain;
353 Idx result_domain_size = 1;
354 bool has_before_incr = false;
355 bool found_proj_var = false;
356
357 for (const auto var: table_vars) {
358 if (!del_vars.exists(var)) {
359 table_and_result_domain.push_back(var->domainSize());
360 table_and_result_offset.push_back(var1offset[var]);
361 found_proj_var = true;
362 result_domain_size *= var->domainSize();
363 result_varSeq << var;
364 } else {
365 if (found_proj_var) has_before_incr = true;
366 }
367 }
368
369 std::vector< Idx > table_and_result_value = table_and_result_domain;
370 std::vector< Idx > table_and_result_down = table_and_result_offset;
371
372 for (unsigned int i = 0; i < table_and_result_down.size(); ++i) {
373 table_and_result_down[i] *= (table_and_result_domain[i] - 1);
374 }
375
376 // create a table "result" containing only the variables of the
377 // projection: the variables are stored in the order in which they appear
378 // in "table" Hence, ++ operations on an instantiation on table will more
379 // or less correspond to a ++ operation on an instantiation on result
382 result->beginMultipleChanges();
383
384 for (const auto var: result_varSeq)
385 *result << *var;
386
387# ifdef GUM_MULTI_DIM_PROJECTION_POINTER
388 result->endMultipleChanges();
389
390 // fill the matrix with the neutral element
391 for (Idx i = 0; i < result_domain_size; ++i) {
392 result->unsafeSet(i, new GUM_SCALAR(neutral_element));
393 }
394
395# else
396 result->endMultipleChanges(neutral_element);
397# endif
398
399 // compute the sum: first loop over the variables X's both in table and
400 // in result and, for each value of these X's, loop over the variables
401 // that are in table but not result. As such, in the internal loop, there
402 // is no increment in the offset of "result", and in the outer loop, this
403 // offset is incremented using a simple ++ operator. For table, the
404 // problem is slightly more complicated: in the outer for loop, we shall
405 // increment the variables of table cap result according to vectors
406 // table_and_result_xxx. Each time a variable of these vectors has been
407 // incremented up to its max, we shall put it down to 0 and increment the
408 // next one, and so on. For the inner loop, this is similar except that
409 // we shall do these operations only when before_incr[xxx] steps in the
410 // loop have already been made.
411 GUM_MULTI_DIM_PROJECTION_TYPE* pt
412 = const_cast< GUM_MULTI_DIM_PROJECTION_TYPE* >(&(table->unsafeGet(0)));
413 GUM_MULTI_DIM_PROJECTION_TYPE* pres
414 = const_cast< GUM_MULTI_DIM_PROJECTION_TYPE* >(&(result->unsafeGet(0)));
415
416 // but before doing so, check whether there exist positive_before_incr.
417 // If this is not the case, optimize by not using before_incr at all
418 if (!has_before_incr) {
419 for (Idx i = 0; i < result_domain_size; ++i) {
420 for (Idx j = 0; j < table_alone_domain_size; ++j) {
421# ifdef GUM_MULTI_DIM_PROJECTION_EFFECTIVE_TYPE
422 GUM_MULTI_DIM_PROJECTION(*pres, *pt);
423# else
424# ifdef GUM_MULTI_DIM_PROJECTION_POINTER
425 **pres = GUM_MULTI_DIM_PROJECTION(*pres, *pt);
426# else
427 *pres = GUM_MULTI_DIM_PROJECTION(*pres, *pt);
428# endif
429# endif
430
431 // update the offset of table
432 ++pt;
433 }
434
435 // update the offset of result
436 ++pres;
437 }
438 } else {
439 // here there are positive before_incr and we should use them to know
440 // when result_offset needs be changed
441 Idx table_offset = 0;
442
443 for (Idx j = 0; j < result_domain_size; ++j) {
444 for (Idx i = 0; i < table_alone_domain_size; ++i) {
445# ifdef GUM_MULTI_DIM_PROJECTION_EFFECTIVE_TYPE
446 GUM_MULTI_DIM_PROJECTION(*pres, pt[table_offset]);
447# else
448# ifdef GUM_MULTI_DIM_PROJECTION_POINTER
449 **pres = GUM_MULTI_DIM_PROJECTION(*pres, pt[table_offset]);
450# else
451 *pres = GUM_MULTI_DIM_PROJECTION(*pres, pt[table_offset]);
452# endif
453# endif
454
455 // update the increment of table for the inner loop
456 for (unsigned int k = 0; k < table_alone_value.size(); ++k) {
457 --table_alone_value[k];
458
459 if (table_alone_value[k]) {
460 table_offset += table_alone_offset[k];
461 break;
462 }
463
464 table_alone_value[k] = table_alone_domain[k];
465 table_offset -= table_alone_down[k];
466 }
467 }
468
469 // update the offset of table for the outer loop
470 for (unsigned int k = 0; k < table_and_result_value.size(); ++k) {
471 --table_and_result_value[k];
472
473 if (table_and_result_value[k]) {
474 table_offset += table_and_result_offset[k];
475 break;
476 }
477
478 table_and_result_value[k] = table_and_result_domain[k];
479 table_offset -= table_and_result_down[k];
480 }
481
482 // update the offset of result for the outer loop
483 //++result_offset;
484 ++pres;
485 }
486 }
487
488 return result;
489 }
490 }
491
492# undef GUM_MULTI_DIM_PROJECTION_TYPE
493
494# ifdef GUM_MULTI_DIM_PROJECTION_POINTER
495# undef GUM_MULTI_DIM_PROJECTION_POINTER
496# endif
497} // namespace gum
498#endif /* GUM_PROJECTION_PATTERN_ALLOWED */
The class for generic Hash Tables.
Definition hashTable.h:637
Multidimensional matrix stored as an array in memory.
<agrum/base/multidim/multiDimImplementation.h>
void beginMultipleChanges() override
Call this method before doing important changes in this MultiDimContainer.
Size size() const noexcept
Returns the size of the sequence.
The generic class for storing (ordered) sequences of objects.
Definition sequence.h:972
Size Idx
Type for indexes.
Definition types.h:79
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
Set< const DiscreteVariable * > VariableSet