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,234 @@
1
+ """Differentiable Hidden Markov Model operator.
2
+
3
+ This module provides a differentiable implementation of the HMM
4
+ forward algorithm using logsumexp for numerical stability.
5
+
6
+ Key technique: Use logsumexp instead of direct probability multiplication
7
+ to maintain numerical stability and enable gradient flow.
8
+
9
+ Applications: Gene finding, chromatin state annotation, profile search.
10
+
11
+ Inherits from HMMOperator to get:
12
+
13
+ - forward_pass() for likelihood computation
14
+ - forward_backward_posteriors() for posterior computation
15
+ - get_log_transition_matrix(), get_log_emission_matrix(),
16
+ get_log_initial_distribution() for parameter access
17
+ """
18
+
19
+ import logging
20
+ from dataclasses import dataclass
21
+ from typing import Any
22
+
23
+ import jax
24
+ import jax.numpy as jnp
25
+ from datarax.core.config import OperatorConfig
26
+ from flax import nnx
27
+ from jaxtyping import Array, Float, Int, PyTree
28
+
29
+ from diffbio.core.base_operators import HMMOperator
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ @dataclass(frozen=True)
35
+ class HMMConfig(OperatorConfig):
36
+ """Configuration for DifferentiableHMM.
37
+
38
+ Attributes:
39
+ num_states: Number of hidden states.
40
+ num_emissions: Number of possible emissions (e.g., 4 for DNA).
41
+ temperature: Temperature for softmax operations.
42
+ learnable_transitions: Whether transition probabilities are learnable.
43
+ learnable_emissions: Whether emission probabilities are learnable.
44
+ """
45
+
46
+ num_states: int = 3
47
+ num_emissions: int = 4
48
+ temperature: float = 1.0
49
+ learnable_transitions: bool = True
50
+ learnable_emissions: bool = True
51
+
52
+
53
+ class DifferentiableHMM(HMMOperator):
54
+ """Differentiable Hidden Markov Model.
55
+
56
+ This operator implements the HMM forward algorithm with differentiable
57
+ operations, enabling gradient-based learning of transition and emission
58
+ parameters.
59
+
60
+ The forward algorithm computes P(observations | model) using dynamic
61
+ programming with logsumexp for numerical stability:
62
+
63
+ alpha[t, j] = sum_i(alpha[t-1, i] * A[i,j]) * B[j, o_t]
64
+
65
+ In log space:
66
+ log_alpha[t, j] = logsumexp_i(log_alpha[t-1, i] + log_A[i,j]) + log_B[j, o_t]
67
+
68
+ Inherits from HMMOperator to get:
69
+
70
+ - forward_pass() for likelihood computation
71
+ - forward_backward_posteriors() for posterior computation
72
+ - get_log_transition_matrix(), get_log_emission_matrix(),
73
+ get_log_initial_distribution() for parameter access
74
+
75
+ Args:
76
+ config: HMMConfig with model parameters.
77
+ rngs: Flax NNX random number generators.
78
+ name: Optional operator name.
79
+
80
+ Example:
81
+ ```python
82
+ config = HMMConfig(num_states=3, num_emissions=4)
83
+ hmm = DifferentiableHMM(config, rngs=nnx.Rngs(42))
84
+ data = {"observations": jnp.array([0, 1, 2, 3])}
85
+ result, state, meta = hmm.apply(data, {}, None)
86
+ ```
87
+ """
88
+
89
+ def __init__(
90
+ self,
91
+ config: HMMConfig,
92
+ *,
93
+ rngs: nnx.Rngs | None = None,
94
+ name: str | None = None,
95
+ ):
96
+ """Initialize the HMM operator.
97
+
98
+ Args:
99
+ config: HMM configuration.
100
+ rngs: Random number generators for initialization.
101
+ name: Optional operator name.
102
+ """
103
+ # HMMOperator handles parameter initialization
104
+ super().__init__(config, rngs=rngs, name=name)
105
+
106
+ # get_log_transition_matrix() is inherited from HMMOperator
107
+ # get_log_emission_matrix() is inherited from HMMOperator
108
+ # get_log_initial_distribution() is inherited from HMMOperator
109
+
110
+ def forward(
111
+ self,
112
+ observations: Int[Array, "seq_len"],
113
+ ) -> Float[Array, ""]:
114
+ """Compute log probability of observations using forward algorithm.
115
+
116
+ Delegates to inherited forward_pass() from HMMOperator.
117
+
118
+ Args:
119
+ observations: Integer-encoded observations (seq_len,).
120
+
121
+ Returns:
122
+ Log probability of the observation sequence.
123
+ """
124
+ return self.forward_pass(observations)
125
+
126
+ def forward_soft(
127
+ self,
128
+ observations: Float[Array, "seq_len n_emissions"],
129
+ ) -> Float[Array, ""]:
130
+ """Compute log probability with soft (probabilistic) observations.
131
+
132
+ This variant accepts soft observations (probability distributions
133
+ over emissions) for fully differentiable operation.
134
+
135
+ Args:
136
+ observations: Soft observations (seq_len, n_emissions).
137
+
138
+ Returns:
139
+ Log probability of the observation sequence.
140
+ """
141
+ log_trans = self.get_log_transition_matrix()
142
+ log_emit = self.get_log_emission_matrix()
143
+ log_init = self.get_log_initial_distribution()
144
+
145
+ # Soft emission: sum over emissions weighted by observation probs
146
+ # log P(o_t | state) = logsumexp(log_emit + log(o_t))
147
+ def soft_emission(obs: Float[Array, "n_emissions"]) -> Float[Array, "n_states"]:
148
+ """Compute log emission probability for soft observation vector."""
149
+ # obs is (n_emissions,), log_emit is (n_states, n_emissions)
150
+ log_obs = jnp.log(obs + 1e-10)
151
+ return jax.scipy.special.logsumexp(log_emit + log_obs, axis=1)
152
+
153
+ # Initialize
154
+ log_alpha = log_init + soft_emission(observations[0])
155
+
156
+ # Forward pass
157
+ def forward_step(
158
+ log_alpha: Float[Array, "n_states"], obs: Float[Array, "n_emissions"]
159
+ ) -> tuple[Float[Array, "n_states"], None]:
160
+ """Advance the forward variable by one time step."""
161
+ log_alpha_expanded = log_alpha[:, None]
162
+ log_alpha_new = jax.scipy.special.logsumexp(log_alpha_expanded + log_trans, axis=0)
163
+ log_alpha_new = log_alpha_new + soft_emission(obs)
164
+ return log_alpha_new, None
165
+
166
+ log_alpha, _ = jax.lax.scan(forward_step, log_alpha, observations[1:])
167
+
168
+ log_prob = jax.scipy.special.logsumexp(log_alpha)
169
+
170
+ return log_prob
171
+
172
+ def forward_backward(
173
+ self,
174
+ observations: Int[Array, "seq_len"],
175
+ ) -> Float[Array, "seq_len num_states"]:
176
+ """Compute state posteriors using forward-backward algorithm.
177
+
178
+ Delegates to inherited forward_backward_posteriors() from HMMOperator.
179
+
180
+ Args:
181
+ observations: Integer-encoded observations.
182
+
183
+ Returns:
184
+ State posteriors P(state_t | observations) for each position.
185
+ """
186
+ return self.forward_backward_posteriors(observations)
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 HMM to observation sequence.
197
+
198
+ This method computes the log-likelihood and state posteriors
199
+ for a given observation sequence.
200
+
201
+ Args:
202
+ data: Dictionary containing:
203
+ - "observations": Integer-encoded observations (seq_len,)
204
+ state: Element state (passed through unchanged)
205
+ metadata: Element metadata (passed through unchanged)
206
+ random_params: Not used (deterministic operator)
207
+ stats: Not used
208
+
209
+ Returns:
210
+ Tuple of (transformed_data, state, metadata):
211
+ - transformed_data contains:
212
+
213
+ - "observations": Original observations
214
+ - "log_likelihood": Log probability of sequence
215
+ - "state_posteriors": P(state | observations) at each position
216
+ - state is passed through unchanged
217
+ - metadata is passed through unchanged
218
+ """
219
+ observations = data["observations"]
220
+
221
+ # Compute log likelihood
222
+ log_likelihood = self.forward(observations)
223
+
224
+ # Compute state posteriors
225
+ state_posteriors = self.forward_backward(observations)
226
+
227
+ # Build output data
228
+ transformed_data = {
229
+ "observations": observations,
230
+ "log_likelihood": log_likelihood,
231
+ "state_posteriors": state_posteriors,
232
+ }
233
+
234
+ return transformed_data, state, metadata
@@ -0,0 +1,272 @@
1
+ """Differentiable Negative Binomial GLM operator.
2
+
3
+ This module provides a differentiable implementation of the negative
4
+ binomial generalized linear model for differential expression analysis,
5
+ inspired by DESeq2.
6
+
7
+ Key technique: Parameterize the NB mean through a log-linear model
8
+ and estimate dispersion parameters per gene.
9
+ """
10
+
11
+ import logging
12
+ from dataclasses import dataclass
13
+ from typing import Any
14
+
15
+ import jax
16
+ import jax.numpy as jnp
17
+ from datarax.core.config import OperatorConfig
18
+ from datarax.core.operator import OperatorModule
19
+ from flax import nnx
20
+ from jaxtyping import Array, Float, PyTree
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class NBGLMConfig(OperatorConfig):
27
+ """Configuration for DifferentiableNBGLM.
28
+
29
+ Attributes:
30
+ n_features: Number of features (genes).
31
+ n_covariates: Number of covariates in design matrix.
32
+ estimate_dispersion: Whether to estimate dispersion parameters.
33
+ """
34
+
35
+ n_features: int = 2000
36
+ n_covariates: int = 2
37
+ estimate_dispersion: bool = True
38
+
39
+
40
+ class DifferentiableNBGLM(OperatorModule):
41
+ """Differentiable Negative Binomial GLM for differential expression.
42
+
43
+ This operator implements a negative binomial GLM where:
44
+ - log(mu) = X @ beta (design matrix @ coefficients)
45
+ - P(count | mu, dispersion) = NB(count; mu, dispersion)
46
+
47
+ Gradients flow through both the coefficient (beta) and dispersion
48
+ parameters, enabling end-to-end learning.
49
+
50
+ The negative binomial distribution is parameterized as:
51
+ - mean = mu
52
+ - variance = mu + mu^2 / dispersion
53
+
54
+ Args:
55
+ config: NBGLMConfig with model parameters.
56
+ rngs: Flax NNX random number generators.
57
+ name: Optional operator name.
58
+
59
+ Example:
60
+ ```python
61
+ config = NBGLMConfig(n_features=2000, n_covariates=2)
62
+ glm = DifferentiableNBGLM(config, rngs=nnx.Rngs(42))
63
+ data = {"counts": counts, "design": design_row, "size_factor": sf}
64
+ result, state, meta = glm.apply(data, {}, None)
65
+ ```
66
+ """
67
+
68
+ def __init__(
69
+ self,
70
+ config: NBGLMConfig,
71
+ *,
72
+ rngs: nnx.Rngs | None = None,
73
+ name: str | None = None,
74
+ ):
75
+ """Initialize the NB GLM operator.
76
+
77
+ Args:
78
+ config: NB GLM configuration.
79
+ rngs: Random number generators for initialization.
80
+ name: Optional operator name.
81
+ """
82
+ super().__init__(config, rngs=rngs, name=name)
83
+
84
+ if rngs is None:
85
+ rngs = nnx.Rngs(0)
86
+
87
+ self.n_features = config.n_features
88
+ self.n_covariates = config.n_covariates
89
+
90
+ # Initialize coefficients (beta)
91
+ # Shape: (n_covariates, n_features)
92
+ key = rngs.params()
93
+ init_beta = jax.random.normal(key, (config.n_covariates, config.n_features)) * 0.1
94
+ self.beta = nnx.Param(init_beta)
95
+
96
+ # Initialize log dispersion parameters
97
+ # Shape: (n_features,)
98
+ # Start with dispersion = 1.0 (log_dispersion = 0)
99
+ self.log_dispersion = nnx.Param(jnp.zeros(config.n_features))
100
+
101
+ def get_coefficients(self) -> Float[Array, "n_covariates n_features"]:
102
+ """Get coefficient matrix.
103
+
104
+ Returns:
105
+ Coefficient matrix beta (n_covariates, n_features).
106
+ """
107
+ return self.beta[...]
108
+
109
+ def get_dispersion(self) -> Float[Array, "n_features"]:
110
+ """Get dispersion parameters.
111
+
112
+ Returns:
113
+ Dispersion parameters (n_features,), always positive.
114
+ """
115
+ # Use softplus to ensure positivity
116
+ return jax.nn.softplus(self.log_dispersion[...]) + 1e-4
117
+
118
+ def predict_mean(
119
+ self,
120
+ design: Float[Array, "n_covariates"],
121
+ size_factor: Float[Array, ""],
122
+ ) -> Float[Array, "n_features"]:
123
+ """Predict mean expression for a sample.
124
+
125
+ Args:
126
+ design: Design matrix row for this sample.
127
+ size_factor: Library size normalization factor.
128
+
129
+ Returns:
130
+ Predicted mean expression (n_features,).
131
+ """
132
+ beta = self.get_coefficients()
133
+
134
+ # log(mu) = design @ beta
135
+ # design is (n_covariates,), beta is (n_covariates, n_features)
136
+ log_mu = jnp.dot(design, beta)
137
+
138
+ # Add size factor effect
139
+ log_mu = log_mu + jnp.log(size_factor + 1e-8)
140
+
141
+ # Exponentiate to get mean
142
+ mu = jnp.exp(log_mu)
143
+
144
+ return mu
145
+
146
+ def negative_binomial_log_prob(
147
+ self,
148
+ counts: Float[Array, "n_features"],
149
+ design: Float[Array, "n_covariates"],
150
+ size_factor: Float[Array, ""],
151
+ ) -> Float[Array, ""]:
152
+ """Compute negative binomial log probability.
153
+
154
+ Uses the parameterization where:
155
+ - variance = mu + mu^2 / dispersion
156
+
157
+ Args:
158
+ counts: Observed counts.
159
+ design: Design matrix row.
160
+ size_factor: Size factor for normalization.
161
+
162
+ Returns:
163
+ Total log probability (scalar).
164
+ """
165
+ mu = self.predict_mean(design, size_factor)
166
+ dispersion = self.get_dispersion()
167
+
168
+ # Negative binomial log probability
169
+ # NB(k; mu, r) where r = dispersion
170
+ # log P(k) = log(Gamma(k + r)) - log(Gamma(k + 1)) - log(Gamma(r))
171
+ # + r * log(r / (r + mu)) + k * log(mu / (r + mu))
172
+
173
+ r = dispersion
174
+ k = counts
175
+
176
+ # Compute log probability using the NB PMF
177
+ # Use jax.scipy.special functions for numerical stability
178
+ log_prob = (
179
+ jax.scipy.special.gammaln(k + r)
180
+ - jax.scipy.special.gammaln(k + 1)
181
+ - jax.scipy.special.gammaln(r)
182
+ + r * jnp.log(r / (r + mu + 1e-8))
183
+ + k * jnp.log(mu / (r + mu + 1e-8) + 1e-8)
184
+ )
185
+
186
+ # Sum over features
187
+ total_log_prob = jnp.sum(log_prob)
188
+
189
+ return total_log_prob
190
+
191
+ def batch_log_likelihood(
192
+ self,
193
+ counts: Float[Array, "n_samples n_features"],
194
+ design: Float[Array, "n_samples n_covariates"],
195
+ size_factors: Float[Array, "n_samples"],
196
+ ) -> Float[Array, ""]:
197
+ """Compute log likelihood for a batch of samples.
198
+
199
+ Args:
200
+ counts: Count matrix (n_samples, n_features).
201
+ design: Design matrix (n_samples, n_covariates).
202
+ size_factors: Size factors (n_samples,).
203
+
204
+ Returns:
205
+ Total log likelihood.
206
+ """
207
+
208
+ def sample_log_prob(args):
209
+ c, d, s = args
210
+ return self.negative_binomial_log_prob(c, d, s)
211
+
212
+ log_probs = jax.vmap(sample_log_prob)((counts, design, size_factors))
213
+ return jnp.sum(log_probs)
214
+
215
+ def apply(
216
+ self,
217
+ data: PyTree,
218
+ state: PyTree,
219
+ metadata: dict[str, Any] | None,
220
+ random_params: Any = None,
221
+ stats: dict[str, Any] | None = None,
222
+ ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
223
+ """Apply NB GLM to count data.
224
+
225
+ This method computes the log likelihood and predicted mean
226
+ for a given sample.
227
+
228
+ Args:
229
+ data: Dictionary containing:
230
+ - "counts": Gene counts (n_features,)
231
+ - "design": Design matrix row (n_covariates,)
232
+ - "size_factor": Library size factor (scalar)
233
+ state: Element state (passed through unchanged)
234
+ metadata: Element metadata (passed through unchanged)
235
+ random_params: Not used (deterministic operator)
236
+ stats: Not used
237
+
238
+ Returns:
239
+ Tuple of (transformed_data, state, metadata):
240
+ - transformed_data contains:
241
+
242
+ - "counts": Original counts
243
+ - "log_likelihood": Log probability of counts
244
+ - "predicted_mean": Predicted expression
245
+ - "dispersion": Dispersion parameters
246
+ - state is passed through unchanged
247
+ - metadata is passed through unchanged
248
+ """
249
+ counts = data["counts"]
250
+ design = data["design"]
251
+ size_factor = data["size_factor"]
252
+
253
+ # Compute log likelihood
254
+ log_likelihood = self.negative_binomial_log_prob(counts, design, size_factor)
255
+
256
+ # Compute predicted mean
257
+ predicted_mean = self.predict_mean(design, size_factor)
258
+
259
+ # Get dispersion
260
+ dispersion = self.get_dispersion()
261
+
262
+ # Build output data
263
+ transformed_data = {
264
+ "counts": counts,
265
+ "design": design,
266
+ "size_factor": size_factor,
267
+ "log_likelihood": log_likelihood,
268
+ "predicted_mean": predicted_mean,
269
+ "dispersion": dispersion,
270
+ }
271
+
272
+ return transformed_data, state, metadata
@@ -0,0 +1,64 @@
1
+ """Variant calling operators for differentiable variant detection.
2
+
3
+ This module provides differentiable components for variant calling:
4
+ - DifferentiablePileup: Generates pileup from aligned reads
5
+ - DeepVariantStylePileup: DeepVariant-style multi-channel pileup images
6
+ - VariantClassifier: MLP-based variant classifier
7
+ - CellTypeAwareVariantClassifier: Cell-type-weighted variant classifier
8
+ - CNNVariantClassifier: CNN-based pileup image classifier (DeepVariant-style)
9
+ - SoftVariantQualityFilter: GMM-based quality filtering (VQSR-style)
10
+ - DifferentiableCNVSegmentation: Attention-based CNV detection
11
+ """
12
+
13
+ from diffbio.operators.variant.classifier import (
14
+ CellTypeAwareVariantClassifier,
15
+ CellTypeAwareVariantClassifierConfig,
16
+ VariantClassifier,
17
+ VariantClassifierConfig,
18
+ )
19
+ from diffbio.operators.variant.cnn_classifier import (
20
+ CNNVariantClassifier,
21
+ CNNVariantClassifierConfig,
22
+ )
23
+ from diffbio.operators.variant.cnv_segmentation import (
24
+ CNVSegmentationConfig,
25
+ DifferentiableCNVSegmentation,
26
+ EnhancedCNVSegmentation,
27
+ EnhancedCNVSegmentationConfig,
28
+ )
29
+ from diffbio.operators.variant.deepvariant_pileup import (
30
+ DeepVariantPileupConfig,
31
+ DeepVariantStylePileup,
32
+ )
33
+ from diffbio.operators.variant.pileup import DifferentiablePileup, PileupConfig
34
+ from diffbio.operators.variant.quality_recalibration import (
35
+ SoftVariantQualityFilter,
36
+ VariantQualityFilterConfig,
37
+ )
38
+
39
+ __all__ = [
40
+ # Pileup
41
+ "DifferentiablePileup",
42
+ "PileupConfig",
43
+ # DeepVariant-style Pileup
44
+ "DeepVariantStylePileup",
45
+ "DeepVariantPileupConfig",
46
+ # MLP Classifier
47
+ "VariantClassifier",
48
+ "VariantClassifierConfig",
49
+ # Cell-Type-Aware Classifier
50
+ "CellTypeAwareVariantClassifier",
51
+ "CellTypeAwareVariantClassifierConfig",
52
+ # CNN Classifier
53
+ "CNNVariantClassifier",
54
+ "CNNVariantClassifierConfig",
55
+ # Quality Filter
56
+ "SoftVariantQualityFilter",
57
+ "VariantQualityFilterConfig",
58
+ # CNV Segmentation
59
+ "CNVSegmentationConfig",
60
+ "DifferentiableCNVSegmentation",
61
+ # Enhanced CNV Segmentation
62
+ "EnhancedCNVSegmentation",
63
+ "EnhancedCNVSegmentationConfig",
64
+ ]