aGrUM 3.0.0
a C++ library for (probabilistic) graphical models
gum::DoorCriteria Class Reference

Implements Backdoor and Frontdoor criteria utilities for a DAG. More...

#include <doorCriteria.h>

Public Types

using NodeSetVec = std::vector< NodeSet >
 Convenience type: list of candidate adjustment sets.

Static Public Member Functions

static bool satisfiesBackdoorCriterion (const DAG &dag, NodeId X, NodeId Y, const NodeSet &Z)
 Check if Z satisfies the backdoor criterion relative to (X, Y).
static NodeSetVec enumerateBackdoorSets (const DAG &dag, NodeId X, NodeId Y, const NodeSet &excluded_nodes=NodeSet(), std::size_t max_cardinality=0, bool only_minimal=true, bool stopAtFirst=false)
 Enumerate valid backdoor adjustment sets.
static NodeSetVec enumerateBackdoorSets (const DAG &dag, NodeId X, NodeId Y, bool stopAtFirst)
 Enumerate valid backdoor adjustment sets — shorthand to set stopAtFirst only.
static std::optional< NodeSetfirstBackdoor (const DAG &dag, NodeId X, NodeId Y, const NodeSet &excluded_nodes=NodeSet(), std::size_t max_cardinality=0, bool only_minimal=true)
 Directly return the first backdoor adjustment set if any.
static bool satisfiesFrontdoorCriterion (const DAG &dag, NodeId X, NodeId Y, const NodeSet &Z)
 Check if Z satisfies the frontdoor criterion relative to (X, Y).
static NodeSetVec enumerateFrontdoorSets (const DAG &dag, NodeId X, NodeId Y, const NodeSet &excluded_nodes=NodeSet(), std::size_t max_cardinality=0, bool only_minimal=true, bool stopAtFirst=false)
 Enumerate valid frontdoor adjustment sets.
static NodeSetVec enumerateFrontdoorSets (const DAG &dag, NodeId X, NodeId Y, bool stopAtFirst)
 Enumerate valid frontdoor adjustment sets — shorthand to set stopAtFirst only.
static std::optional< NodeSetfirstFrontdoor (const DAG &dag, NodeId X, NodeId Y, const NodeSet &excluded_nodes=NodeSet(), std::size_t max_cardinality=0, bool only_minimal=true)
 Directly return the first frontdoor adjustment set if any.
static bool existsUnblockedDirectedPath (const DAG &dag, NodeId X, NodeId Y, const NodeSet &Z)
 Check whether there exists a directed path X->..->Y unblocked by Z.
static std::optional< NodeSetnodesOnDirectedPaths (const DAG &dag, NodeId X, NodeId Y)
 Compute nodes lying on some directed path from X to Y.
static NodeSet backdoorReach (const DAG &dag, NodeId X)
 Compute the "backdoor reach" of a node.
static bool hasBackdoorPath (const DAG &dag, NodeId X, NodeId Y, const NodeSet &Z)
 Test whether there exists an open backdoor path from X to Y given Z.

Static Private Member Functions

static bool _isMinimalBackdoorAdjustment (const DAG &dag, NodeId X, NodeId Y, const NodeSet &Z)
 Check whether Z is a minimal backdoor adjustment set.
static bool _isMinimalFrontdoorAdjustment (const DAG &dag, NodeId X, NodeId Y, const NodeSet &Z)
 Check whether Z is a minimal frontdoor adjustment set.
static bool _existsUnblockedDirectedPath_ (const DAG &dag, NodeId X, NodeId Y, const NodeSet &Z, NodeSet &visited)

Detailed Description

Implements Backdoor and Frontdoor criteria utilities for a DAG.

This class provides helpers to:

  • Check whether a conditioning set satisfies the backdoor/frontdoor criterion.
  • Enumerate all valid backdoor/frontdoor adjustment sets.

The DAG is passed as the first argument to each method rather than stored in the object, making DoorCriteria a stateless utility class.

The implementation mirrors pyAgrum functionality.

Naming correspondences with pyAgrum:

  • enumerateBackdoorSets => pyagrum: backdoor_generator
  • enumerateFrontdoorSets => pyagrum: frontdoor_generator
  • existsUnblockedDirectedPath => pyagrum: exists_unblocked_directed_path
  • nodesOnDirectedPaths => pyagrum: nodes_on_dipath
  • backdoorReach => pyagrum: backdoor_reach
  • hasBackdoorPath => pyagrum: backdoor_path
  • satisfiesBackdoorCriterion => pyagrum: is_backdoor
  • satisfiesFrontdoorCriterion => pyagrum: is_frontdoor

Definition at line 79 of file doorCriteria.h.

Member Typedef Documentation

◆ NodeSetVec

using gum::DoorCriteria::NodeSetVec = std::vector< NodeSet >

Convenience type: list of candidate adjustment sets.

Definition at line 82 of file doorCriteria.h.

Member Function Documentation

◆ _existsUnblockedDirectedPath_()

bool gum::DoorCriteria::_existsUnblockedDirectedPath_ ( const DAG & dag,
NodeId X,
NodeId Y,
const NodeSet & Z,
NodeSet & visited )
staticprivate

Definition at line 421 of file doorCriteria.cpp.

425 {
426 if (visited.contains(X)) return false;
427 visited.insert(X);
428 if (dag.existsArc(X, Y)) return true;
429 for (auto c: dag.children(X)) {
430 if (!Z.contains(c) && _existsUnblockedDirectedPath_(dag, c, Y, Z, visited)) return true;
431 }
432 return false;
433 }
static bool _existsUnblockedDirectedPath_(const DAG &dag, NodeId X, NodeId Y, const NodeSet &Z, NodeSet &visited)

References _existsUnblockedDirectedPath_(), gum::ArcGraphPart::children(), gum::Set< Key >::contains(), gum::ArcGraphPart::existsArc(), and gum::Set< Key >::insert().

Referenced by _existsUnblockedDirectedPath_(), and existsUnblockedDirectedPath().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _isMinimalBackdoorAdjustment()

bool gum::DoorCriteria::_isMinimalBackdoorAdjustment ( const DAG & dag,
NodeId X,
NodeId Y,
const NodeSet & Z )
staticprivate

Check whether Z is a minimal backdoor adjustment set.

Parameters
dagDirected acyclic graph representing the causal structure.
XCause variable.
YEffect variable.
ZCandidate conditioning set.
Returns
True if Z is a valid backdoor set and no strict subset is valid.

Definition at line 514 of file doorCriteria.cpp.

517 {
518 for (auto z: Z) {
519 NodeSet sub = Z;
520 sub.erase(z);
521 if (satisfiesBackdoorCriterion(dag, X, Y, sub)) return false;
522 }
523 return true;
524 }
static bool satisfiesBackdoorCriterion(const DAG &dag, NodeId X, NodeId Y, const NodeSet &Z)
Check if Z satisfies the backdoor criterion relative to (X, Y).
void erase(const Key &k)
Erases an element from the set.
Definition set_tpl.h:553
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...

References gum::Set< Key >::erase(), and satisfiesBackdoorCriterion().

Here is the call graph for this function:

◆ _isMinimalFrontdoorAdjustment()

bool gum::DoorCriteria::_isMinimalFrontdoorAdjustment ( const DAG & dag,
NodeId X,
NodeId Y,
const NodeSet & Z )
staticprivate

Check whether Z is a minimal frontdoor adjustment set.

Parameters
dagDirected acyclic graph representing the causal structure.
XCause variable.
YEffect variable.
ZCandidate conditioning set.
Returns
True if Z is a valid frontdoor set and no strict subset is valid.

Definition at line 526 of file doorCriteria.cpp.

529 {
530 if (Z.empty()) return false; // frontdoor sets must be non-empty
531 for (auto z: Z) {
532 NodeSet sub = Z;
533 sub.erase(z);
534 if (satisfiesFrontdoorCriterion(dag, X, Y, sub)) return false;
535 }
536 return true;
537 }
static bool satisfiesFrontdoorCriterion(const DAG &dag, NodeId X, NodeId Y, const NodeSet &Z)
Check if Z satisfies the frontdoor criterion relative to (X, Y).

References gum::Set< Key >::empty(), gum::Set< Key >::erase(), and satisfiesFrontdoorCriterion().

Referenced by enumerateFrontdoorSets().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ backdoorReach()

NodeSet gum::DoorCriteria::backdoorReach ( const DAG & dag,
NodeId X )
static

Compute the "backdoor reach" of a node.

Backdoor reach = set of nodes reachable from X by traversing one incoming edge (parent step) followed by any sequence of child steps.

Parameters
dagDirected acyclic graph representing the causal structure.
XNode of interest (typically the treatment).
Returns
Set of nodes in the backdoor reach of X.

Definition at line 480 of file doorCriteria.cpp.

480 {
481 NodeSet reach0; // via a parent step
482 NodeSet reach1; // via a child step
483
484 // Seed with 'a' to avoid parent->a->child zig-zag artifacts
485 reach0.insert(a);
486 reach1.insert(a);
487
488 // parents of X are themselves in backdoor reach (X <- parent)
489 for (auto pa: dag.parents(a))
490 reach0.insert(pa);
491
492 // Start the alternating traversal from each parent
493 for (auto pa: dag.parents(a)) {
494 _inner_br(dag, pa, /*pht=*/false, reach0, reach1);
495 }
496
497 NodeSet s = reach0;
498 for (auto n: reach1)
499 s.insert(n);
500 s.erase(a);
501 return s;
502 }
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:510

References gum::Set< Key >::erase(), gum::Set< Key >::insert(), and gum::ArcGraphPart::parents().

Referenced by enumerateFrontdoorSets(), and satisfiesFrontdoorCriterion().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ enumerateBackdoorSets() [1/2]

DoorCriteria::NodeSetVec gum::DoorCriteria::enumerateBackdoorSets ( const DAG & dag,
NodeId X,
NodeId Y,
bool stopAtFirst )
static

Enumerate valid backdoor adjustment sets — shorthand to set stopAtFirst only.

Parameters
dagDirected acyclic graph representing the causal structure.
XCause variable.
YEffect variable.
stopAtFirstIf true, return only the first found set and stop early.
Returns
Vector of valid backdoor adjustment sets (at most one if stopAtFirst is true).

Definition at line 274 of file doorCriteria.cpp.

274 {
275 return enumerateBackdoorSets(dag, X, Y, NodeSet{}, 0, true, stopAtFirst);
276 }
static NodeSetVec enumerateBackdoorSets(const DAG &dag, NodeId X, NodeId Y, const NodeSet &excluded_nodes=NodeSet(), std::size_t max_cardinality=0, bool only_minimal=true, bool stopAtFirst=false)
Enumerate valid backdoor adjustment sets.

References enumerateBackdoorSets().

Here is the call graph for this function:

◆ enumerateBackdoorSets() [2/2]

DoorCriteria::NodeSetVec gum::DoorCriteria::enumerateBackdoorSets ( const DAG & dag,
NodeId X,
NodeId Y,
const NodeSet & excluded_nodes = NodeSet(),
std::size_t max_cardinality = 0,
bool only_minimal = true,
bool stopAtFirst = false )
static

Enumerate valid backdoor adjustment sets.

Parameters
dagDirected acyclic graph representing the causal structure.
XCause variable.
YEffect variable.
excluded_nodesNodes to exclude from candidate sets (e.g., latent variables).
max_cardinalityMaximum set size (0 = no limit).
only_minimalIf true, return only minimal adjustment sets.
stopAtFirstIf true, return only the first found set and stop early.
Returns
Vector of valid backdoor adjustment sets (at most one if stopAtFirst is true).

Definition at line 166 of file doorCriteria.cpp.

172 {
173 NodeSetVec out;
174
175 // Theoretical variant: if X has no parents, {} is a valid backdoor set.
176 if (dag.parents(X).empty()) {
177 out.emplace_back();
178 return out;
179 }
180
181 // If Y is a parent of X, there is an open backdoor X<-Y that {} cannot block.
182 if (dag.existsArc(Y, X)) return out;
183
184 // Reduce around interest = {X, Y}; evidence = {}
185 NodeSet Xset;
186 Xset.insert(X);
187 NodeSet Yset;
188 Yset.insert(Y);
189 NodeSet Zempty;
190
191 // test {} first
192 if (Separation::isBackdoorSeparated(dag, Xset, Yset, Zempty)) {
193 out.push_back(Zempty);
194 if (only_minimal || stopAtFirst) return out;
195 }
196
197 DAG G = Separation::reduceForDSeparation(dag, Xset, Yset, Zempty);
198
199 // Candidate pool = nodes(G) \ (Desc(X) ∪ {X,Y} ∪ excluded_nodes)
200 NodeSet descX = dag.descendants(X);
201 NodeSet possible;
202 for (auto n: G.nodes()) {
203 if (n == X || n == Y) continue;
204 if (excluded_nodes.contains(n)) continue;
205 if (descX.contains(n)) continue;
206 possible.insert(n);
207 }
208 if (possible.empty()) return out;
209
210 auto cand = _sortedVec(possible);
211 const std::size_t N = cand.size();
212 const std::size_t Kmax = (max_cardinality == 0) ? N : std::min(max_cardinality, N);
213
214 // Only prune supersets in only_minimal mode
215 NodeSetVec chosen;
216 const bool prune_supersets = only_minimal;
217
218 NodeSet Xs;
219 Xs.insert(X);
220 NodeSet Ys;
221 Ys.insert(Y);
222
223 // pyagrum tries k >= 1 (never yields {} except our explicit no-parent case above)
224 for (std::size_t k = 1; k <= Kmax; ++k) {
225 std::vector< bool > pick(N, false);
226 std::fill_n(pick.begin(), k, true);
227 do {
228 NodeSet Z;
229 for (std::size_t i = 0; i < N; ++i)
230 if (pick[i]) Z.insert(cand[i]);
231
232 if (prune_supersets) {
233 bool worth = true;
234 for (const auto& s: chosen) {
235 bool subset = true;
236 for (auto u: s)
237 if (!Z.contains(u)) {
238 subset = false;
239 break;
240 }
241 if (subset) {
242 worth = false;
243 break;
244 }
245 }
246 if (!worth) continue;
247 }
248
249 if (Separation::isBackdoorSeparated(G, Xs, Ys, Z)) {
250 if (only_minimal) {
251 chosen.push_back(Z);
252 out.push_back(Z);
253 } else {
254 out.push_back(Z); // keep non-minimal supersets too (e.g., {U,W})
255 }
256 if (stopAtFirst) return out;
257 }
258
259 } while (std::prev_permutation(pick.begin(), pick.end()));
260 }
261
262 // Deterministic order + dedup
263 std::sort(out.begin(), out.end(), _lexLess);
264 out.erase(std::unique(out.begin(),
265 out.end(),
266 [](const NodeSet& a, const NodeSet& b) {
267 return _sortedVec(a) == _sortedVec(b);
268 }),
269 out.end());
270 return out;
271 }
std::vector< NodeSet > NodeSetVec
Convenience type: list of candidate adjustment sets.
static DAG reduceForDSeparation(const DAG &dag, const NodeSet &X, const NodeSet &Y, const NodeSet &Z)
Barren-node pruning relative to the interest set (X \cup Y \cup Z).
static bool isBackdoorSeparated(const DAG &dag, const NodeSet &X, const NodeSet &Y, const NodeSet &Z)
Backdoor-style restriction: only paths whose first edge points into X.

References gum::Set< Key >::contains(), gum::DiGraph::descendants(), gum::Set< Key >::empty(), gum::ArcGraphPart::existsArc(), gum::Set< Key >::insert(), gum::Separation::isBackdoorSeparated(), gum::NodeGraphPart::nodes(), gum::ArcGraphPart::parents(), and gum::Separation::reduceForDSeparation().

Referenced by gum::CausalModel< GUM_SCALAR >::backDoor(), enumerateBackdoorSets(), and firstBackdoor().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ enumerateFrontdoorSets() [1/2]

DoorCriteria::NodeSetVec gum::DoorCriteria::enumerateFrontdoorSets ( const DAG & dag,
NodeId X,
NodeId Y,
bool stopAtFirst )
static

Enumerate valid frontdoor adjustment sets — shorthand to set stopAtFirst only.

Parameters
dagDirected acyclic graph representing the causal structure.
XCause variable.
YEffect variable.
stopAtFirstIf true, return only the first found set and stop early.
Returns
Vector of valid frontdoor adjustment sets (at most one if stopAtFirst is true).

Definition at line 407 of file doorCriteria.cpp.

407 {
408 return enumerateFrontdoorSets(dag, X, Y, NodeSet{}, 0, true, stopAtFirst);
409 }
static NodeSetVec enumerateFrontdoorSets(const DAG &dag, NodeId X, NodeId Y, const NodeSet &excluded_nodes=NodeSet(), std::size_t max_cardinality=0, bool only_minimal=true, bool stopAtFirst=false)
Enumerate valid frontdoor adjustment sets.

References enumerateFrontdoorSets().

Here is the call graph for this function:

◆ enumerateFrontdoorSets() [2/2]

DoorCriteria::NodeSetVec gum::DoorCriteria::enumerateFrontdoorSets ( const DAG & dag,
NodeId X,
NodeId Y,
const NodeSet & excluded_nodes = NodeSet(),
std::size_t max_cardinality = 0,
bool only_minimal = true,
bool stopAtFirst = false )
static

Enumerate valid frontdoor adjustment sets.

Parameters
dagDirected acyclic graph representing the causal structure.
XCause variable.
YEffect variable.
excluded_nodesNodes to exclude from candidate sets (e.g., latent variables).
max_cardinalityMaximum set size (0 = no limit).
only_minimalIf true, return only minimal adjustment sets.
stopAtFirstIf true, return only the first found set and stop early.
Returns
Vector of valid frontdoor adjustment sets (at most one if stopAtFirst is true).

Definition at line 278 of file doorCriteria.cpp.

284 {
285 NodeSetVec out;
286
287 // pyagrum early exit: if X is a parent of Y, yield nothing
288 for (auto p: dag.parents(Y)) {
289 if (p == X) { return out; }
290 }
291
292 // Candidate pool
293 auto possibleOpt = nodesOnDirectedPaths(dag, X, Y);
294 bool noDiPath = !possibleOpt.has_value();
295
296 NodeSet possible;
297 if (possibleOpt.has_value()) { possible = *possibleOpt; }
298
299 if (noDiPath) {
300 // No directed path: use weakly connected component containing both X and Y (undirected BFS)
301 NodeSet cc;
302 {
303 std::vector< NodeId > st;
304 st.push_back(X);
305 cc.insert(X);
306 while (!st.empty()) {
307 NodeId u = st.back();
308 st.pop_back();
309 for (auto v: dag.parents(u))
310 if (!cc.contains(v)) {
311 cc.insert(v);
312 st.push_back(v);
313 }
314 for (auto v: dag.children(u))
315 if (!cc.contains(v)) {
316 cc.insert(v);
317 st.push_back(v);
318 }
319 }
320 }
321 if (!cc.contains(Y)) { return out; }
322 noDiPath = true;
323 possible = cc;
324 possible.erase(X);
325 possible.erase(Y);
326 } else {
327 possible.erase(X);
328 possible.erase(Y);
329 }
330
331 // Prune by backdoor reach of X and user exclusions
332 NodeSet br = backdoorReach(dag, X);
333 for (auto n: br) {
334 possible.erase(n);
335 }
336 for (auto n: excluded_nodes) {
337 possible.erase(n);
338 }
339
340 // Remove "impossible" z with a backdoor to Y given X, on a reduced graph:
341 // interest = {X, Y} ∪ possible; evidence = {} (parity with pyagrum)
342 NodeSet Xset;
343 Xset.insert(X);
344 NodeSet Yset = possible;
345 Yset.insert(Y);
346 NodeSet Zempty;
347
348 DAG g = Separation::reduceForDSeparation(dag, Xset, Yset, Zempty);
349
350 NodeSet impossible;
351 NodeSet condX;
352 condX.insert(X);
353 for (auto z: possible) {
354 NodeSet Zs;
355 Zs.insert(z);
356 if (!Separation::isBackdoorSeparated(g, Zs, NodeSet{Y}, condX)) { impossible.insert(z); }
357 }
358 for (auto z: impossible) {
359 possible.erase(z);
360 }
361
362 if (possible.empty()) { return out; }
363
364 auto cand = _sortedVec(possible);
365
366 if (noDiPath) {
367 // pyagrum: yield each singleton
368 for (auto n: cand) {
369 NodeSet Z;
370 Z.insert(n);
371 out.push_back(Z);
372 if (stopAtFirst) { return out; }
373 }
374 } else {
375 const std::size_t N = cand.size();
376 const std::size_t Kmax = (max_cardinality == 0) ? N : std::min(max_cardinality, N);
377 for (std::size_t k = 1; k <= Kmax; ++k) {
378 std::vector< bool > pick(N, false);
379 std::fill_n(pick.begin(), k, true);
380 do {
381 NodeSet Z;
382 for (std::size_t i = 0; i < N; ++i) {
383 if (pick[i]) { Z.insert(cand[i]); }
384 }
385 // (FD-1): must block all directed X->Y paths
386 if (!existsUnblockedDirectedPath(dag, X, Y, Z)) {
387 if (!only_minimal || _isMinimalFrontdoorAdjustment(dag, X, Y, Z)) {
388 out.push_back(Z);
389 if (stopAtFirst) { return out; }
390 }
391 }
392 } while (std::prev_permutation(pick.begin(), pick.end()));
393 }
394 }
395
396 std::sort(out.begin(), out.end(), _lexLess);
397 out.erase(std::unique(out.begin(),
398 out.end(),
399 [](const NodeSet& a, const NodeSet& b) {
400 return _sortedVec(a) == _sortedVec(b);
401 }),
402 out.end());
403 return out;
404 }
static bool existsUnblockedDirectedPath(const DAG &dag, NodeId X, NodeId Y, const NodeSet &Z)
Check whether there exists a directed path X->..->Y unblocked by Z.
static std::optional< NodeSet > nodesOnDirectedPaths(const DAG &dag, NodeId X, NodeId Y)
Compute nodes lying on some directed path from X to Y.
static NodeSet backdoorReach(const DAG &dag, NodeId X)
Compute the "backdoor reach" of a node.
static bool _isMinimalFrontdoorAdjustment(const DAG &dag, NodeId X, NodeId Y, const NodeSet &Z)
Check whether Z is a minimal frontdoor adjustment set.
Size NodeId
Type for node ids.

References _isMinimalFrontdoorAdjustment(), backdoorReach(), gum::ArcGraphPart::children(), gum::Set< Key >::contains(), gum::Set< Key >::empty(), gum::Set< Key >::erase(), existsUnblockedDirectedPath(), gum::Set< Key >::insert(), gum::Separation::isBackdoorSeparated(), nodesOnDirectedPaths(), gum::ArcGraphPart::parents(), and gum::Separation::reduceForDSeparation().

Referenced by enumerateFrontdoorSets(), firstFrontdoor(), and gum::CausalModel< GUM_SCALAR >::frontDoor().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ existsUnblockedDirectedPath()

bool gum::DoorCriteria::existsUnblockedDirectedPath ( const DAG & dag,
NodeId X,
NodeId Y,
const NodeSet & Z )
static

Check whether there exists a directed path X->..->Y unblocked by Z.

Parameters
dagDirected acyclic graph representing the causal structure.
XStart node.
YEnd node.
ZConditioning set (nodes that block traversal if encountered).
Returns
True if an unblocked directed path exists from X to Y.

Definition at line 413 of file doorCriteria.cpp.

416 {
417 NodeSet visited;
418 return _existsUnblockedDirectedPath_(dag, X, Y, Z, visited);
419 }

References _existsUnblockedDirectedPath_().

Referenced by enumerateFrontdoorSets(), and satisfiesFrontdoorCriterion().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ firstBackdoor()

INLINE std::optional< NodeSet > gum::DoorCriteria::firstBackdoor ( const DAG & dag,
NodeId X,
NodeId Y,
const NodeSet & excluded_nodes = NodeSet(),
std::size_t max_cardinality = 0,
bool only_minimal = true )
static

Directly return the first backdoor adjustment set if any.

Parameters
dagDirected acyclic graph representing the causal structure.
XCause variable.
YEffect variable.
excluded_nodesNodes to exclude from candidate sets (e.g., latent variables).
max_cardinalityMaximum set size (0 = no limit).
only_minimalIf true, return only minimal adjustment sets.
Returns
std::optional<NodeSet> The first valid backdoor adjustment set found, or std::nullopt if none exists.

Definition at line 48 of file doorCriteria_inl.h.

53 {
54 auto bds
55 = enumerateBackdoorSets(dag, X, Y, excluded_nodes, max_cardinality, only_minimal, true);
56 if (bds.empty()) { return std::nullopt; }
57 return bds.front();
58 }

References enumerateBackdoorSets().

Here is the call graph for this function:

◆ firstFrontdoor()

INLINE std::optional< NodeSet > gum::DoorCriteria::firstFrontdoor ( const DAG & dag,
NodeId X,
NodeId Y,
const NodeSet & excluded_nodes = NodeSet(),
std::size_t max_cardinality = 0,
bool only_minimal = true )
static

Directly return the first frontdoor adjustment set if any.

Parameters
dagDirected acyclic graph representing the causal structure.
XCause variable.
YEffect variable.
excluded_nodesNodes to exclude from candidate sets (e.g., latent variables).
max_cardinalityMaximum set size (0 = no limit).
only_minimalIf true, return only minimal adjustment sets.
Returns
std::optional<NodeSet> The first valid frontdoor adjustment set found, or std::nullopt if none exists.

Definition at line 60 of file doorCriteria_inl.h.

65 {
66 auto fds
67 = enumerateFrontdoorSets(dag, X, Y, excluded_nodes, max_cardinality, only_minimal, true);
68 if (fds.empty()) { return std::nullopt; }
69 return fds.front();
70 }

References enumerateFrontdoorSets().

Here is the call graph for this function:

◆ hasBackdoorPath()

bool gum::DoorCriteria::hasBackdoorPath ( const DAG & dag,
NodeId X,
NodeId Y,
const NodeSet & Z )
static

Test whether there exists an open backdoor path from X to Y given Z.

Parameters
dagDirected acyclic graph representing the causal structure.
XCause variable.
YEffect variable.
ZConditioning set.
Returns
True if there exists at least one unblocked backdoor path.

Definition at line 504 of file doorCriteria.cpp.

504 {
505 NodeSet Xs;
506 Xs.insert(X);
507 NodeSet Ys;
508 Ys.insert(Y);
509 return !Separation::isBackdoorSeparated(dag, Xs, Ys, Z);
510 }

References gum::Set< Key >::insert(), and gum::Separation::isBackdoorSeparated().

Here is the call graph for this function:

◆ nodesOnDirectedPaths()

std::optional< NodeSet > gum::DoorCriteria::nodesOnDirectedPaths ( const DAG & dag,
NodeId X,
NodeId Y )
static

Compute nodes lying on some directed path from X to Y.

Parameters
dagDirected acyclic graph representing the causal structure.
XStart node.
YEnd node.
Returns
Set of nodes on at least one X->..->Y directed path.

Definition at line 435 of file doorCriteria.cpp.

435 {
436 const NodeSet f = dag.descendants(X); // forward reach
437 const NodeSet r = dag.ancestors(Y); // reverse reach
438
439 if (!f.contains(Y)) return std::nullopt;
440
441 NodeSet res;
442 // A node is on some X->...->Y path iff reachable from X and can reach Y.
443 for (auto n: f)
444 if (r.contains(n)) res.insert(n);
445
446 // To match pyagrum version:
447 // - do not include X
448 // - do not include Y
449 res.erase(X);
450 res.erase(Y);
451
452 return res;
453 }

References gum::DiGraph::ancestors(), gum::Set< Key >::contains(), gum::DiGraph::descendants(), gum::Set< Key >::erase(), and gum::Set< Key >::insert().

Referenced by enumerateFrontdoorSets().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ satisfiesBackdoorCriterion()

bool gum::DoorCriteria::satisfiesBackdoorCriterion ( const DAG & dag,
NodeId X,
NodeId Y,
const NodeSet & Z )
static

Check if Z satisfies the backdoor criterion relative to (X, Y).

Parameters
dagDirected acyclic graph representing the causal structure.
XCause variable.
YEffect variable.
ZConditioning set.
Returns
True if Z blocks all backdoor paths from X to Y.

Definition at line 110 of file doorCriteria.cpp.

113 {
114 // Rule-out illegal contents up front:
115 // - Z must not contain X (conditioning on X is not a valid adjustment set)
116 // - Z must not contain Y (keep parity with enumeration/universe)
117 if (Z.contains(X) || Z.contains(Y)) return false;
118
119 // - Z must not contain any descendant of X
120 NodeSet descX = dag.descendants(X);
121 for (auto dx: descX) {
122 if (Z.contains(dx)) return false;
123 }
124
125 // Backdoor blocking condition in G_{underline X}
126 NodeSet Xs;
127 Xs.insert(X);
128 NodeSet Ys;
129 Ys.insert(Y);
130 return Separation::isBackdoorSeparated(dag, Xs, Ys, Z);
131 }

References gum::Set< Key >::contains(), gum::DiGraph::descendants(), gum::Set< Key >::insert(), and gum::Separation::isBackdoorSeparated().

Referenced by _isMinimalBackdoorAdjustment().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ satisfiesFrontdoorCriterion()

bool gum::DoorCriteria::satisfiesFrontdoorCriterion ( const DAG & dag,
NodeId X,
NodeId Y,
const NodeSet & Z )
static

Check if Z satisfies the frontdoor criterion relative to (X, Y).

Parameters
dagDirected acyclic graph representing the causal structure.
XCause variable.
YEffect variable.
ZConditioning set.
Returns
True if Z satisfies all three frontdoor conditions (FD-1, FD-2, FD-3).

Definition at line 133 of file doorCriteria.cpp.

136 {
137 if (Z.empty()) return false;
138
139 // (FD-1) Z must intercept all directed X->...->Y paths
140 if (existsUnblockedDirectedPath(dag, X, Y, Z)) return false;
141
142 // (FD-2) Z ∩ backdoor_reach(X) = {}
143 NodeSet br = backdoorReach(dag, X);
144 for (auto z: Z)
145 if (br.contains(z)) return false;
146
147 // (FD-3) check on a d-separation reduced graph with interest = Z ∪ {X,Y}
148 NodeSet Xset;
149 Xset.insert(X);
150 NodeSet Yset;
151 Yset.insert(Y);
152 // interest = X ∪ Y ∪ Z -> pass Xset, Yset, Z as-is
153 DAG reduced = Separation::reduceForDSeparation(dag, Xset, Yset, Z);
154
155 NodeSet condX;
156 condX.insert(X);
157 for (auto z: Z) {
158 NodeSet Zs;
159 Zs.insert(z);
160 // backdoor_path(reduced, z, y, {x}) == !isBackdoorSeparated(reduced, {z}, {y}, {x})
161 if (!Separation::isBackdoorSeparated(reduced, Zs, Yset, condX)) return false;
162 }
163 return true;
164 }

References backdoorReach(), gum::Set< Key >::contains(), gum::Set< Key >::empty(), existsUnblockedDirectedPath(), gum::Set< Key >::insert(), gum::Separation::isBackdoorSeparated(), and gum::Separation::reduceForDSeparation().

Referenced by _isMinimalFrontdoorAdjustment().

Here is the call graph for this function:
Here is the caller graph for this function:

The documentation for this class was generated from the following files: