aGrUM  0.21.0
a C++ library for (probabilistic) graphical models
paramEstimatorML_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 estimating parameters of CPTs using Maximum Likelihood
24  *
25  * @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
26  */
27 #ifndef DOXYGEN_SHOULD_SKIP_THIS
28 
29 namespace gum {
30 
31  namespace learning {
32 
33  /// default constructor
34  template < template < typename > class ALLOC >
35  INLINE ParamEstimatorML< ALLOC >::ParamEstimatorML(
37  const Apriori< ALLOC >& external_apriori,
39  const std::vector< std::pair< std::size_t, std::size_t >,
40  ALLOC< std::pair< std::size_t, std::size_t > > >& ranges,
42  const typename ParamEstimatorML< ALLOC >::allocator_type& alloc) :
46  ranges,
48  alloc) {
50  }
51 
52 
53  /// default constructor
54  template < template < typename > class ALLOC >
57  const Apriori< ALLOC >& external_apriori,
60  const typename ParamEstimatorML< ALLOC >::allocator_type& alloc) :
65  alloc) {
67  }
68 
69 
70  /// copy constructor with a given allocator
71  template < template < typename > class ALLOC >
73  const ParamEstimatorML< ALLOC >& from,
74  const typename ParamEstimatorML< ALLOC >::allocator_type& alloc) :
77  }
78 
79 
80  /// copy constructor
81  template < template < typename > class ALLOC >
83  ParamEstimatorML< ALLOC >(from, this->getAllocator()) {}
84 
85 
86  /// move constructor with a given allocator
87  template < template < typename > class ALLOC >
90  const typename ParamEstimatorML< ALLOC >::allocator_type& alloc) :
93  }
94 
95 
96  /// move constructor
97  template < template < typename > class ALLOC >
100 
101 
102  /// virtual copy constructor with a given allocator
103  template < template < typename > class ALLOC >
105  const typename ParamEstimatorML< ALLOC >::allocator_type& alloc) const {
108  try {
110  } catch (...) {
112  throw;
113  }
114 
115  return new_score;
116  }
117 
118 
119  /// virtual copy constructor
120  template < template < typename > class ALLOC >
122  return clone(this->getAllocator());
123  }
124 
125 
126  /// destructor
127  template < template < typename > class ALLOC >
130  }
131 
132 
133  /// copy operator
134  template < template < typename > class ALLOC >
138  return *this;
139  }
140 
141 
142  /// move operator
143  template < template < typename > class ALLOC >
147  return *this;
148  }
149 
150 
151  /// returns the CPT's parameters corresponding to a given set of nodes
152  template < template < typename > class ALLOC >
153  std::vector< double, ALLOC< double > > ParamEstimatorML< ALLOC >::parameters(
154  const NodeId target_node,
155  const std::vector< NodeId, ALLOC< NodeId > >& conditioning_nodes) {
156  // create an idset that contains all the nodes in the following order:
157  // first, the target node, then all the conditioning nodes
159 
160  // get the counts for all the nodes in the idset and add the external and
161  // score internal aprioris
162  std::vector< double, ALLOC< double > > N_ijk(this->counter_.counts(idset, true));
169 
170 
171  // now, normalize N_ijk
172 
173  // here, we distinguish nodesets with conditioning nodes from those
174  // without conditioning nodes
175  if (!conditioning_nodes.empty()) {
176  // get the counts for all the conditioning nodes, and add them the
177  // external and score internal aprioris
178  std::vector< double, ALLOC< double > > N_ij(
179  this->counter_.counts(idset.conditionalIdCondSet(), false));
184 
187 
188  // check that all conditioning nodes have strictly positive counts
189  for (std::size_t j = std::size_t(0); j < conditioning_domsize; ++j) {
190  if (N_ij[j] == 0) {
191  // get the domain sizes of the conditioning nodes
194 
195  const auto& node2cols = this->counter_.nodeId2Columns();
196  const auto& database = this->counter_.database();
197  if (node2cols.empty()) {
198  for (std::size_t i = std::size_t(0); i < cond_nb; ++i) {
200  }
201  } else {
202  for (std::size_t i = std::size_t(0); i < cond_nb; ++i) {
204  }
205  }
206 
207  // determine the value of each conditioning variable in N_ij[j]
209  Idx offset = 1;
210  std::size_t i;
211  for (i = std::size_t(0); i < cond_nb; ++i) {
212  offsets[i] = offset;
213  offset *= cond_domsize[i];
214  }
215  std::vector< Idx > values(cond_nb);
216  i = 0;
217  offset = j;
218  for (Idx jj = cond_nb - 1; i < cond_nb; ++i, --jj) {
219  values[jj] = offset / offsets[jj];
220  offset %= offsets[jj];
221  }
222 
223  // create the error message
225  str << "The conditioning set <";
226  bool deja = false;
227  for (i = std::size_t(0); i < cond_nb; ++i) {
228  if (deja)
229  str << ", ";
230  else
231  deja = true;
234  const DiscreteVariable& var
235  = dynamic_cast< const DiscreteVariable& >(database.variable(col));
236  str << var.name() << "=" << var.labels()[values[i]];
237  }
240  str << "> for target node " << var.name()
241  << " never appears in the database. Please consider using "
242  << "priors such as smoothing.";
243 
245  }
246  }
247 
248  // normalize the counts
249  for (std::size_t j = std::size_t(0), k = std::size_t(0); j < conditioning_domsize; ++j) {
250  for (std::size_t i = std::size_t(0); i < target_domsize; ++i, ++k) {
251  N_ijk[k] /= N_ij[j];
252  }
253  }
254  } else {
255  // here, there are no conditioning nodes. Hence N_ijk is the marginal
256  // probability distribution over the target node. To normalize it, it
257  // is sufficient to divide each cell by the sum over all the cells
258  double sum = 0;
259  for (const double n_ijk: N_ijk)
260  sum += n_ijk;
261 
262  if (sum != 0) {
263  for (double& n_ijk: N_ijk)
264  n_ijk /= sum;
265  } else {
267 
268  const auto& node2cols = this->counter_.nodeId2Columns();
269  const auto& database = this->counter_.database();
272  str << "No data for target node " << var.name()
273  << ". It is impossible to estimate the parameters by maximum "
274  "likelihood";
276  }
277  }
278 
279  return N_ijk;
280  }
281 
282  } /* namespace learning */
283 
284 } /* namespace gum */
285 
286 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:643
Database(const std::string &filename, const BayesNet< GUM_SCALAR > &bn, const std::vector< std::string > &missing_symbols)