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