aGrUM  0.20.3
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 
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 >
59  NodeId
61  std::string name = node;
62  auto ds = default_domain_size;
63  long range_min = 0;
64  long range_max = long(ds) - 1;
65  std::vector< std::string > labels;
67 
68  if (*(node.rbegin()) == ']') {
69  auto posBrack = node.find('[');
70  if (posBrack != std::string::npos) {
71  name = node.substr(0, posBrack);
72  const auto& s_args = node.substr(posBrack + 1, node.size() - posBrack - 2);
73  const auto& args = split(s_args, ",");
74  if (args.size() == 0) { // n[]
75  GUM_ERROR(InvalidArgument, "Empty range for variable " << node)
76  } else if (args.size() == 1) { // n[4]
77  ds = static_cast< Size >(std::stoi(args[0]));
78  range_min = 0;
79  range_max = long(ds) - 1;
80  } else if (args.size() == 2) { // n[5,10]
81  range_min = std::stol(args[0]);
82  range_max = std::stol(args[1]);
83  if (1 + range_max - range_min < 2) {
84  GUM_ERROR(InvalidArgument, "Invalid range for variable " << node)
85  }
86  ds = static_cast< Size >(1 + range_max - range_min);
87  } else { // n[3.14,5,10,12]
88  for (const auto& tick: args) {
89  ticks.push_back(static_cast< GUM_SCALAR >(std::atof(tick.c_str())));
90  }
91  ds = static_cast< Size >(args.size() - 1);
92  }
93  }
94  } else if (*(node.rbegin()) == '}') { // node like "n{one|two|three}"
95  auto posBrack = node.find('{');
96  if (posBrack != std::string::npos) {
97  name = node.substr(0, posBrack);
98  labels = split(node.substr(posBrack + 1, node.size() - posBrack - 2), "|");
99  if (labels.size() < 2) { GUM_ERROR(InvalidArgument, "Not enough labels in node " << node) }
100  if (!hasUniqueElts(labels)) {
101  GUM_ERROR(InvalidArgument, "Duplicate labels in node " << node)
102  }
103  ds = static_cast< Size >(labels.size());
104  }
105  }
106 
107  if (ds == 0) {
108  GUM_ERROR(InvalidArgument, "No value for variable " << name << ".")
109  } else if (ds == 1) {
111  "Only one value for variable " << name << " (2 at least are needed).");
112  }
113 
114  // now we add the node in the BN
115  NodeId idVar;
116  try {
117  idVar = mn.idFromName(name);
118  } catch (NotFound&) {
119  if (!labels.empty()) {
121  } else if (!ticks.empty()) {
123  } else {
125  }
126  }
127 
128  return idVar;
129  }
130 
131  template < typename GUM_SCALAR >
132  MarkovNet< GUM_SCALAR > MarkovNet< GUM_SCALAR >::fastPrototype(const std::string& dotlike,
133  Size domainSize) {
135 
136 
137  for (const auto& clikchain: split(dotlike, ";")) {
138  NodeSet cliq;
139  for (const auto& node: split(clikchain, "-")) {
141  cliq.insert(idVar);
142  }
143  mn.addFactor(cliq);
144  }
146  mn.setProperty("name", "fastPrototype");
147  return mn;
148  }
149 
150  template < typename GUM_SCALAR >
151  MarkovNet< GUM_SCALAR > MarkovNet< GUM_SCALAR >::fromBN(const BayesNet< GUM_SCALAR >& bn) {
152  MarkovNet< GUM_SCALAR > mn;
153  for (NodeId nod: bn.nodes()) {
154  mn.add(bn.variable(nod), nod);
155  }
156  mn.beginTopologyTransformation();
157  for (NodeId nod: bn.nodes()) {
158  mn.addFactor(bn.cpt(nod));
159  }
160  mn.endTopologyTransformation();
161  mn.setProperty("name", bn.propertyWithDefault("name", "noname"));
162  return mn;
163  }
164 
165  template < typename GUM_SCALAR >
169  }
170 
171  template < typename GUM_SCALAR >
175  }
176 
177  template < typename GUM_SCALAR >
183  }
184 
185  template < typename GUM_SCALAR >
188  if (this != &source) {
193  }
194 
195  return *this;
196  }
197 
198  template < typename GUM_SCALAR >
200  _clearFactors_();
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  template < typename GUM_SCALAR >
229  return _varMap_.get(var);
230  }
231 
232  template < typename GUM_SCALAR >
233  const Potential< GUM_SCALAR >& MarkovNet< GUM_SCALAR >::factor(const NodeSet& varIds) const {
234  return *_factors_[varIds];
235  }
236 
237  template < typename GUM_SCALAR >
239  const NodeSet* res = nullptr;
240  Size smallest = size();
241  for (const auto& kv: factors()) {
242  const auto& fact = kv.first;
243  if (fact.contains(node))
244  if (smallest > fact.size()) {
245  res = &fact;
246  smallest = fact.size();
247  }
248  }
249  if (res == nullptr) {
250  GUM_ERROR(NotFound, "No factor containing node " << node)
251  } else {
252  return *res;
253  }
254  }
255 
256  template < typename GUM_SCALAR >
257  const Potential< GUM_SCALAR >&
258  MarkovNet< GUM_SCALAR >::factor(const std::vector< std::string >& varnames) const {
259  return factor(this->nodeset(varnames));
260  }
261 
262  template < typename GUM_SCALAR >
263  const FactorTable< GUM_SCALAR >& MarkovNet< GUM_SCALAR >::factors() const {
264  return _factors_;
265  }
266 
267  template < typename GUM_SCALAR >
268  INLINE NodeId MarkovNet< GUM_SCALAR >::add(const std::string& name, unsigned int nbrmod) {
269  if (nbrmod < 2) {
271  "Variable " << name << "needs more than " << nbrmod << " modalities");
272  }
273 
274  RangeVariable v(name, name, 0, nbrmod - 1);
275  return add(v);
276  }
277 
278  template < typename GUM_SCALAR >
281 
282  this->graph_.clearEdges();
283 
284  for (const auto& kv: _factors_) {
285  const Potential< double >& c = *kv.second;
286  for (Idx i = 0; i < c.nbrDim(); i++)
287  for (Idx j = i + 1; j < c.nbrDim(); j++)
289  }
290  }
291 
292 
293  template < typename GUM_SCALAR >
295  return add(var, graph().nextNodeId());
296  }
297 
298  template < typename GUM_SCALAR >
300  _varMap_.insert(id, var);
301  this->graph_.addNodeWithId(id);
302  return id;
303  }
304 
305  template < typename GUM_SCALAR >
307  return _varMap_.idFromName(name);
308  }
309 
310  template < typename GUM_SCALAR >
311  INLINE const DiscreteVariable&
312  MarkovNet< GUM_SCALAR >::variableFromName(const std::string& name) const {
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 >
327  INLINE void MarkovNet< GUM_SCALAR >::erase(const std::string& name) {
329  }
330 
331  template < typename GUM_SCALAR >
333  if (!_varMap_.exists(varId)) { GUM_ERROR(InvalidArgument, "No node with id " << varId << ".") }
335  this->graph_.eraseNode(varId);
336 
337  std::vector< NodeSet > vs;
338  for (const auto& kv: _factors_) {
339  if (kv.first.contains(varId)) { vs.push_back(kv.first); }
340  }
341  for (const auto& ns: vs) {
342  _eraseFactor_(ns);
343  }
344  for (const auto& ns: vs) {
345  NodeSet nv = ns;
346  nv.erase(varId);
347  if (nv.size() > 1) _addFactor_(nv);
348  }
349  _rebuildGraph_();
350  }
351 
352  template < typename GUM_SCALAR >
353  void MarkovNet< GUM_SCALAR >::clear() {
354  if (!this->empty()) {
355  auto l = this->nodes();
356  for (const auto no: l) {
357  this->erase(no);
358  }
359  }
360  _rebuildGraph_();
361  }
362 
363 
364  template < typename GUM_SCALAR >
366  output << mn.toString();
367  return output;
368  }
369 
370  template < typename GUM_SCALAR >
372  if (vars.size() == 0) { GUM_ERROR(InvalidArgument, "Empty factor cannot be added.") }
373 
374  if (_factors_.exists(vars)) {
375  GUM_ERROR(InvalidArgument, "A factor for (" << vars << ") already exists.")
376  }
377 
378  auto res = _addFactor_(vars);
379  _rebuildGraph_();
380 
381  return *res;
382  }
383 
384  template < typename GUM_SCALAR >
385  INLINE const Potential< GUM_SCALAR >&
387  auto vars = this->nodeset(varnames);
388  if (_factors_.exists(vars)) {
389  GUM_ERROR(InvalidArgument, "A factor for (" << varnames << ") already exists.")
390  }
391 
392  return addFactor(vars);
393  }
394 
395  template < typename GUM_SCALAR >
396  INLINE const Potential< GUM_SCALAR >&
398  if (factor.nbrDim() == 0) { GUM_ERROR(InvalidArgument, "Empty factor cannot be added.") }
399 
400  NodeSet key;
401  for (Idx i = 0; i < factor.nbrDim(); i++) {
403  }
404 
405  if (_factors_.exists(key)) {
406  GUM_ERROR(InvalidArgument, "A factor for (" << key << ") already exists.")
407  }
408 
409  auto res = _addFactor_(key, &factor);
410  _rebuildGraph_();
411 
412  return *res;
413  }
414 
415  template < typename GUM_SCALAR >
416  INLINE const Potential< GUM_SCALAR >*
419 
420  // in order to be deterministic, the Potential contains all the vars, sorted by
421  // id.
423  for (auto node: vars) {
425  }
427  for (auto node: sorted_nodes) {
428  factor->add(variable(node));
429  }
430 
431  if (src != nullptr) { factor->fillWith(*src); }
433 
434  return factor;
435  }
436 
437 
438  template < typename GUM_SCALAR >
439  INLINE void MarkovNet< GUM_SCALAR >::generateFactors() const {
440  for (const auto& elt: _factors_) {
441  elt.second->random();
442  }
443  }
444 
445  template < typename GUM_SCALAR >
446  INLINE void MarkovNet< GUM_SCALAR >::generateFactor(const NodeSet& vars) const {
447  _factors_[vars]->random();
448  }
449 
450  template < typename GUM_SCALAR >
452  if (_factors_.exists(vars)) {
454  _rebuildGraph_();
455  } else {
456  GUM_ERROR(InvalidArgument, "No factor for " << vars << ".")
457  }
458  }
459 
460  template < typename GUM_SCALAR >
462  auto vars = this->nodeset(varnames);
463  if (_factors_.exists(vars)) {
465  _rebuildGraph_();
466  } else {
467  GUM_ERROR(InvalidArgument, "No factor for " << varnames << ".")
468  }
469  }
470 
471  template < typename GUM_SCALAR >
473  delete _factors_[vars];
475  }
476 
477  template < typename GUM_SCALAR >
478  void MarkovNet< GUM_SCALAR >::_clearFactors_() {
479  for (const auto& kv: _factors_) {
480  delete kv.second;
481  }
482  _factors_.clear();
483  _rebuildGraph_();
484  }
485 
486  template < typename GUM_SCALAR >
488  _clearFactors_();
489  for (const auto& pf: source.factors()) {
491  }
492  _rebuildGraph_();
493  }
494 
495  template < typename GUM_SCALAR >
498  }
499 
500  template < typename GUM_SCALAR >
503  _topologyTransformationInProgress_ = false; // before rebuildGraph of course
504  _rebuildGraph_();
505  }
506  }
507 } /* 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:60