aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
MarkovRandomField_tpl.h
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2025 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2025 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40#pragma once
41
42
49
50#include <algorithm>
51#include <limits>
52#include <set>
53
70
72
73namespace gum {
74 template < typename GUM_SCALAR >
75 NodeId build_node_for_MN(MarkovRandomField< GUM_SCALAR >& mn,
76 const std::string& node,
77 const std::string& default_domain) {
78 auto v = fastVariable< GUM_SCALAR >(node, default_domain);
79
80 NodeId res;
81 try {
82 res = mn.idFromName(v->name());
83 } catch (gum::NotFound&) { res = mn.add(*v); }
84 return res;
85 }
86
87 template < typename GUM_SCALAR >
88 MarkovRandomField< GUM_SCALAR >
89 MarkovRandomField< GUM_SCALAR >::fastPrototype(const std::string& dotlike, Size domainSize) {
90 return fastPrototype(dotlike, "[" + std::to_string(domainSize) + "]");
91 }
92
93 template < typename GUM_SCALAR >
94 MarkovRandomField< GUM_SCALAR >
95 MarkovRandomField< GUM_SCALAR >::fastPrototype(const std::string& dotlike,
96 const std::string& domain) {
97 MarkovRandomField< GUM_SCALAR > mn;
98
99
100 for (const auto& clikchain: split(remove_newline(dotlike), ";")) {
101 NodeSet cliq;
102 for (auto& node: split(clikchain, "--")) {
103 auto idVar = build_node_for_MN(mn, node, domain);
104 cliq.insert(idVar);
105 }
106 mn.addFactor(cliq);
107 }
108 mn.generateFactors();
109 mn.setProperty("name", "fastPrototype");
110 return mn;
111 }
112
113 template < typename GUM_SCALAR >
114 MarkovRandomField< GUM_SCALAR >
115 MarkovRandomField< GUM_SCALAR >::fromBN(const BayesNet< GUM_SCALAR >& bn) {
116 MarkovRandomField< GUM_SCALAR > mn;
117 for (NodeId nod: bn.nodes()) {
118 mn.add(bn.variable(nod), nod);
119 }
120 mn.beginTopologyTransformation();
121 for (NodeId nod: bn.nodes()) {
122 mn.addFactor(bn.cpt(nod));
123 }
124 mn.endTopologyTransformation();
125 mn.setProperty("name", bn.propertyWithDefault("name", "noname"));
126 return mn;
127 }
128
129 template < typename GUM_SCALAR >
130 INLINE MarkovRandomField< GUM_SCALAR >::MarkovRandomField() :
131 IMarkovRandomField< GUM_SCALAR >(), _topologyTransformationInProgress_(false) {
132 GUM_CONSTRUCTOR(MarkovRandomField);
133 }
134
135 template < typename GUM_SCALAR >
136 INLINE MarkovRandomField< GUM_SCALAR >::MarkovRandomField(std::string name) :
137 IMarkovRandomField< GUM_SCALAR >(name), _topologyTransformationInProgress_(false) {
138 GUM_CONSTRUCTOR(MarkovRandomField);
139 }
140
141 template < typename GUM_SCALAR >
142 MarkovRandomField< GUM_SCALAR >::MarkovRandomField(
143 const MarkovRandomField< GUM_SCALAR >& source) :
144 IMarkovRandomField< GUM_SCALAR >(source), _topologyTransformationInProgress_(false),
145 _varMap_(source._varMap_) {
146 GUM_CONS_CPY(MarkovRandomField);
147 _copyFactors_(source);
148 }
149
150 template < typename GUM_SCALAR >
151 MarkovRandomField< GUM_SCALAR >&
152 MarkovRandomField< GUM_SCALAR >::operator=(const MarkovRandomField< GUM_SCALAR >& source) {
153 if (this != &source) {
154 IMarkovRandomField< GUM_SCALAR >::operator=(source);
155 _varMap_ = source._varMap_;
156 _topologyTransformationInProgress_ = false;
157 _copyFactors_(source);
158 }
159
160 return *this;
161 }
162
163 template < typename GUM_SCALAR >
164 MarkovRandomField< GUM_SCALAR >::~MarkovRandomField() {
165 _clearFactors_();
166 GUM_DESTRUCTOR(MarkovRandomField);
167 }
168
169 template < typename GUM_SCALAR >
170 INLINE const DiscreteVariable& MarkovRandomField< GUM_SCALAR >::variable(NodeId id) const {
171 return _varMap_.get(id);
172 }
173
174 template < typename GUM_SCALAR >
175 INLINE void MarkovRandomField< GUM_SCALAR >::changeVariableName(NodeId id,
176 const std::string& new_name) {
177 _varMap_.changeName(id, new_name);
178 }
179
180 template < typename GUM_SCALAR >
181 INLINE void MarkovRandomField< GUM_SCALAR >::changeVariableLabel(NodeId id,
182 const std::string& old_label,
183 const std::string& new_label) {
184 if (variable(id).varType() != VarType::LABELIZED) {
185 GUM_ERROR(NotFound, "Variable " << id << " is not a LabelizedVariable.")
186 }
187 LabelizedVariable* var
188 = dynamic_cast< LabelizedVariable* >(const_cast< DiscreteVariable* >(&variable(id)));
189
190 var->changeLabel(var->posLabel(old_label), new_label);
191 }
192
193 template < typename GUM_SCALAR >
194 INLINE NodeId MarkovRandomField< GUM_SCALAR >::nodeId(const DiscreteVariable& var) const {
195 return _varMap_.get(var);
196 }
197
198 template < typename GUM_SCALAR >
199 const Tensor< GUM_SCALAR >& MarkovRandomField< GUM_SCALAR >::factor(const NodeSet& varIds) const {
200 return *_factors_[varIds];
201 }
202
203 template < typename GUM_SCALAR >
204 const NodeSet& MarkovRandomField< GUM_SCALAR >::smallestFactorFromNode(NodeId node) const {
205 const NodeSet* res = nullptr;
206 Size smallest = size() + 1;
207 for (const auto& kv: factors()) {
208 const auto& fact = kv.first;
209 if (fact.contains(node))
210 if (smallest > fact.size()) {
211 res = &fact;
212 smallest = fact.size();
213 }
214 }
215 if (res == nullptr) {
216 GUM_ERROR(NotFound, "No factor containing node " << node)
217 } else {
218 return *res;
219 }
220 }
221
222 template < typename GUM_SCALAR >
223 const Tensor< GUM_SCALAR >&
224 MarkovRandomField< GUM_SCALAR >::factor(const std::vector< std::string >& varnames) const {
225 return factor(this->nodeset(varnames));
226 }
227
228 template < typename GUM_SCALAR >
229 const FactorTable< GUM_SCALAR >& MarkovRandomField< GUM_SCALAR >::factors() const {
230 return _factors_;
231 }
232
233 template < typename GUM_SCALAR >
234 INLINE NodeId MarkovRandomField< GUM_SCALAR >::add(const std::string& fast_description,
235 unsigned int default_nbrmod) {
236 auto v = fastVariable< GUM_SCALAR >(fast_description, default_nbrmod);
237 if (v->domainSize() < 2) GUM_ERROR(OperationNotAllowed, v->name() << " has a domain size <2")
238 return add(*v);
239 }
240
241 template < typename GUM_SCALAR >
242 INLINE void MarkovRandomField< GUM_SCALAR >::_rebuildGraph_() {
243 if (_topologyTransformationInProgress_) return;
244
245 this->graph_.clearEdges();
246
247 for (const auto& kv: _factors_) {
248 auto& c = *kv.second;
249 for (Idx i = 0; i < c.nbrDim(); i++)
250 for (Idx j = i + 1; j < c.nbrDim(); j++)
251 this->graph_.addEdge(_varMap_.get(c.variable(i)), _varMap_.get(c.variable(j)));
252 }
253 }
254
255 template < typename GUM_SCALAR >
256 INLINE NodeId MarkovRandomField< GUM_SCALAR >::add(const DiscreteVariable& var) {
257 return add(var, graph().nextNodeId());
258 }
259
260 template < typename GUM_SCALAR >
261 INLINE NodeId MarkovRandomField< GUM_SCALAR >::add(const DiscreteVariable& var, NodeId id) {
262 _varMap_.insert(id, var);
263 this->graph_.addNodeWithId(id);
264 return id;
265 }
266
267 template < typename GUM_SCALAR >
268 INLINE NodeId MarkovRandomField< GUM_SCALAR >::idFromName(const std::string& name) const {
269 return _varMap_.idFromName(name);
270 }
271
272 template < typename GUM_SCALAR >
273 INLINE const DiscreteVariable&
274 MarkovRandomField< GUM_SCALAR >::variableFromName(const std::string& name) const {
275 return _varMap_.variableFromName(name);
276 }
277
278 template < typename GUM_SCALAR >
279 INLINE const VariableNodeMap& MarkovRandomField< GUM_SCALAR >::variableNodeMap() const {
280 return _varMap_;
281 }
282
283 template < typename GUM_SCALAR >
284 INLINE void MarkovRandomField< GUM_SCALAR >::erase(const DiscreteVariable& var) {
285 erase(_varMap_.get(var));
286 }
287
288 template < typename GUM_SCALAR >
289 INLINE void MarkovRandomField< GUM_SCALAR >::erase(const std::string& name) {
290 erase(idFromName(name));
291 }
292
293 template < typename GUM_SCALAR >
294 void MarkovRandomField< GUM_SCALAR >::erase(NodeId varId) {
295 if (!_varMap_.exists(varId)) { GUM_ERROR(InvalidArgument, "No node with id " << varId << ".") }
296 _varMap_.erase(varId);
297 this->graph_.eraseNode(varId);
298
299 std::vector< NodeSet > vs;
300 for (const auto& kv: _factors_) {
301 if (kv.first.contains(varId)) { vs.push_back(kv.first); }
302 }
303 for (const auto& ns: vs) {
304 _eraseFactor_(ns);
305 }
306 for (const auto& ns: vs) {
307 NodeSet nv = ns;
308 nv.erase(varId);
309 if (nv.size() > 1) addFactor(nv);
310 }
311 _rebuildGraph_();
312 }
313
314 template < typename GUM_SCALAR >
315 void MarkovRandomField< GUM_SCALAR >::clear() {
316 if (!this->empty()) {
317 auto l = this->nodes();
318 for (const auto no: l) {
319 this->erase(no);
320 }
321 }
322 _rebuildGraph_();
323 }
324
325 template < typename GUM_SCALAR >
326 INLINE std::ostream& operator<<(std::ostream& output, const MarkovRandomField< GUM_SCALAR >& mn) {
327 output << mn.toString();
328 return output;
329 }
330
331 template < typename GUM_SCALAR >
332 Tensor< GUM_SCALAR >&
333 MarkovRandomField< GUM_SCALAR >::_addFactor_(const std::vector< NodeId >& ordered_nodes) {
334 NodeSet vars;
335 for (auto node: ordered_nodes)
336 vars.insert(node);
337
338 if (vars.size() == 0) { GUM_ERROR(InvalidArgument, "Empty factor cannot be added.") }
339
340 if (_factors_.exists(vars)) {
341 GUM_ERROR(InvalidArgument, "A factor for (" << this->names(vars) << ") already exists.")
342 }
343
344 Tensor< GUM_SCALAR >* factor = new Tensor< GUM_SCALAR >();
345
346 for (auto node: ordered_nodes) {
347 factor->add(variable(node));
348 }
349
350 _factors_.insert(vars, factor);
351 _rebuildGraph_();
352
353 return *factor;
354 }
355
356 template < typename GUM_SCALAR >
357 INLINE const Tensor< GUM_SCALAR >&
358 MarkovRandomField< GUM_SCALAR >::addFactor(const NodeSet& vars) {
359 // in order to be deterministic, the Tensor contains all the vars sorted by id.
360 std::vector< NodeId > sorted_nodes;
361 for (auto node: vars) {
362 sorted_nodes.push_back(node);
363 }
364 std::sort(sorted_nodes.begin(), sorted_nodes.end());
365
366 return _addFactor_(sorted_nodes);
367 }
368
369 template < typename GUM_SCALAR >
370 INLINE const Tensor< GUM_SCALAR >&
371 MarkovRandomField< GUM_SCALAR >::addFactor(const std::vector< std::string >& varnames) {
372 std::vector< NodeId > sorted_nodes;
373 for (const auto& v: varnames) {
374 sorted_nodes.push_back(idFromName(v));
375 }
376
377 return _addFactor_(sorted_nodes);
378 }
379
380 template < typename GUM_SCALAR >
381 INLINE const Tensor< GUM_SCALAR >&
382 MarkovRandomField< GUM_SCALAR >::addFactor(const Tensor< GUM_SCALAR >& factor) {
383 std::vector< NodeId > sorted_nodes;
384 for (Idx i = 0; i < factor.nbrDim(); i++) {
385 sorted_nodes.push_back(idFromName(factor.variable(i).name()));
386 }
387 auto& res = _addFactor_(sorted_nodes);
388 res.fillWith(factor);
389
390 return res;
391 }
392
393 template < typename GUM_SCALAR >
394 INLINE void MarkovRandomField< GUM_SCALAR >::generateFactors() const {
395 for (const auto& elt: _factors_) {
396 elt.second->random();
397 }
398 }
399
400 template < typename GUM_SCALAR >
401 INLINE void MarkovRandomField< GUM_SCALAR >::generateFactor(const NodeSet& vars) const {
402 _factors_[vars]->random();
403 }
404
405 template < typename GUM_SCALAR >
406 INLINE void MarkovRandomField< GUM_SCALAR >::eraseFactor(const NodeSet& vars) {
407 if (_factors_.exists(vars)) {
408 _eraseFactor_(vars);
409 _rebuildGraph_();
410 } else {
411 GUM_ERROR(InvalidArgument, "No factor for " << vars << ".")
412 }
413 }
414
415 template < typename GUM_SCALAR >
416 INLINE void
417 MarkovRandomField< GUM_SCALAR >::eraseFactor(const std::vector< std::string >& varnames) {
418 auto vars = this->nodeset(varnames);
419 if (_factors_.exists(vars)) {
420 _eraseFactor_(vars);
421 _rebuildGraph_();
422 } else {
423 GUM_ERROR(InvalidArgument, "No factor for " << varnames << ".")
424 }
425 }
426
427 template < typename GUM_SCALAR >
428 INLINE void MarkovRandomField< GUM_SCALAR >::_eraseFactor_(const NodeSet& vars) {
429 delete _factors_[vars];
430 _factors_.erase(vars);
431 }
432
433 template < typename GUM_SCALAR >
434 void MarkovRandomField< GUM_SCALAR >::_clearFactors_() {
435 for (const auto& kv: _factors_) {
436 delete kv.second;
437 }
438 _factors_.clear();
439 _rebuildGraph_();
440 }
441
442 template < typename GUM_SCALAR >
443 void MarkovRandomField< GUM_SCALAR >::_copyFactors_(
444 const MarkovRandomField< GUM_SCALAR >& source) {
445 _clearFactors_();
446 for (const auto& pf: source.factors()) {
447 addFactor(*pf.second);
448 }
449 _rebuildGraph_();
450 }
451
452 template < typename GUM_SCALAR >
453 INLINE void MarkovRandomField< GUM_SCALAR >::beginTopologyTransformation() {
454 _topologyTransformationInProgress_ = true;
455 }
456
457 template < typename GUM_SCALAR >
458 INLINE void MarkovRandomField< GUM_SCALAR >::endTopologyTransformation() {
459 if (_topologyTransformationInProgress_) {
460 _topologyTransformationInProgress_ = false; // before rebuildGraph of course
461 _rebuildGraph_();
462 }
463 }
464} /* namespace gum */
Class representing Markov random fields.
amplitude aggregator
and aggregator
Class representing a Bayesian network.
Definition BayesNet.h:93
Class representing the minimal interface for Markov random field.
Exception: at least one argument passed to a function is not what was expected.
Exception : the element we looked for cannot be found.
Exception : operation not allowed.
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:539
void erase(const Key &k)
Erases an element from the set.
Definition set_tpl.h:582
count aggregator
#define GUM_ERROR(type, msg)
Definition exceptions.h:72
exists aggregator
forall aggregator
Size NodeId
Type for node ids.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
std::string remove_newline(const std::string &s)
remove all newlines in a string
std::vector< std::string > split(const std::string &str, const std::string &delim)
Split str using the delimiter.
max aggregator
median aggregator
min aggregator
class for LOGIT implementation as multiDim
class for NoisyAND-net implementation as multiDim
class for multiDimNoisyORCompound
class for NoisyOR-net implementation as multiDim
NodeId nextNodeId()
Returns the next value of an unique counter for PRM's node id.
Definition utils_prm.cpp:84
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
std::unique_ptr< DiscreteVariable > fastVariable(std::string var_description, Size default_domain_size)
Create a pointer on a Discrete Variable from a "fast" syntax.
NodeId build_node_for_MN(MarkovRandomField< GUM_SCALAR > &mn, const std::string &node, const std::string &default_domain)
or aggregator
Abstract class for generating Conditional Probability Tables.
std::ostream & operator<<(std::ostream &out, const TiXmlNode &base)
Definition tinyxml.cpp:1516
Utilities for manipulating strings.