aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
dSeparation_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright 2005-2020 Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4  * info_at_agrum_dot_org
5  *
6  * This library is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 /**
23  * @file
24  * @brief d-separation analysis (as described in Koller & Friedman 2009)
25  *
26  * @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
27  */
28 
29 
30 namespace gum {
31 
32 
33  // update a set of potentials, keeping only those d-connected with
34  // query variables given evidence
35  template < typename GUM_SCALAR, template < typename > class TABLE >
37  const IBayesNet< GUM_SCALAR >& bn,
38  const NodeSet& query,
39  const NodeSet& hardEvidence,
40  const NodeSet& softEvidence,
41  Set< const TABLE< GUM_SCALAR >* >& potentials) {
42  const DAG& dag = bn.dag();
43 
44  // mark the set of ancestors of the evidence
46  {
48  for (const auto node: hardEvidence)
50  for (const auto node: softEvidence)
52  while (!anc_to_visit.empty()) {
53  const NodeId node = anc_to_visit.front();
55 
56  if (!ev_ancestors.exists(node)) {
58  for (const auto par: dag.parents(node)) {
60  }
61  }
62  }
63  }
64 
65  // create the marks indicating that we have visited a node
68 
69  /// for relevant potentials: indicate which tables contain a variable
70  /// (nodeId)
72  for (const auto pot: potentials) {
73  const Sequence< const DiscreteVariable* >& vars = pot->variablesSequence();
74  for (const auto var: vars) {
75  const NodeId id = bn.nodeId(*var);
76  if (!node2potentials.exists(id)) {
77  node2potentials.insert(id, Set< const TABLE< GUM_SCALAR >* >());
78  }
80  }
81  }
82 
83  // indicate that we will send the ball to all the query nodes (as children):
84  // in list nodes_to_visit, the first element is the next node to send the
85  // ball to and the Boolean indicates whether we shall reach it from one of
86  // its children (true) or from one parent (false)
87  List< std::pair< NodeId, bool > > nodes_to_visit;
88  for (const auto node: query) {
89  nodes_to_visit.insert(std::pair< NodeId, bool >(node, true));
90  }
91 
92  // perform the bouncing ball until there is no node in the graph to send
93  // the ball to
94  while (!nodes_to_visit.empty() && !node2potentials.empty()) {
95  // get the next node to visit
97  const bool direction = nodes_to_visit.front().second;
99 
100  // check if the node has not already been visited in the same direction
101  bool already_visited;
102  if (direction) {
105  } else {
108  }
109 
110  // if the node belongs to the query, update node2potentials__: remove all
111  // the potentials containing the node
112  if (node2potentials.exists(node)) {
113  auto& pot_set = node2potentials[node];
114  for (const auto pot: pot_set) {
115  const auto& vars = pot->variablesSequence();
116  for (const auto var: vars) {
117  const NodeId id = bn.nodeId(*var);
118  if (id != node) {
121  }
122  }
123  }
125 
126  // if node2potentials__ is empty, no need to go on: all the potentials
127  // are d-connected to the query
128  if (node2potentials.empty()) return;
129  }
130 
131  // if this is the first time we meet the node, then visit it
132  if (!already_visited) {
133  // mark the node as reachable if this is not a hard evidence
135 
136  // bounce the ball toward the neighbors
137  if (direction && !is_hard_evidence) { // visit from a child
138  // visit the parents
139  for (const auto par: dag.parents(node)) {
140  nodes_to_visit.insert(std::pair< NodeId, bool >(par, true));
141  }
142 
143  // visit the children
144  for (const auto chi: dag.children(node)) {
145  nodes_to_visit.insert(std::pair< NodeId, bool >(chi, false));
146  }
147  } else { // visit from a parent
148  if (!hardEvidence.exists(node)) {
149  // visit the children
150  for (const auto chi: dag.children(node)) {
151  nodes_to_visit.insert(std::pair< NodeId, bool >(chi, false));
152  }
153  }
154  if (ev_ancestors.exists(node)) {
155  // visit the parents
156  for (const auto par: dag.parents(node)) {
157  nodes_to_visit.insert(std::pair< NodeId, bool >(par, true));
158  }
159  }
160  }
161  }
162  }
163 
164  // here, all the potentials that belong to node2potentials__ are d-separated
165  // from the query
166  for (const auto elt: node2potentials) {
167  for (const auto pot: elt.second) {
169  }
170  }
171  }
172 
173 
174 } /* namespace gum */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:669