aGrUM  0.21.0
a C++ library for (probabilistic) graphical models
MarkovNet_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright (c) 2005-2021 by 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 #include <algorithm>
32 
33 #include <agrum/MN/MarkovNet.h>
34 
35 #include <agrum/tools/variables/rangeVariable.h>
36 #include <agrum/tools/variables/labelizedVariable.h>
37 #include <agrum/tools/variables/integerVariable.h>
38 #include <agrum/tools/variables/discretizedVariable.h>
39 
40 #include <agrum/tools/multidim/aggregators/amplitude.h>
41 #include <agrum/tools/multidim/aggregators/and.h>
42 #include <agrum/tools/multidim/aggregators/count.h>
43 #include <agrum/tools/multidim/aggregators/exists.h>
44 #include <agrum/tools/multidim/aggregators/forall.h>
45 #include <agrum/tools/multidim/aggregators/max.h>
46 #include <agrum/tools/multidim/aggregators/median.h>
47 #include <agrum/tools/multidim/aggregators/min.h>
48 #include <agrum/tools/multidim/aggregators/or.h>
49 
50 #include <agrum/tools/multidim/ICIModels/multiDimNoisyAND.h>
51 #include <agrum/tools/multidim/ICIModels/multiDimNoisyORCompound.h>
52 #include <agrum/tools/multidim/ICIModels/multiDimNoisyORNet.h>
53 
54 #include <agrum/tools/multidim/ICIModels/multiDimLogit.h>
55 
56 #include <agrum/BN/generator/simpleCPTGenerator.h>
57 #include <agrum/tools/core/utils_string.h>
58 
59 namespace gum {
60  template < typename GUM_SCALAR >
61  NodeId
63  std::string name = node;
64  auto ds = default_domain_size;
65  long range_min = 0;
66  long range_max = long(ds) - 1;
67  std::vector< std::string > labels;
69 
70  if (*(node.rbegin()) == ']') {
71  auto posBrack = node.find('[');
72  if (posBrack != std::string::npos) {
73  name = node.substr(0, posBrack);
74  const auto& s_args = node.substr(posBrack + 1, node.size() - posBrack - 2);
75  const auto& args = split(s_args, ",");
76  if (args.size() == 0) { // n[]
77  GUM_ERROR(InvalidArgument, "Empty range for variable " << node)
78  } else if (args.size() == 1) { // n[4]
79  ds = static_cast< Size >(std::stoi(args[0]));
80  range_min = 0;
81  range_max = long(ds) - 1;
82  } else if (args.size() == 2) { // n[5,10]
83  range_min = std::stol(args[0]);
84  range_max = std::stol(args[1]);
85  if (1 + range_max - range_min < 2) {
86  GUM_ERROR(InvalidArgument, "Invalid range for variable " << node)
87  }
88  ds = static_cast< Size >(1 + range_max - range_min);
89  } else { // n[3.14,5,10,12]
90  for (const auto& tick: args) {
91  ticks.push_back(static_cast< GUM_SCALAR >(std::atof(tick.c_str())));
92  }
93  ds = static_cast< Size >(args.size() - 1);
94  }
95  }
96  } else if (*(node.rbegin()) == '}') { // node like "n{one|two|three}"
97  auto posBrack = node.find('{');
98  if (posBrack != std::string::npos) {
99  name = node.substr(0, posBrack);
100  labels = split(node.substr(posBrack + 1, node.size() - posBrack - 2), "|");
101  if (labels.size() < 2) { GUM_ERROR(InvalidArgument, "Not enough labels in node " << node) }
102  if (!hasUniqueElts(labels)) {
103  GUM_ERROR(InvalidArgument, "Duplicate labels in node " << node)
104  }
105  ds = static_cast< Size >(labels.size());
106  }
107  }
108 
109  if (ds == 0) {
110  GUM_ERROR(InvalidArgument, "No value for variable " << name << ".")
111  } else if (ds == 1) {
113  "Only one value for variable " << name << " (2 at least are needed).");
114  }
115 
116  std::vector< int > values;
117  if (!labels.empty()) {
118  if (std::all_of(labels.begin(), labels.end(), isInteger)) {
119  for (const auto& label: labels)
121  }
122  }
123 
124  // now we add the node in the BN
125  NodeId idVar;
126  try {
127  idVar = mn.idFromName(name);
128  } catch (NotFound&) {
129  if (!values.empty()) {
131  } else if (!labels.empty()) {
133  } else if (!ticks.empty()) {
135  } else {
137  }
138  }
139 
140  return idVar;
141  }
142 
143  template < typename GUM_SCALAR >
144  MarkovNet< GUM_SCALAR > MarkovNet< GUM_SCALAR >::fastPrototype(const std::string& dotlike,
145  Size domainSize) {
147 
148 
149  for (const auto& clikchain: split(dotlike, ";")) {
150  NodeSet cliq;
151  for (const auto& node: split(clikchain, "--")) {
153  cliq.insert(idVar);
154  }
155  mn.addFactor(cliq);
156  }
158  mn.setProperty("name", "fastPrototype");
159  return mn;
160  }
161 
162  template < typename GUM_SCALAR >
163  MarkovNet< GUM_SCALAR > MarkovNet< GUM_SCALAR >::fromBN(const BayesNet< GUM_SCALAR >& bn) {
164  MarkovNet< GUM_SCALAR > mn;
165  for (NodeId nod: bn.nodes()) {
166  mn.add(bn.variable(nod), nod);
167  }
168  mn.beginTopologyTransformation();
169  for (NodeId nod: bn.nodes()) {
170  mn.addFactor(bn.cpt(nod));
171  }
172  mn.endTopologyTransformation();
173  mn.setProperty("name", bn.propertyWithDefault("name", "noname"));
174  return mn;
175  }
176 
177  template < typename GUM_SCALAR >
181  }
182 
183  template < typename GUM_SCALAR >
187  }
188 
189  template < typename GUM_SCALAR >
195  }
196 
197  template < typename GUM_SCALAR >
200  if (this != &source) {
205  }
206 
207  return *this;
208  }
209 
210  template < typename GUM_SCALAR >
212  _clearFactors_();
214  }
215 
216  template < typename GUM_SCALAR >
218  return _varMap_.get(id);
219  }
220 
221  template < typename GUM_SCALAR >
224  }
225 
226  template < typename GUM_SCALAR >
228  const std::string& old_label,
229  const std::string& new_label) {
230  if (variable(id).varType() != VarType::Labelized) {
231  GUM_ERROR(NotFound, "Variable " << id << " is not a LabelizedVariable.")
232  }
234  = dynamic_cast< LabelizedVariable* >(const_cast< DiscreteVariable* >(&variable(id)));
235 
237  }
238 
239  template < typename GUM_SCALAR >
241  return _varMap_.get(var);
242  }
243 
244  template < typename GUM_SCALAR >
245  const Potential< GUM_SCALAR >& MarkovNet< GUM_SCALAR >::factor(const NodeSet& varIds) const {
246  return *_factors_[varIds];
247  }
248 
249  template < typename GUM_SCALAR >
251  const NodeSet* res = nullptr;
252  Size smallest = size();
253  for (const auto& kv: factors()) {
254  const auto& fact = kv.first;
255  if (fact.contains(node))
256  if (smallest > fact.size()) {
257  res = &fact;
258  smallest = fact.size();
259  }
260  }
261  if (res == nullptr) {
262  GUM_ERROR(NotFound, "No factor containing node " << node)
263  } else {
264  return *res;
265  }
266  }
267 
268  template < typename GUM_SCALAR >
269  const Potential< GUM_SCALAR >&
270  MarkovNet< GUM_SCALAR >::factor(const std::vector< std::string >& varnames) const {
271  return factor(this->nodeset(varnames));
272  }
273 
274  template < typename GUM_SCALAR >
275  const FactorTable< GUM_SCALAR >& MarkovNet< GUM_SCALAR >::factors() const {
276  return _factors_;
277  }
278 
279  template < typename GUM_SCALAR >
280  INLINE NodeId MarkovNet< GUM_SCALAR >::add(const std::string& name, unsigned int nbrmod) {
281  if (nbrmod < 2) {
283  "Variable " << name << "needs more than " << nbrmod << " modalities");
284  }
285 
286  RangeVariable v(name, name, 0, nbrmod - 1);
287  return add(v);
288  }
289 
290  template < typename GUM_SCALAR >
293 
294  this->graph_.clearEdges();
295 
296  for (const auto& kv: _factors_) {
297  auto& c = *kv.second;
298  for (Idx i = 0; i < c.nbrDim(); i++)
299  for (Idx j = i + 1; j < c.nbrDim(); j++)
301  }
302  }
303 
304 
305  template < typename GUM_SCALAR >
307  return add(var, graph().nextNodeId());
308  }
309 
310  template < typename GUM_SCALAR >
312  _varMap_.insert(id, var);
313  this->graph_.addNodeWithId(id);
314  return id;
315  }
316 
317  template < typename GUM_SCALAR >
319  return _varMap_.idFromName(name);
320  }
321 
322  template < typename GUM_SCALAR >
323  INLINE const DiscreteVariable&
324  MarkovNet< GUM_SCALAR >::variableFromName(const std::string& name) const {
326  }
327 
328  template < typename GUM_SCALAR >
330  return _varMap_;
331  }
332 
333  template < typename GUM_SCALAR >
335  erase(_varMap_.get(var));
336  }
337 
338  template < typename GUM_SCALAR >
339  INLINE void MarkovNet< GUM_SCALAR >::erase(const std::string& name) {
341  }
342 
343  template < typename GUM_SCALAR >
345  if (!_varMap_.exists(varId)) { GUM_ERROR(InvalidArgument, "No node with id " << varId << ".") }
347  this->graph_.eraseNode(varId);
348 
349  std::vector< NodeSet > vs;
350  for (const auto& kv: _factors_) {
351  if (kv.first.contains(varId)) { vs.push_back(kv.first); }
352  }
353  for (const auto& ns: vs) {
354  _eraseFactor_(ns);
355  }
356  for (const auto& ns: vs) {
357  NodeSet nv = ns;
358  nv.erase(varId);
359  if (nv.size() > 1) _addFactor_(nv);
360  }
361  _rebuildGraph_();
362  }
363 
364  template < typename GUM_SCALAR >
365  void MarkovNet< GUM_SCALAR >::clear() {
366  if (!this->empty()) {
367  auto l = this->nodes();
368  for (const auto no: l) {
369  this->erase(no);
370  }
371  }
372  _rebuildGraph_();
373  }
374 
375 
376  template < typename GUM_SCALAR >
378  output << mn.toString();
379  return output;
380  }
381 
382  template < typename GUM_SCALAR >
384  if (vars.size() == 0) { GUM_ERROR(InvalidArgument, "Empty factor cannot be added.") }
385 
386  if (_factors_.exists(vars)) {
387  GUM_ERROR(InvalidArgument, "A factor for (" << vars << ") already exists.")
388  }
389 
390  auto res = _addFactor_(vars);
391  _rebuildGraph_();
392 
393  return *res;
394  }
395 
396  template < typename GUM_SCALAR >
397  INLINE const Potential< GUM_SCALAR >&
399  auto vars = this->nodeset(varnames);
400  if (_factors_.exists(vars)) {
401  GUM_ERROR(InvalidArgument, "A factor for (" << varnames << ") already exists.")
402  }
403 
404  return addFactor(vars);
405  }
406 
407  template < typename GUM_SCALAR >
408  INLINE const Potential< GUM_SCALAR >&
410  if (factor.nbrDim() == 0) { GUM_ERROR(InvalidArgument, "Empty factor cannot be added.") }
411 
412  NodeSet key;
413  for (Idx i = 0; i < factor.nbrDim(); i++) {
415  }
416 
417  if (_factors_.exists(key)) {
418  GUM_ERROR(InvalidArgument, "A factor for (" << key << ") already exists.")
419  }
420 
421  auto res = _addFactor_(key, &factor);
422  _rebuildGraph_();
423 
424  return *res;
425  }
426 
427  template < typename GUM_SCALAR >
428  INLINE const Potential< GUM_SCALAR >*
431 
432  // in order to be deterministic, the Potential contains all the vars, sorted by
433  // id.
435  for (auto node: vars) {
437  }
439  for (auto node: sorted_nodes) {
440  factor->add(variable(node));
441  }
442 
443  if (src != nullptr) { factor->fillWith(*src); }
445 
446  return factor;
447  }
448 
449 
450  template < typename GUM_SCALAR >
451  INLINE void MarkovNet< GUM_SCALAR >::generateFactors() const {
452  for (const auto& elt: _factors_) {
453  elt.second->random();
454  }
455  }
456 
457  template < typename GUM_SCALAR >
458  INLINE void MarkovNet< GUM_SCALAR >::generateFactor(const NodeSet& vars) const {
459  _factors_[vars]->random();
460  }
461 
462  template < typename GUM_SCALAR >
464  if (_factors_.exists(vars)) {
466  _rebuildGraph_();
467  } else {
468  GUM_ERROR(InvalidArgument, "No factor for " << vars << ".")
469  }
470  }
471 
472  template < typename GUM_SCALAR >
474  auto vars = this->nodeset(varnames);
475  if (_factors_.exists(vars)) {
477  _rebuildGraph_();
478  } else {
479  GUM_ERROR(InvalidArgument, "No factor for " << varnames << ".")
480  }
481  }
482 
483  template < typename GUM_SCALAR >
485  delete _factors_[vars];
487  }
488 
489  template < typename GUM_SCALAR >
490  void MarkovNet< GUM_SCALAR >::_clearFactors_() {
491  for (const auto& kv: _factors_) {
492  delete kv.second;
493  }
494  _factors_.clear();
495  _rebuildGraph_();
496  }
497 
498  template < typename GUM_SCALAR >
500  _clearFactors_();
501  for (const auto& pf: source.factors()) {
503  }
504  _rebuildGraph_();
505  }
506 
507  template < typename GUM_SCALAR >
510  }
511 
512  template < typename GUM_SCALAR >
515  _topologyTransformationInProgress_ = false; // before rebuildGraph of course
516  _rebuildGraph_();
517  }
518  }
519 } /* namespace gum */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:643
NodeId build_node_for_MN(MarkovNet< GUM_SCALAR > &mn, std::string node, Size default_domain_size)
Definition: MarkovNet_tpl.h:62