From 996c6bd9407f2d4a906b4ddbb7873425f77da91c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 22 Mar 2026 17:09:47 +0000 Subject: [PATCH 1/2] test: add tests for RandomCommonCause refuter The `random_common_cause` refuter had no dedicated test file. This commit adds `tests/causal_refuters/test_random_common_cause_refuter.py` with 6 tests covering: - Continuous treatment (linear regression) - Binary treatment (linear regression) - Categorical treatment (linear regression) - Refutation type string validation - Functional API (`refute_random_common_cause` directly) - Reproducibility with fixed `random_state` Also adds the corresponding handler in `tests/causal_refuters/base.py` so `SimpleRefuter` can drive the refuter via `null_refutation_test()`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/causal_refuters/base.py | 18 +++ .../test_random_common_cause_refuter.py | 143 ++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 tests/causal_refuters/test_random_common_cause_refuter.py diff --git a/tests/causal_refuters/base.py b/tests/causal_refuters/base.py index 587bdc9dec..fd08d1acc6 100644 --- a/tests/causal_refuters/base.py +++ b/tests/causal_refuters/base.py @@ -172,6 +172,24 @@ def null_refutation_test( res = True if (error < abs(ate_estimate.value) * self._error_tolerance) else False assert res + elif self.refuter_method == "random_common_cause": + ref = model.refute_estimate( + target_estimand, ate_estimate, method_name=self.refuter_method, num_simulations=20 + ) + + error = abs(ref.new_effect - ate_estimate.value) + + print( + "Error in the refuted estimate = {0} with tolerence {1}%. Estimated={2}, After Refutation={3}".format( + error, self._error_tolerance * 100, ate_estimate.value, ref.new_effect + ) + ) + + print(ref) + + res = True if (error < abs(ate_estimate.value) * self._error_tolerance) else False + assert res + elif self.refuter_method == "dummy_outcome_refuter": if self.transformations is None: ref_list = model.refute_estimate( diff --git a/tests/causal_refuters/test_random_common_cause_refuter.py b/tests/causal_refuters/test_random_common_cause_refuter.py new file mode 100644 index 0000000000..20cce63476 --- /dev/null +++ b/tests/causal_refuters/test_random_common_cause_refuter.py @@ -0,0 +1,143 @@ +import numpy as np +import pytest + +import dowhy.datasets +from dowhy import CausalModel +from dowhy.causal_refuters.random_common_cause import refute_random_common_cause + +from .base import SimpleRefuter + + +@pytest.mark.usefixtures("fixed_seed") +class TestRandomCommonCauseRefuter: + @pytest.mark.parametrize( + ["error_tolerance", "estimator_method", "num_samples"], + [(0.1, "backdoor.linear_regression", 1000)], + ) + def test_refutation_random_common_cause_continuous(self, error_tolerance, estimator_method, num_samples): + refuter_tester = SimpleRefuter(error_tolerance, estimator_method, "random_common_cause") + refuter_tester.continuous_treatment_testsuite(num_samples=num_samples) + + @pytest.mark.parametrize( + ["error_tolerance", "estimator_method", "num_samples"], + [(0.1, "backdoor.linear_regression", 5000)], + ) + def test_refutation_random_common_cause_binary(self, error_tolerance, estimator_method, num_samples): + refuter_tester = SimpleRefuter(error_tolerance, estimator_method, "random_common_cause") + refuter_tester.binary_treatment_testsuite( + tests_to_run="atleast-one-common-cause", num_samples=num_samples + ) + + @pytest.mark.parametrize( + ["error_tolerance", "estimator_method", "num_samples"], + [(0.1, "backdoor.linear_regression", 5000)], + ) + def test_refutation_random_common_cause_category(self, error_tolerance, estimator_method, num_samples): + refuter_tester = SimpleRefuter(error_tolerance, estimator_method, "random_common_cause") + refuter_tester.categorical_treatment_testsuite( + tests_to_run="atleast-one-common-cause", num_samples=num_samples + ) + + def test_refutation_random_common_cause_refutation_type(self): + """Test that the refutation type string is set correctly.""" + data = dowhy.datasets.linear_dataset( + beta=10, + num_common_causes=1, + num_instruments=1, + num_samples=500, + treatment_is_binary=True, + ) + model = CausalModel( + data=data["df"], + treatment=data["treatment_name"], + outcome=data["outcome_name"], + graph=data["gml_graph"], + proceed_when_unidentifiable=True, + test_significance=None, + ) + target_estimand = model.identify_effect(method_name="exhaustive-search") + target_estimand.set_identifier_method("backdoor") + ate_estimate = model.estimate_effect( + identified_estimand=target_estimand, + method_name="backdoor.linear_regression", + test_significance=None, + ) + ref = model.refute_estimate( + target_estimand, ate_estimate, method_name="random_common_cause", num_simulations=10 + ) + assert "random common cause" in ref.refutation_type.lower() + + def test_refute_random_common_cause_functional_api(self): + """Test the functional API refute_random_common_cause directly.""" + data = dowhy.datasets.linear_dataset( + beta=10, + num_common_causes=1, + num_instruments=1, + num_samples=500, + treatment_is_binary=True, + ) + model = CausalModel( + data=data["df"], + treatment=data["treatment_name"], + outcome=data["outcome_name"], + graph=data["gml_graph"], + proceed_when_unidentifiable=True, + test_significance=None, + ) + target_estimand = model.identify_effect(method_name="exhaustive-search") + target_estimand.set_identifier_method("backdoor") + ate_estimate = model.estimate_effect( + identified_estimand=target_estimand, + method_name="backdoor.linear_regression", + test_significance=None, + ) + ref = refute_random_common_cause( + data=data["df"], + target_estimand=target_estimand, + estimate=ate_estimate, + num_simulations=10, + random_state=42, + ) + assert ref is not None + assert ref.new_effect is not None + assert np.isfinite(ref.new_effect) + + def test_refute_random_common_cause_reproducible_with_random_state(self): + """Test that using an integer random_state gives reproducible results.""" + data = dowhy.datasets.linear_dataset( + beta=10, + num_common_causes=1, + num_instruments=1, + num_samples=500, + treatment_is_binary=True, + ) + model = CausalModel( + data=data["df"], + treatment=data["treatment_name"], + outcome=data["outcome_name"], + graph=data["gml_graph"], + proceed_when_unidentifiable=True, + test_significance=None, + ) + target_estimand = model.identify_effect(method_name="exhaustive-search") + target_estimand.set_identifier_method("backdoor") + ate_estimate = model.estimate_effect( + identified_estimand=target_estimand, + method_name="backdoor.linear_regression", + test_significance=None, + ) + ref1 = refute_random_common_cause( + data=data["df"], + target_estimand=target_estimand, + estimate=ate_estimate, + num_simulations=5, + random_state=0, + ) + ref2 = refute_random_common_cause( + data=data["df"], + target_estimand=target_estimand, + estimate=ate_estimate, + num_simulations=5, + random_state=0, + ) + assert ref1.new_effect == pytest.approx(ref2.new_effect) From 5be9bfef4c5c405f2f145ace3bca8a1092037b4d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 06:56:39 +0000 Subject: [PATCH 2/2] test: apply black formatting in random common cause tests Signed-off-by: GitHub Co-authored-by: emrekiciman <5982160+emrekiciman@users.noreply.github.com> --- tests/causal_refuters/test_random_common_cause_refuter.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/causal_refuters/test_random_common_cause_refuter.py b/tests/causal_refuters/test_random_common_cause_refuter.py index 20cce63476..c152d497da 100644 --- a/tests/causal_refuters/test_random_common_cause_refuter.py +++ b/tests/causal_refuters/test_random_common_cause_refuter.py @@ -24,9 +24,7 @@ def test_refutation_random_common_cause_continuous(self, error_tolerance, estima ) def test_refutation_random_common_cause_binary(self, error_tolerance, estimator_method, num_samples): refuter_tester = SimpleRefuter(error_tolerance, estimator_method, "random_common_cause") - refuter_tester.binary_treatment_testsuite( - tests_to_run="atleast-one-common-cause", num_samples=num_samples - ) + refuter_tester.binary_treatment_testsuite(tests_to_run="atleast-one-common-cause", num_samples=num_samples) @pytest.mark.parametrize( ["error_tolerance", "estimator_method", "num_samples"], @@ -34,9 +32,7 @@ def test_refutation_random_common_cause_binary(self, error_tolerance, estimator_ ) def test_refutation_random_common_cause_category(self, error_tolerance, estimator_method, num_samples): refuter_tester = SimpleRefuter(error_tolerance, estimator_method, "random_common_cause") - refuter_tester.categorical_treatment_testsuite( - tests_to_run="atleast-one-common-cause", num_samples=num_samples - ) + refuter_tester.categorical_treatment_testsuite(tests_to_run="atleast-one-common-cause", num_samples=num_samples) def test_refutation_random_common_cause_refutation_type(self): """Test that the refutation type string is set correctly."""