aGrUM  0.14.1
BayesNet_tpl.h
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005 by Pierre-Henri WUILLEMIN et Christophe GONZALES *
3  * {prenom.nom}_at_lip6.fr *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the *
17  * Free Software Foundation, Inc., *
18  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19  ***************************************************************************/
27 #include <limits>
28 #include <set>
29 
30 #include <agrum/BN/BayesNet.h>
31 #include <agrum/BN/IBayesNet.h>
32 #include <agrum/BN/IBayesNet.h>
33 
35 
45 
49 
51 
54 
55 namespace gum {
56  template < typename GUM_SCALAR >
58  std::string node,
59  gum::Size domainSize) {
60  std::string name = node;
61  auto ds = domainSize;
62  std::vector< std::string > labels;
63 
64  // node like "n[5]"
65  auto posBrack = node.find('[');
66  if (posBrack != std::string::npos) {
67  if (*(node.rbegin()) != ']')
68  name = node; // a name with '[' inside but no ']' at the end
69  else {
70  name = node.substr(0, posBrack);
71  ds = static_cast< Size >(
72  std::stoi(node.substr(posBrack + 1, node.size() - posBrack - 2)));
73  }
74  }
75 
76  // node like "n{one|two|three}"
77  posBrack = node.find('{');
78  if (posBrack != std::string::npos) {
79  if (*(node.rbegin()) != '}')
80  name = node; // a name with '{' inside but no '}' at the end
81  else {
82  name = node.substr(0, posBrack);
83  labels = split(node.substr(posBrack + 1, node.size() - posBrack - 2), "|");
84  if (labels.size() < 2) {
85  GUM_ERROR(InvalidArgument, "Not enough labels in node " << node);
86  }
87  if (!hasUniqueElts(labels)) {
88  GUM_ERROR(InvalidArgument, "Duplicate labels in node " << node);
89  }
90  ds = static_cast< Size >(labels.size());
91  }
92  }
93 
94  // now we add the node in the BN
95  NodeId idVar;
96  try {
97  idVar = bn.idFromName(name);
98  } catch (gum::NotFound&) {
99  if (labels.empty()) {
100  idVar = bn.add(LabelizedVariable(name, name, ds));
101  } else {
102  auto l = LabelizedVariable(name, name, 0);
103  for (const auto& label : labels) {
104  l.addLabel(label);
105  }
106  idVar = bn.add(l);
107  }
108  }
109 
110  return idVar;
111  }
112 
113  template < typename GUM_SCALAR >
115  BayesNet< GUM_SCALAR >::fastPrototype(const std::string& dotlike,
116  Size domainSize) {
118 
119 
120  for (const auto& chaine : split(dotlike, ";")) {
121  NodeId lastId = 0;
122  bool notfirst = false;
123  for (const auto& souschaine : split(chaine, "->")) {
124  bool forward = true;
125  for (const auto& node : split(souschaine, "<-")) {
126  auto idVar = build_node(bn, node, domainSize);
127  if (notfirst) {
128  if (forward) {
129  bn.addArc(lastId, idVar);
130  forward = false;
131  } else {
132  bn.addArc(idVar, lastId);
133  }
134  } else {
135  notfirst = true;
136  forward = false;
137  }
138  lastId = idVar;
139  }
140  }
141  }
142  bn.generateCPTs();
143  bn.setProperty("name", dotlike);
144  return bn;
145  }
146 
147  template < typename GUM_SCALAR >
148  INLINE BayesNet< GUM_SCALAR >::BayesNet() : IBayesNet< GUM_SCALAR >() {
149  GUM_CONSTRUCTOR(BayesNet);
150  }
151 
152  template < typename GUM_SCALAR >
153  INLINE BayesNet< GUM_SCALAR >::BayesNet(std::string name) :
154  IBayesNet< GUM_SCALAR >(name) {
155  GUM_CONSTRUCTOR(BayesNet);
156  }
157 
158  template < typename GUM_SCALAR >
160  IBayesNet< GUM_SCALAR >(source), __varMap(source.__varMap) {
161  GUM_CONS_CPY(BayesNet);
162 
163  __copyPotentials(source);
164  }
165 
166  template < typename GUM_SCALAR >
169  if (this != &source) {
171  __varMap = source.__varMap;
172 
173  __clearPotentials();
174  __copyPotentials(source);
175  }
176 
177  return *this;
178  }
179 
180  template < typename GUM_SCALAR >
182  GUM_DESTRUCTOR(BayesNet);
183  for (const auto p : __probaMap) {
184  delete p.second;
185  }
186  }
187 
188  template < typename GUM_SCALAR >
189  INLINE const DiscreteVariable&
191  return __varMap.get(id);
192  }
193 
194  template < typename GUM_SCALAR >
195  INLINE void
197  const std::string& new_name) {
198  __varMap.changeName(id, new_name);
199  }
200 
201  template < typename GUM_SCALAR >
203  NodeId id, const std::string& old_label, const std::string& new_label) {
204  if (variable(id).varType() != VarType::Labelized) {
205  GUM_ERROR(NotFound, "Variable " << id << " is not a LabelizedVariable.");
206  }
207  LabelizedVariable* var = dynamic_cast< LabelizedVariable* >(
208  const_cast< DiscreteVariable* >(&variable(id)));
209 
210  var->changeLabel(var->posLabel(old_label), new_label);
211  }
212 
213 
214  template < typename GUM_SCALAR >
216  return __varMap.get(var);
217  }
218 
219  template < typename GUM_SCALAR >
221  auto ptr = new MultiDimArray< GUM_SCALAR >();
222  NodeId res = 0;
223 
224  try {
225  res = add(var, ptr);
226 
227  } catch (Exception&) {
228  delete ptr;
229  throw;
230  }
231 
232  return res;
233  }
234 
235  template < typename GUM_SCALAR >
236  INLINE NodeId BayesNet< GUM_SCALAR >::add(const std::string& name,
237  unsigned int nbrmod) {
238  if (nbrmod < 2) {
240  "Variable " << name << "needs more than " << nbrmod
241  << " modalities");
242  }
243 
244  LabelizedVariable v(name, name, nbrmod);
245  return add(v);
246  }
247 
248  template < typename GUM_SCALAR >
251  NodeId proposedId = dag().nextNodeId();
252  NodeId res = 0;
253 
254  res = add(var, aContent, proposedId);
255 
256  return res;
257  }
258 
259  template < typename GUM_SCALAR >
261  NodeId id) {
262  auto ptr = new MultiDimArray< GUM_SCALAR >();
263  NodeId res = 0;
264 
265  try {
266  res = add(var, ptr, id);
267 
268  } catch (Exception&) {
269  delete ptr;
270  throw;
271  }
272 
273  return res;
274  }
275 
276  template < typename GUM_SCALAR >
277  NodeId
280  NodeId id) {
281  __varMap.insert(id, var);
282  this->_dag.addNodeWithId(id);
283 
284  auto cpt = new Potential< GUM_SCALAR >(aContent);
285  (*cpt) << variable(id);
286  __probaMap.insert(id, cpt);
287  return id;
288  }
289 
290  template < typename GUM_SCALAR >
291  INLINE NodeId BayesNet< GUM_SCALAR >::idFromName(const std::string& name) const {
292  return __varMap.idFromName(name);
293  }
294 
295  template < typename GUM_SCALAR >
296  INLINE const DiscreteVariable&
297  BayesNet< GUM_SCALAR >::variableFromName(const std::string& name) const {
298  return __varMap.variableFromName(name);
299  }
300 
301  template < typename GUM_SCALAR >
302  INLINE const Potential< GUM_SCALAR >&
304  return *(__probaMap[varId]);
305  }
306 
307  template < typename GUM_SCALAR >
309  return __varMap;
310  }
311 
312  template < typename GUM_SCALAR >
314  erase(__varMap.get(var));
315  }
316 
317  template < typename GUM_SCALAR >
319  if (__varMap.exists(varId)) {
320  // Reduce the variable child's CPT
321  const NodeSet& children = this->children(varId);
322 
323  for (const auto c : children) {
324  __probaMap[c]->erase(variable(varId));
325  }
326 
327  delete __probaMap[varId];
328 
329  __probaMap.erase(varId);
330  __varMap.erase(varId);
331  this->_dag.eraseNode(varId);
332  }
333  }
334 
335  template < typename GUM_SCALAR >
336  INLINE void BayesNet< GUM_SCALAR >::addArc(NodeId tail, NodeId head) {
337  this->_dag.addArc(tail, head);
338  // Add parent in the child's CPT
339  (*(__probaMap[head])) << variable(tail);
340  }
341 
342  template < typename GUM_SCALAR >
343  INLINE void BayesNet< GUM_SCALAR >::eraseArc(const Arc& arc) {
344  if (__varMap.exists(arc.tail()) && __varMap.exists(arc.head())) {
345  NodeId head = arc.head(), tail = arc.tail();
346  this->_dag.eraseArc(arc);
347  // Remove parent froms child's CPT
348  (*(__probaMap[head])) >> variable(tail);
349  }
350  }
351 
352  template < typename GUM_SCALAR >
354  eraseArc(Arc(tail, head));
355  }
356 
357  template < typename GUM_SCALAR >
359  // check that the arc exsists
360  if (!__varMap.exists(arc.tail()) || !__varMap.exists(arc.head())
361  || !dag().existsArc(arc)) {
362  GUM_ERROR(InvalidArc, "a nonexisting arc cannot be reversed");
363  }
364 
365  NodeId tail = arc.tail(), head = arc.head();
366 
367  // check that the reversal does not induce a cycle
368  try {
369  DAG d = dag();
370  d.eraseArc(arc);
371  d.addArc(head, tail);
372  } catch (Exception&) {
373  GUM_ERROR(InvalidArc, "this arc reversal would induce a directed cycle");
374  }
375 
376  // with the same notations as Shachter (1986), "evaluating influence
377  // diagrams",
378  // p.878, we shall first compute the product of probabilities:
379  // pi_j^old (x_j | x_c^old(j) ) * pi_i^old (x_i | x_c^old(i) )
380  Potential< GUM_SCALAR > prod{cpt(tail) * cpt(head)};
381 
382  // modify the topology of the graph: add to tail all the parents of head
383  // and add to head all the parents of tail
384  beginTopologyTransformation();
385  NodeSet new_parents;
386  for (const auto node : this->parents(tail))
387  new_parents.insert(node);
388  for (const auto node : this->parents(head))
389  new_parents.insert(node);
390  // remove arc (head, tail)
391  eraseArc(arc);
392 
393  // add the necessary arcs to the tail
394  for (const auto p : new_parents) {
395  if ((p != tail) && !dag().existsArc(p, tail)) { addArc(p, tail); }
396  }
397 
398  addArc(head, tail);
399  // add the necessary arcs to the head
400  new_parents.erase(tail);
401 
402  for (const auto p : new_parents) {
403  if ((p != head) && !dag().existsArc(p, head)) { addArc(p, head); }
404  }
405 
406  endTopologyTransformation();
407 
408  // update the conditional distributions of head and tail
410  del_vars << &(variable(tail));
411  Potential< GUM_SCALAR > new_cpt_head = prod.margSumOut(del_vars);
412  auto& cpt_head = const_cast< Potential< GUM_SCALAR >& >(cpt(head));
413  cpt_head = std::move(new_cpt_head);
414 
415  Potential< GUM_SCALAR > new_cpt_tail{prod / cpt_head};
416  auto& cpt_tail = const_cast< Potential< GUM_SCALAR >& >(cpt(tail));
417  cpt_tail = std::move(new_cpt_tail);
418  }
419 
420  template < typename GUM_SCALAR >
422  reverseArc(Arc(tail, head));
423  }
424 
425 
426  //==============================================
427  // Aggregators
428  //=============================================
429  template < typename GUM_SCALAR >
431  return add(var, new aggregator::Amplitude< GUM_SCALAR >());
432  }
433 
434  template < typename GUM_SCALAR >
436  if (var.domainSize() > 2) GUM_ERROR(SizeError, "an AND has to be boolean");
437 
438  return add(var, new aggregator::And< GUM_SCALAR >());
439  }
440 
441  template < typename GUM_SCALAR >
443  Idx value) {
444  return add(var, new aggregator::Count< GUM_SCALAR >(value));
445  }
446 
447  template < typename GUM_SCALAR >
449  Idx value) {
450  if (var.domainSize() > 2) GUM_ERROR(SizeError, "an EXISTS has to be boolean");
451 
452  return add(var, new aggregator::Exists< GUM_SCALAR >(value));
453  }
454 
455  template < typename GUM_SCALAR >
457  Idx value) {
458  if (var.domainSize() > 2) GUM_ERROR(SizeError, "an EXISTS has to be boolean");
459 
460  return add(var, new aggregator::Forall< GUM_SCALAR >(value));
461  }
462 
463  template < typename GUM_SCALAR >
465  return add(var, new aggregator::Max< GUM_SCALAR >());
466  }
467 
468  template < typename GUM_SCALAR >
470  return add(var, new aggregator::Median< GUM_SCALAR >());
471  }
472 
473  template < typename GUM_SCALAR >
475  return add(var, new aggregator::Min< GUM_SCALAR >());
476  }
477 
478  template < typename GUM_SCALAR >
480  if (var.domainSize() > 2) GUM_ERROR(SizeError, "an OR has to be boolean");
481 
482  return add(var, new aggregator::Or< GUM_SCALAR >());
483  }
484 
485 
486  //================================
487  // ICIModels
488  //================================
489  template < typename GUM_SCALAR >
491  GUM_SCALAR external_weight) {
492  return addNoisyORCompound(var, external_weight);
493  }
494 
495  template < typename GUM_SCALAR >
497  const DiscreteVariable& var, GUM_SCALAR external_weight) {
498  return add(var, new MultiDimNoisyORCompound< GUM_SCALAR >(external_weight));
499  }
500 
501  template < typename GUM_SCALAR >
503  GUM_SCALAR external_weight) {
504  return add(var, new MultiDimNoisyORNet< GUM_SCALAR >(external_weight));
505  }
506 
507  template < typename GUM_SCALAR >
509  GUM_SCALAR external_weight) {
510  return add(var, new MultiDimNoisyAND< GUM_SCALAR >(external_weight));
511  }
512 
513  template < typename GUM_SCALAR >
515  GUM_SCALAR external_weight) {
516  return add(var, new MultiDimLogit< GUM_SCALAR >(external_weight));
517  }
518 
519  template < typename GUM_SCALAR >
521  GUM_SCALAR external_weight,
522  NodeId id) {
523  return addNoisyORCompound(var, external_weight, id);
524  }
525 
526  template < typename GUM_SCALAR >
528  GUM_SCALAR external_weight,
529  NodeId id) {
530  return add(var, new MultiDimNoisyAND< GUM_SCALAR >(external_weight), id);
531  }
532 
533  template < typename GUM_SCALAR >
535  GUM_SCALAR external_weight,
536  NodeId id) {
537  return add(var, new MultiDimLogit< GUM_SCALAR >(external_weight), id);
538  }
539 
540  template < typename GUM_SCALAR >
542  const DiscreteVariable& var, GUM_SCALAR external_weight, NodeId id) {
543  return add(
544  var, new MultiDimNoisyORCompound< GUM_SCALAR >(external_weight), id);
545  }
546 
547  template < typename GUM_SCALAR >
549  GUM_SCALAR external_weight,
550  NodeId id) {
551  return add(var, new MultiDimNoisyORNet< GUM_SCALAR >(external_weight), id);
552  }
553 
554  template < typename GUM_SCALAR >
556  NodeId head,
557  GUM_SCALAR causalWeight) {
558  auto* CImodel =
559  dynamic_cast< const MultiDimICIModel< GUM_SCALAR >* >(cpt(head).content());
560 
561  if (CImodel != 0) {
562  // or is OK
563  addArc(tail, head);
564 
565  CImodel->causalWeight(variable(tail), causalWeight);
566  } else {
568  "Head variable (" << variable(head).name()
569  << ") is not a CIModel variable !");
570  }
571  }
572 
573  template < typename GUM_SCALAR >
574  INLINE std::ostream& operator<<(std::ostream& output,
575  const BayesNet< GUM_SCALAR >& bn) {
576  output << bn.toString();
577  return output;
578  }
579 
581  template < typename GUM_SCALAR >
583  for (const auto node : nodes())
584  __probaMap[node]->beginMultipleChanges();
585  }
586 
588  template < typename GUM_SCALAR >
590  for (const auto node : nodes())
591  __probaMap[node]->endMultipleChanges();
592  }
593 
595  template < typename GUM_SCALAR >
597  // Removing previous potentials
598  for (const auto& elt : __probaMap) {
599  delete elt.second;
600  }
601 
602  __probaMap.clear();
603  }
604 
606  template < typename GUM_SCALAR >
608  const BayesNet< GUM_SCALAR >& source) {
609  // Copying potentials
610 
611  for (const auto src : source.__probaMap) {
612  // First we build the node's CPT
614  copy_array->beginMultipleChanges();
615  for (gum::Idx i = 0; i < src.second->nbrDim(); i++) {
616  (*copy_array) << variableFromName(src.second->variable(i).name());
617  }
618  copy_array->endMultipleChanges();
619  copy_array->copyFrom(*(src.second));
620 
621  // We add the CPT to the CPT's hashmap
622  __probaMap.insert(src.first, copy_array);
623  }
624  }
625 
626  template < typename GUM_SCALAR >
628  for (const auto node : nodes())
629  generateCPT(node);
630  }
631  template < typename GUM_SCALAR >
632  INLINE void BayesNet< GUM_SCALAR >::generateCPT(NodeId node) const {
634 
635  generator.generateCPT(cpt(node).pos(variable(node)), cpt(node));
636  }
637 
638  template < typename GUM_SCALAR >
640  Potential< GUM_SCALAR >* newPot) {
641  if (cpt(id).nbrDim() != newPot->nbrDim()) {
643  "cannot exchange potentials with different "
644  "dimensions for variable with id "
645  << id);
646  }
647 
648  for (Idx i = 0; i < cpt(id).nbrDim(); i++) {
649  if (&cpt(id).variable(i) != &(newPot->variable(i))) {
651  "cannot exchange potentials because, for variable with id "
652  << id << ", dimension " << i << " differs. ");
653  }
654  }
655 
656  _unsafeChangePotential(id, newPot);
657  }
658 
659  template < typename GUM_SCALAR >
661  NodeId id, Potential< GUM_SCALAR >* newPot) {
662  delete __probaMap[id];
663  __probaMap[id] = newPot;
664  }
665 
666  template < typename GUM_SCALAR >
667  void BayesNet< GUM_SCALAR >::changePotential(const std::string& name,
668  Potential< GUM_SCALAR >* newPot) {
669  changePotential(idFromName(name), newPot);
670  }
671 
672 } /* namespace gum */
void addArc(NodeId tail, NodeId head)
Add an arc in the BN, and update arc.head&#39;s CPT.
Definition: BayesNet_tpl.h:336
Noisy OR representation.
aGrUM&#39;s Potential is a multi-dimensional array with tensor operators.
Definition: potential.h:57
Class representing a Bayesian Network.
Definition: BayesNet.h:76
virtual void beginMultipleChanges() final
Default implementation of MultiDimContainer::set().
virtual Idx nbrDim() const final
Returns the number of vars in the multidimensional container.
Abstract class for generating Conditional Probability Tables.
void changeLabel(Idx pos, const std::string &aLabel) const
change a label for this index
bool hasUniqueElts(std::vector< T > const &x)
NodeId build_node(gum::BayesNet< GUM_SCALAR > &bn, std::string node, gum::Size domainSize)
Definition: BayesNet_tpl.h:57
class LabelizedVariable
class for NoisyOR-net implementation as multiDim
or aggregator
Definition: or.h:53
NodeId add(const DiscreteVariable &var)
Add a variable to the gum::BayesNet.
Definition: BayesNet_tpl.h:220
virtual void eraseArc(const Arc &arc)
removes an arc from the ArcGraphPart
VariableNodeMap __varMap
the map between variable and id
Definition: BayesNet.h:648
forall aggregator
Container used to map discrete variables with nodes.
BayesNet()
Default constructor.
Definition: BayesNet_tpl.h:148
Class representing Bayesian networks.
median aggregator
class for LOGIT implementation as multiDim
Class representing Bayesian networks.
class for multiDimNoisyORCompound
Base class for discrete random variable.
Class representing the minimal interface for Bayesian Network.
Definition: IBayesNet.h:59
gum is the global namespace for all aGrUM entities
Definition: agrum.h:25
std::vector< std::string > split(const std::string &str, const std::string &delim)
Split str using the delimiter.
NodeId head() const
returns the head of the arc
static BayesNet< GUM_SCALAR > fastPrototype(const std::string &dotlike, Size domainSize=2)
Create a bn with a dotlike syntax : &#39;a->b->c;b->d;&#39;.
Definition: BayesNet_tpl.h:115
min aggregator
forall aggregator
Definition: forall.h:52
const DiscreteVariable & variableFromName(const std::string &name) const final
Returns a variable given its name in the gum::BayesNet.
Definition: BayesNet_tpl.h:297
virtual Size domainSize() const =0
And aggregator.
Definition: and.h:52
Logit representation.
Definition: multiDimLogit.h:50
or aggregator
exists aggregator
Definition: exists.h:51
virtual void endMultipleChanges() final
Default implementation of MultiDimContainer::set().
void setProperty(const std::string &name, const std::string &value)
Add or change a property of this DAGModel.
Definition: DAGmodel_inl.h:53
abstract class for Conditional Indepency Models
The base class for all directed edgesThis class is used as a basis for manipulating all directed edge...
forall aggregator
max aggregator
virtual const DiscreteVariable & variable(Idx) const final
Returns a const ref to the ith var.
Base class for all aGrUM&#39;s exceptions.
Definition: exceptions.h:103
Idx posLabel(const std::string &label) const
return the pos from label
Multidimensional matrix stored as an array in memory.
Definition: multiDimArray.h:51
virtual void addArc(const NodeId tail, const NodeId head)
insert a new arc into the directed graph
Definition: DAG_inl.h:40
<agrum/BN/generator/simpleCPTGenerator.h>
NodeProperty< Potential< GUM_SCALAR > *> __probaMap
Mapping between the variable&#39;s id and their CPT.
Definition: BayesNet.h:651
Noisy AND representation.
void generateCPTs() const
randomly generates CPTs for a given structure
Definition: BayesNet_tpl.h:627
virtual void copyFrom(const MultiDimContainer< GUM_SCALAR > &src) const
Basic copy of a MultiDimContainer.
INLINE std::ostream & operator<<(std::ostream &output, const BayesNet< GUM_SCALAR > &bn)
Prints map&#39;s DAG in output using the Graphviz-dot format.
Definition: BayesNet_tpl.h:574
count aggregator
Potential< GUM_SCALAR > margSumOut(const Set< const DiscreteVariable * > &del_vars) const
Projection using sum as operation (and implementation-optimized operations)
std::string toString() const
count aggregator
class for NoisyAND-net implementation as multiDim
median aggregator
<agrum/multidim/multiDimImplementation.h>
Size Idx
Type for indexes.
Definition: types.h:50
Utilities for manipulating strings.
max aggregator
Definition: max.h:51
median aggregator
Definition: median.h:57
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:45
NodeId idFromName(const std::string &name) const final
Returns a variable&#39;s id given its name in the gum::BayesNet.
Definition: BayesNet_tpl.h:291
Base class for labelized discrete random variables.
Base class for dag.
Definition: DAG.h:99
amplitude aggregator
Definition: amplitude.h:52
Size NodeId
Type for node ids.
Definition: graphElements.h:97
void insert(const Key &k)
Inserts a new element into the set.
Definition: set_tpl.h:610
void generateCPT(const Idx &varId, const Potential< GUM_SCALAR > &cpt) override
Generates a CPT using floats.
min aggregator
Definition: min.h:50
NodeId tail() const
returns the tail of the arc
#define GUM_ERROR(type, msg)
Definition: exceptions.h:52
count aggregator
Definition: count.h:54