aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
operatorPattern4MultiDimArray.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
48
49// check if we allowed these patterns to be used
50#ifndef GUM_OPERATOR_PATTERN_ALLOWED
51
52// #warning To use operatorPattern4MultiDimArray.h, you must define
53// GUM_OPERATOR_PATTERN_ALLOWED
54
55#else
56
57namespace gum {
58
59 // a specialized function for combining two multiDimArrays
60
61# ifdef GUM_MULTI_DIM_OPERATOR_NAME
62# define GUM_MULTI_DIM_OPERATOR_TYPE T
63
64 template < typename T >
65 MultiDimArray< T >* GUM_MULTI_DIM_OPERATOR_NAME(const MultiDimArray< T >* t1,
66 const MultiDimArray< T >* t2)
67# endif
68
69 // clang-format off
70
71#ifdef GUM_MULTI_DIM_OPERATOR_POINTER_NAME
72#define GUM_MULTI_DIM_OPERATOR_TYPE T *
73 template <typename T>
74 MultiDimArray<T*>* GUM_MULTI_DIM_OPERATOR_POINTER_NAME(
75 const MultiDimArray<T*>* t1, const MultiDimArray<T*>* t2 )
76#endif
77
78#ifdef GUM_MULTI_DIM_OPERATOR_NAME_F
79#define GUM_MULTI_DIM_OPERATOR_TYPE T
80 template <typename T>
81 MultiDimArray<T>* GUM_MULTI_DIM_OPERATOR_NAME_F(
82 const MultiDimArray<T>* t1,
83 const MultiDimArray<T>* t2,
84 const T ( *f )( const T&, const T& ) )
85#endif
86
87#ifdef GUM_MULTI_DIM_OPERATOR_POINTER_NAME_F
88#define GUM_MULTI_DIM_OPERATOR_TYPE T *
89 template <typename T>
90 MultiDimArray<T*>* GUM_MULTI_DIM_OPERATOR_POINTER_NAME_F(
91 const MultiDimArray<T*>* t1,
92 const MultiDimArray<T*>* t2,
93 const T* ( *f )( const T*, const T* ) )
94#endif
95
96#ifdef GUM_MULTI_DIM_OPERATOR_IMPL2ARRAY_NAME
97#define GUM_MULTI_DIM_OPERATOR_TYPE T
98 template <typename T>
99 MultiDimImplementation<T>* GUM_MULTI_DIM_OPERATOR_IMPL2ARRAY_NAME(
100 const MultiDimImplementation<T>* tt1,
101 const MultiDimImplementation<T>* tt2 )
102#endif
103
104#ifdef GUM_MULTI_DIM_OPERATOR_POINTER_IMPL2ARRAY_NAME
105#define GUM_MULTI_DIM_OPERATOR_TYPE T *
106 template <typename T>
108 GUM_MULTI_DIM_OPERATOR_POINTER_IMPL2ARRAY_NAME(
110 const MultiDimImplementation<T*>* tt2 )
111#endif
112
113 // clang-format on
114
115 {
116
117# ifdef GUM_MULTI_DIM_OPERATOR_IMPL2ARRAY_NAME
118 const MultiDimArray< T >* t1 = reinterpret_cast< const MultiDimArray< T >* >(tt1);
119 const MultiDimArray< T >* t2 = reinterpret_cast< const MultiDimArray< T >* >(tt2);
120# endif
121
122# ifdef GUM_MULTI_DIM_OPERATOR_POINTER_IMPL2ARRAY_NAME
123 const MultiDimArray< T* >* t1 = reinterpret_cast< const MultiDimArray< T* >* >(tt1);
124 const MultiDimArray< T* >* t2 = reinterpret_cast< const MultiDimArray< T* >* >(tt2);
125# endif
126
127 // get the variables of the tables
128 const Sequence< const DiscreteVariable* >& t1_vars = t1->variablesSequence();
129 const Sequence< const DiscreteVariable* >& t2_vars = t2->variablesSequence();
130
131 // get the domain size of the tables' variables
133 {
134 Idx current_offset = 1;
135
136 for (const auto var: t1_vars) {
137 t1_offsets.insert(var, current_offset);
138 current_offset *= var->domainSize();
139 }
140 }
142 {
143 Idx current_offset = 1;
144
145 for (const auto var: t2_vars) {
146 t2_offsets.insert(var, current_offset);
147 current_offset *= var->domainSize();
148 }
149 }
150
151 // we divide the variables of t1 and t2 into 3 separate sets: those that
152 // belong only to t1 (variables t1_alone_xxx), those that belong only to t2
153 // (variables t2_alone_xxx) and those that belong to both tables (variables
154 // t1_and_t2_xxx). For each set, we define how an increment of a given
155 // variable of the set changes the offset in the corresponding table
156 // (variable txxx_offset) and what is the domain size of the variable
157 // (txxx_domain). In addition, we compute the domain size of the Cartesian
158 // product of the variables in each of the 3 sets. Given these data, we
159 // will be able to parse both t1, t2 and the result table t1+t2 without
160 // resorting to instantiations.
161 std::vector< Idx > t1_alone_offset;
162 std::vector< Idx > t1_alone_domain;
163 Idx t1_alone_domain_size = 1;
164
165 std::vector< Idx > t2_alone_offset;
166 std::vector< Idx > t2_alone_domain;
167 Idx t2_alone_domain_size = 1;
168
169 std::vector< Idx > t1_and_t2_1_offset;
170 std::vector< Idx > t1_and_t2_2_offset;
171 std::vector< Idx > t1_and_t2_domain;
172 std::vector< const DiscreteVariable* > t1_and_t2_vars;
173 Idx t1_and_t2_domain_size = 1;
174
175 {
176 for (const auto var: t1_vars)
177 if (t2_vars.exists(var)) {
178 t1_and_t2_domain.push_back(var->domainSize());
179 t1_and_t2_1_offset.push_back(t1_offsets[var]);
180 t1_and_t2_2_offset.push_back(t2_offsets[var]);
181 t1_and_t2_vars.push_back(var);
182 t1_and_t2_domain_size *= var->domainSize();
183 } else {
184 t1_alone_domain.push_back(var->domainSize());
185 t1_alone_offset.push_back(t1_offsets[var]);
186 t1_alone_domain_size *= var->domainSize();
187 }
188
189 for (const auto var: t2_vars)
190 if (!t1_vars.exists(var)) {
191 t2_alone_domain.push_back(var->domainSize());
192 t2_alone_offset.push_back(t2_offsets[var]);
193 t2_alone_domain_size *= var->domainSize();
194 }
195 }
196
197 // a Boolean indicating whether the variables that t1 and t2 have in common
198 // are the first variables and are in the same order. When this is true,
199 // computations can be performed faster
200 bool t1_and_t2_begin_vars = false;
201
202 if (t1_and_t2_vars.size()) {
203 unsigned int nb_t1_t2_vars = 0;
204
205 for (auto iter = t1_vars.begin(); nb_t1_t2_vars != t1_and_t2_vars.size();
206 ++iter, ++nb_t1_t2_vars)
207 if (*iter != t1_and_t2_vars[nb_t1_t2_vars]) break;
208
209 if (nb_t1_t2_vars == t1_and_t2_vars.size()) {
210 nb_t1_t2_vars = 0;
211
212 for (auto iter = t2_vars.begin(); nb_t1_t2_vars != t1_and_t2_vars.size();
213 ++iter, ++nb_t1_t2_vars)
214 if (*iter != t1_and_t2_vars[nb_t1_t2_vars]) break;
215
216 if (nb_t1_t2_vars == t1_and_t2_vars.size()) { t1_and_t2_begin_vars = true; }
217 }
218 }
219
220 // when we will parse t1 and t2 to fill the result table t1+t2, we will use
221 // variables txxx_value : at the beginning they are initialized to the
222 // domain size of the variables (which are, themselves initialized to 0).
223 // Each time we increment a variable (that is, we increase the offset of
224 // the table by txxx_offset), its corresponding txxx_value is decreased by
225 // 1. When the latter is equal to 0, this means that the variable itself
226 // should be reinitialized to 0 as well and that the next variable of the
227 // table should be increased (that is, this is similar to increasing 9 to
228 // 10). As such the offset of txxx should be decreased by txxx_offset * the
229 // domain size of the variable. This quantity is precisely what is stored
230 // into variables txxx_down.
231 std::vector< Idx > t1_and_t2_value = t1_and_t2_domain;
232 std::vector< Idx > t1_and_t2_1_down = t1_and_t2_1_offset;
233 std::vector< Idx > t1_and_t2_2_down = t1_and_t2_2_offset;
234
235 for (unsigned int i = 0; i < t1_and_t2_domain.size(); ++i) {
236 t1_and_t2_1_down[i] *= (t1_and_t2_domain[i] - 1);
237 t1_and_t2_2_down[i] *= (t1_and_t2_domain[i] - 1);
238 }
239
240 std::vector< Idx > t1_alone_value = t1_alone_domain;
241 std::vector< Idx > t1_alone_down = t1_alone_offset;
242
243 for (unsigned int i = 0; i < t1_alone_domain.size(); ++i) {
244 t1_alone_down[i] *= (t1_alone_domain[i] - 1);
245 }
246
247 std::vector< Idx > t2_alone_value = t2_alone_domain;
248 std::vector< Idx > t2_alone_down = t2_alone_offset;
249
250 for (unsigned int i = 0; i < t2_alone_domain.size(); ++i) {
251 t2_alone_down[i] *= (t2_alone_domain[i] - 1);
252 }
253
254 // create a table "result" containing all the variables: the first
255 // variables are those that belong to both t1 and t2. The next variables
256 // are those that belong to t2 but not to t1. Finally, the last variables
257 // are those that belong to t1 but not t2. This order will be used in the
258 // next for loops.
261 result->beginMultipleChanges();
262
263 for (const auto var: t1_vars)
264 if (t2_vars.exists(var)) *result << *var;
265
266 for (const auto var: t2_vars)
267 if (!t1_vars.exists(var)) *result << *var;
268
269 for (const auto var: t1_vars)
270 if (!t2_vars.exists(var)) *result << *var;
271
272 result->endMultipleChanges();
273
274 // here we fill result. The idea is to use 3 loops. The innermost loop
275 // corresponds to the variables that belongs both to t1 and t2. The middle
276 // loop to the variables that belong to t2 but not to t1. Finally, the
277 // outer loop corresponds to the variables that belong to t1 but not t2.
278 GUM_MULTI_DIM_OPERATOR_TYPE* pt1
279 = const_cast< GUM_MULTI_DIM_OPERATOR_TYPE* >(&(t1->unsafeGet(0)));
280 GUM_MULTI_DIM_OPERATOR_TYPE* pt2
281 = const_cast< GUM_MULTI_DIM_OPERATOR_TYPE* >(&(t2->unsafeGet(0)));
282 GUM_MULTI_DIM_OPERATOR_TYPE* pres
283 = const_cast< GUM_MULTI_DIM_OPERATOR_TYPE* >(&(result->unsafeGet(0)));
284 GUM_MULTI_DIM_OPERATOR_TYPE* pt2_deb = pt2;
285 GUM_MULTI_DIM_OPERATOR_TYPE* pt1_alone_begin;
286
287 // test if all the variables in common in t1 and t2 are the first variables
288 // and are in the same order. In this case, we can speed-up the
289 // incrementation processes
290 if (t1_and_t2_begin_vars) {
291 for (Idx i = 0; i < t1_alone_domain_size; ++i) {
292 pt2 = pt2_deb;
293 pt1_alone_begin = pt1;
294
295 for (Idx j = 0; j < t2_alone_domain_size; ++j) {
296 pt1 = pt1_alone_begin;
297
298 for (Idx z = 0; z < t1_and_t2_domain_size; ++z) {
299 *pres = GUM_MULTI_DIM_OPERATOR(*pt1, *pt2);
300 ++pres;
301
302 // update the offset of both t1 and t2
303 ++pt1;
304 ++pt2;
305 }
306 }
307 }
308 } else {
309 Idx t1_offset = 0;
310 Idx t2_offset = 0;
311 Idx t1_alone_begin_offset = 0;
312
313 for (Idx i = 0; i < t1_alone_domain_size; ++i) {
314 t2_offset = 0;
315 t1_alone_begin_offset = t1_offset;
316
317 for (Idx j = 0; j < t2_alone_domain_size; ++j) {
318 t1_offset = t1_alone_begin_offset;
319
320 for (Idx z = 0; z < t1_and_t2_domain_size; ++z) {
321 *pres = GUM_MULTI_DIM_OPERATOR(pt1[t1_offset], pt2[t2_offset]);
322 ++pres;
323
324 // update the offset of both t1 and t2
325 for (unsigned int k = 0; k < t1_and_t2_value.size(); ++k) {
326 if (--t1_and_t2_value[k]) {
327 t1_offset += t1_and_t2_1_offset[k];
328 t2_offset += t1_and_t2_2_offset[k];
329 break;
330 }
331
332 t1_and_t2_value[k] = t1_and_t2_domain[k];
333 t1_offset -= t1_and_t2_1_down[k];
334 t2_offset -= t1_and_t2_2_down[k];
335 }
336 }
337
338 // update the offset of t2 alone
339 for (unsigned int k = 0; k < t2_alone_value.size(); ++k) {
340 if (--t2_alone_value[k]) {
341 t2_offset += t2_alone_offset[k];
342 break;
343 }
344
345 t2_alone_value[k] = t2_alone_domain[k];
346 t2_offset -= t2_alone_down[k];
347 }
348 }
349
350 // update the offset of t1 alone
351 for (unsigned int k = 0; k < t1_alone_value.size(); ++k) {
352 if (--t1_alone_value[k]) {
353 t1_offset += t1_alone_offset[k];
354 break;
355 }
356
357 t1_alone_value[k] = t1_alone_domain[k];
358 t1_offset -= t1_alone_down[k];
359 }
360 }
361 }
362
363 return result;
364 }
365
366# undef GUM_MULTI_DIM_OPERATOR_TYPE
367} // namespace gum
368
369#endif /* GUM_OPERATOR_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.
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