Skip to content

fix: identify all valid mediators in NIE/NDE estimation#1418

Open
kvr06-ai wants to merge 3 commits intopy-why:mainfrom
kvr06-ai:fix/1334-multiple-mediators
Open

fix: identify all valid mediators in NIE/NDE estimation#1418
kvr06-ai wants to merge 3 commits intopy-why:mainfrom
kvr06-ai:fix/1334-multiple-mediators

Conversation

@kvr06-ai
Copy link
Copy Markdown
Contributor

Summary

identify_mediation() breaks after finding the first valid mediator, silently dropping parallel mediators from NIE/NDE estimands. For graphs like D -> 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 early break in identify_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 via CausalModel.identify_effect)
  • tests/causal_identifiers/example_graphs.py: Add parallel-mediators graph 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:

Estimand expression:
 E[d/d[M2  M1](Y) * d/d[D]([M2  M1])]
Estimand assumption 1, Mediation: M2,M1 intercepts (blocks) all directed paths
  from D to Y except the path {D}->{Y}.

Notes

  • The core change is 3 lines in identify_mediation(): None -> [], = var; break -> .append(var), parse_state(var) -> vars.
  • construct_mediation_estimand already uses sp.Array and ",".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 use get_all_directed_paths, so it handles multiple mediators as action/outcome nodes correctly.

@kvr06-ai
Copy link
Copy Markdown
Contributor Author

@emrekiciman Would you mind taking a look? This addresses the parallel mediator gap you tagged as enhancement + docs in #1334.

@emrekiciman
Copy link
Copy Markdown
Member

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?

@kvr06-ai
Copy link
Copy Markdown
Contributor Author

kvr06-ai commented Mar 25, 2026

Yes, correct. The test_backdoor_identifier.py parametrizes over all entries in TEST_GRAPH_SOLUTIONS, and IdentificationTestGraphSolution hardcodes action_nodes=["X"]. The parallel-mediators graph used D as the treatment, so every backdoor test hit a nonexistent node. Fixed by renaming D → X in the fixture (the topology is unchanged). Also added an inline comment to make the naming convention explicit for future contributors.

@kvr06-ai
Copy link
Copy Markdown
Contributor Author

New failure is unrelated — tests/gcm/test_auto.py is in the GCM module (untouched by this PR) and the CI's own flaky-test reporter flags it as such. Happy to re-trigger or help if anything else is needed.

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]>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
return mediation_vars
return sorted(mediation_vars)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Issue with parallel mediators/multiple directed paths

3 participants