aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
operatorPattern4BaseName.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 operatorPattern4MultiDimBase.h, you must define
53// GUM_OPERATOR_PATTERN_ALLOWED
54
55#else
56
57namespace gum {
58
59 // a specialized function for combining two multiDimImplementations whose
60 // real data type is unknown to us
61
62# ifdef GUM_MULTI_DIM_OPERATOR_NAME
63# define GUM_MULTI_DIM_OPERATOR_TYPE T
64
65 template < typename T >
66 MultiDimImplementation< T >* GUM_MULTI_DIM_OPERATOR_NAME(const MultiDimImplementation< T >* t1,
68# endif
69
70 // clang-format off
71
72#ifdef GUM_MULTI_DIM_OPERATOR_POINTER_NAME
73#define GUM_MULTI_DIM_OPERATOR_TYPE T *
74 template <typename T>
75 MultiDimImplementation<T*>* GUM_MULTI_DIM_OPERATOR_POINTER_NAME(
78#endif
79
80#ifdef GUM_MULTI_DIM_OPERATOR_NAME_F
81#define GUM_MULTI_DIM_OPERATOR_TYPE T
82 template <typename T>
83 MultiDimImplementation<T>* GUM_MULTI_DIM_OPERATOR_NAME_F(
86 const T ( *f )( const T&, const T& ) )
87#endif
88
89#ifdef GUM_MULTI_DIM_OPERATOR_POINTER_NAME_F
90#define GUM_MULTI_DIM_OPERATOR_TYPE T *
91 template <typename T>
92 MultiDimImplementation<T*>* GUM_MULTI_DIM_OPERATOR_POINTER_NAME_F(
95 const T ( *f )( const T*, const T* ) )
96#endif
97
98 // clang-format on
99
100 {
101
102 // get the variables of the tables
103 const Sequence< const DiscreteVariable* >& t1_vars = t1->variablesSequence();
104 const Sequence< const DiscreteVariable* >& t2_vars = t2->variablesSequence();
105
106 // get the domain size of the tables' variables
108 {
109 Idx current_offset = 1;
110
111 for (const auto var: t1_vars) {
112 t1_offsets.insert(var, current_offset);
113 current_offset *= var->domainSize();
114 }
115 }
117 {
118 Idx current_offset = 1;
119
120 for (const auto var: t2_vars) {
121 t2_offsets.insert(var, current_offset);
122 current_offset *= var->domainSize();
123 }
124 }
125
126 // we divide the variables of t1 and t2 into 3 separate sets: those that
127 // belong only to t1 (variables t1_alone_xxx), those that belong only to t2
128 // (variables t2_alone_xxx) and those that belong to both tables (variables
129 // t1_and_t2_xxx). For each set, we get the variables of the table
130 // (txxx_var) and the domain size of the variable (txxx_domain). In
131 // addition, we compute the domain size of the Cartesian product of the
132 // variables in each of the 3 sets. Given these data, we will be able to
133 // parse both t1, t2 and the result table t1+t2.
134 std::vector< const DiscreteVariable* > t1_alone_var;
135 std::vector< Idx > t1_alone_domain;
136 Idx t1_alone_domain_size = 1;
137
138 std::vector< const DiscreteVariable* > t2_alone_var;
139 std::vector< Idx > t2_alone_domain;
140 Idx t2_alone_domain_size = 1;
141
142 std::vector< const DiscreteVariable* > t1_and_t2_var;
143 std::vector< Idx > t1_and_t2_domain;
144 Idx t1_and_t2_domain_size = 1;
145
146 {
147 for (const auto var: t1_vars)
148 if (t2_vars.exists(var)) {
149 t1_and_t2_domain.push_back(var->domainSize());
150 t1_and_t2_var.push_back(var);
151 t1_and_t2_domain_size *= var->domainSize();
152 } else {
153 t1_alone_domain.push_back(var->domainSize());
154 t1_alone_var.push_back(var);
155 t1_alone_domain_size *= var->domainSize();
156 }
157
158 for (const auto var: t2_vars)
159 if (!t1_vars.exists(var)) {
160 t2_alone_domain.push_back(var->domainSize());
161 t2_alone_var.push_back(var);
162 t2_alone_domain_size *= var->domainSize();
163 }
164 }
165
166 // a Boolean indicating whether the variables that t1 and t2 have in common
167 // are the first variables and are in the same order. When this is true,
168 // computations can be performed faster
169 bool t1_and_t2_begin_vars = false;
170
171 if (t1_and_t2_var.size()) {
172 unsigned int nb_t1_t2_vars = 0;
173
174 for (const auto var: t1_vars) {
175 if (var != t1_and_t2_var[nb_t1_t2_vars]) break;
176 nb_t1_t2_vars += 1;
177 }
178
179 if (nb_t1_t2_vars == t1_and_t2_var.size()) {
180 nb_t1_t2_vars = 0;
181
182 for (auto iter = t2_vars.begin(); nb_t1_t2_vars != t1_and_t2_var.size();
183 ++iter, ++nb_t1_t2_vars)
184 if (*iter != t1_and_t2_var[nb_t1_t2_vars]) break;
185
186 if (nb_t1_t2_vars == t1_and_t2_var.size()) t1_and_t2_begin_vars = true;
187 }
188 }
189
190 // when we will parse t1 and t2 to fill the result table t1+t2, we will use
191 // variables txxx_value : at the beginning they are initialized to the
192 // domain size of the variables (which are, themselves initialized to 0).
193 // Each time we increment a variable, its corresponding txxx_value is
194 // decreased by 1. When the latter is equal to 0, this means that the
195 // variable itself should be reinitialized to 0 as well and that the next
196 // variable of the table should be increased (that is, this is similar to
197 // increasing 9 to 10).
198 std::vector< Idx > t1_and_t2_value = t1_and_t2_domain;
199 std::vector< Idx > t1_alone_value = t1_alone_domain;
200 std::vector< Idx > t2_alone_value = t2_alone_domain;
201
202 // create a table "result" containing all the variables: the first
203 // variables are those that belong to both t1 and t2. The next variables
204 // are those that belong to t2 but not to t1. Finally, the last variables
205 // are those that belong to t1 but not t2. This order will be used in the
206 // next for loops.
209 result->beginMultipleChanges();
210
211 for (const auto var: t1_vars)
212 if (t2_vars.exists(var)) *result << *var;
213
214 for (const auto var: t2_vars)
215 if (!t1_vars.exists(var)) *result << *var;
216
217 for (const auto var: t1_vars)
218 if (!t2_vars.exists(var)) *result << *var;
219
220 result->endMultipleChanges();
221
222 // here we fill result. The idea is to use 3 loops. The innermost loop
223 // corresponds to the variables that belongs both to t1 and t2. The middle
224 // loop to the variables that belong to t2 but not to t1. Finally, the
225 // outer loop corresponds to the variables that belong to t1 but not t2.
226 Idx result_offset = 0;
227 // TODO: change the followings lines into:
228 // Instantiation t2_inst(t2);
229 // Instantiation t1_inst(t1);
230 // Instantiation t1_alone_begin_inst(t1);
231 // when Tensors will support thread-safe creations of Instantiations
232 Instantiation t2_inst;
233 Instantiation t1_inst;
234 Instantiation t1_alone_begin_inst;
235 for (const auto var: t2_vars)
236 t2_inst.add(*var);
237 for (const auto var: t1_vars)
238 t1_inst.add(*var);
239 for (const auto var: t1_vars)
240 t1_alone_begin_inst.add(*var);
241
242 // test if all the variables in common in t1 and t2 are the first variables
243 // and are in the same order. In this case, we can speed-up the
244 // incrementation processes
245 if (t1_and_t2_begin_vars) {
246 for (Idx i = 0; i < t1_alone_domain_size; ++i) {
247 t2_inst.setFirst();
248 t1_alone_begin_inst = t1_inst;
249
250 for (Idx j = 0; j < t2_alone_domain_size; ++j) {
251 t1_inst = t1_alone_begin_inst;
252
253 for (Idx z = 0; z < t1_and_t2_domain_size; ++z) {
254 result->unsafeSet(result_offset,
255 GUM_MULTI_DIM_OPERATOR(t1->get(t1_inst), t2->get(t2_inst)));
256
257 ++result_offset;
258
259 // update the offset of both t1 and t2
260 ++t1_inst;
261 ++t2_inst;
262 }
263 }
264 }
265 } else {
266 for (Idx i = 0; i < t1_alone_domain_size; ++i) {
267 t2_inst.setFirst();
268 t1_alone_begin_inst = t1_inst;
269
270 for (Idx j = 0; j < t2_alone_domain_size; ++j) {
271 t1_inst = t1_alone_begin_inst;
272
273 for (Idx z = 0; z < t1_and_t2_domain_size; ++z) {
274 result->unsafeSet(result_offset,
275 GUM_MULTI_DIM_OPERATOR(t1->get(t1_inst), t2->get(t2_inst)));
276
277 ++result_offset;
278
279 // update the offset of both t1 and t2
280 for (unsigned int k = 0; k < t1_and_t2_value.size(); ++k) {
281 --t1_and_t2_value[k];
282
283 if (t1_and_t2_value[k]) {
284 t1_inst.incVar(*(t1_and_t2_var[k]));
285 t2_inst.incVar(*(t1_and_t2_var[k]));
286 break;
287 }
288
289 t1_and_t2_value[k] = t1_and_t2_domain[k];
290 t1_inst.setFirstVar(*(t1_and_t2_var[k]));
291 t2_inst.setFirstVar(*(t1_and_t2_var[k]));
292 }
293 }
294
295 // update the offset of t2 alone
296 for (unsigned int k = 0; k < t2_alone_value.size(); ++k) {
297 --t2_alone_value[k];
298
299 if (t2_alone_value[k]) {
300 t2_inst.incVar(*(t2_alone_var[k]));
301 break;
302 }
303
304 t2_alone_value[k] = t2_alone_domain[k];
305 t2_inst.setFirstVar(*(t2_alone_var[k]));
306 }
307 }
308
309 // update the offset of t1 alone
310 for (unsigned int k = 0; k < t1_alone_value.size(); ++k) {
311 --t1_alone_value[k];
312
313 if (t1_alone_value[k]) {
314 t1_inst.incVar(*(t1_alone_var[k]));
315 break;
316 }
317
318 t1_alone_value[k] = t1_alone_domain[k];
319 t1_inst.setFirstVar(*(t1_alone_var[k]));
320 }
321 }
322 }
323
324 return result;
325 }
326
327# undef GUM_MULTI_DIM_OPERATOR_TYPE
328} // namespace gum
329#endif /* GUM_OPERATOR_PATTERN_ALLOWED */
The class for generic Hash Tables.
Definition hashTable.h:637
Class for assigning/browsing values to tuples of discrete variables.
void add(const DiscreteVariable &v) final
Adds a new variable in the Instantiation.
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