aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
scoreBD_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 BD 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/scoreBD.h>
31 # include <sstream>
32 
33 namespace gum {
34 
35  namespace learning {
36 
37  /// default constructor
38  template < template < typename > class ALLOC >
39  INLINE ScoreBD< ALLOC >::ScoreBD(
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 ScoreBD< ALLOC >::allocator_type& alloc) :
47  Score< ALLOC >(parser, apriori, ranges, nodeId2columns, alloc),
48  internal_apriori__(parser.database(), nodeId2columns) {
49  GUM_CONSTRUCTOR(ScoreBD);
50  }
51 
52 
53  /// default constructor
54  template < template < typename > class ALLOC >
55  INLINE ScoreBD< ALLOC >::ScoreBD(
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 ScoreBD< ALLOC >::allocator_type& alloc) :
61  Score< ALLOC >(parser, apriori, nodeId2columns, alloc),
62  internal_apriori__(parser.database(), nodeId2columns) {
63  GUM_CONSTRUCTOR(ScoreBD);
64  }
65 
66 
67  /// copy constructor with a given allocator
68  template < template < typename > class ALLOC >
69  INLINE ScoreBD< ALLOC >::ScoreBD(
70  const ScoreBD< ALLOC >& from,
71  const typename ScoreBD< ALLOC >::allocator_type& alloc) :
72  Score< ALLOC >(from, alloc),
73  internal_apriori__(from.internal_apriori__, alloc),
74  gammalog2__(from.gammalog2__) {
75  GUM_CONS_CPY(ScoreBD);
76  }
77 
78 
79  /// copy constructor
80  template < template < typename > class ALLOC >
81  INLINE ScoreBD< ALLOC >::ScoreBD(const ScoreBD< ALLOC >& from) :
82  ScoreBD< ALLOC >(from, from.getAllocator()) {}
83 
84 
85  /// move constructor with a given allocator
86  template < template < typename > class ALLOC >
87  INLINE ScoreBD< ALLOC >::ScoreBD(
88  ScoreBD< ALLOC >&& from,
89  const typename ScoreBD< 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(ScoreBD);
94  }
95 
96 
97  /// move constructor
98  template < template < typename > class ALLOC >
99  INLINE ScoreBD< ALLOC >::ScoreBD(ScoreBD< ALLOC >&& from) :
100  ScoreBD< ALLOC >(std::move(from), from.getAllocator()) {}
101 
102 
103  /// virtual copy constructor with a given allocator
104  template < template < typename > class ALLOC >
105  ScoreBD< ALLOC >* ScoreBD< ALLOC >::clone(
106  const typename ScoreBD< ALLOC >::allocator_type& alloc) const {
107  ALLOC< ScoreBD< ALLOC > > allocator(alloc);
108  ScoreBD< 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  ScoreBD< ALLOC >* ScoreBD< ALLOC >::clone() const {
123  return clone(this->getAllocator());
124  }
125 
126 
127  /// destructor
128  template < template < typename > class ALLOC >
129  ScoreBD< ALLOC >::~ScoreBD() {
130  GUM_DESTRUCTOR(ScoreBD);
131  }
132 
133 
134  /// copy operator
135  template < template < typename > class ALLOC >
136  ScoreBD< ALLOC >& ScoreBD< ALLOC >::operator=(const ScoreBD< ALLOC >& from) {
137  if (this != &from) {
138  Score< ALLOC >::operator=(from);
139  internal_apriori__ = from.internal_apriori__;
140  }
141  return *this;
142  }
143 
144 
145  /// move operator
146  template < template < typename > class ALLOC >
147  ScoreBD< ALLOC >& ScoreBD< ALLOC >::operator=(ScoreBD< ALLOC >&& from) {
148  if (this != &from) {
149  Score< ALLOC >::operator=(std::move(from));
150  internal_apriori__ = std::move(from.internal_apriori__);
151  }
152  return *this;
153  }
154 
155 
156  /// indicates whether the apriori is compatible (meaningful) with the score
157  template < template < typename > class ALLOC >
158  std::string
159  ScoreBD< ALLOC >::isAprioriCompatible(const std::string& apriori_type,
160  double weight) {
161  if (apriori_type == AprioriNoAprioriType::type) {
162  return "The BD score requires an apriori";
163  }
164 
165  if (weight != 0.0) {
166  return "The apriori is currently compatible with the BD score but if "
167  "you change the weight, it may become 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";
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
181  ScoreBD< ALLOC >::isAprioriCompatible(const Apriori< ALLOC >& apriori) {
182  return isAprioriCompatible(apriori.getType(), apriori.weight());
183  }
184 
185 
186  /// indicates whether the apriori is compatible (meaningful) with the score
187  template < template < typename > class ALLOC >
188  INLINE std::string ScoreBD< ALLOC >::isAprioriCompatible() const {
189  return isAprioriCompatible(*(this->apriori_));
190  }
191 
192 
193  /// returns the internal apriori of the score
194  template < template < typename > class ALLOC >
195  INLINE const Apriori< ALLOC >& ScoreBD< ALLOC >::internalApriori() const {
196  return internal_apriori__;
197  }
198 
199 
200  /// returns the score corresponding to a given nodeset
201  template < template < typename > class ALLOC >
202  double ScoreBD< ALLOC >::score_(const IdCondSet< ALLOC >& idset) {
203  // if the weight of the apriori is 0, then gammaLog2 will fail
204  if (!this->apriori_->isInformative()) {
205  GUM_ERROR(OutOfBounds,
206  "The BD score requires its external apriori to "
207  << "be strictly positive");
208  }
209 
210  // get the counts for all the nodes in the idset and add the apriori
211  std::vector< double, ALLOC< double > > N_ijk(
212  this->counter_.counts(idset, true));
213  const std::size_t all_size = N_ijk.size();
214  std::vector< double, ALLOC< double > > N_prime_ijk(all_size, 0.0);
215  this->apriori_->addAllApriori(idset, N_prime_ijk);
216 
217  double score = 0.0;
218 
219  // here, we distinguish idsets with conditioning nodes from those
220  // without conditioning nodes
221  if (idset.hasConditioningSet()) {
222  // get the counts for the conditioning nodes
223  std::vector< double, ALLOC< double > > N_ij(
224  this->marginalize_(idset[0], N_ijk));
225  const std::size_t conditioning_size = N_ij.size();
226 
227  std::vector< double, ALLOC< double > > N_prime_ij(N_ij.size(), 0.0);
228  this->apriori_->addConditioningApriori(idset, N_prime_ij);
229 
230  // the BD score can be computed as follows:
231  // sum_j=1^qi [ gammalog2 ( N'_ij ) - gammalog2 ( N_ij + N'_ij )
232  // + sum_k=1^ri { gammlog2 ( N_ijk + N'_ijk ) -
233  // gammalog2 ( N'_ijk ) } ]
234  for (std::size_t j = std::size_t(0); j < conditioning_size; ++j) {
235  score
236  += gammalog2__(N_prime_ij[j]) - gammalog2__(N_ij[j] + N_prime_ij[j]);
237  }
238  for (std::size_t k = std::size_t(0); k < all_size; ++k) {
239  score += gammalog2__(N_ijk[k] + N_prime_ijk[k])
240  - gammalog2__(N_prime_ijk[k]);
241  }
242  } else {
243  // the BD score can be computed as follows:
244  // gammalog2 ( N' ) - gammalog2 ( N + N' )
245  // + sum_k=1^ri { gammlog2 ( N_i + N'_i ) - gammalog2 ( N'_i ) }
246  double N = 0.0;
247  double N_prime = 0.0;
248  for (std::size_t k = std::size_t(0); k < all_size; ++k) {
249  score += gammalog2__(N_ijk[k] + N_prime_ijk[k])
250  - gammalog2__(N_prime_ijk[k]);
251  N += N_ijk[k];
252  N_prime += N_prime_ijk[k];
253  }
254  score += gammalog2__(N_prime) - gammalog2__(N + N_prime);
255  }
256 
257  return score;
258  }
259 
260  } /* namespace learning */
261 
262 } /* namespace gum */
263 
264 #endif /* DOXYGEN_SHOULD_SKIP_THIS */