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,243 @@
|
|
|
1
|
+
"""Differentiable differential distribution operator for single-cell analysis.
|
|
2
|
+
|
|
3
|
+
This module provides a differentiable implementation of the KS-test and
|
|
4
|
+
pattern classification for detecting distributional differences between
|
|
5
|
+
two conditions in single-cell expression data, inspired by scDD
|
|
6
|
+
(Korthauer et al., Genome Biology 2016).
|
|
7
|
+
|
|
8
|
+
Key technique: Replace the hard empirical CDF step function with a
|
|
9
|
+
sigmoid-smoothed soft CDF, and replace the hard max in the KS statistic
|
|
10
|
+
with logsumexp-based soft_max from TemperatureOperator.
|
|
11
|
+
|
|
12
|
+
Applications: Identifying genes with differential distributions (shift,
|
|
13
|
+
scale, both, or none) between conditions in scRNA-seq experiments.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
import jax
|
|
21
|
+
import jax.numpy as jnp
|
|
22
|
+
from datarax.core.config import OperatorConfig
|
|
23
|
+
from flax import nnx
|
|
24
|
+
from jaxtyping import Array, Float, PyTree
|
|
25
|
+
|
|
26
|
+
from diffbio.constants import EPSILON
|
|
27
|
+
|
|
28
|
+
from diffbio.core.base_operators import TemperatureOperator
|
|
29
|
+
from diffbio.utils.nn_utils import ensure_rngs
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class DifferentialDistributionConfig(OperatorConfig):
|
|
36
|
+
"""Configuration for differentiable differential distribution testing.
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
n_genes: Number of genes to analyse.
|
|
40
|
+
temperature: Temperature controlling sigmoid smoothness in the soft
|
|
41
|
+
CDF and logsumexp soft max. Lower values yield sharper
|
|
42
|
+
approximations closer to the true KS statistic.
|
|
43
|
+
learnable_temperature: Whether temperature is a learnable parameter.
|
|
44
|
+
n_pattern_classes: Number of distributional pattern categories.
|
|
45
|
+
Default 4 corresponds to (shift, scale, both, none).
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
n_genes: int = 2000
|
|
49
|
+
temperature: float = 1.0
|
|
50
|
+
learnable_temperature: bool = False
|
|
51
|
+
n_pattern_classes: int = 4
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class DifferentiableDifferentialDistribution(TemperatureOperator):
|
|
55
|
+
"""Differentiable KS-test with learned pattern classification.
|
|
56
|
+
|
|
57
|
+
For each gene, this operator:
|
|
58
|
+
|
|
59
|
+
1. Splits cells into two conditions based on binary condition labels.
|
|
60
|
+
2. Computes a soft empirical CDF using sigmoid smoothing:
|
|
61
|
+
``soft_CDF(x, values) = mean(sigmoid((x - values) / temperature))``
|
|
62
|
+
3. Computes a soft KS statistic as the smooth maximum of
|
|
63
|
+
``|CDF_A(x) - CDF_B(x)|`` over evaluation points, using logsumexp.
|
|
64
|
+
4. Extracts distributional features (mean shift, variance ratio,
|
|
65
|
+
zero-proportion difference) and passes them through a learned
|
|
66
|
+
linear head to classify each gene into one of the pattern categories
|
|
67
|
+
(shift, scale, both, none).
|
|
68
|
+
|
|
69
|
+
Inherits from TemperatureOperator to get:
|
|
70
|
+
|
|
71
|
+
- _temperature property for temperature-controlled smoothing
|
|
72
|
+
- soft_max() for logsumexp-based smooth maximum
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
config: DifferentialDistributionConfig with model parameters.
|
|
76
|
+
rngs: Flax NNX random number generators.
|
|
77
|
+
name: Optional operator name.
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
```python
|
|
81
|
+
config = DifferentialDistributionConfig(n_genes=2000, temperature=1.0)
|
|
82
|
+
op = DifferentiableDifferentialDistribution(config, rngs=nnx.Rngs(42))
|
|
83
|
+
data = {"counts": counts, "condition_labels": labels}
|
|
84
|
+
result, state, meta = op.apply(data, {}, None)
|
|
85
|
+
```
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
# Number of features extracted per gene for pattern classification:
|
|
89
|
+
# mean_shift, variance_ratio, zero_proportion_diff
|
|
90
|
+
_N_PATTERN_FEATURES: int = 3
|
|
91
|
+
|
|
92
|
+
def __init__(
|
|
93
|
+
self,
|
|
94
|
+
config: DifferentialDistributionConfig,
|
|
95
|
+
*,
|
|
96
|
+
rngs: nnx.Rngs | None = None,
|
|
97
|
+
name: str | None = None,
|
|
98
|
+
) -> None:
|
|
99
|
+
"""Initialize the differentiable differential distribution operator.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
config: Differential distribution configuration.
|
|
103
|
+
rngs: Random number generators for parameter initialisation.
|
|
104
|
+
name: Optional operator name.
|
|
105
|
+
"""
|
|
106
|
+
super().__init__(config, rngs=rngs, name=name)
|
|
107
|
+
|
|
108
|
+
self.n_genes = config.n_genes
|
|
109
|
+
self.n_pattern_classes = config.n_pattern_classes
|
|
110
|
+
|
|
111
|
+
rngs_safe = ensure_rngs(rngs)
|
|
112
|
+
|
|
113
|
+
# Learned linear head: pattern features -> pattern logits
|
|
114
|
+
self.pattern_head = nnx.Linear(
|
|
115
|
+
in_features=self._N_PATTERN_FEATURES,
|
|
116
|
+
out_features=self.n_pattern_classes,
|
|
117
|
+
rngs=rngs_safe,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def _process_single_gene(
|
|
121
|
+
self,
|
|
122
|
+
gene_values: Float[Array, "n_cells"],
|
|
123
|
+
condition_mask: Float[Array, "n_cells"],
|
|
124
|
+
) -> tuple[Float[Array, ""], Float[Array, "n_patterns"]]:
|
|
125
|
+
"""Process a single gene: compute KS stat and pattern logits.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
gene_values: Expression values for one gene across all cells.
|
|
129
|
+
condition_mask: Binary mask (0/1) indicating condition membership.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Tuple of (ks_statistic, pattern_logits).
|
|
133
|
+
"""
|
|
134
|
+
# Soft splitting: weight contributions by condition membership
|
|
135
|
+
# condition_mask=0 -> condition A, condition_mask=1 -> condition B
|
|
136
|
+
mask_a = 1.0 - condition_mask
|
|
137
|
+
mask_b = condition_mask
|
|
138
|
+
|
|
139
|
+
n_a = jnp.sum(mask_a) + EPSILON
|
|
140
|
+
n_b = jnp.sum(mask_b) + EPSILON
|
|
141
|
+
|
|
142
|
+
# Weighted values for each condition using soft masks
|
|
143
|
+
# For the CDF computation, we use all values but weight by condition
|
|
144
|
+
# To handle variable-size splits in a JIT-compatible way, we compute
|
|
145
|
+
# weighted statistics instead of explicit splits.
|
|
146
|
+
|
|
147
|
+
# For KS: evaluate soft CDF using the full set of values, but weight
|
|
148
|
+
# the indicator functions by condition membership.
|
|
149
|
+
eval_points = gene_values # Evaluate at all cell values
|
|
150
|
+
|
|
151
|
+
temp = self._temperature
|
|
152
|
+
# diff: (n_cells, n_cells) -- eval_points[i] vs gene_values[j]
|
|
153
|
+
diff = eval_points[:, None] - gene_values[None, :]
|
|
154
|
+
sigmoid_vals = jax.nn.sigmoid(diff / temp)
|
|
155
|
+
|
|
156
|
+
# Weighted CDF for condition A: sum(sigmoid * mask_a) / n_a
|
|
157
|
+
cdf_a = jnp.sum(sigmoid_vals * mask_a[None, :], axis=1) / n_a
|
|
158
|
+
# Weighted CDF for condition B: sum(sigmoid * mask_b) / n_b
|
|
159
|
+
cdf_b = jnp.sum(sigmoid_vals * mask_b[None, :], axis=1) / n_b
|
|
160
|
+
|
|
161
|
+
abs_diff = jnp.abs(cdf_a - cdf_b)
|
|
162
|
+
# Use softmax-weighted sum as smooth max: sum_i(x_i * softmax(x_i/T))
|
|
163
|
+
# This stays within [min(x), max(x)] unlike logsumexp which overshoots.
|
|
164
|
+
temp = self._temperature
|
|
165
|
+
weights = jax.nn.softmax(abs_diff / (temp + EPSILON))
|
|
166
|
+
ks_stat = jnp.sum(abs_diff * weights)
|
|
167
|
+
|
|
168
|
+
# Pattern features using weighted statistics
|
|
169
|
+
mean_a = jnp.sum(gene_values * mask_a) / n_a
|
|
170
|
+
mean_b = jnp.sum(gene_values * mask_b) / n_b
|
|
171
|
+
mean_shift = jnp.abs(mean_a - mean_b)
|
|
172
|
+
|
|
173
|
+
var_a = jnp.sum(mask_a * (gene_values - mean_a) ** 2) / n_a + EPSILON
|
|
174
|
+
var_b = jnp.sum(mask_b * (gene_values - mean_b) ** 2) / n_b + EPSILON
|
|
175
|
+
variance_ratio = jax.nn.sigmoid(jnp.log(var_a / var_b))
|
|
176
|
+
|
|
177
|
+
# Soft zero fraction per condition
|
|
178
|
+
soft_zero = jax.nn.sigmoid(-gene_values / (temp + EPSILON))
|
|
179
|
+
frac_zero_a = jnp.sum(soft_zero * mask_a) / n_a
|
|
180
|
+
frac_zero_b = jnp.sum(soft_zero * mask_b) / n_b
|
|
181
|
+
zero_diff = jnp.abs(frac_zero_a - frac_zero_b)
|
|
182
|
+
|
|
183
|
+
features = jnp.stack([mean_shift, variance_ratio, zero_diff])
|
|
184
|
+
pattern_logits = self.pattern_head(features)
|
|
185
|
+
|
|
186
|
+
return ks_stat, pattern_logits
|
|
187
|
+
|
|
188
|
+
def apply(
|
|
189
|
+
self,
|
|
190
|
+
data: PyTree,
|
|
191
|
+
state: PyTree,
|
|
192
|
+
metadata: dict[str, Any] | None,
|
|
193
|
+
random_params: Any = None,
|
|
194
|
+
stats: dict[str, Any] | None = None,
|
|
195
|
+
) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
|
|
196
|
+
"""Apply differentiable differential distribution testing.
|
|
197
|
+
|
|
198
|
+
For each gene, computes a soft KS statistic and classifies the
|
|
199
|
+
distributional difference pattern using a learned linear head.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
data: Dictionary containing:
|
|
203
|
+
- "counts": Gene expression matrix (n_cells, n_genes)
|
|
204
|
+
- "condition_labels": Binary condition labels (n_cells,)
|
|
205
|
+
state: Element state (passed through unchanged).
|
|
206
|
+
metadata: Element metadata (passed through unchanged).
|
|
207
|
+
random_params: Not used.
|
|
208
|
+
stats: Not used.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Tuple of (transformed_data, state, metadata):
|
|
212
|
+
- transformed_data contains:
|
|
213
|
+
|
|
214
|
+
- "counts": Original expression counts
|
|
215
|
+
- "condition_labels": Original condition labels
|
|
216
|
+
- "ks_statistics": Soft KS statistic per gene (n_genes,)
|
|
217
|
+
- "pattern_logits": Pattern class logits (n_genes, n_patterns)
|
|
218
|
+
- "pattern_labels": Predicted pattern labels (n_genes,)
|
|
219
|
+
- state is passed through unchanged
|
|
220
|
+
- metadata is passed through unchanged
|
|
221
|
+
"""
|
|
222
|
+
counts = data["counts"]
|
|
223
|
+
condition_labels = data["condition_labels"]
|
|
224
|
+
|
|
225
|
+
# Process all genes in parallel using vmap over gene dimension (axis 1)
|
|
226
|
+
def process_gene(
|
|
227
|
+
gene_col: Float[Array, "n_cells"],
|
|
228
|
+
) -> tuple[Float[Array, ""], Float[Array, "n_patterns"]]:
|
|
229
|
+
return self._process_single_gene(gene_col, condition_labels)
|
|
230
|
+
|
|
231
|
+
# vmap over columns (genes) of counts: (n_cells, n_genes) -> per-gene
|
|
232
|
+
ks_statistics, pattern_logits = jax.vmap(process_gene, in_axes=1)(counts)
|
|
233
|
+
|
|
234
|
+
pattern_labels = jnp.argmax(pattern_logits, axis=-1)
|
|
235
|
+
|
|
236
|
+
transformed_data = {
|
|
237
|
+
**data,
|
|
238
|
+
"ks_statistics": ks_statistics,
|
|
239
|
+
"pattern_logits": pattern_logits,
|
|
240
|
+
"pattern_labels": pattern_labels,
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return transformed_data, state, metadata
|