diffbio 0.1.3__tar.gz → 0.1.5__tar.gz

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 (230) hide show
  1. {diffbio-0.1.3 → diffbio-0.1.5}/PKG-INFO +8 -3
  2. {diffbio-0.1.3 → diffbio-0.1.5}/benchmarks/README.md +4 -4
  3. {diffbio-0.1.3 → diffbio-0.1.5}/pyproject.toml +10 -6
  4. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/base_operators.py +12 -6
  5. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/data_types.py +3 -2
  6. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/__init__.py +4 -0
  7. diffbio-0.1.5/src/diffbio/core/soft_ops/normalization.py +174 -0
  8. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/uncertainty.py +4 -4
  9. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/evaluation/adapters.py +7 -7
  10. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/_masked_gene_transformer.py +16 -20
  11. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/alignment/profile_hmm.py +2 -3
  12. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/alignment/smith_waterman.py +2 -3
  13. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/alignment/soft_msa.py +2 -2
  14. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/assembly/gnn_assembly.py +3 -2
  15. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/assembly/metagenomic_binning.py +5 -3
  16. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/crispr/guide_scoring.py +3 -2
  17. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/drug_discovery/_graph_utils.py +1 -7
  18. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/drug_discovery/admet_predictor.py +4 -3
  19. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/drug_discovery/attentive_fp.py +3 -6
  20. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/drug_discovery/dti.py +3 -2
  21. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/drug_discovery/fingerprint.py +5 -6
  22. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/drug_discovery/maccs_keys.py +4 -5
  23. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/drug_discovery/property_predictor.py +3 -2
  24. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/drug_discovery/similarity.py +3 -6
  25. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/epigenomics/chromatin_state.py +3 -3
  26. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/epigenomics/contextual.py +2 -2
  27. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/epigenomics/fno_peak_calling.py +3 -2
  28. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/epigenomics/peak_calling.py +6 -6
  29. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/foundation_models/embedding_probe.py +2 -2
  30. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/foundation_models/foundation_model.py +6 -6
  31. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/foundation_models/transformer_encoder.py +3 -3
  32. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/mapping/neural_mapper.py +2 -2
  33. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/metabolomics/isotope_envelope.py +3 -3
  34. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/metabolomics/soft_centroiding.py +3 -3
  35. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/metabolomics/spectral_similarity.py +3 -2
  36. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/molecular_dynamics/force_field.py +2 -2
  37. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/molecular_dynamics/integrator.py +2 -2
  38. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/multiomics/hic_contact.py +2 -2
  39. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/multiomics/multiomics_vae.py +4 -3
  40. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/multiomics/spatial_deconvolution.py +2 -2
  41. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/multiomics/spatial_gene_detection.py +1 -1
  42. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/arcsinh_cofactor.py +3 -3
  43. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/differentiable_pca.py +3 -3
  44. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/embedding.py +2 -2
  45. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/learnable_normalization.py +3 -3
  46. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/learnable_orthogonal_projection.py +4 -3
  47. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/learnable_projection.py +3 -3
  48. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/matrix_free_pca.py +3 -3
  49. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/phate.py +3 -3
  50. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/scaling.py +4 -3
  51. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/soft_pca.py +3 -3
  52. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/umap.py +3 -3
  53. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/vae_normalizer.py +4 -5
  54. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/population/ancestry_estimation.py +3 -2
  55. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/preprocessing/__init__.py +10 -5
  56. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/preprocessing/adapter_removal.py +2 -2
  57. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/preprocessing/duplicate_filter.py +2 -2
  58. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/preprocessing/error_correction.py +2 -2
  59. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/protein/secondary_structure.py +3 -2
  60. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/quality_filter.py +3 -2
  61. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/rna_structure/rna_folding.py +3 -4
  62. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/rnaseq/motif_discovery.py +3 -3
  63. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/rnaseq/splicing_psi.py +3 -3
  64. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/ambient_removal.py +4 -4
  65. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/archetypes.py +2 -2
  66. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/batch_correction.py +2 -2
  67. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/cell_annotation.py +2 -2
  68. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/communication.py +4 -4
  69. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/differential_distribution.py +2 -2
  70. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/doublet_detection.py +14 -53
  71. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/downsampling.py +6 -11
  72. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/enhanced_batch_correction.py +4 -4
  73. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/grn_inference.py +3 -2
  74. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/imputation.py +8 -8
  75. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/ot_trajectory.py +3 -2
  76. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/simulation.py +14 -34
  77. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/sindy_grn.py +2 -2
  78. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/soft_clustering.py +2 -2
  79. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/soft_hvg.py +3 -3
  80. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/spatial_domains.py +4 -4
  81. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/stochastic_gate_selector.py +6 -9
  82. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/switch_de.py +2 -2
  83. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/trajectory.py +5 -4
  84. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/velocity.py +2 -2
  85. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/statistical/em_quantification.py +2 -2
  86. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/statistical/hmm.py +2 -2
  87. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/statistical/nb_glm.py +2 -2
  88. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/variant/classifier.py +4 -4
  89. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/variant/cnn_classifier.py +2 -2
  90. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/variant/cnv_segmentation.py +4 -4
  91. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/variant/deepvariant_pileup.py +3 -3
  92. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/variant/pileup.py +2 -2
  93. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/variant/quality_recalibration.py +2 -2
  94. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/pipelines/adapters.py +4 -3
  95. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/pipelines/differential_expression.py +3 -3
  96. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/pipelines/enhanced_variant_calling.py +13 -7
  97. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/pipelines/joint_preprocessing.py +4 -3
  98. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/pipelines/preprocessing.py +2 -2
  99. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/pipelines/single_cell.py +19 -11
  100. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/pipelines/variant_calling.py +2 -2
  101. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/anndata_source.py +1 -1
  102. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/archive_ii.py +1 -1
  103. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/balifam.py +1 -1
  104. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/bengrn_ground_truth.py +2 -2
  105. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/encode_peaks.py +1 -1
  106. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/immune_human.py +1 -1
  107. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/pancreas.py +1 -1
  108. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/seqfish.py +1 -1
  109. {diffbio-0.1.3 → diffbio-0.1.5}/.gitignore +0 -0
  110. {diffbio-0.1.3 → diffbio-0.1.5}/LICENSE +0 -0
  111. {diffbio-0.1.3 → diffbio-0.1.5}/README.md +0 -0
  112. {diffbio-0.1.3 → diffbio-0.1.5}/benchmarks/crossmodality/README.md +0 -0
  113. {diffbio-0.1.3 → diffbio-0.1.5}/examples/README.md +0 -0
  114. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/__init__.py +0 -0
  115. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/configs.py +0 -0
  116. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/constants.py +0 -0
  117. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/__init__.py +0 -0
  118. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/gnn_components.py +0 -0
  119. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/graph_utils.py +0 -0
  120. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/neural_components.py +0 -0
  121. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/optimal_transport.py +0 -0
  122. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/_projections_permutahedron.py +0 -0
  123. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/_projections_simplex.py +0 -0
  124. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/_projections_transport.py +0 -0
  125. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/_sorting_network.py +0 -0
  126. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/_types.py +0 -0
  127. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/_utils.py +0 -0
  128. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/autograd_safe.py +0 -0
  129. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/comparison.py +0 -0
  130. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/elementwise.py +0 -0
  131. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/logical.py +0 -0
  132. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/quantile.py +0 -0
  133. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/selection.py +0 -0
  134. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/sorting.py +0 -0
  135. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/core/soft_ops/straight_through.py +0 -0
  136. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/evaluation/__init__.py +0 -0
  137. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/evaluation/graders.py +0 -0
  138. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/evaluation/problem.py +0 -0
  139. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/evaluation/runner.py +0 -0
  140. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/losses/__init__.py +0 -0
  141. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/losses/alignment_losses.py +0 -0
  142. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/losses/biological_regularization.py +0 -0
  143. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/losses/metric_losses.py +0 -0
  144. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/losses/self_supervised_losses.py +0 -0
  145. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/losses/singlecell_losses.py +0 -0
  146. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/losses/statistical_losses.py +0 -0
  147. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/__init__.py +0 -0
  148. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/_count_vae.py +0 -0
  149. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/_loss_balancing.py +0 -0
  150. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/_transformer_validation.py +0 -0
  151. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/alignment/__init__.py +0 -0
  152. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/alignment/scoring.py +0 -0
  153. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/assembly/__init__.py +0 -0
  154. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/crispr/__init__.py +0 -0
  155. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/drug_discovery/__init__.py +0 -0
  156. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/drug_discovery/message_passing.py +0 -0
  157. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/drug_discovery/primitives.py +0 -0
  158. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/epigenomics/__init__.py +0 -0
  159. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/foundation_models/__init__.py +0 -0
  160. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/foundation_models/adapters.py +0 -0
  161. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/foundation_models/contracts.py +0 -0
  162. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/foundation_models/experimental.py +0 -0
  163. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/foundation_models/frozen.py +0 -0
  164. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/foundation_models/precomputed.py +0 -0
  165. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/mapping/__init__.py +0 -0
  166. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/metabolomics/__init__.py +0 -0
  167. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/molecular_dynamics/__init__.py +0 -0
  168. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/molecular_dynamics/primitives.py +0 -0
  169. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/multiomics/__init__.py +0 -0
  170. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/normalization/__init__.py +0 -0
  171. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/population/__init__.py +0 -0
  172. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/protein/__init__.py +0 -0
  173. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/rna_structure/__init__.py +0 -0
  174. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/rnaseq/__init__.py +0 -0
  175. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/__init__.py +0 -0
  176. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/singlecell/knockdown_filter.py +0 -0
  177. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/statistical/__init__.py +0 -0
  178. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/variant/__init__.py +0 -0
  179. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/operators/variant/learnable_pileup.py +0 -0
  180. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/pipelines/__init__.py +0 -0
  181. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/pipelines/joint_training.py +0 -0
  182. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/pipelines/minibatch_training.py +0 -0
  183. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/pipelines/perturbation.py +0 -0
  184. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/reductions/__init__.py +0 -0
  185. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/reductions/base.py +0 -0
  186. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/reductions/pca_reduction.py +0 -0
  187. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/reductions/tfidf_reduction.py +0 -0
  188. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/samplers/__init__.py +0 -0
  189. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/samplers/perturbation_sampler.py +0 -0
  190. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sequences/__init__.py +0 -0
  191. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sequences/dna.py +0 -0
  192. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sequences/kmer.py +0 -0
  193. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/__init__.py +0 -0
  194. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/_anndata_shared.py +0 -0
  195. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/_batch_iteration.py +0 -0
  196. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/_benchmark_source.py +0 -0
  197. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/_indexed_batch_source.py +0 -0
  198. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/_utils.py +0 -0
  199. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/anndata_interop.py +0 -0
  200. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/bam.py +0 -0
  201. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/contextual_epigenomics.py +0 -0
  202. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/dti.py +0 -0
  203. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/embeddings.py +0 -0
  204. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/fasta.py +0 -0
  205. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/indexed_embeddings.py +0 -0
  206. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/indexed_view.py +0 -0
  207. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/molnet.py +0 -0
  208. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/multiomics.py +0 -0
  209. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/perturbation/__init__.py +0 -0
  210. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/perturbation/_types.py +0 -0
  211. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/perturbation/_utils.py +0 -0
  212. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/perturbation/concat_source.py +0 -0
  213. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/perturbation/control_mapping.py +0 -0
  214. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/perturbation/experiment_config.py +0 -0
  215. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/perturbation/h5_metadata_cache.py +0 -0
  216. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/perturbation/output_space.py +0 -0
  217. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/perturbation/perturbation_source.py +0 -0
  218. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/sequence_foundation.py +0 -0
  219. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/sources/singlecell_foundation.py +0 -0
  220. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/splitters/__init__.py +0 -0
  221. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/splitters/base.py +0 -0
  222. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/splitters/molecular.py +0 -0
  223. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/splitters/perturbation.py +0 -0
  224. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/splitters/random.py +0 -0
  225. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/splitters/sequence.py +0 -0
  226. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/utils/__init__.py +0 -0
  227. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/utils/dependency_runtime.py +0 -0
  228. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/utils/nn_utils.py +0 -0
  229. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/utils/quality.py +0 -0
  230. {diffbio-0.1.3 → diffbio-0.1.5}/src/diffbio/utils/training.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: diffbio
3
- Version: 0.1.3
4
- Summary: DiffBio: End-to-end differentiable bioinformatics pipelines built on Datarax, Artifex, Opifex, and Calibrax
3
+ Version: 0.1.5
4
+ Summary: End-to-end differentiable bioinformatics for JAX/Flax NNX: alignment, mapping, assembly, variant calling, RNA-seq, single-cell, epigenomics, CRISPR, metabolomics, multi-omics, protein and RNA structure, molecular dynamics and drug-discovery operators composed into trainable pipelines on datarax, artifex, opifex and calibrax
5
5
  Project-URL: Bug Tracker, https://github.com/avitai/DiffBio/issues
6
6
  Project-URL: Documentation, https://diffbio.readthedocs.io
7
7
  Project-URL: Source, https://github.com/avitai/DiffBio
@@ -50,7 +50,7 @@ Requires-Dist: beartype>=0.14.1
50
50
  Requires-Dist: biopython>=1.81
51
51
  Requires-Dist: calibrax>=0.1.5
52
52
  Requires-Dist: chex>=0.1.7
53
- Requires-Dist: datarax>=0.1.7
53
+ Requires-Dist: datarax>=0.1.10
54
54
  Requires-Dist: flax>=0.12.9
55
55
  Requires-Dist: h5py>=3.7
56
56
  Requires-Dist: jax-md>=0.2.27
@@ -74,6 +74,7 @@ Requires-Dist: griffe>=1.7.3; extra == 'all'
74
74
  Requires-Dist: import-linter>=2.5; extra == 'all'
75
75
  Requires-Dist: interrogate>=1.7.0; extra == 'all'
76
76
  Requires-Dist: ipykernel>=6.29.5; extra == 'all'
77
+ Requires-Dist: jax-metal>=0.1.0; (sys_platform == 'darwin' and platform_machine == 'arm64') and extra == 'all'
77
78
  Requires-Dist: jax[cuda12]>=0.11.1; extra == 'all'
78
79
  Requires-Dist: lineax>=0.0.8; extra == 'all'
79
80
  Requires-Dist: matplotlib>=3.7; extra == 'all'
@@ -101,6 +102,7 @@ Requires-Dist: pytest-timeout>=2.1; extra == 'all'
101
102
  Requires-Dist: pytest-xdist>=3.6; extra == 'all'
102
103
  Requires-Dist: pytest>=8.3.5; extra == 'all'
103
104
  Requires-Dist: python-dotenv>=1; extra == 'all'
105
+ Requires-Dist: pyyaml>=6; extra == 'all'
104
106
  Requires-Dist: radon>=6.0.1; extra == 'all'
105
107
  Requires-Dist: rdkit>=2025.9.3; extra == 'all'
106
108
  Requires-Dist: ruff>=0.1.5; extra == 'all'
@@ -185,6 +187,8 @@ Requires-Dist: pymdown-extensions>=10.14.3; extra == 'docs'
185
187
  Provides-Extra: genomics
186
188
  Requires-Dist: pyfaidx>=0.8.0; extra == 'genomics'
187
189
  Requires-Dist: pysam>=0.22.0; extra == 'genomics'
190
+ Provides-Extra: metal
191
+ Requires-Dist: jax-metal>=0.1.0; (sys_platform == 'darwin' and platform_machine == 'arm64') and extra == 'metal'
188
192
  Provides-Extra: soft-ops-advanced
189
193
  Requires-Dist: lineax>=0.0.8; extra == 'soft-ops-advanced'
190
194
  Requires-Dist: optimistix>=0.0.9; extra == 'soft-ops-advanced'
@@ -203,6 +207,7 @@ Requires-Dist: pytest-randomly>=3.16.0; extra == 'test'
203
207
  Requires-Dist: pytest-timeout>=2.1; extra == 'test'
204
208
  Requires-Dist: pytest-xdist>=3.6; extra == 'test'
205
209
  Requires-Dist: pytest>=8.3.5; extra == 'test'
210
+ Requires-Dist: pyyaml>=6; extra == 'test'
206
211
  Provides-Extra: torch-io
207
212
  Requires-Dist: torch>=1.13.0; extra == 'torch-io'
208
213
  Description-Content-Type: text/markdown
@@ -90,13 +90,13 @@ Download datasets before running:
90
90
 
91
91
  ```bash
92
92
  # Single-cell (immune_human, 2GB)
93
- mkdir -p /media/mahdi/ssd23/Data/scib
94
- wget -O /media/mahdi/ssd23/Data/scib/Immune_ALL_human.h5ad \
93
+ mkdir -p /mnt/ssd2/Data/scib
94
+ wget -O /mnt/ssd2/Data/scib/Immune_ALL_human.h5ad \
95
95
  "https://ndownloader.figshare.com/files/25717328"
96
96
 
97
97
  # Trajectory (pancreas, 51MB)
98
- mkdir -p /media/mahdi/ssd23/Data/scvelo
99
- wget -O /media/mahdi/ssd23/Data/scvelo/endocrinogenesis_day15.h5ad \
98
+ mkdir -p /mnt/ssd2/Data/scvelo
99
+ wget -O /mnt/ssd2/Data/scvelo/endocrinogenesis_day15.h5ad \
100
100
  "https://github.com/theislab/scvelo_notebooks/raw/master/data/Pancreas/endocrinogenesis_day15.h5ad"
101
101
  ```
102
102
 
@@ -37,7 +37,7 @@ classifiers = [
37
37
  ]
38
38
  dependencies = [
39
39
  # Datarax - operator, source, and pipeline contracts
40
- "datarax>=0.1.7",
40
+ "datarax>=0.1.10",
41
41
  # Artifex - modeling, modality, and transformer substrate
42
42
  "avitai-artifex>=0.1.5",
43
43
  # Opifex - scientific ML, operator learning, and advanced optimization
@@ -64,16 +64,16 @@ dependencies = [
64
64
  "h5py>=3.7",
65
65
  "jax-md>=0.2.27"
66
66
  ]
67
- description = "DiffBio: End-to-end differentiable bioinformatics pipelines built on Datarax, Artifex, Opifex, and Calibrax"
67
+ description = "End-to-end differentiable bioinformatics for JAX/Flax NNX: alignment, mapping, assembly, variant calling, RNA-seq, single-cell, epigenomics, CRISPR, metabolomics, multi-omics, protein and RNA structure, molecular dynamics and drug-discovery operators composed into trainable pipelines on datarax, artifex, opifex and calibrax"
68
68
  keywords = ["jax", "flax", "bioinformatics", "differentiable", "variant-calling", "alignment", "machine-learning"]
69
69
  license = {file = "LICENSE"}
70
70
  name = "diffbio"
71
71
  readme = "README.md"
72
72
  requires-python = ">=3.12,<3.14"
73
- version = "0.1.3"
73
+ version = "0.1.5"
74
74
 
75
75
  [project.optional-dependencies]
76
- all = ["diffbio[benchmark,chem,cuda12,dev,docs,genomics,soft-ops-advanced,soft-ops-ot,test,torch-io]"]
76
+ all = ["diffbio[benchmark,chem,cuda12,dev,docs,genomics,metal,soft-ops-advanced,soft-ops-ot,test,torch-io]"]
77
77
  benchmark = [
78
78
  # benchmarks/crossmodality/*.py call datasets.load_dataset. It used to arrive
79
79
  # transitively through the sibling packages; they now declare their heavy
@@ -131,6 +131,8 @@ docs = [
131
131
  "pymdown-extensions>=10.14.3"
132
132
  ]
133
133
  genomics = ["pysam>=0.22.0", "pyfaidx>=0.8.0"]
134
+ # Apple Silicon GPU backend; setup.sh selects it on arm64 macOS.
135
+ metal = ["jax-metal>=0.1.0; sys_platform == 'darwin' and platform_machine == 'arm64'"]
134
136
  soft-ops-advanced = ["optimistix>=0.0.9", "lineax>=0.0.8"]
135
137
  soft-ops-ot = ["diffbio[soft-ops-advanced]", "ott-jax>=0.5.0"]
136
138
  test = [
@@ -143,7 +145,9 @@ test = [
143
145
  "pytest-env>=1.0.1",
144
146
  "pytest-randomly>=3.16.0",
145
147
  "pytest-timeout>=2.1",
146
- "pytest-xdist>=3.6"
148
+ "pytest-xdist>=3.6",
149
+ # tests/test_ci_shards.py reads the CI workflow.
150
+ "pyyaml>=6"
147
151
  ]
148
152
  torch-io = ["torch>=1.13.0"]
149
153
 
@@ -341,7 +345,7 @@ save_data = true
341
345
  warmup = true
342
346
  warmup_iterations = 3
343
347
 
344
- [tool.pytest-env]
348
+ [tool.pytest_env]
345
349
  JAX_ENABLE_X64 = "0"
346
350
  JAX_SKIP_CUDA_CONSTRAINTS_CHECK = "1"
347
351
  LD_LIBRARY_PATH = "/usr/local/cuda/lib64"
@@ -139,7 +139,7 @@ class TemperatureOperator(OperatorModule):
139
139
  data: PyTree,
140
140
  state: PyTree,
141
141
  metadata: dict[str, Any] | None,
142
- random_params: Any = None,
142
+ key: jax.Array | None = None,
143
143
  stats: dict[str, Any] | None = None,
144
144
  ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
145
145
  """Base apply method - should be overridden by subclasses."""
@@ -233,7 +233,7 @@ class SequenceOperator(OperatorModule):
233
233
  data: PyTree,
234
234
  state: PyTree,
235
235
  metadata: dict[str, Any] | None,
236
- random_params: Any = None,
236
+ key: jax.Array | None = None,
237
237
  stats: dict[str, Any] | None = None,
238
238
  ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
239
239
  """Base apply method - should be overridden by subclasses."""
@@ -278,6 +278,7 @@ class EncoderDecoderOperator(OperatorModule):
278
278
  self,
279
279
  mean: Float[Array, "... latent_dim"],
280
280
  log_var: Float[Array, "... latent_dim"],
281
+ key: jax.Array | None = None,
281
282
  ) -> Float[Array, "... latent_dim"]:
282
283
  """Sample from latent distribution using reparameterization trick.
283
284
 
@@ -288,11 +289,16 @@ class EncoderDecoderOperator(OperatorModule):
288
289
  Args:
289
290
  mean: Mean of the latent distribution.
290
291
  log_var: Log variance of the latent distribution.
292
+ key: The key to draw epsilon from. ``apply`` passes the record's key, so a
293
+ record's sample follows the record; a training loss such as
294
+ ``compute_elbo_loss`` passes none and draws from the operator's ``sample``
295
+ stream, which advances on every call.
291
296
 
292
297
  Returns:
293
298
  Sampled latent representation.
294
299
  """
295
- key = get_rng_key(self.rngs, "sample", fallback_seed=0)
300
+ if key is None:
301
+ key = get_rng_key(self.rngs, "sample", fallback_seed=0)
296
302
  std = jnp.exp(0.5 * log_var)
297
303
  epsilon = jax.random.normal(key, mean.shape)
298
304
  return mean + std * epsilon
@@ -345,7 +351,7 @@ class EncoderDecoderOperator(OperatorModule):
345
351
  data: PyTree,
346
352
  state: PyTree,
347
353
  metadata: dict[str, Any] | None,
348
- random_params: Any = None,
354
+ key: jax.Array | None = None,
349
355
  stats: dict[str, Any] | None = None,
350
356
  ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
351
357
  """Base apply method - should be overridden by subclasses."""
@@ -442,7 +448,7 @@ class GraphOperator(OperatorModule):
442
448
  data: PyTree,
443
449
  state: PyTree,
444
450
  metadata: dict[str, Any] | None,
445
- random_params: Any = None,
451
+ key: jax.Array | None = None,
446
452
  stats: dict[str, Any] | None = None,
447
453
  ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
448
454
  """Base apply method - should be overridden by subclasses."""
@@ -593,7 +599,7 @@ class HMMOperator(OperatorModule):
593
599
  data: PyTree,
594
600
  state: PyTree,
595
601
  metadata: dict[str, Any] | None,
596
- random_params: Any = None,
602
+ key: jax.Array | None = None,
597
603
  stats: dict[str, Any] | None = None,
598
604
  ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
599
605
  """Base apply method - should be overridden by subclasses."""
@@ -6,6 +6,7 @@ the expected interfaces and data structures across the DiffBio codebase.
6
6
 
7
7
  from typing import Any, Protocol, TypedDict, runtime_checkable
8
8
 
9
+ import jax
9
10
  from jaxtyping import Array, Float
10
11
 
11
12
  # =============================================================================
@@ -166,7 +167,7 @@ class DifferentiableOperator(Protocol):
166
167
  data: dict[str, Any],
167
168
  state: StateDict,
168
169
  metadata: MetadataDict,
169
- random_params: Any = None,
170
+ key: jax.Array | None = None,
170
171
  stats: dict[str, Any] | None = None,
171
172
  ) -> OperatorOutput:
172
173
  """Apply the operator to input data.
@@ -175,7 +176,7 @@ class DifferentiableOperator(Protocol):
175
176
  data: Input data dictionary.
176
177
  state: Element state.
177
178
  metadata: Element metadata.
178
- random_params: Random parameters for stochastic operations.
179
+ key: The record's PRNG key; a stochastic operator draws from it.
179
180
  stats: Statistics dictionary.
180
181
 
181
182
  Returns:
@@ -81,6 +81,9 @@ from diffbio.core.soft_ops.sorting import (
81
81
  top_k_mask,
82
82
  )
83
83
 
84
+ # --- Range-aware normalization ---
85
+ from diffbio.core.soft_ops.normalization import temperature_softmax
86
+
84
87
  # --- Quantile ---
85
88
  from diffbio.core.soft_ops.quantile import (
86
89
  argmedian,
@@ -126,6 +129,7 @@ from diffbio.core.soft_ops.straight_through import (
126
129
  )
127
130
 
128
131
  __all__ = [
132
+ "temperature_softmax",
129
133
  # Types
130
134
  "SoftBool",
131
135
  "SoftIndex",
@@ -0,0 +1,174 @@
1
+ """Temperature softmax with jointly evaluated derivative coefficients.
2
+
3
+ Ordinary division AD forms ``T**-2`` before multiplying the softmax tail.
4
+ The product can be finite even when that reciprocal overflows or the tail
5
+ underflows. Evaluate probability products, score gaps and reciprocal powers
6
+ together in log space. A recursive coefficient JVP preserves mixed derivatives
7
+ at zero gaps; differentiating an outer zero selection would lose them.
8
+
9
+ This requires representable coefficient sums, not merely a representable final
10
+ contraction. Work grows with donor count and derivative order: the first JVP
11
+ has quadratic donor work. It is intended for small donor panels.
12
+ """
13
+
14
+ from functools import partial
15
+
16
+ import jax
17
+ import jax.numpy as jnp
18
+ from jax import Array
19
+
20
+ from diffbio.core.soft_ops._utils import canonicalize_axis
21
+
22
+
23
+ type _Specification = tuple[tuple[int, ...], tuple[tuple[int, int], ...], int]
24
+ type _Inputs = tuple[Array, Array, Array]
25
+
26
+
27
+ def _logits(scores: Array, temperature: Array, covered: Array) -> Array:
28
+ """Shift over covered donors before division, guarding excluded operands."""
29
+ maximum = jnp.max(jnp.where(covered, scores, -jnp.inf), axis=0, keepdims=True)
30
+ shifted = jnp.where(covered, scores - maximum, 0.0)
31
+ return jnp.where(covered, shifted / temperature, -jnp.inf)
32
+
33
+
34
+ def _coefficient_value(
35
+ scores: Array, temperature: Array, covered: Array, specification: _Specification
36
+ ) -> Array:
37
+ """Evaluate a probability/gap monomial divided by a temperature power."""
38
+ indices, gaps, power = specification
39
+ log_weights = jax.nn.log_softmax(_logits(scores, temperature, covered), axis=0)
40
+ magnitude = sum(log_weights[index] for index in indices) - power * jnp.log(temperature)
41
+ sign = jnp.ones_like(magnitude)
42
+ nonzero = jnp.ones_like(magnitude, dtype=jnp.bool_)
43
+ for left, right in gaps:
44
+ gap = scores[left] - scores[right]
45
+ nonzero = nonzero & (gap != 0)
46
+ magnitude = magnitude + jnp.log(jnp.where(gap != 0, jnp.abs(gap), 1.0))
47
+ sign = sign * jnp.sign(gap)
48
+ # Guard the exponential too: a zero gap must not create 0 * inf.
49
+ value = sign * jnp.exp(jnp.where(nonzero, magnitude, 0.0))
50
+ return jnp.where(nonzero, value, 0.0)
51
+
52
+
53
+ _coefficient = jax.custom_jvp(_coefficient_value, nondiff_argnums=(3,))
54
+
55
+
56
+ def _coefficient_jvp(
57
+ specification: _Specification, primals: _Inputs, tangents: _Inputs
58
+ ) -> tuple[Array, Array]:
59
+ """Differentiate complete coefficients, including zero-gap extensions."""
60
+ scores, _, _ = primals
61
+ score_dot, temperature_dot, _ = tangents
62
+ indices, gaps, power = specification
63
+ value = _coefficient(*primals, specification)
64
+ derivative = jnp.zeros_like(value)
65
+ thermal = jnp.zeros_like(value)
66
+ for index in indices:
67
+ for other in range(scores.shape[0]):
68
+ if other != index:
69
+ extended = (*indices, other)
70
+ coefficient = _coefficient(*primals, (extended, gaps, power + 1))
71
+ derivative = derivative + coefficient * (score_dot[index] - score_dot[other])
72
+ thermal = thermal + _coefficient(
73
+ *primals, (extended, (*gaps, (other, index)), power + 2)
74
+ )
75
+ for position, (left, right) in enumerate(gaps):
76
+ remaining = gaps[:position] + gaps[position + 1 :]
77
+ derivative = derivative + _coefficient(*primals, (indices, remaining, power)) * (
78
+ score_dot[left] - score_dot[right]
79
+ )
80
+ if power:
81
+ thermal = thermal - power * _coefficient(*primals, (indices, gaps, power + 1))
82
+ return value, derivative + thermal * temperature_dot
83
+
84
+
85
+ _coefficient.defjvp(_coefficient_jvp)
86
+
87
+
88
+ @jax.custom_jvp
89
+ def _weights(scores: Array, temperature: Array, covered: Array) -> Array:
90
+ """Keep the native shifted forward softmax arithmetic."""
91
+ return jax.nn.softmax(_logits(scores, temperature, covered), axis=0)
92
+
93
+
94
+ def _weights_jvp(primals: _Inputs, tangents: _Inputs) -> tuple[Array, Array]:
95
+ """Contract pairwise sensitivities without rounding either probability first."""
96
+ scores, _, _ = primals
97
+ score_dot, temperature_dot, _ = tangents
98
+ value = _weights(*primals)
99
+ rows = []
100
+ for index in range(scores.shape[0]):
101
+ derivative = jnp.zeros_like(value[index])
102
+ for other in range(scores.shape[0]):
103
+ if other != index:
104
+ probability = (index, other)
105
+ spatial = _coefficient(*primals, (probability, (), 1))
106
+ thermal = _coefficient(*primals, (probability, ((other, index),), 2))
107
+ derivative = derivative + spatial * (score_dot[index] - score_dot[other])
108
+ derivative = derivative + thermal * temperature_dot
109
+ rows.append(derivative)
110
+ return value, jnp.stack(rows)
111
+
112
+
113
+ _weights.defjvp(_weights_jvp)
114
+
115
+
116
+ @partial(jax.jit, static_argnames=("axis",))
117
+ def temperature_softmax(
118
+ scores: Array,
119
+ temperature: float | Array = 1.0,
120
+ *,
121
+ axis: int = -1,
122
+ where: Array | None = None,
123
+ ) -> Array:
124
+ """Normalize small score axes with range-aware temperature derivatives.
125
+
126
+ Equivalent to masked ``jax.nn.softmax(scores / temperature, axis=axis)``
127
+ for valid inputs. Shift before division and evaluate complete derivative
128
+ coefficients in signed log space, preserving sensitivities even when a
129
+ probability rounds to zero or one. Supports JVP, VJP and higher derivatives.
130
+
131
+ This is an opt-in operation for small axes, not the default sorting kernel:
132
+ first derivatives require quadratic axis work and higher orders cost more.
133
+ Coefficients, score differences and their necessary sums must be representable;
134
+ a representable final contraction alone does not guarantee a finite derivative.
135
+
136
+ Args:
137
+ scores: Real floating array. Included scores and their pairwise differences
138
+ must be finite. Excluded scores are ignored, including NaN/infinity.
139
+ temperature: Positive finite real scalar. Callers own runtime validation;
140
+ nonpositive/nonfinite values are outside this numerical contract.
141
+ axis: Static reduction axis. Must be nonempty.
142
+ where: Boolean mask broadcastable to the score shape. Empty slices return
143
+ zero weights and zero derivatives, matching native masked softmax.
144
+
145
+ Returns:
146
+ Weights with the score shape and standard floating dtype promotion.
147
+
148
+ Raises:
149
+ TypeError: Scores are not floating, temperature is complex, or mask is not boolean.
150
+ ValueError: Temperature is not scalar, the axis is invalid/empty, or mask cannot broadcast.
151
+ """
152
+ scores = jnp.asarray(scores)
153
+ temperature = jnp.asarray(temperature)
154
+ if not jnp.issubdtype(scores.dtype, jnp.floating):
155
+ raise TypeError("scores must have a real floating dtype")
156
+ if jnp.issubdtype(temperature.dtype, jnp.complexfloating):
157
+ raise TypeError("temperature must be real")
158
+ if temperature.ndim != 0:
159
+ raise ValueError("temperature must be scalar")
160
+ temperature = temperature.astype(jnp.result_type(scores, temperature))
161
+ axis = canonicalize_axis(axis, scores.ndim)
162
+ if scores.shape[axis] == 0:
163
+ raise ValueError("the normalization axis must be nonempty")
164
+ covered = jnp.ones_like(scores, dtype=jnp.bool_) if where is None else jnp.asarray(where)
165
+ if covered.dtype != jnp.bool_:
166
+ raise TypeError("where must have boolean dtype")
167
+ covered = jnp.broadcast_to(covered, scores.shape)
168
+ scores, covered = jnp.moveaxis(scores, axis, 0), jnp.moveaxis(covered, axis, 0)
169
+ scores = jnp.where(covered, scores, 0.0)
170
+ populated = jnp.any(covered, axis=0, keepdims=True)
171
+ first = (jnp.arange(scores.shape[0]) == 0).reshape((-1,) + (1,) * (scores.ndim - 1))
172
+ safe_coverage = covered | (first & ~populated)
173
+ value = _weights(scores, temperature, safe_coverage)
174
+ return jnp.moveaxis(jnp.where(populated, value, 0.0), 0, axis)
@@ -161,7 +161,7 @@ class EnsembleUQOperator(OperatorModule):
161
161
  data: PyTree,
162
162
  state: PyTree,
163
163
  metadata: dict[str, Any] | None,
164
- random_params: Any = None, # noqa: ARG002
164
+ key: jax.Array | None = None, # noqa: ARG002
165
165
  stats: dict[str, Any] | None = None, # noqa: ARG002
166
166
  ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
167
167
  """Run ensemble forward passes and aggregate with uncertainty.
@@ -170,7 +170,7 @@ class EnsembleUQOperator(OperatorModule):
170
170
  data: Input data dict for the base operator.
171
171
  state: Element state (passed through).
172
172
  metadata: Element metadata (passed through).
173
- random_params: Unused.
173
+ key: Unused.
174
174
  stats: Unused.
175
175
 
176
176
  Returns:
@@ -241,7 +241,7 @@ class MCSamplingUQOperator(OperatorModule):
241
241
  data: PyTree,
242
242
  state: PyTree,
243
243
  metadata: dict[str, Any] | None,
244
- random_params: Any = None, # noqa: ARG002
244
+ key: jax.Array | None = None, # noqa: ARG002
245
245
  stats: dict[str, Any] | None = None, # noqa: ARG002
246
246
  ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
247
247
  """Run Monte Carlo sampling to estimate empirical intervals.
@@ -250,7 +250,7 @@ class MCSamplingUQOperator(OperatorModule):
250
250
  data: Input data dict for the base operator.
251
251
  state: Element state (passed through).
252
252
  metadata: Element metadata (passed through).
253
- random_params: Unused.
253
+ key: Unused.
254
254
  stats: Unused.
255
255
 
256
256
  Returns:
@@ -135,7 +135,7 @@ class TaskAdapter:
135
135
  else:
136
136
  adapted = data_dict
137
137
 
138
- result, _, _ = operator.apply(adapted, {}, None)
138
+ result, _, _ = operator.apply(adapted, {}, None, jax.random.key(self._seed))
139
139
  # Propagate retention info
140
140
  if "retention_weights" not in result and "quality_scores" in adapted:
141
141
  quality = adapted["quality_scores"]
@@ -160,7 +160,7 @@ class TaskAdapter:
160
160
  temperature=temperature,
161
161
  )
162
162
  operator = SoftKMeansClustering(op_config, rngs=nnx.Rngs(self._seed))
163
- result, _, _ = operator.apply(data_dict, {}, None)
163
+ result, _, _ = operator.apply(data_dict, {}, None, jax.random.key(self._seed))
164
164
  return result
165
165
 
166
166
  def _run_de(self, data_dict: dict[str, Any], config: dict[str, Any]) -> dict[str, Any]:
@@ -178,7 +178,7 @@ class TaskAdapter:
178
178
  n_conditions=n_conditions,
179
179
  )
180
180
  pipeline = DifferentialExpressionPipeline(op_config, rngs=nnx.Rngs(self._seed))
181
- result, _, _ = pipeline.apply(data_dict, {}, None)
181
+ result, _, _ = pipeline.apply(data_dict, {}, None, jax.random.key(self._seed))
182
182
 
183
183
  # Add gene names for grader extraction
184
184
  if "gene_names" not in result:
@@ -202,7 +202,7 @@ class TaskAdapter:
202
202
  n_clusters=n_clusters,
203
203
  )
204
204
  operator = DifferentiableHarmony(op_config, rngs=nnx.Rngs(self._seed))
205
- result, _, _ = operator.apply(data_dict, {}, None)
205
+ result, _, _ = operator.apply(data_dict, {}, None, jax.random.key(self._seed))
206
206
  return result
207
207
 
208
208
  def _run_normalization(
@@ -222,7 +222,7 @@ class TaskAdapter:
222
222
  latent_dim=latent_dim,
223
223
  )
224
224
  operator = VAENormalizer(op_config, rngs=nnx.Rngs(self._seed))
225
- result, _, _ = operator.apply(data_dict, {}, None)
225
+ result, _, _ = operator.apply(data_dict, {}, None, jax.random.key(self._seed))
226
226
  return result
227
227
 
228
228
  def _run_trajectory(self, data_dict: dict[str, Any], config: dict[str, Any]) -> dict[str, Any]:
@@ -240,7 +240,7 @@ class TaskAdapter:
240
240
  n_diffusion_components=n_diffusion_components,
241
241
  )
242
242
  operator = DifferentiablePseudotime(op_config, rngs=nnx.Rngs(self._seed))
243
- result, _, _ = operator.apply(data_dict, {}, None)
243
+ result, _, _ = operator.apply(data_dict, {}, None, jax.random.key(self._seed))
244
244
  return result
245
245
 
246
246
  def _run_spatial_analysis(
@@ -260,7 +260,7 @@ class TaskAdapter:
260
260
  n_domains=n_domains,
261
261
  )
262
262
  operator = DifferentiableSpatialDomain(op_config, rngs=nnx.Rngs(self._seed))
263
- result, _, _ = operator.apply(data_dict, {}, None)
263
+ result, _, _ = operator.apply(data_dict, {}, None, jax.random.key(self._seed))
264
264
  return result
265
265
 
266
266
  def _run_cell_annotation(
@@ -8,7 +8,6 @@ construction and one mask/input preparation flow.
8
8
  from __future__ import annotations
9
9
 
10
10
  from dataclasses import dataclass
11
- from typing import Any
12
11
 
13
12
  import jax
14
13
  import jax.numpy as jnp
@@ -63,28 +62,34 @@ def build_masked_gene_transformer_encoder(
63
62
 
64
63
  def build_masked_gene_mask(
65
64
  *,
66
- random_params: Any,
65
+ key: jax.Array | None,
67
66
  mask_ratio: float,
68
67
  n_genes: int,
69
68
  ) -> Array:
70
- """Build a per-gene binary mask for masked-gene transformer operators."""
71
- if random_params is not None and mask_ratio > 0:
72
- noise = jax.random.uniform(random_params, (n_genes,))
73
- return (noise < mask_ratio).astype(jnp.float32)
74
- return jnp.zeros(n_genes, dtype=jnp.float32)
69
+ """Build a per-gene binary mask for masked-gene transformer operators.
70
+
71
+ With ``mask_ratio == 0`` nothing is masked and no key is needed; otherwise the mask is
72
+ drawn from ``key``, which must be given.
73
+ """
74
+ if mask_ratio <= 0:
75
+ return jnp.zeros(n_genes, dtype=jnp.float32)
76
+ if key is None:
77
+ raise ValueError("masking genes needs the record's PRNG key")
78
+ noise = jax.random.uniform(key, (n_genes,))
79
+ return (noise < mask_ratio).astype(jnp.float32)
75
80
 
76
81
 
77
82
  def prepare_masked_gene_batch(
78
83
  data: PyTree,
79
84
  *,
80
- random_params: Any,
85
+ key: jax.Array | None,
81
86
  mask_ratio: float,
82
87
  ) -> tuple[Array, Array, Array]:
83
88
  """Extract counts, int32 gene IDs, and the shared masking vector."""
84
89
  counts = data["counts"]
85
90
  gene_ids = jnp.asarray(data["gene_ids"], dtype=jnp.int32)
86
91
  mask = build_masked_gene_mask(
87
- random_params=random_params,
92
+ key=key,
88
93
  mask_ratio=mask_ratio,
89
94
  n_genes=int(counts.shape[1]),
90
95
  )
@@ -96,23 +101,14 @@ class MaskedGeneTransformerOperatorMixin:
96
101
 
97
102
  config: MaskedGeneTransformerConfigBase
98
103
 
99
- def generate_random_params(
100
- self,
101
- rng: jax.Array,
102
- data_shapes: PyTree,
103
- ) -> jax.Array:
104
- """Return the RNG key used for reproducible masking inside apply."""
105
- del data_shapes
106
- return rng
107
-
108
104
  def prepare_masked_gene_batch(
109
105
  self,
110
106
  data: PyTree,
111
- random_params: Any,
107
+ key: jax.Array | None,
112
108
  ) -> tuple[Array, Array, Array]:
113
109
  """Prepare shared masked-gene inputs for per-cell `vmap` execution."""
114
110
  return prepare_masked_gene_batch(
115
111
  data,
116
- random_params=random_params,
112
+ key=key,
117
113
  mask_ratio=self.config.mask_ratio,
118
114
  )
@@ -41,7 +41,6 @@ class ProfileHMMConfig(OperatorConfig):
41
41
  learnable_profile: Whether profile parameters are learnable.
42
42
  """
43
43
 
44
- cacheable: bool = True
45
44
  profile_length: int = 100
46
45
  alphabet_size: int = 20 # Amino acids by default
47
46
  temperature: float = 1.0
@@ -309,7 +308,7 @@ class ProfileHMMSearch(TemperatureOperator):
309
308
  data: PyTree,
310
309
  state: PyTree,
311
310
  metadata: dict[str, Any] | None,
312
- random_params: Any = None,
311
+ key: jax.Array | None = None,
313
312
  stats: dict[str, Any] | None = None,
314
313
  ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
315
314
  """Apply profile HMM search to sequence.
@@ -319,7 +318,7 @@ class ProfileHMMSearch(TemperatureOperator):
319
318
  - "sequence": One-hot encoded sequence (seq_len, alphabet_size)
320
319
  state: Element state (passed through unchanged)
321
320
  metadata: Element metadata (passed through unchanged)
322
- random_params: Not used (deterministic operator)
321
+ key: Unused.
323
322
  stats: Not used
324
323
 
325
324
  Returns:
@@ -39,7 +39,6 @@ class SmithWatermanConfig(TemperatureConfig):
39
39
  gap_extend: Penalty for extending a gap.
40
40
  """
41
41
 
42
- cacheable: bool = True
43
42
  gap_open: float = DEFAULT_GAP_OPEN
44
43
  gap_extend: float = DEFAULT_GAP_EXTEND
45
44
 
@@ -216,7 +215,7 @@ class SmoothSmithWaterman(TemperatureOperator):
216
215
  data: PyTree,
217
216
  state: PyTree,
218
217
  metadata: dict[str, Any] | None,
219
- random_params: Any = None,
218
+ key: jax.Array | None = None,
220
219
  stats: dict[str, Any] | None = None,
221
220
  ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
222
221
  """Apply alignment to sequence pair data.
@@ -233,7 +232,7 @@ class SmoothSmithWaterman(TemperatureOperator):
233
232
  - "seq2": Second sequence, one-hot encoded (len2, alphabet_size)
234
233
  state: Element state (passed through unchanged)
235
234
  metadata: Element metadata (passed through unchanged)
236
- random_params: Not used (deterministic operator)
235
+ key: Unused.
237
236
  stats: Not used
238
237
 
239
238
  Returns:
@@ -339,7 +339,7 @@ class SoftProgressiveMSA(TemperatureOperator):
339
339
  data: PyTree,
340
340
  state: PyTree,
341
341
  metadata: dict[str, Any] | None,
342
- random_params: Any = None,
342
+ key: jax.Array | None = None,
343
343
  stats: dict[str, Any] | None = None,
344
344
  ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
345
345
  """Apply soft progressive MSA.
@@ -349,7 +349,7 @@ class SoftProgressiveMSA(TemperatureOperator):
349
349
  - "sequences": Input sequences (n_seqs, seq_len, alphabet_size)
350
350
  state: Element state (passed through unchanged)
351
351
  metadata: Element metadata (passed through unchanged)
352
- random_params: Not used
352
+ key: Unused.
353
353
  stats: Not used
354
354
 
355
355
  Returns: