Repository: pgmpy/pgmpy

Issue:
[ENH] Change default scoring method to `BIC-*`
Structure-learning algorithms such as `HillClimbSearch` and `GES` require a structure score for model learning. `pgmpy.estimators.StructureScore.get_scoring_method()` provides defaults based on data type when the user doesn’t specify one. The current defaults are:

1. Continuous: Log-Likelihood
2. Discrete: K2
3. Mixed: Log-Likelihood

Because Log-Likelihood doesn’t include a complexity penalty, it often produces overly dense graphs. We should switch the default to a BIC-based score (Bayesian Information Criterion), which adds a model complexity penalty.

Changes required:
1. Modify `get_scoring_method()` to return the BIC-based structure scoring method (for data types where Log-Likelihood is currently the default).
2. Update tests to reflect the new default and ensure they pass.


Discussion:

Patch:
diff --git a/pgmpy/estimators/StructureScore.py b/pgmpy/estimators/StructureScore.py
index 41790f788..dc2867ccb 100644
--- a/pgmpy/estimators/StructureScore.py
+++ b/pgmpy/estimators/StructureScore.py
@@ -19,22 +19,22 @@ def get_scoring_method(
 ) -> Tuple["StructureScore", "StructureScore"]:
     available_methods = {
         "continuous": {
+            "bic-g": BICGauss,
             "ll-g": LogLikelihoodGauss,
             "aic-g": AICGauss,
-            "bic-g": BICGauss,
         },
         "discrete": {
+            "bic-d": BIC,
             "k2": K2,
             "bdeu": BDeu,
             "bds": BDs,
-            "bic-d": BIC,
             "aic-d": AIC,
             "ll-d": LogLikeliHood,
         },
         "mixed": {
+            "bic-cg": BICCondGauss,
             "ll-cg": LogLikelihoodCondGauss,
             "aic-cg": AICCondGauss,
-            "bic-cg": BICCondGauss,
         },
     }
     all_available_methods = [


Test patch:
diff --git a/pgmpy/tests/test_estimators/test_ExhaustiveSearch.py b/pgmpy/tests/test_estimators/test_ExhaustiveSearch.py
index aab8cca68..beae952b3 100644
--- a/pgmpy/tests/test_estimators/test_ExhaustiveSearch.py
+++ b/pgmpy/tests/test_estimators/test_ExhaustiveSearch.py
@@ -2,8 +2,9 @@
 
 import numpy as np
 import pandas as pd
+import networkx as nx
 
-from pgmpy.estimators import BDeu, BIC, ExhaustiveSearch
+from pgmpy.estimators import BDeu, BIC, ExhaustiveSearch, K2
 
 
 class TestBaseEstimator(unittest.TestCase):
@@ -71,7 +72,8 @@ def test_all_dags(self):
         self.assertSetEqual(abc_dags, abc_dags_ref)
 
     def test_estimate_rand(self):
-        est = self.est_rand.estimate()
+        est_k2 = ExhaustiveSearch(self.rand_data, scoring_method=K2(self.rand_data))
+        est = est_k2.estimate()
         self.assertSetEqual(set(est.nodes()), set(["A", "B", "C"]))
         self.assertEqual(set(est.edges()), {("B", "A"), ("B", "C"), ("C", "A")})
 
@@ -79,17 +81,23 @@ def test_estimate_rand(self):
         self.assertEqual(set(est.edges()), {("B", "A"), ("B", "C"), ("C", "A")})
 
         est_bic = self.est_rand.estimate()
-        self.assertEqual(set(est_bic.edges()), {("B", "A"), ("B", "C"), ("C", "A")})
+        self.assertEqual(set(est_bic.edges()), {("B", "C")})
 
     def test_estimate_titanic(self):
-        e1 = self.est_titanic.estimate()
+        est_k2 = ExhaustiveSearch(
+            self.titanic_data2, scoring_method=K2(self.titanic_data2)
+        )
+        e1 = est_k2.estimate()
         self.assertSetEqual(
             set(e1.edges()),
             set([("Survived", "Pclass"), ("Sex", "Pclass"), ("Sex", "Survived")]),
         )
 
     def test_all_scores(self):
-        scores = self.est_titanic.all_scores()
+        est_k2 = ExhaustiveSearch(
+            self.titanic_data2, scoring_method=K2(self.titanic_data2)
+        )
+        scores = est_k2.all_scores()
         scores_ref = [
             (-2072.9132364404695, []),
             (-2069.071694164769, [("Pclass", "Sex")]),
@@ -147,6 +155,26 @@ def test_all_scores(self):
             [score for score, edges in scores_ref],
         )
 
+    def test_estimate_rand_bic_default(self):
+        """
+        Tests the new default BIC scoring method.
+        """
+        est_bic = self.est_rand.estimate()
+        self.assertSetEqual(set(est_bic.nodes()), set(["A", "B", "C"]))
+        self.assertEqual(set(est_bic.edges()), {("B", "C")})
+        self.assertTrue(nx.is_directed_acyclic_graph(est_bic))
+
+    def test_estimate_titanic_bic_default(self):
+        """
+        Tests the new default BIC scoring method on the titanic dataset.
+        """
+        e1_bic = self.est_titanic.estimate()
+        self.assertSetEqual(
+            set(e1_bic.edges()),
+            set([("Sex", "Survived"), ("Pclass", "Survived"), ("Pclass", "Sex")]),
+        )
+        self.assertTrue(nx.is_directed_acyclic_graph(e1_bic))
+
     def tearDown(self):
         del self.rand_data
         del self.est_rand