aGrUM 3.1.1
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-2026 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-2026 *
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
41#pragma once
42
43
50
51#include <algorithm>
52#include <limits>
53#include <set>
54
71
73
74namespace gum {
75 template < GUM_Numeric GUM_SCALAR >
76 NodeId build_node_for_MN(MarkovRandomField< GUM_SCALAR >& mn,
77 std::string_view node,
78 std::string_view default_domain) {
79 auto v = fastVariable< GUM_SCALAR >(std::string{node}, default_domain);
80
81 NodeId res;
82 if (mn.exists(v->name())) res = mn.idFromName(v->name());
83 else res = mn.add(*v);
84 return res;
85 }
86
87 template < GUM_Numeric GUM_SCALAR >
88 MarkovRandomField< GUM_SCALAR >
89 MarkovRandomField< GUM_SCALAR >::fastPrototype(std::string_view dotlike, Size domainSize) {
90 return fastPrototype(dotlike, "[" + std::to_string(domainSize) + "]");
91 }
92
93 template < GUM_Numeric GUM_SCALAR >
94 MarkovRandomField< GUM_SCALAR >
95 MarkovRandomField< GUM_SCALAR >::fastPrototype(std::string_view dotlike,
96 std::string_view 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", "anonymousMRF");
110 return mn;
111 }
112
113 template < GUM_Numeric 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 < GUM_Numeric GUM_SCALAR >
130 MarkovRandomField< GUM_SCALAR >::MarkovRandomField() :
131 IMarkovRandomField< GUM_SCALAR >(), _topologyTransformationInProgress_(false) {
132 GUM_CONSTRUCTOR(MarkovRandomField);
133 }
134
135 template < GUM_Numeric GUM_SCALAR >
136 MarkovRandomField< GUM_SCALAR >::MarkovRandomField(std::string_view name) :
137 IMarkovRandomField< GUM_SCALAR >(name), _topologyTransformationInProgress_(false) {
138 GUM_CONSTRUCTOR(MarkovRandomField);
139 }
140
141 template < GUM_Numeric GUM_SCALAR >
142 MarkovRandomField< GUM_SCALAR >::MarkovRandomField(
143 const MarkovRandomField< GUM_SCALAR >& source) :
144 IMarkovRandomField< GUM_SCALAR >(source), _topologyTransformationInProgress_(false) {
145 GUM_CONS_CPY(MarkovRandomField);
146 _copyFactors_(source);
147 }
148
149 template < GUM_Numeric GUM_SCALAR >
150 MarkovRandomField< GUM_SCALAR >&
151 MarkovRandomField< GUM_SCALAR >::operator=(const MarkovRandomField< GUM_SCALAR >& source) {
152 if (this != &source) {
153 IMarkovRandomField< GUM_SCALAR >::operator=(source);
154 _topologyTransformationInProgress_ = false;
155 _copyFactors_(source);
156 }
157
158 return *this;
159 }
160
161 template < GUM_Numeric GUM_SCALAR >
162 MarkovRandomField< GUM_SCALAR >& MarkovRandomField< GUM_SCALAR >::operator=(
163 MarkovRandomField< GUM_SCALAR >&& source) noexcept {
164 if (this != &source) {
165 _clearFactors_();
166 UGmodel::operator=(std::move(source));
167 _topologyTransformationInProgress_ = false;
168 _factors_ = std::move(source._factors_);
169 source._rebuildGraph_();
170 GUM_OP_MOV(MarkovRandomField);
171 }
172 return *this;
173 }
174
175 template < GUM_Numeric GUM_SCALAR >
176 MarkovRandomField< GUM_SCALAR >::~MarkovRandomField() {
177 _clearFactors_();
178 GUM_DESTRUCTOR(MarkovRandomField);
179 }
180
181 template < GUM_Numeric GUM_SCALAR >
182 const DiscreteVariable& MarkovRandomField< GUM_SCALAR >::variable(std::string_view name) const {
183 return variable(idFromName(name));
184 }
185
186 template < GUM_Numeric GUM_SCALAR >
187 void MarkovRandomField< GUM_SCALAR >::changeVariableName(NodeId id, std::string_view new_name) {
188 this->varMap_.changeName(id, new_name);
189 }
190
191 template < GUM_Numeric GUM_SCALAR >
192 void MarkovRandomField< GUM_SCALAR >::changeVariableName(std::string_view name,
193 std::string_view new_name) {
194 changeVariableName(idFromName(name), new_name);
195 }
196
197 template < GUM_Numeric GUM_SCALAR >
198 void MarkovRandomField< GUM_SCALAR >::changeVariableLabel(std::string_view name,
199 std::string_view old_label,
200 std::string_view new_label) {
201 changeVariableLabel(idFromName(name), old_label, new_label);
202 }
203
204 template < GUM_Numeric GUM_SCALAR >
205 void MarkovRandomField< GUM_SCALAR >::changeVariableLabel(NodeId id,
206 std::string_view old_label,
207 std::string_view new_label) {
208 if (variable(id).varType() != VarType::LABELIZED) {
209 GUM_ERROR(NotFound, "Variable " << id << " is not a LabelizedVariable.")
210 }
211 auto* var = dynamic_cast< LabelizedVariable* >(const_cast< DiscreteVariable* >(&variable(id)));
212 if (var == nullptr) GUM_ERROR(TypeError, "Variable " << id << " is not a LabelizedVariable.")
213
214 var->changeLabel(var->posLabel(old_label), new_label);
215 }
216
217 template < GUM_Numeric GUM_SCALAR >
218 const Tensor< GUM_SCALAR >& MarkovRandomField< GUM_SCALAR >::factor(const NodeSet& varIds) const {
219 return *_factors_[varIds];
220 }
221
222 template < GUM_Numeric GUM_SCALAR >
223 const NodeSet& MarkovRandomField< GUM_SCALAR >::smallestFactorFromNode(NodeId node) const {
224 const NodeSet* res = nullptr;
225 Size smallest = size() + 1;
226 for (const auto& kv: factors()) {
227 const auto& fact = kv.first;
228 if (fact.contains(node))
229 if (smallest > fact.size()) {
230 res = &fact;
231 smallest = fact.size();
232 }
233 }
234 if (res == nullptr) {
235 GUM_ERROR(NotFound, "No factor containing node " << node)
236 } else {
237 return *res;
238 }
239 }
240
241 template < GUM_Numeric GUM_SCALAR >
242 const Tensor< GUM_SCALAR >&
243 MarkovRandomField< GUM_SCALAR >::factor(const std::vector< std::string >& varnames) const {
244 return factor(this->nodeset(varnames));
245 }
246
247 template < GUM_Numeric GUM_SCALAR >
248 const FactorTable< GUM_SCALAR >& MarkovRandomField< GUM_SCALAR >::factors() const {
249 return _factors_;
250 }
251
252 template < GUM_Numeric GUM_SCALAR >
253 NodeId MarkovRandomField< GUM_SCALAR >::add(std::string_view fast_description,
254 unsigned int default_nbrmod) {
255 auto v = fastVariable< GUM_SCALAR >(std::string{fast_description}, default_nbrmod);
256 if (v->domainSize() < 2) GUM_ERROR(OperationNotAllowed, v->name() << " has a domain size <2")
257 return add(*v);
258 }
259
260 template < GUM_Numeric GUM_SCALAR >
261 void MarkovRandomField< GUM_SCALAR >::_rebuildGraph_() {
262 if (_topologyTransformationInProgress_) return;
263
264 this->graph_.clearEdges();
265
266 for (const auto& kv: _factors_) {
267 auto& c = *kv.second;
268 for (Idx i = 0; i < c.nbrDim(); i++)
269 for (Idx j = i + 1; j < c.nbrDim(); j++)
270 this->graph_.addEdge(this->varMap_.get(c.variable(i)), this->varMap_.get(c.variable(j)));
271 }
272 }
273
274 template < GUM_Numeric GUM_SCALAR >
275 NodeId MarkovRandomField< GUM_SCALAR >::add(const DiscreteVariable& var) {
276 return add(var, graph().nextNodeId());
277 }
278
279 template < GUM_Numeric GUM_SCALAR >
280 NodeId MarkovRandomField< GUM_SCALAR >::add(const DiscreteVariable& var, NodeId id) {
281 this->varMap_.insert(id, var);
282 this->graph_.addNodeWithId(id);
283 return id;
284 }
285
286 template < GUM_Numeric GUM_SCALAR >
287 void MarkovRandomField< GUM_SCALAR >::erase(const DiscreteVariable& var) {
288 erase(this->varMap_.get(var));
289 }
290
291 template < GUM_Numeric GUM_SCALAR >
292 void MarkovRandomField< GUM_SCALAR >::erase(std::string_view name) {
293 erase(idFromName(name));
294 }
295
296 template < GUM_Numeric GUM_SCALAR >
297 void MarkovRandomField< GUM_SCALAR >::erase(NodeId varId) {
298 if (!this->varMap_.exists(varId)) {
299 GUM_ERROR(InvalidArgument, "No node with id " << varId << ".")
300 }
301 this->varMap_.erase(varId);
302 this->graph_.eraseNode(varId);
303
304 std::vector< NodeSet > vs;
305 for (const auto& kv: _factors_) {
306 if (kv.first.contains(varId)) { vs.push_back(kv.first); }
307 }
308 for (const auto& ns: vs) {
309 _eraseFactor_(ns);
310 }
311 for (const auto& ns: vs) {
312 NodeSet nv = ns;
313 nv.erase(varId);
314 if (nv.size() > 1) addFactor(nv);
315 }
316 _rebuildGraph_();
317 }
318
319 template < GUM_Numeric GUM_SCALAR >
320 void MarkovRandomField< GUM_SCALAR >::clear() {
321 if (!this->empty()) {
322 auto l = this->nodes();
323 for (const auto no: l) {
324 this->erase(no);
325 }
326 }
327 _rebuildGraph_();
328 }
329
330 template < GUM_Numeric GUM_SCALAR >
331 std::ostream& operator<<(std::ostream& output, const MarkovRandomField< GUM_SCALAR >& mn) {
332 output << mn.toString();
333 return output;
334 }
335
336 template < GUM_Numeric GUM_SCALAR >
337 Tensor< GUM_SCALAR >&
338 MarkovRandomField< GUM_SCALAR >::_addFactor_(const std::vector< NodeId >& ordered_nodes) {
339 NodeSet vars;
340 for (auto node: ordered_nodes)
341 vars.insert(node);
342
343 if (vars.size() == 0) { GUM_ERROR(InvalidArgument, "Empty factor cannot be added.") }
344
345 if (_factors_.exists(vars)) {
346 GUM_ERROR(InvalidArgument, "A factor for (" << this->names(vars) << ") already exists.")
347 }
348
349 Tensor< GUM_SCALAR >* factor = new Tensor< GUM_SCALAR >();
350
351 for (auto node: ordered_nodes) {
352 factor->add(variable(node));
353 }
354
355 _factors_.insert(vars, factor);
356 _rebuildGraph_();
357
358 return *factor;
359 }
360
361 template < GUM_Numeric GUM_SCALAR >
362 const Tensor< GUM_SCALAR >& MarkovRandomField< GUM_SCALAR >::addFactor(const NodeSet& vars) {
363 // in order to be deterministic, the Tensor contains all the vars sorted by id.
364 std::vector< NodeId > sorted_nodes;
365 for (auto node: vars) {
366 sorted_nodes.push_back(node);
367 }
368 std::sort(sorted_nodes.begin(), sorted_nodes.end());
369
370 return _addFactor_(sorted_nodes);
371 }
372
373 template < GUM_Numeric GUM_SCALAR >
374 const Tensor< GUM_SCALAR >&
375 MarkovRandomField< GUM_SCALAR >::addFactor(const std::vector< std::string >& varnames) {
376 std::vector< NodeId > sorted_nodes;
377 for (const auto& v: varnames) {
378 sorted_nodes.push_back(idFromName(v));
379 }
380
381 return _addFactor_(sorted_nodes);
382 }
383
384 template < GUM_Numeric GUM_SCALAR >
385 const Tensor< GUM_SCALAR >&
386 MarkovRandomField< GUM_SCALAR >::addFactor(const Tensor< GUM_SCALAR >& factor) {
387 std::vector< NodeId > sorted_nodes;
388 for (Idx i = 0; i < factor.nbrDim(); i++) {
389 sorted_nodes.push_back(idFromName(factor.variable(i).name()));
390 }
391 auto& res = _addFactor_(sorted_nodes);
392 res.fillWith(factor);
393
394 return res;
395 }
396
397 template < GUM_Numeric GUM_SCALAR >
398 void MarkovRandomField< GUM_SCALAR >::generateFactors() const {
399 for (const auto& elt: _factors_) {
400 elt.second->random();
401 }
402 }
403
404 template < GUM_Numeric GUM_SCALAR >
405 void MarkovRandomField< GUM_SCALAR >::generateFactor(const NodeSet& vars) const {
406 _factors_[vars]->random();
407 }
408
409 template < GUM_Numeric GUM_SCALAR >
410 void MarkovRandomField< GUM_SCALAR >::eraseFactor(const NodeSet& vars) {
411 if (_factors_.exists(vars)) {
412 _eraseFactor_(vars);
413 _rebuildGraph_();
414 } else {
415 GUM_ERROR(InvalidArgument, "No factor for " << vars << ".")
416 }
417 }
418
419 template < GUM_Numeric GUM_SCALAR >
420 void MarkovRandomField< GUM_SCALAR >::eraseFactor(const std::vector< std::string >& varnames) {
421 auto vars = this->nodeset(varnames);
422 if (_factors_.exists(vars)) {
423 _eraseFactor_(vars);
424 _rebuildGraph_();
425 } else {
426 GUM_ERROR(InvalidArgument, "No factor for " << varnames << ".")
427 }
428 }
429
430 template < GUM_Numeric GUM_SCALAR >
431 void MarkovRandomField< GUM_SCALAR >::_eraseFactor_(const NodeSet& vars) {
432 delete _factors_[vars];
433 _factors_.erase(vars);
434 }
435
436 template < GUM_Numeric GUM_SCALAR >
437 void MarkovRandomField< GUM_SCALAR >::_clearFactors_() {
438 for (const auto& kv: _factors_) {
439 delete kv.second;
440 }
441 _factors_.clear();
442 _rebuildGraph_();
443 }
444
445 template < GUM_Numeric GUM_SCALAR >
446 void MarkovRandomField< GUM_SCALAR >::_copyFactors_(
447 const MarkovRandomField< GUM_SCALAR >& source) {
448 _clearFactors_();
449 for (const auto& pf: source.factors()) {
450 addFactor(*pf.second);
451 }
452 _rebuildGraph_();
453 }
454
455 template < GUM_Numeric GUM_SCALAR >
456 void MarkovRandomField< GUM_SCALAR >::beginTopologyTransformation() {
457 _topologyTransformationInProgress_ = true;
458 }
459
460 template < GUM_Numeric GUM_SCALAR >
461 void MarkovRandomField< GUM_SCALAR >::endTopologyTransformation() {
462 if (_topologyTransformationInProgress_) {
463 _topologyTransformationInProgress_ = false; // before rebuildGraph of course
464 _rebuildGraph_();
465 }
466 }
467} /* namespace gum */
Class representing Markov random fields.
amplitude aggregator
and aggregator
Class representing a Bayesian network.
Definition BayesNet.h:99
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:510
void erase(const Key &k)
Erases an element from the set.
Definition set_tpl.h:553
Exception : wrong type for this operation.
count aggregator
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
exists aggregator
forall aggregator
Size NodeId
Type for node ids.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
std::string remove_newline(std::string_view s)
remove all newlines in a string
std::vector< std::string > split(std::string_view str, std::string_view 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
NodeId build_node_for_MN(MarkovRandomField< GUM_SCALAR > &mn, std::string_view node, std::string_view default_domain)
std::unique_ptr< DiscreteVariable > fastVariable(std::string var_description, Size default_domain_size)
Create a pointer on a Discrete Variable from a "fast" syntax.
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.