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,68 @@
1
+ """Sequence embedding sources following the Datarax source model."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+
8
+ import jax.numpy as jnp
9
+ from flax import nnx
10
+
11
+ from diffbio.sources.indexed_embeddings import IndexedEmbeddingSource, IndexedEmbeddingSourceConfig
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class SequenceEmbeddingSourceConfig(IndexedEmbeddingSourceConfig):
16
+ """Configuration for sequence embedding artifacts."""
17
+
18
+ row_id_key: str = "sequence_ids"
19
+
20
+
21
+ class SequenceEmbeddingSource(IndexedEmbeddingSource):
22
+ """Indexed embedding source specialized for sequence artifacts."""
23
+
24
+ config: SequenceEmbeddingSourceConfig # pyright: ignore[reportIncompatibleVariableOverride]
25
+
26
+ @property
27
+ def sequence_ids(self) -> tuple[str, ...] | None:
28
+ """Tuple of persisted sequence identifiers, if present."""
29
+ return self.row_ids
30
+
31
+ def align_to_reference_sequence_ids(
32
+ self,
33
+ *,
34
+ reference_sequence_ids: list[str] | tuple[str, ...],
35
+ require_sequence_ids: bool = True,
36
+ ) -> jnp.ndarray:
37
+ """Align external embeddings to the benchmark sequence order."""
38
+ return self.align_to_reference_ids(
39
+ reference_ids=reference_sequence_ids,
40
+ require_row_ids=require_sequence_ids,
41
+ artifact_label="Sequence",
42
+ id_display_name="Sequence ID",
43
+ )
44
+
45
+
46
+ def load_sequence_embedding_source(
47
+ path: Path | str,
48
+ *,
49
+ rngs: nnx.Rngs | None = None,
50
+ ) -> SequenceEmbeddingSource:
51
+ """Build the canonical sequence embedding source for an artifact."""
52
+ return SequenceEmbeddingSource(
53
+ SequenceEmbeddingSourceConfig(file_path=str(path)),
54
+ rngs=rngs,
55
+ )
56
+
57
+
58
+ def align_sequence_embeddings(
59
+ *,
60
+ reference_sequence_ids: list[str] | tuple[str, ...],
61
+ artifact_path: Path | str,
62
+ require_sequence_ids: bool = True,
63
+ ) -> jnp.ndarray:
64
+ """Align external embeddings to the benchmark sequence order."""
65
+ return load_sequence_embedding_source(artifact_path).align_to_reference_sequence_ids(
66
+ reference_sequence_ids=reference_sequence_ids,
67
+ require_sequence_ids=require_sequence_ids,
68
+ )
@@ -0,0 +1,68 @@
1
+ """Single-cell embedding sources following the Datarax source model."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+
8
+ import jax.numpy as jnp
9
+ from flax import nnx
10
+
11
+ from diffbio.sources.indexed_embeddings import IndexedEmbeddingSource, IndexedEmbeddingSourceConfig
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class SingleCellEmbeddingSourceConfig(IndexedEmbeddingSourceConfig):
16
+ """Configuration for single-cell embedding artifacts."""
17
+
18
+ row_id_key: str = "cell_ids"
19
+
20
+
21
+ class SingleCellEmbeddingSource(IndexedEmbeddingSource):
22
+ """Indexed embedding source specialized for single-cell artifacts."""
23
+
24
+ config: SingleCellEmbeddingSourceConfig # pyright: ignore[reportIncompatibleVariableOverride]
25
+
26
+ @property
27
+ def cell_ids(self) -> tuple[str, ...] | None:
28
+ """Tuple of persisted cell identifiers, if present."""
29
+ return self.row_ids
30
+
31
+ def align_to_reference_cell_ids(
32
+ self,
33
+ *,
34
+ reference_cell_ids: list[str] | tuple[str, ...],
35
+ require_cell_ids: bool = True,
36
+ ) -> jnp.ndarray:
37
+ """Align external embeddings to the benchmark cell order."""
38
+ return self.align_to_reference_ids(
39
+ reference_ids=reference_cell_ids,
40
+ require_row_ids=require_cell_ids,
41
+ artifact_label="Single-cell",
42
+ id_display_name="Cell ID",
43
+ )
44
+
45
+
46
+ def load_singlecell_embedding_source(
47
+ path: Path | str,
48
+ *,
49
+ rngs: nnx.Rngs | None = None,
50
+ ) -> SingleCellEmbeddingSource:
51
+ """Build the canonical single-cell embedding source for an artifact."""
52
+ return SingleCellEmbeddingSource(
53
+ SingleCellEmbeddingSourceConfig(file_path=str(path)),
54
+ rngs=rngs,
55
+ )
56
+
57
+
58
+ def align_singlecell_embeddings(
59
+ *,
60
+ reference_cell_ids: list[str] | tuple[str, ...],
61
+ artifact_path: Path | str,
62
+ require_cell_ids: bool = True,
63
+ ) -> jnp.ndarray:
64
+ """Align external embeddings to the benchmark cell order."""
65
+ return load_singlecell_embedding_source(artifact_path).align_to_reference_cell_ids(
66
+ reference_cell_ids=reference_cell_ids,
67
+ require_cell_ids=require_cell_ids,
68
+ )
@@ -0,0 +1,63 @@
1
+ """DiffBio splitters module.
2
+
3
+ This module provides dataset splitting utilities extending Datarax's StructuralModule
4
+ for train/validation/test splitting in bioinformatics and drug discovery applications.
5
+
6
+ Splitters:
7
+ SplitterModule: Base class for all splitters
8
+ SplitResult: NamedTuple containing split indices
9
+ RandomSplitter: Simple random splitting
10
+ StratifiedSplitter: Stratified splitting preserving class distribution
11
+ ScaffoldSplitter: Molecular scaffold-based splitting for drug discovery
12
+ TanimotoClusterSplitter: Fingerprint similarity clustering for drug discovery
13
+ SequenceIdentitySplitter: Sequence identity clustering for bioinformatics
14
+ """
15
+
16
+ from diffbio.splitters.base import SplitResult, SplitterConfig, SplitterModule
17
+ from diffbio.splitters.molecular import (
18
+ ScaffoldSplitter,
19
+ ScaffoldSplitterConfig,
20
+ TanimotoClusterSplitter,
21
+ TanimotoClusterSplitterConfig,
22
+ )
23
+ from diffbio.splitters.random import (
24
+ RandomSplitter,
25
+ RandomSplitterConfig,
26
+ StratifiedSplitter,
27
+ StratifiedSplitterConfig,
28
+ )
29
+ from diffbio.splitters.perturbation import (
30
+ FewShotSplitter,
31
+ FewShotSplitterConfig,
32
+ ZeroShotSplitter,
33
+ ZeroShotSplitterConfig,
34
+ )
35
+ from diffbio.splitters.sequence import (
36
+ SequenceIdentitySplitter,
37
+ SequenceIdentitySplitterConfig,
38
+ )
39
+
40
+ __all__ = [
41
+ # Base classes
42
+ "SplitterModule",
43
+ "SplitterConfig",
44
+ "SplitResult",
45
+ # Random splitters
46
+ "RandomSplitter",
47
+ "RandomSplitterConfig",
48
+ "StratifiedSplitter",
49
+ "StratifiedSplitterConfig",
50
+ # Molecular splitters
51
+ "ScaffoldSplitter",
52
+ "ScaffoldSplitterConfig",
53
+ "TanimotoClusterSplitter",
54
+ "TanimotoClusterSplitterConfig",
55
+ # Sequence splitters
56
+ "SequenceIdentitySplitter",
57
+ "SequenceIdentitySplitterConfig",
58
+ # Perturbation splitters
59
+ "FewShotSplitter",
60
+ "FewShotSplitterConfig",
61
+ "ZeroShotSplitter",
62
+ "ZeroShotSplitterConfig",
63
+ ]
@@ -0,0 +1,251 @@
1
+ """Base splitter classes for DiffBio.
2
+
3
+ This module provides the base classes for dataset splitting:
4
+ - SplitResult: NamedTuple containing train/valid/test indices
5
+ - SplitterConfig: Base configuration for splitters
6
+ - SplitterModule: Base class for all splitters
7
+ """
8
+
9
+ import logging
10
+ from collections.abc import Iterable
11
+ from dataclasses import dataclass
12
+ from typing import NamedTuple
13
+
14
+ import jax.numpy as jnp
15
+ import numpy as np
16
+ from flax import nnx
17
+
18
+ from datarax.core.config import StructuralConfig
19
+ from datarax.core.data_source import DataSourceModule
20
+ from datarax.core.structural import StructuralModule
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class SplitResult(NamedTuple):
26
+ """Result of a dataset split operation.
27
+
28
+ Attributes:
29
+ train_indices: Array of indices for training set
30
+ valid_indices: Array of indices for validation set
31
+ test_indices: Array of indices for test set
32
+ """
33
+
34
+ train_indices: jnp.ndarray
35
+ valid_indices: jnp.ndarray
36
+ test_indices: jnp.ndarray
37
+
38
+ @property
39
+ def train_size(self) -> int:
40
+ """Return number of training samples."""
41
+ return len(self.train_indices)
42
+
43
+ @property
44
+ def valid_size(self) -> int:
45
+ """Return number of validation samples."""
46
+ return len(self.valid_indices)
47
+
48
+ @property
49
+ def test_size(self) -> int:
50
+ """Return number of test samples."""
51
+ return len(self.test_indices)
52
+
53
+
54
+ @dataclass(frozen=True)
55
+ class SplitterConfig(StructuralConfig):
56
+ """Base configuration for splitters.
57
+
58
+ Frozen because splitters are non-parametric (StructuralModule).
59
+
60
+ Attributes:
61
+ train_frac: Fraction of data for training (default: 0.8)
62
+ valid_frac: Fraction of data for validation (default: 0.1)
63
+ test_frac: Fraction of data for testing (default: 0.1)
64
+ seed: Random seed for reproducibility (optional)
65
+ """
66
+
67
+ train_frac: float = 0.8
68
+ valid_frac: float = 0.1
69
+ test_frac: float = 0.1
70
+ seed: int | None = None
71
+
72
+ def __post_init__(self):
73
+ """Validate configuration after initialization."""
74
+ super().__post_init__()
75
+ total = self.train_frac + self.valid_frac + self.test_frac
76
+ if not np.isclose(total, 1.0):
77
+ raise ValueError(f"Split fractions must sum to 1.0, got {total}")
78
+
79
+
80
+ class SplitterModule(StructuralModule):
81
+ """Base class for dataset splitters.
82
+
83
+ Inherits from StructuralModule because:
84
+
85
+ - Non-parametric (no learnable parameters)
86
+ - Frozen config (splitting strategy is fixed)
87
+ - Uses process() method pattern
88
+ - Integrates with Datarax data sources
89
+
90
+ Splitters divide data into train/valid/test sets, while Datarax
91
+ SamplerModule controls iteration ORDER within those sets.
92
+
93
+ Args:
94
+ config: Splitter configuration
95
+ rngs: Random number generators for stochastic splitting
96
+ name: Optional name for the module
97
+ """
98
+
99
+ def __init__(
100
+ self,
101
+ config: SplitterConfig,
102
+ *,
103
+ rngs: nnx.Rngs | None = None,
104
+ name: str | None = None,
105
+ ):
106
+ """Initialize SplitterModule.
107
+
108
+ Args:
109
+ config: Splitter configuration
110
+ rngs: Random number generators
111
+ name: Optional module name
112
+ """
113
+ super().__init__(config, rngs=rngs, name=name)
114
+
115
+ def split(self, data_source: DataSourceModule) -> SplitResult:
116
+ """Split a data source into train/valid/test indices.
117
+
118
+ Subclasses must implement this method.
119
+
120
+ Args:
121
+ data_source: Datarax DataSourceModule to split
122
+
123
+ Returns:
124
+ SplitResult with train/valid/test indices
125
+ """
126
+ raise NotImplementedError("Subclasses must implement split()")
127
+
128
+ def process(self, data_source: DataSourceModule) -> SplitResult:
129
+ """Process data source using the split method.
130
+
131
+ Args:
132
+ data_source: Datarax DataSourceModule to split
133
+
134
+ Returns:
135
+ SplitResult with train/valid/test indices
136
+ """
137
+ return self.split(data_source)
138
+
139
+ def k_fold_split(
140
+ self, data_source: DataSourceModule, k: int = 5
141
+ ) -> list[tuple[jnp.ndarray, jnp.ndarray]]:
142
+ """K-fold cross-validation split.
143
+
144
+ Subclasses may implement this method.
145
+
146
+ Args:
147
+ data_source: Datarax DataSourceModule to split
148
+ k: Number of folds
149
+
150
+ Returns:
151
+ List of (train_indices, val_indices) tuples for each fold
152
+ """
153
+ raise NotImplementedError("Subclasses may implement k_fold_split()")
154
+
155
+ def assign_groups_to_splits(
156
+ self,
157
+ groups: Iterable[Iterable[int]],
158
+ total_size: int,
159
+ ) -> SplitResult:
160
+ """Assign grouped indices to train/valid/test by configured fractions.
161
+
162
+ Each input group is placed wholly into one split, preserving group boundaries
163
+ (for example scaffold clusters or sequence-identity clusters).
164
+ """
165
+ train_cutoff = self.config.train_frac * total_size
166
+ valid_cutoff = (self.config.train_frac + self.config.valid_frac) * total_size
167
+
168
+ train_inds: list[int] = []
169
+ valid_inds: list[int] = []
170
+ test_inds: list[int] = []
171
+
172
+ for group in groups:
173
+ group_indices = list(group)
174
+ if len(train_inds) < train_cutoff:
175
+ train_inds.extend(group_indices)
176
+ elif len(train_inds) + len(valid_inds) < valid_cutoff:
177
+ valid_inds.extend(group_indices)
178
+ else:
179
+ test_inds.extend(group_indices)
180
+
181
+ return SplitResult(
182
+ train_indices=jnp.array(train_inds, dtype=jnp.int32),
183
+ valid_indices=jnp.array(valid_inds, dtype=jnp.int32),
184
+ test_indices=jnp.array(test_inds, dtype=jnp.int32),
185
+ )
186
+
187
+ def create_split_sources(
188
+ self,
189
+ data_source: DataSourceModule,
190
+ split_result: SplitResult | None = None,
191
+ lazy: bool = True,
192
+ ) -> tuple[DataSourceModule, DataSourceModule, DataSourceModule]:
193
+ """Create separate data sources for each split.
194
+
195
+ This creates views into the original data source using the split indices.
196
+ Each returned source can be used with Datarax samplers independently.
197
+
198
+ Args:
199
+ data_source: Original data source
200
+ split_result: Pre-computed split (or compute if None)
201
+ lazy: If True, use lazy loading (IndexedViewSource). If False,
202
+ eagerly load into MemorySource (faster iteration but uses memory).
203
+
204
+ Returns:
205
+ Tuple of (train_source, valid_source, test_source)
206
+ """
207
+ if split_result is None:
208
+ split_result = self.split(data_source)
209
+
210
+ if lazy:
211
+ # LAZY LOADING: Create view sources that delegate to original
212
+ from diffbio.sources.indexed_view import (
213
+ IndexedViewSource,
214
+ IndexedViewSourceConfig,
215
+ )
216
+
217
+ train_config = IndexedViewSourceConfig(shuffle=True, seed=self.config.seed)
218
+ valid_config = IndexedViewSourceConfig(shuffle=False)
219
+ test_config = IndexedViewSourceConfig(shuffle=False)
220
+
221
+ return (
222
+ IndexedViewSource(
223
+ train_config, data_source, split_result.train_indices, rngs=self.rngs
224
+ ),
225
+ IndexedViewSource(
226
+ valid_config, data_source, split_result.valid_indices, rngs=self.rngs
227
+ ),
228
+ IndexedViewSource(
229
+ test_config, data_source, split_result.test_indices, rngs=self.rngs
230
+ ),
231
+ )
232
+ else:
233
+ # EAGER LOADING: Load all elements into memory (faster iteration)
234
+ from datarax.sources import MemorySource, MemorySourceConfig
235
+
236
+ train_elements = [data_source[int(i)] for i in split_result.train_indices]
237
+ valid_elements = [data_source[int(i)] for i in split_result.valid_indices]
238
+ test_elements = [data_source[int(i)] for i in split_result.test_indices]
239
+
240
+ train_config = MemorySourceConfig(
241
+ shuffle=True,
242
+ seed=self.config.seed, # pyright: ignore[reportCallIssue]
243
+ )
244
+ valid_config = MemorySourceConfig(shuffle=False)
245
+ test_config = MemorySourceConfig(shuffle=False)
246
+
247
+ return (
248
+ MemorySource(train_config, data=train_elements, rngs=self.rngs),
249
+ MemorySource(valid_config, data=valid_elements, rngs=self.rngs),
250
+ MemorySource(test_config, data=test_elements, rngs=self.rngs),
251
+ )