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,269 @@
1
+ """Differentiable CRISPR Guide Scoring Operator.
2
+
3
+ This module implements a DeepCRISPR-inspired differentiable guide RNA scoring
4
+ operator using a CNN architecture to predict on-target efficiency.
5
+
6
+ The architecture is inspired by DeepCRISPR which uses:
7
+ 1. A deep convolutional denoising neural network (DCDNN) autoencoder
8
+ for unsupervised representation learning
9
+ 2. A CNN classifier for efficiency prediction
10
+
11
+ This implementation provides a simplified but differentiable version that:
12
+ - Uses 1D convolutions over the one-hot encoded sequence
13
+ - Supports optional epigenetic feature channels
14
+ - Outputs efficiency scores in [0, 1]
15
+
16
+ For SpCas9, the standard input is 20nt guide + 3nt PAM = 23nt context.
17
+
18
+ References:
19
+ Chuai et al. (2018). "DeepCRISPR: Optimized CRISPR guide RNA design
20
+ by deep learning." Genome Biology.
21
+ https://github.com/bm2-lab/DeepCRISPR
22
+
23
+ Liu et al. (2021). "Enhancing CRISPR-Cas9 gRNA efficiency prediction
24
+ by data integration and deep learning." Nature Communications.
25
+ """
26
+
27
+ import logging
28
+ from dataclasses import dataclass
29
+ from typing import Any
30
+
31
+ import jax.numpy as jnp
32
+ from artifex.generative_models.core.base import MLP
33
+ from datarax.core.config import OperatorConfig
34
+ from datarax.core.operator import OperatorModule
35
+ from flax import nnx
36
+ from diffbio.utils.nn_utils import ARTIFEX_RELU_MLP_KWARGS
37
+
38
+ logger = logging.getLogger(__name__)
39
+
40
+
41
+ @dataclass(frozen=True)
42
+ class CRISPRScorerConfig(OperatorConfig):
43
+ """Configuration for DifferentiableCRISPRScorer.
44
+
45
+ Attributes:
46
+ guide_length: Length of guide RNA sequence (typically 20-23 nt).
47
+ alphabet_size: Size of nucleotide alphabet (4 for A/C/G/T).
48
+ hidden_channels: CNN hidden channel dimensions.
49
+ fc_dims: Fully connected layer dimensions.
50
+ dropout_rate: Dropout rate for regularization.
51
+ """
52
+
53
+ guide_length: int = 23
54
+ alphabet_size: int = 4
55
+ hidden_channels: tuple[int, ...] = (64, 128, 256)
56
+ fc_dims: tuple[int, ...] = (256, 128)
57
+ dropout_rate: float = 0.2
58
+
59
+ def __post_init__(self) -> None:
60
+ """Validate configuration."""
61
+ super().__post_init__()
62
+ if not self.hidden_channels:
63
+ raise ValueError(
64
+ "CRISPRScorerConfig.hidden_channels must contain at least one channel."
65
+ )
66
+ if not self.fc_dims:
67
+ raise ValueError("CRISPRScorerConfig.fc_dims must contain at least one hidden layer.")
68
+
69
+
70
+ class DifferentiableCRISPRScorer(OperatorModule):
71
+ """DeepCRISPR-style differentiable guide RNA scoring.
72
+
73
+ This operator uses a 1D CNN architecture to predict CRISPR guide RNA
74
+ on-target efficiency from sequence features. The model learns sequence
75
+ patterns that correlate with efficient target cleavage.
76
+
77
+ The architecture consists of:
78
+ 1. 1D convolutional layers for sequence feature extraction
79
+ 2. Batch normalization and ReLU activations
80
+ 3. Fully connected layers for efficiency prediction
81
+ 4. Sigmoid output for efficiency score in [0, 1]
82
+
83
+ Attributes:
84
+ config: Operator configuration.
85
+ conv_layers: 1D convolutional layers.
86
+ conv_bn: Batch normalization layers for conv.
87
+ ffn_backbone: Shared Artifex MLP for score prediction.
88
+ output_head: Final output layer.
89
+
90
+ Example:
91
+ ```python
92
+ from diffbio.operators.crispr import (
93
+ DifferentiableCRISPRScorer,
94
+ CRISPRScorerConfig,
95
+ )
96
+ config = CRISPRScorerConfig(guide_length=23)
97
+ scorer = DifferentiableCRISPRScorer(config, rngs=nnx.Rngs(42))
98
+ data = {"guides": guide_sequences} # (n_guides, length, 4)
99
+ result, _, _ = scorer.apply(data, {}, None)
100
+ scores = result["efficiency_scores"] # (n_guides,)
101
+ ```
102
+ """
103
+
104
+ def __init__(
105
+ self,
106
+ config: CRISPRScorerConfig,
107
+ *,
108
+ rngs: nnx.Rngs,
109
+ ) -> None:
110
+ """Initialize the CRISPR scorer.
111
+
112
+ Args:
113
+ config: Operator configuration.
114
+ rngs: Flax NNX random number generators.
115
+ """
116
+ super().__init__(config, rngs=rngs)
117
+
118
+ channel_pairs = zip((config.alphabet_size, *config.hidden_channels), config.hidden_channels)
119
+ self.conv_layers = nnx.List(
120
+ [
121
+ nnx.Conv(
122
+ in_features=in_channels,
123
+ out_features=out_channels,
124
+ kernel_size=(3,),
125
+ padding="SAME",
126
+ rngs=rngs,
127
+ )
128
+ for in_channels, out_channels in channel_pairs
129
+ ]
130
+ )
131
+ self.conv_bn = nnx.List(
132
+ [nnx.BatchNorm(out_channels, rngs=rngs) for out_channels in config.hidden_channels]
133
+ )
134
+
135
+ # Calculate flattened size after convolutions
136
+ # With SAME padding, spatial size is preserved
137
+ flat_size = config.guide_length * config.hidden_channels[-1]
138
+
139
+ self.ffn_backbone = MLP(
140
+ hidden_dims=list(config.fc_dims),
141
+ in_features=flat_size,
142
+ dropout_rate=config.dropout_rate,
143
+ rngs=rngs,
144
+ **ARTIFEX_RELU_MLP_KWARGS,
145
+ )
146
+
147
+ # Output head for efficiency score
148
+ self.output_head = nnx.Linear(config.fc_dims[-1], 1, rngs=rngs)
149
+
150
+ def extract_features(self, guides: jnp.ndarray) -> jnp.ndarray:
151
+ """Extract features from guide sequences using CNN.
152
+
153
+ Args:
154
+ guides: One-hot encoded guides (n_guides, guide_length, 4).
155
+
156
+ Returns:
157
+ Feature vectors (n_guides, feature_dim).
158
+ """
159
+ # Input shape: (batch, length, channels)
160
+ x = guides
161
+
162
+ # Apply 1D convolutions with batch norm and ReLU
163
+ for conv, bn in zip(self.conv_layers, self.conv_bn):
164
+ x = conv(x)
165
+ x = bn(x)
166
+ x = nnx.relu(x)
167
+
168
+ # Flatten: (batch, length, channels) -> (batch, length * channels)
169
+ batch_size = x.shape[0]
170
+ x = x.reshape(batch_size, -1)
171
+
172
+ return x
173
+
174
+ def predict_efficiency(self, features: jnp.ndarray) -> jnp.ndarray:
175
+ """Predict efficiency score from features.
176
+
177
+ Args:
178
+ features: Feature vectors (n_guides, feature_dim).
179
+
180
+ Returns:
181
+ Efficiency scores (n_guides,) in range [0, 1].
182
+ """
183
+ backbone_output = self.ffn_backbone(features)
184
+ if isinstance(backbone_output, tuple):
185
+ raise TypeError("CRISPR scorer backbone must return a single tensor output.")
186
+
187
+ # Output layer with sigmoid for [0, 1] output
188
+ x = self.output_head(backbone_output)
189
+ scores = nnx.sigmoid(x).squeeze(-1)
190
+
191
+ return scores
192
+
193
+ def apply(
194
+ self,
195
+ data: dict[str, Any],
196
+ state: dict[str, Any],
197
+ metadata: dict[str, Any] | None,
198
+ random_params: Any = None,
199
+ stats: dict[str, Any] | None = None,
200
+ ) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any] | None]:
201
+ """Apply CRISPR scoring to guide sequences.
202
+
203
+ Args:
204
+ data: Dictionary containing:
205
+ - "guides": One-hot encoded guides (n_guides, guide_length, 4).
206
+ state: Per-element state (passed through).
207
+ metadata: Optional metadata (passed through).
208
+ random_params: Random parameters for stochastic operations.
209
+ stats: Optional statistics dictionary.
210
+
211
+ Returns:
212
+ Tuple of (transformed_data, state, metadata) where transformed_data
213
+ contains:
214
+
215
+ - "guides": Original guide sequences.
216
+ - "efficiency_scores": Predicted efficiency (n_guides,).
217
+ - "features": Extracted feature vectors.
218
+ """
219
+ guides = data["guides"]
220
+
221
+ # Extract features using CNN
222
+ features = self.extract_features(guides)
223
+
224
+ # Predict efficiency scores
225
+ efficiency_scores = self.predict_efficiency(features)
226
+
227
+ # Build output
228
+ output = {
229
+ **data,
230
+ "efficiency_scores": efficiency_scores,
231
+ "features": features,
232
+ }
233
+
234
+ return output, state, metadata
235
+
236
+
237
+ def create_crispr_scorer(
238
+ guide_length: int = 23,
239
+ hidden_channels: tuple[int, ...] = (64, 128, 256),
240
+ fc_dims: tuple[int, ...] = (256, 128),
241
+ dropout_rate: float = 0.2,
242
+ seed: int = 42,
243
+ ) -> DifferentiableCRISPRScorer:
244
+ """Factory function to create a CRISPR scorer.
245
+
246
+ Args:
247
+ guide_length: Length of guide RNA sequence.
248
+ hidden_channels: CNN hidden channel dimensions.
249
+ fc_dims: Fully connected layer dimensions.
250
+ dropout_rate: Dropout rate for regularization.
251
+ seed: Random seed for initialization.
252
+
253
+ Returns:
254
+ Configured DifferentiableCRISPRScorer instance.
255
+
256
+ Example:
257
+ ```python
258
+ scorer = create_crispr_scorer(guide_length=23)
259
+ result, _, _ = scorer.apply({"guides": data}, {}, None)
260
+ ```
261
+ """
262
+ config = CRISPRScorerConfig(
263
+ guide_length=guide_length,
264
+ hidden_channels=hidden_channels,
265
+ fc_dims=fc_dims,
266
+ dropout_rate=dropout_rate,
267
+ )
268
+
269
+ return DifferentiableCRISPRScorer(config, rngs=nnx.Rngs(seed))
@@ -0,0 +1,133 @@
1
+ """Drug discovery operators for differentiable chemoinformatics.
2
+
3
+ This module provides differentiable operators for molecular property prediction,
4
+ fingerprint computation, and similarity scoring.
5
+
6
+ Operators:
7
+ MolecularPropertyPredictor: ChemProp-style MPNN for property prediction
8
+ DifferentiableMolecularFingerprint: Neural graph fingerprints
9
+ CircularFingerprintOperator: Differentiable ECFP/Morgan fingerprints
10
+ MACCSKeysOperator: Differentiable MACCS 166 structural keys
11
+ AttentiveFP: Attention-based molecular fingerprint (Xiong et al. 2019)
12
+ ADMETPredictor: Multi-task ADMET property prediction
13
+ MolecularSimilarityOperator: Differentiable Tanimoto/cosine similarity
14
+ DifferentiableDTIPipeline: Integrated differentiable DTI scorer
15
+
16
+ Utilities:
17
+ smiles_to_graph: Convert SMILES to molecular graph
18
+ batch_smiles_to_graphs: Batch conversion with padding
19
+ """
20
+
21
+ from diffbio.operators.drug_discovery.admet_predictor import (
22
+ ADMET_TASK_NAMES,
23
+ ADMET_TASK_TYPES,
24
+ ADMETConfig,
25
+ ADMETPredictor,
26
+ create_admet_predictor,
27
+ )
28
+ from diffbio.operators.drug_discovery.attentive_fp import (
29
+ AttentiveFP,
30
+ AttentiveFPConfig,
31
+ create_attentive_fp,
32
+ )
33
+ from diffbio.operators.drug_discovery.dti import (
34
+ DTI_PIPELINE_INTEGRATION_LAYER,
35
+ PROTEIN_ONE_HOT_PREPROCESSING_VERSION,
36
+ DTIPipelineConfig,
37
+ DifferentiableDTIPipeline,
38
+ build_dti_pipeline_inputs,
39
+ encode_protein_sequences,
40
+ )
41
+ from diffbio.operators.drug_discovery.fingerprint import (
42
+ CircularFingerprintConfig,
43
+ CircularFingerprintOperator,
44
+ DifferentiableMolecularFingerprint,
45
+ MolecularFingerprintConfig,
46
+ create_ecfp4_operator,
47
+ create_ecfp6_operator,
48
+ create_fcfp4_operator,
49
+ create_fingerprint_operator,
50
+ )
51
+ from diffbio.operators.drug_discovery.maccs_keys import (
52
+ MACCSKeysConfig,
53
+ MACCSKeysOperator,
54
+ create_maccs_operator,
55
+ )
56
+ from diffbio.operators.drug_discovery.message_passing import (
57
+ MessagePassingLayer,
58
+ StackedMessagePassing,
59
+ )
60
+ from diffbio.operators.drug_discovery.primitives import (
61
+ AtomFeatureConfig,
62
+ DEFAULT_ATOM_CONFIG,
63
+ DEFAULT_ATOM_FEATURES,
64
+ batch_smiles_to_graphs,
65
+ smiles_to_graph,
66
+ )
67
+ from diffbio.operators.drug_discovery.property_predictor import (
68
+ MolecularPropertyConfig,
69
+ MolecularPropertyPredictor,
70
+ create_property_predictor,
71
+ )
72
+ from diffbio.operators.drug_discovery.similarity import (
73
+ MolecularSimilarityConfig,
74
+ MolecularSimilarityOperator,
75
+ cosine_similarity,
76
+ create_similarity_operator,
77
+ dice_similarity,
78
+ tanimoto_similarity,
79
+ )
80
+
81
+ __all__ = [
82
+ # Primitives
83
+ "smiles_to_graph",
84
+ "batch_smiles_to_graphs",
85
+ "DEFAULT_ATOM_FEATURES",
86
+ "DEFAULT_ATOM_CONFIG",
87
+ "AtomFeatureConfig",
88
+ # Message Passing
89
+ "MessagePassingLayer",
90
+ "StackedMessagePassing",
91
+ # Property Prediction
92
+ "MolecularPropertyConfig",
93
+ "MolecularPropertyPredictor",
94
+ "create_property_predictor",
95
+ # ADMET Prediction
96
+ "ADMETConfig",
97
+ "ADMETPredictor",
98
+ "create_admet_predictor",
99
+ "ADMET_TASK_NAMES",
100
+ "ADMET_TASK_TYPES",
101
+ # DTI Pipeline
102
+ "DTI_PIPELINE_INTEGRATION_LAYER",
103
+ "PROTEIN_ONE_HOT_PREPROCESSING_VERSION",
104
+ "DTIPipelineConfig",
105
+ "DifferentiableDTIPipeline",
106
+ "build_dti_pipeline_inputs",
107
+ "encode_protein_sequences",
108
+ # Neural Fingerprints
109
+ "MolecularFingerprintConfig",
110
+ "DifferentiableMolecularFingerprint",
111
+ "create_fingerprint_operator",
112
+ # Circular Fingerprints (ECFP/Morgan)
113
+ "CircularFingerprintConfig",
114
+ "CircularFingerprintOperator",
115
+ "create_ecfp4_operator",
116
+ "create_ecfp6_operator",
117
+ "create_fcfp4_operator",
118
+ # MACCS Keys
119
+ "MACCSKeysConfig",
120
+ "MACCSKeysOperator",
121
+ "create_maccs_operator",
122
+ # AttentiveFP
123
+ "AttentiveFPConfig",
124
+ "AttentiveFP",
125
+ "create_attentive_fp",
126
+ # Similarity
127
+ "MolecularSimilarityConfig",
128
+ "MolecularSimilarityOperator",
129
+ "create_similarity_operator",
130
+ "tanimoto_similarity",
131
+ "cosine_similarity",
132
+ "dice_similarity",
133
+ ]
@@ -0,0 +1,142 @@
1
+ """Shared helpers for graph-based drug-discovery operators."""
2
+
3
+ import logging
4
+ from typing import Any, Mapping
5
+
6
+ import jax.numpy as jnp
7
+ from flax import nnx
8
+
9
+ from diffbio.operators.drug_discovery.message_passing import StackedMessagePassing
10
+ from diffbio.utils.nn_utils import ensure_rngs
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def stabilize_operator_id(module: Any) -> None:
16
+ """Mark operator unique ID as static for NNX/JAX transformations."""
17
+ module._unique_id = nnx.static(module._unique_id)
18
+
19
+
20
+ def build_encoder(
21
+ *,
22
+ hidden_dim: int,
23
+ num_layers: int,
24
+ in_features: int,
25
+ rngs: nnx.Rngs,
26
+ num_edge_features: int | None = None,
27
+ ) -> StackedMessagePassing:
28
+ """Create a message-passing encoder with optional edge features."""
29
+ encoder_kwargs: dict[str, Any] = {
30
+ "hidden_dim": hidden_dim,
31
+ "num_layers": num_layers,
32
+ "in_features": in_features,
33
+ "rngs": rngs,
34
+ }
35
+ if num_edge_features is not None:
36
+ encoder_kwargs["num_edge_features"] = num_edge_features
37
+ return StackedMessagePassing(**encoder_kwargs)
38
+
39
+
40
+ def _require_config_attr(config: Any, attr: str) -> Any:
41
+ """Read a required config attribute with a clear error message."""
42
+ if not hasattr(config, attr):
43
+ raise AttributeError(
44
+ f"{type(config).__name__} must define '{attr}' for graph encoder initialization."
45
+ )
46
+ return getattr(config, attr)
47
+
48
+
49
+ def initialize_graph_encoder(
50
+ module: Any,
51
+ *,
52
+ rngs: nnx.Rngs | None,
53
+ hidden_dim: int,
54
+ num_layers: int,
55
+ in_features: int,
56
+ num_edge_features: int | None = None,
57
+ attr: str = "encoder",
58
+ ) -> nnx.Rngs:
59
+ """Stabilize ID, ensure RNGs, and attach a message-passing encoder."""
60
+ stabilize_operator_id(module)
61
+ resolved_rngs = ensure_rngs(rngs)
62
+ setattr(
63
+ module,
64
+ attr,
65
+ build_encoder(
66
+ hidden_dim=hidden_dim,
67
+ num_layers=num_layers,
68
+ in_features=in_features,
69
+ rngs=resolved_rngs,
70
+ num_edge_features=num_edge_features,
71
+ ),
72
+ )
73
+ return resolved_rngs
74
+
75
+
76
+ def initialize_graph_encoder_from_config(
77
+ module: Any,
78
+ config: Any,
79
+ *,
80
+ rngs: nnx.Rngs | None,
81
+ num_layers_attr: str = "num_message_passing_steps",
82
+ hidden_dim_attr: str = "hidden_dim",
83
+ in_features_attr: str = "in_features",
84
+ num_edge_features_attr: str = "num_edge_features",
85
+ attr: str = "encoder",
86
+ ) -> nnx.Rngs:
87
+ """Initialize a standard graph encoder from a config object."""
88
+ hidden_dim = _require_config_attr(config, hidden_dim_attr)
89
+ num_layers = _require_config_attr(config, num_layers_attr)
90
+ in_features = _require_config_attr(config, in_features_attr)
91
+ num_edge_features = getattr(config, num_edge_features_attr, None)
92
+
93
+ return initialize_graph_encoder(
94
+ module,
95
+ rngs=rngs,
96
+ hidden_dim=hidden_dim,
97
+ num_layers=num_layers,
98
+ in_features=in_features,
99
+ num_edge_features=num_edge_features,
100
+ attr=attr,
101
+ )
102
+
103
+
104
+ def build_optional_dropout(rate: float, *, rngs: nnx.Rngs) -> nnx.Dropout | None:
105
+ """Create a dropout module only when the configured rate is positive."""
106
+ if rate <= 0:
107
+ return None
108
+ return nnx.Dropout(rate=rate, rngs=rngs)
109
+
110
+
111
+ def unpack_graph_inputs(data: Mapping[str, Any]) -> tuple[Any, Any, Any, Any]:
112
+ """Extract standard molecular graph tensors from an input dictionary."""
113
+ return (
114
+ data["node_features"],
115
+ data["adjacency"],
116
+ data.get("edge_features"),
117
+ data.get("node_mask"),
118
+ )
119
+
120
+
121
+ def graph_sum_readout(
122
+ data: Mapping[str, Any],
123
+ encoder: StackedMessagePassing,
124
+ *,
125
+ dropout: nnx.Dropout | None = None,
126
+ ) -> jnp.ndarray:
127
+ """Encode a graph and sum-pool node states into a graph representation."""
128
+ node_features, adjacency, edge_features, node_mask = unpack_graph_inputs(data)
129
+
130
+ node_hidden = encoder(node_features, adjacency, edge_features)
131
+ if node_mask is not None:
132
+ node_hidden = node_hidden * node_mask[:, None]
133
+
134
+ graph_repr = jnp.sum(node_hidden, axis=0)
135
+ if dropout is not None:
136
+ graph_repr = dropout(graph_repr)
137
+ return graph_repr
138
+
139
+
140
+ def attach_fingerprint(data: Mapping[str, Any], fingerprint: Any) -> dict[str, Any]:
141
+ """Return a shallow copy with a standardized fingerprint output field."""
142
+ return {**data, "fingerprint": fingerprint}