xengsort 2.0.4__tar.gz → 2.0.7__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 (69) hide show
  1. {xengsort-2.0.4 → xengsort-2.0.7}/MANIFEST.in +2 -1
  2. {xengsort-2.0.4/xengsort.egg-info → xengsort-2.0.7}/PKG-INFO +39 -19
  3. {xengsort-2.0.4 → xengsort-2.0.7}/README.md +10 -12
  4. {xengsort-2.0.4 → xengsort-2.0.7}/pyproject.toml +26 -11
  5. xengsort-2.0.7/tests/test_trimmed_reads.py +28 -0
  6. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/cptasks_kmers.py +305 -8
  7. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/dnaencode.py +11 -8
  8. xengsort-2.0.7/xengsort/fastcash_info.py +83 -0
  9. xengsort-2.0.7/xengsort/fastcash_main.py +248 -0
  10. xengsort-2.0.7/xengsort/fastcash_optimize.py +765 -0
  11. xengsort-2.0.7/xengsort/hash_minimizer.py +1113 -0
  12. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/hash_s3c_fbcbvb.py +122 -64
  13. xengsort-2.0.7/xengsort/hash_srh_fbcbvb.py +862 -0
  14. xengsort-2.0.7/xengsort/io/binaryio.py +62 -0
  15. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/io/fastaio.py +201 -18
  16. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/io/fastqio.py +1 -0
  17. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/io/generalio.py +32 -4
  18. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/io/hashio.py +8 -1
  19. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/io/textio.py +1 -1
  20. xengsort-2.0.7/xengsort/io/xorio.py +94 -0
  21. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/kmers.py +1 -1
  22. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/lowlevel/bitarray.py +5 -4
  23. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/lowlevel/conpro.py +15 -8
  24. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/lowlevel/intbitarray.py +1 -1
  25. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/lowlevel/llvm.py +1 -1
  26. xengsort-2.0.7/xengsort/lowlevel/numbautils.py +25 -0
  27. xengsort-2.0.7/xengsort/lowlevel/packedarray.py +186 -0
  28. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/mathutils.py +5 -3
  29. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/parameters.py +5 -4
  30. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/srhash.py +2 -2
  31. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/subtable_hashfunctions.py +23 -18
  32. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/xengsort/xengsort_classify.py +6 -11
  33. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/xengsort/xengsort_index.py +9 -0
  34. xengsort-2.0.7/xengsort/xengsort/xengsort_info.py +83 -0
  35. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/xengsort/xengsort_load.py +4 -5
  36. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/xengsort/xengsort_main.py +57 -71
  37. {xengsort-2.0.4 → xengsort-2.0.7/xengsort.egg-info}/PKG-INFO +39 -19
  38. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort.egg-info/SOURCES.txt +11 -16
  39. xengsort-2.0.7/xengsort.egg-info/requires.txt +5 -0
  40. xengsort-2.0.4/setup.py +0 -42
  41. xengsort-2.0.4/tests/test_encoding.py +0 -21
  42. xengsort-2.0.4/tests/test_trimmed_reads.py +0 -33
  43. xengsort-2.0.4/xengsort/_version.py +0 -27
  44. xengsort-2.0.4/xengsort/xengsort/__pycache__/xengsort_classify.cpython-310.pyc +0 -0
  45. xengsort-2.0.4/xengsort/xengsort/__pycache__/xengsort_classify.cpython-311.pyc +0 -0
  46. xengsort-2.0.4/xengsort/xengsort/__pycache__/xengsort_index.cpython-310.pyc +0 -0
  47. xengsort-2.0.4/xengsort/xengsort/__pycache__/xengsort_index.cpython-311.pyc +0 -0
  48. xengsort-2.0.4/xengsort/xengsort/__pycache__/xengsort_info.cpython-311.pyc +0 -0
  49. xengsort-2.0.4/xengsort/xengsort/__pycache__/xengsort_main.cpython-310.pyc +0 -0
  50. xengsort-2.0.4/xengsort/xengsort/__pycache__/xengsort_main.cpython-311.pyc +0 -0
  51. xengsort-2.0.4/xengsort/xengsort/__pycache__/xengsort_weak.cpython-310.pyc +0 -0
  52. xengsort-2.0.4/xengsort/xengsort/__pycache__/xengsort_weak.cpython-311.pyc +0 -0
  53. xengsort-2.0.4/xengsort/xengsort/config/classify.yaml +0 -60
  54. xengsort-2.0.4/xengsort/xengsort/config/index.yaml +0 -9
  55. xengsort-2.0.4/xengsort.egg-info/not-zip-safe +0 -1
  56. xengsort-2.0.4/xengsort.egg-info/requires.txt +0 -4
  57. {xengsort-2.0.4 → xengsort-2.0.7}/LICENSE +0 -0
  58. {xengsort-2.0.4 → xengsort-2.0.7}/setup.cfg +0 -0
  59. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/io/seqio.py +0 -0
  60. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/lowlevel/aligned_arrays.py +0 -0
  61. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/lowlevel/debug.py +0 -0
  62. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/lowlevel/libc.py +0 -0
  63. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/mask.py +0 -0
  64. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/values/xenograft.py +0 -0
  65. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/xengsort/xengsort_remove.py +0 -0
  66. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort/xengsort/xengsort_weak.py +0 -0
  67. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort.egg-info/dependency_links.txt +0 -0
  68. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort.egg-info/entry_points.txt +0 -0
  69. {xengsort-2.0.4 → xengsort-2.0.7}/xengsort.egg-info/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
- include xengsort/xengsort/*/*
1
+ include xengsort/xengsort/*
2
2
  include xengsort/io/*
3
3
  include xengsort/lowlevel/*
4
4
  include xengsort/values/*
5
+ include xengsort/config/*
@@ -1,9 +1,30 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xengsort
3
- Version: 2.0.4
4
- Summary: A fast xenograft read sorter based on space-efficient k-mer hashing
3
+ Version: 2.0.7
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
- License: MIT
6
+ License: MIT License
7
+
8
+ Copyright (c) 2019-2022 Jens Zentgraf & Sven Rahmann
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
7
28
  Project-URL: Homepage, https://gitlab.com/genomeinformatics/xengsort
8
29
  Project-URL: Bug Tracker, https://gitlab.com/genomeinformatics/xengsort/issues
9
30
  Classifier: Programming Language :: Python :: 3
@@ -14,13 +35,14 @@ Classifier: Environment :: Console
14
35
  Classifier: Intended Audience :: Science/Research
15
36
  Classifier: Natural Language :: English
16
37
  Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
17
- Requires-Python: ==3.11
38
+ Requires-Python: >=3.12
18
39
  Description-Content-Type: text/markdown
19
40
  License-File: LICENSE
20
- Requires-Dist: numpy>=1.24
21
- Requires-Dist: numba>=0.57
41
+ Requires-Dist: numpy>=2
42
+ Requires-Dist: numba>=0.60
22
43
  Requires-Dist: pytest
23
- Requires-Dist: jsonargparse
44
+ Requires-Dist: jsonargparse>=4.29.0
45
+ Requires-Dist: pip
24
46
 
25
47
  # xengsort: Fast lightweight accurate xenograft sorting
26
48
 
@@ -116,7 +138,7 @@ conda create --name xengsort -c conda-forge -c bioconda --file requirements.txt
116
138
  ```
117
139
  which will do the same thing using the packages mentioned in `requirements.txt`.
118
140
 
119
- **Note:** While xengsort works on Windows/Mac without problems, some of the required packages may currently not exist for Windows/Mac.
141
+ **Note:** While xengsort works on Linux without problems, some of the required packages may currently not exist for Windows/Mac.
120
142
 
121
143
  Setting up the environment may take some time, as conda searches and downloads packages.
122
144
  After all dependencies are resolved, you activate the environment and install the package from the repository into this environment.
@@ -127,7 +149,7 @@ pip install -e . # install xengsort package using pip
127
149
  ```
128
150
 
129
151
 
130
- ### Run the Snakemake example workflow
152
+ ## Run the Snakemake example workflow
131
153
 
132
154
  You can in principle remove `snakemake-minimal` and `sra-toolkit` from the list of packages.
133
155
  However, you will not be able to automatically run the provided example workflow or download sequence datasets from SRA.
@@ -170,7 +192,7 @@ This will run for quite some time (especially downloads and index creation), bes
170
192
  The results will appear in the `results/` directory.
171
193
 
172
194
  To run your own human/mouse xenograft analysis, you can continue to use the same index.
173
- All reference files, including the index in ZARR format (`.zarr`) are in the `ref/` directory.
195
+ All reference files, including the index (`.hash` and `.info`) are in the `ref/` directory.
174
196
  You can follow the structure of the Snakefile to create your own custom workflow.
175
197
  Additional information on how to use `xengsort` is given in the usage guide below.
176
198
 
@@ -205,29 +227,27 @@ xengsort index --index myindex -H host.fa.gz -G graft.fa.gz -n 4_500_000_000 [O
205
227
 
206
228
  We must specify the size of the hash table:
207
229
 
208
- - `-n` or `--nobjects`: number of k-mers that will be stored in the hash table. This depends on the used refernece genomes and must be estimated beforehand! As a precise estimate of the number of different k-mers can be difficult, you can err on the safe side and provide a generously large estimate, examine the final (low) load factor and then rebuild the index with a smaller `-n` parameter to aechieve the desired load. There are also some tools that quickly estimate the number of distinct k-mers in large files, such as [ntCard](https://github.com/bcgsc/ntCard) or [KmerEstimate](https://github.com/srbehera11/KmerEstimate). As a guide: Human and mouse genome and transcriptome together comprise around 4.5 billion 25-mers, as shown in the examples above.
230
+ - `-n` or `--nobjects`: number of k-mers that will be stored in the hash table. This depends on the used reference genomes and must be estimated beforehand! As a precise estimate of the number of different k-mers can be difficult, you can err on the safe side and provide a generously large estimate, examine the final (low) load factor and then rebuild the index with a smaller `-n` parameter to achieve the desired load. There are also some tools that quickly estimate the number of distinct k-mers in large files, such as [ntCard](https://github.com/bcgsc/ntCard) or [KmerEstimate](https://github.com/srbehera11/KmerEstimate). As a guide: Human and mouse genome and transcriptome together comprise around 4.5 billion 25-mers, as shown in the examples above.
209
231
  **This option must be specified; there is no default!**
210
232
 
211
233
 
212
234
  We may further specify additional properties of the hash table:
213
235
 
214
- - `-p` or `--pagesize` indicates how many elements can be stored in one bucket (or page). This is 4 by default.
236
+ - `-b` or `--bucketsize` indicates how many elements can be stored in one bucket (or page). This is 4 by default.
215
237
 
216
238
  - `--fill` between 0.0 and 1.0 describes the desired fill rate or load factor of the hash table.
217
239
  Together with `-n`, the number of slots in the table is calculated as `ceil(n/fill)`. In our experiments we used 0.88. (The number of buckets is then the smallest odd integer that is at least `ceil(ceil(n/fill)/p)`.)
218
240
 
219
- - `--aligned` or `--unaligned`: indicates whether each bucket should consume a number of bits that is a power of 2. Using `--aligned` ensures that each bucket stays within the same cache line, but may waste space (padding bits), yielding faster speed but possibliy (much!) larger space requirements. With `--unaligned`, no bits are used for padding and buckets may cross cache line boundaries. This is slightly slower, but may save a little or a lot of space (depending on the bucket size in bits). The default is `--unaligned`, because the speed decrease is small and the memory savings can be significant.
241
+ - `--aligned` or `--unaligned`: indicates whether each bucket should consume a number of bits that is a power of 2. Using `--aligned` ensures that each bucket stays within the same cache line, but may waste space (padding bits), yielding faster speed but possibly (much!) larger space requirements. With `--unaligned`, no bits are used for padding and buckets may cross cache line boundaries. This is slightly slower, but may save a little or a lot of space (depending on the bucket size in bits). The default is `--unaligned`, because the speed decrease is small and the memory savings can be significant.
220
242
 
221
243
  - `--hashfunctions` defines the parameters for the hash functions used to store the key-value pairs. If the parameter is unspecified, different random functions are chosen each time. The hash functions can be specified using a colon separated list: `--hashfunctions linear945:linear9123641:linear349341847`. It is recommended to have them chosen randomly unless you need strictly reproducible behavior, in which case the example given here is recommended.
222
244
 
223
- The final important parameter is about paralellization:
245
+ The final important parameter is about parallelization:
224
246
 
225
- - `-T` defines how many threads are used to calculate weak *k*-mers.
226
-
227
- The following hash table options should not be modified from their default values and are for internal development only:
228
-
229
- - `-P` or `--parameters` can be used to combine all parameters stated above into one string (see the `--help` text)
247
+ - `-W` or `--weakthreads` defines how many threads are used to calculate weak *k*-mers.
230
248
 
249
+ Most of the parameters can also be provided in a config file (`.yaml`):
250
+ - `--cfg` or `--config` defines the path the the config file.
231
251
 
232
252
  ### How to classify
233
253
 
@@ -92,7 +92,7 @@ conda create --name xengsort -c conda-forge -c bioconda --file requirements.txt
92
92
  ```
93
93
  which will do the same thing using the packages mentioned in `requirements.txt`.
94
94
 
95
- **Note:** While xengsort works on Windows/Mac without problems, some of the required packages may currently not exist for Windows/Mac.
95
+ **Note:** While xengsort works on Linux without problems, some of the required packages may currently not exist for Windows/Mac.
96
96
 
97
97
  Setting up the environment may take some time, as conda searches and downloads packages.
98
98
  After all dependencies are resolved, you activate the environment and install the package from the repository into this environment.
@@ -103,7 +103,7 @@ pip install -e . # install xengsort package using pip
103
103
  ```
104
104
 
105
105
 
106
- ### Run the Snakemake example workflow
106
+ ## Run the Snakemake example workflow
107
107
 
108
108
  You can in principle remove `snakemake-minimal` and `sra-toolkit` from the list of packages.
109
109
  However, you will not be able to automatically run the provided example workflow or download sequence datasets from SRA.
@@ -146,7 +146,7 @@ This will run for quite some time (especially downloads and index creation), bes
146
146
  The results will appear in the `results/` directory.
147
147
 
148
148
  To run your own human/mouse xenograft analysis, you can continue to use the same index.
149
- All reference files, including the index in ZARR format (`.zarr`) are in the `ref/` directory.
149
+ All reference files, including the index (`.hash` and `.info`) are in the `ref/` directory.
150
150
  You can follow the structure of the Snakefile to create your own custom workflow.
151
151
  Additional information on how to use `xengsort` is given in the usage guide below.
152
152
 
@@ -181,29 +181,27 @@ xengsort index --index myindex -H host.fa.gz -G graft.fa.gz -n 4_500_000_000 [O
181
181
 
182
182
  We must specify the size of the hash table:
183
183
 
184
- - `-n` or `--nobjects`: number of k-mers that will be stored in the hash table. This depends on the used refernece genomes and must be estimated beforehand! As a precise estimate of the number of different k-mers can be difficult, you can err on the safe side and provide a generously large estimate, examine the final (low) load factor and then rebuild the index with a smaller `-n` parameter to aechieve the desired load. There are also some tools that quickly estimate the number of distinct k-mers in large files, such as [ntCard](https://github.com/bcgsc/ntCard) or [KmerEstimate](https://github.com/srbehera11/KmerEstimate). As a guide: Human and mouse genome and transcriptome together comprise around 4.5 billion 25-mers, as shown in the examples above.
184
+ - `-n` or `--nobjects`: number of k-mers that will be stored in the hash table. This depends on the used reference genomes and must be estimated beforehand! As a precise estimate of the number of different k-mers can be difficult, you can err on the safe side and provide a generously large estimate, examine the final (low) load factor and then rebuild the index with a smaller `-n` parameter to achieve the desired load. There are also some tools that quickly estimate the number of distinct k-mers in large files, such as [ntCard](https://github.com/bcgsc/ntCard) or [KmerEstimate](https://github.com/srbehera11/KmerEstimate). As a guide: Human and mouse genome and transcriptome together comprise around 4.5 billion 25-mers, as shown in the examples above.
185
185
  **This option must be specified; there is no default!**
186
186
 
187
187
 
188
188
  We may further specify additional properties of the hash table:
189
189
 
190
- - `-p` or `--pagesize` indicates how many elements can be stored in one bucket (or page). This is 4 by default.
190
+ - `-b` or `--bucketsize` indicates how many elements can be stored in one bucket (or page). This is 4 by default.
191
191
 
192
192
  - `--fill` between 0.0 and 1.0 describes the desired fill rate or load factor of the hash table.
193
193
  Together with `-n`, the number of slots in the table is calculated as `ceil(n/fill)`. In our experiments we used 0.88. (The number of buckets is then the smallest odd integer that is at least `ceil(ceil(n/fill)/p)`.)
194
194
 
195
- - `--aligned` or `--unaligned`: indicates whether each bucket should consume a number of bits that is a power of 2. Using `--aligned` ensures that each bucket stays within the same cache line, but may waste space (padding bits), yielding faster speed but possibliy (much!) larger space requirements. With `--unaligned`, no bits are used for padding and buckets may cross cache line boundaries. This is slightly slower, but may save a little or a lot of space (depending on the bucket size in bits). The default is `--unaligned`, because the speed decrease is small and the memory savings can be significant.
195
+ - `--aligned` or `--unaligned`: indicates whether each bucket should consume a number of bits that is a power of 2. Using `--aligned` ensures that each bucket stays within the same cache line, but may waste space (padding bits), yielding faster speed but possibly (much!) larger space requirements. With `--unaligned`, no bits are used for padding and buckets may cross cache line boundaries. This is slightly slower, but may save a little or a lot of space (depending on the bucket size in bits). The default is `--unaligned`, because the speed decrease is small and the memory savings can be significant.
196
196
 
197
197
  - `--hashfunctions` defines the parameters for the hash functions used to store the key-value pairs. If the parameter is unspecified, different random functions are chosen each time. The hash functions can be specified using a colon separated list: `--hashfunctions linear945:linear9123641:linear349341847`. It is recommended to have them chosen randomly unless you need strictly reproducible behavior, in which case the example given here is recommended.
198
198
 
199
- The final important parameter is about paralellization:
199
+ The final important parameter is about parallelization:
200
200
 
201
- - `-T` defines how many threads are used to calculate weak *k*-mers.
202
-
203
- The following hash table options should not be modified from their default values and are for internal development only:
204
-
205
- - `-P` or `--parameters` can be used to combine all parameters stated above into one string (see the `--help` text)
201
+ - `-W` or `--weakthreads` defines how many threads are used to calculate weak *k*-mers.
206
202
 
203
+ Most of the parameters can also be provided in a config file (`.yaml`):
204
+ - `--cfg` or `--config` defines the path the the config file.
207
205
 
208
206
  ### How to classify
209
207
 
@@ -1,17 +1,35 @@
1
1
  [build-system]
2
- requires = ["setuptools==68.2.2"]
2
+ requires = ["setuptools>=69.0.3"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
+
5
6
  [project]
6
7
  name = "xengsort"
7
- version = "2.0.4"
8
+ version = "2.0.7"
9
+
10
+ license = {file = "LICENSE"}
11
+
8
12
  authors = [
9
13
  { name="Jens Zentgraf", email="zentgraf@cs.uni-saarland.de" },
10
14
  { name="Sven Rahmann", email="rahmann@cs.uni-saarland.de" }
11
15
  ]
12
- description = "A fast xenograft read sorter based on space-efficient k-mer hashing"
16
+
17
+ description = "xenograft sorting of FASTQ reads into five categories: host, graft, both, neither, ambiguous."
18
+
13
19
  readme = "README.md"
14
- requires-python = "==3.11"
20
+
21
+ requires-python = ">=3.12"
22
+
23
+ dependencies = [
24
+ "numpy>=2",
25
+ "numba>=0.60",
26
+ "pytest",
27
+ "jsonargparse>=4.29.0",
28
+ "pip",
29
+ ]
30
+
31
+ scripts = {xengsort = "xengsort.xengsort.xengsort_main:main"}
32
+
15
33
  classifiers = [
16
34
  "Programming Language :: Python :: 3",
17
35
  "License :: OSI Approved :: MIT License",
@@ -22,13 +40,10 @@ classifiers = [
22
40
  "Natural Language :: English",
23
41
  "Topic :: Scientific/Engineering :: Bio-Informatics"
24
42
  ]
25
- dependencies = [
26
- "numpy>=1.24",
27
- "numba>=0.57",
28
- "pytest",
29
- "jsonargparse",
30
- ]
31
43
 
32
44
  [project.urls]
33
45
  "Homepage" = "https://gitlab.com/genomeinformatics/xengsort"
34
- "Bug Tracker" = "https://gitlab.com/genomeinformatics/xengsort/issues"
46
+ "Bug Tracker" = "https://gitlab.com/genomeinformatics/xengsort/issues"
47
+
48
+ [tool.setuptools]
49
+ packages=["xengsort"]
@@ -0,0 +1,28 @@
1
+ from fastcash.io.fastqio import fastq_chunks_paired
2
+
3
+ def test_fastq_chunks_paired_file2_trimmed():
4
+ pair = ("tests/data/test_chunks1.fq", "tests/data/test_chunks2.fq")
5
+ reads1 = 0
6
+ reads2 = 0
7
+ for buf1, linemarks1, buf2, linemarks2 in fastq_chunks_paired(pair, bufsize=4000, maxreads=40):
8
+ assert len(linemarks1) == len(linemarks2)
9
+ for read in range(len(linemarks1)):
10
+ for nuc in range(linemarks2[read][1]-linemarks2[read][0]-1):
11
+ assert buf2[linemarks2[read][0]:linemarks2[read][1]][nuc] == buf1[linemarks1[read][0]:linemarks1[read][1]][nuc]
12
+ reads1 += len(linemarks1)
13
+ reads2 += len(linemarks2)
14
+ assert reads1 == reads2
15
+ assert reads1 == 60
16
+
17
+ def test_fastq_chunks_paired_file1_trimmed():
18
+ pair = ("tests/data/test_chunks2.fq", "tests/data/test_chunks1.fq")
19
+ reads1 = 0
20
+ reads2 = 0
21
+ for buf1, linemarks1, buf2, linemarks2 in fastq_chunks_paired(pair, bufsize=4000, maxreads=40):
22
+ assert len(linemarks1) == len(linemarks2)
23
+ for read in range(len(linemarks1)):
24
+ for nuc in range(linemarks1[read][1]-linemarks1[read][0]-1):
25
+ assert buf2[linemarks2[read][0]:linemarks2[read][1]][nuc] == buf1[linemarks1[read][0]:linemarks1[read][1]][nuc]
26
+ reads1 += len(linemarks1)
27
+ reads2 += len(linemarks2)
28
+ assert reads1 == reads2 == 60
@@ -1,5 +1,5 @@
1
1
  """
2
- xengsort/cptasks_kmers.py
2
+ fastcash/cptasks_kmers.py
3
3
  Functions for compiling ConsumerProducerTasks for k-mers
4
4
  """
5
5
 
@@ -65,6 +65,7 @@ def compile_cptask_scatter_kmers_from_linemarked(
65
65
  if p >= outbufsize:
66
66
  if j >= 0:
67
67
  outcontrol[j, 7] = outbufsize
68
+ outcontrol[j, 6] = 1 # buffer type k-mer codes
68
69
  mark_buffer_for_reading(outcontrol, j)
69
70
  stj = j - st * noutbuffers_per_subtable # the "local" j for subtable st
70
71
  assert 0 <= stj < noutbuffers_per_subtable
@@ -175,6 +176,221 @@ def compile_cptask_scatter_kmers_from_linemarked(
175
176
  assert outbuffers.shape[1] >= outbufsize
176
177
  assert outinfos.shape[1] == 0
177
178
 
179
+ wait_read = wait_write = 0
180
+ nactive = -1 # active input buffer
181
+ outidx = np.full(nsubtables, (-1), dtype=np.int32)
182
+ outpos = np.full(nsubtables, outbufsize, dtype=np.int32)
183
+ while True:
184
+ nactive, wait = find_buffer_for_reading(incontrol, nactive)
185
+ wait_read += wait
186
+ if nactive < 0: # all finished
187
+ errorcode = int(nactive <= -2)
188
+ break
189
+ active_buffer = inbuffers[nactive]
190
+ buffer_type = incontrol[nactive, 6]
191
+ if buffer_type == 0:
192
+ nseqs = incontrol[nactive, 7]
193
+ errorcode = 0
194
+ linemarks = linemarkbuffers[nactive]
195
+ for i in range(nseqs):
196
+ sq = active_buffer[linemarks[i, 0]:linemarks[i, 1]]
197
+ quick_dna_to_2bits(sq)
198
+ (wait, _errorcode) = _process(
199
+ sq, outidx, outpos, outbuffers, outcontrol)
200
+ wait_write += wait
201
+ errorcode |= _errorcode
202
+ if errorcode:
203
+ mark_my_buffers_failed(incontrol)
204
+ debugprint2("- FAILED: cptask_scatter_kmers", errorcode)
205
+ break
206
+ elif buffer_type == 1:
207
+ nkeys = incontrol[nactive, 7]
208
+ errorcode = 0
209
+ active_buffer = active_buffer.view(np.uint64)
210
+ for i in range(nkeys):
211
+ key = active_buffer[i]
212
+ (wait, _errorcode) = _store(
213
+ key, outidx, outpos, outbuffers, outcontrol)
214
+ wait_write += wait
215
+ errorcode |= _errorcode
216
+ if errorcode:
217
+ mark_my_buffers_failed(incontrol)
218
+ debugprint2("- FAILED: cptask_scatter_kmers", errorcode)
219
+ break
220
+ else:
221
+ raise ValueError("- buffer type {buffer_type} not supported in cptask_scatter_kmers")
222
+
223
+ mark_buffer_for_writing(incontrol, nactive)
224
+ # We break out of while True loop when all bufferes were marked as finished by FASTQ reader processes
225
+ # We then let the next task read our output buffers one final time.
226
+ if not errorcode:
227
+ for st in range(nsubtables):
228
+ j = outidx[st]
229
+ if j >= 0:
230
+ # assert outpos[st] > 0
231
+ outcontrol[j, 7] = outpos[st]
232
+ mark_buffer_for_reading(outcontrol, j)
233
+ debugprint2("- ending: cptask_scatter_kmers", errorcode)
234
+ return (wait_read, wait_write, -errorcode)
235
+
236
+ return _cptask_scatter_kmers
237
+
238
+
239
+ def compile_cptask_scatter_kmers_position_from_linemarked(
240
+ mask, values, rcmode, hf0,
241
+ nsubtables, noutbuffers_per_subtable, outbufsize):
242
+ """
243
+ Compile a cptask function that
244
+ creates (gapped) k-mers and value from linemarked buffers.
245
+ The Value is a compressed representation of chromosome and position.
246
+ - mask: a Mask object
247
+ - rcmode: how to treat reverse complements; f, r, b, max, min
248
+ - hf0: the 0-th hash function that determines the subtable
249
+ - nsubtables: the number of subtables
250
+ - noutbuffers_per_subtable: number of output buffers per subtable
251
+ - outbufsize: the output buffer size for which to compile this function
252
+ """
253
+ debugprint0, debugprint1, debugprint2 = debug.debugprint
254
+ both = (rcmode[0].lower() == "b")
255
+
256
+ k, w, tpl = mask.k, mask.w, mask.tuple
257
+ assert len(tpl) == k
258
+ if k < 1 or k > 32:
259
+ raise ValueError(f"Only 1 <= k <= 32 is supported, but {k=}.")
260
+ codemask = uint64(4**(k - 1) - 1)
261
+ revcomp, ccode = compile_revcomp_and_canonical_code(k, rcmode)
262
+ if mask.is_contiguous:
263
+ debugprint1(f"- compiling cptask_scatter_kmers for contiguous {k}-mers.")
264
+ else:
265
+ debugprint1(f"- compiling cptask_scatter_kmers for gapped ({k},{w})-mers: {mask.mask}.")
266
+
267
+ merge_seq_pos = values.merge_seq_pos
268
+
269
+ @njit(nogil=True, locals=dict(
270
+ code=uint64, st=uint64, sk=uint64,
271
+ j=int32, stj=int32, p=int32, wait=int64))
272
+ def _store(code, chrom, position, outidx, outpos, outbuffers, outcontrol):
273
+ st, sk = hf0(code)
274
+ j = outidx[st]
275
+ p = outpos[st]
276
+ error = 0
277
+ wait_write = 0 # TODO: do we count wait time correctly?
278
+ if p >= outbufsize:
279
+ if j >= 0:
280
+ outcontrol[j, 7] = outbufsize
281
+ outcontrol[j, 6] = 1 # buffer type k-mer codes
282
+ mark_buffer_for_reading(outcontrol, j)
283
+ stj = j - st * noutbuffers_per_subtable # the "local" j for subtable st
284
+ assert 0 <= stj < noutbuffers_per_subtable
285
+ else:
286
+ assert j == -1
287
+ stj = -1
288
+ stoutcontrol = outcontrol[(st * noutbuffers_per_subtable):(st + 1) * noutbuffers_per_subtable]
289
+ stj, wait = find_buffer_for_writing(stoutcontrol, stj)
290
+ outidx[st] = j = (stj + st * noutbuffers_per_subtable)
291
+ outpos[st] = p = 0
292
+ error = outcontrol[j, 1]
293
+ outbuffers[j, p] = sk # put subkey (not code!) into outbuffer
294
+ value = merge_seq_pos(chrom, position)
295
+ outbuffers[j, p + 1] = value
296
+ outpos[st] += 2
297
+ return (wait, error)
298
+
299
+ @njit(nogil=True, locals=dict(
300
+ code=uint64, i=int64, j=int64, c=uint64,
301
+ errorcode=int32, _errorcode=int32,
302
+ wait=int64, _wait=int64))
303
+ def _process_gapped(seq, chrom, offset, *allout): # (outidx, outpos, outbuffers, outcontrol):
304
+ startpoints = seq.size - tpl[k - 1]
305
+ errorcode = 0
306
+ wait = 0
307
+ for i in range(startpoints):
308
+ code = 0
309
+ for j in tpl:
310
+ c = seq[i + j]
311
+ if c > 3:
312
+ break
313
+ code = uint64(code << 2) | uint64(c)
314
+ else: # no break
315
+ if both:
316
+ _wait, errorcode = _store(code, chrom, offset + i, *allout) # outidx, outpos, outbuffers, outcontrol)
317
+ wait += wait
318
+ _wait, _errorcode = _store(revcomp(code), chrom, offset + i, *allout) # outidx, outpos, outbuffers, outcontrol)
319
+ wait += _wait
320
+ errorcode |= _errorcode
321
+ else:
322
+ _wait, errorcode = _store(ccode(code), chrom, offset + i, *allout) # outidx, outpos, outbuffers, outcontrol)
323
+ wait += _wait
324
+ if errorcode != 0:
325
+ break
326
+ return (wait, errorcode)
327
+
328
+ @njit(nogil=True, locals=dict(
329
+ code=uint64, i=int64, j=int64, c=uint64, errorcode=int32,
330
+ wait=int64, _wait=int64))
331
+ def _process_contiguous(seq, chrom, offset, *allout): # (outidx, outpos, outbuffers, outcontrol):
332
+ endpoint = seq.size - (k - 1)
333
+ valid = False
334
+ errorcode = 0
335
+ wait = 0
336
+ i = 0
337
+ while i < endpoint:
338
+ if not valid:
339
+ code = 0
340
+ for j in range(k):
341
+ c = seq[i + j]
342
+ if c > 3:
343
+ i += j + 1 # skip invalid
344
+ break
345
+ code = (code << 2) | c
346
+ else: # no break
347
+ valid = True
348
+ if not valid:
349
+ continue # with while
350
+ else: # was valid, we have an old code
351
+ c = seq[i + k - 1]
352
+ if c > 3:
353
+ valid = False
354
+ i += k # skip invalid
355
+ continue # with while
356
+ code = ((code & codemask) << 2) | c
357
+ # at this point, we have a valid code
358
+ if both:
359
+ _wait, errorcode = _store(code, chrom, offset + i, *allout) # outidx, outpos, outbuffers, outcontrol)
360
+ wait += _wait
361
+ _wait, _errorcode = _store(revcomp(code), chrom, offset + i, *allout) # outidx, outpos, outbuffers, outcontrol)
362
+ wait += _wait
363
+ errorcode |= _errorcode
364
+ else:
365
+ _wait, errorcode = _store(ccode(code), chrom, offset + i, *allout) # outidx, outpos, outbuffers, outcontrol)
366
+ wait += _wait
367
+ i += 1
368
+ if errorcode != 0:
369
+ break
370
+ return (wait, errorcode)
371
+
372
+ _process = _process_contiguous if mask.is_contiguous else _process_gapped
373
+
374
+ @njit(nogil=True, locals=dict(
375
+ nactive=int32, nseqs=int32, i=int32, j=int32, outj=int32,
376
+ wait=int64, wait_read=int64, wait_write=int64,
377
+ errorcode=int32, _errorcode=int32))
378
+ def _cptask_scatter_kmers(inbuffers, incontrol, ininfos, outbuffers, outcontrol, outinfos):
379
+ # find a linemarked buffer to read (among inbuffers)
380
+ # find and reserve nsubtables output buffers among the output buffers [outinfos]
381
+ # incontrol[i, 7]: number of FASTQ reads in input buffer i
382
+ # ininfos[i]: linemarks for input buffer i
383
+ # outcontrol[B, 7]: k-mers in buffer B
384
+ debugprint2("- running: cptask_scatter_kmers; outbuffers shape =", outbuffers.shape)
385
+ assert inbuffers.ndim == 2
386
+ assert outbuffers.ndim == 2
387
+ M, N = ininfos.shape
388
+ assert N % 6 == 0 # We have 6 entries per linemark. Start and end of record and read and chromosome and positions
389
+ linemarkbuffers = ininfos.reshape(M, N // 6, 6)
390
+ assert outbuffers.shape[0] == nsubtables * noutbuffers_per_subtable
391
+ assert outbuffers.shape[1] >= outbufsize
392
+ assert outinfos.shape[1] == 0
393
+
178
394
  wait_read = wait_write = 0
179
395
  nactive = -1 # active input buffer
180
396
  outidx = np.full(nsubtables, (-1), dtype=np.int32)
@@ -187,13 +403,18 @@ def compile_cptask_scatter_kmers_from_linemarked(
187
403
  break
188
404
  active_buffer = inbuffers[nactive]
189
405
  nseqs = incontrol[nactive, 7]
406
+ buffer_type = incontrol[nactive, 6]
407
+ assert buffer_type == 0 # 0 means sequence buffer
408
+
190
409
  errorcode = 0
191
410
  linemarks = linemarkbuffers[nactive]
192
411
  for i in range(nseqs):
193
412
  sq = active_buffer[linemarks[i, 0]:linemarks[i, 1]]
413
+ chromosome = linemarks[i, 4]
414
+ position = linemarks[i, 5]
194
415
  quick_dna_to_2bits(sq)
195
416
  (wait, _errorcode) = _process(
196
- sq, outidx, outpos, outbuffers, outcontrol)
417
+ sq, chromosome, position, outidx, outpos, outbuffers, outcontrol)
197
418
  wait_write += wait
198
419
  errorcode |= _errorcode
199
420
  if errorcode:
@@ -201,7 +422,7 @@ def compile_cptask_scatter_kmers_from_linemarked(
201
422
  debugprint2("- FAILED: cptask_scatter_kmers", errorcode)
202
423
  break
203
424
  mark_buffer_for_writing(incontrol, nactive)
204
- # We break out of while True loop when all bufferes were marked as finished by FASTQ reader processes
425
+ # We break out of while True loop when all buffers were marked as finished by FASTQ reader processes
205
426
  # We then let the next task read our output buffers one final time.
206
427
  if not errorcode:
207
428
  for st in range(nsubtables):
@@ -216,6 +437,68 @@ def compile_cptask_scatter_kmers_from_linemarked(
216
437
  return _cptask_scatter_kmers
217
438
 
218
439
 
440
+ def compile_cptask_insert_subkey_value(
441
+ myhashtable,
442
+ maxfailures=0,
443
+ maxwalk=1000):
444
+ debugprint0, debugprint1, debugprint2 = debug.debugprint
445
+ update = myhashtable.private.update_ssk
446
+
447
+ @njit(nogil=True, locals=dict(
448
+ nactive=int32, value=uint64, mytotal=int64, failed=int64,
449
+ nsubkeys=int32, subkey=uint64, status=int32, result=uint64,
450
+ wait=int64, wait_read=int64, wait_write=int64))
451
+ def _cptask_insert_subkey_value(st, ht, ft, inbuffers, incontrol, ininfos, outbuffers, outcontrol, outinfos):
452
+ """
453
+ Insert subkeys and values of k-mers into subtables of a hash table.
454
+ """
455
+ # incontrol[i, 0]: buffer status
456
+ # incontrol[i, 1]: set to nonzero on error
457
+ # incontrol[i, 7]: k-mers (or subkeys) in input buffer i
458
+ assert inbuffers.ndim == 2
459
+ assert outbuffers.ndim == 2
460
+ assert outbuffers.shape[0] == 1
461
+ assert outbuffers.shape[1] >= maxwalk + 1
462
+ assert outinfos.shape[1] == 0
463
+ debugprint2("- running: cptask_insert_subkey_value; subtable", st)
464
+
465
+ wait_read = wait_write = 0
466
+ nactive = -1 # active input buffer
467
+ active_buffer = inbuffers[0] # irrelevant
468
+ out = outbuffers[0]
469
+ failed = mytotal = 0
470
+ while True:
471
+ nactive, wait = find_buffer_for_reading(incontrol, nactive)
472
+ wait_read += wait
473
+ if nactive < 0: # all finished
474
+ debugprint2("- ending: cptask_insert_subkey_value; subtable", st, "with", mytotal, "total and", failed, "failed k-mers.")
475
+ break
476
+ active_buffer = inbuffers[nactive]
477
+ nsubkeys = incontrol[nactive, 7]
478
+ buffer_type = incontrol[nactive, 6]
479
+ assert buffer_type == 1 # 1 means k-mer/subkey buffer
480
+ # TODO: NOTE: This is an interleaved subkey/value buffer.
481
+ # Is this a different type?
482
+
483
+ # consume the buffer
484
+ for i in range(0, nsubkeys, 2):
485
+ subkey = active_buffer[i]
486
+ value = active_buffer[i + 1]
487
+ status, result = update(ht, st, subkey, value) # see docstring below
488
+ if status & 128 == 0:
489
+ out[result] += 1
490
+ failed += (status == 0)
491
+ mytotal += nsubkeys
492
+ mark_buffer_for_writing(incontrol, nactive)
493
+ if failed > maxfailures:
494
+ mark_my_buffers_failed(incontrol)
495
+ debugprint2("- FAILED: cptask_insert_subkey_value; subtable", st, "with", mytotal, "total and", failed, "failed k-mers.")
496
+ break
497
+ return (mytotal, failed, wait_read, wait_write, -(failed > 0))
498
+
499
+ return _cptask_insert_subkey_value
500
+
501
+
219
502
  def compile_cptask_insert_filtered_subkeys(
220
503
  myhashtable,
221
504
  myfilter,
@@ -229,7 +512,7 @@ def compile_cptask_insert_filtered_subkeys(
229
512
  else:
230
513
  wwo = "without"
231
514
 
232
- @njit(nogil=True, inline="always")
515
+ @njit(nogil=True)
233
516
  def lookup_and_insert_in_subfilter(ft, st, subkey):
234
517
  return True
235
518
  update = myhashtable.private.update_ssk
@@ -268,6 +551,9 @@ def compile_cptask_insert_filtered_subkeys(
268
551
  break
269
552
  active_buffer = inbuffers[nactive]
270
553
  nsubkeys = incontrol[nactive, 7]
554
+ buffer_type = incontrol[nactive, 6]
555
+ assert buffer_type == 1 # 1 means k-mer buffer (or subkeys)
556
+
271
557
  # consume the buffer
272
558
  for subkey in active_buffer[:nsubkeys]:
273
559
  if lookup_and_insert_in_subfilter(ft, st, subkey):
@@ -316,11 +602,22 @@ def compile_cptask_update_existing_subkeys(
316
602
  break
317
603
  active_buffer = inbuffers[nactive]
318
604
  nsubkeys = incontrol[nactive, 7]
605
+ buffer_type = incontrol[nactive, 6]
606
+ # print("&&& buffer_type", buffer_type)
607
+ assert buffer_type == 1 # 1 means k-mer buffer (0 would be sequence buffer)
608
+
319
609
  # consume the buffer
320
- for subkey in active_buffer[:nsubkeys]:
321
- status, result = update_existing(ht, st, subkey, value) # see docstring below
322
- if status & 128 == 0:
323
- failed += (status == 0)
610
+ for ii in range(nsubkeys):
611
+ subkey = active_buffer[ii]
612
+ _, _ = update_existing(ht, st, subkey, value) # see docstring below
613
+ # status, result = update_existing(ht, st, subkey, value) # see docstring below
614
+ # if status & 128:
615
+ # print("update ->", result)
616
+ # else:
617
+ # print("ignore unknown")
618
+ # if status & 128 == 0:
619
+ # failed += (status == 0) # TODO: should we not just ignore new stuff?
620
+ # The current implementation will count not directly insertable stuff as failures.
324
621
  mytotal += nsubkeys
325
622
  mark_buffer_for_writing(incontrol, nactive)
326
623
  debugprint2("- ending: cptask_update_existing_subkeys; subtable", st, "with", mytotal, "total and", failed, "failed k-mers.")