aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
IBNLearner.cpp
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
50
51#include <algorithm>
52#include <iterator>
53
54#include <agrum/agrum.h>
55
62
63// include the inlined functions if necessary
64#ifdef GUM_NO_INLINE
66#endif /* GUM_NO_INLINE */
67
68namespace gum::learning {
70 // get the variables names
71 const auto& var_names = _database_.variableNames();
72 const std::size_t nb_vars = var_names.size();
73 for (auto dom: _database_.domainSizes())
74 _domain_sizes_.push_back(dom);
75 for (std::size_t i = 0; i < nb_vars; ++i) {
76 _nodeId2cols_.insert(NodeId(i), i);
77 }
78
79 // create the parser
81 }
82
83 IBNLearner::Database::Database(std::string_view filename,
84 const std::vector< std::string >& missing_symbols,
85 const bool induceTypes) :
86 Database(IBNLearner::readFile_(filename, missing_symbols)) {
87 // if the usr wants the best translators to be inferred, just do it
88 if (induceTypes) {
89 for (const auto& [first, second]: _database_.betterTranslators()) {
90 // change the translator
91 _database_.changeTranslator(*second, first);
92 // recompute the domain size
93 _domain_sizes_[first] = second->domainSize();
94 }
95 }
96 }
97
98 IBNLearner::Database::Database(std::string_view CSV_filename,
99 const Database& score_database,
100 const std::vector< std::string >& missing_symbols) {
101 // assign to each column name in the CSV file its column
102 IBNLearner::isCSVFileName_(CSV_filename);
103 DBInitializerFromCSV initializer{std::string(CSV_filename)};
104 const auto& prior_names = initializer.variableNames();
105 std::size_t prior_nb_vars = prior_names.size();
106 HashTable< std::string, std::size_t > prior_names2col(prior_nb_vars);
107 for (auto i = std::size_t(0); i < prior_nb_vars; ++i)
108 prior_names2col.insert(prior_names[i], i);
109
110 // check that there are at least as many variables in the a priori
111 // database as those in the score_database
112 if (prior_nb_vars < score_database._database_.nbVariables()) {
114 "the a prior database has fewer variables "
115 "than the observed database")
116 }
117
118 // get the mapping from the columns of score_database to those of
119 // the CSV file
120 const std::vector< std::string >& score_names = score_database.databaseTable().variableNames();
121 const std::size_t score_nb_vars = score_names.size();
122 HashTable< std::size_t, std::size_t > mapping(score_nb_vars);
123 for (auto i = std::size_t(0); i < score_nb_vars; ++i) {
124 try {
125 mapping.insert(i, prior_names2col[score_names[i]]);
126 } catch (Exception const&) {
128 "Variable " << score_names[i]
129 << " of the observed database does not belong to the "
130 << "prior database")
131 }
132 }
133
134 // create the translators for CSV database
135 for (auto i = std::size_t(0); i < score_nb_vars; ++i) {
136 const Variable& var = score_database.databaseTable().variable(i);
137 _database_.insertTranslator(var, mapping[i], missing_symbols);
138 }
139
140 // fill the database
141 initializer.fillDatabase(_database_);
142
143 // get the domain sizes of the variables
144 for (auto dom: _database_.domainSizes())
145 _domain_sizes_.push_back(dom);
146
147 // compute the mapping from node ids to column indices
148 _nodeId2cols_ = score_database.nodeId2Columns();
149
150 // create the parser
152 }
153
157 // create the parser
159 }
160
162 _database_(std::move(from._database_)), _domain_sizes_(std::move(from._domain_sizes_)),
163 _nodeId2cols_(std::move(from._nodeId2cols_)) {
164 // create the parser
166 }
167
169
171 if (this != &from) {
172 delete _parser_;
173 _database_ = from._database_;
176
177 // create the parser
179 }
180
181 return *this;
182 }
183
185 if (this != &from) {
186 delete _parser_;
187 _database_ = std::move(from._database_);
188 _domain_sizes_ = std::move(from._domain_sizes_);
189 _nodeId2cols_ = std::move(from._nodeId2cols_);
190
191 // create the parser
193 }
194
195 return *this;
196 }
197
198 // ===========================================================================
199
200 IBNLearner::IBNLearner(std::string_view filename,
201 const std::vector< std::string >& missing_symbols,
202 const bool induceTypes) :
203 inducedTypes_(induceTypes), scoreDatabase_(filename, missing_symbols, induceTypes),
204 filename_(filename) {
205 noPrior_ = new NoPrior(scoreDatabase_.databaseTable());
206
207 GUM_CONSTRUCTOR(IBNLearner)
208 }
209
211 noPrior_ = new NoPrior(scoreDatabase_.databaseTable());
212 GUM_CONSTRUCTOR(IBNLearner)
213 }
214
236
248 selectedAlgo_(from.selectedAlgo_), algoK2_(std::move(from.algoK2_)),
249 algoSimpleMiic_(std::move(from.algoSimpleMiic_)), algoMiic_(std::move(from.algoMiic_)),
250 kmodeMiic_(from.kmodeMiic_), dag2BN_(std::move(from.dag2BN_)),
253 scoreDatabase_(std::move(from.scoreDatabase_)), ranges_(std::move(from.ranges_)),
254 priorDbname_(std::move(from.priorDbname_)), initialDag_(std::move(from.initialDag_)),
255 filename_(std::move(from.filename_)),
257 noPrior_ = new NoPrior(scoreDatabase_.databaseTable());
258
259 GUM_CONS_MOV(IBNLearner)
260 }
261
263 if (score_) delete score_;
264
265 if (prior_) delete prior_;
266
267 if (noPrior_) delete noPrior_;
268
269 if (priorDatabase_) delete priorDatabase_;
270
271 if (mutualInfo_) delete mutualInfo_;
272
273 if (indepTestPC_) delete indepTestPC_;
274
275 if (indepTestFCI_) delete indepTestFCI_;
276
277 GUM_DESTRUCTOR(IBNLearner)
278 }
279
281 if (this != &from) {
282 if (score_) {
283 delete score_;
284 score_ = nullptr;
285 }
286
287 if (prior_) {
288 delete prior_;
289 prior_ = nullptr;
290 }
291
292 if (priorDatabase_) {
293 delete priorDatabase_;
294 priorDatabase_ = nullptr;
295 }
296
297 if (mutualInfo_) {
298 delete mutualInfo_;
299 mutualInfo_ = nullptr;
300 }
301
303 scoreType_ = from.scoreType_;
305 useEM_ = from.useEM_;
306 noiseEM_ = from.noiseEM_;
307 priorType_ = from.priorType_;
317 algoK2_ = from.algoK2_;
319 algoMiic_ = from.algoMiic_;
320 kmodeMiic_ = from.kmodeMiic_;
321 dag2BN_ = from.dag2BN_;
325 ranges_ = from.ranges_;
328 filename_ = from.filename_;
330 currentAlgorithm_ = nullptr;
331 }
332
333 return *this;
334 }
335
337 if (this != &from) {
338 if (score_) {
339 delete score_;
340 score_ = nullptr;
341 }
342
343 if (prior_) {
344 delete prior_;
345 prior_ = nullptr;
346 }
347
348 if (priorDatabase_) {
349 delete priorDatabase_;
350 priorDatabase_ = nullptr;
351 }
352
353 if (mutualInfo_) {
354 delete mutualInfo_;
355 mutualInfo_ = nullptr;
356 }
357
358 ThreadNumberManager::operator=(std::move(from));
359 scoreType_ = from.scoreType_;
360 paramEstimatorType_ = from.paramEstimatorType_;
361 useEM_ = from.useEM_;
362 noiseEM_ = from.noiseEM_;
363 priorType_ = from.priorType_;
364 priorWeight_ = from.priorWeight_;
365 constraintSliceOrder_ = std::move(from.constraintSliceOrder_);
366 constraintIndegree_ = std::move(from.constraintIndegree_);
367 constraintTabuList_ = std::move(from.constraintTabuList_);
368 constraintForbiddenArcs_ = std::move(from.constraintForbiddenArcs_);
369 constraintNoParentNodes_ = std::move(from.constraintNoParentNodes_);
370 constraintNoChildrenNodes_ = std::move(from.constraintNoChildrenNodes_);
371 constraintMandatoryArcs_ = std::move(from.constraintMandatoryArcs_);
372 selectedAlgo_ = from.selectedAlgo_;
373 algoK2_ = from.algoK2_;
374 algoSimpleMiic_ = std::move(from.algoSimpleMiic_);
375 algoMiic_ = std::move(from.algoMiic_);
376 kmodeMiic_ = from.kmodeMiic_;
377 dag2BN_ = std::move(from.dag2BN_);
378 greedyHillClimbing_ = std::move(from.greedyHillClimbing_);
379 localSearchWithTabuList_ = std::move(from.localSearchWithTabuList_);
380 scoreDatabase_ = std::move(from.scoreDatabase_);
381 ranges_ = std::move(from.ranges_);
382 priorDbname_ = std::move(from.priorDbname_);
383 filename_ = std::move(from.filename_);
384 initialDag_ = std::move(from.initialDag_);
385 nbDecreasingChanges_ = std::move(from.nbDecreasingChanges_);
386 currentAlgorithm_ = nullptr;
387 }
388
389 return *this;
390 }
391
392 DatabaseTable readFile(const std::string& filename) {
393 // get the extension of the file
394 if (auto filename_size = Size(filename.size()); filename_size < 4) {
396 "IBNLearner could not determine the "
397 "file type of the database '"
398 << filename << "'")
399 }
400
401 std::string extension = filename.substr(filename.size() - 4);
402 std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
403
404 if (extension != ".csv") {
406 "IBNLearner does not support yet this type ('" << extension
407 << "')"
408 "of database file")
409 }
410
411 DBInitializerFromCSV initializer(filename);
412
413 const auto& var_names = initializer.variableNames();
414 const std::size_t nb_vars = var_names.size();
415
416 DBTranslatorSet translator_set;
418 for (std::size_t i = 0; i < nb_vars; ++i) {
419 translator_set.insertTranslator(translator, i);
420 }
421
422 DatabaseTable database(translator_set);
423 database.setVariableNames(initializer.variableNames());
424 initializer.fillDatabase(database);
425
426 return database;
427 }
428
429 void IBNLearner::isCSVFileName_(std::string_view filename) {
430 // get the extension of the file
431
432 if (auto filename_size = Size(filename.size()); filename_size < 4) {
434 "IBNLearner could not determine the "
435 "file type of the database")
436 }
437
438 std::string extension(filename.substr(filename.size() - 4));
439 std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
440
441 if (extension != ".csv") {
442 GUM_ERROR(OperationNotAllowed, "IBNLearner does not support yet this type of database file")
443 }
444 }
445
446 DatabaseTable IBNLearner::readFile_(std::string_view filename,
447 const std::vector< std::string >& missing_symbols) {
448 // get the extension of the file
449 isCSVFileName_(filename);
450
451 DBInitializerFromCSV initializer{std::string(filename)};
452
453 const auto& var_names = initializer.variableNames();
454 const std::size_t nb_vars = var_names.size();
455
456 DBTranslatorSet translator_set;
457 DBTranslator4LabelizedVariable translator(missing_symbols);
458 for (std::size_t i = 0; i < nb_vars; ++i) {
459 translator_set.insertTranslator(translator, i);
460 }
461
462 DatabaseTable database(missing_symbols, translator_set);
463 database.setVariableNames(initializer.variableNames());
464 initializer.fillDatabase(database);
465
466 database.reorder();
467
468 return database;
469 }
470
472 // first, save the old score, to be delete if everything is ok
473 Score* old_score = score_;
474
475 // create the new scoring function
476 switch (scoreType_) {
477 case ScoreType::AIC :
478 score_ = new ScoreAIC(scoreDatabase_.parser(),
479 *prior_,
480 ranges_,
481 scoreDatabase_.nodeId2Columns());
482 break;
483
484 case ScoreType::BD :
485 score_ = new ScoreBD(scoreDatabase_.parser(),
486 *prior_,
487 ranges_,
488 scoreDatabase_.nodeId2Columns());
489 break;
490
491 case ScoreType::BDeu :
492 score_ = new ScoreBDeu(scoreDatabase_.parser(),
493 *prior_,
494 ranges_,
495 scoreDatabase_.nodeId2Columns());
496 break;
497
498 case ScoreType::BIC :
499 score_ = new ScoreBIC(scoreDatabase_.parser(),
500 *prior_,
501 ranges_,
502 scoreDatabase_.nodeId2Columns());
503 break;
504
505 case ScoreType::fNML :
506 score_ = new ScorefNML(scoreDatabase_.parser(),
507 *prior_,
508 ranges_,
509 scoreDatabase_.nodeId2Columns());
510 break;
511
512 case ScoreType::K2 :
513 score_ = new ScoreK2(scoreDatabase_.parser(),
514 *prior_,
515 ranges_,
516 scoreDatabase_.nodeId2Columns());
517 break;
518
521 *prior_,
522 ranges_,
523 scoreDatabase_.nodeId2Columns());
524 break;
525
526 case ScoreType::MDL :
527 score_ = new ScoreMDL(scoreDatabase_.parser(),
528 *prior_,
529 ranges_,
530 scoreDatabase_.nodeId2Columns());
531 break;
532
533 default : GUM_ERROR(OperationNotAllowed, "IBNLearner does not support yet this score")
534 }
535
536 // remove the old score, if any
537 if (old_score != nullptr) delete old_score;
538
539 // assign the number of threads
540 score_->setNumberOfThreads(this->isGumNumberOfThreadsOverriden() ? this->getNumberOfThreads()
541 : 0);
542 }
543
545 bool take_into_account_score) {
546 ParamEstimator* param_estimator = nullptr;
547
548 // create the new estimator
549 switch (paramEstimatorType_) {
551 if (take_into_account_score && (score_ != nullptr)) {
552 param_estimator = new ParamEstimatorML(parser,
553 *prior_,
554 score_->internalPrior(),
555 ranges_,
556 scoreDatabase_.nodeId2Columns());
557 } else {
558 param_estimator = new ParamEstimatorML(parser,
559 *prior_,
560 *noPrior_,
561 ranges_,
562 scoreDatabase_.nodeId2Columns());
563 }
564
565 break;
566
567 default :
569 "IBNLearner does not support " << "yet this parameter estimator")
570 }
571
572 // assign the number of threads
573 param_estimator->setNumberOfThreads(
575
576 // assign the set of ranges
577 param_estimator->setRanges(ranges_);
578
579 return param_estimator;
580 }
581
582 /* /// prepares the initial graph for miic
583 MixedGraph IBNLearner::prepareSimpleMiic_() {
584 // Initialize the mixed graph to the fully connected graph
585 MixedGraph mgraph;
586 for (Size i = 0; i < scoreDatabase_.databaseTable().nbVariables(); ++i) {
587 mgraph.addNodeWithId(i);
588 for (Size j = 0; j < i; ++j) {
589 mgraph.addEdge(j, i);
590 }
591 }
592
593 // translating the constraints for miic
594 HashTable< std::pair< NodeId, NodeId >, char > initial_marks;
595 for (const auto& arc: constraintMandatoryArcs_.arcs()) {
596 initial_marks.insert({arc.tail(), arc.head()}, '>');
597 }
598
599 for (const auto& arc: constraintForbiddenArcs_.arcs()) {
600 initial_marks.insert({arc.tail(), arc.head()}, '-');
601 }
602 algoSimpleMiic_.addConstraints(initial_marks);
603
604 // create the mutual entropy object
605 createCorrectedMutualInformation_();
606
607 return mgraph;
608 }*/
609
610 // prepares the initial graph for constraintMiic
612 // Initialize the mixed graph to the fully connected graph
613 MixedGraph mgraph;
614 DiGraph forbiddenGraph;
615 DAG mandatoryGraph;
616
617 // GUM_CHECKPOINT
618 for (Size i = 0; i < scoreDatabase_.databaseTable().nbVariables(); ++i) {
619 mgraph.addNodeWithId(i);
620 forbiddenGraph.addNodeWithId(i);
621 mandatoryGraph.addNodeWithId(i);
622 }
623
624 const EdgeSet& possible_edges = constraintPossibleEdges_.edges();
625
626 if (possible_edges.empty()) {
627 for (const NodeId i: mgraph.nodes()) {
628 for (NodeId j = 0; j < i; ++j) {
629 // contiguous nodeIds !
630 mgraph.addEdge(j, i);
631 }
632 }
633 } else {
634 for (const auto& edge: possible_edges) {
635 mgraph.addEdge(edge.first(), edge.second());
636 }
637 }
638 // GUM_CHECKPOINT
639
640 // translating the mandatory arcs for constraintMiic
641 HashTable< std::pair< NodeId, NodeId >, char > initial_marks;
642 const ArcSet& mandatory_arcs = constraintMandatoryArcs_.arcs();
643
644 // GUM_CHECKPOINT
645 for (const auto& arc: mandatory_arcs) {
646 mandatoryGraph.addArc(arc.tail(), arc.head());
647 // MIIC's marks mechanism (orientationMiic_) handles the reverse direction;
648 // adding it to forbiddenGraph would cause DuplicateElement (unlike preparePC_).
649 }
650
651 // GUM_CHECKPOINT
652 // translating the forbidden arcs for constraintMiic
653 const ArcSet& forbidden_arcs = constraintForbiddenArcs_.arcs();
654 for (const auto& arc: forbidden_arcs) {
655 forbiddenGraph.addArc(arc.tail(), arc.head());
656 }
657
658 // GUM_CHECKPOINT
659 const gum::NodeProperty< gum::Size > sliceOrder = constraintSliceOrder_.sliceOrder();
660 gum::NodeProperty< gum::Size > copyOrder = gum::HashTable(sliceOrder);
661 for (const auto& [n1, r1]: sliceOrder) {
662 for (const auto& [n2, r2]: copyOrder) {
663 if (r1 > r2) {
664 forbiddenGraph.addArc(n1, n2);
665 // initial_marks.insert({n1, n2}, '-');
666 } else if (r2 > r1) {
667 forbiddenGraph.addArc(n2, n1);
668 // initial_marks.insert({n2, n1}, '-');
669 }
670 }
671 copyOrder.erase(n1);
672 }
673
674 // GUM_CHECKPOINT
675 const auto& totalOrder = constraintTotalOrder_.totalOrder();
676 for (auto iter1 = totalOrder.begin(); iter1 != totalOrder.end(); ++iter1) {
677 const auto node1 = *iter1;
678 for (auto iter2 = iter1 + 1; iter2 != totalOrder.end(); ++iter2) {
679 forbiddenGraph.addArc(*iter2, node1);
680 }
681 }
682
683 for (const auto node: constraintNoParentNodes_.nodes()) {
684 for (const auto node2: mgraph.nodes()) {
685 if (node != node2) { forbiddenGraph.addArc(node2, node); }
686 }
687 }
688
689 for (const auto node: constraintNoChildrenNodes_.nodes()) {
690 for (const auto node2: mgraph.nodes()) {
691 if (node != node2) { forbiddenGraph.addArc(node, node2); }
692 }
693 }
694
695 // GUM_CHECKPOINT
696 algoMiic_.setMaxIndegree(constraintIndegree_.maxIndegree());
697 algoMiic_.addConstraints(initial_marks);
698 algoMiic_.setMandatoryGraph(mandatoryGraph);
699 algoMiic_.setForbiddenGraph(forbiddenGraph);
700
701 // GUM_CHECKPOINT
702 // create the mutual entropy object
703 // if ( _mutual_info_ == nullptr) { this->useNMLCorrection(); }
705
706 // GUM_CHECKPOINT
707 return mgraph;
708 }
709
711 MixedGraph mgraph;
712 DiGraph forbiddenGraph;
713 DAG mandatoryGraph;
714
715 for (Size i = 0; i < scoreDatabase_.databaseTable().nbVariables(); ++i) {
716 mgraph.addNodeWithId(i);
717 forbiddenGraph.addNodeWithId(i);
718 mandatoryGraph.addNodeWithId(i);
719 }
720
721 const EdgeSet& possible_edges = constraintPossibleEdges_.edges();
722 if (possible_edges.empty()) {
723 for (const NodeId i: mgraph.nodes()) {
724 for (NodeId j = 0; j < i; ++j) {
725 mgraph.addEdge(j, i);
726 }
727 }
728 } else {
729 for (const auto& edge: possible_edges) {
730 mgraph.addEdge(edge.first(), edge.second());
731 }
732 }
733
734 for (const auto& arc: constraintMandatoryArcs_.arcs()) {
735 mandatoryGraph.addArc(arc.tail(), arc.head());
736 forbiddenGraph.addArc(arc.head(), arc.tail());
737 }
738 for (const auto& arc: constraintForbiddenArcs_.arcs()) {
739 forbiddenGraph.addArc(arc.tail(), arc.head());
740 }
741
742 const gum::NodeProperty< gum::Size > sliceOrder = constraintSliceOrder_.sliceOrder();
743 gum::NodeProperty< gum::Size > copyOrder = gum::HashTable(sliceOrder);
744 for (const auto& [n1, r1]: sliceOrder) {
745 for (const auto& [n2, r2]: copyOrder) {
746 if (r1 > r2) {
747 forbiddenGraph.addArc(n1, n2);
748 } else if (r2 > r1) {
749 forbiddenGraph.addArc(n2, n1);
750 }
751 }
752 copyOrder.erase(n1);
753 }
754
755 const auto& totalOrder = constraintTotalOrder_.totalOrder();
756 for (auto iter1 = totalOrder.begin(); iter1 != totalOrder.end(); ++iter1) {
757 for (auto iter2 = iter1 + 1; iter2 != totalOrder.end(); ++iter2) {
758 forbiddenGraph.addArc(*iter2, *iter1);
759 }
760 }
761
762 for (const auto node: constraintNoParentNodes_.nodes()) {
763 for (const auto node2: mgraph.nodes()) {
764 if (node != node2) { forbiddenGraph.addArc(node2, node); }
765 }
766 }
767 for (const auto node: constraintNoChildrenNodes_.nodes()) {
768 for (const auto node2: mgraph.nodes()) {
769 if (node != node2) { forbiddenGraph.addArc(node, node2); }
770 }
771 }
772
773 // build the independence test (owned by IBNLearner)
774 if (indepTestPC_) {
775 delete indepTestPC_;
776 indepTestPC_ = nullptr;
777 }
780 *noPrior_,
781 ranges_,
782 scoreDatabase_.nodeId2Columns());
783 } else {
785 *noPrior_,
786 ranges_,
787 scoreDatabase_.nodeId2Columns());
788 }
789
790 algoPC_.setMaxIndegree(constraintIndegree_.maxIndegree());
791 algoPC_.setMandatoryGraph(mandatoryGraph);
792 algoPC_.setForbiddenGraph(forbiddenGraph);
793 algoPC_.setIndependenceTest(*indepTestPC_);
794 algoPC_.setAlpha(alphaPc_);
795 algoPC_.setStable(stablePc_);
796 algoPC_.setMaxCondSetSize(maxCondSetSizePc_);
799
800 return mgraph;
801 }
802
804 MixedGraph mgraph;
805 DiGraph forbiddenGraph;
806 DAG mandatoryGraph;
807
808 for (Size i = 0; i < scoreDatabase_.databaseTable().nbVariables(); ++i) {
809 mgraph.addNodeWithId(i);
810 forbiddenGraph.addNodeWithId(i);
811 mandatoryGraph.addNodeWithId(i);
812 }
813
814 const EdgeSet& possible_edges = constraintPossibleEdges_.edges();
815 if (possible_edges.empty()) {
816 for (const NodeId i: mgraph.nodes()) {
817 for (NodeId j = 0; j < i; ++j) {
818 mgraph.addEdge(j, i);
819 }
820 }
821 } else {
822 for (const auto& edge: possible_edges) {
823 mgraph.addEdge(edge.first(), edge.second());
824 }
825 }
826
827 for (const auto& arc: constraintMandatoryArcs_.arcs()) {
828 mandatoryGraph.addArc(arc.tail(), arc.head());
829 forbiddenGraph.addArc(arc.head(), arc.tail());
830 }
831 for (const auto& arc: constraintForbiddenArcs_.arcs()) {
832 forbiddenGraph.addArc(arc.tail(), arc.head());
833 }
834
835 const gum::NodeProperty< gum::Size > sliceOrder = constraintSliceOrder_.sliceOrder();
836 gum::NodeProperty< gum::Size > copyOrder = gum::HashTable(sliceOrder);
837 for (const auto& [n1, r1]: sliceOrder) {
838 for (const auto& [n2, r2]: copyOrder) {
839 if (r1 > r2) {
840 forbiddenGraph.addArc(n1, n2);
841 } else if (r2 > r1) {
842 forbiddenGraph.addArc(n2, n1);
843 }
844 }
845 copyOrder.erase(n1);
846 }
847
848 const auto& totalOrder = constraintTotalOrder_.totalOrder();
849 for (auto iter1 = totalOrder.begin(); iter1 != totalOrder.end(); ++iter1) {
850 for (auto iter2 = iter1 + 1; iter2 != totalOrder.end(); ++iter2) {
851 forbiddenGraph.addArc(*iter2, *iter1);
852 }
853 }
854
855 for (const auto node: constraintNoParentNodes_.nodes()) {
856 for (const auto node2: mgraph.nodes()) {
857 if (node != node2) { forbiddenGraph.addArc(node2, node); }
858 }
859 }
860 for (const auto node: constraintNoChildrenNodes_.nodes()) {
861 for (const auto node2: mgraph.nodes()) {
862 if (node != node2) { forbiddenGraph.addArc(node, node2); }
863 }
864 }
865
866 if (indepTestFCI_) {
867 delete indepTestFCI_;
868 indepTestFCI_ = nullptr;
869 }
872 *noPrior_,
873 ranges_,
874 scoreDatabase_.nodeId2Columns());
875 } else {
877 *noPrior_,
878 ranges_,
879 scoreDatabase_.nodeId2Columns());
880 }
881
882 algoFCI_.setMaxIndegree(constraintIndegree_.maxIndegree());
883 algoFCI_.setMandatoryGraph(mandatoryGraph);
884 algoFCI_.setForbiddenGraph(forbiddenGraph);
885 algoFCI_.setIndependenceTest(*indepTestFCI_);
886 algoFCI_.setAlpha(alphaFci_);
887 algoFCI_.setMaxPathLength(maxPathLengthFci_);
888 algoFCI_.setExhaustiveSepSet(exhaustiveSepSetFci_);
889
890 return mgraph;
891 }
892
897 "Score-based algorithms do not build PDAG. Please use a constraint-based "
898 "algorithm instead")
899 }
900 // check that the database does not contain any missing value
901 if (scoreDatabase_.databaseTable().hasMissingValues()) {
903 "For the moment, the BNLearner is unable to learn "
904 << "structures with missing values in databases")
905 }
906
908 BNLearnerListener listener(this, algoPC_);
909 MixedGraph mgraph = this->preparePC_();
910 return algoPC_.learnPDAG(mgraph);
911 }
912
914 BNLearnerListener listener(this, algoFCI_);
915 MixedGraph mgraph = this->prepareFCI_();
916 return algoFCI_.learnPDAG(mgraph);
917 }
918
919 BNLearnerListener listener(this, algoMiic_);
920 // create the mixedGraph_constraint_MandatoryArcs.arcs
921 MixedGraph mgraph = this->prepareMiic_();
922 algoMiic_.setMutualInformation(*mutualInfo_);
923 return algoMiic_.learnPDAG(mgraph);
924 }
925
927 auto pdag = learnPDAG_();
928 for (const auto i: pdag) {
929 pdag.setName(i, scoreDatabase_.databaseTable().variableName(i));
930 }
931 return pdag;
932 }
933
936 GUM_ERROR(OperationNotAllowed, "learnPAG() is only valid when using the FCI algorithm")
937 }
938 if (scoreDatabase_.databaseTable().hasMissingValues()) {
940 "For the moment, the BNLearner is unable to learn "
941 << "structures with missing values in databases")
942 }
943 BNLearnerListener listener(this, algoFCI_);
944 MixedGraph mgraph = this->prepareFCI_();
945 return algoFCI_.learnPAG(mgraph);
946 }
947
949 auto pag = learnPAG_();
950 for (const auto i: pag) {
951 pag.setName(i, scoreDatabase_.databaseTable().variableName(i));
952 }
953 return pag;
954 }
955
957 createPrior_();
958 createScore_();
959 auto dag = learnDag_();
960 for (const auto i: dag) {
961 dag.setName(i, scoreDatabase_.databaseTable().variableName(i));
962 }
963 return dag;
964 }
965
967 if (mutualInfo_ != nullptr) delete mutualInfo_;
968
970 *noPrior_,
971 ranges_,
972 scoreDatabase_.nodeId2Columns());
973 switch (kmodeMiic_) {
975 case MDL : mutualInfo_->useMDL(); break;
976 case NML : mutualInfo_->useNML(); break;
977 case NoCorr : mutualInfo_->useNoCorr(); break;
978 default :
980 "The BNLearner's corrected mutual information class does "
981 << "not implement yet this correction : " << int(kmodeMiic_))
982 }
983 }
984
986 // check that the database does not contain any missing value
987 if (scoreDatabase_.databaseTable().hasMissingValues()
988 || ((priorDatabase_ != nullptr)
990 && priorDatabase_->databaseTable().hasMissingValues())) {
992 "For the moment, the BNLearner is unable to cope "
993 "with missing values in databases")
994 }
995 // add the mandatory arcs to the initial dag and remove the forbidden ones
996 // from the initial graph
997 DAG init_graph = initialDag_;
998
999 for (const auto& arc: constraintMandatoryArcs_.arcs()) {
1000 if (!init_graph.exists(arc.tail())) init_graph.addNodeWithId(arc.tail());
1001
1002 if (!init_graph.exists(arc.head())) init_graph.addNodeWithId(arc.head());
1003
1004 init_graph.addArc(arc.tail(), arc.head());
1005 }
1006
1007 for (const auto& arc: constraintForbiddenArcs_.arcs())
1008 init_graph.eraseArc(arc);
1009
1010
1011 switch (selectedAlgo_) {
1012 // ========================================================================
1013 case AlgoType::PC : {
1014 BNLearnerListener listener(this, algoPC_);
1015 MixedGraph mgraph = this->preparePC_();
1016 return algoPC_.learnDAG(mgraph);
1017 }
1018
1019 // ========================================================================
1020 case AlgoType::FCI : {
1021 BNLearnerListener listener(this, algoFCI_);
1022 MixedGraph mgraph = this->prepareFCI_();
1023 return algoFCI_.learnDAG(mgraph);
1024 }
1025
1026 // ========================================================================
1027 case AlgoType::MIIC : {
1028 BNLearnerListener listener(this, algoMiic_);
1029 // create the mixedGraph and the corrected mutual information
1030 MixedGraph mgraph = this->prepareMiic_();
1031
1032 algoMiic_.setMutualInformation(*mutualInfo_);
1033 return algoMiic_.learnDAG(mgraph);
1034 }
1035
1036 // ========================================================================
1038 BNLearnerListener listener(this, greedyHillClimbing_);
1046 invariable_constraints;
1047 static_cast< StructuralConstraintMandatoryArcs& >(invariable_constraints)
1049 static_cast< StructuralConstraintForbiddenArcs& >(invariable_constraints)
1051 static_cast< StructuralConstraintPossibleEdges& >(invariable_constraints)
1053 static_cast< StructuralConstraintSliceOrder& >(invariable_constraints)
1055 static_cast< StructuralConstraintNoParentNodes& >(invariable_constraints)
1057 static_cast< StructuralConstraintNoChildrenNodes& >(invariable_constraints)
1059 static_cast< StructuralConstraintTotalOrder& >(invariable_constraints)
1061
1063 variable_constraints;
1064 static_cast< StructuralConstraintIndegree& >(variable_constraints) = constraintIndegree_;
1065
1067 invariable_constraints,
1068 variable_constraints);
1069
1070 // enforce that greedy hill climbing uses arc additions, deletions and reversals
1071 // and only these operations
1072 selector.useArcAdditions(true);
1073 selector.useArcDeletions(true);
1074 selector.useArcReversals(true);
1075 selector.useArcTriangleDeletions(false);
1076
1077 return greedyHillClimbing_.learnStructure(selector, init_graph);
1078 }
1079
1080 // ========================================================================
1090 invariable_constraints;
1091 static_cast< StructuralConstraintMandatoryArcs& >(invariable_constraints)
1093 static_cast< StructuralConstraintForbiddenArcs& >(invariable_constraints)
1095 static_cast< StructuralConstraintPossibleEdges& >(invariable_constraints)
1097 static_cast< StructuralConstraintSliceOrder& >(invariable_constraints)
1099 static_cast< StructuralConstraintNoParentNodes& >(invariable_constraints)
1101 static_cast< StructuralConstraintNoChildrenNodes& >(invariable_constraints)
1103 static_cast< StructuralConstraintTotalOrder& >(invariable_constraints)
1105
1107 variable_constraints;
1108 static_cast< StructuralConstraintIndegree& >(variable_constraints) = constraintIndegree_;
1109
1111 invariable_constraints,
1112 variable_constraints);
1113
1118
1119 return greedyHillClimbing_.learnStructure(selector, init_graph);
1120 }
1121
1122 // ========================================================================
1132 invariable_constraints;
1133 static_cast< StructuralConstraintMandatoryArcs& >(invariable_constraints)
1135 static_cast< StructuralConstraintForbiddenArcs& >(invariable_constraints)
1137 static_cast< StructuralConstraintPossibleEdges& >(invariable_constraints)
1139 static_cast< StructuralConstraintSliceOrder& >(invariable_constraints)
1141 static_cast< StructuralConstraintNoParentNodes& >(invariable_constraints)
1143 static_cast< StructuralConstraintNoChildrenNodes& >(invariable_constraints)
1145 static_cast< StructuralConstraintTotalOrder& >(invariable_constraints)
1147
1149 variable_constraints;
1150 static_cast< StructuralConstraintIndegree& >(variable_constraints) = constraintIndegree_;
1151
1153 invariable_constraints,
1154 variable_constraints);
1155
1156 return greedyThickThinning_.learnStructure(selector, init_graph);
1157 }
1158
1159 // ========================================================================
1169 invariable_constraints;
1170 static_cast< StructuralConstraintMandatoryArcs& >(invariable_constraints)
1172 static_cast< StructuralConstraintForbiddenArcs& >(invariable_constraints)
1174 static_cast< StructuralConstraintPossibleEdges& >(invariable_constraints)
1176 static_cast< StructuralConstraintSliceOrder& >(invariable_constraints)
1178 static_cast< StructuralConstraintNoParentNodes& >(invariable_constraints)
1180 static_cast< StructuralConstraintNoChildrenNodes& >(invariable_constraints)
1182 static_cast< StructuralConstraintTotalOrder& >(invariable_constraints)
1184
1188 variable_constraints;
1189 static_cast< StructuralConstraintTabuList& >(variable_constraints) = constraintTabuList_;
1190 static_cast< StructuralConstraintIndegree& >(variable_constraints) = constraintIndegree_;
1191
1193 invariable_constraints,
1194 variable_constraints);
1195
1200
1201 return localSearchWithTabuList_.learnStructure(selector, init_graph);
1202 }
1203
1204 // ========================================================================
1205 case AlgoType::K2 : {
1206 BNLearnerListener listener(this, algoK2_.approximationScheme());
1213 invariable_constraints;
1214 static_cast< StructuralConstraintMandatoryArcs& >(invariable_constraints)
1216 static_cast< StructuralConstraintForbiddenArcs& >(invariable_constraints)
1218 static_cast< StructuralConstraintPossibleEdges& >(invariable_constraints)
1220 static_cast< StructuralConstraintNoParentNodes& >(invariable_constraints)
1222 static_cast< StructuralConstraintNoChildrenNodes& >(invariable_constraints)
1224 static_cast< StructuralConstraintTotalOrder& >(invariable_constraints)
1226
1227 // if some mandatory arcs are incompatible with the order, use a DAG
1228 // constraint instead of a DiGraph constraint to avoid cycles
1229 const ArcSet& mandatory_arcs
1230 = static_cast< StructuralConstraintMandatoryArcs& >(invariable_constraints).arcs();
1231 const Sequence< NodeId >& order = algoK2_.order();
1232 bool order_compatible = true;
1233
1234 for (const auto& arc: mandatory_arcs) {
1235 if (order.pos(arc.tail()) >= order.pos(arc.head())) {
1236 order_compatible = false;
1237 break;
1238 }
1239 }
1240
1241 if (order_compatible) {
1243 variable_constraints;
1244 static_cast< StructuralConstraintIndegree& >(variable_constraints) = constraintIndegree_;
1245
1247 invariable_constraints,
1248 variable_constraints);
1249
1250 return algoK2_.learnStructure(selector, init_graph);
1251 } else {
1253 variable_constraints;
1254 static_cast< StructuralConstraintIndegree& >(variable_constraints) = constraintIndegree_;
1255
1257 invariable_constraints,
1258 variable_constraints);
1259
1260 return algoK2_.learnStructure(selector, init_graph);
1261 }
1262 }
1263 }
1264
1266 "the learnDAG method has not been implemented for this "
1267 "learning algorithm")
1268 }
1269
1271 if (this->isConstraintBased()) return "";
1272
1273 const auto prior = getPriorType_();
1274
1275 switch (scoreType_) {
1277 case AIC : return ScoreAIC::isPriorCompatible(prior, priorWeight_);
1278
1279 case BD : return ScoreBD::isPriorCompatible(prior, priorWeight_);
1280
1281 case BDeu : return ScoreBDeu::isPriorCompatible(prior, priorWeight_);
1282
1283 case BIC : return ScoreBIC::isPriorCompatible(prior, priorWeight_);
1284
1285 case fNML : return ScorefNML::isPriorCompatible(prior, priorWeight_);
1286
1287 case K2 : return ScoreK2::isPriorCompatible(prior, priorWeight_);
1288
1290
1291 case MDL : return ScoreMDL::isPriorCompatible(prior, priorWeight_);
1292
1293 default : return "IBNLearner does not support yet this score";
1294 }
1295 }
1296
1298 std::pair< std::size_t, std::size_t >
1299 IBNLearner::useCrossValidationFold(const std::size_t learning_fold,
1300 const std::size_t k_fold) {
1301 if (k_fold == 0) { GUM_ERROR(OutOfBounds, "K-fold cross validation with k=0 is forbidden") }
1302
1303 if (learning_fold >= k_fold) {
1305 "In " << k_fold << "-fold cross validation, the learning "
1306 << "fold should be strictly lower than " << k_fold
1307 << " but, here, it is equal to " << learning_fold)
1308 }
1309
1310 const std::size_t db_size = scoreDatabase_.databaseTable().nbRows();
1311 if (k_fold >= db_size) {
1313 "In " << k_fold << "-fold cross validation, the database's "
1314 << "size should be strictly greater than " << k_fold
1315 << " but, here, the database has only " << db_size << "rows")
1316 }
1317
1318 // create the ranges of rows of the test database
1319 const std::size_t foldSize = db_size / k_fold;
1320 const std::size_t unfold_deb = learning_fold * foldSize;
1321 const std::size_t unfold_end = unfold_deb + foldSize;
1322
1323 ranges_.clear();
1324 if (learning_fold == std::size_t(0)) {
1325 ranges_.push_back(std::pair< std::size_t, std::size_t >(unfold_end, db_size));
1326 } else {
1327 ranges_.push_back(std::pair< std::size_t, std::size_t >(std::size_t(0), unfold_deb));
1328
1329 if (learning_fold != k_fold - 1) {
1330 ranges_.push_back(std::pair< std::size_t, std::size_t >(unfold_end, db_size));
1331 }
1332 }
1333
1334 return std::pair< std::size_t, std::size_t >(unfold_deb, unfold_end);
1335 }
1336
1337 std::pair< double, double >
1338 IBNLearner::chi2(const NodeId id1, const NodeId id2, const std::vector< NodeId >& knowing) {
1339 createPrior_();
1341
1342 return chi2score.statistics(id1, id2, knowing);
1343 }
1344
1345 std::pair< double, double > IBNLearner::chi2(std::string_view name1,
1346 std::string_view name2,
1347 const std::vector< std::string >& knowing) {
1348 std::vector< NodeId > knowingIds;
1349 std::transform(knowing.begin(),
1350 knowing.end(),
1351 std::back_inserter(knowingIds),
1352 [this](const std::string& c) { return this->idFromName(c); });
1353 return chi2(idFromName(name1), idFromName(name2), knowingIds);
1354 }
1355
1356 std::pair< double, double >
1357 IBNLearner::G2(const NodeId id1, const NodeId id2, const std::vector< NodeId >& knowing) {
1358 createPrior_();
1360 return g2score.statistics(id1, id2, knowing);
1361 }
1362
1363 std::pair< double, double > IBNLearner::G2(std::string_view name1,
1364 std::string_view name2,
1365 const std::vector< std::string >& knowing) {
1366 std::vector< NodeId > knowingIds;
1367 std::transform(knowing.begin(),
1368 knowing.end(),
1369 std::back_inserter(knowingIds),
1370 [this](const std::string& c) { return this->idFromName(c); });
1371 return G2(idFromName(name1), idFromName(name2), knowingIds);
1372 }
1373
1374 double IBNLearner::logLikelihood(const std::vector< NodeId >& vars,
1375 const std::vector< NodeId >& knowing) {
1376 createPrior_();
1378
1379 std::vector< NodeId > total(vars);
1380 total.insert(total.end(), knowing.begin(), knowing.end());
1381 double LLtotal = ll2score.score(IdCondSet(total, false, true));
1382 if (knowing.size() == (Size)0) {
1383 return LLtotal;
1384 } else {
1385 double LLknw = ll2score.score(IdCondSet(knowing, false, true));
1386 return LLtotal - LLknw;
1387 }
1388 }
1389
1390 double IBNLearner::logLikelihood(const std::vector< std::string >& vars,
1391 const std::vector< std::string >& knowing) {
1392 std::vector< NodeId > ids;
1393 std::vector< NodeId > knowingIds;
1394
1395 auto mapper = [this](const std::string& c) { return this->idFromName(c); };
1396
1397 std::transform(vars.begin(), vars.end(), std::back_inserter(ids), mapper);
1398 std::transform(knowing.begin(), knowing.end(), std::back_inserter(knowingIds), mapper);
1399
1400 return logLikelihood(ids, knowingIds);
1401 }
1402
1404 const NodeId id2,
1405 const std::vector< NodeId >& knowing) {
1406 createPrior_();
1408 *prior_,
1409 databaseRanges());
1410
1411 switch (kmodeMiic_) {
1413 case MDL : cmi.useMDL(); break;
1414
1415 case NML : cmi.useNML(); break;
1416
1417 case NoCorr : cmi.useNoCorr(); break;
1418
1419 default :
1421 "The BNLearner's corrected mutual information class does "
1422 << "not implement yet this correction : " << int(kmodeMiic_))
1423 }
1424 if (knowing.size() == (Size)0) return cmi.score(id1, id2) / scoreDatabase_.weight();
1425 else return cmi.score(id1, id2, knowing) / scoreDatabase_.weight();
1426 }
1427
1428 double IBNLearner::correctedMutualInformation(std::string_view var1,
1429 std::string_view var2,
1430 const std::vector< std::string >& knowing) {
1431 std::vector< NodeId > knowingIds;
1432
1433 auto mapper = [this](const std::string& c) { return this->idFromName(c); };
1434
1435 std::transform(knowing.begin(), knowing.end(), std::back_inserter(knowingIds), mapper);
1436
1437 return correctedMutualInformation(this->idFromName(var1), this->idFromName(var2), knowingIds);
1438 }
1439
1441 const NodeId id2,
1442 const std::vector< NodeId >& knowing) {
1443 const auto prior = NoPrior(scoreDatabase_.databaseTable(), scoreDatabase_.nodeId2Columns());
1445 cmi.useNoCorr();
1446
1447 if (knowing.size() == (Size)0) return cmi.score(id1, id2) / scoreDatabase_.weight();
1448 else return cmi.score(id1, id2, knowing) / scoreDatabase_.weight();
1449 }
1450
1451 double IBNLearner::mutualInformation(std::string_view var1,
1452 std::string_view var2,
1453 const std::vector< std::string >& knowing) {
1454 std::vector< NodeId > knowingIds;
1455
1456 auto mapper = [this](const std::string& c) { return this->idFromName(c); };
1457
1458 std::transform(knowing.begin(), knowing.end(), std::back_inserter(knowingIds), mapper);
1459
1460 return mutualInformation(this->idFromName(var1), this->idFromName(var2), knowingIds);
1461 }
1462
1463 double IBNLearner::score(const NodeId var, const std::vector< NodeId >& knowing) {
1464 createPrior_();
1465 createScore_();
1466
1467 return score_->score(var, knowing);
1468 }
1469
1470 double IBNLearner::score(std::string_view var, const std::vector< std::string >& knowing) {
1471 auto mapper = [this](const std::string& c) { return this->idFromName(c); };
1472
1473 const NodeId id = this->idFromName(var);
1474 std::vector< NodeId > knowingIds;
1475 knowingIds.reserve(knowing.size());
1476 std::transform(knowing.begin(), knowing.end(), std::back_inserter(knowingIds), mapper);
1477
1478 return score(id, knowingIds);
1479 }
1480
1481 std::vector< double > IBNLearner::rawPseudoCount(const std::vector< NodeId >& vars) {
1482 if (this->hasMissingValues()) {
1484 "BNLearner cannot compute pseudo-counts with missing values in the database")
1485 }
1486 if (vars.empty()) {
1487 GUM_ERROR(OutOfBounds, "BNLearner::rawPseudoCount called with an empty vector of variables")
1488 }
1489 Tensor< double > res;
1490
1491 createPrior_();
1493 return count.get(vars);
1494 }
1495
1496 std::vector< double > IBNLearner::rawPseudoCount(const std::vector< std::string >& vars) {
1497 std::vector< NodeId > ids;
1498
1499 auto mapper = [this](const std::string& c) { return this->idFromName(c); };
1500
1501 std::transform(vars.begin(), vars.end(), std::back_inserter(ids), mapper);
1502 return rawPseudoCount(ids);
1503 }
1504
1507 const std::vector< std::pair< std::size_t, std::size_t > >& new_ranges) {
1508 // use a score to detect whether the ranges are ok
1510 score.setRanges(new_ranges);
1511 ranges_ = score.ranges();
1512 }
1513} // namespace gum::learning
A listener that allows BNLearner to be used as a proxy for its inner algorithms.
A class for generic framework of learning algorithms that can easily be used.
A pack of learning algorithms that can easily be used.
virtual void eraseArc(const Arc &arc)
removes an arc from the ArcGraphPart
Base class for dag.
Definition DAG.h:121
void addArc(NodeId tail, NodeId head) final
insert a new arc into the directed graph
Definition DAG_inl.h:75
Base class for all oriented graphs.
Definition diGraph.h:132
void addArc(const NodeId tail, const NodeId head) override
insert a new arc into the directed graph
Definition diGraph_inl.h:59
Base class for all aGrUM's exceptions.
Definition exceptions.h:122
Exception : a I/O format was not found.
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.
Exception: at least one argument passed to a function is not what was expected.
Error: The database contains some missing values.
Error: A name of variable is not found in the database.
Base class for mixed graphs.
Definition mixedGraph.h:146
const NodeGraphPart & nodes() const
return *this as a NodeGraphPart
bool exists(const NodeId id) const
alias for existsNode
virtual void addNodeWithId(const NodeId id)
try to insert a node with the given id
Exception : there is something wrong with an implementation.
Exception : operation not allowed.
Exception : out of bound.
Partial Ancestral Graph: undirected topology with endpoint marks.
Definition PAG.h:90
Base class for partially directed acyclic graphs.
Definition PDAG.h:130
bool empty() const noexcept
Indicates whether the set is the empty set.
Definition set_tpl.h:613
aGrUM's Tensor is a multi-dimensional array with tensor operators.
Definition tensor.h:85
bool isGumNumberOfThreadsOverriden() const override
indicates whether the class containing this ThreadNumberManager set its own number of threads
Size getNumberOfThreads() const override
returns the current max number of threads used by the class containing this ThreadNumberManager
ThreadNumberManager(Size nb_threads=0)
default constructor
ThreadNumberManager & operator=(const ThreadNumberManager &from)
copy operator
void addEdge(NodeId first, NodeId second) override
insert a new edge into the undirected graph
Base class for every random variable.
Definition variable.h:81
A class that redirects gum_signal from algorithms to the listeners of BNLearn.
@ Sorted
descending p-value order (strongest evidence first)
@ Standard
process triples in natural traversal order
The class computing n times the corrected mutual information, as used in the MIIC algorithm.
KModeTypes
the description type for the complexity correction
double score(NodeId var1, NodeId var2)
returns the 2-point mutual information corresponding to a given nodeset
void useNML()
use the kNML penalty function
void useNoCorr()
use no correction/penalty function
void useMDL()
use the MDL penalty function
The class for initializing DatabaseTable and RawDatabaseTable instances from CSV files.
the class used to read a row in the database and to transform it into a set of DBRow instances that c...
The class used to pack sets of generators.
The databases' cell translators for labelized variables.
the class for packing together the translators used to preprocess the datasets
std::size_t insertTranslator(const DBTranslator &translator, const std::size_t column, const bool unique_column=true)
inserts a new translator at the end of the translator set
The class representing a tabular database as used by learning tasks.
void setVariableNames(const std::vector< std::string > &names, const bool from_external_object=true) override
sets the names of the variables
const Variable & variable(const std::size_t k, const bool k_is_input_col=false) const
returns either the kth variable of the database table or the first one corresponding to the kth colum...
The mecanism to compute the next available graph changes for directed structure learning search algor...
void useArcAdditions(bool use)
sets whether or not the selector allows the application of arc additions
void useArcDeletions(bool use)
sets whether or not the selector allows the application of arc deletions
void useArcReversals(bool use)
sets whether or not the selector allows the application of arc reversals
void useArcTriangleDeletions(bool use)
sets whether or not the selector allows the application of arc triangle deletions
a helper to easily read databases
Definition IBNLearner.h:138
const DatabaseTable & databaseTable() const
returns the internal database table
std::vector< std::size_t > _domain_sizes_
the domain sizes of the variables (useful to speed-up computations)
Definition IBNLearner.h:280
DatabaseTable _database_
the database itself
Definition IBNLearner.h:274
Bijection< NodeId, std::size_t > _nodeId2cols_
a bijection assigning to each variable name its NodeId
Definition IBNLearner.h:283
Database(std::string_view file, const std::vector< std::string > &missing_symbols, const bool induceTypes=false)
default constructor
const Bijection< NodeId, std::size_t > & nodeId2Columns() const
returns the mapping between node ids and their columns in the database
Database & operator=(const Database &from)
copy operator
DBRowGeneratorParser * _parser_
the parser used for reading the database
Definition IBNLearner.h:277
StructuralConstraintPossibleEdges constraintPossibleEdges_
the constraint on possible Edges
MixedGraph preparePC_()
prepares the initial graph and independence test for PC
StructuralConstraintNoParentNodes constraintNoParentNodes_
the constraint on no parent nodes
BNLearnerPriorType priorType_
the a priorselected for the score and parameters
PAG learnPAG()
learn a PAG — only valid when useFCI() has been called
std::string priorDbname_
the filename for the Dirichlet a priori, if any
double priorWeight_
the weight of the prior
double noiseEM_
the noise factor (in (0,1)) used by EM for perturbing the CPT during init
std::vector< std::pair< std::size_t, std::size_t > > ranges_
the set of rows' ranges within the database in which learning is done
~IBNLearner() override
destructor
std::string checkScorePriorCompatibility() const
checks whether the current score and prior are compatible
GreedyHillClimbing extendedGreedyHillClimbing_
the extended greedy hill climbing
bool allowArcTriangleDeletions_
whether we allow or not arc deletions during learning
virtual void createPrior_()=0
create the prior used for learning
K2 algoK2_
the K2 algorithm
IndepTestType indepTestTypeFCI_
independence test type for FCI (reuses IndepTestType defined above)
AlgoType selectedAlgo_
the selected learning algorithm
const std::vector< std::pair< std::size_t, std::size_t > > & databaseRanges() const
returns the current database rows' ranges used for learning
bool allowArcAdditions_
whether we allow or not arc additions during learning
double logLikelihood(const std::vector< NodeId > &vars, const std::vector< NodeId > &knowing={})
Return the loglikelihood of vars in the base, conditioned by knowing for the BNLearner.
Database scoreDatabase_
the database to be used by the scores and parameter estimators
ScoreType
an enumeration enabling to select easily the score we wish to use
Definition IBNLearner.h:107
bool useEM_
a Boolean indicating whether we should use EM for parameter learning or not
DAG2BNLearner dag2BN_
the parametric EM
Prior * prior_
the prior used
void useDatabaseRanges(const std::vector< std::pair< std::size_t, std::size_t > > &new_ranges)
use a new set of database rows' ranges to perform learning
std::pair< double, double > chi2(NodeId id1, NodeId id2, const std::vector< NodeId > &knowing={})
Return the <statistic,pvalue> pair for chi2 test in the database.
CorrectedMutualInformation * mutualInfo_
the selected correction for miic
StructuralConstraintNoChildrenNodes constraintNoChildrenNodes_
the constraint on no children nodes
static void isCSVFileName_(std::string_view filename)
checks whether the extension of a CSV filename is correct
gum::learning::FCI algoFCI_
the FCI algorithm
ParamEstimatorType paramEstimatorType_
the type of the parameter estimator
ScoreType scoreType_
the score selected for learning
std::pair< double, double > G2(NodeId id1, NodeId id2, const std::vector< NodeId > &knowing={})
Return the <statistic,pvalue> pair for for G2 test in the database.
const ApproximationScheme * currentAlgorithm_
IndependenceTest * indepTestPC_
owned independence test object for PC (rebuilt before each learn call)
DAG learnDag_()
returns the DAG learnt
Database * priorDatabase_
the database used by the Dirichlet a priori
double mutualInformation(NodeId id1, NodeId id2, const std::vector< NodeId > &knowing={})
Return the mutual information of id1 and id2 in the base, conditioned by knowing for the BNLearner.
void createScore_()
create the score used for learning
PriorType getPriorType_() const
returns the type (as a string) of a given prior
double alphaFci_
FCI parameters.
double alphaPc_
PC parameters.
StructuralConstraintIndegree constraintIndegree_
the constraint for indegrees
bool allowArcDeletions_
whether we allow or not arc deletions during learning
PDAG learnPDAG()
learn a partial structure from a file (must have read the db before and must have selected miic)
static DatabaseTable readFile_(std::string_view filename, const std::vector< std::string > &missing_symbols)
reads a file and returns a databaseVectInRam
std::string filename_
the filename database
bool hasMissingValues() const
returns true if the learner's database has missing values
NodeId idFromName(std::string_view var_name) const
returns the node id corresponding to a variable name
SimpleMiic algoSimpleMiic_
the MIIC algorithm
Score * score_
the score used
StructuralConstraintMandatoryArcs constraintMandatoryArcs_
the constraint on mandatory arcs
Miic algoMiic_
the Constraint MIIC algorithm
void createCorrectedMutualInformation_()
create the Corrected Mutual Information instance for Miic
IndepTestType indepTestTypePC_
StructuralConstraintForbiddenArcs constraintForbiddenArcs_
the constraint on forbidden arcs
StructuralConstraintTotalOrder constraintTotalOrder_
the total order ing constraint
GreedyHillClimbing greedyHillClimbing_
the greedy hill climbing algorithm
DAG learnDAG()
learn a structure from a file (must have read the db before)
double score(NodeId vars, const std::vector< NodeId > &knowing={})
Return the value of the score currently in use by the BNLearner of a variable given a set of other va...
StructuralConstraintTabuList constraintTabuList_
the constraint for tabu lists
DAG initialDag_
an initial DAG given to learners
GreedyThickThinning greedyThickThinning_
the greedy thick-thinning algorithm
MixedGraph prepareFCI_()
prepares the initial graph and independence test for FCI
IBNLearner & operator=(const IBNLearner &)
copy operator
gum::learning::PC algoPC_
the PC algorithm
bool allowArcReversals_
whether we allow or not arc reversals during learning
MixedGraph prepareMiic_()
prepares the initial graph for miic
IBNLearner(std::string_view filename, const std::vector< std::string > &missingSymbols, bool induceTypes=true)
read the database file for the score / parameter estimation and var names
LocalSearchWithTabuList localSearchWithTabuList_
the local search with tabu list algorithm
IndependenceTest * indepTestFCI_
owned independence test object for FCI (rebuilt before each learn call)
std::pair< std::size_t, std::size_t > useCrossValidationFold(const std::size_t learning_fold, const std::size_t k_fold)
sets the ranges of rows to be used for cross-validation learning
ParamEstimator * createParamEstimator_(const DBRowGeneratorParser &parser, bool take_into_account_score=true)
create the parameter estimator used for learning
StructuralConstraintSliceOrder constraintSliceOrder_
the constraint for 2TBNs
const DatabaseTable & database() const
returns the database used by the BNLearner
std::vector< double > rawPseudoCount(const std::vector< NodeId > &vars)
Return the pseudo-counts of NodeIds vars in the base in a raw array.
bool inducedTypes_
the policy for typing variables
CorrectedMutualInformation::KModeTypes kmodeMiic_
the penalty used in MIIC
double correctedMutualInformation(NodeId id1, NodeId id2, const std::vector< NodeId > &knowing={})
Return the mutual information of id1 and id2 in the base, conditioned by knowing for the BNLearner.
bool isConstraintBased() const
indicate if the selected algorithm is constraint-based
void fillDatabase(DATABASE &database, const bool retry_insertion=false)
fills the rows of the database table
const std::vector< std::string > & variableNames()
returns the names of the variables in the input dataset
const DBVector< std::string > & variableNames() const noexcept
returns the variable names for all the columns of the database
std::size_t nbVariables() const noexcept
returns the number of variables (columns) of the database
A class for storing a pair of sets of NodeIds, the second one corresponding to a conditional set.
Definition idCondSet.h:214
the class for computing Chi2 independence test scores
std::pair< double, double > statistics(NodeId var1, NodeId var2, const std::vector< NodeId > &rhs_ids={}) override
get the pair <chi2 statistic,pvalue> for a test var1 indep var2 given rhs_ids
the class for computing G2 independence test scores
Definition indepTestG2.h:66
std::pair< double, double > statistics(NodeId var1, NodeId var2, const std::vector< NodeId > &rhs_ids={}) override
get the pair <G2statistic,pvalue> for a test var1 indep var2 given rhs_ids
The K2 algorithm.
Definition K2.h:63
the no a priorclass: corresponds to 0 weight-sample
Definition noPrior.h:65
The class for estimating parameters of CPTs using Maximum Likelihood.
The base class for estimating parameters of CPTs.
void setRanges(const std::vector< std::pair< std::size_t, std::size_t > > &new_ranges)
sets new ranges to perform the counts used by the parameter estimator
void setNumberOfThreads(Size nb) override
sets the number max of threads that can be used
The class for giving access to pseudo count : count in the database + prior.
Definition pseudoCount.h:67
std::vector< double > get(const std::vector< NodeId > &ids)
returns the pseudo-count of a pair of nodes given some other nodes
the class for computing AIC scores
Definition scoreAIC.h:70
std::string isPriorCompatible() const final
indicates whether the prior is compatible (meaningful) with the score
the class for computing Bayesian Dirichlet (BD) log2 scores
Definition scoreBD.h:83
std::string isPriorCompatible() const final
indicates whether the prior is compatible (meaningful) with the score
the class for computing BDeu scores
Definition scoreBDeu.h:77
std::string isPriorCompatible() const final
indicates whether the prior is compatible (meaningful) with the score
the class for computing BIC scores
Definition scoreBIC.h:70
std::string isPriorCompatible() const final
indicates whether the prior is compatible (meaningful) with the score
the class for computing K2 scores (actually their log2 value)
Definition scoreK2.h:79
std::string isPriorCompatible() const final
indicates whether the prior is compatible (meaningful) with the score
the class for computing Log2-likelihood scores
std::string isPriorCompatible() const final
indicates whether the prior is compatible (meaningful) with the score
double score(const IdCondSet &idset)
returns the score for a given IdCondSet
The base class for all the scores used for learning (BIC, BDeu, etc).
Definition score.h:68
the class for computing fNML scores
Definition scorefNML.h:72
std::string isPriorCompatible() const final
indicates whether the prior is compatible (meaningful) with the score
The base class for structural constraints imposed by DAGs.
the structural constraint for forbidding the creation of some arcs during structure learning
the class for structural constraints limiting the number of parents of nodes in a directed graph
the structural constraint indicating that some arcs shall never be removed or reversed
the structural constraint for forbidding children for some nodes
the structural constraint for forbidding parents for some nodes
the structural constraint for forbidding the creation of some arcs except those defined in the class ...
the "meta-programming" class for storing structural constraints
the structural constraint imposing a partial order over nodes
The class imposing a N-sized tabu list as a structural constraints for learning algorithms.
the structural constraint imposing a total order over some nodes
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Set< Edge > EdgeSet
Some typdefs and define for shortcuts ...
Size NodeId
Type for node ids.
Set< Arc > ArcSet
Some typdefs and define for shortcuts ...
HashTable< NodeId, VAL > NodeProperty
Property on graph elements.
the class for computing Chi2 scores
the class for computing G2 scores
include the inlined functions if necessary
Definition CSVParser.h:55
ScoreBIC ScoreMDL
Definition scoreMDL.h:67
DatabaseTable readFile(const std::string &filename)
STL namespace.
the base class for all the independence tests used for learning
the class for computing Log2-likelihood scores