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.
Files changed (55) hide show
  1. {xengsort-2.2.0/xengsort.egg-info → xengsort-2.2.1}/PKG-INFO +1 -1
  2. {xengsort-2.2.0 → xengsort-2.2.1}/pyproject.toml +1 -1
  3. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/fastcash_main.py +5 -4
  4. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/fastqio.py +56 -30
  5. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/kmers.py +133 -3
  6. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/libc.py +7 -5
  7. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/llvm.py +27 -2
  8. {xengsort-2.2.0 → xengsort-2.2.1/xengsort.egg-info}/PKG-INFO +1 -1
  9. {xengsort-2.2.0 → xengsort-2.2.1}/LICENSE +0 -0
  10. {xengsort-2.2.0 → xengsort-2.2.1}/MANIFEST.in +0 -0
  11. {xengsort-2.2.0 → xengsort-2.2.1}/README.md +0 -0
  12. {xengsort-2.2.0 → xengsort-2.2.1}/setup.cfg +0 -0
  13. {xengsort-2.2.0 → xengsort-2.2.1}/tests/test_trimmed_reads.py +0 -0
  14. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/cptasks_kmers.py +0 -0
  15. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/dnaencode.py +0 -0
  16. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/dnaencode_fast.py +0 -0
  17. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/fastcash_info.py +0 -0
  18. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/fastcash_optimize.py +0 -0
  19. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/hash_minimizer.py +0 -0
  20. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/hash_s3c_fbcbvb.py +0 -0
  21. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/hash_srh_fbcbvb.py +0 -0
  22. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/binaryio.py +0 -0
  23. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/fastaio.py +0 -0
  24. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/generalio.py +0 -0
  25. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/hashio.py +0 -0
  26. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/seqio.py +0 -0
  27. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/textio.py +0 -0
  28. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/io/xorio.py +0 -0
  29. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/aligned_arrays.py +0 -0
  30. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/bitarray.py +0 -0
  31. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/conpro.py +0 -0
  32. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/debug.py +0 -0
  33. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/intbitarray.py +0 -0
  34. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/numbautils.py +0 -0
  35. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/lowlevel/packedarray.py +0 -0
  36. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/mask.py +0 -0
  37. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/mathutils.py +0 -0
  38. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/parameters.py +0 -0
  39. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/srhash.py +0 -0
  40. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/subtable_hashfunctions.py +0 -0
  41. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/values/xenograft.py +0 -0
  42. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/config/classify.yaml +0 -0
  43. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/config/index.yaml +0 -0
  44. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_classify.py +0 -0
  45. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_index.py +0 -0
  46. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_info.py +0 -0
  47. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_load.py +0 -0
  48. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_main.py +0 -0
  49. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_remove.py +0 -0
  50. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort/xengsort/xengsort_weak.py +0 -0
  51. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort.egg-info/SOURCES.txt +0 -0
  52. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort.egg-info/dependency_links.txt +0 -0
  53. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort.egg-info/entry_points.txt +0 -0
  54. {xengsort-2.2.0 → xengsort-2.2.1}/xengsort.egg-info/requires.txt +0 -0
  55. {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.0
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
@@ -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.1"
9
9
 
10
10
  license = {file = "LICENSE"}
11
11
 
@@ -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
- ntodo1=int32, ntodo2=int32, errorcode=int32, offset1=int32, offset2=int32,
428
- skip=int32, nxt1=int32, nxt2=int32, nseqs=int32, nseqs1=int32, nseqs2=int32, nlines1=int32, nlines2=int32,
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
- nlines = 0
478
- for i in range(ntodo):
479
- nlines += active_buffer[i] == 10
480
- nseqs = nlines // 4
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
- nlines += active_buffer[ntodo - i - 1] == 10
493
- if nlines == rm_lines + 1:
487
+ if active_buffer[ntodo - i - 1] == 10:
488
+ record_end = ntodo - i
494
489
  break
495
- offset += 1
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
- break
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
- # assert prev1 < cont1
740
- # assert prev2 < cont2
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
- like compile_kmer_processor, but also uses the current k-mer start position
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, buf.ctypes.data + offset, space)
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 ele
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, buf.ctypes.data, n)
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
- f"llvm.memmove.p0.p0.i64", fnty=fty)
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.0
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