aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
scoreBDeu_tpl.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 /** @file
23  * @brief the class for computing BDeu scores
24  *
25  * @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
26  */
27 
28 #ifndef DOXYGEN_SHOULD_SKIP_THIS
29 
30 # include <agrum/BN/learning/scores_and_tests/scoreBDeu.h>
31 # include <sstream>
32 
33 namespace gum {
34 
35  namespace learning {
36 
37  /// default constructor
38  template < template < typename > class ALLOC >
39  INLINE ScoreBDeu< ALLOC >::ScoreBDeu(
40  const DBRowGeneratorParser< ALLOC >& parser,
41  const Apriori< ALLOC >& apriori,
42  const std::vector< std::pair< std::size_t, std::size_t >,
43  ALLOC< std::pair< std::size_t, std::size_t > > >& ranges,
44  const Bijection< NodeId, std::size_t, ALLOC< std::size_t > >&
45  nodeId2columns,
46  const typename ScoreBDeu< ALLOC >::allocator_type& alloc) :
47  Score< ALLOC >(parser, apriori, ranges, nodeId2columns, alloc),
48  internal_apriori__(parser.database(), nodeId2columns) {
49  GUM_CONSTRUCTOR(ScoreBDeu);
50  }
51 
52 
53  /// default constructor
54  template < template < typename > class ALLOC >
55  INLINE ScoreBDeu< ALLOC >::ScoreBDeu(
56  const DBRowGeneratorParser< ALLOC >& parser,
57  const Apriori< ALLOC >& apriori,
58  const Bijection< NodeId, std::size_t, ALLOC< std::size_t > >&
59  nodeId2columns,
60  const typename ScoreBDeu< ALLOC >::allocator_type& alloc) :
61  Score< ALLOC >(parser, apriori, nodeId2columns, alloc),
62  internal_apriori__(parser.database(), nodeId2columns) {
63  GUM_CONSTRUCTOR(ScoreBDeu);
64  }
65 
66 
67  /// copy constructor with a given allocator
68  template < template < typename > class ALLOC >
69  INLINE ScoreBDeu< ALLOC >::ScoreBDeu(
70  const ScoreBDeu< ALLOC >& from,
71  const typename ScoreBDeu< ALLOC >::allocator_type& alloc) :
72  Score< ALLOC >(from, alloc),
73  internal_apriori__(from.internal_apriori__, alloc),
74  gammalog2__(from.gammalog2__) {
75  GUM_CONS_CPY(ScoreBDeu);
76  }
77 
78 
79  /// copy constructor
80  template < template < typename > class ALLOC >
81  INLINE ScoreBDeu< ALLOC >::ScoreBDeu(const ScoreBDeu< ALLOC >& from) :
82  ScoreBDeu< ALLOC >(from, from.getAllocator()) {}
83 
84 
85  /// move constructor with a given allocator
86  template < template < typename > class ALLOC >
87  INLINE ScoreBDeu< ALLOC >::ScoreBDeu(
88  ScoreBDeu< ALLOC >&& from,
89  const typename ScoreBDeu< ALLOC >::allocator_type& alloc) :
90  Score< ALLOC >(std::move(from), alloc),
91  internal_apriori__(std::move(from.internal_apriori__), alloc),
92  gammalog2__(std::move(from.gammalog2__)) {
93  GUM_CONS_MOV(ScoreBDeu);
94  }
95 
96 
97  /// move constructor
98  template < template < typename > class ALLOC >
99  INLINE ScoreBDeu< ALLOC >::ScoreBDeu(ScoreBDeu< ALLOC >&& from) :
100  ScoreBDeu< ALLOC >(std::move(from), from.getAllocator()) {}
101 
102 
103  /// virtual copy constructor with a given allocator
104  template < template < typename > class ALLOC >
105  ScoreBDeu< ALLOC >* ScoreBDeu< ALLOC >::clone(
106  const typename ScoreBDeu< ALLOC >::allocator_type& alloc) const {
107  ALLOC< ScoreBDeu< ALLOC > > allocator(alloc);
108  ScoreBDeu< ALLOC >* new_score = allocator.allocate(1);
109  try {
110  allocator.construct(new_score, *this, alloc);
111  } catch (...) {
112  allocator.deallocate(new_score, 1);
113  throw;
114  }
115 
116  return new_score;
117  }
118 
119 
120  /// virtual copy constructor
121  template < template < typename > class ALLOC >
122  ScoreBDeu< ALLOC >* ScoreBDeu< ALLOC >::clone() const {
123  return clone(this->getAllocator());
124  }
125 
126 
127  /// destructor
128  template < template < typename > class ALLOC >
129  ScoreBDeu< ALLOC >::~ScoreBDeu() {
130  GUM_DESTRUCTOR(ScoreBDeu);
131  }
132 
133 
134  /// copy operator
135  template < template < typename > class ALLOC >
136  ScoreBDeu< ALLOC >&
137  ScoreBDeu< ALLOC >::operator=(const ScoreBDeu< ALLOC >& from) {
138  if (this != &from) {
139  Score< ALLOC >::operator=(from);
140  internal_apriori__ = from.internal_apriori__;
141  }
142  return *this;
143  }
144 
145 
146  /// move operator
147  template < template < typename > class ALLOC >
148  ScoreBDeu< ALLOC >& ScoreBDeu< ALLOC >::operator=(ScoreBDeu< ALLOC >&& from) {
149  if (this != &from) {
150  Score< ALLOC >::operator=(std::move(from));
151  internal_apriori__ = std::move(from.internal_apriori__);
152  }
153  return *this;
154  }
155 
156 
157  /// indicates whether the apriori is compatible (meaningful) with the score
158  template < template < typename > class ALLOC >
159  std::string
160  ScoreBDeu< ALLOC >::isAprioriCompatible(const std::string& apriori_type,
161  double weight) {
162  // check that the apriori is compatible with the score
163  if (apriori_type == AprioriNoAprioriType::type) { return ""; }
164 
165  if (weight == 0.0) {
166  return "The apriori is currently compatible with the BDeu score but "
167  "if you change the weight, it will become incompatible.";
168  }
169 
170  // known incompatible aprioris
171  if ((apriori_type == AprioriDirichletType::type)
172  || (apriori_type == AprioriSmoothingType::type)) {
173  return "The BDeu score already contains a different 'implicit' apriori. "
174  "Therefore, the learning will probably be biased.";
175  }
176 
177  // apriori types unsupported by the type checker
178  std::stringstream msg;
179  msg << "The apriori '" << apriori_type
180  << "' is not yet supported by method isAprioriCompatible os Score BDeu";
181  return msg.str();
182  }
183 
184 
185  /// indicates whether the apriori is compatible (meaningful) with the score
186  template < template < typename > class ALLOC >
187  INLINE std::string
188  ScoreBDeu< ALLOC >::isAprioriCompatible(const Apriori< ALLOC >& apriori) {
189  return isAprioriCompatible(apriori.getType(), apriori.weight());
190  }
191 
192 
193  /// indicates whether the apriori is compatible (meaningful) with the score
194  template < template < typename > class ALLOC >
195  INLINE std::string ScoreBDeu< ALLOC >::isAprioriCompatible() const {
196  return isAprioriCompatible(*(this->apriori_));
197  }
198 
199 
200  /// returns the internal apriori of the score
201  template < template < typename > class ALLOC >
202  INLINE const Apriori< ALLOC >& ScoreBDeu< ALLOC >::internalApriori() const {
203  return internal_apriori__;
204  }
205 
206 
207  /// sets the effective sample size of the internal apriori
208  template < template < typename > class ALLOC >
209  INLINE void ScoreBDeu< ALLOC >::setEffectiveSampleSize(double ess) {
210  if (ess < 0) {
211  GUM_ERROR(OutOfBounds,
212  "The effective sample size of the BDeu's "
213  "internal apriori must be positive");
214  } else {
215  internal_apriori__.setEffectiveSampleSize(ess);
216  }
217  }
218 
219 
220  /// returns the score corresponding to a given nodeset
221  template < template < typename > class ALLOC >
222  double ScoreBDeu< ALLOC >::score_(const IdCondSet< ALLOC >& idset) {
223  // get the counts for all the nodes in the idset and add the apriori
224  std::vector< double, ALLOC< double > > N_ijk(
225  this->counter_.counts(idset, true));
226  const std::size_t all_size = N_ijk.size();
227 
228  double score = 0.0;
229  const double ess = internal_apriori__.weight();
230  const bool informative_external_apriori = this->apriori_->isInformative();
231 
232 
233  // here, we distinguish idsets with conditioning nodes from those
234  // without conditioning nodes
235  if (idset.hasConditioningSet()) {
236  // get the counts for the conditioning nodes
237  std::vector< double, ALLOC< double > > N_ij(
238  this->marginalize_(idset[0], N_ijk));
239  const std::size_t conditioning_size = N_ij.size();
240  const double ess_qi = ess / conditioning_size;
241  const double ess_riqi = ess / all_size;
242 
243  if (informative_external_apriori) {
244  // the score to compute is that of BD with aprioris
245  // N'_ijk + ESS / (r_i * q_i )
246  // (the + ESS / (r_i * q_i ) is here to take into account the
247  // internal apriori of BDeu)
248  std::vector< double, ALLOC< double > > N_prime_ijk(all_size, 0.0);
249  this->apriori_->addAllApriori(idset, N_prime_ijk);
250  std::vector< double, ALLOC< double > > N_prime_ij(N_ij.size(), 0.0);
251  this->apriori_->addConditioningApriori(idset, N_prime_ij);
252 
253  // the BDeu score can be computed as follows:
254  // sum_j=1^qi [ gammalog2 ( N'_ij + ESS / q_i ) -
255  // gammalog2 ( N_ij + N'_ij + ESS / q_i )
256  // + sum_k=1^ri { gammlog2 ( N_ijk + N'_ijk + ESS / (r_i * q_i ) )
257  // - gammalog2 ( N'_ijk + ESS / (r_i * q_i ) ) } ]
258  for (std::size_t j = std::size_t(0); j < conditioning_size; ++j) {
259  score += gammalog2__(N_prime_ij[j] + ess_qi)
260  - gammalog2__(N_ij[j] + N_prime_ij[j] + ess_qi);
261  }
262  for (std::size_t k = std::size_t(0); k < all_size; ++k) {
263  score += gammalog2__(N_ijk[k] + N_prime_ijk[k] + ess_riqi)
264  - gammalog2__(N_prime_ijk[k] + ess_riqi);
265  }
266  } else {
267  // the BDeu score can be computed as follows:
268  // qi * gammalog2 (ess / qi) - ri * qi * gammalog2 (ess / (ri * qi) )
269  // - sum_j=1^qi [ gammalog2 ( N_ij + ess / qi ) ]
270  // + sum_j=1^qi sum_k=1^ri log [ gammalog2 ( N_ijk + ess / (ri * qi) )
271  // ]
272  score = conditioning_size * gammalog2__(ess_qi)
273  - all_size * gammalog2__(ess_riqi);
274 
275  for (const auto n_ij: N_ij) {
276  score -= gammalog2__(n_ij + ess_qi);
277  }
278  for (const auto n_ijk: N_ijk) {
279  score += gammalog2__(n_ijk + ess_riqi);
280  }
281  }
282  } else {
283  // here, there are no conditioning nodes
284  const double ess_ri = ess / all_size;
285 
286  if (informative_external_apriori) {
287  // the score to compute is that of BD with aprioris
288  // N'_ijk + ESS / ( ri * qi )
289  // (the + ESS / ( ri * qi ) is here to take into account the
290  // internal apriori of K2)
291  std::vector< double, ALLOC< double > > N_prime_ijk(all_size, 0.0);
292  this->apriori_->addAllApriori(idset, N_prime_ijk);
293 
294  // the BDeu score can be computed as follows:
295  // gammalog2 ( N' + ess ) - gammalog2 ( N + N' + ess )
296  // + sum_k=1^ri { gammlog2 ( N_i + N'_i + ESS / ri)
297  // - gammalog2 ( N'_i + ESS / ri ) }
298  double N = 0.0;
299  double N_prime = 0.0;
300  for (std::size_t k = std::size_t(0); k < all_size; ++k) {
301  score += gammalog2__(N_ijk[k] + N_prime_ijk[k] + ess_ri)
302  - gammalog2__(N_prime_ijk[k] + ess_ri);
303  N += N_ijk[k];
304  N_prime += N_prime_ijk[k];
305  }
306  score += gammalog2__(N_prime + ess) - gammalog2__(N + N_prime + ess);
307  } else {
308  // the BDeu score can be computed as follows:
309  // gammalog2 ( ess ) - ri * gammalog2 ( ess / ri )
310  // - gammalog2 ( N + ess )
311  // + sum_k=1^ri log [ gammalog2 ( N_ijk + ess / ri ) ]
312 
313  score = gammalog2__(ess) - all_size * gammalog2__(ess_ri);
314  double N = 0;
315  for (const auto n_ijk: N_ijk) {
316  score += gammalog2__(n_ijk + ess_ri);
317  N += n_ijk;
318  }
319  score -= gammalog2__(N + ess);
320  }
321  }
322 
323  return score;
324  }
325 
326  } /* namespace learning */
327 
328 } /* namespace gum */
329 
330 #endif /* DOXYGEN_SHOULD_SKIP_THIS */