aGrUM  0.20.3
a C++ library for (probabilistic) graphical models
BayesNet_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 /**
23  * @file
24  * @brief Template implementation of BN/BayesNet.h class.
25  *
26  * @author Pierre-Henri WUILLEMIN(@LIP6) and Lionel TORTI
27  */
28 
29 #include <limits>
30 #include <set>
31 
32 #include <agrum/BN/BayesNet.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 #include <agrum/tools/multidim/aggregators/sum.h>
48 
49 #include <agrum/tools/multidim/ICIModels/multiDimNoisyAND.h>
50 #include <agrum/tools/multidim/ICIModels/multiDimNoisyORCompound.h>
51 #include <agrum/tools/multidim/ICIModels/multiDimNoisyORNet.h>
52 
53 #include <agrum/tools/multidim/ICIModels/multiDimLogit.h>
54 
55 #include <agrum/BN/generator/simpleCPTGenerator.h>
56 #include <agrum/tools/core/utils_string.h>
57 
58 namespace gum {
59  template < typename GUM_SCALAR >
60  NodeId
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) { GUM_ERROR(InvalidArgument, "Not enough labels in node " << node) }
101  if (!hasUniqueElts(labels)) {
102  GUM_ERROR(InvalidArgument, "Duplicate labels in node " << node)
103  }
104  ds = static_cast< Size >(labels.size());
105  }
106  }
107 
108  if (ds == 0) {
109  GUM_ERROR(InvalidArgument, "No value for variable " << name << ".")
110  } else if (ds == 1) {
112  "Only one value for variable " << name << " (2 at least are needed).")
113  }
114 
115  // now we add the node in the BN
116  NodeId idVar;
117  try {
118  idVar = bn.idFromName(name);
119  } catch (gum::NotFound&) {
120  if (!labels.empty()) {
122  } else if (!ticks.empty()) {
124  } else {
126  }
127  }
128 
129  return idVar;
130  }
131 
132  template < typename GUM_SCALAR >
133  BayesNet< GUM_SCALAR > BayesNet< GUM_SCALAR >::fastPrototype(const std::string& dotlike,
134  Size domainSize) {
136 
137 
138  for (const auto& chaine: split(dotlike, ";")) {
139  NodeId lastId = 0;
140  bool notfirst = false;
141  for (const auto& souschaine: split(chaine, "->")) {
142  bool forward = true;
143  for (const auto& node: split(souschaine, "<-")) {
144  auto idVar = build_node(bn, node, domainSize);
145  if (notfirst) {
146  if (forward) {
147  bn.addArc(lastId, idVar);
148  forward = false;
149  } else {
150  bn.addArc(idVar, lastId);
151  }
152  } else {
153  notfirst = true;
154  forward = false;
155  }
156  lastId = idVar;
157  }
158  }
159  }
160  bn.generateCPTs();
161  bn.setProperty("name", "fastPrototype");
162  return bn;
163  }
164 
165  template < typename GUM_SCALAR >
168  }
169 
170  template < typename GUM_SCALAR >
173  }
174 
175  template < typename GUM_SCALAR >
179 
181  }
182 
183  template < typename GUM_SCALAR >
185  if (this != &source) {
188 
191  }
192 
193  return *this;
194  }
195 
196  template < typename GUM_SCALAR >
199  for (const auto p: _probaMap_) {
200  delete p.second;
201  }
202  }
203 
204  template < typename GUM_SCALAR >
206  return _varMap_.get(id);
207  }
208 
209  template < typename GUM_SCALAR >
212  }
213 
214  template < typename GUM_SCALAR >
216  const std::string& old_label,
217  const std::string& new_label) {
218  if (variable(id).varType() != VarType::Labelized)
219  GUM_ERROR(NotFound, "Variable " << id << " is not a LabelizedVariable.")
220 
222  = dynamic_cast< LabelizedVariable* >(const_cast< DiscreteVariable* >(&variable(id)));
223 
225  }
226 
227 
228  template < typename GUM_SCALAR >
230  return _varMap_.get(var);
231  }
232 
233  template < typename GUM_SCALAR >
235  auto ptr = new MultiDimArray< GUM_SCALAR >();
236  try {
237  return add(var, ptr);
238  } catch (Exception&) {
239  delete ptr;
240  throw;
241  }
242  }
243 
244  template < typename GUM_SCALAR >
245  INLINE NodeId BayesNet< GUM_SCALAR >::add(const std::string& name, unsigned int nbrmod) {
246  if (nbrmod < 2) {
248  "Variable " << name << "needs more than " << nbrmod << " modalities")
249  }
250 
251  RangeVariable v(name, name, 0, nbrmod - 1);
252  return add(v);
253  }
254 
255  template < typename GUM_SCALAR >
259 
260  return add(var, aContent, proposedId);
261  }
262 
263  template < typename GUM_SCALAR >
265  auto ptr = new MultiDimArray< GUM_SCALAR >();
266 
267  try {
268  return add(var, ptr, id);
269 
270  } catch (Exception&) {
271  delete ptr;
272  throw;
273  }
274  }
275 
276  template < typename GUM_SCALAR >
279  NodeId id) {
280  _varMap_.insert(id, var);
281  this->dag_.addNodeWithId(id);
282 
283  auto cpt = new Potential< GUM_SCALAR >(aContent);
284  (*cpt) << variable(id);
286  return id;
287  }
288 
289  template < typename GUM_SCALAR >
291  return _varMap_.idFromName(name);
292  }
293 
294  template < typename GUM_SCALAR >
295  INLINE const DiscreteVariable&
298  }
299 
300  template < typename GUM_SCALAR >
302  return *(_probaMap_[varId]);
303  }
304 
305  template < typename GUM_SCALAR >
307  return _varMap_;
308  }
309 
310  template < typename GUM_SCALAR >
312  erase(_varMap_.get(var));
313  }
314 
315  template < typename GUM_SCALAR >
317  if (_varMap_.exists(varId)) {
318  // Reduce the variable child's CPT
319  const NodeSet& children = this->children(varId);
320 
321  for (const auto c: children) {
323  }
324 
325  delete _probaMap_[varId];
326 
329  this->dag_.eraseNode(varId);
330  }
331  }
332 
333  template < typename GUM_SCALAR >
334  void BayesNet< GUM_SCALAR >::clear() {
335  if (!this->empty()) {
336  auto l = this->nodes();
337  for (const auto no: l) {
338  this->erase(no);
339  }
340  }
341  }
342 
343  template < typename GUM_SCALAR >
345  if (this->dag_.existsArc(tail, head)) {
346  GUM_ERROR(DuplicateElement, "The arc (" << tail << "," << head << ") already exists.")
347  }
348 
349  this->dag_.addArc(tail, head);
350  // Add parent in the child's CPT
351  (*(_probaMap_[head])) << variable(tail);
352  }
353 
354  template < typename GUM_SCALAR >
355  INLINE void BayesNet< GUM_SCALAR >::addArc(const std::string& tail, const std::string& head) {
356  try {
357  addArc(this->idFromName(tail), this->idFromName(head));
358  } catch (DuplicateElement) {
359  GUM_ERROR(DuplicateElement, "The arc " << tail << "->" << head << " already exists.")
360  }
361  }
362 
363  template < typename GUM_SCALAR >
365  if (_varMap_.exists(arc.tail()) && _varMap_.exists(arc.head())) {
366  NodeId head = arc.head(), tail = arc.tail();
367  this->dag_.eraseArc(arc);
368  // Remove parent from child's CPT
369  (*(_probaMap_[head])) >> variable(tail);
370  }
371  }
372 
373  template < typename GUM_SCALAR >
375  eraseArc(Arc(tail, head));
376  }
377 
378  template < typename GUM_SCALAR >
379  void BayesNet< GUM_SCALAR >::reverseArc(const Arc& arc) {
380  // check that the arc exists
381  if (!_varMap_.exists(arc.tail()) || !_varMap_.exists(arc.head()) || !dag().existsArc(arc)) {
382  GUM_ERROR(InvalidArc, "a non-existing arc cannot be reversed")
383  }
384 
385  NodeId tail = arc.tail(), head = arc.head();
386 
387  // check that the reversal does not induce a cycle
388  try {
389  DAG d = dag();
390  d.eraseArc(arc);
391  d.addArc(head, tail);
392  } catch (Exception&) {
393  GUM_ERROR(InvalidArc, "this arc reversal would induce a directed cycle")
394  }
395 
396  // with the same notations as Shachter (1986), "evaluating influence
397  // diagrams", p.878, we shall first compute the product of probabilities:
398  // pi_j^old (x_j | x_c^old(j) ) * pi_i^old (x_i | x_c^old(i) )
400 
401  // modify the topology of the graph: add to tail all the parents of head
402  // and add to head all the parents of tail
405  for (const auto node: this->parents(tail))
407  for (const auto node: this->parents(head))
409  // remove arc (head, tail)
410  eraseArc(arc);
411 
412  // add the necessary arcs to the tail
413  for (const auto p: new_parents) {
414  if ((p != tail) && !dag().existsArc(p, tail)) { addArc(p, tail); }
415  }
416 
417  addArc(head, tail);
418  // add the necessary arcs to the head
420 
421  for (const auto p: new_parents) {
422  if ((p != head) && !dag().existsArc(p, head)) { addArc(p, head); }
423  }
424 
426 
427  // update the conditional distributions of head and tail
428  Set< const DiscreteVariable* > del_vars;
429  del_vars << &(variable(tail));
431 
432  auto& cpt_head = const_cast< Potential< GUM_SCALAR >& >(cpt(head));
434 
436  auto& cpt_tail = const_cast< Potential< GUM_SCALAR >& >(cpt(tail));
438  }
439 
440  template < typename GUM_SCALAR >
442  reverseArc(Arc(tail, head));
443  }
444 
445 
446  //==============================================
447  // Aggregators
448  //=============================================
449  template < typename GUM_SCALAR >
451  return add(var, new aggregator::Amplitude< GUM_SCALAR >());
452  }
453 
454  template < typename GUM_SCALAR >
456  if (var.domainSize() > 2) GUM_ERROR(SizeError, "an AND has to be boolean")
457 
458  return add(var, new aggregator::And< GUM_SCALAR >());
459  }
460 
461  template < typename GUM_SCALAR >
463  return add(var, new aggregator::Count< GUM_SCALAR >(value));
464  }
465 
466  template < typename GUM_SCALAR >
468  if (var.domainSize() > 2) GUM_ERROR(SizeError, "an EXISTS has to be boolean")
469 
470  return add(var, new aggregator::Exists< GUM_SCALAR >(value));
471  }
472 
473  template < typename GUM_SCALAR >
475  if (var.domainSize() > 2) GUM_ERROR(SizeError, "an EXISTS has to be boolean")
476 
477  return add(var, new aggregator::Forall< GUM_SCALAR >(value));
478  }
479 
480  template < typename GUM_SCALAR >
482  return add(var, new aggregator::Max< GUM_SCALAR >());
483  }
484 
485  template < typename GUM_SCALAR >
487  return add(var, new aggregator::Median< GUM_SCALAR >());
488  }
489 
490  template < typename GUM_SCALAR >
492  return add(var, new aggregator::Min< GUM_SCALAR >());
493  }
494 
495  template < typename GUM_SCALAR >
497  if (var.domainSize() > 2) GUM_ERROR(SizeError, "an OR has to be boolean")
498 
499  return add(var, new aggregator::Or< GUM_SCALAR >());
500  }
501 
502  template < typename GUM_SCALAR >
504  return add(var, new aggregator::Sum< GUM_SCALAR >());
505  }
506 
507  //================================
508  // ICIModels
509  //================================
510  template < typename GUM_SCALAR >
514  }
515 
516  template < typename GUM_SCALAR >
520  }
521 
522  template < typename GUM_SCALAR >
526  }
527 
528  template < typename GUM_SCALAR >
532  }
533 
534  template < typename GUM_SCALAR >
538  }
539 
540  template < typename GUM_SCALAR >
543  NodeId id) {
545  }
546 
547  template < typename GUM_SCALAR >
550  NodeId id) {
552  }
553 
554  template < typename GUM_SCALAR >
557  NodeId id) {
558  return add(var, new MultiDimLogit< GUM_SCALAR >(external_weight), id);
559  }
560 
561  template < typename GUM_SCALAR >
564  NodeId id) {
566  }
567 
568  template < typename GUM_SCALAR >
571  NodeId id) {
573  }
574 
575  template < typename GUM_SCALAR >
577  auto* CImodel = dynamic_cast< const MultiDimICIModel< GUM_SCALAR >* >(cpt(head).content());
578 
579  if (CImodel != 0) {
580  // or is OK
581  addArc(tail, head);
582 
584  } else {
586  "Head variable (" << variable(head).name() << ") is not a CIModel variable !")
587  }
588  }
589 
590  template < typename GUM_SCALAR >
592  output << bn.toString();
593  return output;
594  }
595 
596  /// begin Multiple Change for all CPTs
597  template < typename GUM_SCALAR >
599  for (const auto node: nodes())
601  }
602 
603  /// end Multiple Change for all CPTs
604  template < typename GUM_SCALAR >
606  for (const auto node: nodes())
608  }
609 
610  /// clear all potentials
611  template < typename GUM_SCALAR >
613  // Removing previous potentials
614  for (const auto& elt: _probaMap_) {
615  delete elt.second;
616  }
617 
618  _probaMap_.clear();
619  }
620 
621  /// copy of potentials from a BN to another, using names of vars as ref.
622  template < typename GUM_SCALAR >
624  // Copying potentials
625 
626  for (const auto src: source._probaMap_) {
627  // First we build the node's CPT
630  for (gum::Idx i = 0; i < src.second->nbrDim(); i++) {
632  }
635 
636  // We add the CPT to the CPT hashmap
638  }
639  }
640 
641  template < typename GUM_SCALAR >
643  for (const auto node: nodes())
644  generateCPT(node);
645  }
646 
647  template < typename GUM_SCALAR >
650 
652  }
653 
654  template < typename GUM_SCALAR >
656  if (cpt(id).nbrDim() != newPot->nbrDim()) {
658  "cannot exchange potentials with different "
659  "dimensions for variable with id "
660  << id)
661  }
662 
663  for (Idx i = 0; i < cpt(id).nbrDim(); i++) {
664  if (&cpt(id).variable(i) != &(newPot->variable(i))) {
666  "cannot exchange potentials because, for variable with id "
667  << id << ", dimension " << i << " differs. ")
668  }
669  }
670 
672  }
673 
674  template < typename GUM_SCALAR >
676  delete _probaMap_[id];
677  _probaMap_[id] = newPot;
678  }
679 
680  template < typename GUM_SCALAR >
684  }
685 
686 } /* namespace gum */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:643
NodeId build_node(gum::BayesNet< GUM_SCALAR > &bn, std::string node, gum::Size default_domain_size)
Definition: BayesNet_tpl.h:61