aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
MarkovNet_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright 2005-2020 Pierre-Henri WUILLEMIN(@LIP6) et Christophe GONZALES(@AMU)
4  * (@AMU) 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 Template implementation of BN/MarkovNet.h class.
25  *
26  * @author Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
27  */
28 
29 #include <limits>
30 #include <set>
31 
32 #include <agrum/MN/MarkovNet.h>
33 
34 #include <agrum/tools/variables/rangeVariable.h>
35 #include <agrum/tools/variables/labelizedVariable.h>
36 #include <agrum/tools/variables/discretizedVariable.h>
37 
38 #include <agrum/tools/multidim/aggregators/amplitude.h>
39 #include <agrum/tools/multidim/aggregators/and.h>
40 #include <agrum/tools/multidim/aggregators/count.h>
41 #include <agrum/tools/multidim/aggregators/exists.h>
42 #include <agrum/tools/multidim/aggregators/forall.h>
43 #include <agrum/tools/multidim/aggregators/max.h>
44 #include <agrum/tools/multidim/aggregators/median.h>
45 #include <agrum/tools/multidim/aggregators/min.h>
46 #include <agrum/tools/multidim/aggregators/or.h>
47 
48 #include <agrum/tools/multidim/ICIModels/multiDimNoisyAND.h>
49 #include <agrum/tools/multidim/ICIModels/multiDimNoisyORCompound.h>
50 #include <agrum/tools/multidim/ICIModels/multiDimNoisyORNet.h>
51 
52 #include <agrum/tools/multidim/ICIModels/multiDimLogit.h>
53 
54 #include <agrum/BN/generator/simpleCPTGenerator.h>
55 #include <agrum/tools/core/utils_string.h>
56 
57 namespace gum {
58  template < typename GUM_SCALAR >
60  std::string node,
62  std::string name = node;
63  auto ds = default_domain_size;
64  long range_min = 0;
65  long range_max = long(ds) - 1;
66  std::vector< std::string > labels;
68 
69  if (*(node.rbegin()) == ']') {
70  auto posBrack = node.find('[');
71  if (posBrack != std::string::npos) {
72  name = node.substr(0, posBrack);
73  const auto& s_args = node.substr(posBrack + 1, node.size() - posBrack - 2);
74  const auto& args = split(s_args, ",");
75  if (args.size() == 0) { // n[]
76  GUM_ERROR(InvalidArgument, "Empty range for variable " << node)
77  } else if (args.size() == 1) { // n[4]
78  ds = static_cast< Size >(std::stoi(args[0]));
79  range_min = 0;
80  range_max = long(ds) - 1;
81  } else if (args.size() == 2) { // n[5,10]
82  range_min = std::stol(args[0]);
83  range_max = std::stol(args[1]);
84  if (1 + range_max - range_min < 2) {
85  GUM_ERROR(InvalidArgument, "Invalid range for variable " << node);
86  }
87  ds = static_cast< Size >(1 + range_max - range_min);
88  } else { // n[3.14,5,10,12]
89  for (const auto& tick: args) {
90  ticks.push_back(static_cast< GUM_SCALAR >(std::atof(tick.c_str())));
91  }
92  ds = static_cast< Size >(args.size() - 1);
93  }
94  }
95  } else if (*(node.rbegin()) == '}') { // node like "n{one|two|three}"
96  auto posBrack = node.find('{');
97  if (posBrack != std::string::npos) {
98  name = node.substr(0, posBrack);
99  labels = split(node.substr(posBrack + 1, node.size() - posBrack - 2), "|");
100  if (labels.size() < 2) {
101  GUM_ERROR(InvalidArgument, "Not enough labels in node " << node);
102  }
103  if (!hasUniqueElts(labels)) {
104  GUM_ERROR(InvalidArgument, "Duplicate labels in node " << node);
105  }
106  ds = static_cast< Size >(labels.size());
107  }
108  }
109 
110  if (ds == 0) {
111  GUM_ERROR(InvalidArgument, "No value for variable " << name << ".");
112  } else if (ds == 1) {
114  "Only one value for variable " << name
115  << " (2 at least are needed).");
116  }
117 
118  // now we add the node in the BN
119  NodeId idVar;
120  try {
121  idVar = mn.idFromName(name);
122  } catch (NotFound&) {
123  if (!labels.empty()) {
125  } else if (!ticks.empty()) {
127  } else {
129  }
130  }
131 
132  return idVar;
133  }
134 
135  template < typename GUM_SCALAR >
136  MarkovNet< GUM_SCALAR >
137  MarkovNet< GUM_SCALAR >::fastPrototype(const std::string& dotlike,
138  Size domainSize) {
140 
141 
142  for (const auto& clikchain: split(dotlike, ";")) {
143  NodeSet cliq;
144  for (const auto& node: split(clikchain, "-")) {
146  cliq.insert(idVar);
147  }
148  mn.addFactor(cliq);
149  }
151  mn.setProperty("name", "fastPrototype");
152  return mn;
153  }
154 
155  template < typename GUM_SCALAR >
157  MarkovNet< GUM_SCALAR >::fromBN(const BayesNet< GUM_SCALAR >& bn) {
158  MarkovNet< GUM_SCALAR > mn;
159  for (NodeId nod: bn.nodes()) {
160  mn.add(bn.variable(nod), nod);
161  }
162  mn.beginTopologyTransformation();
163  for (NodeId nod: bn.nodes()) {
164  mn.addFactor(bn.cpt(nod));
165  }
166  mn.endTopologyTransformation();
167  mn.setProperty("name", bn.propertyWithDefault("name", "noname"));
168  return mn;
169  }
170 
171  template < typename GUM_SCALAR >
175  }
176 
177  template < typename GUM_SCALAR >
181  }
182 
183  template < typename GUM_SCALAR >
189  }
190 
191  template < typename GUM_SCALAR >
194  if (this != &source) {
199  }
200 
201  return *this;
202  }
203 
204  template < typename GUM_SCALAR >
206  clearFactors__();
208  }
209 
210  template < typename GUM_SCALAR >
211  INLINE const DiscreteVariable&
212  MarkovNet< GUM_SCALAR >::variable(NodeId id) const {
213  return varMap__.get(id);
214  }
215 
216  template < typename GUM_SCALAR >
217  INLINE void
219  const std::string& new_name) {
221  }
222 
223  template < typename GUM_SCALAR >
224  INLINE void
226  const std::string& old_label,
227  const std::string& new_label) {
228  if (variable(id).varType() != VarType::Labelized) {
229  GUM_ERROR(NotFound, "Variable " << id << " is not a LabelizedVariable.");
230  }
231  LabelizedVariable* var = dynamic_cast< LabelizedVariable* >(
232  const_cast< DiscreteVariable* >(&variable(id)));
233 
235  }
236 
237  template < typename GUM_SCALAR >
238  INLINE NodeId
239  MarkovNet< GUM_SCALAR >::nodeId(const DiscreteVariable& var) const {
240  return varMap__.get(var);
241  }
242 
243  template < typename GUM_SCALAR >
244  const Potential< GUM_SCALAR >&
245  MarkovNet< GUM_SCALAR >::factor(const NodeSet& varIds) const {
246  return *factors__[varIds];
247  }
248 
249  template < typename GUM_SCALAR >
250  const NodeSet&
252  const NodeSet* res = nullptr;
253  Size smallest = size();
254  for (const auto& kv: factors()) {
255  const auto& fact = kv.first;
256  if (fact.contains(node))
257  if (smallest > fact.size()) {
258  res = &fact;
259  smallest = fact.size();
260  }
261  }
262  if (res == nullptr) {
263  GUM_ERROR(NotFound, "No factor containing node " << node)
264  } else {
265  return *res;
266  }
267  }
268 
269  template < typename GUM_SCALAR >
271  const std::vector< std::string >& varnames) const {
272  return factor(this->nodeset(varnames));
273  }
274 
275  template < typename GUM_SCALAR >
276  const FactorTable< GUM_SCALAR >& MarkovNet< GUM_SCALAR >::factors() const {
277  return factors__;
278  }
279 
280  template < typename GUM_SCALAR >
282  unsigned int nbrmod) {
283  if (nbrmod < 2) {
285  "Variable " << name << "needs more than " << nbrmod
286  << " modalities");
287  }
288 
289  RangeVariable v(name, name, 0, nbrmod - 1);
290  return add(v);
291  }
292 
293  template < typename GUM_SCALAR >
296 
297  this->graph_.clearEdges();
298 
299  for (const auto& kv: factors__) {
300  const Potential< double >& c = *kv.second;
301  for (Idx i = 0; i < c.nbrDim(); i++)
302  for (Idx j = i + 1; j < c.nbrDim(); j++)
304  varMap__.get(c.variable(j)));
305  }
306  }
307 
308 
309  template < typename GUM_SCALAR >
311  return add(var, graph().nextNodeId());
312  }
313 
314  template < typename GUM_SCALAR >
316  NodeId id) {
317  varMap__.insert(id, var);
318  this->graph_.addNodeWithId(id);
319  return id;
320  }
321 
322  template < typename GUM_SCALAR >
323  INLINE NodeId
324  MarkovNet< GUM_SCALAR >::idFromName(const std::string& name) const {
325  return varMap__.idFromName(name);
326  }
327 
328  template < typename GUM_SCALAR >
329  INLINE const DiscreteVariable&
330  MarkovNet< GUM_SCALAR >::variableFromName(const std::string& name) const {
332  }
333 
334  template < typename GUM_SCALAR >
336  return varMap__;
337  }
338 
339  template < typename GUM_SCALAR >
341  erase(varMap__.get(var));
342  }
343 
344  template < typename GUM_SCALAR >
345  INLINE void MarkovNet< GUM_SCALAR >::erase(const std::string& name) {
347  }
348 
349  template < typename GUM_SCALAR >
351  if (!varMap__.exists(varId)) {
352  GUM_ERROR(InvalidArgument, "No node with id " << varId << ".")
353  }
355  this->graph_.eraseNode(varId);
356 
357  std::vector< NodeSet > vs;
358  for (const auto& kv: factors__) {
359  if (kv.first.contains(varId)) { vs.push_back(kv.first); }
360  }
361  for (const auto& ns: vs) {
362  eraseFactor__(ns);
363  }
364  for (const auto& ns: vs) {
365  NodeSet nv = ns;
366  nv.erase(varId);
367  if (nv.size() > 1) addFactor__(nv);
368  }
369  rebuildGraph__();
370  }
371 
372  template < typename GUM_SCALAR >
373  void MarkovNet< GUM_SCALAR >::clear() {
374  if (!this->empty()) {
375  auto l = this->nodes();
376  for (const auto no: l) {
377  this->erase(no);
378  }
379  }
380  rebuildGraph__();
381  }
382 
383 
384  template < typename GUM_SCALAR >
386  const MarkovNet< GUM_SCALAR >& mn) {
387  output << mn.toString();
388  return output;
389  }
390 
391  template < typename GUM_SCALAR >
392  INLINE const Potential< GUM_SCALAR >&
394  if (vars.size() == 0) {
395  GUM_ERROR(InvalidArgument, "Empty factor cannot be added.")
396  }
397 
398  if (factors__.exists(vars)) {
399  GUM_ERROR(InvalidArgument, "A factor for (" << vars << ") already exists.")
400  }
401 
402  auto res = addFactor__(vars);
403  rebuildGraph__();
404 
405  return *res;
406  }
407 
408  template < typename GUM_SCALAR >
410  const std::vector< std::string >& varnames) {
411  auto vars = this->nodeset(varnames);
412  if (factors__.exists(vars)) {
414  "A factor for (" << varnames << ") already exists.")
415  }
416 
417  return addFactor(vars);
418  }
419 
420  template < typename GUM_SCALAR >
421  INLINE const Potential< GUM_SCALAR >&
423  if (factor.nbrDim() == 0) {
424  GUM_ERROR(InvalidArgument, "Empty factor cannot be added.");
425  }
426 
427  NodeSet key;
428  for (Idx i = 0; i < factor.nbrDim(); i++) {
430  }
431 
432  if (factors__.exists(key)) {
433  GUM_ERROR(InvalidArgument, "A factor for (" << key << ") already exists.");
434  }
435 
436  auto res = addFactor__(key, &factor);
437  rebuildGraph__();
438 
439  return *res;
440  }
441 
442  template < typename GUM_SCALAR >
443  INLINE const Potential< GUM_SCALAR >*
445  const Potential< GUM_SCALAR >* src) {
447 
448  // in order to be deterministic, the Potential contains all the vars, sorted by
449  // id.
451  for (auto node: vars) {
453  }
455  for (auto node: sorted_nodes) {
456  factor->add(variable(node));
457  }
458 
459  if (src != nullptr) { factor->fillWith(*src); }
461 
462  return factor;
463  }
464 
465 
466  template < typename GUM_SCALAR >
467  INLINE void MarkovNet< GUM_SCALAR >::generateFactors() const {
468  for (const auto& elt: factors__) {
469  elt.second->random();
470  }
471  }
472 
473  template < typename GUM_SCALAR >
474  INLINE void MarkovNet< GUM_SCALAR >::generateFactor(const NodeSet& vars) const {
475  factors__[vars]->random();
476  }
477 
478  template < typename GUM_SCALAR >
480  if (factors__.exists(vars)) {
482  rebuildGraph__();
483  } else {
484  GUM_ERROR(InvalidArgument, "No factor for " << vars << ".")
485  }
486  }
487 
488  template < typename GUM_SCALAR >
490  const std::vector< std::string >& varnames) {
491  auto vars = this->nodeset(varnames);
492  if (factors__.exists(vars)) {
494  rebuildGraph__();
495  } else {
496  GUM_ERROR(InvalidArgument, "No factor for " << varnames << ".")
497  }
498  }
499 
500  template < typename GUM_SCALAR >
502  delete factors__[vars];
504  }
505 
506  template < typename GUM_SCALAR >
507  void MarkovNet< GUM_SCALAR >::clearFactors__() {
508  for (const auto& kv: factors__) {
509  delete kv.second;
510  }
511  factors__.clear();
512  rebuildGraph__();
513  }
514 
515  template < typename GUM_SCALAR >
517  const MarkovNet< GUM_SCALAR >& source) {
518  clearFactors__();
519  for (const auto& pf: source.factors()) {
521  }
522  rebuildGraph__();
523  }
524 
525  template < typename GUM_SCALAR >
528  }
529 
530  template < typename GUM_SCALAR >
534  = false; // before rebuildGraph of course
535  rebuildGraph__();
536  }
537  }
538 } /* namespace gum */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:669
NodeId build_node_for_MN(MarkovNet< GUM_SCALAR > &mn, std::string node, Size default_domain_size)
Definition: MarkovNet_tpl.h:59