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,128 @@
|
|
|
1
|
+
"""Indexed embedding-artifact sources with strict row-identity alignment."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, cast
|
|
7
|
+
|
|
8
|
+
import jax.numpy as jnp
|
|
9
|
+
import numpy as np
|
|
10
|
+
from flax import nnx
|
|
11
|
+
|
|
12
|
+
from diffbio.sources.embeddings import EmbeddingArtifactSource, EmbeddingArtifactSourceConfig
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class IndexedEmbeddingSourceConfig(EmbeddingArtifactSourceConfig):
|
|
17
|
+
"""Configuration for embedding sources that persist row identities."""
|
|
18
|
+
|
|
19
|
+
row_id_key: str | None = None
|
|
20
|
+
|
|
21
|
+
def __post_init__(self) -> None:
|
|
22
|
+
"""Validate the required row-identity field name."""
|
|
23
|
+
super().__post_init__()
|
|
24
|
+
if self.row_id_key is None:
|
|
25
|
+
raise ValueError("row_id_key is required for IndexedEmbeddingSourceConfig")
|
|
26
|
+
if not self.row_id_key.strip():
|
|
27
|
+
raise ValueError("row_id_key must be a non-empty string")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class IndexedEmbeddingSource(EmbeddingArtifactSource):
|
|
31
|
+
"""Eager Datarax-style source for embeddings with optional row identities."""
|
|
32
|
+
|
|
33
|
+
config: IndexedEmbeddingSourceConfig # pyright: ignore[reportIncompatibleVariableOverride]
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
config: IndexedEmbeddingSourceConfig,
|
|
38
|
+
*,
|
|
39
|
+
rngs: nnx.Rngs | None = None,
|
|
40
|
+
name: str | None = None,
|
|
41
|
+
) -> None:
|
|
42
|
+
"""Load the embedding artifact and persist the configured row IDs."""
|
|
43
|
+
super().__init__(config, rngs=rngs, name=name)
|
|
44
|
+
|
|
45
|
+
row_id_key = config.row_id_key
|
|
46
|
+
if row_id_key is None:
|
|
47
|
+
raise ValueError("row_id_key is required for IndexedEmbeddingSource")
|
|
48
|
+
|
|
49
|
+
row_ids_array = self.artifact_metadata.get(row_id_key)
|
|
50
|
+
row_ids: tuple[str, ...] | None = None
|
|
51
|
+
if row_ids_array is not None:
|
|
52
|
+
if int(np.asarray(row_ids_array).shape[0]) != int(self.embeddings.shape[0]):
|
|
53
|
+
raise ValueError(
|
|
54
|
+
f"{row_id_key} must contain one value per embedding row "
|
|
55
|
+
f"({np.asarray(row_ids_array).shape[0]} vs {self.embeddings.shape[0]})."
|
|
56
|
+
)
|
|
57
|
+
row_ids = tuple(str(item) for item in np.asarray(row_ids_array))
|
|
58
|
+
cast(dict[str, Any], self.data)[row_id_key] = row_ids
|
|
59
|
+
|
|
60
|
+
object.__setattr__(self, "_row_ids", row_ids)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def row_id_key(self) -> str:
|
|
64
|
+
"""Configured metadata field name that identifies artifact rows."""
|
|
65
|
+
return cast(str, self.config.row_id_key)
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def row_ids(self) -> tuple[str, ...] | None:
|
|
69
|
+
"""Tuple of persisted row identifiers, if present."""
|
|
70
|
+
return self._row_ids
|
|
71
|
+
|
|
72
|
+
def load(self) -> dict[str, Any]:
|
|
73
|
+
"""Return the eager in-memory payload exposed by the source."""
|
|
74
|
+
payload = super().load()
|
|
75
|
+
if self.row_ids is not None:
|
|
76
|
+
payload[self.row_id_key] = self.row_ids
|
|
77
|
+
return payload
|
|
78
|
+
|
|
79
|
+
def align_to_reference_ids(
|
|
80
|
+
self,
|
|
81
|
+
*,
|
|
82
|
+
reference_ids: list[str] | tuple[str, ...],
|
|
83
|
+
require_row_ids: bool = True,
|
|
84
|
+
artifact_label: str,
|
|
85
|
+
id_display_name: str,
|
|
86
|
+
) -> jnp.ndarray:
|
|
87
|
+
"""Align embedding rows to a reference dataset order."""
|
|
88
|
+
canonical_reference_ids = tuple(str(item) for item in reference_ids)
|
|
89
|
+
|
|
90
|
+
if self.embeddings.ndim != 2:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
f"{artifact_label} embedding artifacts must be rank-2 matrices "
|
|
93
|
+
f"(received shape {self.embeddings.shape})."
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if self.row_ids is None:
|
|
97
|
+
if require_row_ids:
|
|
98
|
+
raise ValueError(
|
|
99
|
+
f"{artifact_label} embedding artifacts must include {self.row_id_key} "
|
|
100
|
+
"for strict alignment."
|
|
101
|
+
)
|
|
102
|
+
if self.embeddings.shape[0] != len(canonical_reference_ids):
|
|
103
|
+
raise ValueError(
|
|
104
|
+
"Positional embedding alignment requires the same number of rows "
|
|
105
|
+
f"as reference items ({self.embeddings.shape[0]} vs "
|
|
106
|
+
f"{len(canonical_reference_ids)})."
|
|
107
|
+
)
|
|
108
|
+
return self.embeddings
|
|
109
|
+
|
|
110
|
+
if len(set(canonical_reference_ids)) != len(canonical_reference_ids):
|
|
111
|
+
raise ValueError(f"Reference {self.row_id_key} values must be unique.")
|
|
112
|
+
if len(set(self.row_ids)) != len(self.row_ids):
|
|
113
|
+
raise ValueError(f"Embedding artifact {self.row_id_key} values must be unique.")
|
|
114
|
+
|
|
115
|
+
reference_set = set(canonical_reference_ids)
|
|
116
|
+
artifact_set = set(self.row_ids)
|
|
117
|
+
if reference_set != artifact_set:
|
|
118
|
+
missing = sorted(reference_set - artifact_set)
|
|
119
|
+
extra = sorted(artifact_set - reference_set)
|
|
120
|
+
raise ValueError(
|
|
121
|
+
f"{id_display_name} mismatch between reference dataset and embedding artifact. "
|
|
122
|
+
f"Missing: {missing}. Extra: {extra}."
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
index_by_row_id = {row_id: index for index, row_id in enumerate(self.row_ids)}
|
|
126
|
+
row_indices = [index_by_row_id[row_id] for row_id in canonical_reference_ids]
|
|
127
|
+
reordered = np.asarray(self.embeddings)[row_indices]
|
|
128
|
+
return jnp.asarray(reordered, dtype=jnp.float32)
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""IndexedViewSource - Lazy-loading view into a data source.
|
|
2
|
+
|
|
3
|
+
This module provides IndexedViewSource, which wraps an existing DataSourceModule
|
|
4
|
+
and provides access only to elements at specified indices. Elements are loaded
|
|
5
|
+
on-demand from the underlying source, enabling lazy loading for large datasets.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Iterator
|
|
11
|
+
|
|
12
|
+
import jax
|
|
13
|
+
import jax.numpy as jnp
|
|
14
|
+
from flax import nnx
|
|
15
|
+
|
|
16
|
+
from datarax.core.config import StructuralConfig
|
|
17
|
+
from datarax.core.data_source import DataSourceModule
|
|
18
|
+
from datarax.typing import Element
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class IndexedViewSourceConfig(StructuralConfig):
|
|
25
|
+
"""Configuration for IndexedViewSource.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
shuffle: Whether to shuffle the view indices on initialization and reset
|
|
29
|
+
seed: Random seed for shuffling (optional)
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
shuffle: bool = False
|
|
33
|
+
seed: int | None = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class IndexedViewSource(DataSourceModule):
|
|
37
|
+
"""Lazy-loading view into a data source using index mapping.
|
|
38
|
+
|
|
39
|
+
This source wraps an existing DataSourceModule and provides access
|
|
40
|
+
only to elements at specified indices. Elements are loaded ON-DEMAND
|
|
41
|
+
from the underlying source, enabling lazy loading for large datasets.
|
|
42
|
+
|
|
43
|
+
Key Features:
|
|
44
|
+
|
|
45
|
+
- LAZY LOADING: Elements fetched from underlying source only when accessed
|
|
46
|
+
- Memory efficient: Only stores indices, not actual data
|
|
47
|
+
- Preserves underlying source's lazy loading behavior
|
|
48
|
+
- Supports shuffling of view indices (not underlying data)
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
```python
|
|
52
|
+
# Create view of first 1000 elements
|
|
53
|
+
indices = jnp.arange(1000)
|
|
54
|
+
config = IndexedViewSourceConfig()
|
|
55
|
+
view = IndexedViewSource(config, original_source, indices)
|
|
56
|
+
view[0] # Fetches original_source[indices[0]] lazily
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
config: Configuration for the view source
|
|
61
|
+
source: Underlying data source to wrap
|
|
62
|
+
indices: Array of indices into the source to expose
|
|
63
|
+
rngs: Random number generators for shuffling
|
|
64
|
+
name: Optional name for the module
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
config: IndexedViewSourceConfig,
|
|
70
|
+
source: DataSourceModule,
|
|
71
|
+
indices: jnp.ndarray,
|
|
72
|
+
*,
|
|
73
|
+
rngs: nnx.Rngs | None = None,
|
|
74
|
+
name: str | None = None,
|
|
75
|
+
):
|
|
76
|
+
"""Initialize IndexedViewSource.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
config: Configuration for the view source
|
|
80
|
+
source: Underlying data source to wrap
|
|
81
|
+
indices: Array of indices into the source to expose
|
|
82
|
+
rngs: Random number generators for shuffling
|
|
83
|
+
name: Optional name for the module
|
|
84
|
+
"""
|
|
85
|
+
super().__init__(config, rngs=rngs, name=name)
|
|
86
|
+
self._source = source
|
|
87
|
+
self._indices = indices
|
|
88
|
+
self._view_indices = jnp.arange(len(indices)) # Local view ordering
|
|
89
|
+
self._current_idx = 0
|
|
90
|
+
|
|
91
|
+
# Apply initial shuffle if configured
|
|
92
|
+
if config.shuffle:
|
|
93
|
+
self._shuffle_view()
|
|
94
|
+
|
|
95
|
+
def _shuffle_view(self) -> None:
|
|
96
|
+
"""Shuffle the view indices (not the underlying data)."""
|
|
97
|
+
if self.rngs is not None and "shuffle" in self.rngs:
|
|
98
|
+
key = self.rngs.shuffle()
|
|
99
|
+
elif self.config.seed is not None:
|
|
100
|
+
key = jax.random.key(self.config.seed)
|
|
101
|
+
else:
|
|
102
|
+
key = jax.random.key(0)
|
|
103
|
+
|
|
104
|
+
self._view_indices = jax.random.permutation(key, self._view_indices)
|
|
105
|
+
|
|
106
|
+
def __len__(self) -> int:
|
|
107
|
+
"""Return number of elements in the view."""
|
|
108
|
+
return len(self._indices)
|
|
109
|
+
|
|
110
|
+
def __getitem__(self, idx: int) -> Element | None:
|
|
111
|
+
"""Get element at view index (LAZY - fetches from underlying source).
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
idx: Index into the VIEW (0 to len(view)-1)
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Element from underlying source at mapped index, or None if out of bounds
|
|
118
|
+
"""
|
|
119
|
+
if idx < 0 or idx >= len(self._indices):
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
# Map view index -> shuffled view index -> original source index
|
|
123
|
+
view_idx = int(self._view_indices[idx])
|
|
124
|
+
source_idx = int(self._indices[view_idx])
|
|
125
|
+
|
|
126
|
+
# LAZY: Fetch from underlying source only now
|
|
127
|
+
return self._source[source_idx]
|
|
128
|
+
|
|
129
|
+
def __iter__(self) -> Iterator[Element]:
|
|
130
|
+
"""Iterate over view elements (LAZY - fetches on demand)."""
|
|
131
|
+
self._current_idx = 0
|
|
132
|
+
return self
|
|
133
|
+
|
|
134
|
+
def __next__(self) -> Element:
|
|
135
|
+
"""Get next element (LAZY)."""
|
|
136
|
+
if self._current_idx >= len(self._indices):
|
|
137
|
+
raise StopIteration
|
|
138
|
+
|
|
139
|
+
element = self[self._current_idx]
|
|
140
|
+
self._current_idx += 1
|
|
141
|
+
|
|
142
|
+
if element is None:
|
|
143
|
+
raise StopIteration
|
|
144
|
+
|
|
145
|
+
return element
|
|
146
|
+
|
|
147
|
+
def reset(self, seed: int | None = None) -> None:
|
|
148
|
+
"""Reset iteration and optionally reshuffle.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
seed: Optional new seed for shuffling
|
|
152
|
+
"""
|
|
153
|
+
self._current_idx = 0
|
|
154
|
+
|
|
155
|
+
if self.config.shuffle:
|
|
156
|
+
if seed is not None:
|
|
157
|
+
# Update seed and reshuffle
|
|
158
|
+
key = jax.random.key(seed)
|
|
159
|
+
self._view_indices = jax.random.permutation(key, jnp.arange(len(self._indices)))
|
|
160
|
+
else:
|
|
161
|
+
self._shuffle_view()
|
|
162
|
+
|
|
163
|
+
def get_batch(self, batch_size: int, key: jax.Array | None = None) -> list[Element]:
|
|
164
|
+
"""Get next batch of elements (LAZY).
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
batch_size: Number of elements to fetch
|
|
168
|
+
key: Optional RNG key (unused, for interface compatibility)
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
List of elements
|
|
172
|
+
"""
|
|
173
|
+
batch = []
|
|
174
|
+
for _ in range(batch_size):
|
|
175
|
+
if self._current_idx >= len(self._indices):
|
|
176
|
+
break
|
|
177
|
+
element = self[self._current_idx]
|
|
178
|
+
if element is not None:
|
|
179
|
+
batch.append(element)
|
|
180
|
+
self._current_idx += 1
|
|
181
|
+
return batch
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def underlying_source(self) -> DataSourceModule:
|
|
185
|
+
"""Access the underlying data source."""
|
|
186
|
+
return self._source
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def source_indices(self) -> jnp.ndarray:
|
|
190
|
+
"""Get the indices into the underlying source."""
|
|
191
|
+
return self._indices
|