aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
ContextualDependenciesCNFWriter_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
44#include <agrum/BN/io/cnf/ContextualDependenciesCNFWriter.h> // to ease IDE parser
45#ifndef DOXYGEN_SHOULD_SKIP_THIS
46
47// to ease parsing in IDE
49
50namespace gum {
51 /* =========================================================================*/
52 /* === GUM_BN_WRITER === */
53 /* =========================================================================*/
54 // Default constructor.
55 template < GUM_Numeric GUM_SCALAR, template < class > class IApproximationPolicy >
57 IApproximationPolicy >::ContextualDependenciesCNFWriter() {
58 GUM_CONSTRUCTOR(ContextualDependenciesCNFWriter)
59 }
60
61 // Default destructor.
62 template < GUM_Numeric GUM_SCALAR, template < class > class IApproximationPolicy >
64 IApproximationPolicy >::~ContextualDependenciesCNFWriter() {
66 }
67
68 //
69 // Writes a Bayesian network in the output stream using the BN format.
70 //
71 // @param ouput The output stream.
72 // @param bn The Bayesian network writen in output.
73 // @throws Raised if an I/O error occurs.
74 template < GUM_Numeric GUM_SCALAR, template < class > class IApproximationPolicy >
76 std::ostream& output,
77 const IBayesNet< GUM_SCALAR >& bn) {
78 Instantiation Order;
79
80 for (auto node: bn.topologicalOrder())
81 Order.add(bn.variable(node));
82
83 if (!output.good()) GUM_ERROR(IOError, "Input/Output error : stream not writable.")
84
85 std::stringstream strfile;
86 std::stringstream strfile2;
87
88 Idx num = 0;
89 Idx numvar = 0;
90 Idx clause = 0;
91 std::stringstream clausstr;
92 std::stringstream clausstr2;
93 gum::HashTable< std::string, Idx > vartable;
94 gum::HashTable< std::string, Idx > protable;
95 gum::HashTable<
96 const gum::DiscreteVariable*,
97 gum::HashTable< std::string, gum::Sequence< gum::Sequence< gum::Instantiation* >* >* >* >
98 cptparamval;
99
100 for (auto node: bn.nodes()) {
101 std::stringstream str0;
102 const DiscreteVariable* var = &bn.variable(node);
103
104 for (Idx i = 0; i < var->domainSize(); i++) {
105 auto stri = std::format("{}_{}", var->name(), var->label(i));
106 vartable.insert(stri, ++num);
107 strfile << std::format("{}::{}\n", num, stri);
108 str0 << std::format("{} ", vartable[stri]);
109 }
110
111 str0 << "0\n";
112 clause++;
113 numvar++;
114 clausstr2 << str0.str();
115 const Tensor< GUM_SCALAR >& cpt = bn.cpt(node);
116 Instantiation inst(cpt);
117 inst.forgetMaster();
118 inst.reorder(Order);
119 cptparamval.insert(
120 var,
121 new gum::HashTable< std::string,
122 gum::Sequence< gum::Sequence< gum::Instantiation* >* >* >());
123
124 for (inst.setFirst(); !inst.end(); ++inst) {
125 if (this->fromExact(cpt[inst]) != 1) {
126 std::string valp = std::format("{}", this->fromExact(cpt[inst]));
127
128 if (!(cptparamval[var])->exists(valp)) {
129 (cptparamval[var])
130 ->insert(valp, new gum::Sequence< gum::Sequence< gum::Instantiation* >* >());
131
132 (*(cptparamval[var]))[valp]->insert(new gum::Sequence< gum::Instantiation* >);
133
134 if (this->fromExact(cpt[inst])) {
135 auto strinst = std::format("{}_val={}", var->name(), this->fromExact(cpt[inst]));
136
137 if (!protable.exists(strinst)) {
138 protable.insert(strinst, ++num);
139 strfile << std::format("{}::{}\n", num, strinst);
140 }
141 }
142 }
143
144 (*(cptparamval[var]))[valp]->front()->insert(new gum::Instantiation(inst));
145 }
146 }
147 }
148
149 std::stringstream str2;
150
151 while (!cptparamval.empty()) {
152 auto itvar = cptparamval.begin();
153
154 while (!(itvar.val())->empty()) {
155 auto itpvall = (itvar.val())->begin(); // needed here
156
157 for (auto pv: *itpvall.val()) {
158 gum::Idx linecount = 0;
159 gum::HashTable< std::string,
160 gum::HashTable< const gum::DiscreteVariable*,
161 std::pair< gum::Set< Idx >*,
162 gum::Set< Idx >* >* >* >
163 orderStruct; // set sizeof Hashtable
164
165 for (const auto* seqv: *pv) {
166 if (seqv->nbrDim() > 1) {
167 for (Idx iInst = 0; iInst < seqv->nbrDim(); iInst++) {
168 gum::Instantiation instpro(*seqv, false);
169 instpro.reorder(Order);
170 const gum::DiscreteVariable* var = &(seqv->variable(iInst));
171 instpro.erase(*var); // reorder instance to optimize make sure
172 // key unicity.
173
174 if (!orderStruct.exists(instpro.toString())) {
175 orderStruct.insert(
176 instpro.toString(),
177 new gum::HashTable< const gum::DiscreteVariable*,
178 std::pair< gum::Set< Idx >*, gum::Set< Idx >* >* >());
179 }
180
181 if (!orderStruct[instpro.toString()]->exists(var)) {
182 orderStruct[instpro.toString()]->insert(
183 var,
184 new std::pair< gum::Set< Idx >*, gum::Set< Idx >* >(
185 new gum::Set< Idx >,
186 new gum::Set< Idx >(
187 seqv->variable(iInst).domainSize()))); // set sizeof Hashtable
188 }
189
190 gum::HashTable< const gum::DiscreteVariable*,
191 std::pair< gum::Set< Idx >*, gum::Set< Idx >* >* >* orderStruct2
192 = orderStruct[instpro.toString()];
193 (*orderStruct2)[var]->first->insert(linecount);
194 (*orderStruct2)[var]->second->insert(seqv->val(iInst));
195 }
196 }
197
198 linecount += 1;
199 }
200
201 gum::Set< gum::Idx > elimination;
202 gum::Sequence< gum::Instantiation* >* newSeq = nullptr;
203
204 for (const auto& [first, second]: orderStruct) {
205 bool added = false;
206
207 for (const auto& [first2, second2]: *second) {
208 if (second2->second->size() == first2->domainSize()) {
209 if (!newSeq) newSeq = new gum::Sequence< gum::Instantiation* >();
210
211 if (!added) {
212 added = true;
213 newSeq->insert(
214 new gum::Instantiation(*((*pv)[(*(second2->first->begin()))]), false));
215 newSeq->back()->erase(*first2);
216 }
217
218 elimination = elimination + *(second2->first);
219 }
220 }
221 }
222
223 if (newSeq) {
224 (itpvall.val())->insert(newSeq);
225
226 for (Idx itelem = pv->size(); itelem > 0; itelem--) {
227 if (elimination.exists(itelem - 1)) {
228 delete ((*pv)[itelem - 1]);
229 pv->erase((*pv)[itelem - 1]);
230 }
231 }
232 }
233
234 while (!orderStruct.empty()) {
235 while (!(orderStruct.begin().val())->empty()) {
236 delete orderStruct.begin().val()->begin().val()->first;
237 delete orderStruct.begin().val()->begin().val()->second;
238 (orderStruct.begin().val())
239 ->erase((orderStruct.begin().val())->beginSafe()); // safe iterator needed here
240 }
241
242 delete orderStruct.begin().val();
243 orderStruct.erase(orderStruct.beginSafe()); // safe iterator needed here
244 }
245 }
246
247 while (!(itpvall.val())->empty()) {
248 auto itpv = (itpvall.val())->begin();
249
250 while (!(*itpv)->empty()) {
251 auto itseqv = (*itpv)->begin();
252
253 for (Idx i = 0; i < (*itseqv)->nbrDim(); i++) {
254 auto str = std::format("{}_{}",
255 (*itseqv)->variable(i).name(),
256 (*itseqv)->val((*itseqv)->variable(i)));
257 str2 << std::format("-{} ", vartable[str]);
258 }
259
260 if (itpvall.key() != "0" && itpvall.key() != "0.0") {
261 auto strinst = std::format("{}_val={}", itvar.key()->name(), itpvall.key());
262 str2 << protable[strinst];
263 }
264
265 str2 << " 0\n";
266 clause++;
267 delete (*itseqv);
268 (*itpv)->erase(itseqv);
269 }
270
271 delete (*itpv);
272 (itpvall.val())->erase(itpv);
273 }
274
275 delete (itpvall.val());
276 (itvar.val())->erase(itvar.val()->beginSafe()); // safe iterator needed here
277 }
278
279 delete (itvar.val());
280 cptparamval.erase(cptparamval.beginSafe()); // safe iterator needed here
281 }
282
283 clausstr << str2.str();
284
285 output << std::format("p cnf {} {}\neclauses {}\n", num, clause, numvar) << clausstr.str()
286 << clausstr2.str() << '\n';
287 output.flush();
288 }
289
290 // Writes a Bayesian network in the referenced file using the BN format.
291 // If the file doesn't exist, it is created.
292 // If the file exists, it's content will be erased.
293 //
294 // @param filePath The path to the file used to write the Bayesian network.
295 // @param bn The Bayesian network writed in the file.
296 // @throws Raised if an I/O error occurs.
297 template < GUM_Numeric GUM_SCALAR, template < class > class IApproximationPolicy >
299 std::string_view filePath,
300 const IBayesNet< GUM_SCALAR >& bn) {
301 std::ofstream output(std::filesystem::path{filePath}, std::ios_base::trunc);
302 std::ofstream outputvar(std::string{filePath} + ".var", std::ios_base::trunc);
303
304 if (!output.good()) GUM_ERROR(IOError, "Input/Output error : " << filePath << " not writable.")
305
306 std::stringstream strfile, strfile2;
307
308 if (!outputvar.good())
309 GUM_ERROR(IOError,
310 "Input/Output error : " << (std::string(filePath) + ".var") << " not writable.")
311
312 Idx num = 0;
313 Idx numvar = 0;
314 Idx clause = 0;
315 std::stringstream clausstr, clausstr2;
316 gum::HashTable< std::string, Idx > vartable;
317 gum::HashTable< std::string, Idx > protable;
318 gum::HashTable<
319 const gum::DiscreteVariable*,
320 gum::HashTable< std::string, gum::Sequence< gum::Sequence< gum::Instantiation* >* >* >* >
321 cptparamval;
322
323 Instantiation Order;
324
325 for (auto node: bn.topologicalOrder())
326 Order.add(bn.variable(node));
327
328 for (auto node: bn.nodes()) {
329 std::stringstream str0;
330 const DiscreteVariable* var = &bn.variable(node);
331
332 for (Idx i = 0; i < bn.variable(node).domainSize(); i++) {
333 auto stri = std::format("{}_{}", var->name(), var->label(i));
334 vartable.insert(stri, ++num);
335 strfile << std::format("{}::{}\n", num, stri);
336 str0 << std::format("{} ", vartable[stri]);
337 }
338
339 str0 << "0\n";
340 clause++;
341 numvar++;
342 clausstr2 << str0.str();
343 const Tensor< GUM_SCALAR >& cpt = bn.cpt(node);
344 Instantiation inst(cpt);
345 inst.forgetMaster();
346 inst.reorder(Order);
347 cptparamval.insert(
348 var,
349 new gum::HashTable< std::string,
350 gum::Sequence< gum::Sequence< gum::Instantiation* >* >* >());
351
352 for (inst.setFirst(); !inst.end(); ++inst) {
353 if (this->fromExact(cpt[inst]) != 1) {
354 std::string valp = std::format("{}", this->fromExact(cpt[inst]));
355
356 if (!(cptparamval[var])->exists(valp)) {
357 (cptparamval[var])
358 ->insert(
359 valp,
360 new gum::Sequence< gum::Sequence< gum::Instantiation* >* >()); // remember
361 // to
362 // verify
363 // protocole for
364 // param = to 1
365
366 (*(cptparamval[var]))[valp]->insert(new gum::Sequence< gum::Instantiation* >);
367
368 if (this->fromExact(cpt[inst])) {
369 auto strinst = std::format("{}_val={}", var->name(), this->fromExact(cpt[inst]));
370
371 if (!protable.exists(strinst)) {
372 protable.insert(strinst, ++num);
373 strfile << std::format("{}::{}\n", num, strinst);
374 }
375 }
376 }
377
378 (*(cptparamval[var]))[valp]->front()->insert(new gum::Instantiation(inst));
379 }
380 }
381 }
382
383 std::stringstream str2;
384
385 while (!cptparamval.empty()) {
386 auto itvar = cptparamval.begin();
387
388 while (!(itvar.val())->empty()) {
389 auto itpvall = (itvar.val())->begin();
390
391 for (auto pv: *itpvall.val()) {
392 gum::Idx linecount = 0;
393 gum::HashTable< std::string,
394 gum::HashTable< const gum::DiscreteVariable*,
395 std::pair< gum::Set< Idx >*,
396 gum::Set< Idx >* >* >* >
397 orderStruct; // set sizeof Hashtable
398
399 gum::Set< gum::Idx > elimination;
400 gum::HashTable< std::string, gum::Instantiation* > newSeqpre;
401
402 for (const auto* seqv: *pv) {
403 if (seqv->nbrDim() > 1) {
404 for (Idx iInst = 0; iInst < seqv->nbrDim(); iInst++) {
405 auto instpro = new gum::Instantiation(*seqv, false);
406 const gum::DiscreteVariable* var = &(seqv->variable(iInst));
407 instpro->erase(*var);
408 instpro->reorder(Order);
409
410 if (!orderStruct.exists(instpro->toString())
411 && !newSeqpre.exists(instpro->toString())) {
412 orderStruct.insert(
413 instpro->toString(),
414 new gum::HashTable< const gum::DiscreteVariable*,
415 std::pair< gum::Set< Idx >*, gum::Set< Idx >* >* >());
416 }
417
418 if (orderStruct.exists(instpro->toString())
419 && !orderStruct[instpro->toString()]->exists(var)) {
420 orderStruct[instpro->toString()]->insert(
421 var,
422 new std::pair< gum::Set< Idx >*, gum::Set< Idx >* >(
423 new gum::Set< Idx >,
424 new gum::Set< Idx >(
425 seqv->variable(iInst).domainSize()))); // set sizeof Hashtable
426 }
427
428 if (orderStruct.exists(instpro->toString())
429 && !newSeqpre.exists(instpro->toString())) {
430 gum::HashTable< const gum::DiscreteVariable*,
431 std::pair< gum::Set< Idx >*, gum::Set< Idx >* >* >* orderStruct2
432 = orderStruct[instpro->toString()];
433 (*orderStruct2)[var]->first->insert(linecount);
434 (*orderStruct2)[var]->second->insert(seqv->val(iInst));
435
436 if ((*orderStruct2)[var]->second->size() == var->domainSize()) {
437 newSeqpre.insert(instpro->toString(), instpro);
438
439 for (const auto& elt: *orderStruct2) {
440 elimination = elimination + *(elt.second->first);
441 delete (elt.second->first);
442 delete (elt.second->second);
443 delete (elt.second);
444 }
445
446 orderStruct2->clear();
447
448 delete orderStruct2;
449 orderStruct.erase(instpro->toString());
450 } else delete instpro;
451 } else if (newSeqpre.exists(instpro->toString())) {
452 elimination.insert(linecount);
453 delete instpro;
454 }
455 }
456 }
457
458 linecount += 1;
459 }
460
461 gum::Sequence< gum::Instantiation* >* newSeq = nullptr;
462
463 if (!newSeqpre.empty()) {
464 newSeq = new gum::Sequence< gum::Instantiation* >();
465
466 for (auto& elt: newSeqpre)
467 newSeq->insert(elt.second);
468
469 (itpvall.val())->insert(newSeq);
470
471 for (Idx itelem = pv->size(); itelem > 0; itelem--) {
472 if (elimination.exists(itelem - 1)) {
473 delete ((*pv)[itelem - 1]);
474 pv->erase((*pv)[itelem - 1]);
475 }
476 }
477 }
478
479 while (!orderStruct.empty()) {
480 while (!(orderStruct.begin().val())->empty()) {
481 delete orderStruct.begin().val()->begin().val()->first;
482 delete orderStruct.begin().val()->begin().val()->second;
483 (orderStruct.begin().val())->erase(orderStruct.begin().val()->beginSafe()); // safe
484 // iterator
485 // needed here
486 }
487
488 delete orderStruct.begin().val();
489 orderStruct.erase(orderStruct.beginSafe()); // safe iterator needed here
490 }
491 }
492
493 while (!(itpvall.val())->empty()) {
494 gum::Sequence< gum::Sequence< gum::Instantiation* >* >::iterator_safe itpv
495 = (itpvall.val())->begin();
496
497 while (!(*itpv)->empty()) {
499
500 for (Idx i = 0; i < (*itseqv)->nbrDim(); i++) {
501 auto str = std::format("{}_{}",
502 (*itseqv)->variable(i).name(),
503 (*itseqv)->val((*itseqv)->variable(i)));
504 str2 << std::format("-{} ", vartable[str]);
505 }
506
507 /*if (itpvall.key().compare("0") != 0 &&
508 itpvall.key().compare("0.0") != 0) { */
509 if (itpvall.key() != "0" && itpvall.key() != "0.0") {
510 auto strinst = std::format("{}_val={}", itvar.key()->name(), itpvall.key());
511 str2 << protable[strinst];
512 }
513
514 str2 << " 0\n";
515 clause++;
516 delete (*itseqv);
517 (*itpv)->erase((*itpv)->beginSafe()); // safe iterator needed here
518 }
519
520 delete (*itpv);
521 (itpvall.val())->erase(itpvall.val()->beginSafe()); // safe iterator needed here
522 }
523
524 delete (itpvall.val());
525 (itvar.val())->erase(itvar.val()->beginSafe()); // safe iterator needed here
526 }
527
528 delete (itvar.val());
529 cptparamval.erase(cptparamval.beginSafe()); // safe iterator needed here
530 }
531
532 clausstr << str2.str();
533
534 output << std::format("p cnf {} {}\neclauses {}\n", num, clause, numvar) << clausstr.str()
535 << clausstr2.str() << '\n';
536 output.flush();
537 outputvar << strfile.str();
538 outputvar.flush();
539
540 outputvar.close();
541 output.close();
542
543 if (outputvar.fail()) GUM_ERROR(IOError, "Writing in the ostream failed.")
544
545 if (output.fail()) GUM_ERROR(IOError, "Writing in the ostream failed.")
546 }
547
548} /* namespace gum */
549
550#endif // DOXYGEN_SHOULD_SKIP_THIS
Definition of classe for BN file output manipulation.
<agrum/BN/io/cnf/ContextualDependenciesCNFWriter.h>
void _doWrite(std::ostream &output, const IBayesNet< GUM_SCALAR > &bn) final
Writes a Bayesian network in the output stream using the BN format.
Base class for discrete random variable.
virtual Size domainSize() const =0
The class for generic Hash Tables.
Definition hashTable.h:640
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
bool empty() const noexcept
Indicates whether the hash table is empty.
bool exists(const Key &key) const
Checks whether there exists an element with a given key in the hashtable.
Class representing the minimal interface for Bayesian network with no numerical data.
Definition IBayesNet.h:75
Class for assigning/browsing values to tuples of discrete variables.
void add(const DiscreteVariable &v) final
Adds a new variable in the Instantiation.
void insert(const Key &k)
Insert an element at the end of the sequence.
const Key & back() const
Returns the last element of the sequence.
The generic class for storing (ordered) sequences of objects.
Definition sequence.h:994
SequenceIteratorSafe< Key > iterator_safe
Types for STL compliance.
Definition sequence.h:1007
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:504
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:510
aGrUM's Tensor is a multi-dimensional array with tensor operators.
Definition tensor.h:85
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
Size Idx
Type for indexes.
Definition types.h:79
gum is the global namespace for all aGrUM entities
Definition agrum.h:46