Repository: pgmpy/pgmpy

Issue:
Z on is_valid_backdoor_adjustment_set does not accept str type.
### Subject of the issue
When Z on is_valid_backdoor_adjustment_set is str type, it fails and throw ValueError exception.

### Your environment
* pgmpy version 0.1.17
* Python version 3.9.6
* Operating System OS X 12.2.1

### Steps to reproduce
Tell us how to reproduce this issue. Please provide a **minimal reproducible code** of
the issue you are facing if possible.

```
from pgmpy.inference import CausalInference
from pgmpy.models import BayesianModel

model = BayesianModel(
    [('abc', 'def'),('ghi', 'abc'),('ghi', 'def')])
causal_model = CausalInference(model)
causal_model.is_valid_backdoor_adjustment_set(X="abc",Y="ghi", Z="def")
```

### Expected behaviour
Tell us what should happen

It returns valid response `True` or `False`.

### Actual behaviour
Tell us what happens instead

It throw `ValueError: Node d not in not in graph`

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/pgmpy/pgmpy/pull/1512?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy) Report
> Merging [#1512](https://codecov.io/gh/pgmpy/pgmpy/pull/1512?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy) (5adf1de) into [dev](https://codecov.io/gh/pgmpy/pgmpy/commit/578a839ed9a5d6603d5fc9dd4411aa2d1304c712?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy) (578a839) will **decrease** coverage by `0.74%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##              dev    #1512      +/-   ##
==========================================
- Coverage   94.78%   94.03%   -0.75%     
==========================================
  Files         143      143              
  Lines       16425    16426       +1     
==========================================
- Hits        15568    15447     -121     
- Misses        857      979     +122     
```


| [Impacted Files](https://codecov.io/gh/pgmpy/pgmpy/pull/1512?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy) | Coverage Δ | |
|---|---|---|
| [pgmpy/inference/CausalInference.py](https://codecov.io/gh/pgmpy/pgmpy/pull/1512/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy#diff-cGdtcHkvaW5mZXJlbmNlL0NhdXNhbEluZmVyZW5jZS5weQ==) | `76.24% <100.00%> (+0.26%)` | :arrow_up: |
| [pgmpy/readwrite/XMLBeliefNetwork.py](https://codecov.io/gh/pgmpy/pgmpy/pull/1512/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy#diff-cGdtcHkvcmVhZHdyaXRlL1hNTEJlbGllZk5ldHdvcmsucHk=) | `53.62% <0.00%> (-39.14%)` | :arrow_down: |
| [...gmpy/tests/test\_readwrite/test\_XMLBeliefNetwork.py](https://codecov.io/gh/pgmpy/pgmpy/pull/1512/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy#diff-cGdtcHkvdGVzdHMvdGVzdF9yZWFkd3JpdGUvdGVzdF9YTUxCZWxpZWZOZXR3b3JrLnB5) | `75.40% <0.00%> (-18.86%)` | :arrow_down: |
| [pgmpy/readwrite/PomdpX.py](https://codecov.io/gh/pgmpy/pgmpy/pull/1512/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy#diff-cGdtcHkvcmVhZHdyaXRlL1BvbWRwWC5weQ==) | `86.62% <0.00%> (-10.37%)` | :arrow_down: |
| [pgmpy/tests/test\_readwrite/test\_PomdpX.py](https://codecov.io/gh/pgmpy/pgmpy/pull/1512/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy#diff-cGdtcHkvdGVzdHMvdGVzdF9yZWFkd3JpdGUvdGVzdF9Qb21kcFgucHk=) | `83.68% <0.00%> (-9.22%)` | :arrow_down: |
| [pgmpy/estimators/MmhcEstimator.py](https://codecov.io/gh/pgmpy/pgmpy/pull/1512/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy#diff-cGdtcHkvZXN0aW1hdG9ycy9NbWhjRXN0aW1hdG9yLnB5) | `98.11% <0.00%> (-1.89%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pgmpy/pgmpy/pull/1512?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pgmpy/pgmpy/pull/1512?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy). Last update [578a839...5adf1de](https://codecov.io/gh/pgmpy/pgmpy/pull/1512?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy).

ankurankan: @1betatsu No worries, thanks a lot for the fixes. 

Patch:
diff --git a/pgmpy/inference/CausalInference.py b/pgmpy/inference/CausalInference.py
index 6e2fd46f..6f83c49d 100644
--- a/pgmpy/inference/CausalInference.py
+++ b/pgmpy/inference/CausalInference.py
@@ -97,7 +97,8 @@ class CausalInference(object):
         >>> inference.is_valid_backdoor_adjustment_set("X", "Y")
         True
         """
-        Z_ = list(Z)
+        Z_ = [Z] if isinstance(Z, str) else list(Z)
+
         observed = [X] + Z_
         parents_d_sep = []
         for p in self.model.predecessors(X):


Test patch:
diff --git a/pgmpy/tests/test_inference/test_CausalInference.py b/pgmpy/tests/test_inference/test_CausalInference.py
index 35aa8f1d..04f82655 100644
--- a/pgmpy/tests/test_inference/test_CausalInference.py
+++ b/pgmpy/tests/test_inference/test_CausalInference.py
@@ -16,6 +16,14 @@ class TestCausalGraphMethods(unittest.TestCase):
         )
         self.inference = CausalInference(self.game)
 
+        self.dag_bd1 = BayesianNetwork([("X", "Y"), ("Z1", "X"), ("Z1", "Y")])
+        self.inference_bd = CausalInference(self.dag_bd1)
+
+        self.dag_bd2 = BayesianNetwork(
+            [("X", "Y"), ("Z1", "X"), ("Z1", "Z2"), ("Z2", "Y")]
+        )
+        self.inference_bd2 = CausalInference(self.dag_bd2)
+
     def test_is_d_separated(self):
         self.assertTrue(self.inference.model.is_dconnected("X", "Y", observed=None))
         self.assertFalse(
@@ -23,7 +31,19 @@ class TestCausalGraphMethods(unittest.TestCase):
         )
 
     def test_backdoor_validation(self):
-        self.inference.is_valid_backdoor_adjustment_set("X", "Y", Z="C")
+        self.assertTrue(
+            self.inference.is_valid_backdoor_adjustment_set("X", "Y", Z="C")
+        )
+
+        # Z accepts str or set[str]
+        self.assertTrue(
+            self.inference_bd.is_valid_backdoor_adjustment_set("X", "Y", Z="Z1")
+        )
+        self.assertTrue(
+            self.inference_bd2.is_valid_backdoor_adjustment_set(
+                "X", "Y", Z={"Z1", "Z2"}
+            )
+        )
 
 
 class TestAdjustmentSet(unittest.TestCase):