aGrUM 3.1.1
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-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
47
54
56
57namespace gum {
58
59 namespace learning {
60
61 // ##########################################################################
62 // Constructors / Destructors
63 // ##########################################################################
64
65 Miic::Miic() : ConstraintBasedLearning() { GUM_CONSTRUCTOR(Miic); }
66
67 Miic::Miic(int maxLog) : ConstraintBasedLearning(maxLog) { GUM_CONSTRUCTOR(Miic); }
68
69 Miic::Miic(const Miic& from) : ConstraintBasedLearning(from) { GUM_CONS_CPY(Miic); }
70
71 Miic::Miic(Miic&& from) : ConstraintBasedLearning(std::move(from)) { GUM_CONS_MOV(Miic); }
72
73 Miic::~Miic() { GUM_DESTRUCTOR(Miic); }
74
75 Miic& Miic::operator=(const Miic& from) {
77 return *this;
78 }
79
82 return *this;
83 }
84
85 // ##########################################################################
86 // Scorer injection
87 // ##########################################################################
88
90
91 // ##########################################################################
92 // Comparator operators
93 // ##########################################################################
94
95 bool GreaterPairOn2nd::operator()(const CondRanking& e1, const CondRanking& e2) const {
96 return e1.second > e2.second;
97 }
98
99 bool GreaterAbsPairOn2nd::operator()(const Ranking& e1, const Ranking& e2) const {
100 return std::abs(e1.second) > std::abs(e2.second);
101 }
102
104 const ProbabilisticRanking& e2) const {
105 double p1xz = std::get< 2 >(e1);
106 double p1yz = std::get< 3 >(e1);
107 double p2xz = std::get< 2 >(e2);
108 double p2yz = std::get< 3 >(e2);
109 double I1 = std::get< 1 >(e1);
110 double I2 = std::get< 1 >(e2);
111 if ((I1 < 0 && I2 < 0) || (I1 >= 0 && I2 >= 0)) {
112 if (std::max(p1xz, p1yz) == std::max(p2xz, p2yz)) {
113 return std::abs(I1) > std::abs(I2);
114 } else {
115 return std::max(p1xz, p1yz) > std::max(p2xz, p2yz);
116 }
117 } else {
118 return I1 < I2;
119 }
120 }
121
122 // ##########################################################################
123 // Skeleton learning
124 // ##########################################################################
125
127 if (mi_ == nullptr) GUM_ERROR(NullElement, "call setMutualInformation before learnSkeleton")
128 timer_.reset();
129 current_step_ = 0;
130
131 _latentCouples_.clear();
133
135 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > sep_set;
136
137 initiation_(*mi_, graph, sep_set, rank);
138 iteration_(*mi_, graph, sep_set, rank);
139
140 return graph;
141 }
142
144 if (mi_ == nullptr)
145 GUM_ERROR(NullElement, "call setMutualInformation before learnMixedStructure")
146 timer_.reset();
147 current_step_ = 0;
148
149 _latentCouples_.clear();
151
153 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > sep_set;
154
155 initiation_(*mi_, graph, sep_set, rank);
156 iteration_(*mi_, graph, sep_set, rank);
157 orientationMiic_(*mi_, graph, sep_set);
158
159 return meekRules_.propagate(graph);
160 }
161
162 // ##########################################################################
163 // PHASE 1 : INITIATION
164 // ##########################################################################
165
168 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet,
170 NodeId x, y;
171 EdgeSet edges = graph.edges();
172 Size steps_init = edges.size();
173
174 for (const Edge& edge: edges) {
175 x = edge.first();
176 y = edge.second();
177
178 double Ixy = mutualInformation.score(x, y);
179
180 if (Ixy <= 0) {
181 graph.eraseEdge(edge);
182 GUM_SL_EMIT(x,
183 y,
184 "Remove " << x << " - " << y,
185 "Independent based on Mutual Information :" << Ixy)
186 sepSet.insert(std::make_pair(x, y), _emptySet_);
187 } else {
188 findBestContributor_(x, y, _emptySet_, graph, mutualInformation, rank);
189 GUM_SL_EMIT(x,
190 y,
191 "Keep " << x << " - " << y,
192 "Dependent based on Mutual Information :" << Ixy)
193 }
194
196 if (onProgress.hasListener()) {
197 GUM_EMIT3(onProgress, (current_step_ * 33) / steps_init, 0., timer_.step());
198 }
199 }
200 }
201
202 // ##########################################################################
203 // PHASE 2 : ITERATION
204 // ##########################################################################
205
208 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet,
210 CondRanking best;
211 Size steps_init = current_step_;
212 Size steps_iter = rank.size();
213
214 try {
215 while (rank.top().second > 0.5) {
216 best = rank.pop();
217
218 const NodeId x = std::get< 0 >(*(best.first));
219 const NodeId y = std::get< 1 >(*(best.first));
220 const NodeId z = std::get< 2 >(*(best.first));
221 std::vector< NodeId > ui = std::move(std::get< 3 >(*(best.first)));
222
223 ui.push_back(z);
224 const double i_xy_ui = mutualInformation.score(x, y, ui);
225 if (i_xy_ui < 0) {
226 graph.eraseEdge(Edge(x, y));
227 GUM_SL_EMIT(x,
228 y,
229 "Remove " << x << " - " << y,
230 "Independent based on MutualInformation knowing Sep "
231 << ui << "Mutual information:" << i_xy_ui)
232 sepSet.insert(std::make_pair(x, y), std::move(ui));
233 } else {
234 findBestContributor_(x, y, ui, graph, mutualInformation, rank);
235 }
236
237 delete best.first;
238
240 if (onProgress.hasListener()) {
242 (current_step_ * 66) / (steps_init + steps_iter),
243 0.,
244 timer_.step());
245 }
246 }
247 } catch (...) {} // heap is empty
248 current_step_ = steps_init + steps_iter;
249 if (onProgress.hasListener()) { GUM_EMIT3(onProgress, 66, 0., timer_.step()); }
250 current_step_ = steps_init + steps_iter;
251 }
252
253 // ##########################################################################
254 // PHASE 3 : ORIENTATION
255 // ##########################################################################
256
258 CorrectedMutualInformation& mutualInformation,
260 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet) {
262
264
265 for (const auto& arc: _mandatoryGraph_.arcs()) {
266 marks.insert({arc.tail(), arc.head()}, '>');
267 marks.insert({arc.head(), arc.tail()}, '-');
268 }
269 for (const Arc& arc: _forbiddenGraph_.arcs()) {
270 if (graph.existsArc(arc.head(), arc.tail())) {
271 marks.insert({arc.tail(), arc.head()}, '-');
272 marks.insert({arc.head(), arc.tail()}, '>');
273 }
274 }
275
276 std::vector< ProbabilisticRanking > proba_triples
277 = unshieldedTriplesMiic_(graph, mutualInformation, sepSet, marks);
278
279 const Size steps_orient = proba_triples.size();
280 Size past_steps = current_step_;
281
283 if (steps_orient > 0) best = proba_triples[0];
284
285 while (!proba_triples.empty() && std::max(std::get< 2 >(best), std::get< 3 >(best)) >= 0.5) {
286 const NodeId x = std::get< 0 >(*std::get< 0 >(best));
287 const NodeId y = std::get< 1 >(*std::get< 0 >(best));
288 const NodeId z = std::get< 2 >(*std::get< 0 >(best));
289
290 const double i3 = std::get< 1 >(best);
291 const double p1 = std::get< 2 >(best);
292 const double p2 = std::get< 3 >(best);
293
294 if (i3 <= 0) {
295 _orientingVstructureMiic_(graph, marks, x, y, z, p1, p2);
296 } else {
297 _propagatingOrientationMiic_(graph, marks, x, y, z, p1, p2);
298 }
299
300 delete std::get< 0 >(best);
301 proba_triples.erase(proba_triples.begin());
302 proba_triples = updateProbaTriples_(graph, proba_triples);
303
304 if (!proba_triples.empty()) best = proba_triples[0];
305
307 if (onProgress.hasListener()) {
309 (current_step_ * 100) / (steps_orient + past_steps),
310 0.,
311 timer_.step());
312 }
313 }
314
315 for (auto iter = _latentCouples_.rbegin(); iter != _latentCouples_.rend(); ++iter) {
316 graph.eraseArc(Arc(iter->head(), iter->tail()));
317 if (_existsDirectedPath_(graph, iter->head(), iter->tail())) {
318 graph.addArc(iter->head(), iter->tail());
319 graph.eraseArc(Arc(iter->tail(), iter->head()));
320 *iter = Arc(iter->head(), iter->tail());
321 }
322 }
323
324 if (onProgress.hasListener()) { GUM_EMIT3(onProgress, 100, 0., timer_.step()); }
325 }
326
327 // ##########################################################################
328 // v-structure orientation
329 // ##########################################################################
330
332 HashTable< std::pair< NodeId, NodeId >, char >& marks,
333 NodeId x,
334 NodeId y,
335 NodeId z,
336 double p1,
337 double p2) {
338 if (marks[{x, z}] == 'o' && marks[{y, z}] == 'o') {
340 if (isArcValid_(graph, x, z)) {
341 graph.eraseEdge(Edge(x, z));
342 graph.addArc(x, z);
343 GUM_SL_EMIT(x, z, "Add Arc " << x << " -> " << z, "V-structure Orientation")
344 marks[{x, z}] = '>';
345 if (graph.existsArc(z, x) && _isNotLatentCouple_(z, x)) {
346 _latentCouples_.emplace_back(z, x);
347 }
348 if (!_arcProbas_.exists(Arc(x, z))) _arcProbas_.insert(Arc(x, z), p1);
349 }
350 } else {
351 graph.eraseEdge(Edge(x, z));
353 if (isArcValid_(graph, z, x)) {
354 graph.addArc(z, x);
355 GUM_SL_EMIT(z, x, "Add Arc " << z << " -> " << x, "V-structure Orientation")
356 marks[{z, x}] = '>';
357 }
358 }
359 }
360
362 if (isArcValid_(graph, y, z)) {
363 graph.eraseEdge(Edge(y, z));
364 graph.addArc(y, z);
365 GUM_SL_EMIT(y, z, "Add Arc " << y << " -> " << z, "V-structure Orientation")
366 marks[{y, z}] = '>';
367 if (graph.existsArc(z, y) && _isNotLatentCouple_(z, y)) {
368 _latentCouples_.emplace_back(z, y);
369 }
370 if (!_arcProbas_.exists(Arc(y, z))) _arcProbas_.insert(Arc(y, z), p2);
371 }
372 } else {
373 graph.eraseEdge(Edge(y, z));
375 if (isArcValid_(graph, z, y)) {
376 graph.addArc(z, y);
377 GUM_SL_EMIT(z, y, "Add Arc " << z << " -> " << y, "V-structure Orientation")
378 marks[{z, y}] = '>';
379 }
380 }
381 }
382 } else if (marks[{x, z}] == '>' && marks[{y, z}] == 'o') {
384 if (isArcValid_(graph, y, z)) {
385 graph.eraseEdge(Edge(y, z));
386 graph.addArc(y, z);
387 GUM_SL_EMIT(y,
388 z,
389 "Add Arc " << y << " -> " << z,
390 "V-structure Orientation | existing "
391 << x << " -> " << z << ", then orienting " << y << " -> " << z)
392 marks[{y, z}] = '>';
393 if (graph.existsArc(z, y) && _isNotLatentCouple_(z, y)) {
394 _latentCouples_.emplace_back(z, y);
395 }
396 if (!_arcProbas_.exists(Arc(y, z))) _arcProbas_.insert(Arc(y, z), p2);
397 }
398 } else {
399 graph.eraseEdge(Edge(y, z));
401 if (isArcValid_(graph, z, y)) {
402 graph.addArc(z, y);
403 GUM_SL_EMIT(z,
404 y,
405 "Add Arc " << z << " -> " << y,
406 "V-structure Orientation | existing "
407 << x << " -> " << z << ", then orienting " << z << " -> " << y)
408 marks[{z, y}] = '>';
409 }
410 }
411 }
412 } else if (marks[{y, z}] == '>' && marks[{x, z}] == 'o') {
414 if (isArcValid_(graph, x, z)) {
415 graph.eraseEdge(Edge(x, z));
416 graph.addArc(x, z);
417 GUM_SL_EMIT(x, z, "Add Arc " << x << " -> " << z, "V-structure Orientation")
418 marks[{x, z}] = '>';
419 if (graph.existsArc(z, x) && _isNotLatentCouple_(z, x)) {
420 _latentCouples_.emplace_back(z, x);
421 }
422 if (!_arcProbas_.exists(Arc(x, z))) _arcProbas_.insert(Arc(x, z), p1);
423 }
424 } else {
425 graph.eraseEdge(Edge(x, z));
427 if (isArcValid_(graph, z, x)) {
428 graph.addArc(z, x);
429 GUM_SL_EMIT(z, x, "Add Arc " << z << " -> " << x, "V-structure Orientation")
430 marks[{z, x}] = '>';
431 }
432 }
433 }
434 }
435 }
436
437 // ##########################################################################
438 // Orientation propagation
439 // ##########################################################################
440
442 HashTable< std::pair< NodeId, NodeId >, char >& marks,
443 NodeId x,
444 NodeId y,
445 NodeId z,
446 double p1,
447 double p2) {
448 if (marks[{x, z}] == '>' && marks[{y, z}] == 'o' && marks[{z, y}] != '-') {
449 graph.eraseEdge(Edge(z, y));
450 if (!_existsDirectedPath_(graph, y, z) && graph.parents(y).empty()) {
451 if (isArcValid_(graph, z, y)) {
452 graph.addArc(z, y);
453 GUM_SL_EMIT(z,
454 y,
455 "Add Arc " << z << " -> " << y,
456 "Propagation MIIC (919) | existing x -> " << z << " and " << z << " - "
457 << y)
458 marks[{z, y}] = '>';
459 marks[{y, z}] = '-';
460 if (!_arcProbas_.exists(Arc(z, y))) _arcProbas_.insert(Arc(z, y), p2);
461 }
462 } else if (!_existsDirectedPath_(graph, z, y) && graph.parents(z).empty()) {
463 if (isArcValid_(graph, y, z)) {
464 graph.addArc(y, z);
465 GUM_SL_EMIT(y, z, "Add Arc " << y << " -> " << z, "Propagation MIIC line 932 ")
466 marks[{z, y}] = '-';
467 marks[{y, z}] = '>';
468 _latentCouples_.emplace_back(y, z);
469 if (!_arcProbas_.exists(Arc(y, z))) _arcProbas_.insert(Arc(y, z), p2);
470 }
471 } else if (!_existsDirectedPath_(graph, y, z)) {
472 if (isArcValid_(graph, z, y)) {
473 graph.addArc(z, y);
474 GUM_SL_EMIT(z, y, "Add Arc " << z << "->" << y, "Propagation MIIC 947 ")
475 marks[{z, y}] = '>';
476 marks[{y, z}] = '-';
477 if (!_arcProbas_.exists(Arc(z, y))) _arcProbas_.insert(Arc(z, y), p2);
478 }
479 } else if (!_existsDirectedPath_(graph, z, y)) {
480 if (isArcValid_(graph, y, z)) {
481 graph.addArc(y, z);
482 GUM_SL_EMIT(z, y, "Add Arc " << z << "->" << y, "Propagation MIIC 959")
483 _latentCouples_.emplace_back(y, z);
484 marks[{z, y}] = '-';
485 marks[{y, z}] = '>';
486 if (!_arcProbas_.exists(Arc(y, z))) _arcProbas_.insert(Arc(y, z), p2);
487 }
488 }
489 } else if (marks[{y, z}] == '>' && marks[{x, z}] == 'o' && marks[{z, x}] != '-') {
490 graph.eraseEdge(Edge(z, x));
491 if (!_existsDirectedPath_(graph, x, z) && graph.parents(x).empty()) {
492 if (isArcValid_(graph, z, x)) {
493 graph.addArc(z, x);
494 GUM_SL_EMIT(z, x, "Add Arc " << z << " -> " << x, "Propagation MIIC 977")
495 marks[{z, x}] = '>';
496 marks[{x, z}] = '-';
497 if (!_arcProbas_.exists(Arc(z, x))) _arcProbas_.insert(Arc(z, x), p1);
498 }
499 } else if (!_existsDirectedPath_(graph, z, x) && graph.parents(z).empty()) {
500 if (isArcValid_(graph, x, z)) {
501 graph.addArc(x, z);
502 GUM_SL_EMIT(x, z, "Add Arc " << x << "->" << z, "Propagation MIIC 990")
503 marks[{z, x}] = '-';
504 marks[{x, z}] = '>';
505 _latentCouples_.emplace_back(x, z);
506 if (!_arcProbas_.exists(Arc(x, z))) _arcProbas_.insert(Arc(x, z), p1);
507 }
508 } else if (!_existsDirectedPath_(graph, x, z)) {
509 if (isArcValid_(graph, z, x)) {
510 graph.addArc(z, x);
511 GUM_SL_EMIT(z, x, "Add Arc " << z << " -> " << x, "Propagation MIIC 1004")
512 marks[{z, x}] = '>';
513 marks[{x, z}] = '-';
514 if (!_arcProbas_.exists(Arc(z, x))) _arcProbas_.insert(Arc(z, x), p1);
515 }
516 } else if (!_existsDirectedPath_(graph, z, x)) {
517 if (isArcValid_(graph, x, z)) {
518 graph.addArc(x, z);
519 GUM_SL_EMIT(x, z, "Add Arc " << x << " -> " << z, "Propagation MIIC 1016")
520 marks[{z, x}] = '-';
521 marks[{x, z}] = '>';
522 _latentCouples_.emplace_back(x, z);
523 if (!_arcProbas_.exists(Arc(x, z))) _arcProbas_.insert(Arc(x, z), p1);
524 }
525 }
526 }
527 }
528
529 // ##########################################################################
530 // Best contributor
531 // ##########################################################################
532
534 NodeId y,
535 const std::vector< NodeId >& ui,
536 const MixedGraph& graph,
537 CorrectedMutualInformation& mutualInformation,
539 double maxP = -1.0;
540 NodeId maxZ = 0;
541
542 const double Ixy_ui = mutualInformation.score(x, y, ui);
543
544 for (const NodeId z: graph) {
545 if (z != x && z != y && std::find(ui.begin(), ui.end(), z) == ui.end()) {
546 double Pnv;
547 double Pb;
548
549 const double Ixyz_ui = mutualInformation.score(x, y, z, ui);
550 double calc_expo1 = -Ixyz_ui * M_LN2;
551 if (calc_expo1 > _maxLog_) {
552 Pnv = 0.0;
553 } else if (calc_expo1 < -_maxLog_) {
554 Pnv = 1.0;
555 } else {
556 Pnv = 1 / (1 + std::exp(calc_expo1));
557 }
558
559 const double Ixz_ui = mutualInformation.score(x, z, ui);
560 const double Iyz_ui = mutualInformation.score(y, z, ui);
561
562 calc_expo1 = -(Ixz_ui - Ixy_ui) * M_LN2;
563 double calc_expo2 = -(Iyz_ui - Ixy_ui) * M_LN2;
564
565 if (calc_expo1 > _maxLog_ || calc_expo2 > _maxLog_) {
566 Pb = 0.0;
567 } else if (calc_expo1 < -_maxLog_ && calc_expo2 < -_maxLog_) {
568 Pb = 1.0;
569 } else {
570 double expo1, expo2;
571 expo1 = (calc_expo1 < -_maxLog_) ? 0.0 : std::exp(calc_expo1);
572 expo2 = (calc_expo2 < -_maxLog_) ? 0.0 : std::exp(calc_expo2);
573 Pb = 1 / (1 + expo1 + expo2);
574 }
575
576 const double min_pnv_pb = std::min(Pnv, Pb);
577 if (min_pnv_pb > maxP) {
578 maxP = min_pnv_pb;
579 maxZ = z;
580 }
581 }
582 }
583 CondRanking final;
584 auto tup = new CondThreePoints{x, y, maxZ, ui};
585 final.first = tup;
586 final.second = maxP;
587 rank.insert(final);
588 }
589
590 // ##########################################################################
591 // Unshielded triples (MIIC probabilistic version)
592 // ##########################################################################
593
594 std::vector< ProbabilisticRanking > Miic::unshieldedTriplesMiic_(
595 const MixedGraph& graph,
596 CorrectedMutualInformation& mutualInformation,
597 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet,
598 HashTable< std::pair< NodeId, NodeId >, char >& marks) {
599 std::vector< ProbabilisticRanking > triples;
600 for (NodeId z: graph) {
601 for (NodeId x: graph.neighbours(z)) {
602 for (NodeId y: graph.neighbours(z)) {
603 if (y < x && !graph.existsEdge(x, y)) {
604 std::vector< NodeId > ui;
605 std::pair< NodeId, NodeId > key = {x, y};
606 std::pair< NodeId, NodeId > rev_key = {y, x};
607 if (sepSet.exists(key)) {
608 ui = sepSet[key];
609 } else if (sepSet.exists(rev_key)) {
610 ui = sepSet[rev_key];
611 }
612 const auto iter_z_place = std::find(ui.begin(), ui.end(), z);
613 if (iter_z_place != ui.end()) ui.erase(iter_z_place);
614
615 const double Ixyz_ui = mutualInformation.score(x, y, z, ui);
616 auto tup = new ThreePoints{x, y, z};
617 ProbabilisticRanking triple{tup, Ixyz_ui, 0.5, 0.5};
618 triples.push_back(triple);
619 if (!marks.exists({x, z})) marks.insert({x, z}, 'o');
620 if (!marks.exists({z, x})) marks.insert({z, x}, 'o');
621 if (!marks.exists({y, z})) marks.insert({y, z}, 'o');
622 if (!marks.exists({z, y})) marks.insert({z, y}, 'o');
623 }
624 }
625 }
626 }
627 triples = updateProbaTriples_(graph, triples);
628 std::sort(triples.begin(), triples.end(), GreaterTupleOnLast());
629 return triples;
630 }
631
632 std::vector< ProbabilisticRanking >
634 std::vector< ProbabilisticRanking > probaTriples) {
635 for (auto& triple: probaTriples) {
636 NodeId x, y, z;
637 x = std::get< 0 >(*std::get< 0 >(triple));
638 y = std::get< 1 >(*std::get< 0 >(triple));
639 z = std::get< 2 >(*std::get< 0 >(triple));
640 const double Ixyz = std::get< 1 >(triple);
641 double Pxz = std::get< 2 >(triple);
642 double Pyz = std::get< 3 >(triple);
643
644 if (Ixyz <= 0) {
645 const double expo = std::exp(Ixyz);
646 const double P0 = (1 + expo) / (1 + 3 * expo);
647 if (Pxz == Pyz && Pyz == 0.5) {
648 std::get< 2 >(triple) = P0;
649 std::get< 3 >(triple) = P0;
650 } else {
651 if (graph.existsArc(x, z) && Pxz >= P0) {
652 std::get< 3 >(triple) = Pxz * (1 / (1 + expo) - 0.5) + 0.5;
653 } else if (graph.existsArc(y, z) && Pyz >= P0) {
654 std::get< 2 >(triple) = Pyz * (1 / (1 + expo) - 0.5) + 0.5;
655 }
656 }
657 } else {
658 const double expo = std::exp(-Ixyz);
659 if (graph.existsArc(x, z) && Pxz >= 0.5) {
660 std::get< 3 >(triple) = Pxz * (1 / (1 + expo) - 0.5) + 0.5;
661 } else if (graph.existsArc(y, z) && Pyz >= 0.5) {
662 std::get< 2 >(triple) = Pyz * (1 / (1 + expo) - 0.5) + 0.5;
663 }
664 }
665 }
666 std::sort(probaTriples.begin(), probaTriples.end(), GreaterTupleOnLast());
667 return probaTriples;
668 }
669
670 // ##########################################################################
671 // Helpers
672 // ##########################################################################
673
674 bool Miic::_isNotLatentCouple_(const NodeId x, const NodeId y) {
675 const auto& lbeg = _latentCouples_.begin();
676 const auto& lend = _latentCouples_.end();
677 return (std::find(lbeg, lend, Arc(x, y)) == lend)
678 && (std::find(lbeg, lend, Arc(y, x)) == lend);
679 }
680
681 } /* namespace learning */
682
683} /* namespace gum */
#define GUM_SL_EMIT(x, y, action, explain)
The Miic algorithm.
Size current_step_
The current step.
The base class for all directed edges.
The base class for all undirected edges.
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.
Heap data structure.
Definition heap.h:141
Val pop()
Removes the top element from the heap and return it.
Definition heap_tpl.h:214
Size size() const noexcept
Returns the number of elements in the heap.
Definition heap_tpl.h:149
const Val & top() const
Returns the element at the top of the heap.
Definition heap_tpl.h:141
Size insert(const Val &val)
inserts a new element (actually a copy) in the heap and returns its index
Definition heap_tpl.h:240
Signaler< Size, double, double > onProgress
Progression, error and time.
Base class for mixed graphs.
Definition mixedGraph.h:146
Exception : a pointer or a reference on a nullptr (0) object.
Size size() const noexcept
Returns the number of elements in the set.
Definition set_tpl.h:607
static bool _existsNonTrivialDirectedPath_(const MixedGraph &graph, NodeId n1, NodeId n2)
Builds a complete MixedGraph on the nodes of template_graph, minus edges forbidden by structural cons...
gum::MeekRules meekRules_
Builds a complete MixedGraph on the nodes of template_graph, minus edges forbidden by structural cons...
bool isArcValid_(const MixedGraph &graph, NodeId x, NodeId y)
HashTable< std::pair< NodeId, NodeId >, char > _initialMarks_
static bool _existsDirectedPath_(const MixedGraph &graph, NodeId n1, NodeId n2)
Builds a complete MixedGraph on the nodes of template_graph, minus edges forbidden by structural cons...
ConstraintBasedLearning & operator=(const ConstraintBasedLearning &)
void applyStructuralConstraints_(MixedGraph &graph)
Builds a complete MixedGraph on the nodes of template_graph, minus edges forbidden by structural cons...
MixedGraph initGraph_(const MixedGraph &template_graph)
Builds a complete MixedGraph on the nodes of template_graph, minus edges forbidden by structural cons...
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
bool operator()(const Ranking &e1, const Ranking &e2) const
Definition Miic.cpp:99
bool operator()(const CondRanking &e1, const CondRanking &e2) const
Definition Miic.cpp:95
bool operator()(const ProbabilisticRanking &e1, const ProbabilisticRanking &e2) const
Definition Miic.cpp:103
std::vector< ProbabilisticRanking > updateProbaTriples_(const MixedGraph &graph, std::vector< ProbabilisticRanking > probaTriples)
Definition Miic.cpp:633
void _orientingVstructureMiic_(MixedGraph &graph, HashTable< std::pair< NodeId, NodeId >, char > &marks, NodeId x, NodeId y, NodeId z, double p1, double p2)
Definition Miic.cpp:331
void orientationMiic_(CorrectedMutualInformation &mutualInformation, MixedGraph &graph, const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > &sepSet)
Definition Miic.cpp:257
Miic & operator=(const Miic &from)
Definition Miic.cpp:75
MixedGraph learnMixedStructure(MixedGraph graph) override
Definition Miic.cpp:143
MixedGraph learnSkeleton(MixedGraph graph) override
Definition Miic.cpp:126
~Miic() override
Definition Miic.cpp:73
void _propagatingOrientationMiic_(MixedGraph &graph, HashTable< std::pair< NodeId, NodeId >, char > &marks, NodeId x, NodeId y, NodeId z, double p1, double p2)
Definition Miic.cpp:441
void iteration_(CorrectedMutualInformation &mutualInformation, MixedGraph &graph, HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > &sepSet, Heap< CondRanking, GreaterPairOn2nd > &rank)
Definition Miic.cpp:206
void findBestContributor_(NodeId x, NodeId y, const std::vector< NodeId > &ui, const MixedGraph &graph, CorrectedMutualInformation &mutualInformation, Heap< CondRanking, GreaterPairOn2nd > &rank)
Definition Miic.cpp:533
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)
Definition Miic.cpp:594
CorrectedMutualInformation * mi_
Definition Miic.h:186
ArcProperty< double > _arcProbas_
Definition Miic.h:187
void initiation_(CorrectedMutualInformation &mutualInformation, MixedGraph &graph, HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > &sepSet, Heap< CondRanking, GreaterPairOn2nd > &rank)
Definition Miic.cpp:166
void setMutualInformation(CorrectedMutualInformation &mi)
Definition Miic.cpp:89
bool _isNotLatentCouple_(NodeId x, NodeId y)
Definition Miic.cpp:674
The class computing n times the corrected mutual information (where n is the size (or the weight) of ...
#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.
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:55
std::pair< ThreePoints *, double > Ranking
Definition Miic.h:72
std::pair< CondThreePoints *, double > CondRanking
Definition Miic.h:71
std::tuple< NodeId, NodeId, NodeId, std::vector< NodeId > > CondThreePoints
Definition Miic.h:70
std::tuple< NodeId, NodeId, NodeId > ThreePoints
std::tuple< ThreePoints *, double, double, double > ProbabilisticRanking
Definition Miic.h:73
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
STL namespace.
#define GUM_EMIT3(signal, arg1, arg2, arg3)
Definition signaler.h:291
Class used to compute response times for benchmark purposes.