aGrUM  0.20.3
a C++ library for (probabilistic) graphical models
MarkovNet.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 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, Size domainSize = 2);
97 
98  /**
99  * build a Markov Network from a Bayesian network
100  * @param bn the Bayesian network
101  * @return a markov network
102  */
103  static MarkovNet< GUM_SCALAR > fromBN(const BayesNet< GUM_SCALAR >& bn);
104 
105  // ===========================================================================
106  /// @name Constructors and Destructor
107  // ===========================================================================
108  /// @{
109 
110  /**
111  * @brief Default constructor.
112  */
113  MarkovNet();
114 
115  /**
116  * @brief Default constructor.
117  *
118  * @param name The MarkovNet's name.
119  */
120  explicit MarkovNet(std::string name);
121 
122  /**
123  * @brief Destructor.
124  */
125  virtual ~MarkovNet() final;
126 
127  /**
128  * @brief Copy constructor.
129  */
130  MarkovNet(const MarkovNet< GUM_SCALAR >& source);
131 
132  /// @}
133  // ===========================================================================
134  /// @name Operators
135  // ===========================================================================
136  /// @{
137 
138  /**
139  * @brief Copy operator.
140  *
141  * @param source The copied MarkovNet.
142  * @return The copy of source.
143  */
144  MarkovNet< GUM_SCALAR >& operator=(const MarkovNet< GUM_SCALAR >& source);
145 
146  /// @}
147  // ===========================================================================
148  /// @name Variable manipulation methods
149  // ===========================================================================
150  /// @{
151 
152  /**
153  * @brief Returns the factor given a nodeset
154  *
155  * @param varId A variable's id in the gum::MarkovNet.
156  * @return The variable's CPT.
157  * @throw NotFound If no variable's id matches varId.
158  */
159  virtual const Potential< GUM_SCALAR >& factor(const NodeSet& varIds) const final;
160 
161  virtual const Potential< GUM_SCALAR >&
162  factor(const std::vector< std::string >& varnames) const final;
163 
164  /**
165  * Returns a factor that contains this variable
166  *
167  * @throw NotFound If no variable's id matches varId.
168  */
169  virtual const NodeSet& smallestFactorFromNode(NodeId node) const final;
170 
171  /**
172  * Returns the set of factors as a IMarkovNet::FactorTable
173  *
174  */
175  virtual const FactorTable< GUM_SCALAR >& factors() const final;
176 
177  /**
178  * @brief Returns a map between variables and nodes of this gum::MarkovNet.
179  *
180  * @return Returns a constant reference to the gum::VariableNodeMap.
181  */
182  virtual const VariableNodeMap& variableNodeMap() const final;
183 
184  /**
185  * @brief Add a variable to the gum::MarkovNet.
186  *
187  * Add a gum::DiscreteVariable, it's associated gum::NodeId
188  *opera
189  * The variable is added by copy to the gum::MarkovNet.
190  * The variable's gum::Potential implementation will be a
191  * gum::MultiDimArray.
192  *
193  * @param var The variable added by copy.
194  * @return Returns the variable's id in the gum::MarkovNet.
195  * @throws DuplicateLabel Raised if variable.name() is already used in this
196  * gum::MarkovNet.
197  */
198  NodeId add(const DiscreteVariable& var);
199 
200  /**
201  * @brief Shortcut for add(gum::LabelizedVariable(name,name,nbrmod))
202  *
203  * Add a gum::LabelizedVariable to the gum::MarkovNet
204  *
205  * This method is just a shortcut for a often used pattern
206  *
207  * @throws DuplicateLabel Raised if variable.name() is already used in this
208  * gum::MarkovNet.
209  * @throws NotAllowed if nbrmod<2
210  */
211  NodeId add(const std::string& name, unsigned int nbrmod);
212 
213  /**
214  * @brief Add a variable to the gum::MarkovNet.
215  *
216  * Add a gum::DiscreteVariable, it's associated gum::NodeId and it's
217  * gum::Potential.
218  *
219  * The variable is added by copy to the gum::MarkovNet.
220  * The variable's gum::Potential implementation will be a
221  * gum::MultiDimArray.
222  *
223  * @param var The variable added by copy.
224  * @param id The variable's forced gum::NodeId in the gum::MarkovNet.
225  * @return Returns the variable's id in the gum::MarkovNet.
226  * @throws DuplicateElement Raised id is already used.
227  * @throws DuplicateLabel Raised if variable.name() is already used in this
228  * gum::MarkovNet.
229  */
230  NodeId add(const DiscreteVariable& var, NodeId id);
231 
232  /**
233  * @brief clear the whole Markov net
234  */
235  void clear();
236 
237  /**
238  * @brief Remove a variable from the gum::MarkovNet.
239  *
240  * Removes the corresponding variable from the gum::MarkovNet and from
241  * all of the factors.
242  *
243  * If no variable matches the given id, then nothing is done.
244  *
245  * @param varId The variable's id to remove.
246  */
247  void erase(NodeId varId);
248 
249  /**
250  * @brief Removes a variable from the gum::MarkovNet.
251  */
252  void erase(const std::string& name);
253 
254  /**
255  * @brief Remove a variable from the gum::MarkovNet.
256  *
257  * Removes the corresponding variable from the gum::MarkovNet and from
258  * all of the factors.
259  *
260  * If no variable matches the given variable, then nothing is done.
261  *
262  * @param var A reference on the variable to remove.
263  */
264  void erase(const DiscreteVariable& var);
265 
266  /**
267  * @brief Returns a gum::DiscreteVariable given its gum::NodeId in the
268  * gum::MarkovNet.
269  *
270  * @param id The variable's id to return.
271  * @returns Returns a constant reference of the gum::DiscreteVariable
272  * corresponding to id in the gum::MarkovNet.
273  * @throw NotFound Raised if id does not match a a variable in the
274  * gum::MarkovNet.
275  */
276  const DiscreteVariable& variable(NodeId id) const final;
277 
278  /**
279  * @brief Returns a gum::DiscreteVariable given its name in the
280  * gum::MarkovNet.
281  * @throw NotFound Raised if id does not match a a variable in the
282  * gum::MarkovNet.
283  */
284  const DiscreteVariable& variable(const std::string& name) const {
285  return variable(idFromName(name));
286  };
287 
288  /**
289  * @brief Changes a variable's name in the gum::MarkovNet.
290  *
291  * This will change the gum::DiscreteVariable names in the gum::MarkovNet.
292  *
293  * @throws DuplicateLabel Raised if newName is already used in this
294  * gum::MarkovNet.
295  * @throws NotFound Raised if no variable matches id.
296  */
297  void changeVariableName(NodeId id, const std::string& new_name);
298 
299  /**
300  * @brief Changes a variable's name.
301  */
302  void changeVariableName(const std::string& name, const std::string& new_name) {
303  changeVariableName(idFromName(name), new_name);
304  }
305 
306  /**
307  * @brief Changes a variable's label in the gum::MarkovNet.
308  *
309  * This will change the gum::LabelizedVariable names in the gum::MarkovNet.
310  *
311  * @throws DuplicateLabel Raised if new_label is already used in this
312  * gum::LabelizedVariable.
313  * @throws NotFound Raised if no variable matches id or if the variable is not
314  * a LabelizedVariable
315  */
316  void changeVariableLabel(NodeId id, const std::string& old_label, const std::string& new_label);
317 
318  /**
319  * @brief Changes a variable's name.
320  */
321  void changeVariableLabel(const std::string& name,
322  const std::string& old_label,
323  const std::string& new_label) {
324  changeVariableLabel(idFromName(name), old_label, new_label);
325  }
326 
327  /**
328  * @brief Returns a variable's id in the gum::MarkovNet.
329  *
330  * @param var The variable from which the gum::NodeId is returned.
331  * @return Returns the gum::DiscreteVariable gum::NodeId in the
332  * gum::MarkovNet.
333  * @throw NotFound If var is not in the gum::MarkovNet.
334  */
335  NodeId nodeId(const DiscreteVariable& var) const final;
336 
337  /**
338  * @brief Returns a variable's id given its name in the gum::MarkovNet.
339  *
340  * @param name The variable's name from which the gum::NodeId is returned.
341  * @return Returns the variable gum::NodeId in the gum::MarkovNet.
342  * @throw NotFound Raised if name does not match a variable in the
343  * gum::MarkovNet.
344  */
345  NodeId idFromName(const std::string& name) const final;
346 
347  /**
348  * @brief Returns a variable given its name in the gum::MarkovNet.
349  *
350  * @param name The variable's name in the gum::MarkovNet.
351  * @return Returns the gum::DiscreteVariable named name in the
352  * gum::MarkovNet.
353  * @throw NotFound Raised if name does not match a variable in the
354  * gum::MarkovNet.
355  */
356  const DiscreteVariable& variableFromName(const std::string& name) const final;
357  /// @}
358 
359  // ===========================================================================
360  /// @name Edge manipulation methods (by the mean of factors)
361  // ===========================================================================
362  /// @{
363 
364  /**
365  * @brief Add a factor (a clique) to the gum::MarkovNet.
366  *
367  * @param vars the scope of the factor
368  * @param varnames the scope of the factor as vector of variable names
369  * @param aContent The gum::MultiDimImplementation to use for this
370  * variable's gum::Potential implementation (will be copied).
371  *
372  * @return a const ref to the factor in the Markov Network
373  *
374  * @warning in order to be deterministic, the Potential contains all the vars
375  * of the clique sorted by id.
376  */
377  const Potential< GUM_SCALAR >& addFactor(const std::vector< std::string >& varnames);
378 
379  const Potential< GUM_SCALAR >& addFactor(const NodeSet& vars);
380 
381  const Potential< GUM_SCALAR >& addFactor(const Potential< GUM_SCALAR >& factor);
382 
383  /**
384  * Removes a factor in the MN, and update head's CTP.
385  *
386  * @param vars the NodeSet
387  * @throw
388  */
389  void eraseFactor(const NodeSet& vars);
390 
391  void eraseFactor(const std::vector< std::string >& varnames);
392  /// @}
393 
394 
395  /// randomly generates factors for a given structure
396  void generateFactors() const;
397 
398  /// randomly generate factor for a given node in a given structure
399  void generateFactor(const NodeSet& vars) const;
400 
401  /// when multiple change in factors/node, no need to update internal structure
402  /// many times during the process but only once at the end.
403  void beginTopologyTransformation();
404 
405  void endTopologyTransformation();
406 
407  private:
408  bool _topologyTransformationInProgress_;
409 
410  /// clear all potentials
411  void _clearFactors_();
412 
413  /// copy of potentials from a MN to another, using names of vars as ref.
414  void _copyFactors_(const MarkovNet< GUM_SCALAR >& source);
415 
416  /// rebuild the graph after strucural changes in the factors
417  void _rebuildGraph_();
418 
419  /// the map between variable and id
420  VariableNodeMap _varMap_;
421 
422  /// the factors
423  FactorTable< GUM_SCALAR > _factors_;
424 
425  const Potential< GUM_SCALAR >* _addFactor_(const NodeSet& vars,
426  const Potential< GUM_SCALAR >* src = nullptr);
427 
428  void _eraseFactor_(const NodeSet& vars);
429 
430  public:
431  using IMarkovNet< GUM_SCALAR >::graph;
432  using IMarkovNet< GUM_SCALAR >::size;
433  using IMarkovNet< GUM_SCALAR >::nodes;
434  using IMarkovNet< GUM_SCALAR >::log10DomainSize;
435  };
436 
437  /// Prints map's DAG in output using the Graphviz-dot format.
438  template < typename GUM_SCALAR >
439  std::ostream& operator<<(std::ostream& output, const MarkovNet< GUM_SCALAR >& bn);
440 
441 
442 #ifndef GUM_NO_EXTERN_TEMPLATE_CLASS
443 
444  extern template class MarkovNet< double >;
445 
446 #endif
447 
448 } /* namespace gum */
449 
450 #include <agrum/MN/MarkovNet_tpl.h>
451 
452 #endif /* GUM_MARKOV_NET_H */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:643