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
diffbio/sequences/dna.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""DNA sequence data types and encoding utilities for DiffBio.
|
|
2
|
+
|
|
3
|
+
This module provides functions and utilities for working with DNA sequences
|
|
4
|
+
in a JAX-compatible, differentiable manner.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Literal
|
|
8
|
+
|
|
9
|
+
import jax
|
|
10
|
+
import jax.numpy as jnp
|
|
11
|
+
from jaxtyping import Array, Float
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# DNA nucleotide alphabet
|
|
15
|
+
DNA_ALPHABET = "ACGT"
|
|
16
|
+
DNA_ALPHABET_SIZE = 4
|
|
17
|
+
|
|
18
|
+
# Mapping from nucleotide to index
|
|
19
|
+
_NUC_TO_IDX = {"A": 0, "C": 1, "G": 2, "T": 3, "N": -1}
|
|
20
|
+
_IDX_TO_NUC = {0: "A", 1: "C", 2: "G", 3: "T"}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def encode_dna_string(sequence: str, handle_n: Literal["uniform", "zero"] = "uniform") -> Array:
|
|
24
|
+
"""Encode a DNA string as a one-hot JAX array.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
sequence: DNA string containing only A, C, G, T, N characters.
|
|
28
|
+
handle_n: How to handle N (unknown) nucleotides:
|
|
29
|
+
- "uniform": Encode as uniform distribution [0.25, 0.25, 0.25, 0.25]
|
|
30
|
+
- "zero": Encode as zeros [0, 0, 0, 0]
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
One-hot encoded array of shape (len(sequence), 4).
|
|
34
|
+
Columns represent A, C, G, T in that order.
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
```python
|
|
38
|
+
encode_dna_string("ACGT")
|
|
39
|
+
```
|
|
40
|
+
Array([[1, 0, 0, 0],
|
|
41
|
+
[0, 1, 0, 0],
|
|
42
|
+
[0, 0, 1, 0],
|
|
43
|
+
[0, 0, 0, 1]], dtype=float32)
|
|
44
|
+
"""
|
|
45
|
+
sequence = sequence.upper()
|
|
46
|
+
|
|
47
|
+
# Convert to indices
|
|
48
|
+
indices = []
|
|
49
|
+
for nuc in sequence:
|
|
50
|
+
if nuc in _NUC_TO_IDX:
|
|
51
|
+
indices.append(_NUC_TO_IDX[nuc])
|
|
52
|
+
else:
|
|
53
|
+
raise ValueError(f"Invalid nucleotide: {nuc}. Expected A, C, G, T, or N.")
|
|
54
|
+
|
|
55
|
+
indices_array = jnp.array(indices, dtype=jnp.int32)
|
|
56
|
+
|
|
57
|
+
# One-hot encode valid nucleotides (indices 0-3)
|
|
58
|
+
# N nucleotides have index -1 and will be handled separately
|
|
59
|
+
valid_mask = indices_array >= 0
|
|
60
|
+
safe_indices = jnp.where(valid_mask, indices_array, 0) # Use 0 for safe indexing
|
|
61
|
+
one_hot = jax.nn.one_hot(safe_indices, DNA_ALPHABET_SIZE)
|
|
62
|
+
|
|
63
|
+
# Handle N nucleotides
|
|
64
|
+
if handle_n == "uniform":
|
|
65
|
+
n_encoding = jnp.ones(DNA_ALPHABET_SIZE) / DNA_ALPHABET_SIZE
|
|
66
|
+
else: # "zero"
|
|
67
|
+
n_encoding = jnp.zeros(DNA_ALPHABET_SIZE)
|
|
68
|
+
|
|
69
|
+
# Apply N encoding where needed
|
|
70
|
+
n_mask = ~valid_mask
|
|
71
|
+
one_hot = jnp.where(n_mask[:, None], n_encoding, one_hot)
|
|
72
|
+
|
|
73
|
+
return one_hot
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def decode_dna_onehot(encoded: Array, threshold: float = 0.5) -> str:
|
|
77
|
+
"""Decode a one-hot encoded DNA array back to a string.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
encoded: One-hot encoded array of shape (length, 4).
|
|
81
|
+
threshold: Minimum confidence to assign a nucleotide.
|
|
82
|
+
Below threshold, returns 'N'.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
DNA string.
|
|
86
|
+
"""
|
|
87
|
+
# Get argmax for each position
|
|
88
|
+
indices = jnp.argmax(encoded, axis=-1)
|
|
89
|
+
max_vals = jnp.max(encoded, axis=-1)
|
|
90
|
+
|
|
91
|
+
# Build string
|
|
92
|
+
result = []
|
|
93
|
+
for i in range(len(indices)):
|
|
94
|
+
idx = int(indices[i])
|
|
95
|
+
max_val = float(max_vals[i])
|
|
96
|
+
if max_val >= threshold and idx in _IDX_TO_NUC:
|
|
97
|
+
result.append(_IDX_TO_NUC[idx])
|
|
98
|
+
else:
|
|
99
|
+
result.append("N")
|
|
100
|
+
|
|
101
|
+
return "".join(result)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def phred_to_probability(phred_scores: Array) -> Array:
|
|
105
|
+
"""Convert Phred quality scores to error probabilities.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
phred_scores: Array of Phred scores (typically 0-40).
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Array of error probabilities in range [0, 1].
|
|
112
|
+
|
|
113
|
+
Note:
|
|
114
|
+
Error probability = 10^(-Q/10) where Q is Phred score.
|
|
115
|
+
"""
|
|
116
|
+
return jnp.power(10.0, -phred_scores / 10.0)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def probability_to_phred(error_prob: Array, max_phred: float = 60.0) -> Array:
|
|
120
|
+
"""Convert error probabilities to Phred quality scores.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
error_prob: Array of error probabilities in range (0, 1].
|
|
124
|
+
max_phred: Maximum Phred score to return (for numerical stability).
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Array of Phred scores.
|
|
128
|
+
"""
|
|
129
|
+
# Clip to avoid log(0)
|
|
130
|
+
safe_prob = jnp.clip(error_prob, 1e-10, 1.0)
|
|
131
|
+
phred = -10.0 * jnp.log10(safe_prob)
|
|
132
|
+
return jnp.clip(phred, 0.0, max_phred)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def soft_encode_dna(
|
|
136
|
+
sequence: Array,
|
|
137
|
+
quality_scores: Array,
|
|
138
|
+
temperature: float = 1.0,
|
|
139
|
+
) -> Array:
|
|
140
|
+
"""Create soft one-hot encoding weighted by quality scores.
|
|
141
|
+
|
|
142
|
+
This creates a differentiable encoding where positions with low quality
|
|
143
|
+
have more uniform distributions (higher entropy).
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
sequence: One-hot encoded sequence of shape (length, 4).
|
|
147
|
+
quality_scores: Phred quality scores of shape (length,).
|
|
148
|
+
temperature: Temperature parameter controlling softness.
|
|
149
|
+
Lower = sharper (more like hard one-hot)
|
|
150
|
+
Higher = softer (more uniform)
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Soft-encoded array of shape (length, 4) where each row
|
|
154
|
+
sums to 1 but may not be exactly one-hot.
|
|
155
|
+
"""
|
|
156
|
+
# Convert quality to confidence (higher quality = higher confidence)
|
|
157
|
+
confidence = 1.0 - phred_to_probability(quality_scores)
|
|
158
|
+
|
|
159
|
+
# Scale one-hot by confidence and add uniform noise for uncertainty
|
|
160
|
+
uniform = jnp.ones(DNA_ALPHABET_SIZE) / DNA_ALPHABET_SIZE
|
|
161
|
+
|
|
162
|
+
# Weighted combination: high confidence -> one-hot, low confidence -> uniform
|
|
163
|
+
soft_encoded = confidence[:, None] * sequence + (1 - confidence[:, None]) * uniform
|
|
164
|
+
|
|
165
|
+
# Apply temperature scaling and softmax for normalization
|
|
166
|
+
logits = jnp.log(soft_encoded + 1e-10) / temperature
|
|
167
|
+
return jax.nn.softmax(logits, axis=-1)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def complement_dna(encoded: Array) -> Array:
|
|
171
|
+
"""Get the complement of a DNA sequence.
|
|
172
|
+
|
|
173
|
+
Complement mapping: A<->T, C<->G
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
encoded: One-hot encoded DNA of shape (..., 4).
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Complemented sequence of same shape.
|
|
180
|
+
"""
|
|
181
|
+
# Complement swaps A<->T (indices 0<->3) and C<->G (indices 1<->2)
|
|
182
|
+
# Permutation: [0,1,2,3] -> [3,2,1,0]
|
|
183
|
+
return encoded[..., ::-1]
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def reverse_complement_dna(encoded: Array) -> Array:
|
|
187
|
+
"""Get the reverse complement of a DNA sequence.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
encoded: One-hot encoded DNA of shape (length, 4).
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Reverse complemented sequence of same shape.
|
|
194
|
+
"""
|
|
195
|
+
return complement_dna(encoded[::-1])
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def gc_content(encoded: Array) -> Float[Array, ""]:
|
|
199
|
+
"""Calculate GC content of a DNA sequence.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
encoded: One-hot encoded DNA of shape (length, 4).
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Scalar GC content as fraction in [0, 1].
|
|
206
|
+
"""
|
|
207
|
+
# G is index 2, C is index 1
|
|
208
|
+
gc_sum = jnp.sum(encoded[:, 1] + encoded[:, 2])
|
|
209
|
+
total = jnp.sum(encoded)
|
|
210
|
+
return gc_sum / (total + 1e-10)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def create_dna_element_data(
|
|
214
|
+
sequence: str | Array,
|
|
215
|
+
quality_scores: Array | None = None,
|
|
216
|
+
) -> dict:
|
|
217
|
+
"""Create data dictionary for a DNA sequence Element.
|
|
218
|
+
|
|
219
|
+
This creates a data structure compatible with Datarax's Element.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
sequence: DNA string or pre-encoded one-hot array.
|
|
223
|
+
quality_scores: Optional Phred quality scores.
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Dictionary suitable for Element(data=...).
|
|
227
|
+
"""
|
|
228
|
+
# Encode sequence if string
|
|
229
|
+
if isinstance(sequence, str):
|
|
230
|
+
encoded_seq = encode_dna_string(sequence)
|
|
231
|
+
else:
|
|
232
|
+
encoded_seq = sequence
|
|
233
|
+
|
|
234
|
+
data = {"sequence": encoded_seq}
|
|
235
|
+
|
|
236
|
+
if quality_scores is not None:
|
|
237
|
+
data["quality_scores"] = quality_scores
|
|
238
|
+
|
|
239
|
+
return data
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""DiffBio data sources module.
|
|
2
|
+
|
|
3
|
+
This module provides data source implementations extending Datarax's DataSourceModule
|
|
4
|
+
for bioinformatics and drug discovery applications.
|
|
5
|
+
|
|
6
|
+
Sources:
|
|
7
|
+
AnnDataSource: AnnData (.h5ad) file reading for single-cell data
|
|
8
|
+
ENCODEPeakSource: ENCODE narrowPeak BED file reading for ChIP-seq peaks
|
|
9
|
+
IndexedViewSource: Lazy-loading view into a data source using index mapping
|
|
10
|
+
MolNetSource: MoleculeNet benchmark datasets for drug discovery
|
|
11
|
+
BAMSource: BAM/CRAM file reading for aligned sequencing reads
|
|
12
|
+
BioSNAPDTISource: Deterministic binary DTI benchmark source
|
|
13
|
+
DavisDTISource: Deterministic affinity DTI benchmark source
|
|
14
|
+
FastaSource: FASTA file reading for DNA/RNA sequences
|
|
15
|
+
|
|
16
|
+
Interop:
|
|
17
|
+
to_anndata: Convert DiffBio data dict to AnnData object
|
|
18
|
+
from_anndata: Convert AnnData object to DiffBio data dict
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from diffbio.sources.anndata_interop import from_anndata, to_anndata
|
|
22
|
+
from diffbio.sources.anndata_source import AnnDataSource, AnnDataSourceConfig
|
|
23
|
+
from diffbio.sources.bam import BAMSource, BAMSourceConfig
|
|
24
|
+
from diffbio.sources.contextual_epigenomics import (
|
|
25
|
+
CONTEXTUAL_EPIGENOMICS_DATASET_CONTRACT_KEYS,
|
|
26
|
+
CONTEXTUAL_TARGET_SEMANTICS,
|
|
27
|
+
build_synthetic_contextual_epigenomics_dataset,
|
|
28
|
+
validate_contextual_epigenomics_dataset,
|
|
29
|
+
)
|
|
30
|
+
from diffbio.sources.dti import (
|
|
31
|
+
DTI_DATASET_CONTRACT_KEYS,
|
|
32
|
+
BioSNAPDTISource,
|
|
33
|
+
DTISourceConfig,
|
|
34
|
+
DavisDTISource,
|
|
35
|
+
build_paired_dti_batch,
|
|
36
|
+
deterministic_dti_split,
|
|
37
|
+
validate_dti_dataset,
|
|
38
|
+
)
|
|
39
|
+
from diffbio.sources.embeddings import (
|
|
40
|
+
EmbeddingArtifactSource,
|
|
41
|
+
EmbeddingArtifactSourceConfig,
|
|
42
|
+
)
|
|
43
|
+
from diffbio.sources.encode_peaks import ENCODEPeakConfig, ENCODEPeakSource
|
|
44
|
+
from diffbio.sources.fasta import FastaSource, FastaSourceConfig
|
|
45
|
+
from diffbio.sources.indexed_view import IndexedViewSource, IndexedViewSourceConfig
|
|
46
|
+
from diffbio.sources.molnet import MolNetSource, MolNetSourceConfig
|
|
47
|
+
from diffbio.sources.multiomics import (
|
|
48
|
+
MULTIOMICS_ARTIFACT_METADATA_KEYS,
|
|
49
|
+
MULTIOMICS_DATASET_PROVENANCE_KEYS,
|
|
50
|
+
MetabolomicsEmbeddingSource,
|
|
51
|
+
MetabolomicsEmbeddingSourceConfig,
|
|
52
|
+
MultiOmicsEmbeddingSource,
|
|
53
|
+
MultiOmicsEmbeddingSourceConfig,
|
|
54
|
+
align_metabolomics_embeddings,
|
|
55
|
+
align_multiomics_embeddings,
|
|
56
|
+
build_multiomics_artifact_metadata,
|
|
57
|
+
build_multiomics_dataset_provenance,
|
|
58
|
+
load_metabolomics_embedding_source,
|
|
59
|
+
load_multiomics_embedding_source,
|
|
60
|
+
validate_multiomics_artifact_metadata,
|
|
61
|
+
validate_multiomics_dataset_provenance,
|
|
62
|
+
)
|
|
63
|
+
from diffbio.sources.perturbation import (
|
|
64
|
+
BatchControlMapping,
|
|
65
|
+
ControlMappingConfig,
|
|
66
|
+
ExperimentConfig,
|
|
67
|
+
GlobalH5MetadataCache,
|
|
68
|
+
H5MetadataCache,
|
|
69
|
+
PerturbationAnnDataSource,
|
|
70
|
+
PerturbationConcatSource,
|
|
71
|
+
PerturbationSourceConfig,
|
|
72
|
+
RandomControlMapping,
|
|
73
|
+
load_experiment_config,
|
|
74
|
+
)
|
|
75
|
+
from diffbio.sources.singlecell_foundation import (
|
|
76
|
+
SingleCellEmbeddingSource,
|
|
77
|
+
SingleCellEmbeddingSourceConfig,
|
|
78
|
+
align_singlecell_embeddings,
|
|
79
|
+
load_singlecell_embedding_source,
|
|
80
|
+
)
|
|
81
|
+
from diffbio.sources.sequence_foundation import (
|
|
82
|
+
SequenceEmbeddingSource,
|
|
83
|
+
SequenceEmbeddingSourceConfig,
|
|
84
|
+
align_sequence_embeddings,
|
|
85
|
+
load_sequence_embedding_source,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
__all__ = [
|
|
89
|
+
"AnnDataSource",
|
|
90
|
+
"AnnDataSourceConfig",
|
|
91
|
+
"BAMSource",
|
|
92
|
+
"BAMSourceConfig",
|
|
93
|
+
"BioSNAPDTISource",
|
|
94
|
+
"CONTEXTUAL_EPIGENOMICS_DATASET_CONTRACT_KEYS",
|
|
95
|
+
"CONTEXTUAL_TARGET_SEMANTICS",
|
|
96
|
+
"DTI_DATASET_CONTRACT_KEYS",
|
|
97
|
+
"DTISourceConfig",
|
|
98
|
+
"DavisDTISource",
|
|
99
|
+
"EmbeddingArtifactSource",
|
|
100
|
+
"EmbeddingArtifactSourceConfig",
|
|
101
|
+
"ENCODEPeakConfig",
|
|
102
|
+
"ENCODEPeakSource",
|
|
103
|
+
"FastaSource",
|
|
104
|
+
"FastaSourceConfig",
|
|
105
|
+
"IndexedViewSource",
|
|
106
|
+
"IndexedViewSourceConfig",
|
|
107
|
+
"MolNetSource",
|
|
108
|
+
"MolNetSourceConfig",
|
|
109
|
+
"MULTIOMICS_ARTIFACT_METADATA_KEYS",
|
|
110
|
+
"MULTIOMICS_DATASET_PROVENANCE_KEYS",
|
|
111
|
+
"MetabolomicsEmbeddingSource",
|
|
112
|
+
"MetabolomicsEmbeddingSourceConfig",
|
|
113
|
+
"MultiOmicsEmbeddingSource",
|
|
114
|
+
"MultiOmicsEmbeddingSourceConfig",
|
|
115
|
+
"from_anndata",
|
|
116
|
+
"to_anndata",
|
|
117
|
+
# Perturbation
|
|
118
|
+
"BatchControlMapping",
|
|
119
|
+
"ControlMappingConfig",
|
|
120
|
+
"ExperimentConfig",
|
|
121
|
+
"GlobalH5MetadataCache",
|
|
122
|
+
"H5MetadataCache",
|
|
123
|
+
"PerturbationAnnDataSource",
|
|
124
|
+
"PerturbationConcatSource",
|
|
125
|
+
"PerturbationSourceConfig",
|
|
126
|
+
"RandomControlMapping",
|
|
127
|
+
"build_synthetic_contextual_epigenomics_dataset",
|
|
128
|
+
"build_paired_dti_batch",
|
|
129
|
+
"deterministic_dti_split",
|
|
130
|
+
"validate_dti_dataset",
|
|
131
|
+
"validate_contextual_epigenomics_dataset",
|
|
132
|
+
"load_experiment_config",
|
|
133
|
+
"SequenceEmbeddingSource",
|
|
134
|
+
"SequenceEmbeddingSourceConfig",
|
|
135
|
+
"align_sequence_embeddings",
|
|
136
|
+
"align_metabolomics_embeddings",
|
|
137
|
+
"align_multiomics_embeddings",
|
|
138
|
+
"build_multiomics_artifact_metadata",
|
|
139
|
+
"build_multiomics_dataset_provenance",
|
|
140
|
+
"load_sequence_embedding_source",
|
|
141
|
+
"load_metabolomics_embedding_source",
|
|
142
|
+
"load_multiomics_embedding_source",
|
|
143
|
+
"SingleCellEmbeddingSource",
|
|
144
|
+
"SingleCellEmbeddingSourceConfig",
|
|
145
|
+
"align_singlecell_embeddings",
|
|
146
|
+
"load_singlecell_embedding_source",
|
|
147
|
+
"validate_multiomics_artifact_metadata",
|
|
148
|
+
"validate_multiomics_dataset_provenance",
|
|
149
|
+
]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Shared helpers for AnnData-backed DiffBio sources."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import jax.numpy as jnp
|
|
9
|
+
import numpy as np
|
|
10
|
+
from flax import nnx
|
|
11
|
+
|
|
12
|
+
from diffbio.sources._utils import _require_anndata
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def to_dense_array(matrix: Any) -> np.ndarray:
|
|
16
|
+
"""Convert a dense or sparse matrix into a float32 NumPy array."""
|
|
17
|
+
import scipy.sparse # noqa: PLC0415
|
|
18
|
+
|
|
19
|
+
if scipy.sparse.issparse(matrix):
|
|
20
|
+
return np.asarray(matrix.toarray(), dtype=np.float32)
|
|
21
|
+
return np.asarray(matrix, dtype=np.float32)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def read_h5ad(config: Any) -> Any:
|
|
25
|
+
"""Load an AnnData object after validating the configured file path."""
|
|
26
|
+
anndata_mod = _require_anndata()
|
|
27
|
+
file_path = Path(str(config.file_path))
|
|
28
|
+
if not file_path.exists():
|
|
29
|
+
raise FileNotFoundError(f"AnnData file not found: {file_path}")
|
|
30
|
+
return anndata_mod.read_h5ad(file_path, backed="r" if config.backed else None)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def load_obsm(adata: Any) -> dict[str, jnp.ndarray]:
|
|
34
|
+
"""Load AnnData embedding matrices as float32 JAX arrays."""
|
|
35
|
+
if adata.obsm is None or len(adata.obsm) == 0:
|
|
36
|
+
return {}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
key: jnp.array(np.asarray(adata.obsm[key], dtype=np.float32)) for key in adata.obsm.keys()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def extract_anndata_annotations(
|
|
44
|
+
adata: Any,
|
|
45
|
+
) -> tuple[dict[str, Any], dict[str, Any], dict[str, jnp.ndarray]]:
|
|
46
|
+
"""Extract obs, var, and obsm tables into the standard in-memory layout."""
|
|
47
|
+
obs = {col: np.asarray(adata.obs[col]) for col in adata.obs.columns}
|
|
48
|
+
var = {col: np.asarray(adata.var[col]) for col in adata.var.columns}
|
|
49
|
+
obsm = load_obsm(adata)
|
|
50
|
+
return obs, var, obsm
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def build_anndata_data(
|
|
54
|
+
*,
|
|
55
|
+
counts: jnp.ndarray,
|
|
56
|
+
obs: dict[str, Any],
|
|
57
|
+
var: dict[str, Any],
|
|
58
|
+
obsm: dict[str, jnp.ndarray],
|
|
59
|
+
) -> dict[str, Any]:
|
|
60
|
+
"""Assemble the canonical in-memory AnnData payload for DiffBio sources."""
|
|
61
|
+
return {
|
|
62
|
+
"counts": counts,
|
|
63
|
+
"obs": obs,
|
|
64
|
+
"var": var,
|
|
65
|
+
"obsm": obsm,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def initialize_eager_source_state(
|
|
70
|
+
source: Any,
|
|
71
|
+
*,
|
|
72
|
+
data: dict[str, Any],
|
|
73
|
+
length: int,
|
|
74
|
+
seed: int,
|
|
75
|
+
shuffle: bool,
|
|
76
|
+
dataset_name: str | None,
|
|
77
|
+
split_name: str | None,
|
|
78
|
+
dataset_info: dict[str, int],
|
|
79
|
+
) -> None:
|
|
80
|
+
"""Populate the common eager-source bookkeeping fields."""
|
|
81
|
+
source.data = data
|
|
82
|
+
source.length = length
|
|
83
|
+
source.index = nnx.Variable(0)
|
|
84
|
+
source.epoch = nnx.Variable(0)
|
|
85
|
+
source._seed = seed
|
|
86
|
+
source.shuffle = shuffle
|
|
87
|
+
source.dataset_name = dataset_name
|
|
88
|
+
source.split_name = split_name
|
|
89
|
+
source._dataset_info = dataset_info
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Utilities for simple stateful batch iteration in data sources."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
|
|
6
|
+
import jax
|
|
7
|
+
|
|
8
|
+
from datarax.typing import Element
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def reset_iteration_state(source: object, seed: int | None = None) -> None:
|
|
14
|
+
"""Reset a source object exposing `_current_idx` iteration state."""
|
|
15
|
+
del seed # API compatibility with DataSourceModule reset signature
|
|
16
|
+
source._current_idx = 0
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def next_batch(
|
|
20
|
+
*,
|
|
21
|
+
batch_size: int,
|
|
22
|
+
key: jax.Array | None,
|
|
23
|
+
current_idx: int,
|
|
24
|
+
total_size: int,
|
|
25
|
+
get_element: Callable[[int], Element],
|
|
26
|
+
) -> tuple[list[Element], int]:
|
|
27
|
+
"""Collect up to `batch_size` elements starting from `current_idx`."""
|
|
28
|
+
del key # API compatibility with DataSourceModule.get_batch signature
|
|
29
|
+
|
|
30
|
+
batch: list[Element] = []
|
|
31
|
+
idx = current_idx
|
|
32
|
+
for _ in range(batch_size):
|
|
33
|
+
if idx >= total_size:
|
|
34
|
+
break
|
|
35
|
+
batch.append(get_element(idx))
|
|
36
|
+
idx += 1
|
|
37
|
+
return batch, idx
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Shared substrate for benchmark-oriented eager data sources."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterator
|
|
6
|
+
import logging
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Callable, Literal, overload
|
|
9
|
+
|
|
10
|
+
import jax.numpy as jnp
|
|
11
|
+
import numpy as np
|
|
12
|
+
from flax import nnx
|
|
13
|
+
|
|
14
|
+
from datarax.core.data_source import DataSourceModule
|
|
15
|
+
|
|
16
|
+
from diffbio.sources._utils import _require_anndata
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def load_benchmark_adata(
|
|
20
|
+
*,
|
|
21
|
+
data_dir: str,
|
|
22
|
+
filename: str,
|
|
23
|
+
subsample: int | None = None,
|
|
24
|
+
seed: int = 42,
|
|
25
|
+
) -> Any:
|
|
26
|
+
"""Load a benchmark h5ad file with optional deterministic subsampling."""
|
|
27
|
+
anndata_mod = _require_anndata()
|
|
28
|
+
path = Path(data_dir) / filename
|
|
29
|
+
adata = anndata_mod.read_h5ad(path)
|
|
30
|
+
|
|
31
|
+
if subsample is not None and subsample < adata.n_obs:
|
|
32
|
+
rng = np.random.default_rng(seed)
|
|
33
|
+
indices = rng.choice(adata.n_obs, size=subsample, replace=False)
|
|
34
|
+
indices.sort()
|
|
35
|
+
adata = adata[indices].copy()
|
|
36
|
+
|
|
37
|
+
return adata
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def load_benchmark_counts(
|
|
41
|
+
*,
|
|
42
|
+
data_dir: str,
|
|
43
|
+
filename: str,
|
|
44
|
+
to_dense: Callable[[Any], np.ndarray],
|
|
45
|
+
subsample: int | None = None,
|
|
46
|
+
seed: int = 42,
|
|
47
|
+
) -> tuple[Any, jnp.ndarray]:
|
|
48
|
+
"""Load a benchmark h5ad file and convert its count matrix to a JAX array."""
|
|
49
|
+
adata = load_benchmark_adata(
|
|
50
|
+
data_dir=data_dir,
|
|
51
|
+
filename=filename,
|
|
52
|
+
subsample=subsample,
|
|
53
|
+
seed=seed,
|
|
54
|
+
)
|
|
55
|
+
counts = jnp.array(to_dense(adata.X))
|
|
56
|
+
return adata, counts
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@overload
|
|
60
|
+
def encode_label_column(
|
|
61
|
+
column: Any,
|
|
62
|
+
*,
|
|
63
|
+
include_names: Literal[False] = False,
|
|
64
|
+
) -> np.ndarray: ...
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@overload
|
|
68
|
+
def encode_label_column(
|
|
69
|
+
column: Any,
|
|
70
|
+
*,
|
|
71
|
+
include_names: Literal[True],
|
|
72
|
+
) -> tuple[np.ndarray, list[str]]: ...
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def encode_label_column(
|
|
76
|
+
column: Any,
|
|
77
|
+
*,
|
|
78
|
+
include_names: bool = False,
|
|
79
|
+
) -> np.ndarray | tuple[np.ndarray, list[str]]:
|
|
80
|
+
"""Encode an observation column into stable int32 label codes."""
|
|
81
|
+
if hasattr(column, "cat"):
|
|
82
|
+
codes = np.asarray(column.cat.codes, dtype=np.int32)
|
|
83
|
+
names = [str(value) for value in column.cat.categories]
|
|
84
|
+
else:
|
|
85
|
+
unique_labels, codes = np.unique(np.asarray(column), return_inverse=True)
|
|
86
|
+
codes = codes.astype(np.int32)
|
|
87
|
+
names = [str(value) for value in unique_labels]
|
|
88
|
+
|
|
89
|
+
if include_names:
|
|
90
|
+
return codes, names
|
|
91
|
+
return codes
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def iter_loaded_rows(
|
|
95
|
+
data: dict[str, Any],
|
|
96
|
+
*,
|
|
97
|
+
static_keys: tuple[str, ...] = ("gene_names",),
|
|
98
|
+
) -> Iterator[dict[str, Any]]:
|
|
99
|
+
"""Iterate row-wise over an eager benchmark payload."""
|
|
100
|
+
static_key_set = set(static_keys)
|
|
101
|
+
for i in range(int(data["n_cells"])):
|
|
102
|
+
yield {
|
|
103
|
+
key: value[i] if hasattr(value, "__getitem__") and key not in static_key_set else value
|
|
104
|
+
for key, value in data.items()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class BenchmarkDataSource(DataSourceModule):
|
|
109
|
+
"""Shared base class for eager benchmark data sources backed by a data dict."""
|
|
110
|
+
|
|
111
|
+
data: dict[str, Any] = nnx.data()
|
|
112
|
+
iter_static_keys: tuple[str, ...] = ("gene_names",)
|
|
113
|
+
|
|
114
|
+
def load(self) -> dict[str, Any]:
|
|
115
|
+
"""Return the eagerly loaded dataset payload."""
|
|
116
|
+
return self.data
|
|
117
|
+
|
|
118
|
+
def __len__(self) -> int:
|
|
119
|
+
"""Return the number of rows in the eagerly loaded dataset."""
|
|
120
|
+
return int(self.data["n_cells"])
|
|
121
|
+
|
|
122
|
+
def __iter__(self) -> Iterator[dict[str, Any]]:
|
|
123
|
+
"""Iterate row-wise through the loaded dataset payload."""
|
|
124
|
+
return iter_loaded_rows(self.data, static_keys=self.iter_static_keys)
|
|
125
|
+
|
|
126
|
+
def _load_benchmark_counts(
|
|
127
|
+
self,
|
|
128
|
+
config: Any,
|
|
129
|
+
filename: str,
|
|
130
|
+
to_dense: Callable[[Any], np.ndarray],
|
|
131
|
+
) -> tuple[Any, jnp.ndarray]:
|
|
132
|
+
"""Load benchmark AnnData and convert its count matrix to a JAX array."""
|
|
133
|
+
return load_benchmark_counts(
|
|
134
|
+
data_dir=config.data_dir,
|
|
135
|
+
filename=filename,
|
|
136
|
+
to_dense=to_dense,
|
|
137
|
+
subsample=getattr(config, "subsample", None),
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def _log_loaded_summary(
|
|
141
|
+
self,
|
|
142
|
+
logger: logging.Logger,
|
|
143
|
+
dataset_name: str,
|
|
144
|
+
metric_keys: tuple[str, ...],
|
|
145
|
+
) -> None:
|
|
146
|
+
"""Log a standard loaded-dataset summary using the requested metric keys."""
|
|
147
|
+
labels = [key[2:] if key.startswith("n_") else key for key in metric_keys]
|
|
148
|
+
metric_summary = ", ".join(f"%d {label}" for label in labels)
|
|
149
|
+
logger.info(
|
|
150
|
+
f"Loaded {dataset_name}: {metric_summary}",
|
|
151
|
+
*(int(self.data[key]) for key in metric_keys),
|
|
152
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Mixin for stateful index-based batching in data sources."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import jax
|
|
6
|
+
|
|
7
|
+
from datarax.typing import Element
|
|
8
|
+
|
|
9
|
+
from diffbio.sources._batch_iteration import next_batch, reset_iteration_state
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class IndexedBatchSourceMixin:
|
|
15
|
+
"""Reusable `reset` and `get_batch` logic for indexable data sources."""
|
|
16
|
+
|
|
17
|
+
def _batch_total_size(self) -> int:
|
|
18
|
+
"""Return the total number of elements available for batching."""
|
|
19
|
+
raise NotImplementedError
|
|
20
|
+
|
|
21
|
+
def _batch_element(self, idx: int) -> Element:
|
|
22
|
+
"""Return the element at the given index."""
|
|
23
|
+
raise NotImplementedError
|
|
24
|
+
|
|
25
|
+
def reset(self, seed: int | None = None) -> None:
|
|
26
|
+
"""Reset iteration state, optionally with a new seed."""
|
|
27
|
+
reset_iteration_state(self, seed)
|
|
28
|
+
|
|
29
|
+
def get_batch(self, batch_size: int, key: jax.Array | None = None) -> list[Element]:
|
|
30
|
+
"""Return the next batch of elements, advancing the internal index."""
|
|
31
|
+
batch, self._current_idx = next_batch(
|
|
32
|
+
batch_size=batch_size,
|
|
33
|
+
key=key,
|
|
34
|
+
current_idx=self._current_idx,
|
|
35
|
+
total_size=self._batch_total_size(),
|
|
36
|
+
get_element=self._batch_element,
|
|
37
|
+
)
|
|
38
|
+
return batch
|