Add component registry and feature contracts#448
Draft
nictru wants to merge 11 commits into
Draft
Conversation
4 tasks
Drop view/backend/scope/node/edge fields from FeatureContract and match compatibility on feature kind alone. Update featurizer contracts and tests.
Introduce contract normalization/resolvers and attach featurizer and predictor contracts from registry decorators using canonical names.
Remove stale ClassVar imports after decorator contract migration, defer zoo loading until the zoo module exists, and satisfy mypy for registry tests and Ray resource typing.
Introduce view aliases, bracket-aware featurizer parsing, a generic raw pass-through featurizer, required-view PCA, and distinct concat block labels for repeated parametric featurizers.
Skip redundant TOY dataset re-downloads and ensure BPE/SMILESVec embeddings are created after TOY fixtures load, with synthetic fallbacks when BPE featurization fails.
Drop pass-through omics featurizers and methylationPCA in favor of raw[view] and pca[methylation]. Move scaled gene expression out of omics and register proteomics preprocessing as normalizedProteomics.
nictru
commented
Jul 6, 2026
Comment on lines
+32
to
+99
| @classmethod | ||
| def distribute_legacy_state( | ||
| cls, | ||
| featurizer: ConcatFeaturizersCellLineFeaturizer, | ||
| state: dict[str, object], | ||
| ) -> None: | ||
| """Map legacy flat preprocessing state onto child featurizers when possible.""" | ||
| from drevalpy.components.featurizers.cell_line.normalized_proteomics import ( | ||
| NormalizedProteomicsCellLineFeaturizer, | ||
| ) | ||
| from drevalpy.components.featurizers.cell_line.pca import PCACellLineFeaturizer | ||
| from drevalpy.components.featurizers.cell_line.scaled_gene_expression import ( | ||
| ScaledGeneExpressionFeaturizer, | ||
| ) | ||
|
|
||
| if not featurizer._children: | ||
| featurizer._materialize_children() | ||
|
|
||
| for name, child in featurizer._children: | ||
| if isinstance(child, ScaledGeneExpressionFeaturizer): | ||
| child_state = {key: state[key] for key in ("gene_expression_scaler", "fitted") if key in state} | ||
| if child_state: | ||
| child.set_state(child_state) | ||
| elif isinstance(child, PCACellLineFeaturizer) and "methylation_pca" in state: | ||
| methylation_pca = state["methylation_pca"] | ||
| child_state = { | ||
| "pca": methylation_pca, | ||
| "view": child._view, | ||
| "fitted": state.get("fitted"), | ||
| } | ||
| if hasattr(methylation_pca, "n_components"): | ||
| child_state["n_components"] = int(methylation_pca.n_components) | ||
| child_state["output_dim"] = int(methylation_pca.n_components) | ||
| child.set_state(child_state) | ||
| elif isinstance(child, NormalizedProteomicsCellLineFeaturizer): | ||
| child_state = {key: state[key] for key in ("proteomics_transformer",) if key in state} | ||
| if child_state: | ||
| child.set_state(child_state) | ||
| _ = name | ||
| if state.get("view_dims") and isinstance(state["view_dims"], dict): | ||
| featurizer._block_dims = {str(key): int(value) for key, value in state["view_dims"].items()} | ||
| output_dim = state.get("output_dim") | ||
| if isinstance(output_dim, int): | ||
| featurizer._output_dim = output_dim | ||
| if state.get("fitted"): | ||
| featurizer._is_fitted = True | ||
|
|
||
| @classmethod | ||
| def collect_legacy_state( | ||
| cls, | ||
| featurizer: ConcatFeaturizersCellLineFeaturizer, | ||
| ) -> dict[str, object]: | ||
| """Flatten child featurizer state for legacy sklearn save/load.""" | ||
| if not featurizer._children: | ||
| featurizer._materialize_children() | ||
| state: dict[str, object] = {"fitted": featurizer._is_fitted} | ||
| for _name, child in featurizer._children: | ||
| child_state = child.get_state() | ||
| if not isinstance(child_state, dict): | ||
| continue | ||
| for key, value in child_state.items(): | ||
| if key != "fitted": | ||
| state[key] = value | ||
| if featurizer._block_dims: | ||
| state["view_dims"] = dict(featurizer._block_dims) | ||
| if featurizer._output_dim: | ||
| state["output_dim"] = featurizer._output_dim | ||
| return state |
Collaborator
Author
There was a problem hiding this comment.
Do we need all the legacy stuff here?
nictru
commented
Jul 6, 2026
| self, | ||
| *, | ||
| featurizers: list[Any] | None = None, | ||
| registry: str = "drug", |
Collaborator
Author
There was a problem hiding this comment.
why is registry configurable? it should probably always be drug here
Require explicit contracts at registration, mark bpePharmaformer as literature, and drop the unused n_bits hyperparameter from view-based fingerprints.
Drug ID one-hot encoding is handled solely by identity, including in concat specs like fingerprints+identity.
Replace PairBatch with ModelInputBatch, add to_feature_matrix(), and make predictor fit/predict batch-based while introducing MatrixPredictor and BlockPredictor helpers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
As the full modularity PR is quite large, I decided to split it into six smaller ones. Also these smaller PRs do not go directly into development, but rather first in a
modularity-aggregatedbranch, which can then hopefully be merged into development relatively easily, as you already reviewed its smaller parts.Below, you can find an AI-generated overview of the changes in this branch:
Summary
Introduces the component foundation for the modularity refactor: a new
drevalpy.componentspackage with registries, featurizer/predictor contracts, and shared feature utilities. This PR is additive only — existingMODEL_FACTORYmodels,drevalpy.modelsimplementations, experiment workflows, and HPO behavior are unchanged.This is PR 1 of 6 in the stacked modularity series. Later PRs will wire baselines, literature models, and structured HPO onto this layer.
What’s added
Component registry & contracts
drevalpy/components/registry/)FeatureKind/FeatureContractfor featurizer–predictor compatibility (contracts.py)extensions.py)Featurizers (implementation, not yet wired into
MODEL_FACTORY)drevalpy/components/featurizers/(omics layers, PCA, fingerprints, drug graphs, concat helpers, identity/tissue featurizers, etc.)Predictor contracts (base only)
predictors/base.py,structured.py,baseline.py— interfaces only; no baseline or literature predictor migrations yetShared data & pair-batch utilities
drevalpy/data/features.pyandpreprocessing.py— feature loading/preprocessing helpers used by featurizerspair_batch.py,pair_batch_build.py,pair_features.py— pair-level feature batching primitivesMinimal config & deps for the new layer
drevalpy/models/config.py—FeaturizerConfig/ModelConfigtypes needed by concat featurizers (noComposedModelor factory wiring yet)pyproject.toml— makespydantica required dependencysetup.cfg— flake8 per-file ignores for the newcomponents/pathsRegistration
register_builtins.pyregisters featurizers only at this stage (no predictors or literature components yet)What’s intentionally unchanged
MODEL_FACTORYand all existingdrevalpy.models.*model classesdrp_model.py,experiment.py, HPO / YAML gridsCompatibility
All existing imports and
MODEL_FACTORYbehavior should matchdevelopment. The new package is present but not used by the default model runtime until later PRs in the stack.Tests
Focused unit tests for the new layer (45 tests), including:
Suggested review focus
Featurizer,Predictor, registries,FeatureContract) the right boundaries for later PRs?ModelConfigin the right place at this stage, or should more config orchestration wait for PR 2?