xengsort 2.2.0__tar.gz → 2.2.2__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 (55) hide show
  1. {xengsort-2.2.0/xengsort.egg-info → xengsort-2.2.2}/PKG-INFO +32 -21
  2. {xengsort-2.2.0 → xengsort-2.2.2}/README.md +31 -20
  3. {xengsort-2.2.0 → xengsort-2.2.2}/pyproject.toml +1 -1
  4. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/cptasks_kmers.py +25 -4
  5. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/fastcash_main.py +5 -4
  6. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/hash_s3c_fbcbvb.py +16 -0
  7. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/io/fastaio.py +7 -3
  8. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/io/fastqio.py +66 -37
  9. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/io/generalio.py +12 -1
  10. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/io/hashio.py +7 -1
  11. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/kmers.py +134 -3
  12. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/lowlevel/libc.py +23 -7
  13. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/lowlevel/llvm.py +27 -2
  14. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/srhash.py +3 -0
  15. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/xengsort/xengsort_classify.py +7 -7
  16. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/xengsort/xengsort_index.py +6 -3
  17. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/xengsort/xengsort_info.py +3 -3
  18. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/xengsort/xengsort_main.py +9 -2
  19. {xengsort-2.2.0 → xengsort-2.2.2/xengsort.egg-info}/PKG-INFO +32 -21
  20. {xengsort-2.2.0 → xengsort-2.2.2}/LICENSE +0 -0
  21. {xengsort-2.2.0 → xengsort-2.2.2}/MANIFEST.in +0 -0
  22. {xengsort-2.2.0 → xengsort-2.2.2}/setup.cfg +0 -0
  23. {xengsort-2.2.0 → xengsort-2.2.2}/tests/test_trimmed_reads.py +0 -0
  24. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/dnaencode.py +0 -0
  25. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/dnaencode_fast.py +0 -0
  26. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/fastcash_info.py +0 -0
  27. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/fastcash_optimize.py +0 -0
  28. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/hash_minimizer.py +0 -0
  29. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/hash_srh_fbcbvb.py +0 -0
  30. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/io/binaryio.py +0 -0
  31. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/io/seqio.py +0 -0
  32. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/io/textio.py +0 -0
  33. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/io/xorio.py +0 -0
  34. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/lowlevel/aligned_arrays.py +0 -0
  35. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/lowlevel/bitarray.py +0 -0
  36. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/lowlevel/conpro.py +0 -0
  37. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/lowlevel/debug.py +0 -0
  38. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/lowlevel/intbitarray.py +0 -0
  39. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/lowlevel/numbautils.py +0 -0
  40. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/lowlevel/packedarray.py +0 -0
  41. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/mask.py +0 -0
  42. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/mathutils.py +0 -0
  43. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/parameters.py +0 -0
  44. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/subtable_hashfunctions.py +0 -0
  45. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/values/xenograft.py +0 -0
  46. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/xengsort/config/classify.yaml +0 -0
  47. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/xengsort/config/index.yaml +0 -0
  48. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/xengsort/xengsort_load.py +0 -0
  49. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/xengsort/xengsort_remove.py +0 -0
  50. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort/xengsort/xengsort_weak.py +0 -0
  51. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort.egg-info/SOURCES.txt +0 -0
  52. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort.egg-info/dependency_links.txt +0 -0
  53. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort.egg-info/entry_points.txt +0 -0
  54. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort.egg-info/requires.txt +0 -0
  55. {xengsort-2.2.0 → xengsort-2.2.2}/xengsort.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xengsort
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: xenograft sorting of FASTQ reads into five categories: host, graft, both, neither, ambiguous.
5
5
  Author-email: Jens Zentgraf <zentgraf@cs.uni-saarland.de>, Sven Rahmann <rahmann@cs.uni-saarland.de>
6
6
  License: MIT License
@@ -52,6 +52,7 @@ This tool, xengsort, uses 3-way bucketed Cuckoo hashing to efficiently solve the
52
52
  For a description of the method an evaluation on several datasets, please see our
53
53
  [article in "Algorithms for Molecular Biology"](https://almob.biomedcentral.com/articles/10.1186/s13015-021-00181-w), or the initial [WABI 2020 publication](https://drops.dagstuhl.de/opus/volltexte/2020/12793/).
54
54
  (There was also a [preprint on bioRxiv](https://www.biorxiv.org/content/10.1101/2020.05.14.095604v2).)
55
+ If you use xengsort to sort converted reads (RRBS or WGBS) there is a [bioRxiv preprint](https://www.biorxiv.org/content/10.64898/2026.10.06.756952v1.abstract) available.
55
56
  BibTeX entrys for citation can be found at the end of this README file.
56
57
 
57
58
  In case of problems,
@@ -71,24 +72,23 @@ Thank you!
71
72
 
72
73
  We here provide an example showing how to run xengsort on a human-captured mouse exome (part of one of the datasets described in the paper).
73
74
  This tutorial uses the workflow management system `Snakemake` to run the example.
74
- This is, however, not necessary to use `xengsort`.
75
+ This is, however, not necessary to use `xengsort`.
75
76
  Please refer to the Usage Guide below to learn how to run `xengsort` on your own data manually.
76
77
 
77
78
 
78
79
  Our software `xengsort` is provided as a Python package.
79
- For efficiency, it uses just-in-time compilation provided by the [numba](http://numba.pydata.org/) package.
80
+ For efficiency, it uses just-in-time compilation provided by the [numba](http://numba.pydata.org/) package.
80
81
  We here use the package manager [conda](https://docs.conda.io/en/latest/) to manage Python packages and to use a separate environment for `xengsort`.
81
82
 
82
83
 
83
- ### Install the conda package manager (miniconda)
84
- Go to https://docs.conda.io/en/latest/miniconda.html and download the Miniconda installer:
85
- Choose Python 3.10 (or higher), your operating system, and preferably the 64 bit version.
86
- Follow the instructions of the installer and append the conda executable to your PATH (even if the installer does not recommend it).
84
+ ### Install the conda package manager (conda-forge)
85
+ Go to https://conda-forge.org/download/ and download the conda-forge installer:
86
+ Choose your operating system, follow the instructions of the installer and append the conda executable to your PATH (even if the installer does not recommend it).
87
87
  You can let the installer do it, or do it manually by editing your ``.bashrc`` or similar file under Linux or MacOS, or by editing the environment variables under Windows.
88
88
  To verify that the installation works, open a new terminal and execute
89
89
  ```
90
- conda --version # ideally 22.9.xx or higher
91
- python --version # ideally 3.10.xx or higher
90
+ conda --version # ideally 26.9.xx or higher
91
+ python --version # ideally 3.12.xx or higher
92
92
  ```
93
93
 
94
94
 
@@ -122,7 +122,7 @@ within the cloned directory tree.
122
122
 
123
123
 
124
124
  ### Create and activate a conda environment
125
- To run our software, a [conda](https://docs.conda.io/en/latest/) environment with the required libraries needs to be created.
125
+ To run our software, a [conda](https://conda-forge.org/docs/) environment with the required libraries needs to be created.
126
126
  A list of needed libraries is provided in the ``environment.yml`` file in the cloned repository;
127
127
  it can be used to create a new environment:
128
128
 
@@ -133,7 +133,7 @@ conda env create
133
133
  which will create an environment named ``xengsort`` with the required dependencies,
134
134
  using the provided ``environment.yml`` file in the same directory.
135
135
 
136
- A more explicit alternative is to run
136
+ A more explicit alternative is to run
137
137
  ```
138
138
  conda create --name xengsort -c conda-forge -c bioconda --file requirements.txt
139
139
  ```
@@ -217,11 +217,11 @@ To build an index for xengsort, several parameters must be provided, which are d
217
217
  First, a file name and a path for the index must be chosen.
218
218
  The index is stored in two files. We will use `myindex` to store the index in the current folder.
219
219
 
220
- Second, two reference genomes (host and graft) must be provided (in FASTA files).
221
- We assume that they are given as `host.fa.gz` and `graft.fa.gz`.
220
+ Second, two reference genomes (host and graft) must be provided (in FASTA files).
221
+ We assume that they are given as `host.fa.gz` and `graft.fa.gz`.
222
222
  The files can be provided as an uncompressed file or compressed using `gzip`, `bzip2` or `xz`.
223
- The corresponding options are `-H` or `--host` and `-G` or `--graft`.
224
- Each option can take several arguments as files.
223
+ The corresponding options are `-H` or `--host` and `-G` or `--graft`.
224
+ Each option can take several arguments as files.
225
225
  ```
226
226
  xengsort index --index myindex -H host.fa.gz -G graft.fa.gz -n 4_500_000_000 [OPTIONS]
227
227
  ```
@@ -249,7 +249,7 @@ The final important parameter is about parallelization:
249
249
 
250
250
  Most of the parameters can also be provided in a config file (`.yaml`):
251
251
  - `--cfg` or `--config` defines the path the the config file.
252
-
252
+
253
253
  ### How to classify
254
254
 
255
255
  To classify a FASTQ sample (one or several single-end or paired-end files), make sure you are in an environment where xengsort and its dependencies are installed.
@@ -257,7 +257,7 @@ Then run the `xengsort classify` command with a previously built index, such as
257
257
  ```
258
258
  xengsort classify --index myindex --fastq single.fq.gz --prefix myresults --mode count
259
259
  ```
260
- for single-end reads, or
260
+ for single-end reads, or
261
261
  ```
262
262
  xengsort classify --index myindex --fastq paired.1.fq.gz --pairs paired.2.fq.gz --prefix myresults --mode count
263
263
  ```
@@ -275,7 +275,7 @@ The parameter `--prefix` or equivalently `--out` is required and defines the pre
275
275
  - `{prefix}.both.1.fq.gz`: reads that could originate from both
276
276
  - `{prefix}.neither.1.fq.gz`: reads that originate from neither host nor graft
277
277
  - `{prefix}.ambiguous.1.fq.gz`: (few) ambiguous reads that cannot be classified,
278
-
278
+
279
279
  and similarly with `.2.fq.gz`. For single-end reads, there is only `.fq.gz` (no numbers).
280
280
 
281
281
  The compression type can be specified using the `--compression` parameter.
@@ -301,7 +301,7 @@ xengsort index --index myindex -H host.fa.gz -G graft.fa.gz -n 9_000_000_000 --b
301
301
  If an index is build using the bisulfite parameter, also the read is converted in the classification step.
302
302
  It is not necessary to change the parameters of the classification step.
303
303
  Currently, it only supports the count-based classification.
304
-
304
+ A detailed description on how the benchmarks on bisulfite sequencing data was performed can be found [here](https://github.com/S-Altend/MethSortBench/tree/main/03_read_sorting).
305
305
 
306
306
  Please report a [GitLab issue](https://gitlab.com/genomeinformatics/xengsort/-/issues) in case of problems.
307
307
 
@@ -315,13 +315,13 @@ Happy sorting!
315
315
  * **Snakemake throws errors when attempting to download the example dataset from SRA**.
316
316
 
317
317
  This may happen for several reasons:
318
- 1. The dataset is quite large, over 20 GB. Make sure you have sufficient space (also temporary space) and sufficient disk quota (on a shared system) to store all of the files.
318
+ 1. The dataset is quite large, over 20 GB. Make sure you have sufficient space (also temporary space) and sufficient disk quota (on a shared system) to store all of the files.
319
319
  1. Another source of trouble may be that for some reason and older version of `sra-tools` gets called. We use the `fasterq-dump` tool to download the data, which did not exist in older verisons. To use a clean environment with a recent version of `sra-tools`, you can run snakemake as `snakemake -j 16 --use-conda`, which will generate a separate environment only for the download (this takes additional time, but may resolve the problem).
320
320
  1. In earlier versions, the dataset was downloaded from a different location which is no longer available. Please check that you are using at least v1.1.0 (`xengsort --version`). If not, please do a fresh clone and follow the tutorial to set up a new environment (delete the old one first) and run `pip install -e .`, as explained above in the tutorial.
321
321
 
322
322
  * **Indexing stops after a few seconds with a failure.**
323
323
 
324
- The most likely cause of this is that you have not specified the size of the hash table (`-n` parameter).
324
+ The most likely cause of this is that you have not specified the size of the hash table (`-n` parameter).
325
325
  (From version 1.0.1, the `-n` parameter is required, and the error cannot occur anymore. Please update to the latest version!)
326
326
  For the human and mouse genomes this is approximately `-n 4_500_000_000` (4.5 billion).
327
327
  It is unfortunately a limitation of the implementation that the hash table size has to be specified in advance.
@@ -377,3 +377,14 @@ In addition, you may also cite the WABI 2020 proceedigs paper:
377
377
  annote = {Keywords: xenograft sorting, alignment-free method, Cuckoo hashing, k-mer}
378
378
  }
379
379
  ```
380
+
381
+ If you use xengsort to sort converted reads, please also cite our corresponding bioRxiv preprint:
382
+ ```
383
+ @article {Altendorfer2026.10.06.756952,
384
+ author = {Altendorfer, Stefan and Zentgraf, Jens and Schmitz, Johanna Elena and Plass, Christoph and Gerh{\"a}user, Clarissa and Rahmann, Sven},
385
+ title = {High-performance xenograft read-sorting for DNA methylation analysis},
386
+ year = {2026},
387
+ doi = {10.64898/2026.10.06.756952},
388
+ journal = {bioRxiv}
389
+ }
390
+ ```
@@ -5,6 +5,7 @@ This tool, xengsort, uses 3-way bucketed Cuckoo hashing to efficiently solve the
5
5
  For a description of the method an evaluation on several datasets, please see our
6
6
  [article in "Algorithms for Molecular Biology"](https://almob.biomedcentral.com/articles/10.1186/s13015-021-00181-w), or the initial [WABI 2020 publication](https://drops.dagstuhl.de/opus/volltexte/2020/12793/).
7
7
  (There was also a [preprint on bioRxiv](https://www.biorxiv.org/content/10.1101/2020.05.14.095604v2).)
8
+ If you use xengsort to sort converted reads (RRBS or WGBS) there is a [bioRxiv preprint](https://www.biorxiv.org/content/10.64898/2026.10.06.756952v1.abstract) available.
8
9
  BibTeX entrys for citation can be found at the end of this README file.
9
10
 
10
11
  In case of problems,
@@ -24,24 +25,23 @@ Thank you!
24
25
 
25
26
  We here provide an example showing how to run xengsort on a human-captured mouse exome (part of one of the datasets described in the paper).
26
27
  This tutorial uses the workflow management system `Snakemake` to run the example.
27
- This is, however, not necessary to use `xengsort`.
28
+ This is, however, not necessary to use `xengsort`.
28
29
  Please refer to the Usage Guide below to learn how to run `xengsort` on your own data manually.
29
30
 
30
31
 
31
32
  Our software `xengsort` is provided as a Python package.
32
- For efficiency, it uses just-in-time compilation provided by the [numba](http://numba.pydata.org/) package.
33
+ For efficiency, it uses just-in-time compilation provided by the [numba](http://numba.pydata.org/) package.
33
34
  We here use the package manager [conda](https://docs.conda.io/en/latest/) to manage Python packages and to use a separate environment for `xengsort`.
34
35
 
35
36
 
36
- ### Install the conda package manager (miniconda)
37
- Go to https://docs.conda.io/en/latest/miniconda.html and download the Miniconda installer:
38
- Choose Python 3.10 (or higher), your operating system, and preferably the 64 bit version.
39
- Follow the instructions of the installer and append the conda executable to your PATH (even if the installer does not recommend it).
37
+ ### Install the conda package manager (conda-forge)
38
+ Go to https://conda-forge.org/download/ and download the conda-forge installer:
39
+ Choose your operating system, follow the instructions of the installer and append the conda executable to your PATH (even if the installer does not recommend it).
40
40
  You can let the installer do it, or do it manually by editing your ``.bashrc`` or similar file under Linux or MacOS, or by editing the environment variables under Windows.
41
41
  To verify that the installation works, open a new terminal and execute
42
42
  ```
43
- conda --version # ideally 22.9.xx or higher
44
- python --version # ideally 3.10.xx or higher
43
+ conda --version # ideally 26.9.xx or higher
44
+ python --version # ideally 3.12.xx or higher
45
45
  ```
46
46
 
47
47
 
@@ -75,7 +75,7 @@ within the cloned directory tree.
75
75
 
76
76
 
77
77
  ### Create and activate a conda environment
78
- To run our software, a [conda](https://docs.conda.io/en/latest/) environment with the required libraries needs to be created.
78
+ To run our software, a [conda](https://conda-forge.org/docs/) environment with the required libraries needs to be created.
79
79
  A list of needed libraries is provided in the ``environment.yml`` file in the cloned repository;
80
80
  it can be used to create a new environment:
81
81
 
@@ -86,7 +86,7 @@ conda env create
86
86
  which will create an environment named ``xengsort`` with the required dependencies,
87
87
  using the provided ``environment.yml`` file in the same directory.
88
88
 
89
- A more explicit alternative is to run
89
+ A more explicit alternative is to run
90
90
  ```
91
91
  conda create --name xengsort -c conda-forge -c bioconda --file requirements.txt
92
92
  ```
@@ -170,11 +170,11 @@ To build an index for xengsort, several parameters must be provided, which are d
170
170
  First, a file name and a path for the index must be chosen.
171
171
  The index is stored in two files. We will use `myindex` to store the index in the current folder.
172
172
 
173
- Second, two reference genomes (host and graft) must be provided (in FASTA files).
174
- We assume that they are given as `host.fa.gz` and `graft.fa.gz`.
173
+ Second, two reference genomes (host and graft) must be provided (in FASTA files).
174
+ We assume that they are given as `host.fa.gz` and `graft.fa.gz`.
175
175
  The files can be provided as an uncompressed file or compressed using `gzip`, `bzip2` or `xz`.
176
- The corresponding options are `-H` or `--host` and `-G` or `--graft`.
177
- Each option can take several arguments as files.
176
+ The corresponding options are `-H` or `--host` and `-G` or `--graft`.
177
+ Each option can take several arguments as files.
178
178
  ```
179
179
  xengsort index --index myindex -H host.fa.gz -G graft.fa.gz -n 4_500_000_000 [OPTIONS]
180
180
  ```
@@ -202,7 +202,7 @@ The final important parameter is about parallelization:
202
202
 
203
203
  Most of the parameters can also be provided in a config file (`.yaml`):
204
204
  - `--cfg` or `--config` defines the path the the config file.
205
-
205
+
206
206
  ### How to classify
207
207
 
208
208
  To classify a FASTQ sample (one or several single-end or paired-end files), make sure you are in an environment where xengsort and its dependencies are installed.
@@ -210,7 +210,7 @@ Then run the `xengsort classify` command with a previously built index, such as
210
210
  ```
211
211
  xengsort classify --index myindex --fastq single.fq.gz --prefix myresults --mode count
212
212
  ```
213
- for single-end reads, or
213
+ for single-end reads, or
214
214
  ```
215
215
  xengsort classify --index myindex --fastq paired.1.fq.gz --pairs paired.2.fq.gz --prefix myresults --mode count
216
216
  ```
@@ -228,7 +228,7 @@ The parameter `--prefix` or equivalently `--out` is required and defines the pre
228
228
  - `{prefix}.both.1.fq.gz`: reads that could originate from both
229
229
  - `{prefix}.neither.1.fq.gz`: reads that originate from neither host nor graft
230
230
  - `{prefix}.ambiguous.1.fq.gz`: (few) ambiguous reads that cannot be classified,
231
-
231
+
232
232
  and similarly with `.2.fq.gz`. For single-end reads, there is only `.fq.gz` (no numbers).
233
233
 
234
234
  The compression type can be specified using the `--compression` parameter.
@@ -254,7 +254,7 @@ xengsort index --index myindex -H host.fa.gz -G graft.fa.gz -n 9_000_000_000 --b
254
254
  If an index is build using the bisulfite parameter, also the read is converted in the classification step.
255
255
  It is not necessary to change the parameters of the classification step.
256
256
  Currently, it only supports the count-based classification.
257
-
257
+ A detailed description on how the benchmarks on bisulfite sequencing data was performed can be found [here](https://github.com/S-Altend/MethSortBench/tree/main/03_read_sorting).
258
258
 
259
259
  Please report a [GitLab issue](https://gitlab.com/genomeinformatics/xengsort/-/issues) in case of problems.
260
260
 
@@ -268,13 +268,13 @@ Happy sorting!
268
268
  * **Snakemake throws errors when attempting to download the example dataset from SRA**.
269
269
 
270
270
  This may happen for several reasons:
271
- 1. The dataset is quite large, over 20 GB. Make sure you have sufficient space (also temporary space) and sufficient disk quota (on a shared system) to store all of the files.
271
+ 1. The dataset is quite large, over 20 GB. Make sure you have sufficient space (also temporary space) and sufficient disk quota (on a shared system) to store all of the files.
272
272
  1. Another source of trouble may be that for some reason and older version of `sra-tools` gets called. We use the `fasterq-dump` tool to download the data, which did not exist in older verisons. To use a clean environment with a recent version of `sra-tools`, you can run snakemake as `snakemake -j 16 --use-conda`, which will generate a separate environment only for the download (this takes additional time, but may resolve the problem).
273
273
  1. In earlier versions, the dataset was downloaded from a different location which is no longer available. Please check that you are using at least v1.1.0 (`xengsort --version`). If not, please do a fresh clone and follow the tutorial to set up a new environment (delete the old one first) and run `pip install -e .`, as explained above in the tutorial.
274
274
 
275
275
  * **Indexing stops after a few seconds with a failure.**
276
276
 
277
- The most likely cause of this is that you have not specified the size of the hash table (`-n` parameter).
277
+ The most likely cause of this is that you have not specified the size of the hash table (`-n` parameter).
278
278
  (From version 1.0.1, the `-n` parameter is required, and the error cannot occur anymore. Please update to the latest version!)
279
279
  For the human and mouse genomes this is approximately `-n 4_500_000_000` (4.5 billion).
280
280
  It is unfortunately a limitation of the implementation that the hash table size has to be specified in advance.
@@ -330,3 +330,14 @@ In addition, you may also cite the WABI 2020 proceedigs paper:
330
330
  annote = {Keywords: xenograft sorting, alignment-free method, Cuckoo hashing, k-mer}
331
331
  }
332
332
  ```
333
+
334
+ If you use xengsort to sort converted reads, please also cite our corresponding bioRxiv preprint:
335
+ ```
336
+ @article {Altendorfer2026.10.06.756952,
337
+ author = {Altendorfer, Stefan and Zentgraf, Jens and Schmitz, Johanna Elena and Plass, Christoph and Gerh{\"a}user, Clarissa and Rahmann, Sven},
338
+ title = {High-performance xenograft read-sorting for DNA methylation analysis},
339
+ year = {2026},
340
+ doi = {10.64898/2026.10.06.756952},
341
+ journal = {bioRxiv}
342
+ }
343
+ ```
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "xengsort"
8
- version = "2.2.0"
8
+ version = "2.2.2"
9
9
 
10
10
  license = {file = "LICENSE"}
11
11
 
@@ -544,15 +544,27 @@ def compile_cptask_insert_filtered_subkeys(
544
544
  myfilter,
545
545
  constant_value=0,
546
546
  maxfailures=0,
547
- maxwalk=1000):
547
+ maxwalk=1000,
548
+ prefetch_distance=8):
548
549
  debugprint0, debugprint1, debugprint2 = debug.debugprint
549
550
  dassert = debug.dassert
551
+
552
+ @njit(nogil=True)
553
+ def dummy_prefetch(*_):
554
+ return
555
+
556
+ prefetch_filter = prefetch_table = dummy_prefetch
550
557
  if myfilter is not None:
551
558
  wwo = "with"
552
559
  lookup_and_insert_in_subfilter = myfilter.private.lookup_and_insert_in_subfilter
560
+ table_prefetch_distance = 0
561
+ filter_prefetch_distance = prefetch_distance
562
+ prefetch_filter = myfilter.private.prefetch_block_by_subkey_subfilter
553
563
  else:
554
564
  wwo = "without"
555
-
565
+ table_prefetch_distance = prefetch_distance
566
+ filter_prefetch_distance = 0
567
+ prefetch_table = myhashtable.private.prefetch_subkey
556
568
  @njit(nogil=True)
557
569
  def lookup_and_insert_in_subfilter(ft, st, subkey):
558
570
  return True
@@ -599,7 +611,12 @@ def compile_cptask_insert_filtered_subkeys(
599
611
  # dassert(buffer_type == 1, __name__, 'buffer_type == 1', buffer_type, message='0 means sequence buffer')
600
612
 
601
613
  # consume the buffer
602
- for subkey in active_buffer[:nsubkeys]:
614
+ for ii in range(nsubkeys):
615
+ subkey = active_buffer[ii]
616
+ if filter_prefetch_distance and ii + filter_prefetch_distance < nsubkeys:
617
+ prefetch_filter(ft, active_buffer[ii + prefetch_distance], st)
618
+ if table_prefetch_distance and ii + table_prefetch_distance < nsubkeys:
619
+ prefetch_table(ht, active_buffer[ii + prefetch_distance], st)
603
620
  if lookup_and_insert_in_subfilter(ft, st, subkey):
604
621
  status, result = update(ht, st, subkey, value) # see docstring below
605
622
  if status & 128 == 0:
@@ -618,9 +635,11 @@ def compile_cptask_insert_filtered_subkeys(
618
635
 
619
636
  def compile_cptask_update_existing_subkeys(
620
637
  myhashtable,
621
- constant_value=1):
638
+ constant_value=1,
639
+ prefetch_distance=8):
622
640
  debugprint0, debugprint1, debugprint2 = debug.debugprint
623
641
  update_existing = myhashtable.private.update_existing_ssk
642
+ prefetch = myhashtable.private.prefetch_subkey
624
643
  value = uint64(constant_value)
625
644
  debugprint1(f"- compiling cptask_update_existing_subkeys: {constant_value=}")
626
645
 
@@ -652,6 +671,8 @@ def compile_cptask_update_existing_subkeys(
652
671
 
653
672
  # consume the buffer
654
673
  for ii in range(nsubkeys):
674
+ if prefetch_distance and ii + prefetch_distance < nsubkeys:
675
+ prefetch(ht, active_buffer[ii + prefetch_distance], st)
655
676
  subkey = active_buffer[ii]
656
677
  _, _ = update_existing(ht, st, subkey, value) # see docstring below
657
678
  # status, result = update_existing(ht, st, subkey, value) # see docstring below
@@ -7,6 +7,7 @@ from argparse import ArgumentParser
7
7
  from importlib import import_module # dynamically import subcommand
8
8
  from importlib.metadata import metadata
9
9
  from os.path import split as splitpath, splitext
10
+ from collections import defaultdict
10
11
 
11
12
  from .lowlevel.debug import set_debugfunctions
12
13
 
@@ -28,7 +29,7 @@ def add_hash_table_arguments(p: ArgumentParser):
28
29
  help="bucket size, i.e. number of elements on a bucket")
29
30
  p.add_argument("--fill", type=float, default="0.9",
30
31
  help="desired fill rate of the hash table")
31
- p.add_argument("--subtables", type=int, default=5,
32
+ p.add_argument("--subtables", type=int, default=5,
32
33
  help="number of subtables used; subtables+1 threads are used")
33
34
 
34
35
 
@@ -51,20 +52,20 @@ def add_hash_arguments(p: ArgumentParser):
51
52
  help="number of objects to be stored")
52
53
  p.add_argument("--type", default="default", # default="3c_fbcbvb",
53
54
  help="hash type (e.g. [s]3c_fbcbvb, 2v_fbcbvb), implemented in hash_{TYPE}.py")
54
- p.add_argument("--unaligned", action="store_const",
55
+ p.add_argument("--unaligned", action="store_const",
55
56
  const=False, dest="aligned", default=None,
56
57
  help="use unaligned buckets (smaller, slightly slower; default)")
57
58
  p.add_argument("--aligned", action="store_const",
58
59
  const=True, dest="aligned", default=None,
59
60
  help="use power-of-two aligned buckets (faster, but larger)")
60
- p.add_argument("--hashfunctions", "--functions",
61
+ p.add_argument("--hashfunctions", "--functions",
61
62
  help="hash functions: 'default', 'random', or func1:func2[:func3]")
62
63
  # less important hash options
63
64
  p.add_argument("--nostatistics", "--nostats", action="store_true",
64
65
  help="do not compute or show index statistics at the end")
65
66
  p.add_argument("--maxwalk", metavar="INT", type=int, default=500,
66
67
  help="maximum length of random walk through hash table before failing [500]")
67
- p.add_argument("--maxfailures", metavar="INT", type=int, default=0,
68
+ p.add_argument("--maxfailures", metavar="INT", type=int, default=0,
68
69
  help="continue even after this many failures [default:0; forever:-1]")
69
70
  p.add_argument("--walkseed", type=int, default=7,
70
71
  help="seed for random walks while inserting elements [7]")
@@ -134,6 +134,22 @@ def build_hash(universe, n, subtables, bucketsize,
134
134
  def prefetch_bucket(table, subtable, bucket):
135
135
  startbit = subtable * subtablebits + bucket * bucketsizebits
136
136
  hprefetch(table, startbit)
137
+ hprefetch(table, startbit + uint64(bucketsizebits - 1))
138
+
139
+ @njit(nogil=True, locals=dict(
140
+ bucket=int64, startbit=uint64))
141
+ def prefetch_subkey(table, subkey, subtable):
142
+ bucket, _ = get_bs1(subkey)
143
+ startbit = subtable * subtablebits + bucket * bucketsizebits
144
+ hprefetch(table, startbit)
145
+
146
+ bucket, _ = get_bs2(subkey)
147
+ startbit = subtable * subtablebits + bucket * bucketsizebits
148
+ hprefetch(table, startbit)
149
+
150
+ bucket, _ = get_bs3(subkey)
151
+ startbit = subtable * subtablebits + bucket * bucketsizebits
152
+ hprefetch(table, startbit)
137
153
 
138
154
  @njit(nogil=True, locals=dict(
139
155
  bucket=int64, startbit=uint64))
@@ -103,6 +103,7 @@ def cptask_read_fasta_into_buffers(fd, W, inbuffers, incontrol, ininfos, outbuff
103
103
  inside_seq = True
104
104
  header_seq = False
105
105
  nseq = 0
106
+ last_seq_length = 2 * W
106
107
  while True:
107
108
  old = nactive
108
109
  old_buffer = active_buffer
@@ -134,7 +135,11 @@ def cptask_read_fasta_into_buffers(fd, W, inbuffers, incontrol, ininfos, outbuff
134
135
  if ntodo < 0:
135
136
  errorcode = ntodo
136
137
  break
138
+ # If we have a long header but a seq smaller W, the offset can be bigger than W
139
+ if header_seq and last_seq_length < W:
140
+ break
137
141
  # Nothing read and the offset is not a full (gapped) k-mer
142
+ # I think this should trigger
138
143
  if offset < W:
139
144
  break
140
145
  # Nothing read but we still have some data in the buffer
@@ -157,7 +162,6 @@ def cptask_read_fasta_into_buffers(fd, W, inbuffers, incontrol, ininfos, outbuff
157
162
  offset = (W - 1)
158
163
  else:
159
164
  offset = end_buffer - linemarkbuffers[nactive, nseq - 1, 3]
160
-
161
165
  outcontrol[nactive, 7] = nseq
162
166
  outcontrol[nactive, 6] = 0 # buffer type sequence data
163
167
 
@@ -417,7 +421,7 @@ def fastaextract(args):
417
421
  seps = args.separators
418
422
  sfx = [args.suffix] if args.suffix else []
419
423
  seps = [_SEPARATORS.get(sep.upper(), sep) for sep in seps]
420
- if items is None:
424
+ if items is None:
421
425
  items = list()
422
426
  seps = list()
423
427
  if len(seps) == 1:
@@ -436,7 +440,7 @@ def fastaextract(args):
436
440
  def get_header_info(header, items, assigner, seps):
437
441
  fields = header.decode("ascii").split()
438
442
  assigners = [i for (i, field) in enumerate(fields) if assigner in field]
439
- if 0 in assigners:
443
+ if 0 in assigners:
440
444
  assigners.remove(0)
441
445
  D = dict()
442
446
  if items is None: