SCENT reimplemented in Python with JAX-powered acceleration for high-performance computing.
SCENT is a statistical framework for mapping enhancer-gene regulatory relationships using single-cell multi-omics data that integrates:
- RNA-seq: Gene expression levels
- ATAC-seq: Chromatin accessibility at regulatory regions
SCENT uses Generalized Linear Models (GLMs) to model the relationship between gene expression and chromatin accessibility:
Gene Expression ~ Intercept + Covariates + Peak Accessibility
Key components:
- Response: RNA-seq counts (Poisson or Negative Binomial distribution)
- Predictor: Binarized ATAC-seq signal (1 if accessible, 0 if not)
- Covariates: Batch effects, sequencing depth, etc.
- Inference: Bootstrap testing for robust p-value estimation
SCENT identifies significant peak-gene associations in a cell-type specific manner, accounting for technical confounders and biological variability.
Reference:
- Sakaue et al. "Tissue-specific enhancer-gene maps from multimodal single-cell data identify causal disease alleles" Nature Genetics (2024)
- Original R implementation: SCENT on GitHub
pySCENT reimplements SCENT in Python using JAX, providing substantial performance improvements while maintaining statistical consistency with the original implementation.
- GPU/TPU Acceleration: JAX-based computation enables hardware acceleration
- Multi-GPU Support: Automatically shards gene-peak pairs across multiple GPUs
- High Consistency: Validated against original SCENT
- Adaptive Bootstrap Strategy: Intelligent sampling for efficiency
- Vectorized Parallelization:
jax.vmapfor simultaneous bootstrap execution
Using uv (recommended):
git clone https://github.com/Antidington/pySCENT.git
cd pySCENT
uv syncUsing pip:
git clone https://github.com/Antidington/pySCENT.git
cd pySCENT
pip install -e .Core dependencies (from pyproject.toml):
jaxqtl- JAX-based QTL mapping librarylineax≥ 0.0.8 - Linear algebra operationsqtl≥ 0.1.10 - QTL utilities
pySCENT runs on CPU by default — no extra configuration needed. To enable GPU or TPU acceleration, install the corresponding JAX build:
NVIDIA GPU (CUDA 12):
# uv
uv pip install -U "jax[cuda12]"
# pip
pip install -U "jax[cuda12]"Apple Silicon (Metal):
pip install jax-metalGoogle Cloud TPU:
pip install -U "jax[tpu]" -f https://storage.googleapis.com/jax-releases/libtpu_releases.htmlVerify which backend is active:
import jax
print(jax.default_backend()) # "cpu", "gpu", or "tpu"
print(jax.devices()) # list available devicespySCENT does not require any code changes to switch backends — JAX automatically dispatches to the available accelerator. For more details, see the JAX installation guide.
By default JAX pre-allocates 75% of GPU memory. For large datasets set these environment variables before importing pySCENT:
# Disable preallocation (allocate on demand)
export XLA_PYTHON_CLIENT_PREALLOCATE=false
# Or cap the fraction (e.g. 40%)
export XLA_PYTHON_CLIENT_MEM_FRACTION=0.4import jax.random as random
from pyscent import io
# Create SCENT object from input files
scent_obj = io.create_scent_object(
rna_matrix="data/rna_matrix.csv", # Gene × Cell expression matrix
atac_matrix="data/atac_matrix.csv", # Peak × Cell accessibility matrix
meta_data="data/metadata.csv", # Cell metadata
peak_info="data/peak_info.csv", # Gene-peak pairs to test
covariates=["batch", "n_counts"], # Covariates to adjust for
celltype_col="cell_type" # Column name for cell type
)
# CPU (default) — control thread count with ncores
results = scent_obj.run_scent(
celltype="T_cell",
regr="poisson",
bootstrap_samples=100,
min_nonzero_frac=0.05,
ncores=4,
key=random.PRNGKey(42),
)
# Single GPU — prints device info, runs on GPU 0
results = scent_obj.run_scent(
celltype="T_cell",
gpu_devices=[0],
max_batch_size=5000, # lower if GPU OOM at large bootstrap stages
)
# Multi-GPU — automatically shards gene-peak pairs across GPUs 0 and 1
results = scent_obj.run_scent(
celltype="T_cell",
gpu_devices=[0, 1],
max_batch_size=5000,
)
# Save results
io.write_results(results, "output/scent_results.csv")
# Display top associations
for res in sorted(results, key=lambda x: x.boot_basic_p)[:5]:
print(f"Gene: {res.gene}, Peak: {res.peak}, "
f"Beta: {res.beta:.3f}, P-value: {res.boot_basic_p:.2e}")When a GPU backend is used, pySCENT prints the detected devices to stdout:
Detected 3 GPU device(s):
[0] NVIDIA A100-SXM4-80GB | VRAM: 81920 MiB | Load: 12%
[1] NVIDIA A100-SXM4-80GB | VRAM: 81920 MiB | Load: 0%
[2] NVIDIA A100-SXM4-80GB | VRAM: 81920 MiB | Load: 0%
Using GPU [0]
gpu_devices |
Behaviour |
|---|---|
None (default) |
Falls back to device argument ("auto" → GPU 0 if available) |
[0] |
Single-GPU mode on GPU 0 |
[0, 1, 2] |
Multi-GPU mode — pairs sharded round-robin, one subprocess per GPU |
Bootstrap stages up to 50,000 replicates are processed in batches (max_batch_size=5000 by default) to avoid GPU OOM. Each batch is a separate jax.vmap call; results are accumulated before computing the p-value.
| Stage | Replicates | Batches (default) |
|---|---|---|
| 1 | 100 | 1 |
| 2 | 500 | 1 |
| 3 | 2,500 | 1 |
| 4 | 25,000 | 5 |
| 5 | 50,000 | 10 |
If OOM still occurs, reduce max_batch_size:
results = scent_obj.run_scent(celltype="T_cell", gpu_devices=[0], max_batch_size=1000)RNA and ATAC matrices are stored internally as sparse matrices (scipy CSR, analogous to R's dgCMatrix). The following input formats are supported:
| Format | Extension | Row/Column Names |
|---|---|---|
| CSV | .csv |
First column = row names, header = column names |
| TSV | .tsv |
Same as CSV, tab-separated |
| H5AD | .h5ad |
var_names = row names, obs_names = column names (auto-transposed from cells×genes to genes×cells) |
| MTX | .mtx, .mtx.gz |
Auto-loaded from companion {stem}_genes.tsv / {stem}_features.tsv and {stem}_barcodes.tsv files if present alongside the MTX |
RNA matrix — genes × cells, raw counts (no normalization):
Cell1 Cell2 Cell3 ...
Gene1 10 5 8 ...
Gene2 0 3 12 ...
ATAC matrix — peaks × cells, raw counts:
Cell1 Cell2 Cell3 ...
chr1:1000-2000 5 0 3 ...
chr2:5000-6000 8 10 2 ...
Metadata — one row per cell, must contain a cell (or cell_id) column:
cell_id,cell_type,batch,n_counts
Cell1,T_cell,batch1,5000
Cell2,B_cell,batch1,4500
Peak info — gene-peak pairs to test (first two columns used):
gene,peak
Gene1,chr1:1000-2000
Gene2,chr2:5000-6000
Results are saved as CSV with columns:
gene: Gene namepeak: Peak coordinatesbeta: Regression coefficient (effect size)se: Standard errorz: Z-scorep: Wald test p-valueboot_basic_p: Bootstrap p-value (recommended)