aGrUM  0.14.3
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 
36 
46 
50 
52 
55 
56 namespace gum {
57  template < typename GUM_SCALAR >
59  std::string node,
60  gum::Size domainSize) {
61  std::string name = node;
62  auto ds = domainSize;
63  std::vector< std::string > labels;
64 
65  // node like "n[5]"
66  auto posBrack = node.find('[');
67  if (posBrack != std::string::npos) {
68  if (*(node.rbegin()) != ']')
69  name = node; // a name with '[' inside but no ']' at the end
70  else {
71  name = node.substr(0, posBrack);
72  ds = static_cast< Size >(
73  std::stoi(node.substr(posBrack + 1, node.size() - posBrack - 2)));
74  }
75  }
76 
77  // node like "n{one|two|three}"
78  posBrack = node.find('{');
79  if (posBrack != std::string::npos) {
80  if (*(node.rbegin()) != '}')
81  name = node; // a name with '{' inside but no '}' at the end
82  else {
83  name = node.substr(0, posBrack);
84  labels = split(node.substr(posBrack + 1, node.size() - posBrack - 2), "|");
85  if (labels.size() < 2) {
86  GUM_ERROR(InvalidArgument, "Not enough labels in node " << node);
87  }
88  if (!hasUniqueElts(labels)) {
89  GUM_ERROR(InvalidArgument, "Duplicate labels in node " << node);
90  }
91  ds = static_cast< Size >(labels.size());
92  }
93  }
94 
95  if (ds == 0) {
96  GUM_ERROR(InvalidArgument, "No value for variable " << name << ".");
97  } else if (ds == 1) {
99  "Only one value for variable " << name
100  << " (2 at least are needed).");
101  }
102 
103  // now we add the node in the BN
104  NodeId idVar;
105  try {
106  idVar = bn.idFromName(name);
107  } catch (gum::NotFound&) {
108  if (labels.empty()) {
109  idVar = bn.add(RangeVariable(name, name, 0, ds - 1));
110  } else {
111  auto l = LabelizedVariable(name, name, 0);
112  for (const auto& label : labels) {
113  l.addLabel(label);
114  }
115  idVar = bn.add(l);
116  }
117  }
118 
119  return idVar;
120  }
121 
122  template < typename GUM_SCALAR >
124  BayesNet< GUM_SCALAR >::fastPrototype(const std::string& dotlike,
125  Size domainSize) {
127 
128 
129  for (const auto& chaine : split(dotlike, ";")) {
130  NodeId lastId = 0;
131  bool notfirst = false;
132  for (const auto& souschaine : split(chaine, "->")) {
133  bool forward = true;
134  for (const auto& node : split(souschaine, "<-")) {
135  auto idVar = build_node(bn, node, domainSize);
136  if (notfirst) {
137  if (forward) {
138  bn.addArc(lastId, idVar);
139  forward = false;
140  } else {
141  bn.addArc(idVar, lastId);
142  }
143  } else {
144  notfirst = true;
145  forward = false;
146  }
147  lastId = idVar;
148  }
149  }
150  }
151  bn.generateCPTs();
152  bn.setProperty("name", dotlike);
153  return bn;
154  }
155 
156  template < typename GUM_SCALAR >
157  INLINE BayesNet< GUM_SCALAR >::BayesNet() : IBayesNet< GUM_SCALAR >() {
158  GUM_CONSTRUCTOR(BayesNet);
159  }
160 
161  template < typename GUM_SCALAR >
162  INLINE BayesNet< GUM_SCALAR >::BayesNet(std::string name) :
163  IBayesNet< GUM_SCALAR >(name) {
164  GUM_CONSTRUCTOR(BayesNet);
165  }
166 
167  template < typename GUM_SCALAR >
169  IBayesNet< GUM_SCALAR >(source), __varMap(source.__varMap) {
170  GUM_CONS_CPY(BayesNet);
171 
172  __copyPotentials(source);
173  }
174 
175  template < typename GUM_SCALAR >
178  if (this != &source) {
180  __varMap = source.__varMap;
181 
182  __clearPotentials();
183  __copyPotentials(source);
184  }
185 
186  return *this;
187  }
188 
189  template < typename GUM_SCALAR >
191  GUM_DESTRUCTOR(BayesNet);
192  for (const auto p : __probaMap) {
193  delete p.second;
194  }
195  }
196 
197  template < typename GUM_SCALAR >
198  INLINE const DiscreteVariable&
200  return __varMap.get(id);
201  }
202 
203  template < typename GUM_SCALAR >
204  INLINE void
206  const std::string& new_name) {
207  __varMap.changeName(id, new_name);
208  }
209 
210  template < typename GUM_SCALAR >
212  NodeId id, const std::string& old_label, const std::string& new_label) {
213  if (variable(id).varType() != VarType::Labelized) {
214  GUM_ERROR(NotFound, "Variable " << id << " is not a LabelizedVariable.");
215  }
216  LabelizedVariable* var = dynamic_cast< LabelizedVariable* >(
217  const_cast< DiscreteVariable* >(&variable(id)));
218 
219  var->changeLabel(var->posLabel(old_label), new_label);
220  }
221 
222 
223  template < typename GUM_SCALAR >
225  return __varMap.get(var);
226  }
227 
228  template < typename GUM_SCALAR >
230  auto ptr = new MultiDimArray< GUM_SCALAR >();
231  NodeId res = 0;
232 
233  try {
234  res = add(var, ptr);
235 
236  } catch (Exception&) {
237  delete ptr;
238  throw;
239  }
240 
241  return res;
242  }
243 
244  template < typename GUM_SCALAR >
245  INLINE NodeId BayesNet< GUM_SCALAR >::add(const std::string& name,
246  unsigned int nbrmod) {
247  if (nbrmod < 2) {
249  "Variable " << name << "needs more than " << nbrmod
250  << " modalities");
251  }
252 
253  LabelizedVariable v(name, name, nbrmod);
254  return add(v);
255  }
256 
257  template < typename GUM_SCALAR >
260  NodeId proposedId = dag().nextNodeId();
261  NodeId res = 0;
262 
263  res = add(var, aContent, proposedId);
264 
265  return res;
266  }
267 
268  template < typename GUM_SCALAR >
270  NodeId id) {
271  auto ptr = new MultiDimArray< GUM_SCALAR >();
272  NodeId res = 0;
273 
274  try {
275  res = add(var, ptr, id);
276 
277  } catch (Exception&) {
278  delete ptr;
279  throw;
280  }
281 
282  return res;
283  }
284 
285  template < typename GUM_SCALAR >
286  NodeId
289  NodeId id) {
290  __varMap.insert(id, var);
291  this->_dag.addNodeWithId(id);
292 
293  auto cpt = new Potential< GUM_SCALAR >(aContent);
294  (*cpt) << variable(id);
295  __probaMap.insert(id, cpt);
296  return id;
297  }
298 
299  template < typename GUM_SCALAR >
300  INLINE NodeId BayesNet< GUM_SCALAR >::idFromName(const std::string& name) const {
301  return __varMap.idFromName(name);
302  }
303 
304  template < typename GUM_SCALAR >
305  INLINE const DiscreteVariable&
306  BayesNet< GUM_SCALAR >::variableFromName(const std::string& name) const {
307  return __varMap.variableFromName(name);
308  }
309 
310  template < typename GUM_SCALAR >
311  INLINE const Potential< GUM_SCALAR >&
313  return *(__probaMap[varId]);
314  }
315 
316  template < typename GUM_SCALAR >
318  return __varMap;
319  }
320 
321  template < typename GUM_SCALAR >
323  erase(__varMap.get(var));
324  }
325 
326  template < typename GUM_SCALAR >
328  if (__varMap.exists(varId)) {
329  // Reduce the variable child's CPT
330  const NodeSet& children = this->children(varId);
331 
332  for (const auto c : children) {
333  __probaMap[c]->erase(variable(varId));
334  }
335 
336  delete __probaMap[varId];
337 
338  __probaMap.erase(varId);
339  __varMap.erase(varId);
340  this->_dag.eraseNode(varId);
341  }
342  }
343 
344  template < typename GUM_SCALAR >
345  INLINE void BayesNet< GUM_SCALAR >::addArc(NodeId tail, NodeId head) {
346  this->_dag.addArc(tail, head);
347  // Add parent in the child's CPT
348  (*(__probaMap[head])) << variable(tail);
349  }
350 
351  template < typename GUM_SCALAR >
352  INLINE void BayesNet< GUM_SCALAR >::eraseArc(const Arc& arc) {
353  if (__varMap.exists(arc.tail()) && __varMap.exists(arc.head())) {
354  NodeId head = arc.head(), tail = arc.tail();
355  this->_dag.eraseArc(arc);
356  // Remove parent froms child's CPT
357  (*(__probaMap[head])) >> variable(tail);
358  }
359  }
360 
361  template < typename GUM_SCALAR >
363  eraseArc(Arc(tail, head));
364  }
365 
366  template < typename GUM_SCALAR >
368  // check that the arc exsists
369  if (!__varMap.exists(arc.tail()) || !__varMap.exists(arc.head())
370  || !dag().existsArc(arc)) {
371  GUM_ERROR(InvalidArc, "a nonexisting arc cannot be reversed");
372  }
373 
374  NodeId tail = arc.tail(), head = arc.head();
375 
376  // check that the reversal does not induce a cycle
377  try {
378  DAG d = dag();
379  d.eraseArc(arc);
380  d.addArc(head, tail);
381  } catch (Exception&) {
382  GUM_ERROR(InvalidArc, "this arc reversal would induce a directed cycle");
383  }
384 
385  // with the same notations as Shachter (1986), "evaluating influence
386  // diagrams",
387  // p.878, we shall first compute the product of probabilities:
388  // pi_j^old (x_j | x_c^old(j) ) * pi_i^old (x_i | x_c^old(i) )
389  Potential< GUM_SCALAR > prod{cpt(tail) * cpt(head)};
390 
391  // modify the topology of the graph: add to tail all the parents of head
392  // and add to head all the parents of tail
393  beginTopologyTransformation();
394  NodeSet new_parents;
395  for (const auto node : this->parents(tail))
396  new_parents.insert(node);
397  for (const auto node : this->parents(head))
398  new_parents.insert(node);
399  // remove arc (head, tail)
400  eraseArc(arc);
401 
402  // add the necessary arcs to the tail
403  for (const auto p : new_parents) {
404  if ((p != tail) && !dag().existsArc(p, tail)) { addArc(p, tail); }
405  }
406 
407  addArc(head, tail);
408  // add the necessary arcs to the head
409  new_parents.erase(tail);
410 
411  for (const auto p : new_parents) {
412  if ((p != head) && !dag().existsArc(p, head)) { addArc(p, head); }
413  }
414 
415  endTopologyTransformation();
416 
417  // update the conditional distributions of head and tail
419  del_vars << &(variable(tail));
420  Potential< GUM_SCALAR > new_cpt_head = prod.margSumOut(del_vars);
421  auto& cpt_head = const_cast< Potential< GUM_SCALAR >& >(cpt(head));
422  cpt_head = std::move(new_cpt_head);
423 
424  Potential< GUM_SCALAR > new_cpt_tail{prod / cpt_head};
425  auto& cpt_tail = const_cast< Potential< GUM_SCALAR >& >(cpt(tail));
426  cpt_tail = std::move(new_cpt_tail);
427  }
428 
429  template < typename GUM_SCALAR >
431  reverseArc(Arc(tail, head));
432  }
433 
434 
435  //==============================================
436  // Aggregators
437  //=============================================
438  template < typename GUM_SCALAR >
440  return add(var, new aggregator::Amplitude< GUM_SCALAR >());
441  }
442 
443  template < typename GUM_SCALAR >
445  if (var.domainSize() > 2) GUM_ERROR(SizeError, "an AND has to be boolean");
446 
447  return add(var, new aggregator::And< GUM_SCALAR >());
448  }
449 
450  template < typename GUM_SCALAR >
452  Idx value) {
453  return add(var, new aggregator::Count< GUM_SCALAR >(value));
454  }
455 
456  template < typename GUM_SCALAR >
458  Idx value) {
459  if (var.domainSize() > 2) GUM_ERROR(SizeError, "an EXISTS has to be boolean");
460 
461  return add(var, new aggregator::Exists< GUM_SCALAR >(value));
462  }
463 
464  template < typename GUM_SCALAR >
466  Idx value) {
467  if (var.domainSize() > 2) GUM_ERROR(SizeError, "an EXISTS has to be boolean");
468 
469  return add(var, new aggregator::Forall< GUM_SCALAR >(value));
470  }
471 
472  template < typename GUM_SCALAR >
474  return add(var, new aggregator::Max< GUM_SCALAR >());
475  }
476 
477  template < typename GUM_SCALAR >
479  return add(var, new aggregator::Median< GUM_SCALAR >());
480  }
481 
482  template < typename GUM_SCALAR >
484  return add(var, new aggregator::Min< GUM_SCALAR >());
485  }
486 
487  template < typename GUM_SCALAR >
489  if (var.domainSize() > 2) GUM_ERROR(SizeError, "an OR has to be boolean");
490 
491  return add(var, new aggregator::Or< GUM_SCALAR >());
492  }
493 
494 
495  //================================
496  // ICIModels
497  //================================
498  template < typename GUM_SCALAR >
500  GUM_SCALAR external_weight) {
501  return addNoisyORCompound(var, external_weight);
502  }
503 
504  template < typename GUM_SCALAR >
506  const DiscreteVariable& var, GUM_SCALAR external_weight) {
507  return add(var, new MultiDimNoisyORCompound< GUM_SCALAR >(external_weight));
508  }
509 
510  template < typename GUM_SCALAR >
512  GUM_SCALAR external_weight) {
513  return add(var, new MultiDimNoisyORNet< GUM_SCALAR >(external_weight));
514  }
515 
516  template < typename GUM_SCALAR >
518  GUM_SCALAR external_weight) {
519  return add(var, new MultiDimNoisyAND< GUM_SCALAR >(external_weight));
520  }
521 
522  template < typename GUM_SCALAR >
524  GUM_SCALAR external_weight) {
525  return add(var, new MultiDimLogit< GUM_SCALAR >(external_weight));
526  }
527 
528  template < typename GUM_SCALAR >
530  GUM_SCALAR external_weight,
531  NodeId id) {
532  return addNoisyORCompound(var, external_weight, id);
533  }
534 
535  template < typename GUM_SCALAR >
537  GUM_SCALAR external_weight,
538  NodeId id) {
539  return add(var, new MultiDimNoisyAND< GUM_SCALAR >(external_weight), id);
540  }
541 
542  template < typename GUM_SCALAR >
544  GUM_SCALAR external_weight,
545  NodeId id) {
546  return add(var, new MultiDimLogit< GUM_SCALAR >(external_weight), id);
547  }
548 
549  template < typename GUM_SCALAR >
551  const DiscreteVariable& var, GUM_SCALAR external_weight, NodeId id) {
552  return add(
553  var, new MultiDimNoisyORCompound< GUM_SCALAR >(external_weight), id);
554  }
555 
556  template < typename GUM_SCALAR >
558  GUM_SCALAR external_weight,
559  NodeId id) {
560  return add(var, new MultiDimNoisyORNet< GUM_SCALAR >(external_weight), id);
561  }
562 
563  template < typename GUM_SCALAR >
565  NodeId head,
566  GUM_SCALAR causalWeight) {
567  auto* CImodel =
568  dynamic_cast< const MultiDimICIModel< GUM_SCALAR >* >(cpt(head).content());
569 
570  if (CImodel != 0) {
571  // or is OK
572  addArc(tail, head);
573 
574  CImodel->causalWeight(variable(tail), causalWeight);
575  } else {
577  "Head variable (" << variable(head).name()
578  << ") is not a CIModel variable !");
579  }
580  }
581 
582  template < typename GUM_SCALAR >
583  INLINE std::ostream& operator<<(std::ostream& output,
584  const BayesNet< GUM_SCALAR >& bn) {
585  output << bn.toString();
586  return output;
587  }
588 
590  template < typename GUM_SCALAR >
592  for (const auto node : nodes())
593  __probaMap[node]->beginMultipleChanges();
594  }
595 
597  template < typename GUM_SCALAR >
599  for (const auto node : nodes())
600  __probaMap[node]->endMultipleChanges();
601  }
602 
604  template < typename GUM_SCALAR >
606  // Removing previous potentials
607  for (const auto& elt : __probaMap) {
608  delete elt.second;
609  }
610 
611  __probaMap.clear();
612  }
613 
615  template < typename GUM_SCALAR >
617  const BayesNet< GUM_SCALAR >& source) {
618  // Copying potentials
619 
620  for (const auto src : source.__probaMap) {
621  // First we build the node's CPT
623  copy_array->beginMultipleChanges();
624  for (gum::Idx i = 0; i < src.second->nbrDim(); i++) {
625  (*copy_array) << variableFromName(src.second->variable(i).name());
626  }
627  copy_array->endMultipleChanges();
628  copy_array->copyFrom(*(src.second));
629 
630  // We add the CPT to the CPT's hashmap
631  __probaMap.insert(src.first, copy_array);
632  }
633  }
634 
635  template < typename GUM_SCALAR >
637  for (const auto node : nodes())
638  generateCPT(node);
639  }
640  template < typename GUM_SCALAR >
641  INLINE void BayesNet< GUM_SCALAR >::generateCPT(NodeId node) const {
643 
644  generator.generateCPT(cpt(node).pos(variable(node)), cpt(node));
645  }
646 
647  template < typename GUM_SCALAR >
649  Potential< GUM_SCALAR >* newPot) {
650  if (cpt(id).nbrDim() != newPot->nbrDim()) {
652  "cannot exchange potentials with different "
653  "dimensions for variable with id "
654  << id);
655  }
656 
657  for (Idx i = 0; i < cpt(id).nbrDim(); i++) {
658  if (&cpt(id).variable(i) != &(newPot->variable(i))) {
660  "cannot exchange potentials because, for variable with id "
661  << id << ", dimension " << i << " differs. ");
662  }
663  }
664 
665  _unsafeChangePotential(id, newPot);
666  }
667 
668  template < typename GUM_SCALAR >
670  NodeId id, Potential< GUM_SCALAR >* newPot) {
671  delete __probaMap[id];
672  __probaMap[id] = newPot;
673  }
674 
675  template < typename GUM_SCALAR >
676  void BayesNet< GUM_SCALAR >::changePotential(const std::string& name,
677  Potential< GUM_SCALAR >* newPot) {
678  changePotential(idFromName(name), newPot);
679  }
680 
681 } /* 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:345
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:58
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:229
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:157
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:124
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:306
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:636
Defines a discrete random variable over an integer interval.
Definition: rangeVariable.h:51
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:583
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>
Header of gumRangeVariable.
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:300
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