87 ApproximationScheme::operator=(from);
93 ApproximationScheme::operator=(std::move(from));
131 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet,
137 for (
const Edge& edge: edges) {
140 double Ixy = mutualInformation.
score(x, y);
143 graph.eraseEdge(edge);
144 sepSet.insert(std::make_pair(x, y),
_emptySet_);
165 HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet,
174 while (rank.
top().second > 0.5) {
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)));
183 const double i_xy_ui = mutualInformation.
score(x, y, ui);
186 sepSet.insert(std::make_pair(x, y), std::move(ui));
217 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet) {
219 Size steps_orient = triples.size();
227 while (i < triples.size()) {
231 x = std::get< 0 >(*triple.first);
232 y = std::get< 1 >(*triple.first);
233 z = std::get< 2 >(*triple.first);
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)) {
240 }
else if (sepSet.exists(rev_key)) {
241 ui = sepSet[rev_key];
243 double Ixyz_ui = triple.second;
247 if (std::find(ui.begin(), ui.end(), z) == ui.end()) {
249 if ((
graph.existsArc(x, z) ||
graph.existsArc(z, x))
250 && (
graph.existsArc(y, z) ||
graph.existsArc(z, y))) {
257 if (
graph.existsArc(z, x)) {
261 else graph.addArc(x, z);
265 if (
graph.hasDirectedPath(z, x)) {
272 if (
graph.existsArc(z, y)) {
276 else graph.addArc(y, z);
280 if (
graph.hasDirectedPath(z, y)) {
299 if (
graph.existsArc(x, z) && !
graph.existsArc(z, y) && !
graph.existsArc(y, z)) {
303 if (
graph.hasDirectedPath(y, z)) {
310 if (
graph.existsArc(y, z) && !
graph.existsArc(z, x) && !
graph.existsArc(x, z)) {
314 if (
graph.hasDirectedPath(x, z)) {
338 graph.eraseArc(
Arc(arc.head(), arc.tail()));
346 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet) {
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);
361 std::vector< ProbabilisticRanking > proba_triples
364 const Size steps_orient = proba_triples.size();
368 if (steps_orient > 0) { best = proba_triples[0]; }
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));
375 const double i3 = std::get< 1 >(best);
377 const double p1 = std::get< 2 >(best);
378 const double p2 = std::get< 3 >(best);
385 delete std::get< 0 >(best);
386 proba_triples.erase(proba_triples.begin());
390 if (!proba_triples.empty()) best = proba_triples[0];
404 graph.eraseArc(
Arc(iter->head(), iter->tail()));
407 graph.addArc(iter->head(), iter->tail());
408 graph.eraseArc(
Arc(iter->tail(), iter->head()));
409 *iter =
Arc(iter->head(), iter->tail());
419 const std::vector< NodeId >& ui,
428 const double Ixy_ui = mutualInformation.
score(x, y, ui);
432 if (z != x && z != y && std::find(ui.begin(), ui.end(), z) == ui.end()) {
437 const double Ixyz_ui = mutualInformation.
score(x, y, z, ui);
438 double calc_expo1 = -Ixyz_ui *
M_LN2;
442 }
else if (calc_expo1 < -
_maxLog_) {
445 Pnv = 1 / (1 + std::exp(calc_expo1));
449 const double Ixz_ui = mutualInformation.
score(x, z, ui);
450 const double Iyz_ui = mutualInformation.
score(y, z, ui);
452 calc_expo1 = -(Ixz_ui - Ixy_ui) *
M_LN2;
453 double calc_expo2 = -(Iyz_ui - Ixy_ui) *
M_LN2;
465 expo1 = std::exp(calc_expo1);
470 expo2 = std::exp(calc_expo2);
472 Pb = 1 / (1 + expo1 + expo2);
476 const double min_pnv_pb = std::min(Pnv, Pb);
477 if (min_pnv_pb > maxP) {
496 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet) {
497 std::vector< Ranking > triples;
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)) {
507 }
else if (sepSet.exists(rev_key)) {
508 ui = sepSet[rev_key];
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); }
514 double Ixyz_ui = mutualInformation.
score(x, y, z, ui);
518 triple.second = Ixyz_ui;
519 triples.push_back(triple);
533 const HashTable< std::pair< NodeId, NodeId >, std::vector< NodeId > >& sepSet,
534 HashTable< std::pair< NodeId, NodeId >,
char >& marks) {
535 std::vector< ProbabilisticRanking > triples;
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)) {
545 }
else if (sepSet.exists(rev_key)) {
546 ui = sepSet[rev_key];
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); }
552 const double Ixyz_ui = mutualInformation.
score(x, y, z, ui);
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'); }
570 std::vector< ProbabilisticRanking >
572 std::vector< ProbabilisticRanking > probaTriples) {
573 for (
auto& triple: probaTriples) {
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);
583 const double expo = std::exp(Ixyz);
584 const double P0 = (1 + expo) / (1 + 3 * expo);
586 if (Pxz == Pyz && Pyz == 0.5) {
587 std::get< 2 >(triple) = P0;
588 std::get< 3 >(triple) = P0;
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;
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;
621 const auto nei_x = essentialGraph.
neighbours(x);
629 essentialGraph.
addArc(y, x);
634 essentialGraph.
addArc(x, y);
639 bool newOrientation =
true;
640 while (newOrientation) {
641 newOrientation =
false;
648 return essentialGraph;
661 const auto nei_x = essentialGraph.
neighbours(x);
669 essentialGraph.
addArc(y, x);
674 essentialGraph.
addArc(x, y);
680 bool newOrientation =
true;
681 while (newOrientation) {
682 newOrientation =
false;
702 for (
auto node: essentialGraph) {
705 for (
const Arc& arc: essentialGraph.
arcs()) {
706 dag.
addArc(arc.tail(), arc.head());
720 if (!(
graph.parents(xi) -
graph.boundary(xj)).empty()) {
733 for (
const auto p:
graph.parents(xj)) {
734 if (
graph.mixedOrientedPath(xi, p).has_value()) {
748 const auto& edge = *(essentialGraph.
edges().begin());
749 NodeId root = edge.first();
754 while (!stack.
empty()) {
757 if (visited.
contains(next))
continue;
758 if (essentialGraph.
children(next).
size() > size_children_root) {
759 size_children_root = essentialGraph.
children(next).
size();
762 for (
const auto n: essentialGraph.
neighbours(next))
770 while (!stack.
empty()) {
773 if (visited.
contains(next))
continue;
774 const auto nei = essentialGraph.
neighbours(next);
775 for (
const auto n: nei) {
780 essentialGraph.
addArc(n, next);
790 const auto neighbours =
graph.neighbours(xj);
791 for (
auto& xi: neighbours) {
801 graph.addArc(xi, xj);
806 graph.addArc(xj, xi);
810 GUM_TRACE(
" + add arc (" << xi <<
"," << xj <<
")")
825 template < GUM_Numeric GUM_SCALAR,
typename GRAPH_CHANGES_
SELECTOR,
typename PARAM_ESTIMATOR >
827 PARAM_ESTIMATOR& estimator,
840 for (
const auto parent:
graph.parents(n2)) {
841 if (
graph.existsArc(parent,
864 while (!nodeFIFO.
empty()) {
865 current = nodeFIFO.
front();
869 for (
const auto new_one:
graph.parents(current)) {
870 if (
graph.existsArc(current,
874 if (new_one == n1) {
return true; }
889 HashTable< std::pair< NodeId, NodeId >,
char >& marks,
896 if (marks[{x, z}] ==
'o' && marks[{y, z}] ==
'o') {
904 GUM_TRACE(
"Adding latent couple (" << z <<
"," << x <<
")")
938 }
else if (marks[{x, z}] ==
'>' && marks[{y, z}] ==
'o') {
946 GUM_TRACE(
"Adding latent couple (" << z <<
"," << y <<
")")
959 }
else if (marks[{y, z}] ==
'>' && marks[{x, z}] ==
'o') {
967 GUM_TRACE(
"Adding latent couple (" << z <<
"," << x <<
")")
985 HashTable< std::pair< NodeId, NodeId >,
char >& marks,
992 if (marks[{x, z}] ==
'>' && marks[{y, z}] ==
'o' && marks[{z, y}] !=
'-') {
1000 marks[{y, z}] =
'-';
1004 GUM_TRACE(
"4.b Adding arc (" << y <<
"," << z <<
")")
1005 marks[{z, y}] =
'-';
1006 marks[{y, z}] =
'>';
1012 marks[{z, y}] =
'>';
1013 marks[{y, z}] =
'-';
1017 GUM_TRACE(
"4.d Adding arc (" << y <<
"," << z <<
")")
1019 marks[{z, y}] =
'-';
1020 marks[{y, z}] =
'>';
1023 }
else if (marks[{y, z}] ==
'>' && marks[{x, z}] ==
'o' && marks[{z, x}] !=
'-') {
1029 marks[{z, x}] =
'>';
1030 marks[{x, z}] =
'-';
1034 GUM_TRACE(
"5.b Adding arc (" << x <<
"," << z <<
")")
1035 marks[{z, x}] =
'-';
1036 marks[{x, z}] =
'>';
1042 marks[{z, x}] =
'>';
1043 marks[{x, z}] =
'-';
1047 GUM_TRACE(
"5.d Adding arc (" << x <<
"," << z <<
")")
1048 marks[{z, x}] =
'-';
1049 marks[{x, z}] =
'>';
1060 return (std::find(lbeg, lend,
Arc(x, y)) == lend)
1061 && (std::find(lbeg, lend,
Arc(y, x)) == lend);
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.
void addArc(NodeId tail, NodeId head) final
insert a new arc into the directed graph
void addArc(const NodeId tail, const NodeId head) override
insert a new arc into the directed graph
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.
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.
Val pop()
Removes the top element from the heap and return it.
Size size() const noexcept
Returns the number of elements in the heap.
const Val & top() const
Returns the element at the top of the heap.
Size insert(const Val &val)
inserts a new element (actually a copy) in the heap and returns its index
Signaler< Size, double, double > onProgress
Progression, error and time.
Generic doubly linked lists.
Val & front() const
Returns a reference to first element of a list, if any.
Val & pushBack(const Val &val)
Inserts a new element (a copy) at the end of the chained list.
bool empty() const noexcept
Returns a boolean indicating whether the chained list is empty.
void popFront()
Removes the first element of a List, if any.
Base class for mixed graphs.
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.
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
void clear()
Removes all the elements, if any, from the set.
bool empty() const noexcept
Indicates whether the set is the empty set.
iterator begin() const
The usual unsafe begin iterator to parse the set.
void insert(const Key &k)
Inserts a new element into the set.
void erase(const Key &k)
Erases an element from the set.
Size size() const noexcept
Returns the number of elements in the set.
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
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.
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.
std::vector< Arc > _latentCouples_
an empty vector of arcs
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.
SimpleMiic()
default constructor
Size _size_
size of the database
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.
std::size_t Size
In aGrUM, hashed values are unsigned long int.
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.
Base classes for mixed directed/undirected graphs.
include the inlined functions if necessary
std::pair< ThreePoints *, double > Ranking
std::pair< CondThreePoints *, double > CondRanking
std::tuple< NodeId, NodeId, NodeId, std::vector< NodeId > > CondThreePoints
std::tuple< NodeId, NodeId, NodeId > ThreePoints
std::tuple< ThreePoints *, double, double, double > ProbabilisticRanking
gum is the global namespace for all aGrUM entities
#define GUM_EMIT3(signal, arg1, arg2, arg3)
Class used to compute response times for benchmark purposes.