Skip to content

[Refactor] Add POC skpro adapter and external-ML fit path for FunctionalCPD#60

Merged
daehyun99 merged 1 commit intowipfrom
codex/refactor-functionalcpd-and-skproadapter
Mar 31, 2026
Merged

[Refactor] Add POC skpro adapter and external-ML fit path for FunctionalCPD#60
daehyun99 merged 1 commit intowipfrom
codex/refactor-functionalcpd-and-skproadapter

Conversation

@daehyun99
Copy link
Copy Markdown
Owner

Motivation

  • Provide a minimal proof-of-concept path to allow FunctionalCPD to use external (skpro-like) estimators when a user specifies a skpro tag and supplies a model object.
  • Keep the FunctionalBayesianNetwork orchestration simple so model.fit(data) delegates fitting to each CPD implementation.

Description

  • Implemented _fit_external_ml() in FunctionalCPD_Refactor to instantiate SkproAdapter and run .fit(X, y) via the provided estimator, and allowed estimator=None by default on the CPD constructor.
  • Added SkproAdapter (pgmpy/factors/hybrid/SkproAdapter.py) which wraps an external estimator and maps parent columns to X and the CPD variable to y before calling model.fit(X, y).
  • Extended tag handling so FunctionalCPD_Refactor recognizes skpro and tags starting with skpro. (e.g. tag=['skpro.BayesianLinearRegressor']).
  • Updated FunctionalBayesianNetwork_Refactor to import the refactor CPD class and left fit orchestration unchanged so each CPD is responsible for fitting itself.
  • Added a focused test that uses a DummySkproRegressor to verify the adapter receives the correct X/y shapes and that model.fit(data) triggers the external fit flow.

Testing

  • Ran pytest -v pgmpy/tests/test_models/test_FunctionalBayesianNetwork_Refactor.py and the suite passed (2 passed).
  • Attempted pre-commit run --all-files but the environment lacks pre-commit (command not found) so hooks were not executed here.

Codex Task

@daehyun99 daehyun99 merged commit bf55ce0 into wip Mar 31, 2026
17 checks passed
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the SkproAdapter class, enabling FunctionalCPD to integrate with external skpro-like estimators. The changes include refactoring the FunctionalCPD fitting logic to support skpro tags and adding corresponding unit tests. Review feedback highlights that both SkproAdapter and FunctionalCPD are missing sample methods necessary for model simulation and inference. Additionally, FunctionalCPD needs to initialize the variables attribute to ensure compatibility with the BaseFactor class requirements.

@@ -0,0 +1,22 @@
class SkproAdapter:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The SkproAdapter class is missing a sample method. In pgmpy, CPDs are expected to provide a way to generate samples (e.g., for use in model.simulate()). Since skpro models are probabilistic estimators, the adapter should implement a sample method that delegates to the underlying model's prediction or sampling API to ensure the fitted model is actually usable for inference.

Comment on lines +11 to 14
def __init__(self, variable, tag, estimator=None):
self.variable = variable
self.tag = tag
self.estimator = estimator
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The FunctionalCPD class inherits from BaseFactor, which requires a variables attribute to function correctly within pgmpy models. This attribute is currently not initialized in the constructor. Additionally, since the parents argument was removed from the constructor in this refactor, you should ensure self.variables is correctly updated during the fit process (e.g., self.variables = [self.variable] + self.parents_) to maintain compatibility with methods like check_model().

Suggested change
def __init__(self, variable, tag, estimator=None):
self.variable = variable
self.tag = tag
self.estimator = estimator
def __init__(self, variable, tag, estimator=None):
self.variable = variable
self.variables = [variable]
self.tag = tag
self.estimator = estimator

Comment on lines +90 to +96
def _fit_external_ml(self):
if self.estimator is None:
raise ValueError("For skpro tag, `estimator` must be provided.")

self.fitted_cpd_ = SkproAdapter(variable=self.variable, model=self.estimator, parents=self.parents_).fit(
self.data_
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The refactored FunctionalCPD class is missing a sample method. As this class acts as a wrapper for various fitted CPD implementations (such as TabularCPD, LinearGaussianCPD, and the new SkproAdapter), it must provide a unified sample interface that delegates the call to self.fitted_cpd_.sample(...). Without this, any model containing this CPD will fail during simulation or sampling-based inference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant