diffbio 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- diffbio/__init__.py +39 -0
- diffbio/configs.py +75 -0
- diffbio/constants.py +204 -0
- diffbio/core/__init__.py +127 -0
- diffbio/core/base_operators.py +612 -0
- diffbio/core/data_types.py +260 -0
- diffbio/core/gnn_components.py +629 -0
- diffbio/core/graph_utils.py +149 -0
- diffbio/core/neural_components.py +270 -0
- diffbio/core/optimal_transport.py +133 -0
- diffbio/core/soft_ops/__init__.py +216 -0
- diffbio/core/soft_ops/_projections_permutahedron.py +1864 -0
- diffbio/core/soft_ops/_projections_simplex.py +240 -0
- diffbio/core/soft_ops/_projections_transport.py +508 -0
- diffbio/core/soft_ops/_sorting_network.py +204 -0
- diffbio/core/soft_ops/_types.py +15 -0
- diffbio/core/soft_ops/_utils.py +342 -0
- diffbio/core/soft_ops/autograd_safe.py +120 -0
- diffbio/core/soft_ops/comparison.py +235 -0
- diffbio/core/soft_ops/elementwise.py +309 -0
- diffbio/core/soft_ops/logical.py +146 -0
- diffbio/core/soft_ops/quantile.py +376 -0
- diffbio/core/soft_ops/selection.py +236 -0
- diffbio/core/soft_ops/sorting.py +926 -0
- diffbio/core/soft_ops/straight_through.py +261 -0
- diffbio/core/uncertainty.py +279 -0
- diffbio/evaluation/__init__.py +42 -0
- diffbio/evaluation/adapters.py +409 -0
- diffbio/evaluation/graders.py +223 -0
- diffbio/evaluation/problem.py +157 -0
- diffbio/evaluation/runner.py +277 -0
- diffbio/losses/__init__.py +59 -0
- diffbio/losses/alignment_losses.py +222 -0
- diffbio/losses/biological_regularization.py +288 -0
- diffbio/losses/metric_losses.py +139 -0
- diffbio/losses/singlecell_losses.py +387 -0
- diffbio/losses/statistical_losses.py +345 -0
- diffbio/operators/__init__.py +60 -0
- diffbio/operators/_count_vae.py +197 -0
- diffbio/operators/_loss_balancing.py +65 -0
- diffbio/operators/_masked_gene_transformer.py +118 -0
- diffbio/operators/_transformer_validation.py +50 -0
- diffbio/operators/alignment/__init__.py +51 -0
- diffbio/operators/alignment/profile_hmm.py +350 -0
- diffbio/operators/alignment/scoring.py +127 -0
- diffbio/operators/alignment/smith_waterman.py +261 -0
- diffbio/operators/alignment/soft_msa.py +419 -0
- diffbio/operators/assembly/__init__.py +27 -0
- diffbio/operators/assembly/gnn_assembly.py +252 -0
- diffbio/operators/assembly/metagenomic_binning.py +296 -0
- diffbio/operators/crispr/__init__.py +17 -0
- diffbio/operators/crispr/guide_scoring.py +269 -0
- diffbio/operators/drug_discovery/__init__.py +133 -0
- diffbio/operators/drug_discovery/_graph_utils.py +142 -0
- diffbio/operators/drug_discovery/admet_predictor.py +285 -0
- diffbio/operators/drug_discovery/attentive_fp.py +411 -0
- diffbio/operators/drug_discovery/dti.py +261 -0
- diffbio/operators/drug_discovery/fingerprint.py +490 -0
- diffbio/operators/drug_discovery/maccs_keys.py +267 -0
- diffbio/operators/drug_discovery/message_passing.py +200 -0
- diffbio/operators/drug_discovery/primitives.py +242 -0
- diffbio/operators/drug_discovery/property_predictor.py +163 -0
- diffbio/operators/drug_discovery/similarity.py +193 -0
- diffbio/operators/epigenomics/__init__.py +35 -0
- diffbio/operators/epigenomics/chromatin_state.py +491 -0
- diffbio/operators/epigenomics/contextual.py +288 -0
- diffbio/operators/epigenomics/fno_peak_calling.py +153 -0
- diffbio/operators/epigenomics/peak_calling.py +555 -0
- diffbio/operators/foundation_models/__init__.py +119 -0
- diffbio/operators/foundation_models/adapters.py +114 -0
- diffbio/operators/foundation_models/contracts.py +245 -0
- diffbio/operators/foundation_models/embedding_probe.py +83 -0
- diffbio/operators/foundation_models/experimental.py +128 -0
- diffbio/operators/foundation_models/foundation_model.py +332 -0
- diffbio/operators/foundation_models/frozen.py +59 -0
- diffbio/operators/foundation_models/precomputed.py +270 -0
- diffbio/operators/foundation_models/transformer_encoder.py +564 -0
- diffbio/operators/mapping/__init__.py +17 -0
- diffbio/operators/mapping/neural_mapper.py +493 -0
- diffbio/operators/metabolomics/__init__.py +39 -0
- diffbio/operators/metabolomics/spectral_similarity.py +315 -0
- diffbio/operators/molecular_dynamics/__init__.py +51 -0
- diffbio/operators/molecular_dynamics/force_field.py +265 -0
- diffbio/operators/molecular_dynamics/integrator.py +304 -0
- diffbio/operators/molecular_dynamics/primitives.py +115 -0
- diffbio/operators/multiomics/__init__.py +38 -0
- diffbio/operators/multiomics/hic_contact.py +377 -0
- diffbio/operators/multiomics/multiomics_vae.py +325 -0
- diffbio/operators/multiomics/spatial_deconvolution.py +316 -0
- diffbio/operators/multiomics/spatial_gene_detection.py +493 -0
- diffbio/operators/normalization/__init__.py +42 -0
- diffbio/operators/normalization/embedding.py +222 -0
- diffbio/operators/normalization/phate.py +400 -0
- diffbio/operators/normalization/umap.py +261 -0
- diffbio/operators/normalization/vae_normalizer.py +258 -0
- diffbio/operators/population/__init__.py +17 -0
- diffbio/operators/population/ancestry_estimation.py +274 -0
- diffbio/operators/preprocessing/__init__.py +76 -0
- diffbio/operators/preprocessing/adapter_removal.py +311 -0
- diffbio/operators/preprocessing/duplicate_filter.py +317 -0
- diffbio/operators/preprocessing/error_correction.py +287 -0
- diffbio/operators/protein/__init__.py +31 -0
- diffbio/operators/protein/secondary_structure.py +509 -0
- diffbio/operators/quality_filter.py +128 -0
- diffbio/operators/rna_structure/__init__.py +35 -0
- diffbio/operators/rna_structure/rna_folding.py +509 -0
- diffbio/operators/rnaseq/__init__.py +23 -0
- diffbio/operators/rnaseq/motif_discovery.py +251 -0
- diffbio/operators/rnaseq/splicing_psi.py +216 -0
- diffbio/operators/singlecell/__init__.py +193 -0
- diffbio/operators/singlecell/ambient_removal.py +333 -0
- diffbio/operators/singlecell/archetypes.py +191 -0
- diffbio/operators/singlecell/batch_correction.py +288 -0
- diffbio/operators/singlecell/cell_annotation.py +519 -0
- diffbio/operators/singlecell/communication.py +704 -0
- diffbio/operators/singlecell/differential_distribution.py +243 -0
- diffbio/operators/singlecell/doublet_detection.py +657 -0
- diffbio/operators/singlecell/downsampling.py +166 -0
- diffbio/operators/singlecell/enhanced_batch_correction.py +519 -0
- diffbio/operators/singlecell/grn_inference.py +336 -0
- diffbio/operators/singlecell/imputation.py +429 -0
- diffbio/operators/singlecell/knockdown_filter.py +176 -0
- diffbio/operators/singlecell/ot_trajectory.py +277 -0
- diffbio/operators/singlecell/simulation.py +444 -0
- diffbio/operators/singlecell/sindy_grn.py +247 -0
- diffbio/operators/singlecell/soft_clustering.py +211 -0
- diffbio/operators/singlecell/spatial_domains.py +677 -0
- diffbio/operators/singlecell/switch_de.py +184 -0
- diffbio/operators/singlecell/trajectory.py +447 -0
- diffbio/operators/singlecell/velocity.py +361 -0
- diffbio/operators/statistical/__init__.py +35 -0
- diffbio/operators/statistical/em_quantification.py +260 -0
- diffbio/operators/statistical/hmm.py +234 -0
- diffbio/operators/statistical/nb_glm.py +272 -0
- diffbio/operators/variant/__init__.py +64 -0
- diffbio/operators/variant/classifier.py +333 -0
- diffbio/operators/variant/cnn_classifier.py +255 -0
- diffbio/operators/variant/cnv_segmentation.py +678 -0
- diffbio/operators/variant/deepvariant_pileup.py +426 -0
- diffbio/operators/variant/pileup.py +240 -0
- diffbio/operators/variant/quality_recalibration.py +274 -0
- diffbio/pipelines/__init__.py +65 -0
- diffbio/pipelines/differential_expression.py +279 -0
- diffbio/pipelines/enhanced_variant_calling.py +326 -0
- diffbio/pipelines/perturbation.py +407 -0
- diffbio/pipelines/preprocessing.py +267 -0
- diffbio/pipelines/single_cell.py +366 -0
- diffbio/pipelines/variant_calling.py +490 -0
- diffbio/samplers/__init__.py +9 -0
- diffbio/samplers/perturbation_sampler.py +142 -0
- diffbio/sequences/__init__.py +34 -0
- diffbio/sequences/dna.py +239 -0
- diffbio/sources/__init__.py +149 -0
- diffbio/sources/_anndata_shared.py +89 -0
- diffbio/sources/_batch_iteration.py +37 -0
- diffbio/sources/_benchmark_source.py +152 -0
- diffbio/sources/_indexed_batch_source.py +38 -0
- diffbio/sources/_utils.py +45 -0
- diffbio/sources/anndata_interop.py +387 -0
- diffbio/sources/anndata_source.py +361 -0
- diffbio/sources/archive_ii.py +174 -0
- diffbio/sources/balifam.py +207 -0
- diffbio/sources/bam.py +265 -0
- diffbio/sources/bengrn_ground_truth.py +306 -0
- diffbio/sources/contextual_epigenomics.py +242 -0
- diffbio/sources/dti.py +359 -0
- diffbio/sources/embeddings.py +203 -0
- diffbio/sources/encode_peaks.py +223 -0
- diffbio/sources/fasta.py +226 -0
- diffbio/sources/immune_human.py +172 -0
- diffbio/sources/indexed_embeddings.py +128 -0
- diffbio/sources/indexed_view.py +191 -0
- diffbio/sources/molnet.py +493 -0
- diffbio/sources/multiomics.py +279 -0
- diffbio/sources/pancreas.py +108 -0
- diffbio/sources/perturbation/__init__.py +69 -0
- diffbio/sources/perturbation/_types.py +51 -0
- diffbio/sources/perturbation/_utils.py +125 -0
- diffbio/sources/perturbation/concat_source.py +115 -0
- diffbio/sources/perturbation/control_mapping.py +215 -0
- diffbio/sources/perturbation/experiment_config.py +261 -0
- diffbio/sources/perturbation/h5_metadata_cache.py +218 -0
- diffbio/sources/perturbation/output_space.py +52 -0
- diffbio/sources/perturbation/perturbation_source.py +513 -0
- diffbio/sources/seqfish.py +145 -0
- diffbio/sources/sequence_foundation.py +68 -0
- diffbio/sources/singlecell_foundation.py +68 -0
- diffbio/splitters/__init__.py +63 -0
- diffbio/splitters/base.py +251 -0
- diffbio/splitters/molecular.py +330 -0
- diffbio/splitters/perturbation.py +199 -0
- diffbio/splitters/random.py +217 -0
- diffbio/splitters/sequence.py +201 -0
- diffbio/utils/__init__.py +55 -0
- diffbio/utils/dependency_runtime.py +115 -0
- diffbio/utils/nn_utils.py +157 -0
- diffbio/utils/quality.py +45 -0
- diffbio/utils/training.py +585 -0
- diffbio-0.1.0.dist-info/METADATA +480 -0
- diffbio-0.1.0.dist-info/RECORD +202 -0
- diffbio-0.1.0.dist-info/WHEEL +4 -0
- diffbio-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Shared adapter interfaces for benchmark-facing foundation-model integrations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from typing import Any, Protocol
|
|
7
|
+
|
|
8
|
+
import jax.numpy as jnp
|
|
9
|
+
|
|
10
|
+
from diffbio.operators.foundation_models.contracts import (
|
|
11
|
+
FoundationArtifactSpec,
|
|
12
|
+
build_foundation_model_metadata,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
_CANONICAL_BENCHMARK_METADATA_KEYS = (
|
|
16
|
+
"embedding_source",
|
|
17
|
+
"foundation_source_name",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _validate_benchmark_text(value: str, *, field_name: str) -> None:
|
|
22
|
+
"""Require non-empty adapter metadata text for stable benchmark contracts."""
|
|
23
|
+
if not value:
|
|
24
|
+
raise ValueError(f"{field_name} must be non-empty.")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class FoundationBenchmarkAdapter(Protocol):
|
|
28
|
+
"""Common benchmark-facing contract for foundation-model adapters."""
|
|
29
|
+
|
|
30
|
+
def result_data(self) -> dict[str, Any]:
|
|
31
|
+
"""Return canonical operator metadata for benchmark tagging."""
|
|
32
|
+
...
|
|
33
|
+
|
|
34
|
+
def benchmark_metadata(self) -> dict[str, Any]:
|
|
35
|
+
"""Return benchmark metadata describing the adapter source."""
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SequenceFoundationAdapter(FoundationBenchmarkAdapter, Protocol):
|
|
40
|
+
"""Shared contract for sequence foundation-model adapters."""
|
|
41
|
+
|
|
42
|
+
def load_dataset_embeddings(
|
|
43
|
+
self,
|
|
44
|
+
*,
|
|
45
|
+
reference_sequence_ids: Sequence[str],
|
|
46
|
+
one_hot_sequences: Any,
|
|
47
|
+
) -> jnp.ndarray:
|
|
48
|
+
"""Return embeddings aligned to a benchmark dataset order."""
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class FoundationBenchmarkAdapterBase:
|
|
53
|
+
"""Base implementation for stable benchmark metadata handling."""
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
*,
|
|
58
|
+
artifact_spec: FoundationArtifactSpec,
|
|
59
|
+
source_name: str,
|
|
60
|
+
embedding_source: str,
|
|
61
|
+
extra_metadata: dict[str, Any] | None = None,
|
|
62
|
+
) -> None:
|
|
63
|
+
_validate_benchmark_text(source_name, field_name="source_name")
|
|
64
|
+
_validate_benchmark_text(embedding_source, field_name="embedding_source")
|
|
65
|
+
normalized_extra_metadata = (
|
|
66
|
+
{} if extra_metadata is None else dict(sorted(extra_metadata.items()))
|
|
67
|
+
)
|
|
68
|
+
conflicting_keys = [
|
|
69
|
+
key for key in _CANONICAL_BENCHMARK_METADATA_KEYS if key in normalized_extra_metadata
|
|
70
|
+
]
|
|
71
|
+
if conflicting_keys:
|
|
72
|
+
keys = ", ".join(conflicting_keys)
|
|
73
|
+
raise ValueError(f"extra_metadata cannot override canonical key(s): {keys}")
|
|
74
|
+
|
|
75
|
+
self.artifact_spec = artifact_spec
|
|
76
|
+
self.source_name = source_name
|
|
77
|
+
self.embedding_source = embedding_source
|
|
78
|
+
self.extra_metadata = normalized_extra_metadata
|
|
79
|
+
|
|
80
|
+
def result_data(self) -> dict[str, Any]:
|
|
81
|
+
"""Return benchmark-ready foundation-model metadata."""
|
|
82
|
+
return {"foundation_model": build_foundation_model_metadata(self.artifact_spec)}
|
|
83
|
+
|
|
84
|
+
def benchmark_metadata(self) -> dict[str, Any]:
|
|
85
|
+
"""Return benchmark metadata describing the adapter source."""
|
|
86
|
+
metadata: dict[str, Any] = {
|
|
87
|
+
"embedding_source": self.embedding_source,
|
|
88
|
+
"foundation_source_name": self.source_name,
|
|
89
|
+
}
|
|
90
|
+
metadata.update(self.extra_metadata)
|
|
91
|
+
return metadata
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
_FOUNDATION_ADAPTER_REGISTRY: dict[str, type[object]] = {}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def register_foundation_adapter(adapter_key: str, adapter_cls: type[object]) -> None:
|
|
98
|
+
"""Register a benchmark-facing foundation-model adapter class."""
|
|
99
|
+
_validate_benchmark_text(adapter_key, field_name="adapter_key")
|
|
100
|
+
_FOUNDATION_ADAPTER_REGISTRY[adapter_key] = adapter_cls
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_foundation_adapter_cls(adapter_key: str) -> type[object]:
|
|
104
|
+
"""Return the registered adapter class for a canonical adapter key."""
|
|
105
|
+
try:
|
|
106
|
+
return _FOUNDATION_ADAPTER_REGISTRY[adapter_key]
|
|
107
|
+
except KeyError as exc:
|
|
108
|
+
raise KeyError(f"No adapter registered for {adapter_key!r}.") from exc
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def create_foundation_adapter(adapter_key: str, *args: Any, **kwargs: Any) -> object:
|
|
112
|
+
"""Instantiate a registered foundation-model adapter."""
|
|
113
|
+
adapter_cls = get_foundation_adapter_cls(adapter_key)
|
|
114
|
+
return adapter_cls(*args, **kwargs)
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""Shared contracts for DiffBio foundation-model operators.
|
|
2
|
+
|
|
3
|
+
This module centralizes the common metadata, output schema, and registry used
|
|
4
|
+
by DiffBio foundation-model operators. The contract is intentionally shared
|
|
5
|
+
across sequence, single-cell, and future imported biological foundation models
|
|
6
|
+
so downstream code can rely on one stable interface instead of task-specific
|
|
7
|
+
ad hoc keys.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from enum import StrEnum
|
|
14
|
+
from typing import Any, Mapping
|
|
15
|
+
|
|
16
|
+
import jax.numpy as jnp
|
|
17
|
+
from datarax.core.config import OperatorConfig
|
|
18
|
+
from datarax.core.operator import OperatorModule
|
|
19
|
+
from jaxtyping import Array, PyTree
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FoundationModelKind(StrEnum):
|
|
23
|
+
"""Supported high-level foundation-model families."""
|
|
24
|
+
|
|
25
|
+
SEQUENCE_TRANSFORMER = "sequence_transformer"
|
|
26
|
+
SINGLE_CELL_TRANSFORMER = "single_cell_transformer"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AdapterMode(StrEnum):
|
|
30
|
+
"""How DiffBio integrates the underlying foundation model."""
|
|
31
|
+
|
|
32
|
+
PRECOMPUTED = "precomputed"
|
|
33
|
+
FROZEN_ENCODER = "frozen_encoder"
|
|
34
|
+
NATIVE_TRAINABLE = "native_trainable"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class PoolingStrategy(StrEnum):
|
|
38
|
+
"""Canonical pooling strategies for foundation-model outputs."""
|
|
39
|
+
|
|
40
|
+
NONE = "none"
|
|
41
|
+
MEAN = "mean"
|
|
42
|
+
CLS = "cls"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
FOUNDATION_TAG_KEYS = (
|
|
46
|
+
"model_family",
|
|
47
|
+
"adapter_mode",
|
|
48
|
+
"artifact_id",
|
|
49
|
+
"preprocessing_version",
|
|
50
|
+
)
|
|
51
|
+
FOUNDATION_METADATA_KEYS = (*FOUNDATION_TAG_KEYS, "pooling_strategy")
|
|
52
|
+
FOUNDATION_BENCHMARK_COMPARISON_AXES = (
|
|
53
|
+
"dataset",
|
|
54
|
+
"task",
|
|
55
|
+
*FOUNDATION_TAG_KEYS,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(frozen=True)
|
|
60
|
+
class FoundationEmbeddingOperatorConfig(OperatorConfig):
|
|
61
|
+
"""Shared config fields for foundation-model operators."""
|
|
62
|
+
|
|
63
|
+
adapter_mode: AdapterMode = AdapterMode.NATIVE_TRAINABLE
|
|
64
|
+
artifact_id: str = "diffbio.builtin"
|
|
65
|
+
preprocessing_version: str = "native_v1"
|
|
66
|
+
|
|
67
|
+
def __post_init__(self) -> None:
|
|
68
|
+
"""Validate foundation-model metadata fields."""
|
|
69
|
+
super().__post_init__()
|
|
70
|
+
_validate_ascii_text(self.artifact_id, field_name="artifact_id")
|
|
71
|
+
_validate_ascii_text(
|
|
72
|
+
self.preprocessing_version,
|
|
73
|
+
field_name="preprocessing_version",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass(frozen=True)
|
|
78
|
+
class FoundationArtifactSpec:
|
|
79
|
+
"""Immutable spec describing a foundation-model artifact and interface."""
|
|
80
|
+
|
|
81
|
+
model_family: FoundationModelKind
|
|
82
|
+
artifact_id: str
|
|
83
|
+
preprocessing_version: str
|
|
84
|
+
adapter_mode: AdapterMode
|
|
85
|
+
pooling_strategy: PoolingStrategy
|
|
86
|
+
|
|
87
|
+
def __post_init__(self) -> None:
|
|
88
|
+
"""Validate artifact fields for JAX-safe metadata encoding."""
|
|
89
|
+
_validate_ascii_text(self.artifact_id, field_name="artifact_id")
|
|
90
|
+
_validate_ascii_text(
|
|
91
|
+
self.preprocessing_version,
|
|
92
|
+
field_name="preprocessing_version",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _validate_ascii_text(value: str, *, field_name: str) -> None:
|
|
97
|
+
"""Require non-empty ASCII metadata for JAX-safe output encoding."""
|
|
98
|
+
if not value:
|
|
99
|
+
raise ValueError(f"{field_name} must be non-empty.")
|
|
100
|
+
try:
|
|
101
|
+
value.encode("ascii")
|
|
102
|
+
except UnicodeEncodeError as exc:
|
|
103
|
+
raise ValueError(f"{field_name} must be ASCII-only.") from exc
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def encode_foundation_text(value: str) -> Array:
|
|
107
|
+
"""Encode ASCII metadata text as a JAX array for jit-safe outputs."""
|
|
108
|
+
_validate_ascii_text(value, field_name="metadata_text")
|
|
109
|
+
encoded = value.encode("ascii")
|
|
110
|
+
return jnp.asarray(list(encoded), dtype=jnp.uint8)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def decode_foundation_text(value: Array) -> str:
|
|
114
|
+
"""Decode a JAX uint8 text array back into an ASCII string."""
|
|
115
|
+
data = bytes(int(item) for item in value.tolist())
|
|
116
|
+
return data.decode("ascii")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def decode_foundation_model_metadata(
|
|
120
|
+
metadata: Mapping[str, Array | str],
|
|
121
|
+
) -> dict[str, str]:
|
|
122
|
+
"""Decode canonical foundation-model metadata into benchmark-friendly strings."""
|
|
123
|
+
decoded: dict[str, str] = {}
|
|
124
|
+
for key in FOUNDATION_METADATA_KEYS:
|
|
125
|
+
value = metadata.get(key)
|
|
126
|
+
if value is None:
|
|
127
|
+
continue
|
|
128
|
+
if isinstance(value, str):
|
|
129
|
+
decoded[key] = value
|
|
130
|
+
continue
|
|
131
|
+
decoded[key] = decode_foundation_text(value)
|
|
132
|
+
return decoded
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def build_foundation_benchmark_metadata(
|
|
136
|
+
metadata: Mapping[str, Array | str],
|
|
137
|
+
*,
|
|
138
|
+
dataset: str,
|
|
139
|
+
task: str,
|
|
140
|
+
) -> dict[str, str]:
|
|
141
|
+
"""Build the shared benchmark-facing foundation metadata contract."""
|
|
142
|
+
_validate_ascii_text(dataset, field_name="dataset")
|
|
143
|
+
_validate_ascii_text(task, field_name="task")
|
|
144
|
+
decoded = decode_foundation_model_metadata(metadata)
|
|
145
|
+
missing = [key for key in FOUNDATION_TAG_KEYS if key not in decoded]
|
|
146
|
+
if missing:
|
|
147
|
+
missing_fields = ", ".join(missing)
|
|
148
|
+
raise ValueError(f"foundation metadata missing required field(s): {missing_fields}")
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
"dataset": dataset,
|
|
152
|
+
"task": task,
|
|
153
|
+
**decoded,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def build_foundation_model_metadata(
|
|
158
|
+
artifact_spec: FoundationArtifactSpec,
|
|
159
|
+
) -> dict[str, Array]:
|
|
160
|
+
"""Build a jit-safe metadata payload for a foundation-model result."""
|
|
161
|
+
return {
|
|
162
|
+
"model_family": encode_foundation_text(artifact_spec.model_family.value),
|
|
163
|
+
"artifact_id": encode_foundation_text(artifact_spec.artifact_id),
|
|
164
|
+
"preprocessing_version": encode_foundation_text(artifact_spec.preprocessing_version),
|
|
165
|
+
"adapter_mode": encode_foundation_text(artifact_spec.adapter_mode.value),
|
|
166
|
+
"pooling_strategy": encode_foundation_text(artifact_spec.pooling_strategy.value),
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class FoundationEmbeddingMixin:
|
|
171
|
+
"""Mixin providing canonical outputs for foundation-model operators."""
|
|
172
|
+
|
|
173
|
+
config: FoundationEmbeddingOperatorConfig
|
|
174
|
+
foundation_model_kind: FoundationModelKind
|
|
175
|
+
|
|
176
|
+
def foundation_pooling_strategy(self) -> PoolingStrategy:
|
|
177
|
+
"""Return the pooling strategy used for the global embedding."""
|
|
178
|
+
return PoolingStrategy.NONE
|
|
179
|
+
|
|
180
|
+
def foundation_artifact_spec(self) -> FoundationArtifactSpec:
|
|
181
|
+
"""Build the artifact spec for the current operator."""
|
|
182
|
+
return FoundationArtifactSpec(
|
|
183
|
+
model_family=self.foundation_model_kind,
|
|
184
|
+
artifact_id=self.config.artifact_id,
|
|
185
|
+
preprocessing_version=self.config.preprocessing_version,
|
|
186
|
+
adapter_mode=self.config.adapter_mode,
|
|
187
|
+
pooling_strategy=self.foundation_pooling_strategy(),
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
def foundation_result(
|
|
191
|
+
self,
|
|
192
|
+
data: PyTree,
|
|
193
|
+
embeddings: Array,
|
|
194
|
+
*,
|
|
195
|
+
token_embeddings: Array | None = None,
|
|
196
|
+
extra_outputs: dict[str, Any] | None = None,
|
|
197
|
+
) -> dict[str, Any]:
|
|
198
|
+
"""Build the canonical operator result payload."""
|
|
199
|
+
transformed_data = {
|
|
200
|
+
**data,
|
|
201
|
+
"embeddings": embeddings,
|
|
202
|
+
"foundation_model": build_foundation_model_metadata(self.foundation_artifact_spec()),
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if token_embeddings is not None:
|
|
206
|
+
transformed_data["token_embeddings"] = token_embeddings
|
|
207
|
+
|
|
208
|
+
if extra_outputs:
|
|
209
|
+
for key in ("embeddings", "token_embeddings", "foundation_model"):
|
|
210
|
+
if key in extra_outputs:
|
|
211
|
+
raise ValueError(f"extra_outputs cannot override canonical key {key!r}.")
|
|
212
|
+
transformed_data.update(extra_outputs)
|
|
213
|
+
|
|
214
|
+
return transformed_data
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
_FOUNDATION_MODEL_REGISTRY: dict[FoundationModelKind, type[OperatorModule]] = {}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def register_foundation_model(
|
|
221
|
+
model_family: FoundationModelKind,
|
|
222
|
+
operator_cls: type[OperatorModule],
|
|
223
|
+
) -> None:
|
|
224
|
+
"""Register an operator class for a foundation-model family."""
|
|
225
|
+
_FOUNDATION_MODEL_REGISTRY[model_family] = operator_cls
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def get_foundation_model_cls(
|
|
229
|
+
model_family: FoundationModelKind,
|
|
230
|
+
) -> type[OperatorModule]:
|
|
231
|
+
"""Return the registered operator class for a foundation-model family."""
|
|
232
|
+
try:
|
|
233
|
+
return _FOUNDATION_MODEL_REGISTRY[model_family]
|
|
234
|
+
except KeyError as exc:
|
|
235
|
+
raise KeyError(f"No operator registered for {model_family.value!r}.") from exc
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def create_foundation_model(
|
|
239
|
+
model_family: FoundationModelKind,
|
|
240
|
+
*args: Any,
|
|
241
|
+
**kwargs: Any,
|
|
242
|
+
) -> OperatorModule:
|
|
243
|
+
"""Instantiate a registered foundation-model operator."""
|
|
244
|
+
operator_cls = get_foundation_model_cls(model_family)
|
|
245
|
+
return operator_cls(*args, **kwargs)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Lightweight probing operators for foundation-model embeddings."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import jax
|
|
9
|
+
import jax.numpy as jnp
|
|
10
|
+
from datarax.core.config import OperatorConfig
|
|
11
|
+
from datarax.core.operator import OperatorModule
|
|
12
|
+
from flax import nnx
|
|
13
|
+
from jaxtyping import PyTree
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True, kw_only=True)
|
|
17
|
+
class EmbeddingProbeConfig(OperatorConfig):
|
|
18
|
+
"""Configuration for a lightweight embedding probe."""
|
|
19
|
+
|
|
20
|
+
input_dim: int
|
|
21
|
+
n_classes: int
|
|
22
|
+
hidden_dim: int | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class LinearEmbeddingProbe(OperatorModule):
|
|
26
|
+
"""Small classifier for probing embedding quality on downstream tasks."""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
config: EmbeddingProbeConfig,
|
|
31
|
+
*,
|
|
32
|
+
rngs: nnx.Rngs | None = None,
|
|
33
|
+
name: str | None = None,
|
|
34
|
+
) -> None:
|
|
35
|
+
super().__init__(config, rngs=rngs, name=name)
|
|
36
|
+
|
|
37
|
+
if rngs is None:
|
|
38
|
+
rngs = nnx.Rngs(0)
|
|
39
|
+
|
|
40
|
+
self.hidden = None
|
|
41
|
+
if config.hidden_dim is not None:
|
|
42
|
+
self.hidden = nnx.Linear(
|
|
43
|
+
in_features=config.input_dim,
|
|
44
|
+
out_features=config.hidden_dim,
|
|
45
|
+
rngs=rngs,
|
|
46
|
+
)
|
|
47
|
+
classifier_in_dim = config.hidden_dim
|
|
48
|
+
else:
|
|
49
|
+
classifier_in_dim = config.input_dim
|
|
50
|
+
|
|
51
|
+
self.classifier = nnx.Linear(
|
|
52
|
+
in_features=classifier_in_dim,
|
|
53
|
+
out_features=config.n_classes,
|
|
54
|
+
rngs=rngs,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def apply(
|
|
58
|
+
self,
|
|
59
|
+
data: PyTree,
|
|
60
|
+
state: PyTree,
|
|
61
|
+
metadata: dict[str, Any] | None,
|
|
62
|
+
random_params: Any = None,
|
|
63
|
+
stats: dict[str, Any] | None = None,
|
|
64
|
+
) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
|
|
65
|
+
"""Predict class probabilities from input embeddings."""
|
|
66
|
+
del random_params, stats
|
|
67
|
+
|
|
68
|
+
embeddings = data["embeddings"]
|
|
69
|
+
features = embeddings
|
|
70
|
+
if self.hidden is not None:
|
|
71
|
+
features = nnx.relu(self.hidden(features))
|
|
72
|
+
|
|
73
|
+
logits = self.classifier(features)
|
|
74
|
+
probabilities = jax.nn.softmax(logits, axis=-1)
|
|
75
|
+
predicted_labels = jnp.argmax(probabilities, axis=-1)
|
|
76
|
+
|
|
77
|
+
transformed_data = {
|
|
78
|
+
**data,
|
|
79
|
+
"logits": logits,
|
|
80
|
+
"probabilities": probabilities,
|
|
81
|
+
"predicted_labels": predicted_labels,
|
|
82
|
+
}
|
|
83
|
+
return transformed_data, state, metadata
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Experimental foundation-model boundary policy.
|
|
2
|
+
|
|
3
|
+
This module is a policy namespace, not a stable implementation surface. It
|
|
4
|
+
keeps speculative foundation-model capabilities explicit until benchmarks,
|
|
5
|
+
provenance, regression guards, and docs promote them into stable support.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from types import MappingProxyType
|
|
12
|
+
from typing import Mapping
|
|
13
|
+
|
|
14
|
+
from diffbio.operators.foundation_models.contracts import _validate_ascii_text
|
|
15
|
+
|
|
16
|
+
EXPERIMENTAL_FOUNDATION_MODEL_NAMESPACE = "diffbio.operators.foundation_models.experimental"
|
|
17
|
+
FOUNDATION_EXPERIMENTAL_PROMOTION_CRITERIA = (
|
|
18
|
+
"explicit experimental namespace",
|
|
19
|
+
"canonical artifact provenance",
|
|
20
|
+
"downstream benchmark suite",
|
|
21
|
+
"Calibrax regression guard",
|
|
22
|
+
"shared audit bundle",
|
|
23
|
+
"stable documentation update",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class ExperimentalFoundationCapability:
|
|
29
|
+
"""Fail-closed policy record for speculative foundation-model scope."""
|
|
30
|
+
|
|
31
|
+
key: str
|
|
32
|
+
display_name: str
|
|
33
|
+
stable_exclusion: str
|
|
34
|
+
namespace: str = EXPERIMENTAL_FOUNDATION_MODEL_NAMESPACE
|
|
35
|
+
stable_support: bool = False
|
|
36
|
+
benchmark_status: str = "benchmark_unverified"
|
|
37
|
+
required_promotion_criteria: tuple[str, ...] = FOUNDATION_EXPERIMENTAL_PROMOTION_CRITERIA
|
|
38
|
+
|
|
39
|
+
def __post_init__(self) -> None:
|
|
40
|
+
"""Validate the policy record cannot silently become stable."""
|
|
41
|
+
for field_name, value in (
|
|
42
|
+
("key", self.key),
|
|
43
|
+
("display_name", self.display_name),
|
|
44
|
+
("stable_exclusion", self.stable_exclusion),
|
|
45
|
+
("namespace", self.namespace),
|
|
46
|
+
("benchmark_status", self.benchmark_status),
|
|
47
|
+
):
|
|
48
|
+
_validate_ascii_text(value, field_name=field_name)
|
|
49
|
+
if self.namespace != EXPERIMENTAL_FOUNDATION_MODEL_NAMESPACE:
|
|
50
|
+
raise ValueError(
|
|
51
|
+
"experimental foundation capabilities must use the experimental namespace."
|
|
52
|
+
)
|
|
53
|
+
if self.stable_support:
|
|
54
|
+
raise ValueError("experimental foundation capabilities cannot be stable.")
|
|
55
|
+
if self.required_promotion_criteria != FOUNDATION_EXPERIMENTAL_PROMOTION_CRITERIA:
|
|
56
|
+
raise ValueError(
|
|
57
|
+
"experimental foundation capabilities must use the shared promotion criteria."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
_FOUNDATION_EXPERIMENTAL_CAPABILITIES = {
|
|
62
|
+
"long_context_sequence_models": ExperimentalFoundationCapability(
|
|
63
|
+
key="long_context_sequence_models",
|
|
64
|
+
display_name="Long-context sequence models",
|
|
65
|
+
stable_exclusion=(
|
|
66
|
+
"No stable long-context sequence model support until promotion criteria are satisfied."
|
|
67
|
+
),
|
|
68
|
+
),
|
|
69
|
+
"hyena_style_sequence_models": ExperimentalFoundationCapability(
|
|
70
|
+
key="hyena_style_sequence_models",
|
|
71
|
+
display_name="Hyena-style sequence models",
|
|
72
|
+
stable_exclusion=(
|
|
73
|
+
"No stable Hyena-style runtime support until promotion criteria are satisfied."
|
|
74
|
+
),
|
|
75
|
+
),
|
|
76
|
+
"external_native_trainable_checkpoint_import": ExperimentalFoundationCapability(
|
|
77
|
+
key="external_native_trainable_checkpoint_import",
|
|
78
|
+
display_name="External native_trainable checkpoint import",
|
|
79
|
+
stable_exclusion=(
|
|
80
|
+
"The stable native_trainable adapter mode only covers DiffBio-native "
|
|
81
|
+
"operators, not external checkpoint conversion."
|
|
82
|
+
),
|
|
83
|
+
),
|
|
84
|
+
"peft_finetuning": ExperimentalFoundationCapability(
|
|
85
|
+
key="peft_finetuning",
|
|
86
|
+
display_name="PEFT fine-tuning utilities",
|
|
87
|
+
stable_exclusion=(
|
|
88
|
+
"No stable PEFT utilities are shipped until promotion criteria are satisfied."
|
|
89
|
+
),
|
|
90
|
+
),
|
|
91
|
+
"lora_adaptation": ExperimentalFoundationCapability(
|
|
92
|
+
key="lora_adaptation",
|
|
93
|
+
display_name="LoRA adaptation utilities",
|
|
94
|
+
stable_exclusion=(
|
|
95
|
+
"No stable LoRA adaptation utilities are shipped until promotion "
|
|
96
|
+
"criteria are satisfied."
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
}
|
|
100
|
+
FOUNDATION_EXPERIMENTAL_CAPABILITIES: Mapping[
|
|
101
|
+
str,
|
|
102
|
+
ExperimentalFoundationCapability,
|
|
103
|
+
] = MappingProxyType(_FOUNDATION_EXPERIMENTAL_CAPABILITIES)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def is_experimental_foundation_capability(key: str) -> bool:
|
|
107
|
+
"""Return whether a capability is explicitly fenced as experimental."""
|
|
108
|
+
return key in FOUNDATION_EXPERIMENTAL_CAPABILITIES
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_experimental_foundation_capability(
|
|
112
|
+
key: str,
|
|
113
|
+
) -> ExperimentalFoundationCapability:
|
|
114
|
+
"""Return the experimental policy record for a capability key."""
|
|
115
|
+
try:
|
|
116
|
+
return FOUNDATION_EXPERIMENTAL_CAPABILITIES[key]
|
|
117
|
+
except KeyError as exc:
|
|
118
|
+
raise KeyError(f"No experimental foundation capability registered for {key!r}.") from exc
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
__all__ = [
|
|
122
|
+
"EXPERIMENTAL_FOUNDATION_MODEL_NAMESPACE",
|
|
123
|
+
"FOUNDATION_EXPERIMENTAL_CAPABILITIES",
|
|
124
|
+
"FOUNDATION_EXPERIMENTAL_PROMOTION_CRITERIA",
|
|
125
|
+
"ExperimentalFoundationCapability",
|
|
126
|
+
"get_experimental_foundation_capability",
|
|
127
|
+
"is_experimental_foundation_capability",
|
|
128
|
+
]
|