aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
MarkovNet.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright 2005-2020 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 Class representing Markov networks
25  *
26  * @author Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
27  *
28  */
29 #ifndef GUM_MARKOV_NET_H
30 #define GUM_MARKOV_NET_H
31 
32 #include <utility>
33 
34 #include <agrum/agrum.h>
35 
36 #include <agrum/tools/core/hashTable.h>
37 
38 #include <agrum/MN/IMarkovNet.h>
39 #include <agrum/tools/multidim/potential.h>
40 
41 #include <agrum/BN/BayesNet.h>
42 
43 namespace gum {
44 
45  /**
46  * @class MarkovNet
47  * @headerfile MarkovNet.h <agrum/MN/MarkovNet.h>
48  * @brief Class representing a Markov Network.
49  * @ingroup mn_group
50  *
51  * Markov Networks are a undirected probabilistic graphical model in which nodes
52  * are random variables and the probability distribution is defined by the
53  * product:
54  *
55  * <center>\f$P(X_1, \ldots, X_n) \propto \prod_{C \in {\cal C}}
56  * f_{C}(X_C)\f$,</center>
57  *
58  * where \f$f_{C}$ is a factor on a clique of the undirected graph, \f${\cal C}$
59  * is the set of cliques in the graph and \f$X_C$ is the instantiation of the
60  * variables in the clique f_$C$.
61  *
62  * The probability distribution can be represented as a undirected acyclic
63  * graph where:
64  * - Nodes are discrete random variables.
65  * - An arc A - B represent a dependency between variables A and B.
66  *
67  *
68  * You can print a MarkovNet using
69  * gum::operator<<(std::ostream&, const MarkovNet<GUM_SCALAR>&).
70  */
71  template < typename GUM_SCALAR >
72  class MarkovNet: public IMarkovNet< GUM_SCALAR > {
73  public:
74  /**
75  * Create a Markov network with a dot-like syntax which specifies:
76  * - the structure "a-b-c;b-d-e;".
77  * - the type of the variables with different syntax:
78  * + by default, a variable is a gum::RangeVariable using the default
79  * domainSize (second argument)
80  * + with "a[10]", the variable is a gum::RangeVariable using 10 as
81  * domainSize (from 0 to 9)
82  * + with "a[3,7]", the variable is a gum::RangeVariable using a domainSize
83  * from 3 to 7
84  * + with "a[1,3.14,5,6.2]", the variable is a gum::DiscretizedVariable
85  * using the given ticks (at least 3 values)
86  * + with "a{top|middle|bottom}", the variable is a gum::LabelizedVariable
87  * using the given labels.
88  *
89  * Note that if the dot-like string contains such a specification more than
90  * once for a variable, the first specification will be used.
91  *
92  * @param dotlike the string containing the specification
93  * @param domainSize the default domain size for variables
94  * @return the resulting Bayesian network
95  */
96  static MarkovNet< GUM_SCALAR > fastPrototype(const std::string& dotlike,
97  Size domainSize = 2);
98 
99  /**
100  * build a Markov Network from a Bayesian network
101  * @param bn the Bayesian network
102  * @return a markov network
103  */
104  static MarkovNet< GUM_SCALAR > fromBN(const BayesNet< GUM_SCALAR >& bn);
105 
106  // ===========================================================================
107  /// @name Constructors and Destructor
108  // ===========================================================================
109  /// @{
110 
111  /**
112  * @brief Default constructor.
113  */
114  MarkovNet();
115 
116  /**
117  * @brief Default constructor.
118  *
119  * @param name The MarkovNet's name.
120  */
121  explicit MarkovNet(std::string name);
122 
123  /**
124  * @brief Destructor.
125  */
126  virtual ~MarkovNet() final;
127 
128  /**
129  * @brief Copy constructor.
130  */
131  MarkovNet(const MarkovNet< GUM_SCALAR >& source);
132 
133  /// @}
134  // ===========================================================================
135  /// @name Operators
136  // ===========================================================================
137  /// @{
138 
139  /**
140  * @brief Copy operator.
141  *
142  * @param source The copied MarkovNet.
143  * @return The copy of source.
144  */
145  MarkovNet< GUM_SCALAR >& operator=(const MarkovNet< GUM_SCALAR >& source);
146 
147  /// @}
148  // ===========================================================================
149  /// @name Variable manipulation methods
150  // ===========================================================================
151  /// @{
152 
153  /**
154  * @brief Returns the factor given a nodeset
155  *
156  * @param varId A variable's id in the gum::MarkovNet.
157  * @return The variable's CPT.
158  * @throw NotFound If no variable's id matches varId.
159  */
160  virtual const Potential< GUM_SCALAR >&
161  factor(const NodeSet& varIds) const final;
162 
163  virtual const Potential< GUM_SCALAR >&
164  factor(const std::vector< std::string >& varnames) const final;
165 
166  /**
167  * Returns a factor that contains this variable
168  *
169  * @throw NotFound If no variable's id matches varId.
170  */
171  virtual const NodeSet& smallestFactorFromNode(NodeId node) const final;
172 
173  /**
174  * Returns the set of factors as a IMarkovNet::FactorTable
175  *
176  */
177  virtual const FactorTable< GUM_SCALAR >& factors() const final;
178 
179  /**
180  * @brief Returns a map between variables and nodes of this gum::MarkovNet.
181  *
182  * @return Returns a constant reference to the gum::VariableNodeMap.
183  */
184  virtual const VariableNodeMap& variableNodeMap() const final;
185 
186  /**
187  * @brief Add a variable to the gum::MarkovNet.
188  *
189  * Add a gum::DiscreteVariable, it's associated gum::NodeId
190  *opera
191  * The variable is added by copy to the gum::MarkovNet.
192  * The variable's gum::Potential implementation will be a
193  * gum::MultiDimArray.
194  *
195  * @param var The variable added by copy.
196  * @return Returns the variable's id in the gum::MarkovNet.
197  * @throws DuplicateLabel Raised if variable.name() is already used in this
198  * gum::MarkovNet.
199  */
200  NodeId add(const DiscreteVariable& var);
201 
202  /**
203  * @brief Shortcut for add(gum::LabelizedVariable(name,name,nbrmod))
204  *
205  * Add a gum::LabelizedVariable to the gum::MarkovNet
206  *
207  * This method is just a shortcut for a often used pattern
208  *
209  * @throws DuplicateLabel Raised if variable.name() is already used in this
210  * gum::MarkovNet.
211  * @throws NotAllowed if nbrmod<2
212  */
213  NodeId add(const std::string& name, unsigned int nbrmod);
214 
215  /**
216  * @brief Add a variable to the gum::MarkovNet.
217  *
218  * Add a gum::DiscreteVariable, it's associated gum::NodeId and it's
219  * gum::Potential.
220  *
221  * The variable is added by copy to the gum::MarkovNet.
222  * The variable's gum::Potential implementation will be a
223  * gum::MultiDimArray.
224  *
225  * @param var The variable added by copy.
226  * @param id The variable's forced gum::NodeId in the gum::MarkovNet.
227  * @return Returns the variable's id in the gum::MarkovNet.
228  * @throws DuplicateElement Raised id is already used.
229  * @throws DuplicateLabel Raised if variable.name() is already used in this
230  * gum::MarkovNet.
231  */
232  NodeId add(const DiscreteVariable& var, NodeId id);
233 
234  /**
235  * @brief clear the whole Markov net
236  */
237  void clear();
238 
239  /**
240  * @brief Remove a variable from the gum::MarkovNet.
241  *
242  * Removes the corresponding variable from the gum::MarkovNet and from
243  * all of the factors.
244  *
245  * If no variable matches the given id, then nothing is done.
246  *
247  * @param varId The variable's id to remove.
248  */
249  void erase(NodeId varId);
250 
251  /**
252  * @brief Removes a variable from the gum::MarkovNet.
253  */
254  void erase(const std::string& name);
255 
256  /**
257  * @brief Remove a variable from the gum::MarkovNet.
258  *
259  * Removes the corresponding variable from the gum::MarkovNet and from
260  * all of the factors.
261  *
262  * If no variable matches the given variable, then nothing is done.
263  *
264  * @param var A reference on the variable to remove.
265  */
266  void erase(const DiscreteVariable& var);
267 
268  /**
269  * @brief Returns a gum::DiscreteVariable given its gum::NodeId in the
270  * gum::MarkovNet.
271  *
272  * @param id The variable's id to return.
273  * @returns Returns a constant reference of the gum::DiscreteVariable
274  * corresponding to id in the gum::MarkovNet.
275  * @throw NotFound Raised if id does not match a a variable in the
276  * gum::MarkovNet.
277  */
278  const DiscreteVariable& variable(NodeId id) const final;
279 
280  /**
281  * @brief Returns a gum::DiscreteVariable given its name in the
282  * gum::MarkovNet.
283  * @throw NotFound Raised if id does not match a a variable in the
284  * gum::MarkovNet.
285  */
286  const DiscreteVariable& variable(const std::string& name) const {
287  return variable(idFromName(name));
288  };
289 
290  /**
291  * @brief Changes a variable's name in the gum::MarkovNet.
292  *
293  * This will change the gum::DiscreteVariable names in the gum::MarkovNet.
294  *
295  * @throws DuplicateLabel Raised if newName is already used in this
296  * gum::MarkovNet.
297  * @throws NotFound Raised if no variable matches id.
298  */
299  void changeVariableName(NodeId id, const std::string& new_name);
300 
301  /**
302  * @brief Changes a variable's name.
303  */
304  void changeVariableName(const std::string& name, const std::string& new_name) {
305  changeVariableName(idFromName(name), new_name);
306  }
307 
308  /**
309  * @brief Changes a variable's label in the gum::MarkovNet.
310  *
311  * This will change the gum::LabelizedVariable names in the gum::MarkovNet.
312  *
313  * @throws DuplicateLabel Raised if new_label is already used in this
314  * gum::LabelizedVariable.
315  * @throws NotFound Raised if no variable matches id or if the variable is not
316  * a LabelizedVariable
317  */
318  void changeVariableLabel(NodeId id,
319  const std::string& old_label,
320  const std::string& new_label);
321 
322  /**
323  * @brief Changes a variable's name.
324  */
325  void changeVariableLabel(const std::string& name,
326  const std::string& old_label,
327  const std::string& new_label) {
328  changeVariableLabel(idFromName(name), old_label, new_label);
329  }
330 
331  /**
332  * @brief Returns a variable's id in the gum::MarkovNet.
333  *
334  * @param var The variable from which the gum::NodeId is returned.
335  * @return Returns the gum::DiscreteVariable gum::NodeId in the
336  * gum::MarkovNet.
337  * @throw NotFound If var is not in the gum::MarkovNet.
338  */
339  NodeId nodeId(const DiscreteVariable& var) const final;
340 
341  /**
342  * @brief Returns a variable's id given its name in the gum::MarkovNet.
343  *
344  * @param name The variable's name from which the gum::NodeId is returned.
345  * @return Returns the variable gum::NodeId in the gum::MarkovNet.
346  * @throw NotFound Raised if name does not match a variable in the
347  * gum::MarkovNet.
348  */
349  NodeId idFromName(const std::string& name) const final;
350 
351  /**
352  * @brief Returns a variable given its name in the gum::MarkovNet.
353  *
354  * @param name The variable's name in the gum::MarkovNet.
355  * @return Returns the gum::DiscreteVariable named name in the
356  * gum::MarkovNet.
357  * @throw NotFound Raised if name does not match a variable in the
358  * gum::MarkovNet.
359  */
360  const DiscreteVariable& variableFromName(const std::string& name) const final;
361  /// @}
362 
363  // ===========================================================================
364  /// @name Edge manipulation methods (by the mean of factors)
365  // ===========================================================================
366  /// @{
367 
368  /**
369  * @brief Add a factor (a clique) to the gum::MarkovNet.
370  *
371  * @param vars the scope of the factor
372  * @param varnames the scope of the factor as vector of variable names
373  * @param aContent The gum::MultiDimImplementation to use for this
374  * variable's gum::Potential implementation (will be copied).
375  *
376  * @return a const ref to the factor in the Markov Network
377  *
378  * @warning in order to be deterministic, the Potential contains all the vars
379  * of the clique sorted by id.
380  */
381  const Potential< GUM_SCALAR >&
382  addFactor(const std::vector< std::string >& varnames);
383 
384  const Potential< GUM_SCALAR >& addFactor(const NodeSet& vars);
385 
386  const Potential< GUM_SCALAR >&
387  addFactor(const Potential< GUM_SCALAR >& factor);
388 
389  /**
390  * Removes a factor in the MN, and update head's CTP.
391  *
392  * @param vars the NodeSet
393  * @throw
394  */
395  void eraseFactor(const NodeSet& vars);
396 
397  void eraseFactor(const std::vector< std::string >& varnames);
398  /// @}
399 
400 
401  /// randomly generates factors for a given structure
402  void generateFactors() const;
403 
404  /// randomly generate factor for a given node in a given structure
405  void generateFactor(const NodeSet& vars) const;
406 
407  /// when multiple change in factors/node, no need to update internal structure
408  /// many times during the process but only once at the end.
409  void beginTopologyTransformation();
410 
411  void endTopologyTransformation();
412 
413  private:
414  bool topologyTransformationInProgress__;
415 
416  /// clear all potentials
417  void clearFactors__();
418 
419  /// copy of potentials from a MN to another, using names of vars as ref.
420  void copyFactors__(const MarkovNet< GUM_SCALAR >& source);
421 
422  /// rebuild the graph after strucural changes in the factors
423  void rebuildGraph__();
424 
425  /// the map between variable and id
426  VariableNodeMap varMap__;
427 
428  /// the factors
429  FactorTable< GUM_SCALAR > factors__;
430 
431  const Potential< GUM_SCALAR >* addFactor__(const NodeSet& vars,
432  const Potential< GUM_SCALAR >* src
433  = nullptr);
434 
435  void eraseFactor__(const NodeSet& vars);
436 
437  public:
438  using IMarkovNet< GUM_SCALAR >::graph;
439  using IMarkovNet< GUM_SCALAR >::size;
440  using IMarkovNet< GUM_SCALAR >::nodes;
441  using IMarkovNet< GUM_SCALAR >::log10DomainSize;
442  };
443 
444  /// Prints map's DAG in output using the Graphviz-dot format.
445  template < typename GUM_SCALAR >
446  std::ostream& operator<<(std::ostream& output,
447  const MarkovNet< GUM_SCALAR >& bn);
448 
449 
450 #ifndef GUM_NO_EXTERN_TEMPLATE_CLASS
451 
452  extern template class MarkovNet< double >;
453 
454 #endif
455 
456 } /* namespace gum */
457 
458 #include <agrum/MN/MarkovNet_tpl.h>
459 
460 #endif /* GUM_MARKOV_NET_H */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:669