aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
Miic.cpp
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2025 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2025 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40
41
47
55
57
58namespace gum {
59
60 namespace learning {
61
63 Miic::Miic() : _maxLog_(100), _size_(0) { GUM_CONSTRUCTOR(Miic); }
64
66 Miic::Miic(int maxLog) : _maxLog_(maxLog), _size_(0) { GUM_CONSTRUCTOR(Miic); }
67
69 Miic::Miic(const Miic& from) : ApproximationScheme(from), _size_(from._size_) {
70 GUM_CONS_CPY(Miic);
71 }
72
74 Miic::Miic(Miic&& from) : ApproximationScheme(std::move(from)), _size_(from._size_) {
75 GUM_CONS_MOV(Miic);
76 }
77
79 Miic::~Miic() { GUM_DESTRUCTOR(Miic); }
80
82 Miic& Miic::operator=(const Miic& from) {
83 ApproximationScheme::operator=(from);
84 return *this;
85 }
86
89 ApproximationScheme::operator=(std::move(from));
90 return *this;
91 }
92
93 bool GreaterPairOn2nd::operator()(const CondRanking& e1, const CondRanking& e2) const {
94 return e1.second > e2.second;
95 }
96
97 bool GreaterAbsPairOn2nd::operator()(const Ranking& e1, const Ranking& e2) const {
98 return std::abs(e1.second) > std::abs(e2.second);
99 }
100
102 const ProbabilisticRanking& e2) const {
103 double p1xz = std::get< 2 >(e1);
104 double p1yz = std::get< 3 >(e1);
105 double p2xz = std::get< 2 >(e2);
106 double p2yz = std::get< 3 >(e2);
107 double I1 = std::get< 1 >(e1);
108 double I2 = std::get< 1 >(e2);
109 // First, we look at the sign of information.
110 // Then, the probability values
111 // and finally the abs value of information.
112 if ((I1 < 0 && I2 < 0) || (I1 >= 0 && I2 >= 0)) {
113 if (std::max(p1xz, p1yz) == std::max(p2xz, p2yz)) {
114 return std::abs(I1) > std::abs(I2);
115 } else {
116 return std::max(p1xz, p1yz) > std::max(p2xz, p2yz);
117 }
118 } else {
119 return I1 < I2;
120 }
121 }
122
125 MixedGraph graph) {
126 // GUM_TRACE_VAR(mutualInformation.score(gum::NodeId (5), gum::NodeId (6)) );
127
128 timer_.reset();
129 current_step_ = 0;
130
131 // clear the vector of latent arcs to be sure
132 _latentCouples_.clear();
133
136
138 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > sep_set;
139
140 initiation_(mutualInformation, graph, sep_set, rank);
141
142 iteration_(mutualInformation, graph, sep_set, rank);
143
144 return graph;
145 }
146
148 MixedGraph graph) {
149 // GUM_TRACE_VAR(mutualInformation.score(gum::NodeId (5), gum::NodeId (6)) );
150
151 timer_.reset();
152 current_step_ = 0;
153
154 // clear the vector of latent arcs to be sure
155 _latentCouples_.clear();
156
159
161 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > sep_set;
162
163 initiation_(mutualInformation, graph, sep_set, rank);
164
165 iteration_(mutualInformation, graph, sep_set, rank);
166
167 orientationMiic_(mutualInformation, graph, sep_set);
168 // Propagates existing orientations thanks to Meek rules
169
170 return meekRules_.propagate(graph);
171 // todo : check meekRules_.choices()
172 }
173
177
179 return meekRules_.propagateToCPDAG(learnMixedStructure(I, initialGraph));
180 // @todo: check the meekRules.choices()
181 }
182
184 return meekRules_.propagateToDAG(learnMixedStructure(I, initialGraph));
185 // @todo: check the meekRules.choices()
186 }
187
189 template < typename GUM_SCALAR, typename GRAPH_CHANGES_SELECTOR, typename PARAM_ESTIMATOR >
190 BayesNet< GUM_SCALAR > Miic::learnBN(GRAPH_CHANGES_SELECTOR& selector,
191 PARAM_ESTIMATOR& estimator,
192 DAG initial_dag) {
194 learnStructure(selector, initial_dag));
195 }
196
197 /*
198 * PHASE 1 : INITIATION
199 *
200 * We go over all edges and test if the variables are independent. If they
201 * are,
202 * the edge is deleted. If not, the best contributor is found.
203 */
205 MixedGraph& graph,
206 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet,
208 NodeId x, y;
209 EdgeSet edges = graph.edges();
210 Size steps_init = edges.size();
211
212 for (const Edge& edge: edges) {
213 x = edge.first();
214 y = edge.second();
215 if (isForbiddenEdge_(x, y)) { // Erase Forbidden edges
216 GUM_SL_EMIT(x, y, "Remove " << x << " - " << y, " Constraints : Forbidden edge")
217 graph.eraseEdge(edge);
218 }
219
220 else {
221 double Ixy = mutualInformation.score(x, y);
222
223 if (Ixy <= 0) { //< K
224 graph.eraseEdge(edge);
225 GUM_SL_EMIT(x,
226 y,
227 "Remove " << x << " - " << y,
228 "Independent based on Mutual Information :" << Ixy)
229
230 sepSet.insert(std::make_pair(x, y), _emptySet_);
231 } else {
232 findBestContributor_(x, y, _emptySet_, graph, mutualInformation, rank);
233 GUM_SL_EMIT(x,
234 y,
235 "Keep " << x << " - " << y,
236 "Dependent based on Mutual Information :" << Ixy)
237 }
238
240 if (onProgress.hasListener()) {
241 GUM_EMIT3(onProgress, (current_step_ * 33) / steps_init, 0., timer_.step());
242 }
243 }
244 }
245 }
246
247 /*
248 * PHASE 2 : ITERATION
249 *
250 * As long as we find important nodes for edges, we go over them to see if
251 * we can assess the independence of the variables.
252 */
254 MixedGraph& graph,
255 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet,
257 // if no triples to further examine pass
258 CondRanking best;
259 Size steps_init = current_step_;
260 Size steps_iter = rank.size();
261
262 try {
263 while (rank.top().second > 0.5) {
264 best = rank.pop();
265
266 const NodeId x = std::get< 0 >(*(best.first));
267 const NodeId y = std::get< 1 >(*(best.first));
268 const NodeId z = std::get< 2 >(*(best.first));
269 std::vector< NodeId > ui = std::move(std::get< 3 >(*(best.first)));
270
271 ui.push_back(z);
272 const double i_xy_ui = mutualInformation.score(x, y, ui);
273 if (i_xy_ui < 0) {
274 graph.eraseEdge(Edge(x, y));
275 GUM_SL_EMIT(x,
276 y,
277 "Remove " << x << " - " << y,
278 "Independent based on MutualInformation knowing Sep "
279 << ui << "Mutual information:" << i_xy_ui)
280
281 sepSet.insert(std::make_pair(x, y), std::move(ui));
282 } else {
283 findBestContributor_(x, y, ui, graph, mutualInformation, rank);
284 }
285
286 delete best.first;
287
289 if (onProgress.hasListener()) {
291 (current_step_ * 66) / (steps_init + steps_iter),
292 0.,
293 timer_.step());
294 }
295 }
296 } catch (...) {} // here, rank is empty
297 current_step_ = steps_init + steps_iter;
298 if (onProgress.hasListener()) { GUM_EMIT3(onProgress, 66, 0., timer_.step()); }
299 current_step_ = steps_init + steps_iter;
300 }
301
302 /*
303 * PHASE 3 : ORIENTATION
304 *
305 * Try to assess v-structures and propagate them.
306 */
307
310 CorrectedMutualInformation& mutualInformation,
311 MixedGraph& graph,
312 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet) {
313 // structure to store the orientations marks -, o, or >,
314 // Considers the head of the arc/edge: first node -* second node
316
317 // marks always correspond to the head of the arc/edge. - is for a forbidden
318 // arc, > for a mandatory arc
319 // we start @by adding the mandatory arcs
320
321
322 /* for (auto& iter = mandaArcs.begin(); iter != mandaArcs.end(); ++iter) {
323 // if (graph.existsEdge(iter.value_type().head(), iter.tail()) &&
324 // _isMandatoryArc_(iter.head(), iter.tail())) {
325 // graph.eraseEdge(Edge(iter.head(), iter.tail()));
326 // graph.addArc(iter.head(), iter.tail());
327 // }
328 }*/
329
330 for (auto& arc: _mandatoryGraph_.arcs()) {
331 // Check if the edge exists in graph
332 if (graph.existsEdge(arc.head(), arc.tail())) {
333 graph.eraseEdge(Edge(arc.head(), arc.tail()));
334 GUM_SL_EMIT(arc.tail(),
335 arc.head(),
336 "Add Arc" << arc.tail() << "->" << arc.head(),
337 "Mandatory")
338 graph.addArc(arc.tail(), arc.head());
339 marks.insert({arc.tail(), arc.head()}, '>');
340 marks.insert({arc.head(), arc.tail()}, '-');
341 } else {
342 // If the edge doesn't exist we add the arc as is to graph
343 graph.addArc(arc.tail(), arc.head());
344 marks.insert({arc.tail(), arc.head()}, '>');
345 marks.insert({arc.head(), arc.tail()}, '-');
346 }
347 }
348
349 for (Arc arc: _forbiddenGraph_.arcs()) {
350 // Check if the edge exists in graph
351 if (graph.existsEdge(Edge(arc.tail(), arc.head()))) {
352 graph.eraseEdge(Edge(arc.tail(), arc.head()));
353
354 // Arc is forced in the other direction
355 graph.addArc(arc.head(), arc.tail());
356 GUM_SL_EMIT(arc.head(),
357 arc.tail(),
358 "Add Arc" << arc.head() << "->" << arc.tail(),
359 "Forbidden in the other orientation")
360 marks.insert({arc.tail(), arc.head()}, '-');
361 marks.insert({arc.head(), arc.tail()}, '>');
362 }
363 }
364
365 std::vector< ProbabilisticRanking > proba_triples
366 = unshieldedTriplesMiic_(graph, mutualInformation, sepSet, marks);
367
368 const Size steps_orient = proba_triples.size();
369 Size past_steps = current_step_;
370
372 if (steps_orient > 0) { best = proba_triples[0]; }
373
374 while (!proba_triples.empty() && std::max(std::get< 2 >(best), std::get< 3 >(best)) >= 0.5) {
375 const NodeId x = std::get< 0 >(*std::get< 0 >(best));
376 const NodeId y = std::get< 1 >(*std::get< 0 >(best));
377 const NodeId z = std::get< 2 >(*std::get< 0 >(best));
378
379 const double i3 = std::get< 1 >(best);
380
381 const double p1 = std::get< 2 >(best);
382 const double p2 = std::get< 3 >(best);
383
384 if (i3 <= 0) {
385 _orientingVstructureMiic_(graph, marks, x, y, z, p1, p2);
386 } else {
387 _propagatingOrientationMiic_(graph, marks, x, y, z, p1, p2);
388 }
389
390 delete std::get< 0 >(best);
391 proba_triples.erase(proba_triples.begin());
392 // actualisation of the list of triples
393 proba_triples = updateProbaTriples_(graph, proba_triples);
394
395 if (!proba_triples.empty()) best = proba_triples[0];
396
398 if (onProgress.hasListener()) {
400 (current_step_ * 100) / (steps_orient + past_steps),
401 0.,
402 timer_.step());
403 }
404 } // while
405
406 // erasing the double headed arcs
407 for (auto iter = _latentCouples_.rbegin(); iter != _latentCouples_.rend(); ++iter) {
408 graph.eraseArc(Arc(iter->head(), iter->tail()));
409 if (_existsDirectedPath_(graph, iter->head(), iter->tail())) {
410 // if we find a cycle, we force the competing edge
411 graph.addArc(iter->head(), iter->tail());
412 graph.eraseArc(Arc(iter->tail(), iter->head()));
413 *iter = Arc(iter->head(), iter->tail());
414 }
415 }
416
417 if (onProgress.hasListener()) { GUM_EMIT3(onProgress, 100, 0., timer_.step()); }
418 }
419
421 HashTable< std::pair< NodeId, NodeId >, char >& marks,
422 NodeId x,
423 NodeId y,
424 NodeId z,
425 double p1,
426 double p2) {
427 // v-structure discovery
428 if (marks[{x, z}] == 'o' && marks[{y, z}] == 'o') { // If x-z-y
429 if (!_existsNonTrivialDirectedPath_(graph, z, x)) {
430 if (isArcValid_(graph, x, z)) {
431 graph.eraseEdge(Edge(x, z));
432 graph.addArc(x, z);
433 GUM_SL_EMIT(x, z, "Add Arc " << x << " -> " << z, "V-structure Orientation")
434 // GUM_TRACE("1.a Removing edge (" << x << "," << z << ")")
435 // GUM_TRACE("1.a Adding arc (" << x << "," << z << ")")
436
437 marks[{x, z}] = '>';
438 if (graph.existsArc(z, x) && _isNotLatentCouple_(z, x)) {
439 // GUM_TRACE("Adding latent couple (" << z << "," << x << ")")
440 _latentCouples_.emplace_back(z, x);
441 }
442 if (!_arcProbas_.exists(Arc(x, z))) _arcProbas_.insert(Arc(x, z), p1);
443 }
444
445 } else {
446 graph.eraseEdge(Edge(x, z));
447 // GUM_TRACE("1.b Adding arc (" << x << "," << z << ")")
448 if (!_existsNonTrivialDirectedPath_(graph, x, z)) {
449 if (isArcValid_(graph, z, x)) {
450 graph.addArc(z, x);
451 GUM_SL_EMIT(z, x, "Add Arc " << z << " -> " << x, "V-structure Orientation")
452 // GUM_TRACE("1.b Removing edge (" << x << "," << z << ")")
453 marks[{z, x}] = '>';
454 }
455 }
456 }
457
458 if (!_existsNonTrivialDirectedPath_(graph, z, y)) {
459 if (isArcValid_(graph, y, z)) {
460 graph.eraseEdge(Edge(y, z));
461 graph.addArc(y, z);
462 GUM_SL_EMIT(y, z, "Add Arc " << y << " -> " << z, "V-structure Orientation")
463 // GUM_TRACE("1.c Removing edge (" << y << "," << z << ")")
464 // GUM_TRACE("1.c Adding arc (" << y << "," << z << ")")
465 marks[{y, z}] = '>';
466 if (graph.existsArc(z, y) && _isNotLatentCouple_(z, y)) {
467 _latentCouples_.emplace_back(z, y);
468 }
469 if (!_arcProbas_.exists(Arc(y, z))) _arcProbas_.insert(Arc(y, z), p2);
470 }
471 } else {
472 graph.eraseEdge(Edge(y, z));
473 // GUM_TRACE("1.d Removing edge (" << y << "," << z << ")")
474 if (!_existsNonTrivialDirectedPath_(graph, y, z)) {
475 if (isArcValid_(graph, z, y)) {
476 graph.addArc(z, y);
477 GUM_SL_EMIT(z, y, "Add Arc " << z << " -> " << y, "V-structure Orientation")
478 // GUM_TRACE("1.d Adding arc (" << z << "," << y << ")")
479 marks[{z, y}] = '>';
480 }
481 }
482 }
483 } else if (marks[{x, z}] == '>' && marks[{y, z}] == 'o') { // If x->z-y
484 if (!_existsNonTrivialDirectedPath_(graph, z, y)) {
485 if (isArcValid_(graph, y, z)) {
486 graph.eraseEdge(Edge(y, z));
487 graph.addArc(y, z);
488 GUM_SL_EMIT(y,
489 z,
490 "Add Arc " << y << " -> " << z,
491 "V-structure Orientation | existing "
492 << x << " -> " << z << ", then orienting " << y << " -> " << z)
493 // GUM_TRACE("2.a Removing edge (" << y << "," << z << ")")
494 // GUM_TRACE("2.a Adding arc (" << y << "," << z << ")")
495 marks[{y, z}] = '>';
496 if (graph.existsArc(z, y) && _isNotLatentCouple_(z, y)) {
497 _latentCouples_.emplace_back(z, y);
498 }
499 if (!_arcProbas_.exists(Arc(y, z))) _arcProbas_.insert(Arc(y, z), p2);
500 }
501 } else {
502 graph.eraseEdge(Edge(y, z));
503 // GUM_TRACE("2.b Removing edge (" << y << "," << z << ")")
504 if (!_existsNonTrivialDirectedPath_(graph, y, z)) {
505 if (isArcValid_(graph, z, y)) {
506 graph.addArc(z, y);
507 GUM_SL_EMIT(z,
508 y,
509 "Add Arc " << z << " -> " << y,
510 "V-structure Orientation | existing "
511 << x << " -> " << z << ", then orienting " << z << " -> " << y)
512 // GUM_TRACE("2.b Adding arc (" << y << "," << z << ")")
513 marks[{z, y}] = '>';
514 }
515 }
516 }
517
518 } else if (marks[{y, z}] == '>' && marks[{x, z}] == 'o') { // If y->z-x
519 if (!_existsNonTrivialDirectedPath_(graph, z, x)) {
520 if (isArcValid_(graph, x, z)) {
521 graph.eraseEdge(Edge(x, z));
522 graph.addArc(x, z);
523 GUM_SL_EMIT(x, z, "Add Arc " << x << " -> " << z, "V-structure Orientation")
524 // GUM_TRACE("3.a Removing edge (" << x << "," << z << ")")
525 // GUM_TRACE("3.a Adding arc (" << x << "," << z << ")")
526 marks[{x, z}] = '>';
527 if (graph.existsArc(z, x) && _isNotLatentCouple_(z, x)) {
528 _latentCouples_.emplace_back(z, x);
529 }
530 if (!_arcProbas_.exists(Arc(x, z))) _arcProbas_.insert(Arc(x, z), p1);
531 }
532
533 } else {
534 graph.eraseEdge(Edge(x, z));
535 // GUM_TRACE("3.b Removing edge (" << x << "," << z << ")")
536 if (!_existsNonTrivialDirectedPath_(graph, x, z)) {
537 if (isArcValid_(graph, z, x)) {
538 graph.addArc(z, x);
539 GUM_SL_EMIT(z, x, "Add Arc " << z << " -> " << x, "V-structure Orientation")
540 // GUM_TRACE("3.b Adding arc (" << x << "," << z << ")")
541 marks[{z, x}] = '>';
542 }
543 }
544 }
545 }
546 }
547
549 HashTable< std::pair< NodeId, NodeId >, char >& marks,
550 NodeId x,
551 NodeId y,
552 NodeId z,
553 double p1,
554 double p2) {
555 // orientation propagation
556 if (marks[{x, z}] == '>' && marks[{y, z}] == 'o' && marks[{z, y}] != '-') {
557 graph.eraseEdge(Edge(z, y));
558 if (!_existsDirectedPath_(graph, y, z) && graph.parents(y).empty()) {
559 if (isArcValid_(graph, z, y)) {
560 graph.addArc(z, y);
561 GUM_SL_EMIT(z,
562 y,
563 "Add Arc " << z << " -> " << y,
564 "Propagation MIIC (919) | existing x -> " << z << " and " << z << " - "
565 << y)
566 // GUM_TRACE("4.a Adding arc (" << z << "," << y << ")")
567 marks[{z, y}] = '>';
568 marks[{y, z}] = '-';
569 if (!_arcProbas_.exists(Arc(z, y))) _arcProbas_.insert(Arc(z, y), p2);
570 }
571
572 } else if (!_existsDirectedPath_(graph, z, y) && graph.parents(z).empty()) {
573 if (isArcValid_(graph, y, z)) {
574 graph.addArc(y, z);
575 GUM_SL_EMIT(y, z, "Add Arc " << y << " -> " << z, "Propagation MIIC line 932 ")
576 // GUM_TRACE("4.b Adding arc (" << y << "," << z << ")")
577
578 marks[{z, y}] = '-';
579 marks[{y, z}] = '>';
580 _latentCouples_.emplace_back(y, z);
581 if (!_arcProbas_.exists(Arc(y, z))) _arcProbas_.insert(Arc(y, z), p2);
582 }
583
584 } else if (!_existsDirectedPath_(graph, y, z)) {
585 if (isArcValid_(graph, z, y)) {
586 graph.addArc(z, y);
587 GUM_SL_EMIT(z, y, "Add Arc " << z << "->" << y, "Propagation MIIC 947 ")
588 // GUM_TRACE("4.c Adding arc (" << z << "," << y << ")")
589 marks[{z, y}] = '>';
590 marks[{y, z}] = '-';
591 if (!_arcProbas_.exists(Arc(z, y))) _arcProbas_.insert(Arc(z, y), p2);
592 }
593
594 } else if (!_existsDirectedPath_(graph, z, y)) {
595 if (isArcValid_(graph, y, z)) {
596 graph.addArc(y, z);
597 GUM_SL_EMIT(z, y, "Add Arc " << z << "->" << y, "Propagation MIIC 959")
598 // GUM_TRACE("4.d Adding arc (" << y << "," << z << ")")
599
600 _latentCouples_.emplace_back(y, z);
601 marks[{z, y}] = '-';
602 marks[{y, z}] = '>';
603 if (!_arcProbas_.exists(Arc(y, z))) _arcProbas_.insert(Arc(y, z), p2);
604 }
605 }
606 } else if (marks[{y, z}] == '>' && marks[{x, z}] == 'o' && marks[{z, x}] != '-') {
607 graph.eraseEdge(Edge(z, x));
608 // GUM_TRACE("5. Removing edge (" << z << "," << x << ")")
609 if (!_existsDirectedPath_(graph, x, z) && graph.parents(x).empty()) {
610 if (isArcValid_(graph, z, x)) {
611 graph.addArc(z, x);
612 GUM_SL_EMIT(z, x, "Add Arc " << z << " -> " << x, "Propagation MIIC 977")
613 // GUM_TRACE("5.a Adding arc (" << z << "," << x << ")")
614 marks[{z, x}] = '>';
615 marks[{x, z}] = '-';
616 if (!_arcProbas_.exists(Arc(z, x))) _arcProbas_.insert(Arc(z, x), p1);
617 }
618
619 } else if (!_existsDirectedPath_(graph, z, x) && graph.parents(z).empty()) {
620 if (isArcValid_(graph, x, z)) {
621 graph.addArc(x, z);
622 GUM_SL_EMIT(x, z, "Add Arc " << x << "->" << z, "Propagation MIIC 990")
623 // GUM_TRACE("5.b Adding arc (" << x << "," << z << ")")
624 marks[{z, x}] = '-';
625 marks[{x, z}] = '>';
626 _latentCouples_.emplace_back(x, z);
627 if (!_arcProbas_.exists(Arc(x, z))) _arcProbas_.insert(Arc(x, z), p1);
628 }
629
630 } else if (!_existsDirectedPath_(graph, x, z)) {
631 if (isArcValid_(graph, z, x)) {
632 graph.addArc(z, x);
633 GUM_SL_EMIT(z, x, "Add Arc " << z << " -> " << x, "Propagation MIIC 1004")
634 // GUM_TRACE("5.c Adding arc (" << z << "," << x << ")")
635 marks[{z, x}] = '>';
636 marks[{x, z}] = '-';
637 if (!_arcProbas_.exists(Arc(z, x))) _arcProbas_.insert(Arc(z, x), p1);
638 }
639 } else if (!_existsDirectedPath_(graph, z, x)) {
640 if (isArcValid_(graph, x, z)) {
641 graph.addArc(x, z);
642 GUM_SL_EMIT(x, z, "Add Arc " << x << " -> " << z, "Propagation MIIC 1016")
643 // GUM_TRACE("5.d Adding arc (" << x << "," << z << ")")
644 marks[{z, x}] = '-';
645 marks[{x, z}] = '>';
646 _latentCouples_.emplace_back(x, z);
647 if (!_arcProbas_.exists(Arc(x, z))) _arcProbas_.insert(Arc(x, z), p1);
648 }
649 }
650 }
651 }
652
654 gum::ArcSet L; // create a set of all double-headed arcs
655 for (gum::NodeId x: mg.nodes())
656 for (NodeId y: mg.parents(x))
657 // If there is a mutual parent-child relationship, add the arc to the set
658 if (mg.parents(y).contains(x)) {
659 if (x > y) {
660 continue; // Avoid duplicate arcs by considering only one direction
661 } else {
662 L.insert(gum::Arc(x, y));
663 }
664 }
665
666 // If there are double-headed arcs
667 if (not L.empty()) {
668 while (true) {
669 bool withdrawFlag_L = false;
670 for (auto arc: ArcSet(L)) {
671 bool tail_head = _existsDirectedPath_(mg, arc.tail(), arc.head());
672 bool head_tail = _existsDirectedPath_(mg, arc.head(), arc.tail());
673 bool withdrawFlag_arc = false;
674
675 // Case 1: There is already a path from tail to head and no path from head to tail
676 if (tail_head && !head_tail) {
677 // Erase the arc from head to tail to avoid cycles
678 mg.eraseArc(Arc(arc.head(), arc.tail()));
679 withdrawFlag_arc = true;
680
681 // Case 2: There is already a path from head to tail and no path from tail to head
682 } else if (!tail_head && head_tail) {
683 // Erase the arc from tail to head to avoid cycles
684 mg.eraseArc(Arc(arc.tail(), arc.head()));
685 withdrawFlag_arc = true;
686
687 // Case 3: There is no path between tail and head
688 } else if (!tail_head && !head_tail) {
689 // Choose an arbitrary orientation and erase the corresponding arc
690 mg.eraseArc(Arc(arc.head(), arc.tail()));
691 withdrawFlag_arc = true;
692 }
693
694 // Remove the arc from the set if it was processed
695 if (withdrawFlag_arc) {
696 L.erase(arc);
697 withdrawFlag_L = true;
698 }
699 }
700 // If all double-headed arcs are processed, exit the loop
701 if (L.empty()) { break; }
702
703 // If no arcs were withdrawn, erase an arbitrary double arc in the graph (the first one in
704 // L). Hoping the situation will improve. ┐( ̄ヘ ̄)┌ If we arrive here, it's because the
705 // double-headed arc creates cycles in both directions
706 if (!withdrawFlag_L) {
707 auto it = L.begin();
708 auto arc = *it;
709 mg.eraseArc(Arc(arc.head(), arc.tail()));
710 mg.eraseArc(Arc(arc.tail(), arc.head()));
711 L.erase(arc);
712 }
713 }
714 }
715 }
716
719 NodeId y,
720 const std::vector< NodeId >& ui,
721 const MixedGraph& graph,
722 CorrectedMutualInformation& mutualInformation,
724 double maxP = -1.0;
725 NodeId maxZ = 0;
726
727 // compute N
728 // __N = I.N();
729 const double Ixy_ui = mutualInformation.score(x, y, ui);
730
731 for (const NodeId z: graph) {
732 // if z!=x and z!=y and z not in ui
733 if (z != x && z != y && std::find(ui.begin(), ui.end(), z) == ui.end()) {
734 double Pnv;
735 double Pb;
736
737 // Computing Pnv
738 const double Ixyz_ui = mutualInformation.score(x, y, z, ui);
739 double calc_expo1 = -Ixyz_ui * M_LN2;
740 // if exponential are too high or to low, crop them at _maxLog_
741 if (calc_expo1 > _maxLog_) {
742 Pnv = 0.0;
743 } else if (calc_expo1 < -_maxLog_) {
744 Pnv = 1.0;
745 } else {
746 Pnv = 1 / (1 + std::exp(calc_expo1));
747 }
748
749 // Computing Pb
750 const double Ixz_ui = mutualInformation.score(x, z, ui);
751 const double Iyz_ui = mutualInformation.score(y, z, ui);
752
753 calc_expo1 = -(Ixz_ui - Ixy_ui) * M_LN2;
754 double calc_expo2 = -(Iyz_ui - Ixy_ui) * M_LN2;
755
756 // if exponential are too high or to low, crop them at _maxLog_
757 if (calc_expo1 > _maxLog_ || calc_expo2 > _maxLog_) {
758 Pb = 0.0;
759 } else if (calc_expo1 < -_maxLog_ && calc_expo2 < -_maxLog_) {
760 Pb = 1.0;
761 } else {
762 double expo1, expo2;
763 if (calc_expo1 < -_maxLog_) {
764 expo1 = 0.0;
765 } else {
766 expo1 = std::exp(calc_expo1);
767 }
768 if (calc_expo2 < -_maxLog_) {
769 expo2 = 0.0;
770 } else {
771 expo2 = std::exp(calc_expo2);
772 }
773 Pb = 1 / (1 + expo1 + expo2);
774 }
775
776 // Getting max(min(Pnv, pb))
777 const double min_pnv_pb = std::min(Pnv, Pb);
778 if (min_pnv_pb > maxP) {
779 maxP = min_pnv_pb;
780 maxZ = z;
781 }
782 } // if z not in (x, y)
783 } // for z in graph.nodes
784 // storing best z in rank_
785 CondRanking final;
786 auto tup = new CondThreePoints{x, y, maxZ, ui};
787 final.first = tup;
788 final.second = maxP;
789 rank.insert(final);
790 }
791
794 std::vector< Ranking > Miic::unshieldedTriples_(
795 const MixedGraph& graph,
796 CorrectedMutualInformation& mutualInformation,
797 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet) {
798 std::vector< Ranking > triples;
799 for (NodeId z: graph) {
800 for (NodeId x: graph.neighbours(z)) {
801 for (NodeId y: graph.neighbours(z)) {
802 if (y < x && !graph.existsEdge(x, y)) {
803 std::vector< NodeId > ui;
804 std::pair< NodeId, NodeId > key = {x, y};
805 std::pair< NodeId, NodeId > rev_key = {y, x};
806 if (sepSet.exists(key)) {
807 ui = sepSet[key];
808 } else if (sepSet.exists(rev_key)) {
809 ui = sepSet[rev_key];
810 }
811 // remove z from ui if it's present
812 const auto iter_z_place = std::find(ui.begin(), ui.end(), z);
813 if (iter_z_place != ui.end()) { ui.erase(iter_z_place); }
814
815 double Ixyz_ui = mutualInformation.score(x, y, z, ui);
816 Ranking triple;
817 auto tup = new ThreePoints{x, y, z};
818 triple.first = tup;
819 triple.second = Ixyz_ui;
820 triples.push_back(triple);
821 }
822 }
823 }
824 }
825 std::sort(triples.begin(), triples.end(), GreaterAbsPairOn2nd());
826 return triples;
827 }
828
831 std::vector< ProbabilisticRanking > Miic::unshieldedTriplesMiic_(
832 const MixedGraph& graph,
833 CorrectedMutualInformation& mutualInformation,
834 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet,
835 HashTable< std::pair< NodeId, NodeId >, char >& marks) {
836 std::vector< ProbabilisticRanking > triples;
837 for (NodeId z: graph) {
838 for (NodeId x: graph.neighbours(z)) {
839 for (NodeId y: graph.neighbours(z)) {
840 if (y < x && !graph.existsEdge(x, y)) {
841 std::vector< NodeId > ui;
842 std::pair< NodeId, NodeId > key = {x, y};
843 std::pair< NodeId, NodeId > rev_key = {y, x};
844 if (sepSet.exists(key)) {
845 ui = sepSet[key];
846 } else if (sepSet.exists(rev_key)) {
847 ui = sepSet[rev_key];
848 }
849 // remove z from ui if it's present
850 const auto iter_z_place = std::find(ui.begin(), ui.end(), z);
851 if (iter_z_place != ui.end()) { ui.erase(iter_z_place); }
852
853 const double Ixyz_ui = mutualInformation.score(x, y, z, ui);
854 auto tup = new ThreePoints{x, y, z};
855 ProbabilisticRanking triple{tup, Ixyz_ui, 0.5, 0.5};
856 triples.push_back(triple);
857 if (!marks.exists({x, z})) { marks.insert({x, z}, 'o'); }
858 if (!marks.exists({z, x})) { marks.insert({z, x}, 'o'); }
859 if (!marks.exists({y, z})) { marks.insert({y, z}, 'o'); }
860 if (!marks.exists({z, y})) { marks.insert({z, y}, 'o'); }
861 }
862 }
863 }
864 }
865 triples = updateProbaTriples_(graph, triples);
866 std::sort(triples.begin(), triples.end(), GreaterTupleOnLast());
867 return triples;
868 }
869
871 std::vector< ProbabilisticRanking >
873 std::vector< ProbabilisticRanking > probaTriples) {
874 for (auto& triple: probaTriples) {
875 NodeId x, y, z;
876 x = std::get< 0 >(*std::get< 0 >(triple));
877 y = std::get< 1 >(*std::get< 0 >(triple));
878 z = std::get< 2 >(*std::get< 0 >(triple));
879 const double Ixyz = std::get< 1 >(triple);
880 double Pxz = std::get< 2 >(triple);
881 double Pyz = std::get< 3 >(triple);
882
883 if (Ixyz <= 0) {
884 const double expo = std::exp(Ixyz);
885 const double P0 = (1 + expo) / (1 + 3 * expo);
886 // distinguish between the initialization and the update process
887 if (Pxz == Pyz && Pyz == 0.5) {
888 std::get< 2 >(triple) = P0;
889 std::get< 3 >(triple) = P0;
890 } else {
891 if (graph.existsArc(x, z) && Pxz >= P0) {
892 std::get< 3 >(triple) = Pxz * (1 / (1 + expo) - 0.5) + 0.5;
893 } else if (graph.existsArc(y, z) && Pyz >= P0) {
894 std::get< 2 >(triple) = Pyz * (1 / (1 + expo) - 0.5) + 0.5;
895 }
896 }
897 } else {
898 const double expo = std::exp(-Ixyz);
899 if (graph.existsArc(x, z) && Pxz >= 0.5) {
900 std::get< 3 >(triple) = Pxz * (1 / (1 + expo) - 0.5) + 0.5;
901 } else if (graph.existsArc(y, z) && Pyz >= 0.5) {
902 std::get< 2 >(triple) = Pyz * (1 / (1 + expo) - 0.5) + 0.5;
903 }
904 }
905 }
906 std::sort(probaTriples.begin(), probaTriples.end(), GreaterTupleOnLast());
907 return probaTriples;
908 }
909
911 const std::vector< Arc > Miic::latentVariables() const { return _latentCouples_; }
912
913 void Miic::addConstraints(HashTable< std::pair< NodeId, NodeId >, char > constraints) {
914 this->_initialMarks_ = constraints;
915 }
916
918 const NodeId n1,
919 const NodeId n2) {
920 for (const auto parent: graph.parents(n2)) {
921 if (graph.existsArc(n2, parent)) // if there is a double arc, pass
922 continue;
923 if (parent == n1) // trivial directed path => not recognized
924 continue;
925 if (_existsDirectedPath_(graph, n1, parent)) return true;
926 }
927 return false;
928 }
929
930 bool Miic::_existsDirectedPath_(const MixedGraph& graph, const NodeId n1, const NodeId n2) {
931 // not recursive version => use a FIFO for simulating the recursion
932 List< NodeId > nodeFIFO;
933 // mark[node] = successor if visited, else mark[node] does not exist
934 Set< NodeId > mark;
935
936 mark.insert(n2);
937 nodeFIFO.pushBack(n2);
938
939 NodeId current;
940
941 while (!nodeFIFO.empty()) {
942 current = nodeFIFO.front();
943 nodeFIFO.popFront();
944
945 // check the parents
946 for (const auto new_one: graph.parents(current)) {
947 if (graph.existsArc(current,
948 new_one)) // if there is a double arc, pass
949 continue;
950
951 if (new_one == n1) { return true; }
952
953 if (mark.exists(new_one)) // if this node is already marked, do not
954 continue; // check it again
955
956 mark.insert(new_one);
957 nodeFIFO.pushBack(new_one);
958 }
959 }
960
961 return false;
962 }
963
964 bool Miic::_isNotLatentCouple_(const NodeId x, const NodeId y) {
965 const auto& lbeg = _latentCouples_.begin();
966 const auto& lend = _latentCouples_.end();
967
968 return (std::find(lbeg, lend, Arc(x, y)) == lend)
969 && (std::find(lbeg, lend, Arc(y, x)) == lend);
970 }
971
972 void Miic::setMandatoryGraph(const gum::DAG mandaGraph) { this->_mandatoryGraph_ = mandaGraph; }
973
974 void Miic::setForbiddenGraph(const gum::DiGraph forbidGraph) {
975 this->_forbiddenGraph_ = forbidGraph;
976 }
977
979
981 return ((graph.parents(x).size() >= _maxIndegree_));
982 }
983
985 return (_forbiddenGraph_.existsArc(x, y));
986 }
987
988 bool Miic::isArcValid_(const MixedGraph graph, NodeId x, NodeId y) {
989 return (isForbiddenArc_(x, y) == false && isMaxIndegree_(graph, y) == false);
990 }
991
993 return (_forbiddenGraph_.existsArc(x, y) && _forbiddenGraph_.existsArc(y, x));
994 }
995
996 } /* namespace learning */
997
998} /* namespace gum */
A class that, given a structure and a parameter estimator returns a full Bayes net.
The Miic algorithm.
#define GUM_SL_EMIT(x, y, action, explain)
Definition Miic.h:74
Size current_step_
The current step.
ApproximationScheme(bool verbosity=false)
bool existsArc(const Arc &arc) const
indicates whether a given arc exists
const NodeSet & parents(NodeId id) const
returns the set of nodes with arc ingoing to a given node
virtual void eraseArc(const Arc &arc)
removes an arc from the ArcGraphPart
The base class for all directed edges.
Base class for dag.
Definition DAG.h:121
Base class for all oriented graphs.
Definition diGraph.h:130
virtual void addArc(const NodeId tail, const NodeId head)
insert a new arc into the directed graph
Definition diGraph_inl.h:55
virtual void eraseEdge(const Edge &edge)
removes an edge from the EdgeGraphPart
const EdgeSet & edges() const
returns the set of edges stored within the EdgeGraphPart
bool existsEdge(const Edge &edge) const
indicates whether a given edge exists
const NodeSet & neighbours(NodeId id) const
returns the set of node neighbours to a given node
The base class for all undirected edges.
The class for generic Hash Tables.
Definition hashTable.h:637
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
Heap data structure.
Definition heap.h:141
Size size() const noexcept
Returns the number of elements in the heap.
Definition heap_tpl.h:148
Val pop()
Removes the top element from the heap and return it.
Definition heap_tpl.h:213
Size insert(const Val &val)
inserts a new element (actually a copy) in the heap and returns its index
Definition heap_tpl.h:239
const Val & top() const
Returns the element at the top of the heap.
Definition heap_tpl.h:140
Signaler3< Size, double, double > onProgress
Progression, error and time.
Generic doubly linked lists.
Definition list.h:379
Val & front() const
Returns a reference to first element of a list, if any.
Definition list_tpl.h:1703
bool empty() const noexcept
Returns a boolean indicating whether the chained list is empty.
Definition list_tpl.h:1831
void popFront()
Removes the first element of a List, if any.
Definition list_tpl.h:1825
Val & pushBack(const Val &val)
Inserts a new element (a copy) at the end of the chained list.
Definition list_tpl.h:1488
Base class for mixed graphs.
Definition mixedGraph.h:146
const NodeGraphPart & nodes() const
return *this as a NodeGraphPart
Base class for partially directed acyclic graphs.
Definition PDAG.h:130
iterator begin() const
The usual unsafe begin iterator to parse the set.
Definition set_tpl.h:438
Size size() const noexcept
Returns the number of elements in the set.
Definition set_tpl.h:636
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:533
bool contains(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:497
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:539
bool empty() const noexcept
Indicates whether the set is the empty set.
Definition set_tpl.h:642
void erase(const Key &k)
Erases an element from the set.
Definition set_tpl.h:582
The class computing n times the corrected mutual information, as used in the MIIC algorithm.
double score(NodeId var1, NodeId var2)
returns the 2-point mutual information corresponding to a given nodeset
static BayesNet< GUM_SCALAR > createBN(ParamEstimator &estimator, const DAG &dag)
create a BN from a DAG using a one pass generator (typically ML)
bool operator()(const Ranking &e1, const Ranking &e2) const
Definition Miic.cpp:97
bool operator()(const CondRanking &e1, const CondRanking &e2) const
Definition Miic.cpp:93
bool operator()(const ProbabilisticRanking &e1, const ProbabilisticRanking &e2) const
Definition Miic.cpp:101
static bool _existsDirectedPath_(const MixedGraph &graph, NodeId n1, NodeId n2)
checks for directed paths in a graph, consider double arcs like edges
Definition Miic.cpp:930
std::vector< ProbabilisticRanking > updateProbaTriples_(const MixedGraph &graph, std::vector< ProbabilisticRanking > probaTriples)
Gets the orientation probabilities like MIIC for the orientation phase.
Definition Miic.cpp:872
gum::DAG _mandatoryGraph_
Graph that contains the mandatories arcs.
Definition Miic.h:361
bool isMaxIndegree_(MixedGraph graph, NodeId x)
Definition Miic.cpp:980
void _orientingVstructureMiic_(MixedGraph &graph, HashTable< std::pair< NodeId, NodeId >, char > &marks, NodeId x, NodeId y, NodeId z, double p1, double p2)
Definition Miic.cpp:420
void orientationMiic_(CorrectedMutualInformation &mutualInformation, MixedGraph &graph, const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > &sepSet)
Orientation phase from the MIIC algorithm, returns a mixed graph that may contain circles.
Definition Miic.cpp:309
gum::DiGraph _forbiddenGraph_
Graph that contains the forbidden arcs.
Definition Miic.h:364
void setMandatoryGraph(gum::DAG mandaGraph)
Definition Miic.cpp:972
Miic & operator=(const Miic &from)
copy operator
Definition Miic.cpp:82
MixedGraph learnMixedStructure(CorrectedMutualInformation &mutualInformation, MixedGraph graph)
learns the structure of a MixedGraph (Meek rules not used here).
Definition Miic.cpp:147
void setMaxIndegree(gum::Size n)
Definition Miic.cpp:978
gum::MeekRules meekRules_
Object that can propagates orientations to non-oriented edges.
Definition Miic.h:332
gum::Size _maxIndegree_
maximum number of parents
Definition Miic.h:349
const std::vector< NodeId > _emptySet_
an empty conditioning set
Definition Miic.h:344
PDAG learnPDAG(CorrectedMutualInformation &mutualInformation, MixedGraph graph)
learns the structure of an Essential Graph
Definition Miic.cpp:178
~Miic() override
destructor
Definition Miic.cpp:79
std::vector< Arc > _latentCouples_
an empty vector of arcs
Definition Miic.h:346
void addConstraints(HashTable< std::pair< NodeId, NodeId >, char > constraints)
Set a ensemble of constraints for the orientation phase.
Definition Miic.cpp:913
void _propagatingOrientationMiic_(MixedGraph &graph, HashTable< std::pair< NodeId, NodeId >, char > &marks, NodeId x, NodeId y, NodeId z, double p1, double p2)
Definition Miic.cpp:548
void iteration_(CorrectedMutualInformation &mutualInformation, MixedGraph &graph, HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > &sepSet, Heap< CondRanking, GreaterPairOn2nd > &rank)
Iteration phase.
Definition Miic.cpp:253
bool isArcValid_(MixedGraph graph, NodeId x, NodeId y)
Definition Miic.cpp:988
void findBestContributor_(NodeId x, NodeId y, const std::vector< NodeId > &ui, const MixedGraph &graph, CorrectedMutualInformation &mutualInformation, Heap< CondRanking, GreaterPairOn2nd > &rank)
finds the best contributor node for a pair given a conditioning set
Definition Miic.cpp:718
void setForbiddenGraph(gum::DiGraph forbidGraph)
Set ForbiddenGraph (resp. MadatoryGraph) which contains the forbidden (resp. mandatory) arcs.
Definition Miic.cpp:974
std::vector< ProbabilisticRanking > unshieldedTriplesMiic_(const MixedGraph &graph, CorrectedMutualInformation &mutualInformation, const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > &sepSet, HashTable< std::pair< NodeId, NodeId >, char > &marks)
gets the list of unshielded triples in the graph in decreasing value of |I'(x, y, z|{ui}...
Definition Miic.cpp:831
bool isForbiddenArc_(NodeId x, NodeId y) const
Check constraints.
Definition Miic.cpp:984
Miic()
default constructor
Definition Miic.cpp:63
BayesNet< GUM_SCALAR > learnBN(GRAPH_CHANGES_SELECTOR &selector, PARAM_ESTIMATOR &estimator, DAG initial_dag=DAG())
learns the structure and the parameters of a BN
Definition Miic.cpp:190
std::vector< Ranking > unshieldedTriples_(const MixedGraph &graph, CorrectedMutualInformation &mutualInformation, const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > &sepSet)
gets the list of unshielded triples in the graph in decreasing value of |I'(x, y, z|{ui}...
Definition Miic.cpp:794
HashTable< std::pair< NodeId, NodeId >, char > _initialMarks_
Initial marks for the orientation phase, used to convey constraints.
Definition Miic.h:358
bool isForbiddenEdge_(NodeId x, NodeId y)
Definition Miic.cpp:992
ArcProperty< double > _arcProbas_
Storing the propabilities for each arc set in the graph.
Definition Miic.h:355
void initiation_(CorrectedMutualInformation &mutualInformation, MixedGraph &graph, HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > &sepSet, Heap< CondRanking, GreaterPairOn2nd > &rank)
Initiation phase.
Definition Miic.cpp:204
static bool _existsNonTrivialDirectedPath_(const MixedGraph &graph, NodeId n1, NodeId n2)
checks for directed paths in a graph, considering double arcs like edges, not considering arc as a di...
Definition Miic.cpp:917
Size _size_
size of the database
Definition Miic.h:352
bool _isNotLatentCouple_(NodeId x, NodeId y)
Definition Miic.cpp:964
void orientDoubleHeadedArcs_(MixedGraph &mg)
Orient double headed arcs to avoid cycles.
Definition Miic.cpp:653
const std::vector< Arc > latentVariables() const
get the list of arcs hiding latent variables
Definition Miic.cpp:911
int _maxLog_
Fixes the maximum log that we accept in exponential computations.
Definition Miic.h:342
MixedGraph learnSkeleton(CorrectedMutualInformation &mutualInformation, MixedGraph graph)
learns the skeleton of a MixedGraph (no orientation).
Definition Miic.cpp:124
DAG learnStructure(CorrectedMutualInformation &I, MixedGraph graph)
learns the structure of a Bayesian network, i.e. a DAG, by first learning an Essential graph and then...
Definition Miic.cpp:183
The class computing n times the corrected mutual information (where n is the size (or the weight) of ...
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 ...
Class hash tables iterators.
Heaps definition.
Useful macros for maths.
#define M_LN2
Definition math_utils.h:63
Base classes for mixed directed/undirected graphs.
include the inlined functions if necessary
Definition CSVParser.h:54
std::pair< ThreePoints *, double > Ranking
Definition Miic.h:90
std::pair< CondThreePoints *, double > CondRanking
Definition Miic.h:87
std::tuple< NodeId, NodeId, NodeId, std::vector< NodeId > > CondThreePoints
Definition Miic.h:86
std::tuple< NodeId, NodeId, NodeId > ThreePoints
Definition Miic.h:89
std::tuple< ThreePoints *, double, double, double > ProbabilisticRanking
Definition Miic.h:92
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
STL namespace.
#define GUM_EMIT3(signal, arg1, arg2, arg3)
Definition signaler3.h:61
Class used to compute response times for benchmark purposes.