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.
Files changed (202) hide show
  1. diffbio/__init__.py +39 -0
  2. diffbio/configs.py +75 -0
  3. diffbio/constants.py +204 -0
  4. diffbio/core/__init__.py +127 -0
  5. diffbio/core/base_operators.py +612 -0
  6. diffbio/core/data_types.py +260 -0
  7. diffbio/core/gnn_components.py +629 -0
  8. diffbio/core/graph_utils.py +149 -0
  9. diffbio/core/neural_components.py +270 -0
  10. diffbio/core/optimal_transport.py +133 -0
  11. diffbio/core/soft_ops/__init__.py +216 -0
  12. diffbio/core/soft_ops/_projections_permutahedron.py +1864 -0
  13. diffbio/core/soft_ops/_projections_simplex.py +240 -0
  14. diffbio/core/soft_ops/_projections_transport.py +508 -0
  15. diffbio/core/soft_ops/_sorting_network.py +204 -0
  16. diffbio/core/soft_ops/_types.py +15 -0
  17. diffbio/core/soft_ops/_utils.py +342 -0
  18. diffbio/core/soft_ops/autograd_safe.py +120 -0
  19. diffbio/core/soft_ops/comparison.py +235 -0
  20. diffbio/core/soft_ops/elementwise.py +309 -0
  21. diffbio/core/soft_ops/logical.py +146 -0
  22. diffbio/core/soft_ops/quantile.py +376 -0
  23. diffbio/core/soft_ops/selection.py +236 -0
  24. diffbio/core/soft_ops/sorting.py +926 -0
  25. diffbio/core/soft_ops/straight_through.py +261 -0
  26. diffbio/core/uncertainty.py +279 -0
  27. diffbio/evaluation/__init__.py +42 -0
  28. diffbio/evaluation/adapters.py +409 -0
  29. diffbio/evaluation/graders.py +223 -0
  30. diffbio/evaluation/problem.py +157 -0
  31. diffbio/evaluation/runner.py +277 -0
  32. diffbio/losses/__init__.py +59 -0
  33. diffbio/losses/alignment_losses.py +222 -0
  34. diffbio/losses/biological_regularization.py +288 -0
  35. diffbio/losses/metric_losses.py +139 -0
  36. diffbio/losses/singlecell_losses.py +387 -0
  37. diffbio/losses/statistical_losses.py +345 -0
  38. diffbio/operators/__init__.py +60 -0
  39. diffbio/operators/_count_vae.py +197 -0
  40. diffbio/operators/_loss_balancing.py +65 -0
  41. diffbio/operators/_masked_gene_transformer.py +118 -0
  42. diffbio/operators/_transformer_validation.py +50 -0
  43. diffbio/operators/alignment/__init__.py +51 -0
  44. diffbio/operators/alignment/profile_hmm.py +350 -0
  45. diffbio/operators/alignment/scoring.py +127 -0
  46. diffbio/operators/alignment/smith_waterman.py +261 -0
  47. diffbio/operators/alignment/soft_msa.py +419 -0
  48. diffbio/operators/assembly/__init__.py +27 -0
  49. diffbio/operators/assembly/gnn_assembly.py +252 -0
  50. diffbio/operators/assembly/metagenomic_binning.py +296 -0
  51. diffbio/operators/crispr/__init__.py +17 -0
  52. diffbio/operators/crispr/guide_scoring.py +269 -0
  53. diffbio/operators/drug_discovery/__init__.py +133 -0
  54. diffbio/operators/drug_discovery/_graph_utils.py +142 -0
  55. diffbio/operators/drug_discovery/admet_predictor.py +285 -0
  56. diffbio/operators/drug_discovery/attentive_fp.py +411 -0
  57. diffbio/operators/drug_discovery/dti.py +261 -0
  58. diffbio/operators/drug_discovery/fingerprint.py +490 -0
  59. diffbio/operators/drug_discovery/maccs_keys.py +267 -0
  60. diffbio/operators/drug_discovery/message_passing.py +200 -0
  61. diffbio/operators/drug_discovery/primitives.py +242 -0
  62. diffbio/operators/drug_discovery/property_predictor.py +163 -0
  63. diffbio/operators/drug_discovery/similarity.py +193 -0
  64. diffbio/operators/epigenomics/__init__.py +35 -0
  65. diffbio/operators/epigenomics/chromatin_state.py +491 -0
  66. diffbio/operators/epigenomics/contextual.py +288 -0
  67. diffbio/operators/epigenomics/fno_peak_calling.py +153 -0
  68. diffbio/operators/epigenomics/peak_calling.py +555 -0
  69. diffbio/operators/foundation_models/__init__.py +119 -0
  70. diffbio/operators/foundation_models/adapters.py +114 -0
  71. diffbio/operators/foundation_models/contracts.py +245 -0
  72. diffbio/operators/foundation_models/embedding_probe.py +83 -0
  73. diffbio/operators/foundation_models/experimental.py +128 -0
  74. diffbio/operators/foundation_models/foundation_model.py +332 -0
  75. diffbio/operators/foundation_models/frozen.py +59 -0
  76. diffbio/operators/foundation_models/precomputed.py +270 -0
  77. diffbio/operators/foundation_models/transformer_encoder.py +564 -0
  78. diffbio/operators/mapping/__init__.py +17 -0
  79. diffbio/operators/mapping/neural_mapper.py +493 -0
  80. diffbio/operators/metabolomics/__init__.py +39 -0
  81. diffbio/operators/metabolomics/spectral_similarity.py +315 -0
  82. diffbio/operators/molecular_dynamics/__init__.py +51 -0
  83. diffbio/operators/molecular_dynamics/force_field.py +265 -0
  84. diffbio/operators/molecular_dynamics/integrator.py +304 -0
  85. diffbio/operators/molecular_dynamics/primitives.py +115 -0
  86. diffbio/operators/multiomics/__init__.py +38 -0
  87. diffbio/operators/multiomics/hic_contact.py +377 -0
  88. diffbio/operators/multiomics/multiomics_vae.py +325 -0
  89. diffbio/operators/multiomics/spatial_deconvolution.py +316 -0
  90. diffbio/operators/multiomics/spatial_gene_detection.py +493 -0
  91. diffbio/operators/normalization/__init__.py +42 -0
  92. diffbio/operators/normalization/embedding.py +222 -0
  93. diffbio/operators/normalization/phate.py +400 -0
  94. diffbio/operators/normalization/umap.py +261 -0
  95. diffbio/operators/normalization/vae_normalizer.py +258 -0
  96. diffbio/operators/population/__init__.py +17 -0
  97. diffbio/operators/population/ancestry_estimation.py +274 -0
  98. diffbio/operators/preprocessing/__init__.py +76 -0
  99. diffbio/operators/preprocessing/adapter_removal.py +311 -0
  100. diffbio/operators/preprocessing/duplicate_filter.py +317 -0
  101. diffbio/operators/preprocessing/error_correction.py +287 -0
  102. diffbio/operators/protein/__init__.py +31 -0
  103. diffbio/operators/protein/secondary_structure.py +509 -0
  104. diffbio/operators/quality_filter.py +128 -0
  105. diffbio/operators/rna_structure/__init__.py +35 -0
  106. diffbio/operators/rna_structure/rna_folding.py +509 -0
  107. diffbio/operators/rnaseq/__init__.py +23 -0
  108. diffbio/operators/rnaseq/motif_discovery.py +251 -0
  109. diffbio/operators/rnaseq/splicing_psi.py +216 -0
  110. diffbio/operators/singlecell/__init__.py +193 -0
  111. diffbio/operators/singlecell/ambient_removal.py +333 -0
  112. diffbio/operators/singlecell/archetypes.py +191 -0
  113. diffbio/operators/singlecell/batch_correction.py +288 -0
  114. diffbio/operators/singlecell/cell_annotation.py +519 -0
  115. diffbio/operators/singlecell/communication.py +704 -0
  116. diffbio/operators/singlecell/differential_distribution.py +243 -0
  117. diffbio/operators/singlecell/doublet_detection.py +657 -0
  118. diffbio/operators/singlecell/downsampling.py +166 -0
  119. diffbio/operators/singlecell/enhanced_batch_correction.py +519 -0
  120. diffbio/operators/singlecell/grn_inference.py +336 -0
  121. diffbio/operators/singlecell/imputation.py +429 -0
  122. diffbio/operators/singlecell/knockdown_filter.py +176 -0
  123. diffbio/operators/singlecell/ot_trajectory.py +277 -0
  124. diffbio/operators/singlecell/simulation.py +444 -0
  125. diffbio/operators/singlecell/sindy_grn.py +247 -0
  126. diffbio/operators/singlecell/soft_clustering.py +211 -0
  127. diffbio/operators/singlecell/spatial_domains.py +677 -0
  128. diffbio/operators/singlecell/switch_de.py +184 -0
  129. diffbio/operators/singlecell/trajectory.py +447 -0
  130. diffbio/operators/singlecell/velocity.py +361 -0
  131. diffbio/operators/statistical/__init__.py +35 -0
  132. diffbio/operators/statistical/em_quantification.py +260 -0
  133. diffbio/operators/statistical/hmm.py +234 -0
  134. diffbio/operators/statistical/nb_glm.py +272 -0
  135. diffbio/operators/variant/__init__.py +64 -0
  136. diffbio/operators/variant/classifier.py +333 -0
  137. diffbio/operators/variant/cnn_classifier.py +255 -0
  138. diffbio/operators/variant/cnv_segmentation.py +678 -0
  139. diffbio/operators/variant/deepvariant_pileup.py +426 -0
  140. diffbio/operators/variant/pileup.py +240 -0
  141. diffbio/operators/variant/quality_recalibration.py +274 -0
  142. diffbio/pipelines/__init__.py +65 -0
  143. diffbio/pipelines/differential_expression.py +279 -0
  144. diffbio/pipelines/enhanced_variant_calling.py +326 -0
  145. diffbio/pipelines/perturbation.py +407 -0
  146. diffbio/pipelines/preprocessing.py +267 -0
  147. diffbio/pipelines/single_cell.py +366 -0
  148. diffbio/pipelines/variant_calling.py +490 -0
  149. diffbio/samplers/__init__.py +9 -0
  150. diffbio/samplers/perturbation_sampler.py +142 -0
  151. diffbio/sequences/__init__.py +34 -0
  152. diffbio/sequences/dna.py +239 -0
  153. diffbio/sources/__init__.py +149 -0
  154. diffbio/sources/_anndata_shared.py +89 -0
  155. diffbio/sources/_batch_iteration.py +37 -0
  156. diffbio/sources/_benchmark_source.py +152 -0
  157. diffbio/sources/_indexed_batch_source.py +38 -0
  158. diffbio/sources/_utils.py +45 -0
  159. diffbio/sources/anndata_interop.py +387 -0
  160. diffbio/sources/anndata_source.py +361 -0
  161. diffbio/sources/archive_ii.py +174 -0
  162. diffbio/sources/balifam.py +207 -0
  163. diffbio/sources/bam.py +265 -0
  164. diffbio/sources/bengrn_ground_truth.py +306 -0
  165. diffbio/sources/contextual_epigenomics.py +242 -0
  166. diffbio/sources/dti.py +359 -0
  167. diffbio/sources/embeddings.py +203 -0
  168. diffbio/sources/encode_peaks.py +223 -0
  169. diffbio/sources/fasta.py +226 -0
  170. diffbio/sources/immune_human.py +172 -0
  171. diffbio/sources/indexed_embeddings.py +128 -0
  172. diffbio/sources/indexed_view.py +191 -0
  173. diffbio/sources/molnet.py +493 -0
  174. diffbio/sources/multiomics.py +279 -0
  175. diffbio/sources/pancreas.py +108 -0
  176. diffbio/sources/perturbation/__init__.py +69 -0
  177. diffbio/sources/perturbation/_types.py +51 -0
  178. diffbio/sources/perturbation/_utils.py +125 -0
  179. diffbio/sources/perturbation/concat_source.py +115 -0
  180. diffbio/sources/perturbation/control_mapping.py +215 -0
  181. diffbio/sources/perturbation/experiment_config.py +261 -0
  182. diffbio/sources/perturbation/h5_metadata_cache.py +218 -0
  183. diffbio/sources/perturbation/output_space.py +52 -0
  184. diffbio/sources/perturbation/perturbation_source.py +513 -0
  185. diffbio/sources/seqfish.py +145 -0
  186. diffbio/sources/sequence_foundation.py +68 -0
  187. diffbio/sources/singlecell_foundation.py +68 -0
  188. diffbio/splitters/__init__.py +63 -0
  189. diffbio/splitters/base.py +251 -0
  190. diffbio/splitters/molecular.py +330 -0
  191. diffbio/splitters/perturbation.py +199 -0
  192. diffbio/splitters/random.py +217 -0
  193. diffbio/splitters/sequence.py +201 -0
  194. diffbio/utils/__init__.py +55 -0
  195. diffbio/utils/dependency_runtime.py +115 -0
  196. diffbio/utils/nn_utils.py +157 -0
  197. diffbio/utils/quality.py +45 -0
  198. diffbio/utils/training.py +585 -0
  199. diffbio-0.1.0.dist-info/METADATA +480 -0
  200. diffbio-0.1.0.dist-info/RECORD +202 -0
  201. diffbio-0.1.0.dist-info/WHEEL +4 -0
  202. 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
+ ]