aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
operatorPattern4BaseName.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright 2005-2020 Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4  * info_at_agrum_dot_org
5  *
6  * This library is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 /**
23  * @file
24  * @brief the pattern used by all "basename" binary operators
25  *
26  * @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
27  */
28 
29 // check if we allowed these patterns to be used
30 #ifndef GUM_OPERATOR_PATTERN_ALLOWED
31 
32 // #warning To use operatorPattern4MultiDimBase.h, you must define
33 // GUM_OPERATOR_PATTERN_ALLOWED
34 
35 #else
36 
37 namespace gum {
38 
39  // a specialized function for combining two multiDimImplementations whose
40  // real data type is unknown to us
41 
42 # ifdef GUM_MULTI_DIM_OPERATOR_NAME
43 # define GUM_MULTI_DIM_OPERATOR_TYPE T
44  template < typename T >
45  MultiDimImplementation< T >*
46  GUM_MULTI_DIM_OPERATOR_NAME(const MultiDimImplementation< T >* t1,
47  const MultiDimImplementation< T >* t2)
48 # endif
49 
50  // clang-format off
51 
52 #ifdef GUM_MULTI_DIM_OPERATOR_POINTER_NAME
53 #define GUM_MULTI_DIM_OPERATOR_TYPE T *
54  template <typename T>
55  MultiDimImplementation<T*>* GUM_MULTI_DIM_OPERATOR_POINTER_NAME(
56  const MultiDimImplementation<T*>* t1,
57  const MultiDimImplementation<T*>* t2 )
58 #endif
59 
60 #ifdef GUM_MULTI_DIM_OPERATOR_NAME_F
61 #define GUM_MULTI_DIM_OPERATOR_TYPE T
62  template <typename T>
63  MultiDimImplementation<T>* GUM_MULTI_DIM_OPERATOR_NAME_F(
64  const MultiDimImplementation<T>* t1,
65  const MultiDimImplementation<T>* t2,
66  const T ( *f )( const T&, const T& ) )
67 #endif
68 
69 #ifdef GUM_MULTI_DIM_OPERATOR_POINTER_NAME_F
70 #define GUM_MULTI_DIM_OPERATOR_TYPE T *
71  template <typename T>
72  MultiDimImplementation<T*>* GUM_MULTI_DIM_OPERATOR_POINTER_NAME_F(
73  const MultiDimImplementation<T*>* t1,
74  const MultiDimImplementation<T*>* t2,
75  const T ( *f )( const T*, const T* ) )
76 #endif
77 
78  // clang-format on
79 
80  {
81 
82  // get the variables of the tables
83  const Sequence< const DiscreteVariable* >& t1_vars = t1->variablesSequence();
84  const Sequence< const DiscreteVariable* >& t2_vars = t2->variablesSequence();
85 
86  // get the domain size of the tables' variables
87  HashTable< const DiscreteVariable*, Idx > t1_offsets;
88  {
89  Idx current_offset = 1;
90 
91  for (const auto var: t1_vars) {
92  t1_offsets.insert(var, current_offset);
93  current_offset *= var->domainSize();
94  }
95  }
96  HashTable< const DiscreteVariable*, Idx > t2_offsets;
97  {
98  Idx current_offset = 1;
99 
100  for (const auto var: t2_vars) {
101  t2_offsets.insert(var, current_offset);
102  current_offset *= var->domainSize();
103  }
104  }
105 
106  // we divide the variables of t1 and t2 into 3 separate sets: those that
107  // belong only to t1 (variables t1_alone_xxx), those that belong only to t2
108  // (variables t2_alone_xxx) and those that belong to both tables (variables
109  // t1_and_t2_xxx). For each set, we get the variables of the table
110  // (txxx_var) and the domain size of the variable (txxx_domain). In
111  // addition, we compute the domain size of the Cartesian product of the
112  // variables in each of the 3 sets. Given these data, we will be able to
113  // parse both t1, t2 and the result table t1+t2.
114  std::vector< const DiscreteVariable* > t1_alone_var;
115  std::vector< Idx > t1_alone_domain;
116  Idx t1_alone_domain_size = 1;
117 
118  std::vector< const DiscreteVariable* > t2_alone_var;
119  std::vector< Idx > t2_alone_domain;
120  Idx t2_alone_domain_size = 1;
121 
122  std::vector< const DiscreteVariable* > t1_and_t2_var;
123  std::vector< Idx > t1_and_t2_domain;
124  Idx t1_and_t2_domain_size = 1;
125 
126  {
127  for (const auto var: t1_vars)
128  if (t2_vars.exists(var)) {
129  t1_and_t2_domain.push_back(var->domainSize());
130  t1_and_t2_var.push_back(var);
131  t1_and_t2_domain_size *= var->domainSize();
132  } else {
133  t1_alone_domain.push_back(var->domainSize());
134  t1_alone_var.push_back(var);
135  t1_alone_domain_size *= var->domainSize();
136  }
137 
138  for (const auto var: t2_vars)
139  if (!t1_vars.exists(var)) {
140  t2_alone_domain.push_back(var->domainSize());
141  t2_alone_var.push_back(var);
142  t2_alone_domain_size *= var->domainSize();
143  }
144  }
145 
146  // a Boolean indicating whether the variables that t1 and t2 have in common
147  // are the first variables and are in the same order. When this is true,
148  // computations can be performed faster
149  bool t1_and_t2_begin_vars = false;
150 
151  if (t1_and_t2_var.size()) {
152  unsigned int nb_t1_t2_vars = 0;
153 
154  for (const auto var: t1_vars) {
155  if (var != t1_and_t2_var[nb_t1_t2_vars]) break;
156  nb_t1_t2_vars += 1;
157  }
158 
159  if (nb_t1_t2_vars == t1_and_t2_var.size()) {
160  nb_t1_t2_vars = 0;
161 
162  for (auto iter = t2_vars.begin(); nb_t1_t2_vars != t1_and_t2_var.size();
163  ++iter, ++nb_t1_t2_vars)
164  if (*iter != t1_and_t2_var[nb_t1_t2_vars]) break;
165 
166  if (nb_t1_t2_vars == t1_and_t2_var.size()) t1_and_t2_begin_vars = true;
167  }
168  }
169 
170  // when we will parse t1 and t2 to fill the result table t1+t2, we will use
171  // variables txxx_value : at the beginning they are initialized to the
172  // domain size of the variables (which are, themselves initialized to 0).
173  // Each time we increment a variable, its corresponding txxx_value is
174  // decreased by 1. When the latter is equal to 0, this means that the
175  // variable itself should be reinitialized to 0 as well and that the next
176  // variable of the table should be increased (that is, this is similar to
177  // increasing 9 to 10).
178  std::vector< Idx > t1_and_t2_value = t1_and_t2_domain;
179  std::vector< Idx > t1_alone_value = t1_alone_domain;
180  std::vector< Idx > t2_alone_value = t2_alone_domain;
181 
182  // create a table "result" containing all the variables: the first
183  // variables are those that belong to both t1 and t2. The next variables
184  // are those that belong to t2 but not to t1. Finally, the last variables
185  // are those that belong to t1 but not t2. This order will be used in the
186  // next for loops.
187  MultiDimArray< GUM_MULTI_DIM_OPERATOR_TYPE >* result
188  = new MultiDimArray< GUM_MULTI_DIM_OPERATOR_TYPE >;
189  result->beginMultipleChanges();
190 
191  for (const auto var: t1_vars)
192  if (t2_vars.exists(var)) *result << *var;
193 
194  for (const auto var: t2_vars)
195  if (!t1_vars.exists(var)) *result << *var;
196 
197  for (const auto var: t1_vars)
198  if (!t2_vars.exists(var)) *result << *var;
199 
200  result->endMultipleChanges();
201 
202  // here we fill result. The idea is to use 3 loops. The innermost loop
203  // corresponds to the variables that belongs both to t1 and t2. The middle
204  // loop to the variables that belong to t2 but not to t1. Finally, the
205  // outer loop corresponds to the variables that belong to t1 but not t2.
206  Idx result_offset = 0;
207  Instantiation t2_inst(t2);
208  Instantiation t1_inst(t1);
209  Instantiation t1_alone_begin_inst(t1);
210 
211  // test if all the variables in common in t1 and t2 are the first variables
212  // and are in the same order. In this case, we can speed-up the
213  // incrementation processes
214  if (t1_and_t2_begin_vars) {
215  for (Idx i = 0; i < t1_alone_domain_size; ++i) {
216  t2_inst.setFirst();
217  t1_alone_begin_inst = t1_inst;
218 
219  for (Idx j = 0; j < t2_alone_domain_size; ++j) {
220  t1_inst = t1_alone_begin_inst;
221 
222  for (Idx z = 0; z < t1_and_t2_domain_size; ++z) {
223  result->unsafeSet(
224  result_offset,
225  GUM_MULTI_DIM_OPERATOR(t1->get(t1_inst), t2->get(t2_inst)));
226 
227  ++result_offset;
228 
229  // update the offset of both t1 and t2
230  ++t1_inst;
231  ++t2_inst;
232  }
233  }
234  }
235  } else {
236  for (Idx i = 0; i < t1_alone_domain_size; ++i) {
237  t2_inst.setFirst();
238  t1_alone_begin_inst = t1_inst;
239 
240  for (Idx j = 0; j < t2_alone_domain_size; ++j) {
241  t1_inst = t1_alone_begin_inst;
242 
243  for (Idx z = 0; z < t1_and_t2_domain_size; ++z) {
244  result->unsafeSet(
245  result_offset,
246  GUM_MULTI_DIM_OPERATOR(t1->get(t1_inst), t2->get(t2_inst)));
247 
248  ++result_offset;
249 
250  // update the offset of both t1 and t2
251  for (unsigned int k = 0; k < t1_and_t2_value.size(); ++k) {
252  --t1_and_t2_value[k];
253 
254  if (t1_and_t2_value[k]) {
255  t1_inst.incVar(*(t1_and_t2_var[k]));
256  t2_inst.incVar(*(t1_and_t2_var[k]));
257  break;
258  }
259 
260  t1_and_t2_value[k] = t1_and_t2_domain[k];
261  t1_inst.setFirstVar(*(t1_and_t2_var[k]));
262  t2_inst.setFirstVar(*(t1_and_t2_var[k]));
263  }
264  }
265 
266  // update the offset of t2 alone
267  for (unsigned int k = 0; k < t2_alone_value.size(); ++k) {
268  --t2_alone_value[k];
269 
270  if (t2_alone_value[k]) {
271  t2_inst.incVar(*(t2_alone_var[k]));
272  break;
273  }
274 
275  t2_alone_value[k] = t2_alone_domain[k];
276  t2_inst.setFirstVar(*(t2_alone_var[k]));
277  }
278  }
279 
280  // update the offset of t1 alone
281  for (unsigned int k = 0; k < t1_alone_value.size(); ++k) {
282  --t1_alone_value[k];
283 
284  if (t1_alone_value[k]) {
285  t1_inst.incVar(*(t1_alone_var[k]));
286  break;
287  }
288 
289  t1_alone_value[k] = t1_alone_domain[k];
290  t1_inst.setFirstVar(*(t1_alone_var[k]));
291  }
292  }
293  }
294 
295  return result;
296  }
297 
298 # undef GUM_MULTI_DIM_OPERATOR_TYPE
299 } // namespace gum
300 #endif /* GUM_OPERATOR_PATTERN_ALLOWED */