aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
SimpleMiic.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
55
57
58namespace gum {
59
60 namespace learning {
61
63 SimpleMiic::SimpleMiic() : _maxLog_(100), _size_(0) { GUM_CONSTRUCTOR(SimpleMiic); }
64
66 SimpleMiic::SimpleMiic(int maxLog) : _maxLog_(maxLog), _size_(0) {
67 GUM_CONSTRUCTOR(SimpleMiic);
68 }
69
72 ApproximationScheme(from), _size_(from._size_) {
73 GUM_CONS_CPY(SimpleMiic);
74 }
75
78 ApproximationScheme(std::move(from)), _size_(from._size_) {
79 GUM_CONS_MOV(SimpleMiic);
80 }
81
83 SimpleMiic::~SimpleMiic() { GUM_DESTRUCTOR(SimpleMiic); }
84
87 ApproximationScheme::operator=(from);
88 return *this;
89 }
90
93 ApproximationScheme::operator=(std::move(from));
94 return *this;
95 }
96
100 timer_.reset();
101 current_step_ = 0;
102
103 // clear the vector of latent arcs to be sure
104 _latentCouples_.clear();
105
108
110 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > sep_set;
111
112 initiation_(mutualInformation, graph, sep_set, rank);
113
114 iteration_(mutualInformation, graph, sep_set, rank);
115
116 orientationMiic_(mutualInformation, graph, sep_set);
117
118 return graph;
119 }
120
121 /*
122 * PHASE 1 : INITIATION
123 *
124 * We go over all edges and test if the variables are independent. If they
125 * are,
126 * the edge is deleted. If not, the best contributor is found.
127 */
129 CorrectedMutualInformation& mutualInformation,
131 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet,
133 NodeId x, y;
134 EdgeSet edges = graph.edges();
135 Size steps_init = edges.size();
136
137 for (const Edge& edge: edges) {
138 x = edge.first();
139 y = edge.second();
140 double Ixy = mutualInformation.score(x, y);
141
142 if (Ixy <= 0) { //< K
143 graph.eraseEdge(edge);
144 sepSet.insert(std::make_pair(x, y), _emptySet_);
145 } else {
146 findBestContributor_(x, y, _emptySet_, graph, mutualInformation, rank);
147 }
148
150 if (onProgress.hasListener()) {
151 GUM_EMIT3(onProgress, (current_step_ * 33) / steps_init, 0., timer_.step());
152 }
153 }
154 }
155
156 /*
157 * PHASE 2 : ITERATION
158 *
159 * As long as we find important nodes for edges, we go over them to see if
160 * we can assess the independence of the variables.
161 */
163 CorrectedMutualInformation& mutualInformation,
165 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet,
167 // if no triples to further examine pass
168 CondRanking best;
169
170 Size steps_init = current_step_;
171 Size steps_iter = rank.size();
172
173 try {
174 while (rank.top().second > 0.5) {
175 best = rank.pop();
176
177 const NodeId x = std::get< 0 >(*(best.first));
178 const NodeId y = std::get< 1 >(*(best.first));
179 const NodeId z = std::get< 2 >(*(best.first));
180 std::vector< NodeId > ui = std::move(std::get< 3 >(*(best.first)));
181
182 ui.push_back(z);
183 const double i_xy_ui = mutualInformation.score(x, y, ui);
184 if (i_xy_ui < 0) {
185 graph.eraseEdge(Edge(x, y));
186 sepSet.insert(std::make_pair(x, y), std::move(ui));
187 } else {
188 findBestContributor_(x, y, ui, graph, mutualInformation, rank);
189 }
190
191 delete best.first;
192
194 if (onProgress.hasListener()) {
196 (current_step_ * 66) / (steps_init + steps_iter),
197 0.,
198 timer_.step());
199 }
200 }
201 } catch (...) {} // here, rank is empty
202 current_step_ = steps_init + steps_iter;
203 if (onProgress.hasListener()) { GUM_EMIT3(onProgress, 66, 0., timer_.step()); }
204 current_step_ = steps_init + steps_iter;
205 }
206
207 /*
208 * PHASE 3 : ORIENTATION
209 *
210 * Try to assess v-structures and propagate them.
211 */
212
215 CorrectedMutualInformation& mutualInformation,
217 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet) {
218 std::vector< Ranking > triples = unshieldedTriples_(graph, mutualInformation, sepSet);
219 Size steps_orient = triples.size();
220 Size past_steps = current_step_;
221
222 NodeId i = 0;
223 // list of elements that we shouldnt read again, ie elements that are
224 // eligible to
225 // rule 0 after the first time they are tested, and elements on which rule 1
226 // has been applied
227 while (i < triples.size()) {
228 // if i not in do_not_reread
229 Ranking triple = triples[i];
230 NodeId x, y, z;
231 x = std::get< 0 >(*triple.first);
232 y = std::get< 1 >(*triple.first);
233 z = std::get< 2 >(*triple.first);
234
235 std::vector< NodeId > ui;
236 std::pair< NodeId, NodeId > key = {x, y};
237 std::pair< NodeId, NodeId > rev_key = {y, x};
238 if (sepSet.exists(key)) {
239 ui = sepSet[key];
240 } else if (sepSet.exists(rev_key)) {
241 ui = sepSet[rev_key];
242 }
243 double Ixyz_ui = triple.second;
244 // try Rule 0
245 if (Ixyz_ui < 0) {
246 // if ( z not in Sep[x,y])
247 if (std::find(ui.begin(), ui.end(), z) == ui.end()) {
248 // if what we want to add already exists : pass
249 if ((graph.existsArc(x, z) || graph.existsArc(z, x))
250 && (graph.existsArc(y, z) || graph.existsArc(z, y))) {
251 ++i;
252 } else {
253 i = 0;
254 graph.eraseEdge(Edge(x, z));
255 graph.eraseEdge(Edge(y, z));
256 // checking for cycles
257 if (graph.existsArc(z, x)) {
258 graph.eraseArc(Arc(z, x));
259 // if we find a directed path, we force the competing edge
260 if (graph.hasDirectedPath(z, x)) _latentCouples_.emplace_back(z, x);
261 else graph.addArc(x, z);
262 graph.addArc(z, x);
263 } else {
264 // if we find a directed path, we force the competing edge
265 if (graph.hasDirectedPath(z, x)) {
266 graph.addArc(z, x);
267 _latentCouples_.emplace_back(z, x);
268 } else {
269 graph.addArc(x, z);
270 }
271 }
272 if (graph.existsArc(z, y)) {
273 graph.eraseArc(Arc(z, y));
274 // if we find a directed path, we force the competing edge
275 if (graph.hasDirectedPath(z, y)) _latentCouples_.emplace_back(z, y);
276 else graph.addArc(y, z);
277 graph.addArc(z, y);
278 } else {
279 // if we find a directed path, we force the competing edge
280 if (graph.hasDirectedPath(z, y)) {
281 graph.addArc(z, y);
282 _latentCouples_.emplace_back(z, y);
283 } else {
284 graph.addArc(y, z);
285 }
286 }
287 if (graph.existsArc(z, x) && _isNotLatentCouple_(z, x)) {
288 _latentCouples_.emplace_back(z, x);
289 }
290 if (graph.existsArc(z, y) && _isNotLatentCouple_(z, y)) {
291 _latentCouples_.emplace_back(z, y);
292 }
293 }
294 } else {
295 ++i;
296 }
297 } else { // try Rule 1
298 bool reset{false};
299 if (graph.existsArc(x, z) && !graph.existsArc(z, y) && !graph.existsArc(y, z)) {
300 reset = true;
301 graph.eraseEdge(Edge(z, y));
302 // if we find a directed path, we force the competing edge
303 if (graph.hasDirectedPath(y, z)) {
304 graph.addArc(y, z);
305 _latentCouples_.emplace_back(y, z);
306 } else {
307 graph.addArc(z, y);
308 }
309 }
310 if (graph.existsArc(y, z) && !graph.existsArc(z, x) && !graph.existsArc(x, z)) {
311 reset = true;
312 graph.eraseEdge(Edge(z, x));
313 // if we find a directed path, we force the competing edge
314 if (graph.hasDirectedPath(x, z)) {
315 graph.addArc(x, z);
316 _latentCouples_.emplace_back(x, z);
317 } else {
318 graph.addArc(z, x);
319 }
320 }
321
322 if (reset) {
323 i = 0;
324 } else {
325 ++i;
326 }
327 } // if rule 0 or rule 1
328 if (onProgress.hasListener()) {
330 ((current_step_ + i) * 100) / (past_steps + steps_orient),
331 0.,
332 timer_.step());
333 }
334 } // while
335
336 // erasing the the double headed arcs
337 for (const Arc& arc: _latentCouples_) {
338 graph.eraseArc(Arc(arc.head(), arc.tail()));
339 }
340 }
341
344 CorrectedMutualInformation& mutualInformation,
346 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet) {
347 // structure to store the orientations marks -, o, or >,
348 // Considers the head of the arc/edge first node -* second node
350
351 // marks always correspond to the head of the arc/edge. - is for a forbidden
352 // arc, > for a mandatory arc
353 // we start by adding the mandatory arcs
354 for (auto iter = marks.begin(); iter != marks.end(); ++iter) {
355 if (graph.existsEdge(iter.key().first, iter.key().second) && iter.val() == '>') {
356 graph.eraseEdge(Edge(iter.key().first, iter.key().second));
357 graph.addArc(iter.key().first, iter.key().second);
358 }
359 }
360
361 std::vector< ProbabilisticRanking > proba_triples
362 = unshieldedTriplesMiic_(graph, mutualInformation, sepSet, marks);
363
364 const Size steps_orient = proba_triples.size();
365 Size past_steps = current_step_;
366
368 if (steps_orient > 0) { best = proba_triples[0]; }
369
370 while (!proba_triples.empty() && std::max(std::get< 2 >(best), std::get< 3 >(best)) > 0.5) {
371 const NodeId x = std::get< 0 >(*std::get< 0 >(best));
372 const NodeId y = std::get< 1 >(*std::get< 0 >(best));
373 const NodeId z = std::get< 2 >(*std::get< 0 >(best));
374
375 const double i3 = std::get< 1 >(best);
376
377 const double p1 = std::get< 2 >(best);
378 const double p2 = std::get< 3 >(best);
379 if (i3 <= 0) {
380 _orientingVstructureMiic_(graph, marks, x, y, z, p1, p2);
381 } else {
382 _propagatingOrientationMiic_(graph, marks, x, y, z, p1, p2);
383 }
384
385 delete std::get< 0 >(best);
386 proba_triples.erase(proba_triples.begin());
387 // actualisation of the list of triples
388 proba_triples = updateProbaTriples_(graph, proba_triples);
389
390 if (!proba_triples.empty()) best = proba_triples[0];
391
393 if (onProgress.hasListener()) {
395 (current_step_ * 100) / (steps_orient + past_steps),
396 0.,
397 timer_.step());
398 }
399 } // while
400
401 // erasing the double headed arcs
402 // GUM_TRACE(_latentCouples_)
403 for (auto iter = _latentCouples_.rbegin(); iter != _latentCouples_.rend(); ++iter) {
404 graph.eraseArc(Arc(iter->head(), iter->tail()));
405 if (_existsDirectedPath_(graph, iter->head(), iter->tail())) {
406 // if we find a cycle, we force the competing edge
407 graph.addArc(iter->head(), iter->tail());
408 graph.eraseArc(Arc(iter->tail(), iter->head()));
409 *iter = Arc(iter->head(), iter->tail());
410 }
411 }
412
413 if (onProgress.hasListener()) { GUM_EMIT3(onProgress, 100, 0., timer_.step()); }
414 }
415
418 NodeId y,
419 const std::vector< NodeId >& ui,
420 const MixedGraph& graph,
421 CorrectedMutualInformation& mutualInformation,
423 double maxP = -1.0;
424 NodeId maxZ = 0;
425
426 // compute N
427 // __N = I.N();
428 const double Ixy_ui = mutualInformation.score(x, y, ui);
429
430 for (const NodeId z: graph) {
431 // if z!=x and z!=y and z not in ui
432 if (z != x && z != y && std::find(ui.begin(), ui.end(), z) == ui.end()) {
433 double Pnv;
434 double Pb;
435
436 // Computing Pnv
437 const double Ixyz_ui = mutualInformation.score(x, y, z, ui);
438 double calc_expo1 = -Ixyz_ui * M_LN2;
439 // if exponential are too high or to low, crop them at _maxLog_
440 if (calc_expo1 > _maxLog_) {
441 Pnv = 0.0;
442 } else if (calc_expo1 < -_maxLog_) {
443 Pnv = 1.0;
444 } else {
445 Pnv = 1 / (1 + std::exp(calc_expo1));
446 }
447
448 // Computing Pb
449 const double Ixz_ui = mutualInformation.score(x, z, ui);
450 const double Iyz_ui = mutualInformation.score(y, z, ui);
451
452 calc_expo1 = -(Ixz_ui - Ixy_ui) * M_LN2;
453 double calc_expo2 = -(Iyz_ui - Ixy_ui) * M_LN2;
454
455 // if exponential are too high or to low, crop them at _maxLog_
456 if (calc_expo1 > _maxLog_ || calc_expo2 > _maxLog_) {
457 Pb = 0.0;
458 } else if (calc_expo1 < -_maxLog_ && calc_expo2 < -_maxLog_) {
459 Pb = 1.0;
460 } else {
461 double expo1, expo2;
462 if (calc_expo1 < -_maxLog_) {
463 expo1 = 0.0;
464 } else {
465 expo1 = std::exp(calc_expo1);
466 }
467 if (calc_expo2 < -_maxLog_) {
468 expo2 = 0.0;
469 } else {
470 expo2 = std::exp(calc_expo2);
471 }
472 Pb = 1 / (1 + expo1 + expo2);
473 }
474
475 // Getting max(min(Pnv, pb))
476 const double min_pnv_pb = std::min(Pnv, Pb);
477 if (min_pnv_pb > maxP) {
478 maxP = min_pnv_pb;
479 maxZ = z;
480 }
481 } // if z not in (x, y)
482 } // for z in graph.nodes
483 // storing best z in rank_
484 CondRanking final;
485 auto tup = new CondThreePoints{x, y, maxZ, ui};
486 final.first = tup;
487 final.second = maxP;
488 rank.insert(final);
489 }
490
493 std::vector< Ranking > SimpleMiic::unshieldedTriples_(
494 const MixedGraph& graph,
495 CorrectedMutualInformation& mutualInformation,
496 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet) {
497 std::vector< Ranking > triples;
498 for (NodeId z: graph) {
499 for (NodeId x: graph.neighbours(z)) {
500 for (NodeId y: graph.neighbours(z)) {
501 if (y < x && !graph.existsEdge(x, y)) {
502 std::vector< NodeId > ui;
503 std::pair< NodeId, NodeId > key = {x, y};
504 std::pair< NodeId, NodeId > rev_key = {y, x};
505 if (sepSet.exists(key)) {
506 ui = sepSet[key];
507 } else if (sepSet.exists(rev_key)) {
508 ui = sepSet[rev_key];
509 }
510 // remove z from ui if it's present
511 const auto iter_z_place = std::find(ui.begin(), ui.end(), z);
512 if (iter_z_place != ui.end()) { ui.erase(iter_z_place); }
513
514 double Ixyz_ui = mutualInformation.score(x, y, z, ui);
515 Ranking triple;
516 auto tup = new ThreePoints{x, y, z};
517 triple.first = tup;
518 triple.second = Ixyz_ui;
519 triples.push_back(triple);
520 }
521 }
522 }
523 }
524 std::sort(triples.begin(), triples.end(), GreaterAbsPairOn2nd());
525 return triples;
526 }
527
530 std::vector< ProbabilisticRanking > SimpleMiic::unshieldedTriplesMiic_(
531 const MixedGraph& graph,
532 CorrectedMutualInformation& mutualInformation,
533 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet,
534 HashTable< std::pair< NodeId, NodeId >, char >& marks) {
535 std::vector< ProbabilisticRanking > triples;
536 for (NodeId z: graph) {
537 for (NodeId x: graph.neighbours(z)) {
538 for (NodeId y: graph.neighbours(z)) {
539 if (y < x && !graph.existsEdge(x, y)) {
540 std::vector< NodeId > ui;
541 std::pair< NodeId, NodeId > key = {x, y};
542 std::pair< NodeId, NodeId > rev_key = {y, x};
543 if (sepSet.exists(key)) {
544 ui = sepSet[key];
545 } else if (sepSet.exists(rev_key)) {
546 ui = sepSet[rev_key];
547 }
548 // remove z from ui if it's present
549 const auto iter_z_place = std::find(ui.begin(), ui.end(), z);
550 if (iter_z_place != ui.end()) { ui.erase(iter_z_place); }
551
552 const double Ixyz_ui = mutualInformation.score(x, y, z, ui);
553 auto tup = new ThreePoints{x, y, z};
554 ProbabilisticRanking triple{tup, Ixyz_ui, 0.5, 0.5};
555 triples.push_back(triple);
556 if (!marks.exists({x, z})) { marks.insert({x, z}, 'o'); }
557 if (!marks.exists({z, x})) { marks.insert({z, x}, 'o'); }
558 if (!marks.exists({y, z})) { marks.insert({y, z}, 'o'); }
559 if (!marks.exists({z, y})) { marks.insert({z, y}, 'o'); }
560 }
561 }
562 }
563 }
564 triples = updateProbaTriples_(graph, triples);
565 std::sort(triples.begin(), triples.end(), GreaterTupleOnLast());
566 return triples;
567 }
568
570 std::vector< ProbabilisticRanking >
572 std::vector< ProbabilisticRanking > probaTriples) {
573 for (auto& triple: probaTriples) {
574 NodeId x, y, z;
575 x = std::get< 0 >(*std::get< 0 >(triple));
576 y = std::get< 1 >(*std::get< 0 >(triple));
577 z = std::get< 2 >(*std::get< 0 >(triple));
578 const double Ixyz = std::get< 1 >(triple);
579 double Pxz = std::get< 2 >(triple);
580 double Pyz = std::get< 3 >(triple);
581
582 if (Ixyz <= 0) {
583 const double expo = std::exp(Ixyz);
584 const double P0 = (1 + expo) / (1 + 3 * expo);
585 // distinguish between the initialization and the update process
586 if (Pxz == Pyz && Pyz == 0.5) {
587 std::get< 2 >(triple) = P0;
588 std::get< 3 >(triple) = P0;
589 } else {
590 if (graph.existsArc(x, z) && Pxz >= P0) {
591 std::get< 3 >(triple) = Pxz * (1 / (1 + expo) - 0.5) + 0.5;
592 } else if (graph.existsArc(y, z) && Pyz >= P0) {
593 std::get< 2 >(triple) = Pyz * (1 / (1 + expo) - 0.5) + 0.5;
594 }
595 }
596 } else {
597 const double expo = std::exp(-Ixyz);
598 if (graph.existsArc(x, z) && Pxz >= 0.5) {
599 std::get< 3 >(triple) = Pxz * (1 / (1 + expo) - 0.5) + 0.5;
600 } else if (graph.existsArc(y, z) && Pyz >= 0.5) {
601 std::get< 2 >(triple) = Pyz * (1 / (1 + expo) - 0.5) + 0.5;
602 }
603 }
604 }
605 std::sort(probaTriples.begin(), probaTriples.end(), GreaterTupleOnLast());
606 return probaTriples;
607 }
608
612
614 MixedGraph essentialGraph = learnMixedStructure(I, initialGraph);
615
616 // orientate remaining edges
617 const Sequence< NodeId > order = essentialGraph.topologicalOrder();
618
619 // first, forbidden arcs force arc in the other direction
620 for (NodeId x: order) {
621 const auto nei_x = essentialGraph.neighbours(x);
622 for (NodeId y: nei_x)
623 if (isForbidenArc_(x, y)) {
624 essentialGraph.eraseEdge(Edge(x, y));
625 if (isForbidenArc_(y, x)) {
626 // GUM_TRACE("Neither arc allowed for edge (" << x << "," << y << ")")
627 } else {
628 // GUM_TRACE("Forced orientation : " << y << "->" << x)
629 essentialGraph.addArc(y, x);
630 }
631 } else if (isForbidenArc_(y, x)) {
632 essentialGraph.eraseEdge(Edge(x, y));
633 // GUM_TRACE("Forced orientation : " << x << "->" << y)
634 essentialGraph.addArc(x, y);
635 }
636 }
637
638 // then propagates existing orientations thanks to Meek rules
639 bool newOrientation = true;
640 while (newOrientation) {
641 newOrientation = false;
642 for (NodeId x: order) {
643 if (!essentialGraph.parents(x).empty()) {
644 newOrientation |= propagatesRemainingOrientableEdges_(essentialGraph, x);
645 }
646 }
647 }
648 return essentialGraph;
649 }
650
654 MixedGraph essentialGraph = learnMixedStructure(I, initialGraph);
655 // orientate remaining edges
656
657 const Sequence< NodeId > order = essentialGraph.topologicalOrder();
658
659 // first, forbidden arcs force arc in the other direction
660 for (NodeId x: order) {
661 const auto nei_x = essentialGraph.neighbours(x);
662 for (NodeId y: nei_x)
663 if (isForbidenArc_(x, y)) {
664 essentialGraph.eraseEdge(Edge(x, y));
665 if (isForbidenArc_(y, x)) {
666 // GUM_TRACE("Neither arc allowed for edge (" << x << "," << y << ")")
667 } else {
668 // GUM_TRACE("Forced orientation : " << y << "->" << x)
669 essentialGraph.addArc(y, x);
670 }
671 } else if (isForbidenArc_(y, x)) {
672 essentialGraph.eraseEdge(Edge(x, y));
673 // GUM_TRACE("Forced orientation : " << x << "->" << y)
674 essentialGraph.addArc(x, y);
675 }
676 }
677 // GUM_TRACE(essentialGraph.toDot());
678
679 // first, propagate existing orientations
680 bool newOrientation = true;
681 while (newOrientation) {
682 newOrientation = false;
683 for (NodeId x: order) {
684 if (!essentialGraph.parents(x).empty()) {
685 newOrientation |= propagatesRemainingOrientableEdges_(essentialGraph, x);
686 }
687 }
688 }
689 // GUM_TRACE(essentialGraph.toDot());
691 // GUM_TRACE(essentialGraph.toDot());
692
693 // then decide the orientation for double arcs
694 for (NodeId x: order)
695 for (NodeId y: essentialGraph.parents(x))
696 if (essentialGraph.parents(y).contains(x)) {
697 // GUM_TRACE(" + Resolving double arcs (poorly)")
698 essentialGraph.eraseArc(Arc(y, x));
699 }
700
701 DAG dag;
702 for (auto node: essentialGraph) {
703 dag.addNodeWithId(node);
704 }
705 for (const Arc& arc: essentialGraph.arcs()) {
706 dag.addArc(arc.tail(), arc.head());
707 }
708
709 return dag;
710 }
711
713 // no cycle
714 if (_existsDirectedPath_(graph, xj, xi)) {
715 // GUM_TRACE("cycle(" << xi << "-" << xj << ")")
716 return false;
717 }
718
719 // R1
720 if (!(graph.parents(xi) - graph.boundary(xj)).empty()) {
721 // GUM_TRACE("R1(" << xi << "-" << xj << ")")
722 return true;
723 }
724
725 // R2
726 if (_existsDirectedPath_(graph, xi, xj)) {
727 // GUM_TRACE("R2(" << xi << "-" << xj << ")")
728 return true;
729 }
730
731 // R3
732 int nbr = 0;
733 for (const auto p: graph.parents(xj)) {
734 if (graph.mixedOrientedPath(xi, p).has_value()) {
735 nbr += 1;
736 if (nbr == 2) {
737 // GUM_TRACE("R3(" << xi << "-" << xj << ")")
738 return true;
739 }
740 }
741 }
742 return false;
743 }
744
746 // then decide the orientation for remaining edges
747 while (!essentialGraph.edges().empty()) {
748 const auto& edge = *(essentialGraph.edges().begin());
749 NodeId root = edge.first();
750 Size size_children_root = essentialGraph.children(root).size();
751 NodeSet visited;
752 NodeSet stack{root};
753 // check the best root for the set of neighbours
754 while (!stack.empty()) {
755 NodeId next = *(stack.begin());
756 stack.erase(next);
757 if (visited.contains(next)) continue;
758 if (essentialGraph.children(next).size() > size_children_root) {
759 size_children_root = essentialGraph.children(next).size();
760 root = next;
761 }
762 for (const auto n: essentialGraph.neighbours(next))
763 if (!stack.contains(n) && !visited.contains(n)) stack.insert(n);
764 visited.insert(next);
765 }
766 // orientation now
767 visited.clear();
768 stack.clear();
769 stack.insert(root);
770 while (!stack.empty()) {
771 NodeId next = *(stack.begin());
772 stack.erase(next);
773 if (visited.contains(next)) continue;
774 const auto nei = essentialGraph.neighbours(next);
775 for (const auto n: nei) {
776 if (!stack.contains(n) && !visited.contains(n)) stack.insert(n);
777 // GUM_TRACE(" + amap reasonably orientation for " << n << "->" << next);
778 if (propagatesRemainingOrientableEdges_(essentialGraph, next)) continue;
779 else essentialGraph.eraseEdge(Edge(n, next));
780 essentialGraph.addArc(n, next);
781 }
782 visited.insert(next);
783 }
784 }
785 }
786
789 bool res = false;
790 const auto neighbours = graph.neighbours(xj);
791 for (auto& xi: neighbours) {
792 bool i_j = isOrientable_(graph, xi, xj);
793 bool j_i = isOrientable_(graph, xj, xi);
794 if (i_j || j_i) {
795 // GUM_TRACE(" + Removing edge (" << xi << "," << xj << ")")
796 graph.eraseEdge(Edge(xi, xj));
797 res = true;
798 }
799 if (i_j) {
800 // GUM_TRACE(" + add arc (" << xi << "," << xj << ")")
801 graph.addArc(xi, xj);
803 }
804 if (j_i) {
805 // GUM_TRACE(" + add arc (" << xi << "," << xj << ")")
806 graph.addArc(xj, xi);
808 }
809 if (i_j && j_i) {
810 GUM_TRACE(" + add arc (" << xi << "," << xj << ")")
811 _latentCouples_.emplace_back(xi, xj);
812 }
813 }
814
815 return res;
816 }
817
819 const std::vector< Arc > SimpleMiic::latentVariables() const {
820 // GUM_CHECKPOINT
821 return _latentCouples_;
822 }
823
825 template < GUM_Numeric GUM_SCALAR, typename GRAPH_CHANGES_SELECTOR, typename PARAM_ESTIMATOR >
826 BayesNet< GUM_SCALAR > SimpleMiic::learnBN(GRAPH_CHANGES_SELECTOR& selector,
827 PARAM_ESTIMATOR& estimator,
828 DAG initial_dag) {
830 learnStructure(selector, initial_dag));
831 }
832
833 void SimpleMiic::addConstraints(HashTable< std::pair< NodeId, NodeId >, char > constraints) {
834 this->_initialMarks_ = constraints;
835 }
836
838 const NodeId n1,
839 const NodeId n2) {
840 for (const auto parent: graph.parents(n2)) {
841 if (graph.existsArc(parent,
842 n2)) // if there is a double arc, pass
843 continue;
844 if (parent == n1) // trivial directed path => not recognized
845 continue;
846 if (_existsDirectedPath_(graph, n1, parent)) return true;
847 }
848 return false;
849 }
850
852 const NodeId n1,
853 const NodeId n2) {
854 // not recursive version => use a FIFO for simulating the recursion
855 List< NodeId > nodeFIFO;
856 // mark[node] = successor if visited, else mark[node] does not exist
857 Set< NodeId > mark;
858
859 mark.insert(n2);
860 nodeFIFO.pushBack(n2);
861
862 NodeId current;
863
864 while (!nodeFIFO.empty()) {
865 current = nodeFIFO.front();
866 nodeFIFO.popFront();
867
868 // check the parents
869 for (const auto new_one: graph.parents(current)) {
870 if (graph.existsArc(current,
871 new_one)) // if there is a double arc, pass
872 continue;
873
874 if (new_one == n1) { return true; }
875
876 if (mark.exists(new_one)) // if this node is already marked, do not
877 continue; // check it again
878
879 mark.insert(new_one);
880 nodeFIFO.pushBack(new_one);
881 }
882 }
883
884 return false;
885 }
886
887 void
889 HashTable< std::pair< NodeId, NodeId >, char >& marks,
890 NodeId x,
891 NodeId y,
892 NodeId z,
893 double p1,
894 double p2) {
895 // v-structure discovery
896 if (marks[{x, z}] == 'o' && marks[{y, z}] == 'o') { // If x-z-y
898 graph.eraseEdge(Edge(x, z));
899 graph.addArc(x, z);
900 // GUM_TRACE("1.a Removing edge (" << x << "," << z << ")")
901 // GUM_TRACE("1.a Adding arc (" << x << "," << z << ")")
902 marks[{x, z}] = '>';
903 if (graph.existsArc(z, x) && _isNotLatentCouple_(z, x)) {
904 GUM_TRACE("Adding latent couple (" << z << "," << x << ")")
905 _latentCouples_.emplace_back(z, x);
906 }
907 if (!_arcProbas_.exists(Arc(x, z))) _arcProbas_.insert(Arc(x, z), p1);
908 } else {
909 graph.eraseEdge(Edge(x, z));
910 // GUM_TRACE("1.b Adding arc (" << x << "," << z << ")")
912 graph.addArc(z, x);
913 // GUM_TRACE("1.b Removing edge (" << x << "," << z << ")")
914 marks[{z, x}] = '>';
915 }
916 }
917
919 graph.eraseEdge(Edge(y, z));
920 graph.addArc(y, z);
921 // GUM_TRACE("1.c Removing edge (" << y << "," << z << ")")
922 // GUM_TRACE("1.c Adding arc (" << y << "," << z << ")")
923 marks[{y, z}] = '>';
924 if (graph.existsArc(z, y) && _isNotLatentCouple_(z, y)) {
925 // GUM_TRACE("Adding latent couple (" << z << "," << y << ")")
926 _latentCouples_.emplace_back(z, y);
927 }
928 if (!_arcProbas_.exists(Arc(y, z))) _arcProbas_.insert(Arc(y, z), p2);
929 } else {
930 graph.eraseEdge(Edge(y, z));
931 // GUM_TRACE("1.d Removing edge (" << y << "," << z << ")")
933 graph.addArc(z, y);
934 // GUM_TRACE("1.d Adding arc (" << z << "," << y << ")")
935 marks[{z, y}] = '>';
936 }
937 }
938 } else if (marks[{x, z}] == '>' && marks[{y, z}] == 'o') { // If x->z-y
940 graph.eraseEdge(Edge(y, z));
941 graph.addArc(y, z);
942 // GUM_TRACE("2.a Removing edge (" << y << "," << z << ")")
943 // GUM_TRACE("2.a Adding arc (" << y << "," << z << ")")
944 marks[{y, z}] = '>';
945 if (graph.existsArc(z, y) && _isNotLatentCouple_(z, y)) {
946 GUM_TRACE("Adding latent couple (" << z << "," << y << ")")
947 _latentCouples_.emplace_back(z, y);
948 }
949 if (!_arcProbas_.exists(Arc(y, z))) _arcProbas_.insert(Arc(y, z), p2);
950 } else {
951 graph.eraseEdge(Edge(y, z));
952 // GUM_TRACE("2.b Removing edge (" << y << "," << z << ")")
954 graph.addArc(z, y);
955 // GUM_TRACE("2.b Adding arc (" << y << "," << z << ")")
956 marks[{z, y}] = '>';
957 }
958 }
959 } else if (marks[{y, z}] == '>' && marks[{x, z}] == 'o') {
961 graph.eraseEdge(Edge(x, z));
962 graph.addArc(x, z);
963 // GUM_TRACE("3.a Removing edge (" << x << "," << z << ")")
964 // GUM_TRACE("3.a Adding arc (" << x << "," << z << ")")
965 marks[{x, z}] = '>';
966 if (graph.existsArc(z, x) && _isNotLatentCouple_(z, x)) {
967 GUM_TRACE("Adding latent couple (" << z << "," << x << ")")
968 _latentCouples_.emplace_back(z, x);
969 }
970 if (!_arcProbas_.exists(Arc(x, z))) _arcProbas_.insert(Arc(x, z), p1);
971 } else {
972 graph.eraseEdge(Edge(x, z));
973 // GUM_TRACE("3.b Removing edge (" << x << "," << z << ")")
975 graph.addArc(z, x);
976 // GUM_TRACE("3.b Adding arc (" << x << "," << z << ")")
977 marks[{z, x}] = '>';
978 }
979 }
980 }
981 }
982
985 HashTable< std::pair< NodeId, NodeId >, char >& marks,
986 NodeId x,
987 NodeId y,
988 NodeId z,
989 double p1,
990 double p2) {
991 // orientation propagation
992 if (marks[{x, z}] == '>' && marks[{y, z}] == 'o' && marks[{z, y}] != '-') {
993 graph.eraseEdge(Edge(z, y));
994 // std::cout << "4. Removing edge (" << z << "," << y << ")" <<
995 // std::endl;
996 if (!_existsDirectedPath_(graph, y, z) && graph.parents(y).empty()) {
997 graph.addArc(z, y);
998 // GUM_TRACE("4.a Adding arc (" << z << "," << y << ")")
999 marks[{z, y}] = '>';
1000 marks[{y, z}] = '-';
1001 if (!_arcProbas_.exists(Arc(z, y))) _arcProbas_.insert(Arc(z, y), p2);
1002 } else if (!_existsDirectedPath_(graph, z, y) && graph.parents(z).empty()) {
1003 graph.addArc(y, z);
1004 GUM_TRACE("4.b Adding arc (" << y << "," << z << ")")
1005 marks[{z, y}] = '-';
1006 marks[{y, z}] = '>';
1007 _latentCouples_.emplace_back(y, z);
1008 if (!_arcProbas_.exists(Arc(y, z))) _arcProbas_.insert(Arc(y, z), p2);
1009 } else if (!_existsDirectedPath_(graph, y, z)) {
1010 graph.addArc(z, y);
1011 // GUM_TRACE("4.c Adding arc (" << z << "," << y << ")")
1012 marks[{z, y}] = '>';
1013 marks[{y, z}] = '-';
1014 if (!_arcProbas_.exists(Arc(z, y))) _arcProbas_.insert(Arc(z, y), p2);
1015 } else if (!_existsDirectedPath_(graph, z, y)) {
1016 graph.addArc(y, z);
1017 GUM_TRACE("4.d Adding arc (" << y << "," << z << ")")
1018 _latentCouples_.emplace_back(y, z);
1019 marks[{z, y}] = '-';
1020 marks[{y, z}] = '>';
1021 if (!_arcProbas_.exists(Arc(y, z))) _arcProbas_.insert(Arc(y, z), p2);
1022 }
1023 } else if (marks[{y, z}] == '>' && marks[{x, z}] == 'o' && marks[{z, x}] != '-') {
1024 graph.eraseEdge(Edge(z, x));
1025 // GUM_TRACE("5. Removing edge (" << z << "," << x << ")")
1026 if (!_existsDirectedPath_(graph, x, z) && graph.parents(x).empty()) {
1027 graph.addArc(z, x);
1028 // GUM_TRACE("5.a Adding arc (" << z << "," << x << ")")
1029 marks[{z, x}] = '>';
1030 marks[{x, z}] = '-';
1031 if (!_arcProbas_.exists(Arc(z, x))) _arcProbas_.insert(Arc(z, x), p1);
1032 } else if (!_existsDirectedPath_(graph, z, x) && graph.parents(z).empty()) {
1033 graph.addArc(x, z);
1034 GUM_TRACE("5.b Adding arc (" << x << "," << z << ")")
1035 marks[{z, x}] = '-';
1036 marks[{x, z}] = '>';
1037 _latentCouples_.emplace_back(x, z);
1038 if (!_arcProbas_.exists(Arc(x, z))) _arcProbas_.insert(Arc(x, z), p1);
1039 } else if (!_existsDirectedPath_(graph, x, z)) {
1040 graph.addArc(z, x);
1041 // GUM_TRACE("5.c Adding arc (" << z << "," << x << ")")
1042 marks[{z, x}] = '>';
1043 marks[{x, z}] = '-';
1044 if (!_arcProbas_.exists(Arc(z, x))) _arcProbas_.insert(Arc(z, x), p1);
1045 } else if (!_existsDirectedPath_(graph, z, x)) {
1046 graph.addArc(x, z);
1047 GUM_TRACE("5.d Adding arc (" << x << "," << z << ")")
1048 marks[{z, x}] = '-';
1049 marks[{x, z}] = '>';
1050 _latentCouples_.emplace_back(x, z);
1051 if (!_arcProbas_.exists(Arc(x, z))) _arcProbas_.insert(Arc(x, z), p1);
1052 }
1053 }
1054 }
1055
1057 const auto& lbeg = _latentCouples_.begin();
1058 const auto& lend = _latentCouples_.end();
1059
1060 return (std::find(lbeg, lend, Arc(x, y)) == lend)
1061 && (std::find(lbeg, lend, Arc(y, x)) == lend);
1062 }
1063
1065 return (_initialMarks_.exists({x, y}) && _initialMarks_[{x, y}] == '-');
1066 }
1067 } /* namespace learning */
1068
1069} /* namespace gum */
A class that, given a structure and a parameter estimator returns a full Bayes net.
The SimpleMiic algorithm.
Size current_step_
The current step.
ApproximationScheme(bool verbosity=false)
const NodeSet & parents(NodeId id) const
returns the set of nodes with arc ingoing to a given node
NodeSet children(const NodeSet &ids) const
returns the set of nodes which consists in the node and its parents returns the set of children of a ...
virtual void eraseArc(const Arc &arc)
removes an arc from the ArcGraphPart
const ArcSet & arcs() const
returns the set of arcs stored within the ArcGraphPart
The base class for all directed edges.
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
void addArc(const NodeId tail, const NodeId head) override
insert a new arc into the directed graph
Definition diGraph_inl.h:59
Sequence< NodeId > topologicalOrder() const
Build and return a topological order.
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
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:640
iterator begin()
Returns an unsafe iterator pointing to the beginning of the hashtable.
const iterator & end() noexcept
Returns the unsafe iterator pointing to the end of the hashtable.
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.
Generic doubly linked lists.
Definition list.h:378
Val & front() const
Returns a reference to first element of a list, if any.
Definition list_tpl.h:1694
Val & pushBack(const Val &val)
Inserts a new element (a copy) at the end of the chained list.
Definition list_tpl.h:1481
bool empty() const noexcept
Returns a boolean indicating whether the chained list is empty.
Definition list_tpl.h:1822
void popFront()
Removes the first element of a List, if any.
Definition list_tpl.h:1816
Base class for mixed graphs.
Definition mixedGraph.h:146
virtual void addNodeWithId(const NodeId id)
try to insert a node with the given id
bool contains(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:468
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:504
void clear()
Removes all the elements, if any, from the set.
Definition set_tpl.h:315
bool empty() const noexcept
Indicates whether the set is the empty set.
Definition set_tpl.h:613
iterator begin() const
The usual unsafe begin iterator to parse the set.
Definition set_tpl.h:409
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:510
void erase(const Key &k)
Erases an element from the set.
Definition set_tpl.h:553
Size size() const noexcept
Returns the number of elements in the set.
Definition set_tpl.h:607
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)
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...
bool isOrientable_(const MixedGraph &graph, NodeId xi, NodeId xj) const
const std::vector< Arc > latentVariables() const
get the list of arcs hiding latent variables
const std::vector< NodeId > _emptySet_
an empty conditioning set
Definition SimpleMiic.h:290
MixedGraph learnMixedStructure(CorrectedMutualInformation &mutualInformation, MixedGraph graph)
learns the structure of an Essential Graph
SimpleMiic & operator=(const SimpleMiic &from)
copy operator
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.
void _propagatingOrientationMiic_(MixedGraph &graph, HashTable< std::pair< NodeId, NodeId >, char > &marks, NodeId x, NodeId y, NodeId z, double p1, double p2)
void iteration_(CorrectedMutualInformation &mutualInformation, MixedGraph &graph, HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > &sepSet, Heap< CondRanking, GreaterPairOn2nd > &rank)
Iteration phase.
bool _isNotLatentCouple_(NodeId x, NodeId y)
int _maxLog_
Fixes the maximum log that we accept in exponential computations.
Definition SimpleMiic.h:288
void _orientingVstructureMiic_(MixedGraph &graph, HashTable< std::pair< NodeId, NodeId >, char > &marks, NodeId x, NodeId y, NodeId z, double p1, double p2)
~SimpleMiic() override
destructor
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}...
ArcProperty< double > _arcProbas_
Storing the probabilities for each arc set in the graph.
Definition SimpleMiic.h:298
std::vector< Arc > _latentCouples_
an empty vector of arcs
Definition SimpleMiic.h:292
static bool _existsDirectedPath_(const MixedGraph &graph, NodeId n1, NodeId n2)
checks for directed paths in a graph, consider double arcs like edges
HashTable< std::pair< NodeId, NodeId >, char > _initialMarks_
Initial marks for the orientation phase, used to convey constraints.
Definition SimpleMiic.h:301
SimpleMiic()
default constructor
Size _size_
size of the database
Definition SimpleMiic.h:295
void propagatesOrientationInChainOfRemainingEdges_(MixedGraph &graph)
heuristic for remaining edges when everything else has been tried
void orientationLatents_(CorrectedMutualInformation &mutualInformation, MixedGraph &graph, const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > &sepSet)
variant trying to propagate both orientations in a bidirected arc
MixedGraph learnPDAG(CorrectedMutualInformation &mutualInformation, MixedGraph graph)
learns the structure of an Essential Graph
bool propagatesRemainingOrientableEdges_(MixedGraph &graph, NodeId xj)
Tries to orient edges incident to xj using Meek rules.
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
bool isForbidenArc_(NodeId x, NodeId y) const
void addConstraints(HashTable< std::pair< NodeId, NodeId >, char > constraints)
Set a ensemble of constraints for the orientation phase.
BayesNet< GUM_SCALAR > learnBN(GRAPH_CHANGES_SELECTOR &selector, PARAM_ESTIMATOR &estimator, DAG initial_dag=DAG())
learns the structure and the parameters of a BN
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...
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}...
std::vector< ProbabilisticRanking > updateProbaTriples_(const MixedGraph &graph, std::vector< ProbabilisticRanking > probaTriples)
Updates orientation probabilities for the remaining unoriented triples.
void initiation_(CorrectedMutualInformation &mutualInformation, MixedGraph &graph, HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > > &sepSet, Heap< CondRanking, GreaterPairOn2nd > &rank)
Initiation phase.
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< NodeId > NodeSet
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: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.