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