Skip to content

Commit

Permalink
Add implementation of is_outcome_ancestor
Browse files Browse the repository at this point in the history
  • Loading branch information
vartikatewari committed Dec 2, 2021
1 parent a51345a commit efeeeb0
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions .idea/y0.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions src/y0/controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

"""Predicates for good, bad, and neutral controls."""

from .algorithm.conditional_independencies import are_d_separated
from .dsl import Probability, Variable
from .graph import NxMixedGraph

Expand Down Expand Up @@ -44,3 +45,27 @@ def is_bad_control(graph: NxMixedGraph, query: Probability, variable: Variable)
"""
_control_precondition(graph, query, variable)
raise NotImplementedError


def is_outcome_ancestor(
graph: NxMixedGraph, cause: Variable, effect: Variable, variable: Variable
) -> bool:
"""Check if the variable is an outcome ancestor given a causal query and graph.
> In Model 8, Z is not a confounder nor does it block any back-door paths. Likewise,
controlling for Z does not open any back-door paths from X to Y . Thus, in terms of
asymptotic bias, Z is a “neutral control.” Analysis shows, however, that controlling for
Z reduces the variation of the outcome variable Y , and helps to improve the precision
of the ACE estimate in finite samples (Hahn, 2004; White and Lu, 2011; Henckel et al.,
2019; Rotnitzky and Smucler, 2019).
:param graph: An ADMG
:param cause: The intervention in the causal query
:param effect: The outcome of the causal query
:param variable: The variable to check
:return: If the variable is a bad control
"""
if variable == cause:
return False
judgement = are_d_separated(graph, cause, variable)
return judgement.separated and variable in graph.ancestors_inclusive(effect)
6 changes: 5 additions & 1 deletion tests/test_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import unittest

from y0.controls import is_bad_control, is_good_control
from y0.controls import is_bad_control, is_good_control, is_outcome_ancestor
from y0.dsl import U1, U2, A, M, P, U, W, X, Y, Z
from y0.graph import NxMixedGraph

Expand Down Expand Up @@ -95,3 +95,7 @@ def test_bad_controls(self):
for model in bad_test_models:
with self.subTest():
self.assertTrue(is_bad_control(model, P(Y @ X), Z))

def test_neutral_controls(self):
"""Test neutral controls."""
self.assertTrue(is_outcome_ancestor(model_8, X, Y, Z))

0 comments on commit efeeeb0

Please sign in to comment.