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,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
|
+
]
|