diff --git a/pgmpy/base/DAG.py b/pgmpy/base/DAG.py
index 50cdb9f..2d9f4c0 100644
--- a/pgmpy/base/DAG.py
+++ b/pgmpy/base/DAG.py
@@ -443,3 +443,100 @@ class DAG(nx.DiGraph):
         blanket_nodes = set(blanket_nodes)
         blanket_nodes.remove(node)
         return list(blanket_nodes)
+
+    def active_trail_nodes(self, variables, observed=None):
+        """
+        Returns a dictionary with the given variables as keys and all the nodes reachable
+        from that respective variable as values.
+        Parameters
+        ----------
+        variables: str or array like
+            variables whose active trails are to be found.
+        observed : List of nodes (optional)
+            If given the active trails would be computed assuming these nodes to be observed.
+        Examples
+        --------
+        >>> from pgmpy.base import DAG
+        >>> student = DAG()
+        >>> student.add_nodes_from(['diff', 'intel', 'grades'])
+        >>> student.add_edges_from([('diff', 'grades'), ('intel', 'grades')])
+        >>> student.active_trail_nodes('diff')
+        {'diff': {'diff', 'grades'}}
+        >>> student.active_trail_nodes(['diff', 'intel'], observed='grades')
+        {'diff': {'diff', 'intel'}, 'intel': {'diff', 'intel'}}
+        References
+        ----------
+        Details of the algorithm can be found in 'Probabilistic Graphical Model
+        Principles and Techniques' - Koller and Friedman
+        Page 75 Algorithm 3.1
+        """
+        if observed:
+            observed_list = observed if isinstance(observed, (list, tuple)) else [observed]
+        else:
+            observed_list = []
+        ancestors_list = self._get_ancestors_of(observed_list)
+
+        # Direction of flow of information
+        # up ->  from parent to child
+        # down -> from child to parent
+
+        active_trails = {}
+        for start in variables if isinstance(variables, (list, tuple)) else [variables]:
+            visit_list = set()
+            visit_list.add((start, 'up'))
+            traversed_list = set()
+            active_nodes = set()
+            while visit_list:
+                node, direction = visit_list.pop()
+                if (node, direction) not in traversed_list:
+                    if node not in observed_list:
+                        active_nodes.add(node)
+                    traversed_list.add((node, direction))
+                    if direction == 'up' and node not in observed_list:
+                        for parent in self.predecessors(node):
+                            visit_list.add((parent, 'up'))
+                        for child in self.successors(node):
+                            visit_list.add((child, 'down'))
+                    elif direction == 'down':
+                        if node not in observed_list:
+                            for child in self.successors(node):
+                                visit_list.add((child, 'down'))
+                        if node in ancestors_list:
+                            for parent in self.predecessors(node):
+                                visit_list.add((parent, 'up'))
+            active_trails[start] = active_nodes
+        return active_trails
+
+    def _get_ancestors_of(self, obs_nodes_list):
+        """
+        Returns a dictionary of all ancestors of all the observed nodes including the
+        node itself.
+        Parameters
+        ----------
+        obs_nodes_list: string, list-type
+            name of all the observed nodes
+        Examples
+        --------
+        >>> from pgmpy.base import DAG
+        >>> model = DAG([('D', 'G'), ('I', 'G'), ('G', 'L'),
+        ...                        ('I', 'L')])
+        >>> model._get_ancestors_of('G')
+        {'D', 'G', 'I'}
+        >>> model._get_ancestors_of(['G', 'I'])
+        {'D', 'G', 'I'}
+        """
+        if not isinstance(obs_nodes_list, (list, tuple)):
+            obs_nodes_list = [obs_nodes_list]
+
+        for node in obs_nodes_list:
+            if node not in self.nodes():
+                raise ValueError('Node {s} not in not in graph'.format(s=node))
+
+        ancestors_list = set()
+        nodes_list = set(obs_nodes_list)
+        while nodes_list:
+            node = nodes_list.pop()
+            if node not in ancestors_list:
+                nodes_list.update(self.predecessors(node))
+            ancestors_list.add(node)
+        return ancestors_list
diff --git a/pgmpy/models/BayesianModel.py b/pgmpy/models/BayesianModel.py
index 22a3e44..b5e4c92 100644
--- a/pgmpy/models/BayesianModel.py
+++ b/pgmpy/models/BayesianModel.py
@@ -390,110 +390,6 @@ class BayesianModel(DAG):
                                      " is not equal to 1.".format(node=node))
         return True
 
-    def _get_ancestors_of(self, obs_nodes_list):
-        """
-        Returns a dictionary of all ancestors of all the observed nodes including the
-        node itself.
-
-        Parameters
-        ----------
-        obs_nodes_list: string, list-type
-            name of all the observed nodes
-
-        Examples
-        --------
-        >>> from pgmpy.models import BayesianModel
-        >>> model = BayesianModel([('D', 'G'), ('I', 'G'), ('G', 'L'),
-        ...                        ('I', 'L')])
-        >>> model._get_ancestors_of('G')
-        {'D', 'G', 'I'}
-        >>> model._get_ancestors_of(['G', 'I'])
-        {'D', 'G', 'I'}
-        """
-        if not isinstance(obs_nodes_list, (list, tuple)):
-            obs_nodes_list = [obs_nodes_list]
-
-        for node in obs_nodes_list:
-            if node not in self.nodes():
-                raise ValueError('Node {s} not in not in graph'.format(s=node))
-
-        ancestors_list = set()
-        nodes_list = set(obs_nodes_list)
-        while nodes_list:
-            node = nodes_list.pop()
-            if node not in ancestors_list:
-                nodes_list.update(self.predecessors(node))
-            ancestors_list.add(node)
-        return ancestors_list
-
-    def active_trail_nodes(self, variables, observed=None):
-        """
-        Returns a dictionary with the given variables as keys and all the nodes reachable
-        from that respective variable as values.
-
-        Parameters
-        ----------
-
-        variables: str or array like
-            variables whose active trails are to be found.
-
-        observed : List of nodes (optional)
-            If given the active trails would be computed assuming these nodes to be observed.
-
-        Examples
-        --------
-        >>> from pgmpy.models import BayesianModel
-        >>> student = BayesianModel()
-        >>> student.add_nodes_from(['diff', 'intel', 'grades'])
-        >>> student.add_edges_from([('diff', 'grades'), ('intel', 'grades')])
-        >>> student.active_trail_nodes('diff')
-        {'diff': {'diff', 'grades'}}
-        >>> student.active_trail_nodes(['diff', 'intel'], observed='grades')
-        {'diff': {'diff', 'intel'}, 'intel': {'diff', 'intel'}}
-
-        References
-        ----------
-        Details of the algorithm can be found in 'Probabilistic Graphical Model
-        Principles and Techniques' - Koller and Friedman
-        Page 75 Algorithm 3.1
-        """
-        if observed:
-            observed_list = observed if isinstance(observed, (list, tuple)) else [observed]
-        else:
-            observed_list = []
-        ancestors_list = self._get_ancestors_of(observed_list)
-
-        # Direction of flow of information
-        # up ->  from parent to child
-        # down -> from child to parent
-
-        active_trails = {}
-        for start in variables if isinstance(variables, (list, tuple)) else [variables]:
-            visit_list = set()
-            visit_list.add((start, 'up'))
-            traversed_list = set()
-            active_nodes = set()
-            while visit_list:
-                node, direction = visit_list.pop()
-                if (node, direction) not in traversed_list:
-                    if node not in observed_list:
-                        active_nodes.add(node)
-                    traversed_list.add((node, direction))
-                    if direction == 'up' and node not in observed_list:
-                        for parent in self.predecessors(node):
-                            visit_list.add((parent, 'up'))
-                        for child in self.successors(node):
-                            visit_list.add((child, 'down'))
-                    elif direction == 'down':
-                        if node not in observed_list:
-                            for child in self.successors(node):
-                                visit_list.add((child, 'down'))
-                        if node in ancestors_list:
-                            for parent in self.predecessors(node):
-                                visit_list.add((parent, 'up'))
-            active_trails[start] = active_nodes
-        return active_trails
-
     def local_independencies(self, variables):
         """
         Returns an instance of Independencies containing the local independencies

Move `BayesianModel.active_trail_nodes` method
Move BayesianModel.active_trail_nodes to DAG
diff --git a/pgmpy/tests/test_models/test_BayesianModel.py b/pgmpy/tests/test_models/test_BayesianModel.py
index be8d9c4..45b7c2f 100644
--- a/pgmpy/tests/test_models/test_BayesianModel.py
+++ b/pgmpy/tests/test_models/test_BayesianModel.py
@@ -11,6 +11,7 @@ import pgmpy.tests.help_functions as hf
 from pgmpy.factors.discrete import TabularCPD, JointProbabilityDistribution, DiscreteFactor
 from pgmpy.independencies import Independencies
 from pgmpy.estimators import BayesianEstimator, BaseEstimator, MaximumLikelihoodEstimator
+from pgmpy.base import DAG
 
 
 class TestBaseModelCreation(unittest.TestCase):
@@ -237,17 +238,19 @@ class TestBayesianModelCPD(unittest.TestCase):
     def setUp(self):
         self.G = BayesianModel([('d', 'g'), ('i', 'g'), ('g', 'l'),
                                 ('i', 's')])
+        self.G2 = DAG([('d', 'g'), ('i', 'g'), ('g', 'l'),
+                                ('i', 's')])
 
     def test_active_trail_nodes(self):
-        self.assertEqual(sorted(self.G.active_trail_nodes('d')['d']), ['d', 'g', 'l'])
-        self.assertEqual(sorted(self.G.active_trail_nodes('i')['i']), ['g', 'i', 'l', 's'])
-        self.assertEqual(sorted(self.G.active_trail_nodes(['d', 'i'])['d']), ['d', 'g', 'l'])
+        self.assertEqual(sorted(self.G2.active_trail_nodes('d')['d']), ['d', 'g', 'l'])
+        self.assertEqual(sorted(self.G2.active_trail_nodes('i')['i']), ['g', 'i', 'l', 's'])
+        self.assertEqual(sorted(self.G2.active_trail_nodes(['d', 'i'])['d']), ['d', 'g', 'l'])
 
     def test_active_trail_nodes_args(self):
-        self.assertEqual(sorted(self.G.active_trail_nodes(['d', 'l'], observed='g')['d']), ['d', 'i', 's'])
-        self.assertEqual(sorted(self.G.active_trail_nodes(['d', 'l'], observed='g')['l']), ['l'])
-        self.assertEqual(sorted(self.G.active_trail_nodes('s', observed=['i', 'l'])['s']), ['s'])
-        self.assertEqual(sorted(self.G.active_trail_nodes('s', observed=['d', 'l'])['s']), ['g', 'i', 's'])
+        self.assertEqual(sorted(self.G2.active_trail_nodes(['d', 'l'], observed='g')['d']), ['d', 'i', 's'])
+        self.assertEqual(sorted(self.G2.active_trail_nodes(['d', 'l'], observed='g')['l']), ['l'])
+        self.assertEqual(sorted(self.G2.active_trail_nodes('s', observed=['i', 'l'])['s']), ['s'])
+        self.assertEqual(sorted(self.G2.active_trail_nodes('s', observed=['d', 'l'])['s']), ['g', 'i', 's'])
 
     def test_is_active_trail_triplets(self):
         self.assertTrue(self.G.is_active_trail('d', 'l'))

pip install -e .[dev]
parse_log_pytest
requirements.txt
nose mock pytest
apt-get update apt-get install -y build-essential
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
decorator==4.4.2
importlib-metadata==4.8.3
iniconfig==1.1.1
mock==5.2.0
networkx==2.5.1
nose==1.3.7
numpy==1.19.5
packaging==21.3
pandas==1.1.5
-e git+https://github.com/top-tier-ci-coders/pgmpy.git@4117e98f53b348dcd78ab88549a6de1ad89f08ef#egg=pgmpy
pluggy==1.0.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
python-dateutil==2.9.0.post0
pytz==2025.2
scipy==1.5.4
six==1.17.0
tomli==1.2.3
typing_extensions==4.1.1
wrapt==1.16.0
zipp==3.6.0

name: pgmpy
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - decorator==4.4.2
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - mock==5.2.0
      - networkx==2.5.1
      - nose==1.3.7
      - numpy==1.19.5
      - packaging==21.3
      - pandas==1.1.5
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - scipy==1.5.4
      - six==1.17.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - wrapt==1.16.0
      - zipp==3.6.0
prefix: /opt/conda/envs/pgmpy

pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelCPD::test_active_trail_nodes pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelCPD::test_active_trail_nodes_args
pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelFitPredict::test_bayesian_fit pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelFitPredict::test_connected_predict pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelFitPredict::test_connected_predict_probability pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelFitPredict::test_disconnected_fit pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelFitPredict::test_fit_missing_data pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelFitPredict::test_predict pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelFitPredict::test_predict_probability_errors
pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_add_edge_nonstring pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_add_edge_result_cycle pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_add_edge_selfloop pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_add_edge_string pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_add_edges_from_nonstring pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_add_edges_from_result_cycle pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_add_edges_from_self_loop pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_add_edges_from_string pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_add_node_nonstring pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_add_node_string pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_add_nodes_from_non_string pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_add_nodes_from_string pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_class_init_with_data_nonstring pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_class_init_with_data_string pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_class_init_without_data pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_update_node_parents pgmpy/tests/test_models/test_BayesianModel.py::TestBaseModelCreation::test_update_node_parents_bm_constructor pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_copy pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_get_ancestors_of_failure pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_get_ancestors_of_success pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_get_cardinality pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_get_cardinality_with_node pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_get_immoralities pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_get_independencies pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_is_iequivalent pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_is_imap pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_local_independencies pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_markov_blanet pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_moral_graph pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_moral_graph_with_edge_present_over_parents pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_remove_node pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelMethods::test_remove_nodes_from pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelCPD::test_add_multiple_cpds pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelCPD::test_add_single_cpd pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelCPD::test_check_model pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelCPD::test_check_model1 pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelCPD::test_check_model2 pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelCPD::test_get_cpds pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelCPD::test_get_cpds1 pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelCPD::test_is_active_trail pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelCPD::test_is_active_trail_args pgmpy/tests/test_models/test_BayesianModel.py::TestBayesianModelCPD::test_is_active_trail_triplets pgmpy/tests/test_models/test_BayesianModel.py::TestDAGCPDOperations::test_add_multiple_cpds pgmpy/tests/test_models/test_BayesianModel.py::TestDAGCPDOperations::test_add_single_cpd pgmpy/tests/test_models/test_BayesianModel.py::TestDAGCPDOperations::test_get_values_for_node pgmpy/tests/test_models/test_BayesianModel.py::TestDAGCPDOperations::test_get_values_raises_error pgmpy/tests/test_models/test_BayesianModel.py::TestDAGCPDOperations::test_remove_multiple_cpds pgmpy/tests/test_models/test_BayesianModel.py::TestDAGCPDOperations::test_remove_multiple_cpds_string pgmpy/tests/test_models/test_BayesianModel.py::TestDAGCPDOperations::test_remove_single_cpd pgmpy/tests/test_models/test_BayesianModel.py::TestDAGCPDOperations::test_remove_single_cpd_string
pgmpy/models/BayesianModel.py pgmpy/base/DAG.py
pgmpy/models/BayesianModel.py pgmpy/base/DAG.py