gatac 0.1.0__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.
- gatac-0.1.0/.gitignore +28 -0
- gatac-0.1.0/LICENSE +28 -0
- gatac-0.1.0/PKG-INFO +227 -0
- gatac-0.1.0/README.md +175 -0
- gatac-0.1.0/gatac/__init__.py +128 -0
- gatac-0.1.0/gatac/_files.py +32 -0
- gatac-0.1.0/gatac/_progress.py +33 -0
- gatac-0.1.0/gatac/_rapids.py +22 -0
- gatac-0.1.0/gatac/_readers.py +140 -0
- gatac-0.1.0/gatac/_workers.py +31 -0
- gatac-0.1.0/gatac/cli.py +771 -0
- gatac-0.1.0/gatac/dr/__init__.py +26 -0
- gatac-0.1.0/gatac/dr/_clustering.py +573 -0
- gatac-0.1.0/gatac/dr/_gpu.py +340 -0
- gatac-0.1.0/gatac/dr/_stream.py +267 -0
- gatac-0.1.0/gatac/dr/lsi.py +2175 -0
- gatac-0.1.0/gatac/dr/spectral.py +581 -0
- gatac-0.1.0/gatac/gpu.py +78 -0
- gatac-0.1.0/gatac/io/__init__.py +22 -0
- gatac-0.1.0/gatac/io/_footer.py +187 -0
- gatac-0.1.0/gatac/io/convert.py +682 -0
- gatac-0.1.0/gatac/io/fasta.py +250 -0
- gatac-0.1.0/gatac/io/fragments.py +1027 -0
- gatac-0.1.0/gatac/io/gpu_convert.py +681 -0
- gatac-0.1.0/gatac/motif/__init__.py +40 -0
- gatac-0.1.0/gatac/motif/_bias_matching.py +244 -0
- gatac-0.1.0/gatac/motif/_gsea.py +643 -0
- gatac-0.1.0/gatac/motif/chromvar.py +1150 -0
- gatac-0.1.0/gatac/motif/motif.py +2208 -0
- gatac-0.1.0/gatac/peak/__init__.py +18 -0
- gatac-0.1.0/gatac/peak/_macs.py +836 -0
- gatac-0.1.0/gatac/peak/markers.py +529 -0
- gatac-0.1.0/gatac/peak/peaks.py +1244 -0
- gatac-0.1.0/gatac/pl/__init__.py +10 -0
- gatac-0.1.0/gatac/pl/footprint.py +139 -0
- gatac-0.1.0/gatac/pl/fragments.py +77 -0
- gatac-0.1.0/gatac/pl/metrics.py +113 -0
- gatac-0.1.0/gatac/pl/motifs.py +68 -0
- gatac-0.1.0/gatac/pp/__init__.py +45 -0
- gatac-0.1.0/gatac/pp/_combine.py +179 -0
- gatac-0.1.0/gatac/pp/_utils.py +35 -0
- gatac-0.1.0/gatac/pp/amulet.py +538 -0
- gatac-0.1.0/gatac/pp/features.py +310 -0
- gatac-0.1.0/gatac/pp/filter.py +279 -0
- gatac-0.1.0/gatac/pp/gene.py +808 -0
- gatac-0.1.0/gatac/pp/genescore.py +709 -0
- gatac-0.1.0/gatac/pp/genome.py +144 -0
- gatac-0.1.0/gatac/pp/metrics.py +335 -0
- gatac-0.1.0/gatac/pp/process.py +88 -0
- gatac-0.1.0/gatac/pp/tile.py +923 -0
- gatac-0.1.0/gatac/pp/tile_scan.py +373 -0
- gatac-0.1.0/gatac/py.typed +0 -0
- gatac-0.1.0/gatac/tobias/__init__.py +21 -0
- gatac-0.1.0/gatac/tobias/_minpack_relu.cu +528 -0
- gatac-0.1.0/gatac/tobias/bindetect.py +1521 -0
- gatac-0.1.0/gatac/tobias/footprint.py +996 -0
- gatac-0.1.0/hatch.toml +120 -0
- gatac-0.1.0/pyproject.toml +171 -0
- gatac-0.1.0/tests/api/test_dr.py +261 -0
- gatac-0.1.0/tests/api/test_motif.py +254 -0
- gatac-0.1.0/tests/api/test_peak.py +272 -0
- gatac-0.1.0/tests/api/test_pl.py +34 -0
- gatac-0.1.0/tests/api/test_pp.py +115 -0
- gatac-0.1.0/tests/conftest.py +145 -0
- gatac-0.1.0/tests/regression/test_amulet_poisson.py +28 -0
- gatac-0.1.0/tests/regression/test_bg_peaks_random_state.py +48 -0
- gatac-0.1.0/tests/regression/test_bindetect_sites.py +149 -0
- gatac-0.1.0/tests/regression/test_cli.py +102 -0
- gatac-0.1.0/tests/regression/test_enrichment_threshold.py +61 -0
- gatac-0.1.0/tests/regression/test_extensions.py +53 -0
- gatac-0.1.0/tests/regression/test_fasta.py +134 -0
- gatac-0.1.0/tests/regression/test_fragment_files.py +857 -0
- gatac-0.1.0/tests/regression/test_gpu.py +72 -0
- gatac-0.1.0/tests/regression/test_gpu_convert.py +274 -0
- gatac-0.1.0/tests/regression/test_gsea_scoring.py +24 -0
- gatac-0.1.0/tests/regression/test_lsi_determinism.py +166 -0
- gatac-0.1.0/tests/regression/test_motif_regression.py +60 -0
- gatac-0.1.0/tests/regression/test_package.py +66 -0
- gatac-0.1.0/tests/regression/test_readers.py +127 -0
- gatac-0.1.0/tests/regression/test_tobias_pipeline.py +194 -0
gatac-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
|
|
3
|
+
data/*
|
|
4
|
+
refs/*
|
|
5
|
+
uv.lock
|
|
6
|
+
pixi.lock
|
|
7
|
+
*.pyc
|
|
8
|
+
*.log
|
|
9
|
+
AGENTS.md
|
|
10
|
+
GEMINI.md
|
|
11
|
+
|
|
12
|
+
*.parquet
|
|
13
|
+
*.csv
|
|
14
|
+
*.h5ad
|
|
15
|
+
.venv
|
|
16
|
+
|
|
17
|
+
__pycache__/
|
|
18
|
+
|
|
19
|
+
# doc build related
|
|
20
|
+
_build/
|
|
21
|
+
docs/_build/
|
|
22
|
+
docs/api/generated/
|
|
23
|
+
|
|
24
|
+
# llm
|
|
25
|
+
agents/
|
|
26
|
+
|
|
27
|
+
# CI test outputs
|
|
28
|
+
test-data/
|
gatac-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Louis Faure
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
gatac-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gatac
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: GPU-accelerated ATAC-seq fragment processing and tile matrix generation
|
|
5
|
+
Project-URL: Documentation, https://gatac.readthedocs.io
|
|
6
|
+
Project-URL: Source, https://github.com/scverse/gatac
|
|
7
|
+
Project-URL: Issues, https://github.com/scverse/gatac/issues
|
|
8
|
+
Project-URL: Changelog, https://gatac.readthedocs.io/en/latest/release-notes/index.html
|
|
9
|
+
Author: Louis Faure
|
|
10
|
+
License-Expression: BSD-3-Clause
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ATAC-seq,GPU,RAPIDS,chromatin accessibility,single-cell
|
|
13
|
+
Classifier: Environment :: GPU :: NVIDIA CUDA :: 12
|
|
14
|
+
Classifier: Environment :: GPU :: NVIDIA CUDA :: 13
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
20
|
+
Requires-Python: >=3.13
|
|
21
|
+
Requires-Dist: anndata>=0.10.8
|
|
22
|
+
Requires-Dist: duckdb>=2.0
|
|
23
|
+
Requires-Dist: h5py>=3.11
|
|
24
|
+
Requires-Dist: matplotlib>=3.10
|
|
25
|
+
Requires-Dist: numba>=0.60
|
|
26
|
+
Requires-Dist: numpy>=2
|
|
27
|
+
Requires-Dist: pandas>=2.3
|
|
28
|
+
Requires-Dist: pyarrow>=22.0.0
|
|
29
|
+
Requires-Dist: scanpy>=1.11.5
|
|
30
|
+
Requires-Dist: scikit-learn>=1.6
|
|
31
|
+
Requires-Dist: scipy>=1.13
|
|
32
|
+
Requires-Dist: seaborn>=0.13.2
|
|
33
|
+
Requires-Dist: statsmodels>=0.14
|
|
34
|
+
Requires-Dist: tqdm>=4.66.0
|
|
35
|
+
Provides-Extra: archr-compat
|
|
36
|
+
Requires-Dist: annoy>=1.17; extra == 'archr-compat'
|
|
37
|
+
Provides-Extra: bigwig
|
|
38
|
+
Requires-Dist: pybigwig>=0.3.22; extra == 'bigwig'
|
|
39
|
+
Provides-Extra: rapids-cu12
|
|
40
|
+
Requires-Dist: cudf-cu12>=26.02; extra == 'rapids-cu12'
|
|
41
|
+
Requires-Dist: cugraph-cu12>=26.02; extra == 'rapids-cu12'
|
|
42
|
+
Requires-Dist: cuml-cu12>=26.02; extra == 'rapids-cu12'
|
|
43
|
+
Requires-Dist: cupy-cuda12x[ctk]>=14; extra == 'rapids-cu12'
|
|
44
|
+
Requires-Dist: nvidia-nvcomp-cu12>=5.1; extra == 'rapids-cu12'
|
|
45
|
+
Provides-Extra: rapids-cu13
|
|
46
|
+
Requires-Dist: cudf-cu13>=26.02; extra == 'rapids-cu13'
|
|
47
|
+
Requires-Dist: cugraph-cu13>=26.02; extra == 'rapids-cu13'
|
|
48
|
+
Requires-Dist: cuml-cu13>=26.02; extra == 'rapids-cu13'
|
|
49
|
+
Requires-Dist: cupy-cuda13x[ctk]>=14; extra == 'rapids-cu13'
|
|
50
|
+
Requires-Dist: nvidia-nvcomp-cu13>=5.1; extra == 'rapids-cu13'
|
|
51
|
+
Description-Content-Type: text/markdown
|
|
52
|
+
|
|
53
|
+
[](https://github.com/scverse/gatac/actions/workflows/test-gpu.yml)
|
|
54
|
+
[](https://codecov.io/gh/scverse/gatac)
|
|
55
|
+
|
|
56
|
+
# GATAC: GPU-Accelerated scATACseq Analysis
|
|
57
|
+
|
|
58
|
+
GATAC is a GPU-accelerated toolkit for end-to-end ATAC-seq data processing.
|
|
59
|
+
Starting from raw fragment files, it produces analysis-ready sparse matrices
|
|
60
|
+
(tile, peak, and gene activity) that integrate directly with the
|
|
61
|
+
[scverse](https://scverse.org) ecosystem — AnnData, Scanpy, and SnapATAC2.
|
|
62
|
+
Computations are offloaded to the GPU via [RAPIDS cuDF](https://rapids.ai),
|
|
63
|
+
[CuPy](https://cupy.dev), and [cuML](https://docs.rapids.ai/api/cuml/stable/),
|
|
64
|
+
delivering 10–50× speedups over CPU-only workflows on typical single-cell
|
|
65
|
+
datasets.
|
|
66
|
+
|
|
67
|
+
GATAC workflow takes advantage of [Apache Parquet](https://parquet.apache.org) file format. Parquet's columnar layout and
|
|
68
|
+
built-in compression make it an ideal staging format for GPU-based pipelines:
|
|
69
|
+
columns are read independently, so only the genomic coordinates actually needed
|
|
70
|
+
for a given operation are transferred to device memory. RAPIDS cuDF can read
|
|
71
|
+
Parquet directly into GPU memory with zero CPU round-trips, enabling streaming
|
|
72
|
+
aggregation over datasets that far exceed the size of available GPU RAM.
|
|
73
|
+
|
|
74
|
+
## Installation
|
|
75
|
+
|
|
76
|
+
GATAC needs Linux and an NVIDIA GPU. Install it with the extra of your CUDA
|
|
77
|
+
version (`nvidia-smi` reports it), which installs RAPIDS and CuPy:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install 'gatac[rapids-cu12]' # or 'gatac[rapids-cu13]'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
From a clone of this repository: `uv sync --extra rapids-cu12`. See the
|
|
84
|
+
[installation guide](https://gatac.readthedocs.io/en/latest/installation.html)
|
|
85
|
+
for the optional extras and conda.
|
|
86
|
+
|
|
87
|
+
## CLI Usage
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Convert TSV.GZ to Parquet
|
|
91
|
+
gatac convert fragments.tsv.gz
|
|
92
|
+
|
|
93
|
+
# Several samples: name each sample's cells by sample (sample1_<barcode>),
|
|
94
|
+
# so that they stay distinct in a combined matrix. {sample} is written as is:
|
|
95
|
+
# gatac fills it in for each file (samples/s1.tsv.gz -> s1_, ...)
|
|
96
|
+
gatac convert "samples/*.tsv.gz" --output-dir parquets/ --barcode-prefix "{sample}_"
|
|
97
|
+
|
|
98
|
+
# Cell Ranger outputs, all named atac_fragments.tsv.gz: prefix the files too
|
|
99
|
+
# ({sample} is then the run directory: runs/A/outs/... -> A_)
|
|
100
|
+
gatac convert "runs/*/outs/atac_fragments.tsv.gz" --output-dir parquets/ \
|
|
101
|
+
--file-prefix "{sample}_" --barcode-prefix "{sample}_"
|
|
102
|
+
|
|
103
|
+
# Set (or change) the barcode prefix of a converted file, in place: only the
|
|
104
|
+
# file footer is rewritten, in milliseconds
|
|
105
|
+
gatac rename sample1.parquet --barcode-prefix sample1_
|
|
106
|
+
|
|
107
|
+
# Compute quality metrics (TSSe, etc.)
|
|
108
|
+
gatac metrics fragments.parquet -g annotations.gtf -o fragments_metrics.csv
|
|
109
|
+
|
|
110
|
+
# Compute quality metrics (TSSe, etc.)
|
|
111
|
+
gatac metrics fragments.parquet -g annotations.gtf --batch-size 64
|
|
112
|
+
|
|
113
|
+
# Filter fragments by minimum fragment count
|
|
114
|
+
gatac filter fragments.parquet -m 500 -o fragments_filtered.parquet
|
|
115
|
+
|
|
116
|
+
# Filter fragments using metrics and quality threshold
|
|
117
|
+
gatac filter fragments.parquet --metrics metrics.csv --filter "tsse_score > 5" -o fragments_filtered.parquet
|
|
118
|
+
|
|
119
|
+
# Filter fragments with genome-based chromosome filtering
|
|
120
|
+
gatac filter fragments.parquet -g hg38 -m 500 -o fragments_filtered.parquet
|
|
121
|
+
|
|
122
|
+
# Generate tile matrix (m=min_unique_fragments)
|
|
123
|
+
gatac tile fragments.parquet -g hg38 -t 500 -m 100
|
|
124
|
+
|
|
125
|
+
# Generate tile matrix with quality filtering using metrics CSV
|
|
126
|
+
gatac tile fragments.parquet -g hg38 --metrics metrics.csv --filter "tsse_score > 5"
|
|
127
|
+
|
|
128
|
+
# Generate gene activity matrix
|
|
129
|
+
gatac gene fragments.parquet -g annotations.gtf
|
|
130
|
+
|
|
131
|
+
# Generate gene activity matrix with filtering
|
|
132
|
+
gatac gene fragments.parquet -g annotations.gtf --metrics metrics.csv --filter "tsse_score > 5"
|
|
133
|
+
|
|
134
|
+
# Generate ArchR-style distance-weighted gene activity SCORE matrix
|
|
135
|
+
gatac genescore fragments.parquet -g annotations.gtf
|
|
136
|
+
|
|
137
|
+
# Gene score with a custom gene model (function of distance x to the TSS)
|
|
138
|
+
gatac genescore fragments.parquet -g annotations.gtf --gene-model "exp(-abs(x)/10000) + exp(-1)"
|
|
139
|
+
|
|
140
|
+
# One tile matrix of several samples (each with its own barcode prefix),
|
|
141
|
+
# most accessible tiles only
|
|
142
|
+
gatac tile sample1.parquet sample2.parquet -g hg38 -n 500000 -o combined.h5ad
|
|
143
|
+
|
|
144
|
+
# One gene matrix (or gene score matrix) of several samples
|
|
145
|
+
gatac gene sample1.parquet sample2.parquet -g annotations.gtf -o genes.h5ad
|
|
146
|
+
gatac genescore sample1.parquet sample2.parquet -g annotations.gtf -o gene_scores.h5ad
|
|
147
|
+
|
|
148
|
+
# Merge h5ad files (file-by-file)
|
|
149
|
+
gatac combine sample1.h5ad sample2.h5ad -o combined.h5ad
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Python API
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
import gatac as ga
|
|
156
|
+
|
|
157
|
+
# Convert to parquet
|
|
158
|
+
ga.io.make_parquet("fragments.tsv.gz")
|
|
159
|
+
|
|
160
|
+
# Name the sample's cells sample1_<barcode>, in place (footer only)
|
|
161
|
+
ga.io.set_barcode_prefix("sample1.parquet", "sample1_")
|
|
162
|
+
|
|
163
|
+
# Compute metrics
|
|
164
|
+
metrics = ga.pp.compute_metrics("fragments.parquet", "annotations.gtf")
|
|
165
|
+
|
|
166
|
+
# Create tile matrix (pre-filtered by unique fragments)
|
|
167
|
+
adata = ga.pp.make_tile_matrix(
|
|
168
|
+
"fragments.parquet",
|
|
169
|
+
chrom_sizes="hg38",
|
|
170
|
+
min_fragments_per_cell=500,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Create tile matrix using pre-computed metrics for filtering
|
|
174
|
+
# metrics can be a path to a CSV or a cuDF DataFrame
|
|
175
|
+
adata = ga.pp.make_tile_matrix("fragments.parquet", chrom_sizes="hg38", metrics=metrics, filter_query="tsse_score > 5")
|
|
176
|
+
|
|
177
|
+
# Create gene activity matrix
|
|
178
|
+
adata_gene = ga.pp.make_gene_matrix(
|
|
179
|
+
"fragments.parquet", gene_anno="annotations.gtf", upstream=2000, include_gene_body=True
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Create gene activity matrix with filtering
|
|
183
|
+
adata_gene = ga.pp.make_gene_matrix(
|
|
184
|
+
"fragments.parquet", gene_anno="annotations.gtf", metrics=metrics, filter_query="tsse_score > 5"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Create ArchR-style distance-weighted gene activity SCORE matrix
|
|
188
|
+
# (faithful GPU port of ArchR addGeneScoreMatrix; defaults match ArchR)
|
|
189
|
+
adata_score = ga.pp.make_gene_score_matrix(
|
|
190
|
+
"fragments.parquet",
|
|
191
|
+
gene_anno="annotations.gtf", # GTF/GFF, or CSV: symbol,seqnames,start,end,strand
|
|
192
|
+
gene_model="exp(-abs(x)/5000) + exp(-1)",
|
|
193
|
+
tile_size=500,
|
|
194
|
+
extend_upstream=(1000, 100000),
|
|
195
|
+
extend_downstream=(1000, 100000),
|
|
196
|
+
use_gene_boundaries=True,
|
|
197
|
+
scale_to=10000,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Filter fragments by minimum fragment count
|
|
201
|
+
ga.pp.filter_fragments("fragments.parquet", min_fragments_per_cell=500)
|
|
202
|
+
|
|
203
|
+
# Filter fragments using metrics with quality threshold
|
|
204
|
+
ga.pp.filter_fragments("fragments.parquet", metrics=metrics, filter_query="tsse_score > 5")
|
|
205
|
+
|
|
206
|
+
# Filter multiple files with genome-based chromosome filtering
|
|
207
|
+
ga.pp.filter_fragments(
|
|
208
|
+
["sample1.parquet", "sample2.parquet"],
|
|
209
|
+
metrics=metrics,
|
|
210
|
+
filter_query="tsse_score > 5 and n_unique > 1000",
|
|
211
|
+
chrom_sizes="hg38",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Select features (single AnnData)
|
|
215
|
+
ga.pp.select_features(adata, n_features=50000)
|
|
216
|
+
|
|
217
|
+
# One tile matrix of several samples, most accessible tiles only
|
|
218
|
+
ga.pp.make_tile_matrix(
|
|
219
|
+
["sample1.parquet", "sample2.parquet"], chrom_sizes="hg38", n_features=500000, output_path="combined.h5ad"
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# One gene matrix of several samples, written as it is built
|
|
223
|
+
ga.pp.make_gene_matrix(["sample1.parquet", "sample2.parquet"], gene_anno="annotations.gtf", output_path="genes.h5ad")
|
|
224
|
+
|
|
225
|
+
# Merge multiple AnnData files
|
|
226
|
+
ga.pp.combine(input_paths=["sample1.h5ad", "sample2.h5ad"], output_path="merged.h5ad")
|
|
227
|
+
```
|
gatac-0.1.0/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
[](https://github.com/scverse/gatac/actions/workflows/test-gpu.yml)
|
|
2
|
+
[](https://codecov.io/gh/scverse/gatac)
|
|
3
|
+
|
|
4
|
+
# GATAC: GPU-Accelerated scATACseq Analysis
|
|
5
|
+
|
|
6
|
+
GATAC is a GPU-accelerated toolkit for end-to-end ATAC-seq data processing.
|
|
7
|
+
Starting from raw fragment files, it produces analysis-ready sparse matrices
|
|
8
|
+
(tile, peak, and gene activity) that integrate directly with the
|
|
9
|
+
[scverse](https://scverse.org) ecosystem — AnnData, Scanpy, and SnapATAC2.
|
|
10
|
+
Computations are offloaded to the GPU via [RAPIDS cuDF](https://rapids.ai),
|
|
11
|
+
[CuPy](https://cupy.dev), and [cuML](https://docs.rapids.ai/api/cuml/stable/),
|
|
12
|
+
delivering 10–50× speedups over CPU-only workflows on typical single-cell
|
|
13
|
+
datasets.
|
|
14
|
+
|
|
15
|
+
GATAC workflow takes advantage of [Apache Parquet](https://parquet.apache.org) file format. Parquet's columnar layout and
|
|
16
|
+
built-in compression make it an ideal staging format for GPU-based pipelines:
|
|
17
|
+
columns are read independently, so only the genomic coordinates actually needed
|
|
18
|
+
for a given operation are transferred to device memory. RAPIDS cuDF can read
|
|
19
|
+
Parquet directly into GPU memory with zero CPU round-trips, enabling streaming
|
|
20
|
+
aggregation over datasets that far exceed the size of available GPU RAM.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
GATAC needs Linux and an NVIDIA GPU. Install it with the extra of your CUDA
|
|
25
|
+
version (`nvidia-smi` reports it), which installs RAPIDS and CuPy:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install 'gatac[rapids-cu12]' # or 'gatac[rapids-cu13]'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
From a clone of this repository: `uv sync --extra rapids-cu12`. See the
|
|
32
|
+
[installation guide](https://gatac.readthedocs.io/en/latest/installation.html)
|
|
33
|
+
for the optional extras and conda.
|
|
34
|
+
|
|
35
|
+
## CLI Usage
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Convert TSV.GZ to Parquet
|
|
39
|
+
gatac convert fragments.tsv.gz
|
|
40
|
+
|
|
41
|
+
# Several samples: name each sample's cells by sample (sample1_<barcode>),
|
|
42
|
+
# so that they stay distinct in a combined matrix. {sample} is written as is:
|
|
43
|
+
# gatac fills it in for each file (samples/s1.tsv.gz -> s1_, ...)
|
|
44
|
+
gatac convert "samples/*.tsv.gz" --output-dir parquets/ --barcode-prefix "{sample}_"
|
|
45
|
+
|
|
46
|
+
# Cell Ranger outputs, all named atac_fragments.tsv.gz: prefix the files too
|
|
47
|
+
# ({sample} is then the run directory: runs/A/outs/... -> A_)
|
|
48
|
+
gatac convert "runs/*/outs/atac_fragments.tsv.gz" --output-dir parquets/ \
|
|
49
|
+
--file-prefix "{sample}_" --barcode-prefix "{sample}_"
|
|
50
|
+
|
|
51
|
+
# Set (or change) the barcode prefix of a converted file, in place: only the
|
|
52
|
+
# file footer is rewritten, in milliseconds
|
|
53
|
+
gatac rename sample1.parquet --barcode-prefix sample1_
|
|
54
|
+
|
|
55
|
+
# Compute quality metrics (TSSe, etc.)
|
|
56
|
+
gatac metrics fragments.parquet -g annotations.gtf -o fragments_metrics.csv
|
|
57
|
+
|
|
58
|
+
# Compute quality metrics (TSSe, etc.)
|
|
59
|
+
gatac metrics fragments.parquet -g annotations.gtf --batch-size 64
|
|
60
|
+
|
|
61
|
+
# Filter fragments by minimum fragment count
|
|
62
|
+
gatac filter fragments.parquet -m 500 -o fragments_filtered.parquet
|
|
63
|
+
|
|
64
|
+
# Filter fragments using metrics and quality threshold
|
|
65
|
+
gatac filter fragments.parquet --metrics metrics.csv --filter "tsse_score > 5" -o fragments_filtered.parquet
|
|
66
|
+
|
|
67
|
+
# Filter fragments with genome-based chromosome filtering
|
|
68
|
+
gatac filter fragments.parquet -g hg38 -m 500 -o fragments_filtered.parquet
|
|
69
|
+
|
|
70
|
+
# Generate tile matrix (m=min_unique_fragments)
|
|
71
|
+
gatac tile fragments.parquet -g hg38 -t 500 -m 100
|
|
72
|
+
|
|
73
|
+
# Generate tile matrix with quality filtering using metrics CSV
|
|
74
|
+
gatac tile fragments.parquet -g hg38 --metrics metrics.csv --filter "tsse_score > 5"
|
|
75
|
+
|
|
76
|
+
# Generate gene activity matrix
|
|
77
|
+
gatac gene fragments.parquet -g annotations.gtf
|
|
78
|
+
|
|
79
|
+
# Generate gene activity matrix with filtering
|
|
80
|
+
gatac gene fragments.parquet -g annotations.gtf --metrics metrics.csv --filter "tsse_score > 5"
|
|
81
|
+
|
|
82
|
+
# Generate ArchR-style distance-weighted gene activity SCORE matrix
|
|
83
|
+
gatac genescore fragments.parquet -g annotations.gtf
|
|
84
|
+
|
|
85
|
+
# Gene score with a custom gene model (function of distance x to the TSS)
|
|
86
|
+
gatac genescore fragments.parquet -g annotations.gtf --gene-model "exp(-abs(x)/10000) + exp(-1)"
|
|
87
|
+
|
|
88
|
+
# One tile matrix of several samples (each with its own barcode prefix),
|
|
89
|
+
# most accessible tiles only
|
|
90
|
+
gatac tile sample1.parquet sample2.parquet -g hg38 -n 500000 -o combined.h5ad
|
|
91
|
+
|
|
92
|
+
# One gene matrix (or gene score matrix) of several samples
|
|
93
|
+
gatac gene sample1.parquet sample2.parquet -g annotations.gtf -o genes.h5ad
|
|
94
|
+
gatac genescore sample1.parquet sample2.parquet -g annotations.gtf -o gene_scores.h5ad
|
|
95
|
+
|
|
96
|
+
# Merge h5ad files (file-by-file)
|
|
97
|
+
gatac combine sample1.h5ad sample2.h5ad -o combined.h5ad
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Python API
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
import gatac as ga
|
|
104
|
+
|
|
105
|
+
# Convert to parquet
|
|
106
|
+
ga.io.make_parquet("fragments.tsv.gz")
|
|
107
|
+
|
|
108
|
+
# Name the sample's cells sample1_<barcode>, in place (footer only)
|
|
109
|
+
ga.io.set_barcode_prefix("sample1.parquet", "sample1_")
|
|
110
|
+
|
|
111
|
+
# Compute metrics
|
|
112
|
+
metrics = ga.pp.compute_metrics("fragments.parquet", "annotations.gtf")
|
|
113
|
+
|
|
114
|
+
# Create tile matrix (pre-filtered by unique fragments)
|
|
115
|
+
adata = ga.pp.make_tile_matrix(
|
|
116
|
+
"fragments.parquet",
|
|
117
|
+
chrom_sizes="hg38",
|
|
118
|
+
min_fragments_per_cell=500,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Create tile matrix using pre-computed metrics for filtering
|
|
122
|
+
# metrics can be a path to a CSV or a cuDF DataFrame
|
|
123
|
+
adata = ga.pp.make_tile_matrix("fragments.parquet", chrom_sizes="hg38", metrics=metrics, filter_query="tsse_score > 5")
|
|
124
|
+
|
|
125
|
+
# Create gene activity matrix
|
|
126
|
+
adata_gene = ga.pp.make_gene_matrix(
|
|
127
|
+
"fragments.parquet", gene_anno="annotations.gtf", upstream=2000, include_gene_body=True
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Create gene activity matrix with filtering
|
|
131
|
+
adata_gene = ga.pp.make_gene_matrix(
|
|
132
|
+
"fragments.parquet", gene_anno="annotations.gtf", metrics=metrics, filter_query="tsse_score > 5"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Create ArchR-style distance-weighted gene activity SCORE matrix
|
|
136
|
+
# (faithful GPU port of ArchR addGeneScoreMatrix; defaults match ArchR)
|
|
137
|
+
adata_score = ga.pp.make_gene_score_matrix(
|
|
138
|
+
"fragments.parquet",
|
|
139
|
+
gene_anno="annotations.gtf", # GTF/GFF, or CSV: symbol,seqnames,start,end,strand
|
|
140
|
+
gene_model="exp(-abs(x)/5000) + exp(-1)",
|
|
141
|
+
tile_size=500,
|
|
142
|
+
extend_upstream=(1000, 100000),
|
|
143
|
+
extend_downstream=(1000, 100000),
|
|
144
|
+
use_gene_boundaries=True,
|
|
145
|
+
scale_to=10000,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Filter fragments by minimum fragment count
|
|
149
|
+
ga.pp.filter_fragments("fragments.parquet", min_fragments_per_cell=500)
|
|
150
|
+
|
|
151
|
+
# Filter fragments using metrics with quality threshold
|
|
152
|
+
ga.pp.filter_fragments("fragments.parquet", metrics=metrics, filter_query="tsse_score > 5")
|
|
153
|
+
|
|
154
|
+
# Filter multiple files with genome-based chromosome filtering
|
|
155
|
+
ga.pp.filter_fragments(
|
|
156
|
+
["sample1.parquet", "sample2.parquet"],
|
|
157
|
+
metrics=metrics,
|
|
158
|
+
filter_query="tsse_score > 5 and n_unique > 1000",
|
|
159
|
+
chrom_sizes="hg38",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Select features (single AnnData)
|
|
163
|
+
ga.pp.select_features(adata, n_features=50000)
|
|
164
|
+
|
|
165
|
+
# One tile matrix of several samples, most accessible tiles only
|
|
166
|
+
ga.pp.make_tile_matrix(
|
|
167
|
+
["sample1.parquet", "sample2.parquet"], chrom_sizes="hg38", n_features=500000, output_path="combined.h5ad"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# One gene matrix of several samples, written as it is built
|
|
171
|
+
ga.pp.make_gene_matrix(["sample1.parquet", "sample2.parquet"], gene_anno="annotations.gtf", output_path="genes.h5ad")
|
|
172
|
+
|
|
173
|
+
# Merge multiple AnnData files
|
|
174
|
+
ga.pp.combine(input_paths=["sample1.h5ad", "sample2.h5ad"], output_path="merged.h5ad")
|
|
175
|
+
```
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GATAC - GPU-accelerated ATAC-seq processing toolkit.
|
|
3
|
+
|
|
4
|
+
Public API:
|
|
5
|
+
- io: Fragment files (reading, writing, conversion)
|
|
6
|
+
- pp: Preprocessing functions
|
|
7
|
+
- dr: Dimensionality reduction
|
|
8
|
+
- peak: Peak calling and marker peaks
|
|
9
|
+
- motif: Motif enrichment and chromVAR
|
|
10
|
+
- tobias: TOBIAS footprinting and differential TF binding
|
|
11
|
+
- pl: Plotting
|
|
12
|
+
- gpu: GPU helpers for code built on GATAC
|
|
13
|
+
|
|
14
|
+
Installed extensions (packages with a ``gatac.ext`` entry point) are reached
|
|
15
|
+
as ``gatac.<name>``, imported on first use; see :func:`extensions`.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import importlib
|
|
19
|
+
import logging
|
|
20
|
+
import os
|
|
21
|
+
from importlib.metadata import EntryPoint, entry_points, version
|
|
22
|
+
from typing import TYPE_CHECKING
|
|
23
|
+
|
|
24
|
+
# libcudf means to default KvikIO to POSIX reads, but opens the first file
|
|
25
|
+
# before applying that, so each process probes cuFile/GPUDirect Storage once
|
|
26
|
+
# (seconds on nodes without it). Apply libcudf's intended default up front.
|
|
27
|
+
os.environ.setdefault("KVIKIO_COMPAT_MODE", "ON")
|
|
28
|
+
# KvikIO 26.10 writes files with direct I/O by default, which fails with an
|
|
29
|
+
# illegal memory access on some systems (WSL2); cuDF's to_csv / to_parquet go
|
|
30
|
+
# through it. Writing through the page cache, as before 26.10, works everywhere.
|
|
31
|
+
os.environ.setdefault("KVIKIO_AUTO_DIRECT_IO_WRITE", "OFF")
|
|
32
|
+
|
|
33
|
+
# The submodules are imported on first use (gatac.pp, `from gatac import pp`):
|
|
34
|
+
# `import gatac` is quick, and the CLI parses its arguments and shows --help
|
|
35
|
+
# without RAPIDS. Each checks first that RAPIDS is installed (gatac._rapids).
|
|
36
|
+
_SUBMODULES = ("io", "pp", "dr", "peak", "motif", "tobias", "pl", "gpu")
|
|
37
|
+
|
|
38
|
+
if TYPE_CHECKING:
|
|
39
|
+
from . import dr, gpu, io, motif, peak, pl, pp, tobias # noqa: F401
|
|
40
|
+
|
|
41
|
+
__version__ = version("gatac")
|
|
42
|
+
__all__ = ["io", "pp", "dr", "peak", "motif", "tobias", "pl", "gpu", "extensions", "set_verbosity"]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# Extensions
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
# A package extends GATAC by declaring an entry point in the "gatac.ext"
|
|
49
|
+
# group, e.g. in its pyproject.toml:
|
|
50
|
+
#
|
|
51
|
+
# [project.entry-points."gatac.ext"]
|
|
52
|
+
# chromafold = "gatac_chromafold"
|
|
53
|
+
#
|
|
54
|
+
# and ``gatac.chromafold`` then imports gatac_chromafold on first access.
|
|
55
|
+
# Core names always win: an extension cannot replace gatac.io, gatac.pp, ...
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def extensions() -> dict[str, EntryPoint]:
|
|
59
|
+
"""
|
|
60
|
+
Installed GATAC extensions, by the name they are reached at (``gatac.<name>``).
|
|
61
|
+
|
|
62
|
+
Examples
|
|
63
|
+
--------
|
|
64
|
+
>>> import gatac as ga
|
|
65
|
+
>>> ga.extensions()
|
|
66
|
+
{'chromafold': EntryPoint(name='chromafold', value='gatac_chromafold', group='gatac.ext')}
|
|
67
|
+
"""
|
|
68
|
+
return {ep.name: ep for ep in entry_points(group="gatac.ext")}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def __getattr__(name: str):
|
|
72
|
+
if name in _SUBMODULES:
|
|
73
|
+
return importlib.import_module(f".{name}", __name__)
|
|
74
|
+
ep = extensions().get(name) if not name.startswith("_") else None
|
|
75
|
+
if ep is None:
|
|
76
|
+
raise AttributeError(f"module 'gatac' has no attribute {name!r}")
|
|
77
|
+
module = ep.load()
|
|
78
|
+
globals()[name] = module
|
|
79
|
+
return module
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def __dir__() -> list[str]:
|
|
83
|
+
return sorted(set(globals()) | set(_SUBMODULES) | set(extensions()))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
# Package-level logging
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
# GATAC's INFO messages are shown by default: on stderr while logging is not
|
|
90
|
+
# configured, and through the application's own handlers once it is (root
|
|
91
|
+
# logger handlers, e.g. logging.basicConfig or pytest's caplog), so they are
|
|
92
|
+
# never printed twice. gatac.set_verbosity("WARNING") silences them.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class _DefaultHandler(logging.StreamHandler):
|
|
96
|
+
"""GATAC's messages on stderr, while the root logger has no handler of its own."""
|
|
97
|
+
|
|
98
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
99
|
+
if not logging.getLogger().handlers:
|
|
100
|
+
super().emit(record)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
_logger = logging.getLogger("gatac")
|
|
104
|
+
_logger.setLevel(logging.INFO)
|
|
105
|
+
_handler = _DefaultHandler()
|
|
106
|
+
_handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%H:%M:%S"))
|
|
107
|
+
_logger.addHandler(_handler)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def set_verbosity(level: str | int) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Set the logging verbosity for all GATAC submodules.
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
level
|
|
117
|
+
A standard logging level name (``"DEBUG"``, ``"INFO"``,
|
|
118
|
+
``"WARNING"``, ``"ERROR"``) or the corresponding integer constant.
|
|
119
|
+
|
|
120
|
+
Examples
|
|
121
|
+
--------
|
|
122
|
+
>>> import gatac
|
|
123
|
+
>>> gatac.set_verbosity("DEBUG") # show all messages
|
|
124
|
+
>>> gatac.set_verbosity("WARNING") # silence info messages
|
|
125
|
+
"""
|
|
126
|
+
if isinstance(level, str):
|
|
127
|
+
level = getattr(logging, level.upper())
|
|
128
|
+
logging.getLogger("gatac").setLevel(level)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Output files that are only replaced by complete ones."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from collections.abc import Iterator
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@contextmanager
|
|
12
|
+
def replacing(output_path: str | Path) -> Iterator[Path]:
|
|
13
|
+
"""
|
|
14
|
+
A temporary path next to ``output_path``, moved onto it when the block succeeds.
|
|
15
|
+
|
|
16
|
+
On an error the temporary file is removed and ``output_path``, if it
|
|
17
|
+
exists, is left as it was: a reader never sees a partial output.
|
|
18
|
+
"""
|
|
19
|
+
output_path = Path(output_path)
|
|
20
|
+
# hidden, per process, and with the output's suffix for writers that check it
|
|
21
|
+
tmp_path = output_path.with_name(f".{output_path.name}.{os.getpid()}.tmp{output_path.suffix}")
|
|
22
|
+
try:
|
|
23
|
+
yield tmp_path
|
|
24
|
+
os.replace(tmp_path, output_path)
|
|
25
|
+
finally:
|
|
26
|
+
tmp_path.unlink(missing_ok=True)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def write_h5ad(adata, output_path: str | Path) -> None:
|
|
30
|
+
"""``adata.write_h5ad(output_path)``, replacing ``output_path`` only once the file is complete."""
|
|
31
|
+
with replacing(output_path) as tmp_path:
|
|
32
|
+
adata.write_h5ad(str(tmp_path))
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""GATAC's progress bars: shown in a terminal or a notebook, not in logs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def show_progress() -> bool:
|
|
10
|
+
"""
|
|
11
|
+
Whether progress bars are shown: when stderr is a terminal or in a
|
|
12
|
+
Jupyter kernel, so that pipelines (e.g. Snakemake) keep clean logs, and
|
|
13
|
+
never with ``TQDM_DISABLE`` set.
|
|
14
|
+
"""
|
|
15
|
+
if os.environ.get("TQDM_DISABLE", "").lower() in ("1", "true", "yes"):
|
|
16
|
+
return False
|
|
17
|
+
if sys.stderr is not None and sys.stderr.isatty():
|
|
18
|
+
return True
|
|
19
|
+
ipython = sys.modules.get("IPython")
|
|
20
|
+
shell = ipython.get_ipython() if ipython is not None else None
|
|
21
|
+
return getattr(shell, "kernel", None) is not None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def tqdm(*args, disable: bool = False, **kwargs):
|
|
25
|
+
""":class:`tqdm.auto.tqdm`, disabled unless :func:`show_progress`."""
|
|
26
|
+
from tqdm.auto import tqdm as _tqdm
|
|
27
|
+
|
|
28
|
+
return _tqdm(*args, disable=disable or not show_progress(), **kwargs)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def trange(*args, **kwargs):
|
|
32
|
+
"""``tqdm(range(*args), **kwargs)``."""
|
|
33
|
+
return tqdm(range(*args), **kwargs)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""The check, first in every GPU module of GATAC, that RAPIDS is installed."""
|
|
2
|
+
|
|
3
|
+
INSTALL_HINT = (
|
|
4
|
+
"With pip, install the extra matching the CUDA version nvidia-smi reports: "
|
|
5
|
+
"pip install 'gatac[rapids-cu12]' or 'gatac[rapids-cu13]'. "
|
|
6
|
+
"With conda, install RAPIDS from the rapidsai channel: "
|
|
7
|
+
"https://gatac.readthedocs.io/en/latest/installation.html"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def require() -> None:
|
|
12
|
+
"""
|
|
13
|
+
Import cuDF and CuPy, or say how to install them.
|
|
14
|
+
|
|
15
|
+
``pip install gatac`` without a RAPIDS extra installs no RAPIDS; this tells
|
|
16
|
+
how to fix it instead of failing on ``import cudf`` deep inside GATAC.
|
|
17
|
+
"""
|
|
18
|
+
try:
|
|
19
|
+
import cudf # noqa: F401
|
|
20
|
+
import cupy # noqa: F401
|
|
21
|
+
except ModuleNotFoundError as e:
|
|
22
|
+
raise ImportError(f"GATAC runs on the GPU through RAPIDS, which is missing ({e}). {INSTALL_HINT}") from e
|