xengsort 2.2.0__tar.gz → 2.2.1__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.
- {xengsort-2.2.0/xengsort.egg-info → xengsort-2.2.1}/PKG-INFO +1 -1
- {xengsort-2.2.0 → xengsort-2.2.1}/pyproject.toml +1 -1
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/fastcash_main.py +5 -4
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/fastqio.py +56 -30
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/kmers.py +133 -3
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/libc.py +7 -5
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/llvm.py +27 -2
- {xengsort-2.2.0 → xengsort-2.2.1/xengsort.egg-info}/PKG-INFO +1 -1
- {xengsort-2.2.0 → xengsort-2.2.1}/LICENSE +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/MANIFEST.in +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/README.md +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/setup.cfg +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/tests/test_trimmed_reads.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/cptasks_kmers.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/dnaencode.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/dnaencode_fast.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/fastcash_info.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/fastcash_optimize.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/hash_minimizer.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/hash_s3c_fbcbvb.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/hash_srh_fbcbvb.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/binaryio.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/fastaio.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/generalio.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/hashio.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/seqio.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/textio.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/xorio.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/aligned_arrays.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/bitarray.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/conpro.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/debug.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/intbitarray.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/numbautils.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/packedarray.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/mask.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/mathutils.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/parameters.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/srhash.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/subtable_hashfunctions.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/values/xenograft.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/config/classify.yaml +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/config/index.yaml +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_classify.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_index.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_info.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_load.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_main.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_remove.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_weak.py +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort.egg-info/SOURCES.txt +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort.egg-info/dependency_links.txt +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort.egg-info/entry_points.txt +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/xengsort.egg-info/requires.txt +0 -0
- {xengsort-2.2.0 → xengsort-2.2.1}/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.
|
|
3
|
+
Version: 2.2.1
|
|
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
|
|
@@ -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]")
|
|
@@ -383,6 +383,7 @@ def cptask_read_pairedfastq_into_buffers(fd, inbuffers, incontrol, ininfos, outb
|
|
|
383
383
|
else:
|
|
384
384
|
assert False
|
|
385
385
|
|
|
386
|
+
# count new lines to determine last complete record pair in both buffers
|
|
386
387
|
nlines1, nlines2 = 0, 0
|
|
387
388
|
for i in range(ntodo1):
|
|
388
389
|
nlines1 += active_buffer1[i] == 10
|
|
@@ -392,9 +393,6 @@ def cptask_read_pairedfastq_into_buffers(fd, inbuffers, incontrol, ininfos, outb
|
|
|
392
393
|
nseqs2 = nlines2 // 4
|
|
393
394
|
nseqs = min(nseqs1, nseqs2)
|
|
394
395
|
|
|
395
|
-
outcontrol[nactive, 7] = nseqs
|
|
396
|
-
outcontrol[nactive, 6] = 0
|
|
397
|
-
|
|
398
396
|
rm_lines = nlines1 - nseqs * 4
|
|
399
397
|
assert rm_lines >= 0
|
|
400
398
|
if rm_lines == 0 and active_buffer1[ntodo1 - 1] == 10:
|
|
@@ -419,14 +417,19 @@ def cptask_read_pairedfastq_into_buffers(fd, inbuffers, incontrol, ininfos, outb
|
|
|
419
417
|
break
|
|
420
418
|
offset2 += 1
|
|
421
419
|
|
|
420
|
+
# update outcontrol
|
|
421
|
+
outcontrol[nactive, 7] = nseqs
|
|
422
|
+
outcontrol[nactive, 4] = ntodo1 - offset1
|
|
423
|
+
outcontrol[nactive, 5] = ntodo2 - offset2
|
|
424
|
+
outcontrol[nactive, 6] = 0
|
|
425
|
+
|
|
422
426
|
mark_buffer_for_writing(outcontrol, nactive, force=True) # nothing left to read; re-use buffer
|
|
423
427
|
return (wait_read, wait_write, errorcode)
|
|
424
428
|
|
|
425
429
|
|
|
426
430
|
@njit(nogil=True, locals=dict(
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
wait=int64, wait_read=int64, wait_write=int64, buffersize=int64))
|
|
431
|
+
ntodo=int32, errorcode=int32, offset=int32, last_record_end=int32,
|
|
432
|
+
wait=int64, wait_read=int64, wait_write=int64, buffersize=int64, nactive=int32))
|
|
430
433
|
def cptask_read_fastq_into_buffers(fd, inbuffers, incontrol, ininfos, outbuffers, outcontrol, outinfos):
|
|
431
434
|
"""
|
|
432
435
|
Has to be run as a thread within a consumer producer task.
|
|
@@ -439,9 +442,10 @@ def cptask_read_fastq_into_buffers(fd, inbuffers, incontrol, ininfos, outbuffers
|
|
|
439
442
|
assert N % 4 == 0
|
|
440
443
|
|
|
441
444
|
offset = ntodo = wait_read = wait_write = 0
|
|
442
|
-
nseqs = 0
|
|
443
445
|
nactive = -1
|
|
444
446
|
active_buffer = outbuffers[0] # irrelevant
|
|
447
|
+
line_starts = np.zeros(4, dtype=np.int8)
|
|
448
|
+
line_start_positions = np.zeros(4, np.int32)
|
|
445
449
|
|
|
446
450
|
while True:
|
|
447
451
|
old = nactive
|
|
@@ -474,25 +478,45 @@ def cptask_read_fastq_into_buffers(fd, inbuffers, incontrol, ininfos, outbuffers
|
|
|
474
478
|
break
|
|
475
479
|
ntodo = offset
|
|
476
480
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
outcontrol[nactive, 7] = nseqs
|
|
483
|
-
outcontrol[nactive, 6] = 0
|
|
484
|
-
|
|
485
|
-
rm_lines = nlines - nseqs * 4
|
|
486
|
-
assert rm_lines >= 0
|
|
487
|
-
if rm_lines == 0 and active_buffer[ntodo - 1] == 10:
|
|
488
|
-
offset = 0
|
|
481
|
+
# search for last complete entry
|
|
482
|
+
record_end = 0
|
|
483
|
+
if active_buffer[ntodo - 1] == 10:
|
|
484
|
+
record_end = ntodo
|
|
489
485
|
else:
|
|
490
|
-
nlines = offset = 0
|
|
491
486
|
for i in range(ntodo):
|
|
492
|
-
|
|
493
|
-
|
|
487
|
+
if active_buffer[ntodo - i - 1] == 10:
|
|
488
|
+
record_end = ntodo - i
|
|
494
489
|
break
|
|
495
|
-
|
|
490
|
+
|
|
491
|
+
idx = 0
|
|
492
|
+
for i in range(record_end - 1):
|
|
493
|
+
if active_buffer[record_end - i - 1] == 10:
|
|
494
|
+
line_starts[4 - idx] = active_buffer[record_end - i]
|
|
495
|
+
line_start_positions[4 - idx] = record_end - i
|
|
496
|
+
idx += 1
|
|
497
|
+
if idx == 5:
|
|
498
|
+
break
|
|
499
|
+
|
|
500
|
+
last_record_end = 0
|
|
501
|
+
if idx < 4:
|
|
502
|
+
last_record_end = 0
|
|
503
|
+
elif idx == 4 and line_starts[2] == 43 and active_buffer[0] == 64:
|
|
504
|
+
last_record_end = record_end
|
|
505
|
+
elif idx > 4 and line_starts[0] == 64 and line_starts[2] == 43:
|
|
506
|
+
last_record_end = record_end
|
|
507
|
+
elif idx > 4 and line_starts[1] == 43 and line_starts[3] == 64:
|
|
508
|
+
last_record_end = line_start_positions[3]
|
|
509
|
+
elif idx > 4 and line_starts[0] == 43 and line_starts[2] == 64:
|
|
510
|
+
last_record_end = line_start_positions[2]
|
|
511
|
+
elif idx > 4 and line_starts[1] == 64 and line_starts[3] == 43:
|
|
512
|
+
last_record_end = line_start_positions[1]
|
|
513
|
+
else:
|
|
514
|
+
debugprint0("- Invalid FASTQ file, each record must have four lines.")
|
|
515
|
+
|
|
516
|
+
# add bytes up to last complete record to outcontrol
|
|
517
|
+
offset = ntodo - last_record_end
|
|
518
|
+
outcontrol[nactive, 4] = last_record_end
|
|
519
|
+
outcontrol[nactive, 6] = 0
|
|
496
520
|
|
|
497
521
|
mark_buffer_for_writing(outcontrol, nactive, force=True) # nothing left to read; re-use buffer
|
|
498
522
|
return (wait_read, wait_write, errorcode)
|
|
@@ -566,7 +590,7 @@ def _find_fastq_seqmarks(buf, linemarks, sequence_id=-1):
|
|
|
566
590
|
|
|
567
591
|
Return pair (m, nxt), where:
|
|
568
592
|
m: number of sequences processed
|
|
569
|
-
nxt: position at which to continue processing
|
|
593
|
+
nxt: position at which to continue processing
|
|
570
594
|
in the next iteration (start of new entry, '@')
|
|
571
595
|
For sequence i, we have
|
|
572
596
|
linemarks[i, 0] = start of sequence line
|
|
@@ -628,7 +652,7 @@ def fastq_chunks(files,
|
|
|
628
652
|
Yield all chunks from a list or tuple of FASTQ 'files'.
|
|
629
653
|
A chunk is a pair (buffer, linemarks), where
|
|
630
654
|
- buffer is readable/writable byte buffer
|
|
631
|
-
- buffer[linemarks[i,0]:linemarks[i,1]] contains the i-th sequence
|
|
655
|
+
- buffer[linemarks[i,0]:linemarks[i,1]] contains the i-th sequence
|
|
632
656
|
of the chunk; other line marks are not stored.
|
|
633
657
|
- The part of linemarks that is returned is such that
|
|
634
658
|
linemarks.shape[0] is at most maxreads and equals the number of reads.
|
|
@@ -676,7 +700,7 @@ def fastq_chunks_paired(pair, bufsize=2**23, maxreads=2**23 // 200):
|
|
|
676
700
|
Yield all chunks from a pair of InputFileHandler objects.
|
|
677
701
|
A chunk is a pair (buffer, linemarks), where
|
|
678
702
|
- buffer is readable/writable byte buffer
|
|
679
|
-
- buffer[linemarks[i,0]:linemarks[i,1]] contains the i-th sequence
|
|
703
|
+
- buffer[linemarks[i,0]:linemarks[i,1]] contains the i-th sequence
|
|
680
704
|
of the chunk; other line marks are not stored.
|
|
681
705
|
- The part of linemarks that is returned is such that
|
|
682
706
|
linemarks.shape[0] is at most maxreads and equals the number of reads.
|
|
@@ -711,7 +735,8 @@ def fastq_chunks_paired(pair, bufsize=2**23, maxreads=2**23 // 200):
|
|
|
711
735
|
available2 = prev2 + read2
|
|
712
736
|
|
|
713
737
|
if read1 == 0 and read2 == 0:
|
|
714
|
-
|
|
738
|
+
if available1 == 0 and available2 == 0:
|
|
739
|
+
break
|
|
715
740
|
|
|
716
741
|
m1, cont1 = _find_fastq_seqmarks(buf1[:available1], linemarks1)
|
|
717
742
|
m2, cont2 = _find_fastq_seqmarks(buf2[:available2], linemarks2)
|
|
@@ -727,6 +752,7 @@ def fastq_chunks_paired(pair, bufsize=2**23, maxreads=2**23 // 200):
|
|
|
727
752
|
else:
|
|
728
753
|
chunk = (buf1, linemarks1[:m2], buf2, linemarks2[:m2])
|
|
729
754
|
cont1 = linemarks1[m2, 2]
|
|
755
|
+
|
|
730
756
|
yield chunk
|
|
731
757
|
prev1 = available1 - cont1
|
|
732
758
|
prev2 = available2 - cont2
|
|
@@ -736,8 +762,8 @@ def fastq_chunks_paired(pair, bufsize=2**23, maxreads=2**23 // 200):
|
|
|
736
762
|
if prev2 > 0:
|
|
737
763
|
buf2[:prev2] = buf2[cont2:available2]
|
|
738
764
|
buf2[prev2:] = 0
|
|
739
|
-
|
|
740
|
-
|
|
765
|
+
assert prev1 < cont1
|
|
766
|
+
assert prev2 < cont2
|
|
741
767
|
|
|
742
768
|
|
|
743
769
|
# FASTQ checking ####################################################
|
|
@@ -788,7 +814,7 @@ def fastqcheck_single(files, args):
|
|
|
788
814
|
|
|
789
815
|
def guess_pairs(filenames, replacer):
|
|
790
816
|
"""guess_pairs(filenames, replacer):
|
|
791
|
-
Return a list of file names where the read number (1/2)
|
|
817
|
+
Return a list of file names where the read number (1/2)
|
|
792
818
|
has been replaced by the 'replacer'.
|
|
793
819
|
Example: guess_pairs(['abc_R1.fq', 'xyz_R1.fq'], 2)
|
|
794
820
|
returns ['abc_R2.fq', 'xyz_R2.fq'].
|
|
@@ -328,6 +328,7 @@ def compile_positional_kmer_processor(shp, func, rcmode="f", new_encoding=False)
|
|
|
328
328
|
|
|
329
329
|
return k, processor
|
|
330
330
|
|
|
331
|
+
|
|
331
332
|
def compile_positional_translated_kmer_processor(shp, func, rcmode="f", new_encoding=False):
|
|
332
333
|
"""
|
|
333
334
|
like compile_positional_kmer_processor, but also translates C->T and G->A and
|
|
@@ -480,9 +481,7 @@ def compile_positional_translated_kmer_processor(shp, func, rcmode="f", new_enco
|
|
|
480
481
|
|
|
481
482
|
def compile_positional_kmer_processor_issh(shp, func, rcmode="f", new_encoding=False):
|
|
482
483
|
"""
|
|
483
|
-
|
|
484
|
-
as an additional argument to func:
|
|
485
|
-
func(hashtable, kmercode, position, *parameters)
|
|
484
|
+
computes all gapped k-mers of a sequences and applies func on them
|
|
486
485
|
"""
|
|
487
486
|
both = (rcmode == "both")
|
|
488
487
|
k = len(shp)
|
|
@@ -594,3 +593,134 @@ def compile_positional_kmer_processor_issh(shp, func, rcmode="f", new_encoding=F
|
|
|
594
593
|
if failed is True:
|
|
595
594
|
return
|
|
596
595
|
return k, issh_processor_fast
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def compile_get_gapped_kmers_issh(mask, rcmode='max', new_encoding=True, sampled=False):
|
|
599
|
+
"""
|
|
600
|
+
computes all gapped k-mers of a sequences and writes them to an numpy array
|
|
601
|
+
|
|
602
|
+
"""
|
|
603
|
+
k = mask.k
|
|
604
|
+
shp = mask.tuple
|
|
605
|
+
w = mask.w
|
|
606
|
+
if k < 1 or k > 32:
|
|
607
|
+
raise ValueError(f"only 1<=k<=32 is supported, but k={k}.")
|
|
608
|
+
revcomp, ccode = compile_revcomp_and_canonical_code_new(k, rcmode) if new_encoding else compile_revcomp_and_canonical_code_old(k, rcmode)
|
|
609
|
+
invalid = uint64(np.iinfo(np.uint64).max)
|
|
610
|
+
popcnt = compile_popcount('uint64')
|
|
611
|
+
cttz = compile_cttz('uint64')
|
|
612
|
+
|
|
613
|
+
weight, width = uint64(len(shp)), uint64(max(shp) + 1)
|
|
614
|
+
sig_mask = 0
|
|
615
|
+
m = np.zeros(width, dtype=np.uint64)
|
|
616
|
+
for i in shp:
|
|
617
|
+
sig_mask |= 1 << (width - i - 1)
|
|
618
|
+
sig_mask = uint64(sig_mask)
|
|
619
|
+
|
|
620
|
+
for i in range(width - 1):
|
|
621
|
+
m[i + 1] = m[i] + (sig_mask >> (width - i - 1) & 1)
|
|
622
|
+
|
|
623
|
+
C = np.zeros((width, width), dtype=np.uint64)
|
|
624
|
+
masks = np.zeros((width, width), dtype=np.uint64)
|
|
625
|
+
for g in range(width):
|
|
626
|
+
for j in range(width - 1):
|
|
627
|
+
for k in shp:
|
|
628
|
+
if k + j < width and (sig_mask >> (width - (k + j) - 1) & 1) == 1 and m[k] == m[k + j] - m[j] + m[g]:
|
|
629
|
+
C[g, j] |= 1 << (width - k - 1)
|
|
630
|
+
masks[g, j] |= (3 << 2 * (weight - m[k + j] - 1))
|
|
631
|
+
|
|
632
|
+
@njit(nogil=True, locals=dict(best=uint64, best_i=uint64, best_j=uint64, use=uint64))
|
|
633
|
+
def find_best_prev(s, to_cover):
|
|
634
|
+
best = 0
|
|
635
|
+
best_i, best_j = 0, 1
|
|
636
|
+
for z in range(0, s - 1):
|
|
637
|
+
for k in range(1, s):
|
|
638
|
+
use = popcnt(to_cover & C[z, k])
|
|
639
|
+
if use > best:
|
|
640
|
+
best = use
|
|
641
|
+
best_i, best_j = z, k
|
|
642
|
+
return best_i, best_j
|
|
643
|
+
|
|
644
|
+
@njit(nogil=True, locals=dict(mask=uint64, n_ones=uint64, positions=uint64, n_ones_new=uint64))
|
|
645
|
+
def compute_shift(g, k):
|
|
646
|
+
mask = masks[g, k]
|
|
647
|
+
n_ones = cttz(mask) // 2
|
|
648
|
+
positions = width - shp[weight - n_ones - 1] - 1 + k
|
|
649
|
+
n_ones_new = popcnt(sig_mask & (2**positions - 1))
|
|
650
|
+
return 2 * (n_ones_new - n_ones)
|
|
651
|
+
|
|
652
|
+
steps = []
|
|
653
|
+
last = 0
|
|
654
|
+
# new position does not need to be covered
|
|
655
|
+
to_cover = sig_mask & ~uint64(1)
|
|
656
|
+
while to_cover != 0:
|
|
657
|
+
g, k = find_best_prev(width - 1, to_cover)
|
|
658
|
+
shift = compute_shift(g, k)
|
|
659
|
+
remove = to_cover & C[g, k]
|
|
660
|
+
to_cover &= ~remove
|
|
661
|
+
steps.append([k, shift, masks[g, k]])
|
|
662
|
+
last = max(last, k)
|
|
663
|
+
steps = np.array(steps, dtype=np.uint64)
|
|
664
|
+
|
|
665
|
+
@njit(nogil=True, locals=dict(code=uint64, shift=uint64, k=uint64, mask=uint64, contained_ns=uint64, c=uint8, end_naive=uint64))
|
|
666
|
+
def issh_processor_fast(ht, seq, start, end, *parameters):
|
|
667
|
+
if (end - start) < (width - 1):
|
|
668
|
+
return
|
|
669
|
+
hashes = np.zeros(last, dtype=np.uint64)
|
|
670
|
+
# compute history
|
|
671
|
+
end_naive = min(start + last, end)
|
|
672
|
+
for i in range(start, end_naive):
|
|
673
|
+
contained_ns = 0
|
|
674
|
+
code = 0
|
|
675
|
+
for j in shp:
|
|
676
|
+
c = seq[i + j]
|
|
677
|
+
contained_ns |= (c > 3)
|
|
678
|
+
code = (code << 2) | min(uint64(c), uint64(3))
|
|
679
|
+
hashes[i] = code
|
|
680
|
+
ht[i] = code if not (contained_ns & sig_mask) else invalid
|
|
681
|
+
|
|
682
|
+
# necessary to check for Ns in first window
|
|
683
|
+
contained_ns = 0
|
|
684
|
+
for i in range(width):
|
|
685
|
+
contained_ns = (contained_ns << 1) | (seq[start + last + i] > 3)
|
|
686
|
+
contained_ns = contained_ns >> 1
|
|
687
|
+
|
|
688
|
+
# compute gapped k-mers iteratively based on previous gapped k-mers
|
|
689
|
+
for i in range(start + last, (end - start) - (width - 1)):
|
|
690
|
+
contained_ns = contained_ns << 1
|
|
691
|
+
code = 0
|
|
692
|
+
for k, shift, mask in steps:
|
|
693
|
+
code |= (hashes[(i - k) % last] & mask) << shift
|
|
694
|
+
c = seq[i + width - 1]
|
|
695
|
+
contained_ns |= c > 3
|
|
696
|
+
code |= min(uint64(c), uint64(3))
|
|
697
|
+
|
|
698
|
+
hashes[i % last] = code
|
|
699
|
+
ht[i] = code if not (contained_ns & sig_mask) else invalid
|
|
700
|
+
|
|
701
|
+
@njit(nogil=True)
|
|
702
|
+
def canonical_codes_inplace(arr, nhashes):
|
|
703
|
+
for i in range(nhashes):
|
|
704
|
+
arr[i] = ccode(arr[i])
|
|
705
|
+
return nhashes
|
|
706
|
+
|
|
707
|
+
@njit(nogil=True)
|
|
708
|
+
def canonical_codes_sampled(arr, nhashes):
|
|
709
|
+
for idx, hash_idx in enumerate(range(0, nhashes, w // 2)):
|
|
710
|
+
arr[idx] = ccode(arr[hash_idx])
|
|
711
|
+
return idx + 1
|
|
712
|
+
|
|
713
|
+
canonical_codes = canonical_codes_sampled if sampled else canonical_codes_inplace
|
|
714
|
+
|
|
715
|
+
@njit(nogil=True)
|
|
716
|
+
def get_gapped_kmers(seq, hashes):
|
|
717
|
+
hashes[:] = 0
|
|
718
|
+
issh_processor_fast(hashes, seq, 0, len(seq))
|
|
719
|
+
return canonical_codes(hashes, len(seq) - w + 1)
|
|
720
|
+
|
|
721
|
+
@njit(nogil=True)
|
|
722
|
+
def get_gapped_kmers_forward(seq, hashes):
|
|
723
|
+
issh_processor_fast(hashes, seq, 0, len(seq))
|
|
724
|
+
return max(0, len(seq) - w + 1)
|
|
725
|
+
|
|
726
|
+
return get_gapped_kmers_forward if rcmode == 'f' and not sampled else get_gapped_kmers
|
|
@@ -87,16 +87,17 @@ def read_block(fd, buf, offset=0, ATTEMPTS=5):
|
|
|
87
87
|
if space <= 0:
|
|
88
88
|
return -99_999 # no space means overflow
|
|
89
89
|
|
|
90
|
+
ptr = buf.ctypes.data
|
|
90
91
|
# Wrap this in a loop to handle EAGAIN errors
|
|
91
92
|
for attempt in range(ATTEMPTS):
|
|
92
|
-
res = _read(fd,
|
|
93
|
-
err = errno()
|
|
93
|
+
res = _read(fd, ptr + offset, space)
|
|
94
94
|
if res > 0:
|
|
95
95
|
# print(res)
|
|
96
96
|
fill = offset + res
|
|
97
97
|
return fill if fill <= buflen else (-99_999) # -99_999 means overflow
|
|
98
98
|
elif res == 0:
|
|
99
99
|
return 0
|
|
100
|
+
err = errno()
|
|
100
101
|
if err == 11:
|
|
101
102
|
continue # EAGAIN: try again, no data
|
|
102
103
|
return (-err) if err > 0 else (-99_998) # negative errno on error
|
|
@@ -109,18 +110,19 @@ def read_block(fd, buf, offset=0, ATTEMPTS=5):
|
|
|
109
110
|
def write_block(fd, buf, n, ATTEMPTS=5):
|
|
110
111
|
"""
|
|
111
112
|
Write n bytes from buf to the file with file descriptor fd (int32).
|
|
112
|
-
Return number of
|
|
113
|
+
Return number of written bytes
|
|
113
114
|
"""
|
|
114
115
|
N = n
|
|
115
116
|
written = 0
|
|
116
117
|
attempts = 0
|
|
118
|
+
ptr = buf.ctypes.data
|
|
117
119
|
while n > 0:
|
|
118
|
-
res = _write(fd,
|
|
119
|
-
err = errno()
|
|
120
|
+
res = _write(fd, ptr + written, n)
|
|
120
121
|
if res > 0:
|
|
121
122
|
written += res
|
|
122
123
|
n -= res
|
|
123
124
|
elif res < 0: # indicates error; error is in err
|
|
125
|
+
err = errno()
|
|
124
126
|
return (-err) if err > 0 else (-99_998)
|
|
125
127
|
else: # res == 0: nothing done
|
|
126
128
|
attempts += 1
|
|
@@ -47,7 +47,7 @@ from warnings import warn
|
|
|
47
47
|
|
|
48
48
|
import numpy as np
|
|
49
49
|
import numba
|
|
50
|
-
from numba import njit, uint64
|
|
50
|
+
from numba import njit, uint64, types
|
|
51
51
|
import llvmlite
|
|
52
52
|
from llvmlite import ir
|
|
53
53
|
|
|
@@ -155,7 +155,7 @@ def compile_memmove():
|
|
|
155
155
|
def codegen(context, builder, sig, args):
|
|
156
156
|
fty = ir.FunctionType(ir.VoidType(), (ir.IntType(8).as_pointer(), ir.IntType(8).as_pointer(), ir.IntType(64), ir.IntType(1)))
|
|
157
157
|
_memmove = builder.module.declare_intrinsic(
|
|
158
|
-
|
|
158
|
+
"llvm.memmove.p0.p0.i64", fnty=fty)
|
|
159
159
|
volatile = ir.Constant(ir.IntType(1), 1)
|
|
160
160
|
return builder.call(_memmove, (builder.inttoptr(args[0], ir.IntType(8).as_pointer()), builder.inttoptr(args[1], ir.IntType(8).as_pointer()), args[2], volatile,))
|
|
161
161
|
return sig, codegen
|
|
@@ -171,7 +171,32 @@ def compile_memmove():
|
|
|
171
171
|
return memmove
|
|
172
172
|
|
|
173
173
|
|
|
174
|
+
# MEMCOPY
|
|
174
175
|
|
|
176
|
+
_I1 = ir.IntType(1)
|
|
177
|
+
_I64 = ir.IntType(64)
|
|
178
|
+
_VOIDPTR = ir.IntType(8).as_pointer()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@numba.extending.intrinsic
|
|
182
|
+
def memcpy(typingctx, dst, dst_off, src, src_off, nbytes):
|
|
183
|
+
"""Emit llvm.memcpy(dst+dst_off, src+src_off, nbytes)."""
|
|
184
|
+
if not isinstance(dst, types.Array) or not isinstance(src, types.Array):
|
|
185
|
+
return None
|
|
186
|
+
sig = types.void(dst, dst_off, src, src_off, nbytes)
|
|
187
|
+
|
|
188
|
+
def codegen(context, builder, signature, args):
|
|
189
|
+
dst_a = context.make_array(dst)(context, builder, value=args[0])
|
|
190
|
+
src_a = context.make_array(src)(context, builder, value=args[2])
|
|
191
|
+
dst_ptr = builder.gep(builder.bitcast(dst_a.data, _VOIDPTR), [args[1]])
|
|
192
|
+
src_ptr = builder.gep(builder.bitcast(src_a.data, _VOIDPTR), [args[3]])
|
|
193
|
+
# llvm.memcpy.p0i8.p0i8.i64(dst, src, n, isvolatile)
|
|
194
|
+
memcpy = builder.module.declare_intrinsic(
|
|
195
|
+
"llvm.memcpy", [_VOIDPTR, _VOIDPTR, _I64])
|
|
196
|
+
builder.call(memcpy, [dst_ptr, src_ptr, args[4], ir.Constant(_I1, 0)])
|
|
197
|
+
return context.get_dummy_value()
|
|
198
|
+
|
|
199
|
+
return sig, codegen
|
|
175
200
|
|
|
176
201
|
# POPCOUNT #############################################
|
|
177
202
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xengsort
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|