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,493 @@
1
+ """Neural network-based read mapper for differentiable alignment.
2
+
3
+ This module provides a neural network approach to read mapping that
4
+ enables gradient flow through the mapping process.
5
+
6
+ Key technique: Uses cross-attention between read and reference embeddings
7
+ to compute soft alignment scores, enabling end-to-end differentiable mapping.
8
+
9
+ Applications: Differentiable read mapping for joint optimization with
10
+ downstream variant calling or assembly pipelines.
11
+ """
12
+
13
+ import logging
14
+ from dataclasses import dataclass
15
+ from typing import Any
16
+
17
+ import jax
18
+ import jax.numpy as jnp
19
+ from flax import nnx
20
+ from jaxtyping import Array, Float, PyTree
21
+
22
+ from diffbio.configs import TemperatureConfig
23
+
24
+ from diffbio.core.base_operators import TemperatureOperator
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class NeuralReadMapperConfig(TemperatureConfig):
31
+ """Configuration for NeuralReadMapper.
32
+
33
+ Attributes:
34
+ read_length: Expected read length.
35
+ reference_window: Reference window size.
36
+ embedding_dim: Dimension of sequence embeddings.
37
+ num_heads: Number of attention heads.
38
+ num_layers: Number of transformer layers.
39
+ dropout_rate: Dropout rate for regularization.
40
+ temperature: Temperature for softmax operations.
41
+ """
42
+
43
+ read_length: int = 150
44
+ reference_window: int = 500
45
+ embedding_dim: int = 64
46
+ num_heads: int = 4
47
+ num_layers: int = 4
48
+ dropout_rate: float = 0.1
49
+ temperature: float = 1.0
50
+
51
+
52
+ class SequenceEncoder(nnx.Module):
53
+ """Encoder for DNA sequences.
54
+
55
+ Converts one-hot encoded sequences to dense embeddings
56
+ with positional encoding.
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ embedding_dim: int,
62
+ max_length: int,
63
+ *,
64
+ rngs: nnx.Rngs,
65
+ ):
66
+ """Initialize the sequence encoder.
67
+
68
+ Args:
69
+ embedding_dim: Output embedding dimension.
70
+ max_length: Maximum sequence length.
71
+ rngs: Random number generators.
72
+ """
73
+ super().__init__()
74
+
75
+ # Project from one-hot (4 bases) to embedding dim
76
+ self.input_projection = nnx.Linear(
77
+ in_features=4,
78
+ out_features=embedding_dim,
79
+ rngs=rngs,
80
+ )
81
+
82
+ # Learnable positional encoding
83
+ key = rngs.params()
84
+ self.positional_encoding = nnx.Param(
85
+ jax.random.normal(key, (max_length, embedding_dim)) * 0.02
86
+ )
87
+
88
+ def __call__(
89
+ self,
90
+ sequence: Float[Array, "batch length 4"],
91
+ ) -> Float[Array, "batch length embedding_dim"]:
92
+ """Encode a one-hot sequence.
93
+
94
+ Args:
95
+ sequence: One-hot encoded DNA sequence.
96
+
97
+ Returns:
98
+ Dense sequence embeddings with positional encoding.
99
+ """
100
+ # Project to embedding dimension
101
+ embeddings = self.input_projection(sequence)
102
+
103
+ # Add positional encoding
104
+ seq_len = sequence.shape[1]
105
+ pos_enc = self.positional_encoding[:seq_len]
106
+ embeddings = embeddings + pos_enc
107
+
108
+ return embeddings
109
+
110
+
111
+ class CrossAttentionLayer(nnx.Module):
112
+ """Cross-attention layer for read-reference alignment."""
113
+
114
+ def __init__(
115
+ self,
116
+ embedding_dim: int,
117
+ num_heads: int,
118
+ dropout_rate: float,
119
+ *,
120
+ rngs: nnx.Rngs,
121
+ ):
122
+ """Initialize the cross-attention layer.
123
+
124
+ Args:
125
+ embedding_dim: Embedding dimension.
126
+ num_heads: Number of attention heads.
127
+ dropout_rate: Dropout rate.
128
+ rngs: Random number generators.
129
+ """
130
+ super().__init__()
131
+
132
+ self.num_heads = num_heads
133
+
134
+ # Query, Key, Value projections
135
+ self.query_proj = nnx.Linear(
136
+ in_features=embedding_dim,
137
+ out_features=embedding_dim,
138
+ rngs=rngs,
139
+ )
140
+ self.key_proj = nnx.Linear(
141
+ in_features=embedding_dim,
142
+ out_features=embedding_dim,
143
+ rngs=rngs,
144
+ )
145
+ self.value_proj = nnx.Linear(
146
+ in_features=embedding_dim,
147
+ out_features=embedding_dim,
148
+ rngs=rngs,
149
+ )
150
+ self.output_proj = nnx.Linear(
151
+ in_features=embedding_dim,
152
+ out_features=embedding_dim,
153
+ rngs=rngs,
154
+ )
155
+
156
+ self.dropout = nnx.Dropout(rate=dropout_rate, rngs=rngs) if dropout_rate > 0 else None
157
+
158
+ def __call__(
159
+ self,
160
+ query: Float[Array, "batch query_len dim"],
161
+ key_value: Float[Array, "batch kv_len dim"],
162
+ *,
163
+ deterministic: bool = True,
164
+ ) -> Float[Array, "batch query_len dim"]:
165
+ """Apply cross-attention from read tokens to reference tokens.
166
+
167
+ Args:
168
+ query: Query embeddings (read).
169
+ key_value: Key/Value embeddings (reference).
170
+ deterministic: Whether to disable stochastic dropout.
171
+
172
+ Returns:
173
+ Attended embeddings.
174
+ """
175
+ batch_size = query.shape[0]
176
+ query_len = query.shape[1]
177
+ kv_len = key_value.shape[1]
178
+
179
+ # Project Q, K, V
180
+ Q = self.query_proj(query)
181
+ K = self.key_proj(key_value)
182
+ V = self.value_proj(key_value)
183
+
184
+ head_dim = Q.shape[-1] // self.num_heads
185
+ scale = head_dim**-0.5
186
+
187
+ # Reshape for multi-head attention
188
+ Q = Q.reshape(batch_size, query_len, self.num_heads, head_dim)
189
+ K = K.reshape(batch_size, kv_len, self.num_heads, head_dim)
190
+ V = V.reshape(batch_size, kv_len, self.num_heads, head_dim)
191
+
192
+ # Transpose to (batch, heads, length, dim)
193
+ Q = Q.transpose(0, 2, 1, 3)
194
+ K = K.transpose(0, 2, 1, 3)
195
+ V = V.transpose(0, 2, 1, 3)
196
+
197
+ # Compute attention scores
198
+ attn_scores = jnp.einsum("bhqd,bhkd->bhqk", Q, K) * scale
199
+ attn_probs = jax.nn.softmax(attn_scores, axis=-1)
200
+
201
+ # Apply dropout to attention
202
+ if self.dropout is not None and not deterministic:
203
+ attn_probs = self.dropout(attn_probs)
204
+
205
+ # Compute attended values
206
+ attended = jnp.einsum("bhqk,bhkd->bhqd", attn_probs, V)
207
+
208
+ # Reshape back
209
+ attended = attended.transpose(0, 2, 1, 3)
210
+ attended = attended.reshape(batch_size, query_len, -1)
211
+
212
+ # Output projection
213
+ output = self.output_proj(attended)
214
+
215
+ return output
216
+
217
+
218
+ class TransformerBlock(nnx.Module):
219
+ """Transformer block with cross-attention and feedforward."""
220
+
221
+ def __init__(
222
+ self,
223
+ embedding_dim: int,
224
+ num_heads: int,
225
+ dropout_rate: float,
226
+ *,
227
+ rngs: nnx.Rngs,
228
+ ):
229
+ """Initialize the transformer block.
230
+
231
+ Args:
232
+ embedding_dim: Embedding dimension.
233
+ num_heads: Number of attention heads.
234
+ dropout_rate: Dropout rate.
235
+ rngs: Random number generators.
236
+ """
237
+ super().__init__()
238
+
239
+ self.cross_attention = CrossAttentionLayer(
240
+ embedding_dim=embedding_dim,
241
+ num_heads=num_heads,
242
+ dropout_rate=dropout_rate,
243
+ rngs=rngs,
244
+ )
245
+
246
+ self.layer_norm1 = nnx.LayerNorm(
247
+ num_features=embedding_dim,
248
+ rngs=rngs,
249
+ )
250
+ self.layer_norm2 = nnx.LayerNorm(
251
+ num_features=embedding_dim,
252
+ rngs=rngs,
253
+ )
254
+
255
+ # Feedforward
256
+ self.ff_linear1 = nnx.Linear(
257
+ in_features=embedding_dim,
258
+ out_features=embedding_dim * 4,
259
+ rngs=rngs,
260
+ )
261
+ self.ff_linear2 = nnx.Linear(
262
+ in_features=embedding_dim * 4,
263
+ out_features=embedding_dim,
264
+ rngs=rngs,
265
+ )
266
+
267
+ def __call__(
268
+ self,
269
+ read_embeddings: Float[Array, "batch read_len dim"],
270
+ ref_embeddings: Float[Array, "batch ref_len dim"],
271
+ *,
272
+ deterministic: bool = True,
273
+ ) -> Float[Array, "batch read_len dim"]:
274
+ """Apply transformer block.
275
+
276
+ Args:
277
+ read_embeddings: Read embeddings.
278
+ ref_embeddings: Reference embeddings.
279
+ deterministic: If True, disable dropout.
280
+
281
+ Returns:
282
+ Transformed read embeddings.
283
+ """
284
+ # Cross-attention with residual
285
+ attended = self.cross_attention(
286
+ read_embeddings, ref_embeddings, deterministic=deterministic
287
+ )
288
+ x = self.layer_norm1(read_embeddings + attended)
289
+
290
+ # Feedforward with residual
291
+ ff_out = self.ff_linear2(nnx.gelu(self.ff_linear1(x)))
292
+ x = self.layer_norm2(x + ff_out)
293
+
294
+ return x
295
+
296
+
297
+ class NeuralReadMapper(TemperatureOperator):
298
+ """Neural network-based read mapper.
299
+
300
+ This operator uses cross-attention between read and reference
301
+ embeddings to compute soft alignment scores, enabling fully
302
+ differentiable read mapping.
303
+
304
+ Algorithm:
305
+ 1. Encode read and reference with positional embeddings
306
+ 2. Apply transformer layers with cross-attention
307
+ 3. Compute position-wise alignment scores
308
+ 4. Apply softmax for position probabilities
309
+ 5. Compute mapping quality from confidence
310
+
311
+ Inherits from TemperatureOperator to get:
312
+
313
+ - _temperature property for temperature-controlled smoothing
314
+ - soft_max() for logsumexp-based smooth maximum
315
+ - soft_argmax() for soft position selection
316
+
317
+ Args:
318
+ config: NeuralReadMapperConfig with model parameters.
319
+ rngs: Flax NNX random number generators.
320
+ name: Optional operator name.
321
+
322
+ Example:
323
+ ```python
324
+ config = NeuralReadMapperConfig(embedding_dim=64)
325
+ mapper = NeuralReadMapper(config, rngs=nnx.Rngs(42))
326
+ data = {"read": read_onehot, "reference": ref_onehot}
327
+ result, state, meta = mapper.apply(data, {}, None)
328
+ ```
329
+ """
330
+
331
+ def __init__(
332
+ self,
333
+ config: NeuralReadMapperConfig,
334
+ *,
335
+ rngs: nnx.Rngs | None = None,
336
+ name: str | None = None,
337
+ ):
338
+ """Initialize the neural read mapper.
339
+
340
+ Args:
341
+ config: Mapper configuration.
342
+ rngs: Random number generators for initialization.
343
+ name: Optional operator name.
344
+ """
345
+ super().__init__(config, rngs=rngs, name=name)
346
+
347
+ if rngs is None:
348
+ rngs = nnx.Rngs(0)
349
+
350
+ # Read encoder
351
+ self.read_encoder = SequenceEncoder(
352
+ embedding_dim=config.embedding_dim,
353
+ max_length=config.read_length,
354
+ rngs=rngs,
355
+ )
356
+
357
+ # Reference encoder
358
+ self.ref_encoder = SequenceEncoder(
359
+ embedding_dim=config.embedding_dim,
360
+ max_length=config.reference_window,
361
+ rngs=rngs,
362
+ )
363
+
364
+ # Transformer layers
365
+ self.transformer_layers = nnx.List(
366
+ [
367
+ TransformerBlock(
368
+ embedding_dim=config.embedding_dim,
369
+ num_heads=config.num_heads,
370
+ dropout_rate=config.dropout_rate,
371
+ rngs=rngs,
372
+ )
373
+ for _ in range(config.num_layers)
374
+ ]
375
+ )
376
+
377
+ # Output projections
378
+ self.score_projection = nnx.Linear(
379
+ in_features=config.embedding_dim,
380
+ out_features=1,
381
+ rngs=rngs,
382
+ )
383
+
384
+ self.quality_projection = nnx.Linear(
385
+ in_features=config.embedding_dim,
386
+ out_features=1,
387
+ rngs=rngs,
388
+ )
389
+
390
+ def compute_alignment_scores(
391
+ self,
392
+ read: Float[Array, "batch read_len 4"],
393
+ reference: Float[Array, "batch ref_len 4"],
394
+ *,
395
+ deterministic: bool = True,
396
+ ) -> tuple[
397
+ Float[Array, "batch ref_len"],
398
+ Float[Array, "batch embedding_dim"],
399
+ ]:
400
+ """Compute alignment scores for each reference position.
401
+
402
+ Args:
403
+ read: One-hot encoded read.
404
+ reference: One-hot encoded reference.
405
+ deterministic: If True, disable dropout.
406
+
407
+ Returns:
408
+ Tuple of (position scores, read summary embedding).
409
+ """
410
+ # Encode sequences
411
+ read_emb = self.read_encoder(read)
412
+ ref_emb = self.ref_encoder(reference)
413
+
414
+ # Apply transformer layers
415
+ for layer in self.transformer_layers:
416
+ read_emb = layer(read_emb, ref_emb, deterministic=deterministic)
417
+
418
+ # Global read representation (mean pooling)
419
+ read_summary = jnp.mean(read_emb, axis=1) # (batch, dim)
420
+
421
+ # Learned per-position alignment scoring over read/reference interaction features.
422
+ interaction_features = ref_emb * read_summary[:, None, :]
423
+ scores = self.score_projection(interaction_features).squeeze(-1)
424
+
425
+ return scores, read_summary
426
+
427
+ def apply(
428
+ self,
429
+ data: PyTree,
430
+ state: PyTree,
431
+ metadata: dict[str, Any] | None,
432
+ random_params: Any = None,
433
+ stats: dict[str, Any] | None = None,
434
+ ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
435
+ """Apply neural read mapping.
436
+
437
+ Args:
438
+ data: Dictionary containing:
439
+ - "read": One-hot encoded read (batch, read_len, 4)
440
+ - "reference": One-hot encoded reference (batch, ref_len, 4)
441
+ state: Element state (passed through unchanged)
442
+ metadata: Element metadata (passed through unchanged)
443
+ random_params: Not used
444
+ stats: Not used
445
+
446
+ Returns:
447
+ Tuple of (transformed_data, state, metadata):
448
+ - transformed_data contains:
449
+
450
+ - "read": Original read
451
+ - "reference": Original reference
452
+ - "alignment_scores": Scores for each reference position
453
+ - "position_probs": Softmax probabilities over positions
454
+ - "best_position": Most likely mapping position
455
+ - "mapping_quality": Confidence score for mapping
456
+ - state is passed through unchanged
457
+ - metadata is passed through unchanged
458
+ """
459
+ read = data["read"]
460
+ reference = data["reference"]
461
+
462
+ # Compute alignment scores
463
+ deterministic = not self.config.stochastic
464
+ scores, read_summary = self.compute_alignment_scores(
465
+ read, reference, deterministic=deterministic
466
+ )
467
+
468
+ # Position probabilities via softmax
469
+ # Use inherited _temperature property from TemperatureOperator
470
+ position_probs = jax.nn.softmax(scores / self._temperature, axis=-1)
471
+
472
+ # Best position (argmax)
473
+ best_position = jnp.argmax(position_probs, axis=-1)
474
+
475
+ # Mapping quality: higher when distribution is peaked
476
+ # Use negative entropy as quality measure
477
+ entropy = -jnp.sum(position_probs * jnp.log(position_probs + 1e-10), axis=-1)
478
+ max_entropy = jnp.log(jnp.array(reference.shape[1], dtype=jnp.float32))
479
+ entropy_confidence = 1.0 - (entropy / max_entropy)
480
+ learned_confidence = jax.nn.sigmoid(self.quality_projection(read_summary).squeeze(-1))
481
+ mapping_quality = entropy_confidence * learned_confidence
482
+
483
+ # Build output
484
+ transformed_data = {
485
+ "read": read,
486
+ "reference": reference,
487
+ "alignment_scores": scores,
488
+ "position_probs": position_probs,
489
+ "best_position": best_position,
490
+ "mapping_quality": mapping_quality,
491
+ }
492
+
493
+ return transformed_data, state, metadata
@@ -0,0 +1,39 @@
1
+ """Differentiable metabolomics operators for DiffBio.
2
+
3
+ This module provides differentiable operators for metabolomics analysis,
4
+ including spectral similarity computation using deep learning.
5
+
6
+ Operators:
7
+ DifferentiableSpectralSimilarity: MS2DeepScore-style Siamese network
8
+ for predicting molecular structural similarity from MS/MS spectra.
9
+
10
+ Example:
11
+ ```python
12
+ from diffbio.operators.metabolomics import (
13
+ DifferentiableSpectralSimilarity,
14
+ SpectralSimilarityConfig,
15
+ create_spectral_similarity,
16
+ bin_spectrum,
17
+ )
18
+ # Create operator
19
+ operator = create_spectral_similarity(n_bins=1000, embedding_dim=200)
20
+ # Compute spectral embeddings
21
+ spectra = jax.random.uniform(jax.random.PRNGKey(0), (10, 1000))
22
+ result, _, _ = operator.apply({"spectra": spectra}, {}, None)
23
+ embeddings = result["embeddings"] # (10, 200)
24
+ ```
25
+ """
26
+
27
+ from diffbio.operators.metabolomics.spectral_similarity import (
28
+ DifferentiableSpectralSimilarity,
29
+ SpectralSimilarityConfig,
30
+ bin_spectrum,
31
+ create_spectral_similarity,
32
+ )
33
+
34
+ __all__ = [
35
+ "DifferentiableSpectralSimilarity",
36
+ "SpectralSimilarityConfig",
37
+ "bin_spectrum",
38
+ "create_spectral_similarity",
39
+ ]