fix: identify all valid mediators in NIE/NDE estimation#1418
fix: identify all valid mediators in NIE/NDE estimation#1418kvr06-ai wants to merge 3 commits intopy-why:mainfrom
Conversation
|
@emrekiciman Would you mind taking a look? This addresses the parallel mediator gap you tagged as enhancement + docs in #1334. |
|
Hi @kvr06-ai thanks for creating this PR to address this issue! Could you take a look at the failing test case? It looks like the test_backdoor_identifier.py might be picking up the new example graph you added for parallel mediators, but failing because it expects a non-existent node X? |
|
Yes, correct. The |
|
New failure is unrelated — |
identify_mediation() broke after finding the first valid mediator, silently dropping parallel mediators from NIE/NDE estimands. Signed-off-by: Kaushik Rajan <[email protected]>
The backdoor identifier tests hardcode action_nodes=["X"]; the graph fixture must use X as the treatment label to match. Signed-off-by: Kaushik Rajan <[email protected]>
…ors fixture Signed-off-by: Kaushik Rajan <[email protected]>
406afd8 to
0b83b1e
Compare
There was a problem hiding this comment.
Pull request overview
Fixes mediation identification for NIE/NDE so that all valid mediators are included (supporting parallel mediators), addressing #1334.
Changes:
- Update
identify_mediation()to collect all valid mediator nodes instead of stopping after the first match. - Add regression + end-to-end tests covering single/parallel/no mediator scenarios and NIE identification.
- Add a reusable “parallel mediators” example graph fixture for identifier tests.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
dowhy/causal_identifier/auto_identifier.py |
Removes early-exit behavior in mediator discovery so NIE/NDE estimands can include multiple mediators. |
tests/test_causal_graph.py |
Adds direct unit tests for identify_mediation plus an end-to-end NIE identification regression test. |
tests/causal_identifiers/example_graphs.py |
Adds a parallel-mediators test graph fixture reflecting the issue’s graph structure. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| break | ||
| return parse_state(mediation_var) | ||
| mediation_vars.append(candidate_var) | ||
| return mediation_vars |
There was a problem hiding this comment.
eligible_variables is a set, so mediation_vars.append(candidate_var) will produce a non-deterministic mediator ordering across runs (and therefore non-deterministic NIE/NDE estimand strings/assumptions via ",".join(mediator_nodes)). Consider sorting the mediators before returning (e.g., iterate over sorted(eligible_variables) or return sorted(mediation_vars)) to keep outputs stable and reproducible.
| return mediation_vars | |
| return sorted(mediation_vars) |
Summary
identify_mediation()breaks after finding the first valid mediator, silently dropping parallel mediators from NIE/NDE estimands. For graphs likeD -> M1 -> Y, D -> M2 -> Y, D -> Y, only M1 (or whichever is found first) appeared in the estimand.Fixes #1334
Changes
dowhy/causal_identifier/auto_identifier.py: Remove the earlybreakinidentify_mediation()and collect all valid mediators into the returned list. The downstream functions (construct_mediation_estimand,identify_mediation_first_stage_confounders,identify_mediation_second_stage_confounders) already handle lists of mediators correctly, so no changes are needed there.tests/test_causal_graph.py: Add 4 test functions:test_identify_mediation_single_mediator(regression)test_identify_mediation_parallel_mediators(the fix)test_identify_mediation_no_mediator(negative)test_nie_with_parallel_mediators(end-to-end viaCausalModel.identify_effect)tests/causal_identifiers/example_graphs.py: Addparallel-mediatorsgraph definition (the exact graph from Issue with parallel mediators/multiple directed paths #1334).Testing
All 4 new tests pass. The end-to-end test confirms both M1 and M2 appear in the NIE estimand:
Notes
identify_mediation():None->[],= var; break->.append(var),parse_state(var)->vars.construct_mediation_estimandalready usessp.Arrayand",".join(mediator_nodes), so the estimand expression and assumption text handle multiple mediators without changes.identify_backdoor(called by the confounder identification functions) does not useget_all_directed_paths, so it handles multiple mediators as action/outcome nodes correctly.