diffbio 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- diffbio/__init__.py +39 -0
- diffbio/configs.py +75 -0
- diffbio/constants.py +204 -0
- diffbio/core/__init__.py +127 -0
- diffbio/core/base_operators.py +612 -0
- diffbio/core/data_types.py +260 -0
- diffbio/core/gnn_components.py +629 -0
- diffbio/core/graph_utils.py +149 -0
- diffbio/core/neural_components.py +270 -0
- diffbio/core/optimal_transport.py +133 -0
- diffbio/core/soft_ops/__init__.py +216 -0
- diffbio/core/soft_ops/_projections_permutahedron.py +1864 -0
- diffbio/core/soft_ops/_projections_simplex.py +240 -0
- diffbio/core/soft_ops/_projections_transport.py +508 -0
- diffbio/core/soft_ops/_sorting_network.py +204 -0
- diffbio/core/soft_ops/_types.py +15 -0
- diffbio/core/soft_ops/_utils.py +342 -0
- diffbio/core/soft_ops/autograd_safe.py +120 -0
- diffbio/core/soft_ops/comparison.py +235 -0
- diffbio/core/soft_ops/elementwise.py +309 -0
- diffbio/core/soft_ops/logical.py +146 -0
- diffbio/core/soft_ops/quantile.py +376 -0
- diffbio/core/soft_ops/selection.py +236 -0
- diffbio/core/soft_ops/sorting.py +926 -0
- diffbio/core/soft_ops/straight_through.py +261 -0
- diffbio/core/uncertainty.py +279 -0
- diffbio/evaluation/__init__.py +42 -0
- diffbio/evaluation/adapters.py +409 -0
- diffbio/evaluation/graders.py +223 -0
- diffbio/evaluation/problem.py +157 -0
- diffbio/evaluation/runner.py +277 -0
- diffbio/losses/__init__.py +59 -0
- diffbio/losses/alignment_losses.py +222 -0
- diffbio/losses/biological_regularization.py +288 -0
- diffbio/losses/metric_losses.py +139 -0
- diffbio/losses/singlecell_losses.py +387 -0
- diffbio/losses/statistical_losses.py +345 -0
- diffbio/operators/__init__.py +60 -0
- diffbio/operators/_count_vae.py +197 -0
- diffbio/operators/_loss_balancing.py +65 -0
- diffbio/operators/_masked_gene_transformer.py +118 -0
- diffbio/operators/_transformer_validation.py +50 -0
- diffbio/operators/alignment/__init__.py +51 -0
- diffbio/operators/alignment/profile_hmm.py +350 -0
- diffbio/operators/alignment/scoring.py +127 -0
- diffbio/operators/alignment/smith_waterman.py +261 -0
- diffbio/operators/alignment/soft_msa.py +419 -0
- diffbio/operators/assembly/__init__.py +27 -0
- diffbio/operators/assembly/gnn_assembly.py +252 -0
- diffbio/operators/assembly/metagenomic_binning.py +296 -0
- diffbio/operators/crispr/__init__.py +17 -0
- diffbio/operators/crispr/guide_scoring.py +269 -0
- diffbio/operators/drug_discovery/__init__.py +133 -0
- diffbio/operators/drug_discovery/_graph_utils.py +142 -0
- diffbio/operators/drug_discovery/admet_predictor.py +285 -0
- diffbio/operators/drug_discovery/attentive_fp.py +411 -0
- diffbio/operators/drug_discovery/dti.py +261 -0
- diffbio/operators/drug_discovery/fingerprint.py +490 -0
- diffbio/operators/drug_discovery/maccs_keys.py +267 -0
- diffbio/operators/drug_discovery/message_passing.py +200 -0
- diffbio/operators/drug_discovery/primitives.py +242 -0
- diffbio/operators/drug_discovery/property_predictor.py +163 -0
- diffbio/operators/drug_discovery/similarity.py +193 -0
- diffbio/operators/epigenomics/__init__.py +35 -0
- diffbio/operators/epigenomics/chromatin_state.py +491 -0
- diffbio/operators/epigenomics/contextual.py +288 -0
- diffbio/operators/epigenomics/fno_peak_calling.py +153 -0
- diffbio/operators/epigenomics/peak_calling.py +555 -0
- diffbio/operators/foundation_models/__init__.py +119 -0
- diffbio/operators/foundation_models/adapters.py +114 -0
- diffbio/operators/foundation_models/contracts.py +245 -0
- diffbio/operators/foundation_models/embedding_probe.py +83 -0
- diffbio/operators/foundation_models/experimental.py +128 -0
- diffbio/operators/foundation_models/foundation_model.py +332 -0
- diffbio/operators/foundation_models/frozen.py +59 -0
- diffbio/operators/foundation_models/precomputed.py +270 -0
- diffbio/operators/foundation_models/transformer_encoder.py +564 -0
- diffbio/operators/mapping/__init__.py +17 -0
- diffbio/operators/mapping/neural_mapper.py +493 -0
- diffbio/operators/metabolomics/__init__.py +39 -0
- diffbio/operators/metabolomics/spectral_similarity.py +315 -0
- diffbio/operators/molecular_dynamics/__init__.py +51 -0
- diffbio/operators/molecular_dynamics/force_field.py +265 -0
- diffbio/operators/molecular_dynamics/integrator.py +304 -0
- diffbio/operators/molecular_dynamics/primitives.py +115 -0
- diffbio/operators/multiomics/__init__.py +38 -0
- diffbio/operators/multiomics/hic_contact.py +377 -0
- diffbio/operators/multiomics/multiomics_vae.py +325 -0
- diffbio/operators/multiomics/spatial_deconvolution.py +316 -0
- diffbio/operators/multiomics/spatial_gene_detection.py +493 -0
- diffbio/operators/normalization/__init__.py +42 -0
- diffbio/operators/normalization/embedding.py +222 -0
- diffbio/operators/normalization/phate.py +400 -0
- diffbio/operators/normalization/umap.py +261 -0
- diffbio/operators/normalization/vae_normalizer.py +258 -0
- diffbio/operators/population/__init__.py +17 -0
- diffbio/operators/population/ancestry_estimation.py +274 -0
- diffbio/operators/preprocessing/__init__.py +76 -0
- diffbio/operators/preprocessing/adapter_removal.py +311 -0
- diffbio/operators/preprocessing/duplicate_filter.py +317 -0
- diffbio/operators/preprocessing/error_correction.py +287 -0
- diffbio/operators/protein/__init__.py +31 -0
- diffbio/operators/protein/secondary_structure.py +509 -0
- diffbio/operators/quality_filter.py +128 -0
- diffbio/operators/rna_structure/__init__.py +35 -0
- diffbio/operators/rna_structure/rna_folding.py +509 -0
- diffbio/operators/rnaseq/__init__.py +23 -0
- diffbio/operators/rnaseq/motif_discovery.py +251 -0
- diffbio/operators/rnaseq/splicing_psi.py +216 -0
- diffbio/operators/singlecell/__init__.py +193 -0
- diffbio/operators/singlecell/ambient_removal.py +333 -0
- diffbio/operators/singlecell/archetypes.py +191 -0
- diffbio/operators/singlecell/batch_correction.py +288 -0
- diffbio/operators/singlecell/cell_annotation.py +519 -0
- diffbio/operators/singlecell/communication.py +704 -0
- diffbio/operators/singlecell/differential_distribution.py +243 -0
- diffbio/operators/singlecell/doublet_detection.py +657 -0
- diffbio/operators/singlecell/downsampling.py +166 -0
- diffbio/operators/singlecell/enhanced_batch_correction.py +519 -0
- diffbio/operators/singlecell/grn_inference.py +336 -0
- diffbio/operators/singlecell/imputation.py +429 -0
- diffbio/operators/singlecell/knockdown_filter.py +176 -0
- diffbio/operators/singlecell/ot_trajectory.py +277 -0
- diffbio/operators/singlecell/simulation.py +444 -0
- diffbio/operators/singlecell/sindy_grn.py +247 -0
- diffbio/operators/singlecell/soft_clustering.py +211 -0
- diffbio/operators/singlecell/spatial_domains.py +677 -0
- diffbio/operators/singlecell/switch_de.py +184 -0
- diffbio/operators/singlecell/trajectory.py +447 -0
- diffbio/operators/singlecell/velocity.py +361 -0
- diffbio/operators/statistical/__init__.py +35 -0
- diffbio/operators/statistical/em_quantification.py +260 -0
- diffbio/operators/statistical/hmm.py +234 -0
- diffbio/operators/statistical/nb_glm.py +272 -0
- diffbio/operators/variant/__init__.py +64 -0
- diffbio/operators/variant/classifier.py +333 -0
- diffbio/operators/variant/cnn_classifier.py +255 -0
- diffbio/operators/variant/cnv_segmentation.py +678 -0
- diffbio/operators/variant/deepvariant_pileup.py +426 -0
- diffbio/operators/variant/pileup.py +240 -0
- diffbio/operators/variant/quality_recalibration.py +274 -0
- diffbio/pipelines/__init__.py +65 -0
- diffbio/pipelines/differential_expression.py +279 -0
- diffbio/pipelines/enhanced_variant_calling.py +326 -0
- diffbio/pipelines/perturbation.py +407 -0
- diffbio/pipelines/preprocessing.py +267 -0
- diffbio/pipelines/single_cell.py +366 -0
- diffbio/pipelines/variant_calling.py +490 -0
- diffbio/samplers/__init__.py +9 -0
- diffbio/samplers/perturbation_sampler.py +142 -0
- diffbio/sequences/__init__.py +34 -0
- diffbio/sequences/dna.py +239 -0
- diffbio/sources/__init__.py +149 -0
- diffbio/sources/_anndata_shared.py +89 -0
- diffbio/sources/_batch_iteration.py +37 -0
- diffbio/sources/_benchmark_source.py +152 -0
- diffbio/sources/_indexed_batch_source.py +38 -0
- diffbio/sources/_utils.py +45 -0
- diffbio/sources/anndata_interop.py +387 -0
- diffbio/sources/anndata_source.py +361 -0
- diffbio/sources/archive_ii.py +174 -0
- diffbio/sources/balifam.py +207 -0
- diffbio/sources/bam.py +265 -0
- diffbio/sources/bengrn_ground_truth.py +306 -0
- diffbio/sources/contextual_epigenomics.py +242 -0
- diffbio/sources/dti.py +359 -0
- diffbio/sources/embeddings.py +203 -0
- diffbio/sources/encode_peaks.py +223 -0
- diffbio/sources/fasta.py +226 -0
- diffbio/sources/immune_human.py +172 -0
- diffbio/sources/indexed_embeddings.py +128 -0
- diffbio/sources/indexed_view.py +191 -0
- diffbio/sources/molnet.py +493 -0
- diffbio/sources/multiomics.py +279 -0
- diffbio/sources/pancreas.py +108 -0
- diffbio/sources/perturbation/__init__.py +69 -0
- diffbio/sources/perturbation/_types.py +51 -0
- diffbio/sources/perturbation/_utils.py +125 -0
- diffbio/sources/perturbation/concat_source.py +115 -0
- diffbio/sources/perturbation/control_mapping.py +215 -0
- diffbio/sources/perturbation/experiment_config.py +261 -0
- diffbio/sources/perturbation/h5_metadata_cache.py +218 -0
- diffbio/sources/perturbation/output_space.py +52 -0
- diffbio/sources/perturbation/perturbation_source.py +513 -0
- diffbio/sources/seqfish.py +145 -0
- diffbio/sources/sequence_foundation.py +68 -0
- diffbio/sources/singlecell_foundation.py +68 -0
- diffbio/splitters/__init__.py +63 -0
- diffbio/splitters/base.py +251 -0
- diffbio/splitters/molecular.py +330 -0
- diffbio/splitters/perturbation.py +199 -0
- diffbio/splitters/random.py +217 -0
- diffbio/splitters/sequence.py +201 -0
- diffbio/utils/__init__.py +55 -0
- diffbio/utils/dependency_runtime.py +115 -0
- diffbio/utils/nn_utils.py +157 -0
- diffbio/utils/quality.py +45 -0
- diffbio/utils/training.py +585 -0
- diffbio-0.1.0.dist-info/METADATA +480 -0
- diffbio-0.1.0.dist-info/RECORD +202 -0
- diffbio-0.1.0.dist-info/WHEEL +4 -0
- diffbio-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
"""Differentiable peak calling for ChIP-seq and ATAC-seq data.
|
|
2
|
+
|
|
3
|
+
This module implements a CNN-based differentiable peak caller that can be
|
|
4
|
+
used for ChIP-seq and ATAC-seq analysis with end-to-end gradient flow.
|
|
5
|
+
|
|
6
|
+
Optionally includes a VAE-based denoising stage (inspired by SCALE) that
|
|
7
|
+
encodes the coverage signal into a latent space and decodes it using a
|
|
8
|
+
Poisson decoder before peak detection.
|
|
9
|
+
|
|
10
|
+
Inherits from TemperatureOperator to get:
|
|
11
|
+
|
|
12
|
+
- _temperature property for temperature-controlled smoothing
|
|
13
|
+
- soft_max() for logsumexp-based smooth maximum
|
|
14
|
+
- soft_argmax() for soft position selection
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
import flax.nnx as nnx
|
|
22
|
+
import jax
|
|
23
|
+
import jax.numpy as jnp
|
|
24
|
+
from artifex.generative_models.core.losses.divergence import gaussian_kl_divergence
|
|
25
|
+
from datarax.core.config import OperatorConfig
|
|
26
|
+
|
|
27
|
+
from diffbio.core import soft_ops
|
|
28
|
+
from diffbio.core.base_operators import TemperatureOperator
|
|
29
|
+
from diffbio.utils.nn_utils import ensure_rngs, get_rng_key
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class _PeakDetectionConfig:
|
|
36
|
+
"""Peak detection hyperparameters."""
|
|
37
|
+
|
|
38
|
+
window_size: int = 200
|
|
39
|
+
num_filters: int = 32
|
|
40
|
+
kernel_sizes: tuple[int, ...] = (5, 11, 21)
|
|
41
|
+
threshold: float = 0.5
|
|
42
|
+
temperature: float = 1.0
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class _PeakDenoisingConfig:
|
|
47
|
+
"""Optional VAE denoising hyperparameters."""
|
|
48
|
+
|
|
49
|
+
learnable_temperature: bool = True
|
|
50
|
+
min_peak_width: int = 50
|
|
51
|
+
use_vae_denoising: bool = False
|
|
52
|
+
vae_latent_dim: int = 16
|
|
53
|
+
vae_hidden_dim: int = 64
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class PeakCallerConfig(_PeakDetectionConfig, _PeakDenoisingConfig, OperatorConfig):
|
|
58
|
+
"""Configuration for differentiable peak caller."""
|
|
59
|
+
|
|
60
|
+
def __post_init__(self) -> None:
|
|
61
|
+
"""Validate the peak caller configuration."""
|
|
62
|
+
super().__post_init__()
|
|
63
|
+
|
|
64
|
+
if self.window_size <= 0:
|
|
65
|
+
raise ValueError("window_size must be positive.")
|
|
66
|
+
if self.num_filters <= 0:
|
|
67
|
+
raise ValueError("num_filters must be positive.")
|
|
68
|
+
if not self.kernel_sizes or any(kernel_size <= 0 for kernel_size in self.kernel_sizes):
|
|
69
|
+
raise ValueError("kernel_sizes must contain only positive integers.")
|
|
70
|
+
if not 0.0 <= self.threshold <= 1.0:
|
|
71
|
+
raise ValueError("threshold must be between 0.0 and 1.0.")
|
|
72
|
+
if self.temperature <= 0.0:
|
|
73
|
+
raise ValueError("temperature must be positive.")
|
|
74
|
+
if self.min_peak_width <= 0:
|
|
75
|
+
raise ValueError("min_peak_width must be positive.")
|
|
76
|
+
if self.vae_latent_dim <= 0:
|
|
77
|
+
raise ValueError("vae_latent_dim must be positive.")
|
|
78
|
+
if self.vae_hidden_dim <= 0:
|
|
79
|
+
raise ValueError("vae_hidden_dim must be positive.")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class SignalVAE(nnx.Module):
|
|
83
|
+
"""VAE for denoising coverage signals with Poisson decoder.
|
|
84
|
+
|
|
85
|
+
Inspired by SCALE (Single-Cell ATAC-seq Analysis via Latent feature
|
|
86
|
+
Extraction), this module uses a VAE with a Poisson decoder to denoise
|
|
87
|
+
coverage signals. The Poisson distribution naturally models count data
|
|
88
|
+
(read coverage) and the latent space captures the underlying signal.
|
|
89
|
+
|
|
90
|
+
Architecture:
|
|
91
|
+
Encoder: coverage -> hidden -> (mean, logvar)
|
|
92
|
+
Reparameterize: z = mean + exp(0.5 * logvar) * epsilon
|
|
93
|
+
Decoder: z -> hidden -> log_rate (Poisson parameter)
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
signal_dim: int,
|
|
99
|
+
latent_dim: int = 16,
|
|
100
|
+
hidden_dim: int = 64,
|
|
101
|
+
*,
|
|
102
|
+
rngs: nnx.Rngs,
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Initialize the signal VAE.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
signal_dim: Dimension of the input signal (length).
|
|
108
|
+
latent_dim: Dimension of the latent space.
|
|
109
|
+
hidden_dim: Dimension of hidden layers.
|
|
110
|
+
rngs: Random number generators for initialization.
|
|
111
|
+
"""
|
|
112
|
+
super().__init__()
|
|
113
|
+
self.signal_dim = signal_dim
|
|
114
|
+
self.latent_dim = latent_dim
|
|
115
|
+
|
|
116
|
+
# Encoder
|
|
117
|
+
self.enc_hidden = nnx.Linear(signal_dim, hidden_dim, rngs=rngs)
|
|
118
|
+
self.enc_mean = nnx.Linear(hidden_dim, latent_dim, rngs=rngs)
|
|
119
|
+
self.enc_logvar = nnx.Linear(hidden_dim, latent_dim, rngs=rngs)
|
|
120
|
+
|
|
121
|
+
# Decoder (Poisson: outputs log-rate)
|
|
122
|
+
self.dec_hidden = nnx.Linear(latent_dim, hidden_dim, rngs=rngs)
|
|
123
|
+
self.dec_output = nnx.Linear(hidden_dim, signal_dim, rngs=rngs)
|
|
124
|
+
|
|
125
|
+
def encode(self, x: jax.Array) -> tuple[jax.Array, jax.Array]:
|
|
126
|
+
"""Encode signal to latent distribution parameters.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
x: Input signal of shape (..., signal_dim).
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Tuple of (mean, logvar) each of shape (..., latent_dim).
|
|
133
|
+
"""
|
|
134
|
+
h = nnx.relu(self.enc_hidden(x))
|
|
135
|
+
mean = self.enc_mean(h)
|
|
136
|
+
logvar = jnp.clip(self.enc_logvar(h), -10.0, 10.0)
|
|
137
|
+
return mean, logvar
|
|
138
|
+
|
|
139
|
+
def decode(self, z: jax.Array) -> jax.Array:
|
|
140
|
+
"""Decode latent representation to Poisson log-rate.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
z: Latent representation of shape (..., latent_dim).
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Log-rate for Poisson decoder of shape (..., signal_dim).
|
|
147
|
+
"""
|
|
148
|
+
h = nnx.relu(self.dec_hidden(z))
|
|
149
|
+
log_rate = self.dec_output(h)
|
|
150
|
+
return log_rate
|
|
151
|
+
|
|
152
|
+
def __call__(
|
|
153
|
+
self,
|
|
154
|
+
x: jax.Array,
|
|
155
|
+
rng_key: jax.Array,
|
|
156
|
+
) -> tuple[jax.Array, jax.Array, jax.Array, jax.Array]:
|
|
157
|
+
"""Forward pass: encode, sample, decode.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
x: Input signal of shape (..., signal_dim).
|
|
161
|
+
rng_key: JAX PRNG key for reparameterization sampling.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Tuple of (denoised, mean, logvar, log_rate):
|
|
165
|
+
- denoised: Denoised signal (exp(log_rate)), shape (..., signal_dim).
|
|
166
|
+
- mean: Latent mean, shape (..., latent_dim).
|
|
167
|
+
- logvar: Latent log-variance, shape (..., latent_dim).
|
|
168
|
+
- log_rate: Raw Poisson log-rate, shape (..., signal_dim).
|
|
169
|
+
"""
|
|
170
|
+
mean, logvar = self.encode(x)
|
|
171
|
+
|
|
172
|
+
# Reparameterization trick
|
|
173
|
+
std = jnp.exp(0.5 * logvar)
|
|
174
|
+
epsilon = jax.random.normal(rng_key, mean.shape)
|
|
175
|
+
z = mean + std * epsilon
|
|
176
|
+
|
|
177
|
+
log_rate = self.decode(z)
|
|
178
|
+
# Poisson rate = exp(log_rate), clamp for stability
|
|
179
|
+
denoised = jnp.exp(jnp.clip(log_rate, -10.0, 10.0))
|
|
180
|
+
|
|
181
|
+
return denoised, mean, logvar, log_rate
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class PeakDetectionCNN(nnx.Module):
|
|
185
|
+
"""CNN module for detecting peak patterns in coverage signals.
|
|
186
|
+
|
|
187
|
+
Uses multi-scale convolutions to capture peaks of varying widths.
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
def __init__(
|
|
191
|
+
self,
|
|
192
|
+
num_filters: int = 32,
|
|
193
|
+
kernel_sizes: tuple[int, ...] = (5, 11, 21),
|
|
194
|
+
*,
|
|
195
|
+
rngs: nnx.Rngs,
|
|
196
|
+
):
|
|
197
|
+
"""Initialize the peak detection CNN.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
num_filters: Number of filters per convolution layer.
|
|
201
|
+
kernel_sizes: Kernel sizes for multi-scale detection.
|
|
202
|
+
rngs: Random number generators for initialization.
|
|
203
|
+
"""
|
|
204
|
+
super().__init__()
|
|
205
|
+
self.num_filters = num_filters
|
|
206
|
+
self.kernel_sizes = kernel_sizes
|
|
207
|
+
|
|
208
|
+
# Multi-scale convolution layers - use nnx.List for proper parameter tracking
|
|
209
|
+
self.conv_layers = nnx.List(
|
|
210
|
+
[
|
|
211
|
+
nnx.Conv(
|
|
212
|
+
in_features=1,
|
|
213
|
+
out_features=num_filters,
|
|
214
|
+
kernel_size=(kernel_size,),
|
|
215
|
+
padding="SAME",
|
|
216
|
+
rngs=rngs,
|
|
217
|
+
)
|
|
218
|
+
for kernel_size in kernel_sizes
|
|
219
|
+
]
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Combine multi-scale features
|
|
223
|
+
total_features = num_filters * len(kernel_sizes)
|
|
224
|
+
self.combine_conv = nnx.Conv(
|
|
225
|
+
in_features=total_features,
|
|
226
|
+
out_features=num_filters,
|
|
227
|
+
kernel_size=(3,),
|
|
228
|
+
padding="SAME",
|
|
229
|
+
rngs=rngs,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# Final prediction layer
|
|
233
|
+
self.output_conv = nnx.Conv(
|
|
234
|
+
in_features=num_filters,
|
|
235
|
+
out_features=1,
|
|
236
|
+
kernel_size=(1,),
|
|
237
|
+
padding="SAME",
|
|
238
|
+
rngs=rngs,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
def __call__(self, x: jax.Array) -> jax.Array:
|
|
242
|
+
"""Forward pass for peak detection.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
x: Input coverage signal of shape (batch, length, 1) or (batch, length).
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Peak scores of shape (batch, length).
|
|
249
|
+
"""
|
|
250
|
+
# Ensure input has channel dimension
|
|
251
|
+
if x.ndim == 2:
|
|
252
|
+
x = x[..., None]
|
|
253
|
+
|
|
254
|
+
# Multi-scale convolutions
|
|
255
|
+
features = []
|
|
256
|
+
for conv in self.conv_layers:
|
|
257
|
+
feat = nnx.relu(conv(x))
|
|
258
|
+
features.append(feat)
|
|
259
|
+
|
|
260
|
+
# Concatenate multi-scale features
|
|
261
|
+
combined = jnp.concatenate(features, axis=-1)
|
|
262
|
+
|
|
263
|
+
# Combine and predict
|
|
264
|
+
x = nnx.relu(self.combine_conv(combined))
|
|
265
|
+
scores = self.output_conv(x)
|
|
266
|
+
|
|
267
|
+
return scores.squeeze(-1)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class DifferentiablePeakCaller(TemperatureOperator):
|
|
271
|
+
"""Differentiable peak caller for ChIP-seq and ATAC-seq data.
|
|
272
|
+
|
|
273
|
+
This operator uses a CNN-based approach to detect peaks in coverage
|
|
274
|
+
signals, with soft thresholding for end-to-end differentiability.
|
|
275
|
+
|
|
276
|
+
Optionally applies VAE-based denoising before peak detection, using a
|
|
277
|
+
Poisson decoder (per SCALE) to model count data. When VAE denoising
|
|
278
|
+
is enabled, the pipeline is:
|
|
279
|
+
coverage -> VAE encoder -> latent -> Poisson decoder -> denoised -> CNN -> peaks
|
|
280
|
+
|
|
281
|
+
The operator processes coverage data and outputs:
|
|
282
|
+
- Peak probabilities at each position
|
|
283
|
+
- Peak boundaries (soft)
|
|
284
|
+
- Peak summits
|
|
285
|
+
- Denoised coverage (when VAE is enabled)
|
|
286
|
+
|
|
287
|
+
Example:
|
|
288
|
+
```python
|
|
289
|
+
config = PeakCallerConfig(
|
|
290
|
+
window_size=200,
|
|
291
|
+
num_filters=32,
|
|
292
|
+
threshold=0.5,
|
|
293
|
+
use_vae_denoising=True,
|
|
294
|
+
)
|
|
295
|
+
peak_caller = DifferentiablePeakCaller(config, rngs=rngs)
|
|
296
|
+
|
|
297
|
+
data = {"coverage": coverage_signal}
|
|
298
|
+
result, state, metadata = peak_caller.apply(data, {}, None)
|
|
299
|
+
peak_probs = result["peak_probabilities"]
|
|
300
|
+
```
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
def __init__(self, config: PeakCallerConfig, *, rngs: nnx.Rngs | None = None):
|
|
304
|
+
"""Initialize the differentiable peak caller.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
config: Configuration for the peak caller.
|
|
308
|
+
rngs: Random number generators for initialization.
|
|
309
|
+
"""
|
|
310
|
+
super().__init__(config, rngs=rngs)
|
|
311
|
+
self.config = config
|
|
312
|
+
|
|
313
|
+
# Initialize RNGs if not provided
|
|
314
|
+
if rngs is None:
|
|
315
|
+
rngs = nnx.Rngs(0)
|
|
316
|
+
|
|
317
|
+
self._rngs = ensure_rngs(rngs)
|
|
318
|
+
|
|
319
|
+
# Learnable threshold parameter
|
|
320
|
+
self.threshold = nnx.Param(jnp.array(config.threshold))
|
|
321
|
+
|
|
322
|
+
# Temperature is managed by TemperatureOperator via self._temperature
|
|
323
|
+
|
|
324
|
+
# Peak detection CNN
|
|
325
|
+
self.peak_cnn = PeakDetectionCNN(
|
|
326
|
+
num_filters=config.num_filters,
|
|
327
|
+
kernel_sizes=config.kernel_sizes,
|
|
328
|
+
rngs=rngs,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
# Local maximum detection kernel (for summit finding)
|
|
332
|
+
self.summit_kernel_size = config.min_peak_width
|
|
333
|
+
|
|
334
|
+
# Optional VAE denoiser
|
|
335
|
+
self._use_vae = config.use_vae_denoising
|
|
336
|
+
if self._use_vae:
|
|
337
|
+
# Signal dim is set per-call since it depends on input length,
|
|
338
|
+
# but we pre-initialize the VAE with a fixed config dim.
|
|
339
|
+
# For position-independent denoising, we use a 1D VAE per position
|
|
340
|
+
# applied across the signal length. This uses a fixed-dim VAE.
|
|
341
|
+
self.vae_encoder = SignalVAE(
|
|
342
|
+
signal_dim=config.window_size,
|
|
343
|
+
latent_dim=config.vae_latent_dim,
|
|
344
|
+
hidden_dim=config.vae_hidden_dim,
|
|
345
|
+
rngs=rngs,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
def _vae_denoise(self, coverage: jax.Array) -> tuple[jax.Array, jax.Array]:
|
|
349
|
+
"""Apply VAE-based denoising to coverage signal.
|
|
350
|
+
|
|
351
|
+
Processes each sample in the batch independently through the VAE.
|
|
352
|
+
The signal is reshaped into windows of size window_size, denoised
|
|
353
|
+
per-window, and reassembled.
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
coverage: Coverage signal of shape (batch, length).
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Tuple of (denoised_coverage, kl_loss):
|
|
360
|
+
- denoised_coverage: Denoised signal of shape (batch, length).
|
|
361
|
+
- kl_loss: Scalar KL divergence loss.
|
|
362
|
+
"""
|
|
363
|
+
batch_size, length = coverage.shape
|
|
364
|
+
window_size = self.config.window_size
|
|
365
|
+
|
|
366
|
+
# Pad signal to be divisible by window_size
|
|
367
|
+
remainder = length % window_size
|
|
368
|
+
if remainder != 0:
|
|
369
|
+
pad_amount = window_size - remainder
|
|
370
|
+
coverage_padded = jnp.pad(coverage, ((0, 0), (0, pad_amount)), mode="edge")
|
|
371
|
+
else:
|
|
372
|
+
pad_amount = 0
|
|
373
|
+
coverage_padded = coverage
|
|
374
|
+
|
|
375
|
+
padded_length = coverage_padded.shape[1]
|
|
376
|
+
num_windows = padded_length // window_size
|
|
377
|
+
|
|
378
|
+
# Reshape to (batch * num_windows, window_size)
|
|
379
|
+
windows = coverage_padded.reshape(batch_size * num_windows, window_size)
|
|
380
|
+
|
|
381
|
+
# Log-transform for VAE input (coverage is count-like data)
|
|
382
|
+
vae_input = jnp.log1p(jnp.abs(windows))
|
|
383
|
+
|
|
384
|
+
# Get RNG key for sampling
|
|
385
|
+
rng_key = get_rng_key(self._rngs, "sample", fallback_seed=0)
|
|
386
|
+
|
|
387
|
+
# Run VAE forward pass
|
|
388
|
+
denoised_windows, mean, logvar, _ = self.vae_encoder(vae_input, rng_key)
|
|
389
|
+
|
|
390
|
+
# KL divergence via artifex
|
|
391
|
+
kl_loss = gaussian_kl_divergence(mean, logvar, reduction="sum")
|
|
392
|
+
|
|
393
|
+
# Reassemble signal
|
|
394
|
+
denoised_padded = denoised_windows.reshape(batch_size, padded_length)
|
|
395
|
+
|
|
396
|
+
# Remove padding
|
|
397
|
+
denoised = denoised_padded[:, :length]
|
|
398
|
+
|
|
399
|
+
return denoised, kl_loss
|
|
400
|
+
|
|
401
|
+
def _soft_local_max(
|
|
402
|
+
self, scores: jax.Array, window_size: int, temperature: jax.Array | float
|
|
403
|
+
) -> jax.Array:
|
|
404
|
+
"""Compute soft local maximum indicator.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
scores: Peak scores of shape (batch, length).
|
|
408
|
+
window_size: Window size for local maximum detection.
|
|
409
|
+
temperature: Temperature for softmax.
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
Soft local maximum indicators of shape (batch, length).
|
|
413
|
+
"""
|
|
414
|
+
_, length = scores.shape
|
|
415
|
+
half_window = window_size // 2
|
|
416
|
+
|
|
417
|
+
# Pad scores for windowed comparison
|
|
418
|
+
padded = jnp.pad(
|
|
419
|
+
scores, ((0, 0), (half_window, half_window)), mode="constant", constant_values=-jnp.inf
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
# Extract windows around each position
|
|
423
|
+
def extract_windows(padded_row: jax.Array) -> jax.Array:
|
|
424
|
+
indices = jnp.arange(length)
|
|
425
|
+
windows = jax.vmap(lambda i: jax.lax.dynamic_slice(padded_row, (i,), (window_size,)))(
|
|
426
|
+
indices
|
|
427
|
+
)
|
|
428
|
+
return windows
|
|
429
|
+
|
|
430
|
+
windows = jax.vmap(extract_windows)(padded) # (batch, length, window_size)
|
|
431
|
+
|
|
432
|
+
# Compute softmax over window to find local maximum
|
|
433
|
+
# The center position should have high weight if it's the maximum
|
|
434
|
+
center_idx = half_window
|
|
435
|
+
window_softmax = jax.nn.softmax(windows / temperature, axis=-1)
|
|
436
|
+
|
|
437
|
+
# Extract probability of center being the maximum
|
|
438
|
+
local_max_prob = window_softmax[:, :, center_idx]
|
|
439
|
+
|
|
440
|
+
return local_max_prob
|
|
441
|
+
|
|
442
|
+
def _compute_peak_boundaries(self, peak_probs: jax.Array) -> tuple[jax.Array, jax.Array]:
|
|
443
|
+
"""Compute soft peak boundaries.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
peak_probs: Peak probabilities of shape (batch, length).
|
|
447
|
+
|
|
448
|
+
Returns:
|
|
449
|
+
Tuple of (peak_starts, peak_ends) as soft indicators.
|
|
450
|
+
"""
|
|
451
|
+
# Compute gradient of peak probabilities
|
|
452
|
+
# Rising edge indicates start, falling edge indicates end
|
|
453
|
+
grad = jnp.diff(peak_probs, axis=-1, prepend=peak_probs[:, :1])
|
|
454
|
+
|
|
455
|
+
# Soft peak starts (positive gradient)
|
|
456
|
+
peak_starts = soft_ops.greater(grad, 0.0, softness=0.1)
|
|
457
|
+
|
|
458
|
+
# Soft peak ends (negative gradient)
|
|
459
|
+
peak_ends = soft_ops.less(grad, 0.0, softness=0.1)
|
|
460
|
+
|
|
461
|
+
return peak_starts, peak_ends
|
|
462
|
+
|
|
463
|
+
def apply(
|
|
464
|
+
self,
|
|
465
|
+
data: dict[str, Any],
|
|
466
|
+
state: dict[str, Any],
|
|
467
|
+
metadata: dict | None,
|
|
468
|
+
random_params: dict | None = None,
|
|
469
|
+
stats: dict | None = None,
|
|
470
|
+
) -> tuple[dict, dict, dict | None]:
|
|
471
|
+
"""Apply peak calling to coverage data.
|
|
472
|
+
|
|
473
|
+
When VAE denoising is enabled, the coverage signal is first denoised
|
|
474
|
+
through a VAE with Poisson decoder before peak detection.
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
data: Dictionary containing:
|
|
478
|
+
- 'coverage': Coverage signal of shape (batch, length) or (length,)
|
|
479
|
+
state: Operator state dictionary.
|
|
480
|
+
metadata: Optional metadata dictionary.
|
|
481
|
+
random_params: Optional random parameters.
|
|
482
|
+
stats: Optional statistics dictionary.
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
Tuple of (output_data, state, metadata) where output_data contains:
|
|
486
|
+
|
|
487
|
+
- 'coverage': Original coverage signal
|
|
488
|
+
- 'peak_scores': Raw peak detection scores
|
|
489
|
+
- 'peak_probabilities': Soft peak probabilities
|
|
490
|
+
- 'peak_summits': Soft summit indicators
|
|
491
|
+
- 'peak_starts': Soft peak start indicators
|
|
492
|
+
- 'peak_ends': Soft peak end indicators
|
|
493
|
+
- 'denoised_coverage': Denoised signal (only when VAE enabled)
|
|
494
|
+
- 'vae_kl_loss': KL divergence loss (only when VAE enabled)
|
|
495
|
+
"""
|
|
496
|
+
del random_params, stats # Unused
|
|
497
|
+
|
|
498
|
+
coverage = data["coverage"]
|
|
499
|
+
|
|
500
|
+
# Handle single sequence input
|
|
501
|
+
single_input = coverage.ndim == 1
|
|
502
|
+
if single_input:
|
|
503
|
+
coverage = coverage[None, :]
|
|
504
|
+
|
|
505
|
+
# Optional VAE denoising
|
|
506
|
+
vae_extras: dict[str, Any] = {}
|
|
507
|
+
if self._use_vae:
|
|
508
|
+
denoised, kl_loss = self._vae_denoise(coverage)
|
|
509
|
+
vae_extras["denoised_coverage"] = denoised
|
|
510
|
+
vae_extras["vae_kl_loss"] = kl_loss
|
|
511
|
+
# Use denoised signal for peak detection
|
|
512
|
+
cnn_input = denoised
|
|
513
|
+
else:
|
|
514
|
+
cnn_input = coverage
|
|
515
|
+
|
|
516
|
+
# Get peak scores from CNN
|
|
517
|
+
peak_scores = self.peak_cnn(cnn_input)
|
|
518
|
+
|
|
519
|
+
# Apply soft threshold
|
|
520
|
+
temperature = jnp.abs(self._temperature) + 1e-6
|
|
521
|
+
peak_probs = soft_ops.greater(peak_scores, self.threshold[...], softness=temperature)
|
|
522
|
+
|
|
523
|
+
# Find soft summits (local maxima)
|
|
524
|
+
summit_probs = self._soft_local_max(
|
|
525
|
+
peak_scores * peak_probs, # Weight by peak probability
|
|
526
|
+
self.summit_kernel_size,
|
|
527
|
+
temperature,
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
# Compute soft peak boundaries
|
|
531
|
+
peak_starts, peak_ends = self._compute_peak_boundaries(peak_probs)
|
|
532
|
+
|
|
533
|
+
# Remove batch dimension if input was single
|
|
534
|
+
if single_input:
|
|
535
|
+
peak_scores = peak_scores[0]
|
|
536
|
+
peak_probs = peak_probs[0]
|
|
537
|
+
summit_probs = summit_probs[0]
|
|
538
|
+
peak_starts = peak_starts[0]
|
|
539
|
+
peak_ends = peak_ends[0]
|
|
540
|
+
coverage = coverage[0]
|
|
541
|
+
for key in list(vae_extras.keys()):
|
|
542
|
+
if key == "denoised_coverage":
|
|
543
|
+
vae_extras[key] = vae_extras[key][0]
|
|
544
|
+
|
|
545
|
+
output_data = {
|
|
546
|
+
**data,
|
|
547
|
+
"peak_scores": peak_scores,
|
|
548
|
+
"peak_probabilities": peak_probs,
|
|
549
|
+
"peak_summits": summit_probs,
|
|
550
|
+
"peak_starts": peak_starts,
|
|
551
|
+
"peak_ends": peak_ends,
|
|
552
|
+
**vae_extras,
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return output_data, state, metadata
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Foundation model operators for DiffBio.
|
|
2
|
+
|
|
3
|
+
This module provides transformer-based sequence encoders and single-cell
|
|
4
|
+
foundation model infrastructure plus shared adapter contracts.
|
|
5
|
+
|
|
6
|
+
Operators:
|
|
7
|
+
TransformerSequenceEncoder: BERT-style transformer for sequence embedding
|
|
8
|
+
DifferentiableFoundationModel: Geneformer/scGPT-style foundation model
|
|
9
|
+
GeneTokenizer: Rank-value gene tokenization via soft sorting
|
|
10
|
+
|
|
11
|
+
Factory Functions:
|
|
12
|
+
create_dna_encoder: Create encoder for DNA sequences
|
|
13
|
+
create_rna_encoder: Create encoder for RNA sequences
|
|
14
|
+
create_foundation_model: Create registered foundation-model operator
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from diffbio.operators.foundation_models.contracts import (
|
|
18
|
+
AdapterMode,
|
|
19
|
+
FOUNDATION_BENCHMARK_COMPARISON_AXES,
|
|
20
|
+
FoundationArtifactSpec,
|
|
21
|
+
FoundationEmbeddingMixin,
|
|
22
|
+
FoundationEmbeddingOperatorConfig,
|
|
23
|
+
FoundationModelKind,
|
|
24
|
+
PoolingStrategy,
|
|
25
|
+
build_foundation_benchmark_metadata,
|
|
26
|
+
build_foundation_model_metadata,
|
|
27
|
+
create_foundation_model,
|
|
28
|
+
decode_foundation_model_metadata,
|
|
29
|
+
decode_foundation_text,
|
|
30
|
+
encode_foundation_text,
|
|
31
|
+
get_foundation_model_cls,
|
|
32
|
+
register_foundation_model,
|
|
33
|
+
)
|
|
34
|
+
from diffbio.operators.foundation_models.adapters import (
|
|
35
|
+
FoundationBenchmarkAdapter,
|
|
36
|
+
SequenceFoundationAdapter,
|
|
37
|
+
create_foundation_adapter,
|
|
38
|
+
get_foundation_adapter_cls,
|
|
39
|
+
register_foundation_adapter,
|
|
40
|
+
)
|
|
41
|
+
from diffbio.operators.foundation_models.embedding_probe import (
|
|
42
|
+
EmbeddingProbeConfig,
|
|
43
|
+
LinearEmbeddingProbe,
|
|
44
|
+
)
|
|
45
|
+
from diffbio.operators.foundation_models.experimental import (
|
|
46
|
+
EXPERIMENTAL_FOUNDATION_MODEL_NAMESPACE,
|
|
47
|
+
FOUNDATION_EXPERIMENTAL_CAPABILITIES,
|
|
48
|
+
FOUNDATION_EXPERIMENTAL_PROMOTION_CRITERIA,
|
|
49
|
+
ExperimentalFoundationCapability,
|
|
50
|
+
get_experimental_foundation_capability,
|
|
51
|
+
is_experimental_foundation_capability,
|
|
52
|
+
)
|
|
53
|
+
from diffbio.operators.foundation_models.foundation_model import (
|
|
54
|
+
DifferentiableFoundationModel,
|
|
55
|
+
FoundationModelConfig,
|
|
56
|
+
GeneTokenizer,
|
|
57
|
+
)
|
|
58
|
+
from diffbio.operators.foundation_models.precomputed import (
|
|
59
|
+
DNABERT2PrecomputedAdapter,
|
|
60
|
+
GeneformerPrecomputedAdapter,
|
|
61
|
+
NucleotideTransformerPrecomputedAdapter,
|
|
62
|
+
ProteinLMPrecomputedAdapter,
|
|
63
|
+
ScGPTPrecomputedAdapter,
|
|
64
|
+
SequencePrecomputedAdapter,
|
|
65
|
+
SingleCellPrecomputedAdapter,
|
|
66
|
+
)
|
|
67
|
+
from diffbio.operators.foundation_models.frozen import FrozenSequenceEncoderAdapter
|
|
68
|
+
from diffbio.operators.foundation_models.transformer_encoder import (
|
|
69
|
+
TransformerSequenceEncoder,
|
|
70
|
+
TransformerSequenceEncoderConfig,
|
|
71
|
+
create_dna_encoder,
|
|
72
|
+
create_rna_encoder,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
__all__ = [
|
|
76
|
+
"DifferentiableFoundationModel",
|
|
77
|
+
"DNABERT2PrecomputedAdapter",
|
|
78
|
+
"EmbeddingProbeConfig",
|
|
79
|
+
"EXPERIMENTAL_FOUNDATION_MODEL_NAMESPACE",
|
|
80
|
+
"FOUNDATION_EXPERIMENTAL_CAPABILITIES",
|
|
81
|
+
"FOUNDATION_EXPERIMENTAL_PROMOTION_CRITERIA",
|
|
82
|
+
"FoundationBenchmarkAdapter",
|
|
83
|
+
"ExperimentalFoundationCapability",
|
|
84
|
+
"FoundationModelConfig",
|
|
85
|
+
"GeneTokenizer",
|
|
86
|
+
"GeneformerPrecomputedAdapter",
|
|
87
|
+
"FrozenSequenceEncoderAdapter",
|
|
88
|
+
"LinearEmbeddingProbe",
|
|
89
|
+
"NucleotideTransformerPrecomputedAdapter",
|
|
90
|
+
"ProteinLMPrecomputedAdapter",
|
|
91
|
+
"ScGPTPrecomputedAdapter",
|
|
92
|
+
"SequenceFoundationAdapter",
|
|
93
|
+
"SequencePrecomputedAdapter",
|
|
94
|
+
"AdapterMode",
|
|
95
|
+
"FOUNDATION_BENCHMARK_COMPARISON_AXES",
|
|
96
|
+
"FoundationArtifactSpec",
|
|
97
|
+
"FoundationEmbeddingMixin",
|
|
98
|
+
"FoundationEmbeddingOperatorConfig",
|
|
99
|
+
"FoundationModelKind",
|
|
100
|
+
"PoolingStrategy",
|
|
101
|
+
"SingleCellPrecomputedAdapter",
|
|
102
|
+
"TransformerSequenceEncoder",
|
|
103
|
+
"TransformerSequenceEncoderConfig",
|
|
104
|
+
"build_foundation_benchmark_metadata",
|
|
105
|
+
"build_foundation_model_metadata",
|
|
106
|
+
"create_foundation_adapter",
|
|
107
|
+
"create_foundation_model",
|
|
108
|
+
"create_dna_encoder",
|
|
109
|
+
"decode_foundation_model_metadata",
|
|
110
|
+
"decode_foundation_text",
|
|
111
|
+
"encode_foundation_text",
|
|
112
|
+
"get_foundation_adapter_cls",
|
|
113
|
+
"get_foundation_model_cls",
|
|
114
|
+
"create_rna_encoder",
|
|
115
|
+
"get_experimental_foundation_capability",
|
|
116
|
+
"register_foundation_adapter",
|
|
117
|
+
"register_foundation_model",
|
|
118
|
+
"is_experimental_foundation_capability",
|
|
119
|
+
]
|