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,288 @@
1
+ """Differentiable Harmony-style batch correction operator.
2
+
3
+ This module provides a differentiable implementation of batch correction
4
+ using soft clustering with batch-aware centroid updates.
5
+
6
+ Key technique: Unrolled iterations enable gradient flow through
7
+ the entire batch correction process.
8
+
9
+ Applications: Multi-sample integration, batch effect removal.
10
+
11
+ Inherits from TemperatureOperator to get:
12
+
13
+ - _temperature property for temperature-controlled smoothing
14
+ - soft_max() for logsumexp-based smooth maximum
15
+ - soft_argmax() for soft position selection
16
+ """
17
+
18
+ import logging
19
+ from dataclasses import dataclass
20
+ from typing import Any
21
+
22
+ import jax
23
+ import jax.numpy as jnp
24
+ from datarax.core.config import OperatorConfig
25
+ from flax import nnx
26
+ from jaxtyping import Array, Float, Int, PyTree
27
+
28
+
29
+ from diffbio.core.base_operators import TemperatureOperator
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ @dataclass(frozen=True)
35
+ class BatchCorrectionConfig(OperatorConfig):
36
+ """Configuration for DifferentiableHarmony.
37
+
38
+ Attributes:
39
+ n_clusters: Number of clusters for soft assignment.
40
+ n_features: Dimensionality of input embeddings.
41
+ n_batches: Number of distinct batches.
42
+ n_iterations: Number of correction iterations.
43
+ theta: Diversity penalty parameter.
44
+ sigma: Soft assignment bandwidth.
45
+ temperature: Temperature for softmax operations.
46
+ """
47
+
48
+ n_clusters: int = 100
49
+ n_features: int = 50
50
+ n_batches: int = 2
51
+ n_iterations: int = 10
52
+ theta: float = 2.0
53
+ sigma: float = 0.1
54
+ temperature: float = 1.0
55
+
56
+
57
+ class DifferentiableHarmony(TemperatureOperator):
58
+ """Differentiable Harmony-style batch correction.
59
+
60
+ This operator implements iterative batch correction using soft
61
+ clustering with batch-aware updates. The fixed number of iterations
62
+ enables gradient flow through the entire correction process.
63
+
64
+ Algorithm:
65
+ 1. Initialize cluster centroids from data
66
+ 2. Soft assignment of cells to clusters
67
+ 3. Compute batch-aware centroid corrections
68
+ 4. Update cell embeddings toward corrected centroids
69
+ 5. Repeat for n_iterations
70
+
71
+ Inherits from TemperatureOperator to get:
72
+
73
+ - _temperature property for temperature-controlled smoothing
74
+ - soft_max() for logsumexp-based smooth maximum
75
+ - soft_argmax() for soft position selection
76
+
77
+ Args:
78
+ config: BatchCorrectionConfig with model parameters.
79
+ rngs: Flax NNX random number generators.
80
+ name: Optional operator name.
81
+
82
+ Example:
83
+ ```python
84
+ config = BatchCorrectionConfig(n_clusters=100, n_batches=3)
85
+ harmony = DifferentiableHarmony(config, rngs=nnx.Rngs(42))
86
+ data = {"embeddings": X, "batch_labels": batch}
87
+ result, state, meta = harmony.apply(data, {}, None)
88
+ ```
89
+ """
90
+
91
+ def __init__(
92
+ self,
93
+ config: BatchCorrectionConfig,
94
+ *,
95
+ rngs: nnx.Rngs | None = None,
96
+ name: str | None = None,
97
+ ):
98
+ """Initialize the batch correction operator.
99
+
100
+ Args:
101
+ config: Batch correction configuration.
102
+ rngs: Random number generators for initialization.
103
+ name: Optional operator name.
104
+ """
105
+ super().__init__(config, rngs=rngs, name=name)
106
+
107
+ rngs = rngs or nnx.Rngs(0)
108
+ # Temperature is now managed by TemperatureOperator via self._temperature
109
+
110
+ # Initialize cluster centroids
111
+ key = rngs.params()
112
+ centroid_shape = (config.n_clusters, config.n_features)
113
+ init_centroids = jax.random.normal(key, centroid_shape) * 0.1
114
+ self.cluster_centroids = nnx.Param(init_centroids)
115
+
116
+ def compute_soft_assignments(
117
+ self,
118
+ embeddings: Float[Array, "n_cells n_features"],
119
+ centroids: Float[Array, "n_clusters n_features"],
120
+ ) -> Float[Array, "n_cells n_clusters"]:
121
+ """Compute soft cluster assignments.
122
+
123
+ Args:
124
+ embeddings: Cell embeddings.
125
+ centroids: Cluster centroids.
126
+
127
+ Returns:
128
+ Soft assignment probabilities.
129
+ """
130
+ # Compute squared distances
131
+ # ||x - c||² = ||x||² + ||c||² - 2 * x · c
132
+ emb_sq = jnp.sum(embeddings**2, axis=-1, keepdims=True)
133
+ cent_sq = jnp.sum(centroids**2, axis=-1)
134
+ dot_product = jnp.einsum("nf,kf->nk", embeddings, centroids)
135
+ distances_sq = emb_sq + cent_sq - 2 * dot_product
136
+
137
+ # Soft assignments
138
+ # Use inherited _temperature property from TemperatureOperator
139
+ assignments = jax.nn.softmax(
140
+ -distances_sq / (self.config.sigma * self._temperature),
141
+ axis=-1,
142
+ )
143
+
144
+ return assignments
145
+
146
+ def compute_batch_proportions(
147
+ self,
148
+ batch_labels: Int[Array, "n_cells"],
149
+ assignments: Float[Array, "n_cells n_clusters"],
150
+ ) -> Float[Array, "n_clusters n_batches"]:
151
+ """Compute batch proportions within each cluster.
152
+
153
+ Args:
154
+ batch_labels: Batch assignments for each cell.
155
+ assignments: Soft cluster assignments.
156
+
157
+ Returns:
158
+ Proportion of each batch in each cluster.
159
+ """
160
+ # Create one-hot batch encoding
161
+ batch_onehot = jax.nn.one_hot(batch_labels, self.config.n_batches) # (n_cells, n_batches)
162
+
163
+ # Weighted count of each batch in each cluster
164
+ # (n_cells, n_clusters).T @ (n_cells, n_batches) -> (n_clusters, n_batches)
165
+ batch_counts = jnp.einsum("nk,nb->kb", assignments, batch_onehot)
166
+
167
+ # Normalize to get proportions
168
+ total_per_cluster = jnp.sum(batch_counts, axis=-1, keepdims=True) + 1e-10
169
+ batch_proportions = batch_counts / total_per_cluster
170
+
171
+ return batch_proportions
172
+
173
+ def correction_step(
174
+ self,
175
+ embeddings: Float[Array, "n_cells n_features"],
176
+ batch_labels: Int[Array, "n_cells"],
177
+ centroids: Float[Array, "n_clusters n_features"],
178
+ ) -> tuple[Float[Array, "n_cells n_features"], Float[Array, "n_cells n_clusters"]]:
179
+ """Perform one correction iteration.
180
+
181
+ Args:
182
+ embeddings: Current cell embeddings.
183
+ batch_labels: Batch assignments.
184
+ centroids: Current cluster centroids.
185
+
186
+ Returns:
187
+ Corrected embeddings and soft assignments.
188
+ """
189
+ # Compute soft assignments
190
+ assignments = self.compute_soft_assignments(embeddings, centroids)
191
+
192
+ # Compute batch proportions
193
+ batch_props = self.compute_batch_proportions(batch_labels, assignments)
194
+
195
+ # Global batch proportions (target)
196
+ batch_onehot = jax.nn.one_hot(batch_labels, self.config.n_batches)
197
+ global_batch_props = jnp.mean(batch_onehot, axis=0) # (n_batches,)
198
+
199
+ # Compute correction direction for each cell
200
+ # Move cells toward cluster centroids weighted by assignment and batch correction
201
+ weighted_centroids = jnp.einsum(
202
+ "nk,kf->nf", assignments, centroids
203
+ ) # (n_cells, n_features)
204
+
205
+ # Correction: small step toward weighted centroid
206
+ correction = (weighted_centroids - embeddings) * 0.1
207
+
208
+ # Apply batch-specific scaling
209
+ # Cells from over-represented batches get larger corrections
210
+ batch_idx = batch_labels # (n_cells,)
211
+ # Get batch proportion at each cell's most likely cluster
212
+ top_cluster = jnp.argmax(assignments, axis=-1)
213
+ cell_batch_prop = batch_props[top_cluster, batch_idx] # (n_cells,)
214
+ cell_global_prop = global_batch_props[batch_idx] # (n_cells,)
215
+
216
+ # Scale correction by how overrepresented the batch is
217
+ correction_scale = jnp.clip(cell_batch_prop / (cell_global_prop + 1e-10), 0.5, 2.0)
218
+ correction = correction * correction_scale[:, None]
219
+
220
+ # Apply correction
221
+ corrected = embeddings + correction
222
+
223
+ return corrected, assignments
224
+
225
+ def apply(
226
+ self,
227
+ data: PyTree,
228
+ state: PyTree,
229
+ metadata: dict[str, Any] | None,
230
+ random_params: Any = None,
231
+ stats: dict[str, Any] | None = None,
232
+ ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
233
+ """Apply batch correction to cell embeddings.
234
+
235
+ Args:
236
+ data: Dictionary containing:
237
+ - "embeddings": Cell embeddings (n_cells, n_features)
238
+ - "batch_labels": Batch assignments (n_cells,)
239
+ state: Element state (passed through unchanged)
240
+ metadata: Element metadata (passed through unchanged)
241
+ random_params: Not used
242
+ stats: Not used
243
+
244
+ Returns:
245
+ Tuple of (transformed_data, state, metadata):
246
+ - transformed_data contains:
247
+
248
+ - "embeddings": Original embeddings
249
+ - "batch_labels": Original batch labels
250
+ - "corrected_embeddings": Batch-corrected embeddings
251
+ - "cluster_assignments": Final soft cluster assignments
252
+ - state is passed through unchanged
253
+ - metadata is passed through unchanged
254
+ """
255
+ embeddings = data["embeddings"]
256
+ batch_labels = data["batch_labels"]
257
+ centroids = self.cluster_centroids[...]
258
+
259
+ # Run correction iterations
260
+ corrected = embeddings
261
+ assignments = None
262
+
263
+ def iteration_step(carry, _):
264
+ corrected_emb, centroids = carry
265
+ new_corrected, new_assignments = self.correction_step(
266
+ corrected_emb, batch_labels, centroids
267
+ )
268
+ return (new_corrected, centroids), new_assignments
269
+
270
+ (corrected, _), assignments = jax.lax.scan(
271
+ iteration_step,
272
+ (corrected, centroids),
273
+ None,
274
+ length=self.config.n_iterations,
275
+ )
276
+
277
+ # Get final assignments
278
+ final_assignments = self.compute_soft_assignments(corrected, centroids)
279
+
280
+ # Build output data
281
+ transformed_data = {
282
+ "embeddings": embeddings,
283
+ "batch_labels": batch_labels,
284
+ "corrected_embeddings": corrected,
285
+ "cluster_assignments": final_assignments,
286
+ }
287
+
288
+ return transformed_data, state, metadata