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.
Files changed (80) hide show
  1. gatac-0.1.0/.gitignore +28 -0
  2. gatac-0.1.0/LICENSE +28 -0
  3. gatac-0.1.0/PKG-INFO +227 -0
  4. gatac-0.1.0/README.md +175 -0
  5. gatac-0.1.0/gatac/__init__.py +128 -0
  6. gatac-0.1.0/gatac/_files.py +32 -0
  7. gatac-0.1.0/gatac/_progress.py +33 -0
  8. gatac-0.1.0/gatac/_rapids.py +22 -0
  9. gatac-0.1.0/gatac/_readers.py +140 -0
  10. gatac-0.1.0/gatac/_workers.py +31 -0
  11. gatac-0.1.0/gatac/cli.py +771 -0
  12. gatac-0.1.0/gatac/dr/__init__.py +26 -0
  13. gatac-0.1.0/gatac/dr/_clustering.py +573 -0
  14. gatac-0.1.0/gatac/dr/_gpu.py +340 -0
  15. gatac-0.1.0/gatac/dr/_stream.py +267 -0
  16. gatac-0.1.0/gatac/dr/lsi.py +2175 -0
  17. gatac-0.1.0/gatac/dr/spectral.py +581 -0
  18. gatac-0.1.0/gatac/gpu.py +78 -0
  19. gatac-0.1.0/gatac/io/__init__.py +22 -0
  20. gatac-0.1.0/gatac/io/_footer.py +187 -0
  21. gatac-0.1.0/gatac/io/convert.py +682 -0
  22. gatac-0.1.0/gatac/io/fasta.py +250 -0
  23. gatac-0.1.0/gatac/io/fragments.py +1027 -0
  24. gatac-0.1.0/gatac/io/gpu_convert.py +681 -0
  25. gatac-0.1.0/gatac/motif/__init__.py +40 -0
  26. gatac-0.1.0/gatac/motif/_bias_matching.py +244 -0
  27. gatac-0.1.0/gatac/motif/_gsea.py +643 -0
  28. gatac-0.1.0/gatac/motif/chromvar.py +1150 -0
  29. gatac-0.1.0/gatac/motif/motif.py +2208 -0
  30. gatac-0.1.0/gatac/peak/__init__.py +18 -0
  31. gatac-0.1.0/gatac/peak/_macs.py +836 -0
  32. gatac-0.1.0/gatac/peak/markers.py +529 -0
  33. gatac-0.1.0/gatac/peak/peaks.py +1244 -0
  34. gatac-0.1.0/gatac/pl/__init__.py +10 -0
  35. gatac-0.1.0/gatac/pl/footprint.py +139 -0
  36. gatac-0.1.0/gatac/pl/fragments.py +77 -0
  37. gatac-0.1.0/gatac/pl/metrics.py +113 -0
  38. gatac-0.1.0/gatac/pl/motifs.py +68 -0
  39. gatac-0.1.0/gatac/pp/__init__.py +45 -0
  40. gatac-0.1.0/gatac/pp/_combine.py +179 -0
  41. gatac-0.1.0/gatac/pp/_utils.py +35 -0
  42. gatac-0.1.0/gatac/pp/amulet.py +538 -0
  43. gatac-0.1.0/gatac/pp/features.py +310 -0
  44. gatac-0.1.0/gatac/pp/filter.py +279 -0
  45. gatac-0.1.0/gatac/pp/gene.py +808 -0
  46. gatac-0.1.0/gatac/pp/genescore.py +709 -0
  47. gatac-0.1.0/gatac/pp/genome.py +144 -0
  48. gatac-0.1.0/gatac/pp/metrics.py +335 -0
  49. gatac-0.1.0/gatac/pp/process.py +88 -0
  50. gatac-0.1.0/gatac/pp/tile.py +923 -0
  51. gatac-0.1.0/gatac/pp/tile_scan.py +373 -0
  52. gatac-0.1.0/gatac/py.typed +0 -0
  53. gatac-0.1.0/gatac/tobias/__init__.py +21 -0
  54. gatac-0.1.0/gatac/tobias/_minpack_relu.cu +528 -0
  55. gatac-0.1.0/gatac/tobias/bindetect.py +1521 -0
  56. gatac-0.1.0/gatac/tobias/footprint.py +996 -0
  57. gatac-0.1.0/hatch.toml +120 -0
  58. gatac-0.1.0/pyproject.toml +171 -0
  59. gatac-0.1.0/tests/api/test_dr.py +261 -0
  60. gatac-0.1.0/tests/api/test_motif.py +254 -0
  61. gatac-0.1.0/tests/api/test_peak.py +272 -0
  62. gatac-0.1.0/tests/api/test_pl.py +34 -0
  63. gatac-0.1.0/tests/api/test_pp.py +115 -0
  64. gatac-0.1.0/tests/conftest.py +145 -0
  65. gatac-0.1.0/tests/regression/test_amulet_poisson.py +28 -0
  66. gatac-0.1.0/tests/regression/test_bg_peaks_random_state.py +48 -0
  67. gatac-0.1.0/tests/regression/test_bindetect_sites.py +149 -0
  68. gatac-0.1.0/tests/regression/test_cli.py +102 -0
  69. gatac-0.1.0/tests/regression/test_enrichment_threshold.py +61 -0
  70. gatac-0.1.0/tests/regression/test_extensions.py +53 -0
  71. gatac-0.1.0/tests/regression/test_fasta.py +134 -0
  72. gatac-0.1.0/tests/regression/test_fragment_files.py +857 -0
  73. gatac-0.1.0/tests/regression/test_gpu.py +72 -0
  74. gatac-0.1.0/tests/regression/test_gpu_convert.py +274 -0
  75. gatac-0.1.0/tests/regression/test_gsea_scoring.py +24 -0
  76. gatac-0.1.0/tests/regression/test_lsi_determinism.py +166 -0
  77. gatac-0.1.0/tests/regression/test_motif_regression.py +60 -0
  78. gatac-0.1.0/tests/regression/test_package.py +66 -0
  79. gatac-0.1.0/tests/regression/test_readers.py +127 -0
  80. 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
+ [![CI-Pass](https://github.com/scverse/gatac/actions/workflows/test-gpu.yml/badge.svg)](https://github.com/scverse/gatac/actions/workflows/test-gpu.yml)
54
+ [![codecov](https://codecov.io/gh/scverse/gatac/graph/badge.svg?token=YMSDUHQRBB)](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
+ [![CI-Pass](https://github.com/scverse/gatac/actions/workflows/test-gpu.yml/badge.svg)](https://github.com/scverse/gatac/actions/workflows/test-gpu.yml)
2
+ [![codecov](https://codecov.io/gh/scverse/gatac/graph/badge.svg?token=YMSDUHQRBB)](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