Compare commits

...
209 Commits
Author SHA1 Message Date
Heng Li 395c8d678a r815: fixed a memory leak 2018-07-15 22:11:32 -04:00
Heng Li 830da7fa27 r814: resumed versioning 2018-07-15 11:48:14 -04:00
Heng Li a655cbef86 print SAM header; remove tmp files 2018-07-15 11:03:18 -04:00
Heng Li 4b707aac92 working with toy examples 2018-07-15 10:55:00 -04:00
Heng Li 951c0d1d35 apparently mm_append_cigar() wastes some memory 2018-07-14 23:47:44 -04:00
Heng Li 3545e35a42 pairing in the split-idx mode 2018-07-14 23:43:34 -04:00
Heng Li e5277dbf5c code backup 2018-07-14 22:52:36 -04:00
Heng Li 1a55227d5a write hits to tmp files (unfinished) 2018-07-14 12:15:10 -04:00
Heng Li 5cfa621b2d use unmapped records 2018-07-07 12:49:15 -05:00
Heng Li a609a07f8c optionally output unmapped query in PAF 2018-07-07 10:26:08 -05:00
Heng Li bcf92b3c46 compute query coverage 2018-07-06 21:52:01 -05:00
Heng Li 097378ab90 reworked break point counting 2018-07-06 09:46:26 -04:00
Heng Li 10bbbe28c5 added asmstat 2018-07-05 13:22:32 -04:00
Hyeshik Chang c92a6866f3 Release the GIL to allow native Python threading. 2018-07-05 08:00:27 -04:00
Maël Kerbiriou 6908dc59a5 --splice implied when searching for splicing sites on single strand 2018-07-05 07:41:26 -04:00
Heng Li 50dae10421 paftools call to show statistics on longer indels 2018-07-03 14:28:09 -04:00
Heng Li 0517972d02 Release minimap2-2.11 (r797) 2018-06-21 00:04:08 -04:00
Heng Li d46e68e6ad r796: don't use ssize_t 2018-06-20 12:45:27 -04:00
Heng Li 2584a4149a r295: use -r2000 for ava-ont, NOT for ava-pb 2018-06-20 12:24:43 -04:00
Heng Li 66674afd09 r794: fixed a bug in seed filtering 2018-06-20 10:26:29 -04:00
Heng Li e9ca0c9dab added __version__; resolved #165
Not sure if this is the right way. Apparently working.
2018-06-19 15:40:26 -04:00
Heng Li 7e6e8ca73f r792: fixed -Wextra warnings and resolved #184 2018-06-19 15:26:58 -04:00
Ilya Kolpakov 408e098859 fix deserialization of zero-length reference names 2018-06-19 14:46:05 -04:00
Ilya Kolpakov 57f37551f8 expose mm_idx_is_idx, mm_idx_load and mm_idx_dump 2018-06-19 14:46:05 -04:00
Ilya Kolpakov 4c66b689c3 fix serialization of empty names in mm_idx_dump 2018-06-19 14:46:05 -04:00
mvdbeek 1bde2cf076 Allow setting max_frag_len on a per alignment level 2018-06-19 14:39:30 -04:00
mvdbeek 31fc0f218a Allow setting max_frag_len parameter in Aligner class 2018-06-19 14:39:30 -04:00
Aaron Wenger 3d3bcc29a8 Fix CIGAR reallocation with --eqx
Fix the logic that calculates the number of CIGAR entries when
match "M" entries are expanded into "=" and "X".  The number
of entries depends not on the number of mismatches but rather
on the number of transitions between "=" to "X".
2018-06-19 14:37:41 -04:00
Hasindu Gamaarachchi 99dcd75f64 added support for 64 bit ARM architectures 2018-06-19 14:28:45 -04:00
Heng Li 154d2caf5b r784: support the =/X CIGAR operators (#156) 2018-05-30 16:11:22 -04:00
Heng Li a3afeec0b2 r783: reverted to r781 (#155) 2018-05-30 15:25:34 -04:00
Heng Li 3573784b4d r782: no mask a chain having long ref ovlp (#155) 2018-05-30 13:53:45 -04:00
Heng Li 872f300955 r781: fixed the buggy heapmerge (resolves #166) 2018-05-30 11:55:14 -04:00
Heng Li d7b61a039e updated citation 2018-05-30 11:11:55 -04:00
Heng Li 248158a3e1 Resolved #168: citing the manpage for cs 2018-05-30 11:03:16 -04:00
Heng Li 9f4309c376 r777: avoid skipping too many seeds 2018-05-11 10:25:18 -04:00
Heng Li 463f9309f9 Merge branch 'hot-fix' into fix-long-gap 2018-05-11 10:13:19 -04:00
Heng Li abe989e355 the previous fix on int overflow is incomplete 2018-05-11 10:12:57 -04:00
Heng Li 881b4ca3a2 r774: Merge branch 'hot-fix' into fix-long-gap 2018-05-11 10:02:17 -04:00
Heng Li 10c6dd2551 r773: fixed an integer overflow 2018-05-11 10:01:23 -04:00
Heng Li 7ec6721c44 r772: option -Y not working 2018-05-11 10:00:11 -04:00
Heng Li e61812ee55 reduced gap len to trigger bad seed filtering 2018-05-01 16:17:21 -04:00
Heng Li 734ac379bb r770: matching N bases not working properly (#155) 2018-04-30 19:55:23 -04:00
Heng Li 759f8e4ac9 r769: filter out seeds breaking long gaps 2018-04-24 15:37:37 -04:00
Heng Li aef7b0744c r768: shortened preset; added dv tag (#25)
Also added asm20 to command line help (#151)
2018-04-24 12:48:54 -04:00
Heng Li 39f836eac8 r767: don't crash when there is no "cg" (#153) 2018-04-24 12:32:37 -04:00
Heng Li cbeb86dad6 Merge remote-tracking branch 'origin/master' 2018-04-10 09:12:26 -04:00
Heng Li 372c90ceb5 r764: fixed incorrect inversion mapq (#148) 2018-04-10 09:11:49 -04:00
apregier 2e2e69107c Small bugfix for paftools.js
The bug resulted in the wrong coverage being printed in some cases.
2018-04-04 16:29:54 -04:00
Heng Li ee4cd089f7 r763: fine control long join flank len (#128) 2018-03-29 14:16:58 -04:00
Heng Li 2d7ec75d50 Release minimap2-2.10 (r761) 2018-03-27 11:45:44 -04:00
Heng Li 7938ed4893 fixed two mappy warnings 2018-03-26 15:12:54 -04:00
Heng Li 4740423afa Merge remote-tracking branch 'origin/master' 2018-03-26 14:37:17 -04:00
Heng Li 1776311a9b updated cookbook 2018-03-26 14:37:02 -04:00
Heng Li c1a3e05cb0 Merge pull request #138 from zingdle/patch-1
Fix typo in README.md
2018-03-26 07:50:39 -04:00
zingdle ecb6703d4a Update README.md
Fix typo.
TD;DR -> TL;DR
2018-03-26 14:54:52 +08:00
Heng Li 0bf97a367b r755: junceval to only evaluate chromosomes 2018-03-24 23:10:06 -04:00
Heng Li 1c504d72e4 r754: option to only change name 2018-03-23 12:27:11 -04:00
Heng Li 5ef9580b17 r753: change bandwidth in ava-ont to 2000bp 2018-03-23 10:15:23 -04:00
Heng Li 08bd2123b6 r752: option to copy comments to output (#136) 2018-03-23 10:04:33 -04:00
Heng Li 8766d286df r751: optionally output MD (#118) 2018-03-22 14:15:33 -04:00
Heng Li 623b5d9d48 r750: check puts() return (#132 & #103) 2018-03-22 11:31:58 -04:00
Heng Li 18659118cd r749: don't print version etc at low verbose 2018-03-22 11:10:55 -04:00
Heng Li d1050f4eaf r748: optionally to use system getopt() (#134) 2018-03-19 11:18:26 -04:00
Heng Li b81d45510e Revision v2 2018-03-16 11:18:06 -04:00
Heng Li d135feb1a5 response to reviewers' comments, round 2 2018-03-15 21:59:57 -04:00
Heng Li 242ff4e91d r745: junceval - recognize "-" as file name 2018-03-15 12:51:46 -04:00
Heng Li 7a0c1316ce added more examples to cookbook 2018-03-12 22:24:22 -04:00
Heng Li 77ebd479f4 r743: added VCF output to "call" 2018-03-12 21:51:31 -04:00
Heng Li e3f226a9d9 working on section "Full-Genome Alignment"
found an apparent bug in paftools.js call. To debug...
2018-03-12 16:12:18 -04:00
Heng Li bdc615c1d4 r741: added --min-occ-floor to improve #107 2018-03-12 14:32:27 -04:00
Heng Li ad1beaf255 backup; not finished 2018-03-12 13:20:25 -04:00
Heng Li de0480ac5b finished section "Read Overlap" 2018-03-12 13:05:51 -04:00
Heng Li f2866533a8 finished "mapping genomic reads" 2018-03-12 12:50:21 -04:00
Heng Li 0173850ef0 don't use bullets - they are hard to read 2018-03-12 12:42:13 -04:00
Heng Li acea3594fb updated cookbook 2018-03-12 12:39:57 -04:00
Heng Li f78a247749 Started to work on a cookbook 2018-03-12 12:00:44 -04:00
Heng Li ccaf12e1a2 r734: removed debugging print in call() 2018-03-09 23:46:21 -05:00
Heng Li 96b132c97d fixed a bug/typo in Aligner.seq() (#126) 2018-03-08 19:12:12 -05:00
Heng Li 70428ca3a8 speedup tag parsing for sam2paf 2018-03-02 10:17:55 -05:00
Heng Li 9aea79d621 faster tag parsing 2018-03-02 10:09:14 -05:00
Heng Li 1770988627 make calling work with sam2paf output 2018-03-02 10:03:06 -05:00
Heng Li 2bfdad34bb minor cleanup to last commit 2018-03-02 01:07:37 -05:00
Heng Li 953766cedd generate cs; not carefully tested 2018-03-02 00:14:55 -05:00
Heng Li dc61301d9f sam2paf cleanup 2018-03-01 21:58:12 -05:00
Heng Li 19e05a099d for clarity 2018-03-01 18:25:29 -05:00
Heng Li 0238caa8b1 clarify minimap2 not working for >2Gbp seq #129 2018-03-01 18:23:40 -05:00
Heng Li a22ebb9836 use SSE compiler flags more precisely (#127) 2018-02-26 09:51:01 -05:00
Heng Li eeb314edd6 Release minimap2-2.9 (r720) 2018-02-24 09:31:09 -05:00
Heng Li 83c57a9d98 r719: fixed bad memory access 2018-02-23 17:27:41 -05:00
Heng Li 24a4808826 r718: retrieve sequence from the index 2018-02-23 10:18:26 -05:00
Heng Li 29ed675ee5 bugfix: in-place revcomp() not working 2018-02-20 11:05:54 -05:00
Heng Li 7dc7097208 added reverse complement 2018-02-20 09:41:25 -05:00
Heng Li f434653432 added peakrss(); not used for now 2018-02-17 20:40:31 -05:00
Heng Li 090361c25b fixed a typo in mappy (#117) 2018-02-16 21:10:57 -05:00
Heng Li e1f18690f6 paftools-r713: fixed bug for cov1 regions 2018-02-16 12:20:46 -05:00
Heng Li 54a42aafe5 keep documents in sync 2018-02-15 17:21:09 -05:00
Heng Li 8fc5f8dc90 r711: assign proper mapq to primary inversions 2018-02-15 14:34:59 -05:00
Heng Li a0d62519c1 r710: fixed incorrect inversion coordinate (#112) 2018-02-15 14:23:42 -05:00
Heng Li b71c01b316 added test data for inversions 2018-02-15 11:04:27 -05:00
Heng Li 1372977a37 r708: implemented double Z-drop thresholds (#112)
When aligning long reads, we would prefer to align through low-quality
regions. This requires a large Z-drop threshold. However, to find small
inversions, we need to use a small Z-drop. This commit address this
conflict with two Z-drop thresholds. When Z-drop exceeds the smaller
threshold, we perform a local alignment to check if there is a potential
inversion. If there is one, we break the alignment; otherwise we break
the alignment only if Z-drop excess the larger threshold.

This commit also fixes a bug that reported wrong coordinates when the
inversion is on the forward strand (#112).
2018-02-15 10:50:49 -05:00
Heng Li c0e0d5d84b r707: bugfix for inversions on rev strand (#112) 2018-02-14 14:09:03 -05:00
Heng Li b328795051 r706: don't segfault upon wrong FASTA/Q (#111)
The lack of robustness cost me several hours to identify.
2018-02-13 10:00:22 -05:00
Heng Li 3b17d62ccd minor doc improvements 2018-02-12 22:27:02 -05:00
Heng Li 874b8c4795 rework misc/README; progressing but unfinished 2018-02-12 22:06:05 -05:00
Heng Li 7ef5490884 r703: added --max-clip-ratio
still testing the option
2018-02-12 13:29:18 -05:00
Heng Li f66de7df59 updated paftools revision number to r702 2018-02-12 11:39:04 -05:00
Heng Li fbbd4e0968 changed output messages for clarity 2018-02-12 11:32:32 -05:00
Heng Li 8c89ba005e merged cnt-feat into paftools 2018-02-12 11:29:42 -05:00
Heng Li 50775a1e6f added liftOver
tested on a few toy examples
2018-02-12 11:12:23 -05:00
Heng Li 87cf650168 r697: fixed wrong #mapped reads in junceval 2018-02-09 18:45:57 -05:00
Heng Li 6e65c5e631 Revision 1 2018-02-09 17:39:02 -05:00
Heng Li a58b05a61b extend the section on genome alignment 2018-02-09 13:59:00 -05:00
Heng Li 42dab6319b don't call SNPs involving 'n' 2018-02-09 13:27:17 -05:00
Heng Li 8428809369 convert MUMmer's delta to PAF 2018-02-09 12:14:28 -05:00
Heng Li 642af5591e merged intron-eval into paftools 2018-02-09 11:07:14 -05:00
Heng Li 2025a6279a merged gff2bed and mapstat to paftools 2018-02-09 10:47:12 -05:00
Heng Li 3465b04724 merged a few converters to paftools 2018-02-09 10:23:30 -05:00
Heng Li 66c5e71fa8 merging scripts into one long script 2018-02-09 10:00:35 -05:00
Heng Li 01560f1db0 Revision v1, to be submitted 2018-02-07 11:07:08 -05:00
Heng Li 39535565ee first round of revision 2018-02-06 16:19:22 -05:00
Heng Li a8d476c6ad r686: end seed trimming don't go over long join 2018-02-06 11:31:32 -05:00
Heng Li 29b4a1786c r685: tune end seed filter again 2018-02-05 11:48:22 -05:00
Heng Li dbf284b2d9 r684: separate end score from min_chain_score 2018-02-05 11:40:38 -05:00
Heng Li 3df5015668 General doc improvements 2018-02-02 21:45:46 -05:00
Heng Li 756379bf83 Documented paf2diff.js (more to come later) 2018-02-02 14:43:58 -05:00
Heng Li 41fd8a966a Documented ov-eval.js 2018-02-02 14:18:18 -05:00
Heng Li 86e4933b1a Added TOC 2018-02-02 14:12:30 -05:00
Heng Li a633a744b6 more doc in misc 2018-02-02 14:07:17 -05:00
Heng Li ddc31f57ba Documented some k8 scripts; more coming 2018-02-02 13:57:08 -05:00
Heng Li 35d3e064bf r677: reduce the change of missing hits
that are close to end of alignments. It is still possible to create examples
that fail the heuristic.
2018-02-02 10:35:33 -05:00
Heng Li 997ab9bb2e Fixed the description of --dual 2018-02-01 15:11:51 -05:00
Heng Li 53ce317e59 Release minimap2-2.8 (r672) 2018-02-01 12:50:20 -05:00
Heng Li da6947cfa3 r671: cleanup command line options 2018-01-31 13:59:52 -05:00
Heng Li 46d6349af4 r670: added PE support to mappy
and minor code cleanup
2018-01-31 11:33:08 -05:00
Heng Li 12a5a5fa3c r669: improved self chain extension (#10)
This has not fully resolved #10, only alleviated the issue.
2018-01-30 20:05:02 -05:00
Heng Li ad18fa490d fixed typos 2018-01-30 10:11:50 -05:00
Heng Li 43bfa6199d r667: warn if one query file has fewer records #92 2018-01-28 17:36:21 -05:00
Heng Li 72b9b0e3b6 r666: report if >=3 query files in SR mode #92 2018-01-28 17:15:57 -05:00
Heng Li 6205fa6f21 document --heap-sort 2018-01-26 15:15:40 -05:00
Heng Li d676a5314b r664: use --heat-sort for sr by default 2018-01-26 12:25:42 -05:00
Heng Li dfc78b39d3 refactor the old sorting 2018-01-26 09:37:48 -05:00
Heng Li 7b57c9a619 heap sort working on MT 2018-01-26 09:21:45 -05:00
Heng Li 123bc1d91d put option operations in another file 2018-01-26 08:38:37 -05:00
Heng Li dd18307e66 code backup 2018-01-25 21:52:49 -05:00
Heng Li 543fa12e68 r659: for C++ compatibility 2018-01-19 10:40:18 -05:00
Heng Li af1a871270 r658: gives a warning if -N0 is used 2018-01-19 08:33:20 -05:00
Heng Li 2b71181a37 r657: check -p (#96)
Well, in principle, every option should be checked. Will do when someone raise
issues...
2018-01-19 01:03:38 -05:00
Heng Li 0454e6be91 explain -M in the manpage 2018-01-18 11:47:11 -05:00
Heng Li 33f8157961 r655: options to map to one strand of the ref #91 2018-01-16 10:34:30 -05:00
Heng Li eecc06086f Released minimap2-2.7 (r654) 2018-01-09 13:16:00 -05:00
Heng Li dfea113f28 r653: the last change may write "N" wrongly 2018-01-08 11:33:53 -05:00
Heng Li 1842d7f5b5 allow to exclude regions 2018-01-07 22:35:56 -05:00
Heng Li f5cfd439ee r651: incorrectly treat introns as deletions
This happened when the last operation during backtracking is an intron.
2018-01-07 19:42:50 -05:00
Heng Li 248b43cc47 work with targets in BED12 2018-01-07 19:39:25 -05:00
Heng Li bf72969ab1 added a bed counter
I know there are tools for this purpose, but they don't quite meet my exact
need.
2018-01-07 15:19:18 -05:00
Heng Li 7b5a601d48 support paired-end reads
this gives unnecessary warnings, which will be fixed later.
2018-01-07 13:53:26 -05:00
Heng Li 405d531100 don't test python v3.3 2018-01-05 22:17:00 -05:00
Heng Li e9607fcd9b allow to convert read names
ONT read names are just too long and too hard to compress
2018-01-05 22:05:15 -05:00
Heng Li a465a920ec bug in block starts; added color and short-name 2018-01-05 21:45:56 -05:00
Heng Li b20839be77 more robust ID conversion 2018-01-05 17:55:24 -05:00
Heng Li 209beb9955 convert EnsEMBL to UCSC name (optional) 2018-01-05 17:41:18 -05:00
Heng Li cfe87f50c1 convert GTF/GFF3 to BED12 2018-01-05 17:10:43 -05:00
Heng Li 680b971bb0 deleted duplicated entries 2018-01-01 19:52:21 -05:00
Heng Li 6b0d3c1fa8 added PHONY and comments to makefile 2017-12-30 20:54:11 -05:00
Heng Li dc9e3dcf4a r639: changed -O/-E validation 2017-12-30 20:39:29 -05:00
Heng Li cc75c12905 r638: disabled scoring checking
I haven't figured out the exact bounds...
2017-12-30 07:50:40 -05:00
Heng Li f159e1c2d3 new section on HPC k-mers 2017-12-24 19:11:23 -05:00
Heng Li 3a375d3436 renamed paf2ovlp to ov-eval 2017-12-24 18:04:00 -05:00
Heng Li 626f10e0d0 evaluate sensitivity in the same script 2017-12-24 17:55:42 -05:00
Heng Li b997578078 find reads overlaps based on reference alignment 2017-12-24 17:20:27 -05:00
Heng Li ce8a48d715 fixed two minor typos in references 2017-12-24 13:14:47 -05:00
Heng Li 99879e9e75 a new section on estimating sequence divergence 2017-12-24 12:57:38 -05:00
Heng Li c969d1a1ce updated direct RNA-seq results; cite syndip
and a few minor changes
2017-12-24 11:06:17 -05:00
Heng Li fcac296c4a clarify ARM-NEON support in README 2017-12-18 23:01:44 -05:00
Heng Li e420b17496 r629: API to construct index from strings 2017-12-18 22:29:46 -05:00
Heng Li 23a846c594 Merge pull request #81 from hasindu2008/master
minimap2 on ARM processors
2017-12-16 09:40:41 -05:00
Hasindu Gamaarachchi 8995e2e078 added support for arm neon 2017-12-15 17:42:19 +11:00
Heng Li ab345e600b r626: function to check incorrect scoring system 2017-12-13 12:23:43 -05:00
Heng Li d003a00d71 r625: HPC sketch still has one minor issue 2017-12-13 09:40:42 -05:00
Heng Li ae85dcde76 added esterr.o dependencies 2017-12-13 09:01:48 -05:00
Heng Li eb819c29e8 Release minimap2-2.6 (r623) 2017-12-12 11:09:59 -05:00
Heng Li fb630de40a r622: fixed bug in sdust due to recent refactor 2017-12-11 15:32:28 -05:00
Heng Li 43960a8ca7 r621: --print-qname also shows kalloc status 2017-12-11 12:30:08 -05:00
Heng Li f6608fe99c r620: revamped thread-local memory management
* Don't preallocate sdust_buf or minizer list. kalloc should be fast enough -
  benchmarks needed to confirm.

* Fixed a memory leak caused by divergence estimate (post v2.5)

* Reset the kalloc buffer after mapping a long query. This reduces peak memory
  when large chunks of memory are allocated, at the cost of performance, though.
2017-12-11 12:11:10 -05:00
Heng Li 98a6e52c06 r618: heuristics to avoid tiny terminal exons 2017-12-11 00:57:55 -05:00
Heng Li 824712a4ee r617: removed some unused code 2017-12-10 17:54:50 -05:00
Heng Li 0e42628ef6 r611: document --idx-no-seq; better inv aln 2017-12-08 13:16:18 -05:00
Heng Li 98a999fe44 r611: added pseudocount when est divergence 2017-12-08 12:57:57 -05:00
Heng Li fec7bd713f r610: warning if db sequence is 0-lengthed (#69) 2017-12-07 21:05:39 -05:00
Heng Li 2f693e8ca4 r609: bugfix - SDUST masking not working 2017-12-07 11:45:38 -05:00
Heng Li 704ff9f4c6 r607: estimate sequence divergence
Currently using the simplest method. There may be a more accurate estimate.
2017-12-06 16:14:39 -05:00
Heng Li 68c63f2d68 r606: fixed a sketch bug for long 256bp k-mer
sketch() writes {-1,-1} to the output array.
2017-12-06 16:13:29 -05:00
Heng Li 76206f574f support both SAM and PAF as input 2017-12-02 21:57:19 -05:00
Heng Li e575f884e1 convert spliced PAF to BED 2017-12-02 21:23:41 -05:00
Heng Li 571161d5a2 changed badges 2017-12-01 16:16:27 -05:00
Heng Li 07d41efc2b explain secondary/supplementary aln for RNA-seq 2017-11-30 23:02:20 -05:00
Heng Li 984f7846c0 r601: bugfix - a similar issue to r600
This bug unsets the alignment score of suboptimal alignments.
2017-11-30 11:51:34 -05:00
Heng Li af1d6afba9 r600: bugfix - missing secondary alignments (#71)
This should very rarely happen to typical data, but has a higher chance in
artifactual data.
2017-11-30 11:34:10 -05:00
Heng Li cbdb6c069f when there are incorrect anno, warn but not abort 2017-11-24 12:48:49 -05:00
Heng Li 35b6d9f7d5 direct manpage to HTML 2017-11-24 11:05:20 -05:00
Heng Li 39a9666246 convert PAF to LAST's cigar output
also added the support of BLAST-like output for "--cs=short".
2017-11-18 20:30:44 -05:00
Heng Li 662d05dc02 fixed incorrect var coordinate 2017-11-12 20:25:33 -05:00
Heng Li 379457c18b use mapq threshold 2017-11-12 19:01:34 -05:00
Heng Li 03169d590b print cov-1 regions (not BED output any more) 2017-11-12 18:45:49 -05:00
Heng Li 8c8d446820 find regions covered by one contig 2017-11-12 18:41:01 -05:00
Heng Li 0ddb064f17 dnadiff-like script; improvement coming 2017-11-12 15:07:29 -05:00
Heng Li 131cfc6938 r574: build index without sequences 2017-11-11 21:38:38 -05:00
Heng Li 2f463b1db0 r573: prepare to generalize index 2017-11-11 19:54:06 -05:00
52 changed files with 7022 additions and 1918 deletions
-4
View File
@@ -10,10 +10,6 @@ matrix:
python: "2.7"
before_install: pip install cython
script: python setup.py build_ext
- language: python
python: "3.3"
before_install: pip install cython
script: python setup.py build_ext
- language: python
python: "3.5"
before_install: pip install cython
+49 -14
View File
@@ -1,17 +1,28 @@
CFLAGS= -g -Wall -O2 -Wc++-compat
CFLAGS= -g -Wall -O2 -Wc++-compat #-Wextra
CPPFLAGS= -DHAVE_KALLOC
INCLUDES=
OBJS= kthread.o kalloc.o misc.o bseq.o sketch.o sdust.o index.o chain.o align.o hit.o map.o format.o pe.o ksw2_ll_sse.o
OBJS= kthread.o kalloc.o misc.o bseq.o sketch.o sdust.o options.o index.o chain.o align.o hit.o map.o format.o pe.o esterr.o splitidx.o ksw2_ll_sse.o
PROG= minimap2
PROG_EXTRA= sdust minimap2-lite
LIBS= -lm -lz -lpthread
ifeq ($(sse2only),)
ifeq ($(arm_neon),) # if arm_neon is not defined
ifeq ($(sse2only),) # if sse2only is not defined
OBJS+=ksw2_extz2_sse41.o ksw2_extd2_sse41.o ksw2_exts2_sse41.o ksw2_extz2_sse2.o ksw2_extd2_sse2.o ksw2_exts2_sse2.o ksw2_dispatch.o
else
else # if sse2only is defined
OBJS+=ksw2_extz2_sse.o ksw2_extd2_sse.o ksw2_exts2_sse.o
endif
else # if arm_neon is defined
OBJS+=ksw2_extz2_neon.o ksw2_extd2_neon.o ksw2_exts2_neon.o
INCLUDES+=-Isse2neon
ifeq ($(aarch64),) #if aarch64 is not defined
CFLAGS+=-D_FILE_OFFSET_BITS=64 -mfpu=neon -fsigned-char
else #if aarch64 is defined
CFLAGS+=-D_FILE_OFFSET_BITS=64 -fsigned-char
endif
endif
.PHONY:all extra clean depend
.SUFFIXES:.c .o
.c.o:
@@ -33,29 +44,49 @@ libminimap2.a:$(OBJS)
sdust:sdust.c getopt.o kalloc.o kalloc.h kdq.h kvec.h kseq.h sdust.h
$(CC) -D_SDUST_MAIN $(CFLAGS) $< getopt.o kalloc.o -o $@ -lz
# SSE-specific targets on x86/x86_64
ifeq ($(arm_neon),) # if arm_neon is defined, compile this target with the default setting (i.e. no -msse2)
ksw2_ll_sse.o:ksw2_ll_sse.c ksw2.h kalloc.h
$(CC) -c $(CFLAGS) -msse2 $(CPPFLAGS) $(INCLUDES) $< -o $@
endif
ksw2_extz2_sse41.o:ksw2_extz2_sse.c ksw2.h kalloc.h
$(CC) -c -msse4 $(CFLAGS) $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@
$(CC) -c $(CFLAGS) -msse4.1 $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@
ksw2_extz2_sse2.o:ksw2_extz2_sse.c ksw2.h kalloc.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -DKSW_CPU_DISPATCH -DKSW_SSE2_ONLY $(INCLUDES) $< -o $@
$(CC) -c $(CFLAGS) -msse2 -mno-sse4.1 $(CPPFLAGS) -DKSW_CPU_DISPATCH -DKSW_SSE2_ONLY $(INCLUDES) $< -o $@
ksw2_extd2_sse41.o:ksw2_extd2_sse.c ksw2.h kalloc.h
$(CC) -c -msse4 $(CFLAGS) $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@
$(CC) -c $(CFLAGS) -msse4.1 $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@
ksw2_extd2_sse2.o:ksw2_extd2_sse.c ksw2.h kalloc.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -DKSW_CPU_DISPATCH -DKSW_SSE2_ONLY $(INCLUDES) $< -o $@
$(CC) -c $(CFLAGS) -msse2 -mno-sse4.1 $(CPPFLAGS) -DKSW_CPU_DISPATCH -DKSW_SSE2_ONLY $(INCLUDES) $< -o $@
ksw2_exts2_sse41.o:ksw2_exts2_sse.c ksw2.h kalloc.h
$(CC) -c -msse4 $(CFLAGS) $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@
$(CC) -c $(CFLAGS) -msse4.1 $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@
ksw2_exts2_sse2.o:ksw2_exts2_sse.c ksw2.h kalloc.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -DKSW_CPU_DISPATCH -DKSW_SSE2_ONLY $(INCLUDES) $< -o $@
$(CC) -c $(CFLAGS) -msse2 -mno-sse4.1 $(CPPFLAGS) -DKSW_CPU_DISPATCH -DKSW_SSE2_ONLY $(INCLUDES) $< -o $@
ksw2_dispatch.o:ksw2_dispatch.c ksw2.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@
$(CC) -c $(CFLAGS) -msse4.1 $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@
# NEON-specific targets on ARM
ksw2_extz2_neon.o:ksw2_extz2_sse.c ksw2.h kalloc.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -DKSW_SSE2_ONLY -D__SSE2__ $(INCLUDES) $< -o $@
ksw2_extd2_neon.o:ksw2_extd2_sse.c ksw2.h kalloc.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -DKSW_SSE2_ONLY -D__SSE2__ $(INCLUDES) $< -o $@
ksw2_exts2_neon.o:ksw2_exts2_sse.c ksw2.h kalloc.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -DKSW_SSE2_ONLY -D__SSE2__ $(INCLUDES) $< -o $@
# other non-file targets
clean:
rm -fr gmon.out *.o a.out $(PROG) $(PROG_EXTRA) *~ *.a *.dSYM build dist mappy.so mappy.c python/mappy.c mappy.egg*
rm -fr gmon.out *.o a.out $(PROG) $(PROG_EXTRA) *~ *.a *.dSYM build dist mappy*.so mappy.c python/mappy.c mappy.egg*
depend:
(LC_ALL=C; export LC_ALL; makedepend -Y -- $(CFLAGS) $(CPPFLAGS) -- *.c)
@@ -65,6 +96,7 @@ depend:
align.o: minimap.h mmpriv.h bseq.h ksw2.h kalloc.h
bseq.o: bseq.h kvec.h kalloc.h kseq.h
chain.o: minimap.h mmpriv.h bseq.h kalloc.h
esterr.o: mmpriv.h minimap.h bseq.h
example.o: minimap.h kseq.h
format.o: kalloc.h mmpriv.h minimap.h bseq.h
getopt.o: getopt.h
@@ -75,9 +107,12 @@ ksw2_extd2_sse.o: ksw2.h kalloc.h
ksw2_exts2_sse.o: ksw2.h kalloc.h
ksw2_extz2_sse.o: ksw2.h kalloc.h
ksw2_ll_sse.o: ksw2.h kalloc.h
kthread.o: kthread.h
main.o: bseq.h minimap.h mmpriv.h getopt.h
map.o: kthread.h kvec.h kalloc.h sdust.h mmpriv.h minimap.h bseq.h khash.h
misc.o: minimap.h ksort.h
map.o: ksort.h
misc.o: mmpriv.h minimap.h bseq.h ksort.h
options.o: mmpriv.h minimap.h bseq.h
pe.o: mmpriv.h minimap.h bseq.h kvec.h kalloc.h ksort.h
sdust.o: kalloc.h kdq.h kvec.h sdust.h
sketch.o: kvec.h kalloc.h minimap.h
sketch.o: kvec.h kalloc.h mmpriv.h minimap.h bseq.h
+211
View File
@@ -1,3 +1,214 @@
Release 2.11-r797 (20 June 2018)
--------------------------------
Changes to minimap2:
* Improved alignment accuracy in low-complexity regions for SV calling. Thank
@armintoepfer for multiple offline examples.
* Added option --eqx to encode sequence match/mismatch with the =/X CIGAR
operators (#156, #157 and #175).
* When compiled with VC++, minimap2 generated wrong alignments due to a
comparison between a signed integer and an unsigned integer (#184). Also
fixed warnings reported by "clang -Wextra".
* Fixed incorrect anchor filtering due to a missing 64- to 32-bit cast.
* Fixed incorrect mapping quality for inversions (#148).
* Fixed incorrect alignment involving ambiguous bases (#155).
* Fixed incorrect presets: option `-r 2000` is intended to be used with
ava-ont, not ava-pb. The bug was introduced in 2.10.
* Fixed a bug when --for-only/--rev-only is used together with --sr or
--heap-sort=yes (#166).
* Fixed option -Y that was not working in the previous releases.
* Added option --lj-min-ratio to fine control the alignment of long gaps
found by the "long-join" heuristic (#128).
* Exposed `mm_idx_is_idx`, `mm_idx_load` and `mm_idx_dump` C APIs (#177).
Also fixed a bug when indexing without reference names (this feature is not
exposed to the command line).
Changes to mappy:
* Added `__version__` (#165).
* Exposed the maximum fragment length parameter to mappy (#174).
Changes to paftools:
* Don't crash when there is no "cg" tag (#153).
* Fixed wrong coverage report by "paftools.js call" (#145).
This version may produce slightly different base-level alignment. The overall
alignment statistics should remain similar.
(2.11: 20 June 2018, r797)
Release 2.10-r761 (27 March 2018)
---------------------------------
Changes to minimap2:
* Optionally output the MD tag for compatibility with existing tools (#63,
#118 and #137).
* Use SSE compiler flags more precisely to prevent compiling errors on certain
machines (#127).
* Added option --min-occ-floor to set a minimum occurrence threshold. Presets
intended for assembly-to-reference alignment set this option to 100. This
option alleviates issues with regions having high copy numbers (#107).
* Exit with non-zero code on file writing errors (e.g. disk full; #103 and
#132).
* Added option -y to copy FASTA/FASTQ comments in query sequences to the
output (#136).
* Added the asm20 preset for alignments between genomes at 5-10% sequence
divergence.
* Changed the band-width in the ava-ont preset from 500 to 2000. Oxford
Nanopore reads may contain long deletion sequencing errors that break
chaining.
Changes to mappy, the Python binding:
* Fixed a typo in Align.seq() (#126).
Changes to paftools.js, the companion script:
* Command sam2paf now converts the MD tag to cs.
* Support VCF output for assembly-to-reference variant calling (#109).
This version should produce identical alignment for read overlapping, RNA-seq
read mapping, and genomic read mapping. We have also added a cook book to show
the variety uses of minimap2 on real datasets. Please see cookbook.md in the
minimap2 source code directory.
(2.10: 27 March 2017, r761)
Release 2.9-r720 (23 February 2018)
-----------------------------------
This release fixed multiple minor bugs.
* Fixed two bugs that lead to incorrect inversion alignment. Also improved the
sensitivity to small inversions by using double Z-drop cutoff (#112).
* Fixed an issue that may cause the end of a query sequence unmapped (#104).
* Added a mappy API to retrieve sequences from the index (#126) and to reverse
complement DNA sequences. Fixed a bug where the `best_n` parameter did not
work (#117).
* Avoided segmentation fault given incorrect FASTQ input (#111).
* Combined all auxiliary javascripts to paftools.js. Fixed several bugs in
these scripts at the same time.
(2.9: 24 February 2018, r720)
Release 2.8-r672 (1 February 2018)
----------------------------------
Notable changes in this release include:
* Speed up short-read alignment by ~10%. The overall mapping accuracy stays
the same, but the output alignments are not always identical to v2.7 due to
unstable sorting employed during chaining. Long-read alignment is not
affected by this change as the speedup is short-read specific.
* Mappy now supports paired-end short-read alignment (#87). Please see
python/README.rst for details.
* Added option --for-only and --rev-only to perform alignment against the
forward or the reverse strand of the reference genome only (#91).
* Alleviated the issue with undesired diagonal alignment in the self mapping
mode (#10). Even if the output is not ideal, it should not interfere with
other alignments. Fully resolving the issue is intricate and may require
additional heuristic thresholds.
* Enhanced error checking against incorrect input (#92 and #96).
For long query sequences, minimap2 should output identical alignments to v2.7.
(2.8: 1 February 2018, r672)
Release 2.7-r654 (9 January 2018)
---------------------------------
This release fixed a bug in the splice mode and added a few minor features:
* Fixed a bug that occasionally takes an intron as a long deletion in the
splice mode. This was caused by wrong backtracking at the last CIGAR
operator. The current fix eliminates the error, but it is not optimal in
that it often produces a wrong junction when the last operator is an intron.
A future version of minimap2 may improve upon this.
* Support high-end ARM CPUs that implement the NEON instruction set (#81).
This enables minimap2 to work on Raspberry Pi 3 and Odroid XU4.
* Added a C API to construct a minimizer index from a set of C strings (#80).
* Check scoring specified on the command line (#79). Due to the 8-bit limit,
excessively large score penalties fail minimap2.
For genomic sequences, minimap2 should give identical alignments to v2.6.
(2.7: 9 January 2018, r654)
Release 2.6-r623 (12 December 2017)
-----------------------------------
This release adds several features and fixes two minor bugs:
* Optionally build an index without sequences. This helps to reduce the
peak memory for read overlapping and is automatically applied when
base-level alignment is not requested.
* Approximately estimate per-base sequence divergence (i.e. 1-identity)
without performing base-level alignment, using a MashMap-like method. The
estimate is written to a new dv:f tag.
* Reduced the number of tiny terminal exons in RNA-seq alignment. The current
setting is conservative. Increase --end-seed-pen to drop more such exons.
* Reduced the peak memory when aligning long query sequences.
* Fixed a bug that is caused by HPC minimizers longer than 256bp. This should
have no effect in practice, but it is recommended to rebuild HPC indices if
possible.
* Fixed a bug when identifying identical hits (#71). This should only affect
artifactual reference consisting of near identical sequences.
For genomic sequences, minimap2 should give nearly identical alignments to
v2.5, except the new dv:f tag.
(2.6: 12 December 2017, r623)
Release 2.5-r572 (11 November 2017)
-----------------------------------
+49 -41
View File
@@ -1,10 +1,7 @@
[![Release](https://img.shields.io/badge/Release-v2.5-blue.svg?style=flat)](https://github.com/lh3/minimap2/releases)
[![BioConda](https://img.shields.io/conda/vn/bioconda/minimap2.svg?style=flat)](https://anaconda.org/bioconda/minimap2)
[![GitHub Downloads](https://img.shields.io/github/downloads/lh3/minimap2/total.svg?style=social&logo=github&label=Download)](https://github.com/lh3/minimap2/releases)
[![BioConda Install](https://img.shields.io/conda/dn/bioconda/minimap2.svg?style=flag&label=BioConda%20install)](https://anaconda.org/bioconda/minimap2)
[![PyPI](https://img.shields.io/pypi/v/mappy.svg?style=flat)](https://pypi.python.org/pypi/mappy)
[![Python Version](https://img.shields.io/pypi/pyversions/mappy.svg?style=flat)](https://pypi.python.org/pypi/mappy)
[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat)](LICENSE.txt)
[![Build Status](https://travis-ci.org/lh3/minimap2.svg?branch=master)](https://travis-ci.org/lh3/minimap2)
[![Downloads](https://img.shields.io/github/downloads/lh3/minimap2/total.svg?style=flat)](https://github.com/lh3/minimap2/releases)
## <a name="started"></a>Getting Started
```sh
git clone https://github.com/lh3/minimap2
@@ -41,7 +38,7 @@ man ./minimap2.1
- [Advanced features](#advanced)
- [Working with >65535 CIGAR operations](#long-cigar)
- [The cs optional tag](#cs)
- [Evaluation scripts](#eval)
- [Working with the PAF format](#paftools)
- [Algorithm overview](#algo)
- [Getting help](#help)
- [Citing minimap2](#cite)
@@ -64,22 +61,25 @@ mainstream long-read mappers such as BLASR, BWA-MEM, NGMLR and GMAP. It is more
accurate on simulated long reads and produces biologically meaningful alignment
ready for downstream analyses. For >100bp Illumina short reads, minimap2 is
three times as fast as BWA-MEM and Bowtie2, and as accurate on simulated data.
Detailed evaluations are available from the [minimap2 preprint][preprint].
Detailed evaluations are available from the [minimap2 paper][doi] or the
[preprint][preprint].
### <a name="install"></a>Installation
Minimap2 only works on x86-64 CPUs. You can acquire precompiled binaries from
Minimap2 is optimized for x86-64 CPUs. You can acquire precompiled binaries from
the [release page][release] with:
```sh
curl -L https://github.com/lh3/minimap2/releases/download/v2.5/minimap2-2.5_x64-linux.tar.bz2 \
| tar -jxvf -
./minimap2-2.5_x64-linux/minimap2
curl -L https://github.com/lh3/minimap2/releases/download/v2.11/minimap2-2.11_x64-linux.tar.bz2 | tar -jxvf -
./minimap2-2.11_x64-linux/minimap2
```
If you want to compile from the source, you need to have a C compiler, GNU make
and zlib development files installed. Then type `make` in the source code
directory to compile. If you see compilation errors, try `make sse2only=1`
to disable SSE4 code, which will make minimap2 slightly slower.
Minimap2 also works with ARM CPUs supporting the NEON instruction sets. To
compile for 32 bit ARM architectures (such as ARMv7), use `make arm_neon=1`. To compile for for 64 bit ARM architectures (such as ARMv8), use `make arm_neon=1 aarch64=1`.
### <a name="general"></a>General usage
Without any options, minimap2 takes a reference database and a query sequence
@@ -137,7 +137,7 @@ Nanopore reads.
#### <a name="map-long-splice"></a>Map long mRNA/cDNA reads
```sh
minimap2 -ax splice -uf ref.fa iso-seq.fq > aln.sam # PacBio Iso-seq/traditional cDNA
minimap2 -ax splice -uf -C5 ref.fa iso-seq.fq > aln.sam # PacBio Iso-seq/traditional cDNA
minimap2 -ax splice ref.fa nanopore-cdna.fa > aln.sam # Nanopore 2D cDNA-seq
minimap2 -ax splice -uf -k14 ref.fa direct-rna.fq > aln.sam # Nanopore Direct RNA-seq
minimap2 -ax splice --splice-flank=no SIRV.fa SIRV-seq.fa # mapping against SIRV control
@@ -154,6 +154,20 @@ slight improvement to accuracy. For noisy Nanopore Direct RNA-seq reads, it is
recommended to use a smaller k-mer size for increased sensitivity to the first
or the last exons.
Minimap2 rates an alignment by the score of the max-scoring sub-segment,
*excluding* introns, and marks the best alignment as primary in SAM. When a
spliced gene also has unspliced pseudogenes, minimap2 does not intentionally
prefer spliced alignment, though in practice it more often marks the spliced
alignment as the primary. By default, minimap2 outputs up to five secondary
alignments (i.e. likely pseudogenes in the context of RNA-seq mapping). This
can be tuned with option **-N**.
For long RNA-seq reads, minimap2 may produce chimeric alignments potentially
caused by gene fusions/structural variations or by an intron longer than the
max intron length **-G** (200k by default). For now, it is not recommended to
apply an excessively large **-G** as this slows down minimap2 and sometimes
leads to false alignments.
It is worth noting that by default `-x splice` prefers GT[A/G]..[C/T]AG
over GT[C/T]..[A/G]AG, and then over other splicing signals. Considering
one additional base improves the junction accuracy for noisy reads, but
@@ -215,7 +229,7 @@ sorting) still work with such BAM records; tools that read CIGAR will
effectively ignore these records. It has been decided that future tools will
will seamlessly recognize long-cigar records generated by option `-L`.
**TD;DR**: if you work with ultra-long reads and use tools that only process
**TL;DR**: if you work with ultra-long reads and use tools that only process
BAM files, please add option `-L`.
#### <a name="cs"></a>The cs optional tag
@@ -240,28 +254,17 @@ similar to the `MD` SAM tag but is standalone and easier to parse.
If `--cs=long` is used, the `cs` string also contains identical sequences in
the alignment. The above example will become
`=CGATCG-ata=AATAGAGTAG+gtc=GAAT*at=GCA`. The long form of `cs` encodes both
reference and query sequences in one string.
reference and query sequences in one string. The `cs` tag also encodes intron
positions and splicing signals (see the [minimap2 manpage][manpage-cs] for
details).
#### <a name="eval"></a>Evaluation scripts
#### <a name="paftools"></a>Working with the PAF format
Minimap2 comes with several (java)scripts for evaluating the accuracy of
minimap2. These scripts require the [k8][k8] javascript shell to run.
Recent minimap2 binary release tar-balls contain a copy of k8 executable, a
single file. Here are a few examples on how to use these scripts:
```sh
# Generate reads from PBSIM alignment (truth encoded in read names)
k8 misc/sim-pbsim.js ref.fa.fai pbsim-aln.maf > pbsim-reads.fq
# Generate reads from mason2 alignment (not tested for simulated SVs)
k8 misc/sim-mason2.js mason2-aln.sam > mason2-reads.fq
# Evaluate mapping accuracy with ROC-like curve
k8 misc/sim-eval.js my-aln.sam.gz > result.txt
k8 misc/sim-eval.js my-aln.paf.gz > result.txt
# Collect alignment statistics
k8 misc/mapstat.js my-aln.sam > result.txt
# Compare spliced junctions to existing gene annotations
k8 misc/intron-eval.js anno.gtf my-spliced-aln.sam > result.txt
```
Minimap2 also comes with a (java)script [paftools.js](misc/paftools.js) that
processes alignments in the PAF format. It calls variants from
assembly-to-reference alignment, lifts over BED files based on alignment,
converts between formats and provides utilities for various evaluations. For
details, please see [misc/README.md](misc/README.md).
### <a name="algo"></a>Algorithm overview
@@ -309,16 +312,17 @@ highlighted in bold. The description may help to tune minimap2 parameters.
### <a name="help"></a>Getting help
Manpage [minimap2.1](minimap2.1) provides detailed description of minimap2
Manpage [minimap2.1][manpage] provides detailed description of minimap2
command line options and optional tags. If you encounter bugs or have further
questions or requests, you can raise an issue at the [issue page][issue].
There is not a specific mailing list for the time being.
### <a name="cite"></a>Citing minimap2
If you use minimap2 in your work, please consider to cite:
If you use minimap2 in your work, please cite:
> Li, H. (2017). Minimap2: fast pairwise alignment for long nucleotide sequences. [arXiv:1708.01492][preprint]
> Li, H. (2018). Minimap2: pairwise alignment for nucleotide sequences.
> Bioinformatics. [doi:10.1093/bioinformatics/bty191][doi]
## <a name="dguide"></a>Developers' Guide
@@ -341,12 +345,13 @@ mappy` or [from BioConda][mappyconda] via `conda install -c bioconda mappy`.
regions where seed positions may be suboptimal. This should not be a big
concern because even the optimal alignment may be wrong in such regions.
* Minimap2 requires SSE2 instructions to compile. It is possible to add
non-SSE2 support, but it would make minimap2 slower by several times.
* Minimap2 requires SSE2 instructions on x86 CPUs or NEON on ARM CPUs. It is
possible to add non-SIMD support, but it would make minimap2 slower by
several times.
In general, minimap2 is a young project with most code written since June, 2017.
It may have bugs and room for improvements. Bug reports and suggestions are
warmly welcomed.
* Minimap2 does not work with a single query or database sequence ~2
billion bases or longer (2,147,483,647 to be exact). The total length of all
sequences can well exceed this threshold.
@@ -363,3 +368,6 @@ warmly welcomed.
[mappyconda]: https://anaconda.org/bioconda/mappy
[issue]: https://github.com/lh3/minimap2/issues
[k8]: https://github.com/attractivechaos/k8
[manpage]: https://lh3.github.io/minimap2/minimap2.html
[manpage-cs]: https://lh3.github.io/minimap2/minimap2.html#10
[doi]: https://doi.org/10.1093/bioinformatics/bty191
+302 -86
View File
@@ -1,22 +1,24 @@
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "minimap.h"
#include "mmpriv.h"
#include "ksw2.h"
static void ksw_gen_simple_mat(int m, int8_t *mat, int8_t a, int8_t b)
static void ksw_gen_simple_mat(int m, int8_t *mat, int8_t a, int8_t b, int8_t sc_ambi)
{
int i, j;
a = a < 0? -a : a;
b = b > 0? -b : b;
sc_ambi = sc_ambi > 0? -sc_ambi : sc_ambi;
for (i = 0; i < m - 1; ++i) {
for (j = 0; j < m - 1; ++j)
mat[i * m + j] = i == j? a : b;
mat[i * m + m - 1] = 0;
mat[i * m + m - 1] = sc_ambi;
}
for (j = 0; j < m; ++j)
mat[(m - 1) * m + j] = 0;
mat[(m - 1) * m + j] = sc_ambi;
}
static inline void mm_seq_rev(uint32_t len, uint8_t *seq)
@@ -27,45 +29,70 @@ static inline void mm_seq_rev(uint32_t len, uint8_t *seq)
t = seq[i], seq[i] = seq[len - 1 - i], seq[len - 1 - i] = t;
}
static inline int test_zdrop_aux(int32_t score, int i, int j, int32_t *max, int *max_i, int *max_j, int e, int zdrop)
static inline void update_max_zdrop(int32_t score, int i, int j, int32_t *max, int *max_i, int *max_j, int e, int *max_zdrop, int pos[2][2])
{
if (score < *max) {
int li = i - *max_i;
int lj = j - *max_j;
int diff = li > lj? li - lj : lj - li;
if (*max - score > zdrop + diff * e)
return 1;
int z = *max - score - diff * e;
if (z > *max_zdrop) {
*max_zdrop = z;
pos[0][0] = *max_i, pos[0][1] = i + 1;
pos[1][0] = *max_j, pos[1][1] = j + 1;
}
} else *max = score, *max_i = i, *max_j = j;
return 0;
}
static int mm_check_zdrop(const uint8_t *qseq, const uint8_t *tseq, uint32_t n_cigar, uint32_t *cigar, const int8_t *mat, int8_t q, int8_t e, int zdrop)
static int mm_test_zdrop(void *km, const mm_mapopt_t *opt, const uint8_t *qseq, const uint8_t *tseq, uint32_t n_cigar, uint32_t *cigar, const int8_t *mat)
{
uint32_t k;
int32_t score = 0, max = 0, max_i = -1, max_j = -1, i = 0, j = 0;
for (k = 0; k < n_cigar; ++k) {
int32_t score = 0, max = INT32_MIN, max_i = -1, max_j = -1, i = 0, j = 0, max_zdrop = 0;
int pos[2][2] = {{-1, -1}, {-1, -1}}, q_len, t_len;
// find the score and the region where score drops most along diagonal
for (k = 0, score = 0; k < n_cigar; ++k) {
uint32_t l, op = cigar[k]&0xf, len = cigar[k]>>4;
if (op == 0) {
for (l = 0; l < len; ++l) {
score += mat[tseq[i + l] * 5 + qseq[j + l]];
if (test_zdrop_aux(score, i+l, j+l, &max, &max_i, &max_j, e, zdrop)) return 1;
update_max_zdrop(score, i+l, j+l, &max, &max_i, &max_j, opt->e, &max_zdrop, pos);
}
i += len, j += len;
} else if (op == 1) {
score -= q + e * len, j += len;
if (test_zdrop_aux(score, i, j, &max, &max_i, &max_j, e, zdrop)) return 1;
} else if (op == 2 || op == 3) {
score -= q + e * len, i += len;
if (test_zdrop_aux(score, i, j, &max, &max_i, &max_j, e, zdrop)) return 1;
} else if (op == 1 || op == 2 || op == 3) {
score -= opt->q + opt->e * len;
if (op == 1) j += len; // insertion
else i += len; // deletion
update_max_zdrop(score, i, j, &max, &max_i, &max_j, opt->e, &max_zdrop, pos);
}
}
return 0;
// test if there is an inversion in the most dropped region
q_len = pos[1][1] - pos[1][0], t_len = pos[0][1] - pos[0][0];
if (!(opt->flag&(MM_F_SPLICE|MM_F_SR|MM_F_FOR_ONLY|MM_F_REV_ONLY)) && max_zdrop > opt->zdrop_inv && q_len < opt->max_gap && t_len < opt->max_gap) {
uint8_t *qseq2;
void *qp;
int q_off, t_off;
qseq2 = (uint8_t*)kmalloc(km, q_len);
for (i = 0; i < q_len; ++i) {
int c = qseq[pos[1][1] - i - 1];
qseq2[i] = c >= 4? 4 : 3 - c;
}
qp = ksw_ll_qinit(km, 2, q_len, qseq2, 5, mat);
score = ksw_ll_i16(qp, t_len, tseq + pos[0][0], opt->q, opt->e, &q_off, &t_off);
kfree(km, qseq2);
kfree(km, qp);
if (score >= opt->min_chain_score * opt->a && score >= opt->min_dp_max)
return 2; // there is a potential inversion
}
return max_zdrop > opt->zdrop? 1 : 0;
}
static void mm_fix_cigar(mm_reg1_t *r, const uint8_t *qseq, const uint8_t *tseq, int *qshift, int *tshift)
{
mm_extra_t *p = r->p;
int32_t k, toff = 0, qoff = 0, to_shrink = 0;
int32_t toff = 0, qoff = 0, to_shrink = 0;
uint32_t k;
*qshift = *tshift = 0;
if (p->n_cigar <= 1) return;
for (k = 0; k < p->n_cigar; ++k) { // indel left alignment
@@ -120,10 +147,10 @@ static void mm_fix_cigar(mm_reg1_t *r, const uint8_t *qseq, const uint8_t *tseq,
}
}
static void mm_update_extra(mm_reg1_t *r, const uint8_t *qseq, const uint8_t *qual, const uint8_t *tseq, const int8_t *mat, int8_t q, int8_t e)
static void mm_update_extra(mm_reg1_t *r, const uint8_t *qseq, const uint8_t *tseq, const int8_t *mat, int8_t q, int8_t e)
{
uint32_t k, l, toff = 0, qoff = 0;
int32_t s = 0, max = 0, qshift, tshift;
uint32_t k, l;
int32_t s = 0, max = 0, qshift, tshift, toff = 0, qoff = 0;
mm_extra_t *p = r->p;
if (p == 0) return;
mm_fix_cigar(r, qseq, tseq, &qshift, &tshift);
@@ -172,12 +199,12 @@ static void mm_append_cigar(mm_reg1_t *r, uint32_t n_cigar, uint32_t *cigar) //
mm_extra_t *p;
if (n_cigar == 0) return;
if (r->p == 0) {
uint32_t capacity = n_cigar + sizeof(mm_extra_t);
uint32_t capacity = n_cigar + sizeof(mm_extra_t)/4;
kroundup32(capacity);
r->p = (mm_extra_t*)calloc(capacity, 4);
r->p->capacity = capacity;
} else if (r->p->n_cigar + n_cigar + sizeof(mm_extra_t) > r->p->capacity) {
r->p->capacity = r->p->n_cigar + n_cigar + sizeof(mm_extra_t);
} else if (r->p->n_cigar + n_cigar + sizeof(mm_extra_t)/4 > r->p->capacity) {
r->p->capacity = r->p->n_cigar + n_cigar + sizeof(mm_extra_t)/4;
kroundup32(r->p->capacity);
r->p = (mm_extra_t*)realloc(r->p, r->p->capacity * 4);
}
@@ -192,9 +219,79 @@ static void mm_append_cigar(mm_reg1_t *r, uint32_t n_cigar, uint32_t *cigar) //
}
}
static void mm_align_pair(void *km, const mm_mapopt_t *opt, int qlen, const uint8_t *qseq, int tlen, const uint8_t *tseq, const int8_t *mat, int w, int end_bonus, int flag, ksw_extz_t *ez)
static void mm_update_cigar_eqx(mm_reg1_t *r, const uint8_t *qseq, const uint8_t *tseq) // written by @armintoepfer
{
uint32_t n_EQX = 0;
uint32_t k, l, m, cap, toff = 0, qoff = 0, n_M = 0;
mm_extra_t *p;
if (r->p == 0) return;
for (k = 0; k < r->p->n_cigar; ++k) {
uint32_t op = r->p->cigar[k]&0xf, len = r->p->cigar[k]>>4;
if (op == 0) {
while (len > 0) {
for (l = 0; l < len && qseq[qoff + l] == tseq[toff + l]; ++l) {} // run of "="; TODO: N<=>N is converted to "="
if (l > 0) { ++n_EQX; len -= l; toff += l; qoff += l; }
for (l = 0; l < len && qseq[qoff + l] != tseq[toff + l]; ++l) {} // run of "X"
if (l > 0) { ++n_EQX; len -= l; toff += l; qoff += l; }
}
++n_M;
} else if (op == 1) { // insertion
qoff += len;
} else if (op == 2) { // deletion
toff += len;
} else if (op == 3) { // intron
toff += len;
}
}
// update in-place if we can
if (n_EQX == n_M) {
for (k = 0; k < r->p->n_cigar; ++k) {
uint32_t op = r->p->cigar[k]&0xf, len = r->p->cigar[k]>>4;
if (op == 0) r->p->cigar[k] = len << 4 | 7;
}
return;
}
// allocate new storage
cap = r->p->n_cigar + (n_EQX - n_M) + sizeof(mm_extra_t);
kroundup32(cap);
p = (mm_extra_t*)calloc(cap, 4);
memcpy(p, r->p, sizeof(mm_extra_t));
p->capacity = cap;
// update cigar while copying
toff = qoff = m = 0;
for (k = 0; k < r->p->n_cigar; ++k) {
uint32_t op = r->p->cigar[k]&0xf, len = r->p->cigar[k]>>4;
if (op == 0) { // match/mismatch
while (len > 0) {
// match
for (l = 0; l < len && qseq[qoff + l] == tseq[toff + l]; ++l) {}
if (l > 0) p->cigar[m++] = l << 4 | 7;
len -= l;
toff += l, qoff += l;
// mismatch
for (l = 0; l < len && qseq[qoff + l] != tseq[toff + l]; ++l) {}
if (l > 0) p->cigar[m++] = l << 4 | 8;
len -= l;
toff += l, qoff += l;
}
continue;
} else if (op == 1) { // insertion
qoff += len;
} else if (op == 2) { // deletion
toff += len;
} else if (op == 3) { // intron
toff += len;
}
p->cigar[m++] = r->p->cigar[k];
}
p->n_cigar = m;
free(r->p);
r->p = p;
}
static void mm_align_pair(void *km, const mm_mapopt_t *opt, int qlen, const uint8_t *qseq, int tlen, const uint8_t *tseq, const int8_t *mat, int w, int end_bonus, int zdrop, int flag, ksw_extz_t *ez)
{
int zdrop = opt->zdrop;
if (mm_dbg_flag & MM_DBG_PRINT_ALN_SEQ) {
int i;
fprintf(stderr, "===> q=(%d,%d), e=(%d,%d), bw=%d, flag=%d, zdrop=%d <===\n", opt->q, opt->q2, opt->e, opt->e2, w, flag, opt->zdrop);
@@ -209,6 +306,13 @@ static void mm_align_pair(void *km, const mm_mapopt_t *opt, int qlen, const uint
ksw_extz2_sse(km, qlen, qseq, tlen, tseq, 5, mat, opt->q, opt->e, w, zdrop, end_bonus, flag, ez);
else
ksw_extd2_sse(km, qlen, qseq, tlen, tseq, 5, mat, opt->q, opt->e, opt->q2, opt->e2, w, zdrop, end_bonus, flag, ez);
if (mm_dbg_flag & MM_DBG_PRINT_ALN_SEQ) {
int i;
fprintf(stderr, "score=%d, cigar=", ez->score);
for (i = 0; i < ez->n_cigar; ++i)
fprintf(stderr, "%d%c", ez->cigar[i]>>4, "MIDN"[ez->cigar[i]&0xf]);
fprintf(stderr, "\n");
}
}
static inline int mm_get_hplen_back(const mm_idx_t *mi, uint32_t rid, uint32_t x)
@@ -222,7 +326,7 @@ static inline int mm_get_hplen_back(const mm_idx_t *mi, uint32_t rid, uint32_t x
static inline void mm_adjust_minier(const mm_idx_t *mi, uint8_t *const qseq0[2], mm128_t *a, int32_t *r, int32_t *q)
{
if (mi->is_hpc) {
if (mi->flag & MM_I_HPC) {
const uint8_t *qseq = qseq0[a->x>>63];
int i, c;
*q = (int32_t)a->y;
@@ -237,20 +341,30 @@ static inline void mm_adjust_minier(const mm_idx_t *mi, uint8_t *const qseq0[2],
}
}
static void mm_filter_bad_seeds(void *km, int as1, int cnt1, mm128_t *a, int min_gap, int diff_thres, int max_ext_len, int max_ext_cnt)
static int *collect_long_gaps(void *km, int as1, int cnt1, mm128_t *a, int min_gap, int *n_)
{
int max_st, max_en, n, i, k, max, *K;
int i, n, *K;
*n_ = 0;
for (i = 1, n = 0; i < cnt1; ++i) { // count the number of gaps longer than min_gap
int gap = ((int32_t)a[as1 + i].y - a[as1 + i - 1].y) - ((int32_t)a[as1 + i].x - a[as1 + i - 1].x);
if (gap < -min_gap || gap > min_gap) ++n;
}
if (n <= 1) return;
if (n <= 1) return 0;
K = (int*)kmalloc(km, n * sizeof(int));
for (i = 1, n = 0; i < cnt1; ++i) { // store the positions of long gaps
int gap = ((int32_t)a[as1 + i].y - a[as1 + i - 1].y) - ((int32_t)a[as1 + i].x - a[as1 + i - 1].x);
if (gap < -min_gap || gap > min_gap)
K[n++] = i;
}
*n_ = n;
return K;
}
static void mm_filter_bad_seeds(void *km, int as1, int cnt1, mm128_t *a, int min_gap, int diff_thres, int max_ext_len, int max_ext_cnt)
{
int max_st, max_en, n, i, k, max, *K;
K = collect_long_gaps(km, as1, cnt1, a, min_gap, &n);
if (K == 0) return;
max = 0, max_st = max_en = -1;
for (k = 0;; ++k) { // traverse long gaps
int gap, l, n_ins = 0, n_del = 0, qs, rs, max_diff = 0, max_diff_l = -1;
@@ -262,7 +376,7 @@ static void mm_filter_bad_seeds(void *km, int as1, int cnt1, mm128_t *a, int min
if (k == n) break;
}
i = K[k];
gap = ((int32_t)a[as1 + i].y - a[as1 + i - 1].y) - ((int32_t)a[as1 + i].x - a[as1 + i - 1].x);
gap = ((int32_t)a[as1 + i].y - (int32_t)a[as1 + i - 1].y) - (int32_t)(a[as1 + i].x - a[as1 + i - 1].x);
if (gap > 0) n_ins += gap;
else n_del += -gap;
qs = (int32_t)a[as1 + i - 1].y;
@@ -270,7 +384,7 @@ static void mm_filter_bad_seeds(void *km, int as1, int cnt1, mm128_t *a, int min
for (l = k + 1; l < n && l <= k + max_ext_cnt; ++l) {
int j = K[l], diff;
if ((int32_t)a[as1 + j].y - qs > max_ext_len || (int32_t)a[as1 + j].x - rs > max_ext_len) break;
gap = ((int32_t)a[as1 + j].y - (int32_t)a[as1 + j - 1].y) - (a[as1 + j].x - a[as1 + j - 1].x);
gap = ((int32_t)a[as1 + j].y - (int32_t)a[as1 + j - 1].y) - (int32_t)(a[as1 + j].x - a[as1 + j - 1].x);
if (gap > 0) n_ins += gap;
else n_del += -gap;
diff = n_ins + n_del - abs(n_ins - n_del);
@@ -283,37 +397,79 @@ static void mm_filter_bad_seeds(void *km, int as1, int cnt1, mm128_t *a, int min
kfree(km, K);
}
static void mm_fix_bad_ends(const mm_reg1_t *r, const mm128_t *a, int bw, int32_t *as, int32_t *cnt)
static void mm_filter_bad_seeds_alt(void *km, int as1, int cnt1, mm128_t *a, int min_gap, int max_ext)
{
int32_t i, l;
int n, k, *K;
K = collect_long_gaps(km, as1, cnt1, a, min_gap, &n);
if (K == 0) return;
for (k = 0; k < n;) {
int i = K[k], l;
int gap1 = ((int32_t)a[as1 + i].y - (int32_t)a[as1 + i - 1].y) - ((int32_t)a[as1 + i].x - (int32_t)a[as1 + i - 1].x);
int re1 = (int32_t)a[as1 + i].x;
int qe1 = (int32_t)a[as1 + i].y;
gap1 = gap1 > 0? gap1 : -gap1;
for (l = k + 1; l < n; ++l) {
int j = K[l], gap2, q_span_pre, rs2, qs2, m;
if ((int32_t)a[as1 + j].y - qe1 > max_ext || (int32_t)a[as1 + j].x - re1 > max_ext) break;
gap2 = ((int32_t)a[as1 + j].y - (int32_t)a[as1 + j - 1].y) - (int32_t)(a[as1 + j].x - a[as1 + j - 1].x);
q_span_pre = a[as1 + j - 1].y >> 32 & 0xff;
rs2 = (int32_t)a[as1 + j - 1].x + q_span_pre;
qs2 = (int32_t)a[as1 + j - 1].x + q_span_pre;
m = rs2 - re1 < qs2 - qe1? rs2 - re1 : qs2 - qe1;
gap2 = gap2 > 0? gap2 : -gap2;
if (m > gap1 + gap2) break;
re1 = (int32_t)a[as1 + j].x;
qe1 = (int32_t)a[as1 + j].y;
gap1 = gap2;
}
if (l > k + 1) {
int j, end = K[l - 1];
for (j = K[k]; j < end; ++j)
a[as1 + j].y |= MM_SEED_IGNORE;
a[as1 + end].y |= MM_SEED_LONG_JOIN;
}
k = l;
}
kfree(km, K);
}
static void mm_fix_bad_ends(const mm_reg1_t *r, const mm128_t *a, int bw, int min_match, int32_t *as, int32_t *cnt)
{
int32_t i, l, m;
*as = r->as, *cnt = r->cnt;
if (r->cnt < 3) return;
l = a[r->as].y >> 32 & 0xff;
m = l = a[r->as].y >> 32 & 0xff;
for (i = r->as + 1; i < r->as + r->cnt - 1; ++i) {
int32_t lq, lr, min, max;
int32_t q_span = a[i].y >> 32 & 0xff;
if (a[i].y & MM_SEED_LONG_JOIN) break;
lr = (int32_t)a[i].x - (int32_t)a[i-1].x;
lq = (int32_t)a[i].y - (int32_t)a[i-1].y;
min = lr < lq? lr : lq;
max = lr > lq? lr : lq;
if (max - min > l >> 1) *as = i;
l += min;
if (l >= bw << 1) break;
m += min < q_span? min : q_span;
if (l >= bw << 1 || (m >= min_match && m >= bw) || m >= r->mlen >> 1) break;
}
*cnt = r->as + r->cnt - *as;
l = a[r->as + r->cnt - 1].y >> 32 & 0xff;
m = l = a[r->as + r->cnt - 1].y >> 32 & 0xff;
for (i = r->as + r->cnt - 2; i > *as; --i) {
int32_t lq, lr, min, max;
int32_t q_span = a[i+1].y >> 32 & 0xff;
if (a[i+1].y & MM_SEED_LONG_JOIN) break;
lr = (int32_t)a[i+1].x - (int32_t)a[i].x;
lq = (int32_t)a[i+1].y - (int32_t)a[i].y;
min = lr < lq? lr : lq;
max = lr > lq? lr : lq;
if (max - min > l >> 1) *cnt = i + 1 - *as;
l += min;
if (l >= bw) break;
m += min < q_span? min : q_span;
if (l >= bw << 1 || (m >= min_match && m >= bw) || m >= r->mlen >> 1) break;
}
}
static void mm_max_stretch(const mm_mapopt_t *opt, const mm_reg1_t *r, const mm128_t *a, int32_t *as, int32_t *cnt)
static void mm_max_stretch(const mm_reg1_t *r, const mm128_t *a, int32_t *as, int32_t *cnt)
{
int32_t i, score, max_score, len, max_i, max_len;
@@ -341,7 +497,49 @@ static void mm_max_stretch(const mm_mapopt_t *opt, const mm_reg1_t *r, const mm1
*as = max_i, *cnt = max_len;
}
static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int qlen, uint8_t *qseq0[2], uint8_t *qual0[2], mm_reg1_t *r, mm_reg1_t *r2, int n_a, mm128_t *a, ksw_extz_t *ez, int splice_flag)
static int mm_seed_ext_score(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, const int8_t mat[25], int qlen, uint8_t *qseq0[2], const mm128_t *a)
{
uint8_t *qseq, *tseq;
int q_span = a->y>>32&0xff, qs, qe, rs, re, rid, score, q_off, t_off, ext_len = opt->anchor_ext_len;
void *qp;
rid = a->x<<1>>33;
re = (uint32_t)a->x + 1, rs = re - q_span;
qe = (uint32_t)a->y + 1, qs = qe - q_span;
rs = rs - ext_len > 0? rs - ext_len : 0;
qs = qs - ext_len > 0? qs - ext_len : 0;
re = re + ext_len < (int32_t)mi->seq[rid].len? re + ext_len : mi->seq[rid].len;
qe = qe + ext_len < qlen? qe + ext_len : qlen;
tseq = (uint8_t*)kmalloc(km, re - rs);
mm_idx_getseq(mi, rid, rs, re, tseq);
qseq = qseq0[a->x>>63] + qs;
qp = ksw_ll_qinit(km, 2, qe - qs, qseq, 5, mat);
score = ksw_ll_i16(qp, re - rs, tseq, opt->q, opt->e, &q_off, &t_off);
kfree(km, tseq);
kfree(km, qp);
return score;
}
static void mm_fix_bad_ends_splice(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, const mm_reg1_t *r, const int8_t mat[25], int qlen, uint8_t *qseq0[2], const mm128_t *a, int *as1, int *cnt1)
{ // this assumes a very crude k-mer based mode; it is not necessary to use a good model just for filtering bounary exons
int score;
double log_gap;
*as1 = r->as, *cnt1 = r->cnt;
if (r->cnt < 3) return;
log_gap = log((int32_t)a[r->as + 1].x - (int32_t)a[r->as].x);
if ((a[r->as].y>>32&0xff) < log_gap + opt->anchor_ext_shift) {
score = mm_seed_ext_score(km, opt, mi, mat, qlen, qseq0, &a[r->as]);
if ((double)score / mat[0] < log_gap + opt->anchor_ext_shift) // a more exact format is "score < log_4(gap) + shift"
++(*as1), --(*cnt1);
}
log_gap = log((int32_t)a[r->as + r->cnt - 1].x - (int32_t)a[r->as + r->cnt - 2].x);
if ((a[r->as + r->cnt - 1].y>>32&0xff) < log_gap + opt->anchor_ext_shift) {
score = mm_seed_ext_score(km, opt, mi, mat, qlen, qseq0, &a[r->as + r->cnt - 1]);
if ((double)score / mat[0] < log_gap + opt->anchor_ext_shift)
--(*cnt1);
}
}
static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int qlen, uint8_t *qseq0[2], mm_reg1_t *r, mm_reg1_t *r2, int n_a, mm128_t *a, ksw_extz_t *ez, int splice_flag)
{
int is_sr = !!(opt->flag & MM_F_SR), is_splice = !!(opt->flag & MM_F_SPLICE);
int32_t rid = a[r->as].x<<1>>33, rev = a[r->as].x>>63, as1, cnt1;
@@ -351,24 +549,27 @@ static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int
int32_t rs1, qs1, re1, qe1;
int8_t mat[25];
if (is_sr) assert(!mi->is_hpc); // HPC won't work with SR because with HPC we can't easily tell if there is a gap
if (is_sr) assert(!(mi->flag & MM_I_HPC)); // HPC won't work with SR because with HPC we can't easily tell if there is a gap
r2->cnt = 0;
if (r->cnt == 0) return;
ksw_gen_simple_mat(5, mat, opt->a, opt->b);
ksw_gen_simple_mat(5, mat, opt->a, opt->b, opt->sc_ambi);
bw = (int)(opt->bw * 1.5 + 1.);
if (is_sr && !mi->is_hpc) {
mm_max_stretch(opt, r, a, &as1, &cnt1);
if (is_sr && !(mi->flag & MM_I_HPC)) {
mm_max_stretch(r, a, &as1, &cnt1);
rs = (int32_t)a[as1].x + 1 - (int32_t)(a[as1].y>>32&0xff);
qs = (int32_t)a[as1].y + 1 - (int32_t)(a[as1].y>>32&0xff);
re = (int32_t)a[as1+cnt1-1].x + 1;
qe = (int32_t)a[as1+cnt1-1].y + 1;
} else {
if (!is_splice)
mm_fix_bad_ends(r, a, opt->bw, &as1, &cnt1);
else as1 = r->as, cnt1 = r->cnt;
if (is_splice) {
mm_fix_bad_ends_splice(km, opt, mi, r, mat, qlen, qseq0, a, &as1, &cnt1);
} else {
mm_fix_bad_ends(r, a, opt->bw, opt->min_chain_score * 2, &as1, &cnt1);
}
mm_filter_bad_seeds(km, as1, cnt1, a, 10, 40, opt->max_gap>>1, 10);
mm_filter_bad_seeds_alt(km, as1, cnt1, a, 30, opt->max_gap>>1);
mm_adjust_minier(mi, qseq0, &a[as1], &rs, &qs);
mm_adjust_minier(mi, qseq0, &a[as1 + cnt1 - 1], &re, &qe);
}
@@ -392,7 +593,7 @@ static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int
rs0 = rs - l > 0? rs - l : 0;
l = qlen - qe;
l += l * opt->a + opt->end_bonus > opt->q? (l * opt->a + opt->end_bonus - opt->q) / opt->e : 0;
re0 = re + l < mi->seq[rid].len? re + l : mi->seq[rid].len;
re0 = re + l < (int32_t)mi->seq[rid].len? re + l : mi->seq[rid].len;
} else {
// compute rs0 and qs0
rs0 = (int32_t)a[r->as].x + 1 - (int32_t)(a[r->as].y>>32&0xff);
@@ -436,17 +637,25 @@ static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int
}
}
}
if (qe < qlen && re < mi->seq[rid].len) {
if (qe < qlen && re < (int32_t)mi->seq[rid].len) {
l = qlen - qe < opt->max_gap? qlen - qe : opt->max_gap;
qe1 = qe1 < qe + l? qe1 : qe + l;
qe0 = qe0 > qe1? qe0 : qe1; // at least include qe0
l += l * opt->a > opt->q? (l * opt->a - opt->q) / opt->e : 0;
l = l < opt->max_gap? l : opt->max_gap;
l = l < mi->seq[rid].len - re? l : mi->seq[rid].len - re;
l = l < (int32_t)mi->seq[rid].len - re? l : mi->seq[rid].len - re;
re1 = re1 < re + l? re1 : re + l;
re0 = re0 > re1? re0 : re1;
} else re0 = re, qe0 = qe;
}
if (a[r->as].y & MM_SEED_SELF) {
int max_ext = r->qs > r->rs? r->qs - r->rs : r->rs - r->qs;
if (r->rs - rs0 > max_ext) rs0 = r->rs - max_ext;
if (r->qs - qs0 > max_ext) qs0 = r->qs - max_ext;
max_ext = r->qe > r->re? r->qe - r->re : r->re - r->qe;
if (re0 - r->re > max_ext) re0 = r->re + max_ext;
if (qe0 - r->qe > max_ext) qe0 = r->qe + max_ext;
}
assert(re0 > rs0);
tseq = (uint8_t*)kmalloc(km, re0 - rs0);
@@ -456,7 +665,7 @@ static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int
mm_idx_getseq(mi, rid, rs0, rs, tseq);
mm_seq_rev(qs - qs0, qseq);
mm_seq_rev(rs - rs0, tseq);
mm_align_pair(km, opt, qs - qs0, qseq, rs - rs0, tseq, mat, bw, opt->end_bonus, extra_flag|KSW_EZ_EXTZ_ONLY|KSW_EZ_RIGHT|KSW_EZ_REV_CIGAR, ez);
mm_align_pair(km, opt, qs - qs0, qseq, rs - rs0, tseq, mat, bw, opt->end_bonus, r->split_inv? opt->zdrop_inv : opt->zdrop, extra_flag|KSW_EZ_EXTZ_ONLY|KSW_EZ_RIGHT|KSW_EZ_REV_CIGAR, ez);
if (ez->n_cigar > 0) {
mm_append_cigar(r, ez->n_cigar, ez->cigar);
r->p->dp_score += ez->max;
@@ -470,15 +679,16 @@ static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int
for (i = is_sr? cnt1 - 1 : 1; i < cnt1; ++i) { // gap filling
if ((a[as1+i].y & (MM_SEED_IGNORE|MM_SEED_TANDEM)) && i != cnt1 - 1) continue;
if (is_sr && !mi->is_hpc) {
if (is_sr && !(mi->flag & MM_I_HPC)) {
re = (int32_t)a[as1 + i].x + 1;
qe = (int32_t)a[as1 + i].y + 1;
} else mm_adjust_minier(mi, qseq0, &a[as1 + i], &re, &qe);
re1 = re, qe1 = qe;
if (i == cnt1 - 1 || (a[as1+i].y&MM_SEED_LONG_JOIN) || (qe - qs >= opt->min_ksw_len && re - rs >= opt->min_ksw_len)) {
int j, bw1 = bw;
int j, bw1 = bw, zdrop_code;
if (a[as1+i].y & MM_SEED_LONG_JOIN)
bw1 = qe - qs > re - rs? qe - qs : re - rs;
// perform alignment
qseq = &qseq0[rev][qs];
mm_idx_getseq(mi, rid, rs, re, tseq);
if (is_sr) { // perform ungapped alignment
@@ -490,10 +700,12 @@ static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int
}
ez->cigar = ksw_push_cigar(km, &ez->n_cigar, &ez->m_cigar, ez->cigar, 0, qe - qs);
} else { // perform normal gapped alignment
mm_align_pair(km, opt, qe - qs, qseq, re - rs, tseq, mat, bw1, -1, extra_flag|KSW_EZ_APPROX_MAX, ez); // first pass: with approximate Z-drop
mm_align_pair(km, opt, qe - qs, qseq, re - rs, tseq, mat, bw1, -1, opt->zdrop, extra_flag|KSW_EZ_APPROX_MAX, ez); // first pass: with approximate Z-drop
}
if (mm_check_zdrop(qseq, tseq, ez->n_cigar, ez->cigar, mat, opt->q, opt->e, opt->zdrop))
mm_align_pair(km, opt, qe - qs, qseq, re - rs, tseq, mat, bw1, -1, extra_flag, ez); // second pass: lift approximate
// test Z-drop and inversion Z-drop
if ((zdrop_code = mm_test_zdrop(km, opt, qseq, tseq, ez->n_cigar, ez->cigar, mat)) != 0)
mm_align_pair(km, opt, qe - qs, qseq, re - rs, tseq, mat, bw1, -1, zdrop_code == 2? opt->zdrop_inv : opt->zdrop, extra_flag, ez); // second pass: lift approximate
// update CIGAR
if (ez->n_cigar > 0)
mm_append_cigar(r, ez->n_cigar, ez->cigar);
if (ez->zdropped) { // truncated by Z-drop; TODO: sometimes Z-drop kicks in because the next seed placement is wrong. This can be fixed in principle.
@@ -505,8 +717,10 @@ static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int
r->p->dp_score += ez->max;
re1 = rs + (ez->max_t + 1);
qe1 = qs + (ez->max_q + 1);
if (cnt1 - (j + 1) >= opt->min_cnt)
if (cnt1 - (j + 1) >= opt->min_cnt) {
mm_split_reg(r, r2, as1 + j + 1 - r->as, qlen, a);
if (zdrop_code == 2) r2->split_inv = 1;
}
break;
} else r->p->dp_score += ez->score;
rs = re, qs = qe;
@@ -516,7 +730,7 @@ static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int
if (!dropped && qe < qe0 && re < re0) { // right extension
qseq = &qseq0[rev][qe];
mm_idx_getseq(mi, rid, re, re0, tseq);
mm_align_pair(km, opt, qe0 - qe, qseq, re0 - re, tseq, mat, bw, opt->end_bonus, extra_flag|KSW_EZ_EXTZ_ONLY, ez);
mm_align_pair(km, opt, qe0 - qe, qseq, re0 - re, tseq, mat, bw, opt->end_bonus, opt->zdrop, extra_flag|KSW_EZ_EXTZ_ONLY, ez);
if (ez->n_cigar > 0) {
mm_append_cigar(r, ez->n_cigar, ez->cigar);
r->p->dp_score += ez->max;
@@ -533,7 +747,8 @@ static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int
assert(re1 - rs1 <= re0 - rs0);
if (r->p) {
mm_idx_getseq(mi, rid, rs1, re1, tseq);
mm_update_extra(r, &qseq0[r->rev][qs1], qual0[r->rev]? &qual0[r->rev][qs1] : 0, tseq, mat, opt->q, opt->e);
mm_update_extra(r, &qseq0[r->rev][qs1], tseq, mat, opt->q, opt->e);
if (opt->flag & MM_F_EQX) mm_update_cigar_eqx(r, &qseq0[r->rev][qs1], tseq);
if (rev && r->p->trans_strand)
r->p->trans_strand ^= 3; // flip to the read strand
}
@@ -541,10 +756,10 @@ static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int
kfree(km, tseq);
}
static int mm_align1_inv(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int qlen, uint8_t *qseq0[2], uint8_t *qual0[2], const mm_reg1_t *r1, const mm_reg1_t *r2, mm_reg1_t *r_inv, ksw_extz_t *ez)
static int mm_align1_inv(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int qlen, uint8_t *qseq0[2], const mm_reg1_t *r1, const mm_reg1_t *r2, mm_reg1_t *r_inv, ksw_extz_t *ez)
{
int tl, ql, score, ret = 0, q_off, t_off;
uint8_t *tseq, *qseq, *qual;
uint8_t *tseq, *qseq;
int8_t mat[25];
void *qp;
@@ -553,16 +768,15 @@ static int mm_align1_inv(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, i
if (r1->id != r1->parent && r1->parent != MM_PARENT_TMP_PRI) return 0;
if (r2->id != r2->parent && r2->parent != MM_PARENT_TMP_PRI) return 0;
if (r1->rid != r2->rid || r1->rev != r2->rev) return 0;
ql = r2->qs - r1->qe;
ql = r1->rev? r1->qs - r2->qe : r2->qs - r1->qe;
tl = r2->rs - r1->re;
if (ql < opt->min_chain_score || ql > opt->max_gap) return 0;
if (tl < opt->min_chain_score || tl > opt->max_gap) return 0;
ksw_gen_simple_mat(5, mat, opt->a, opt->b);
ksw_gen_simple_mat(5, mat, opt->a, opt->b, opt->sc_ambi);
tseq = (uint8_t*)kmalloc(km, tl);
mm_idx_getseq(mi, r1->rid, r1->re, r2->rs, tseq);
qseq = &qseq0[!r1->rev][qlen - r2->qs];
qual = qual0[!r1->rev]? &qseq0[!r1->rev][qlen - r2->qs] : 0;
qseq = r1->rev? &qseq0[0][r2->qe] : &qseq0[1][qlen - r2->qs];
mm_seq_rev(ql, qseq);
mm_seq_rev(tl, tseq);
@@ -573,7 +787,7 @@ static int mm_align1_inv(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, i
mm_seq_rev(tl, tseq);
if (score < opt->min_dp_max) goto end_align1_inv;
q_off = ql - (q_off + 1), t_off = tl - (t_off + 1);
mm_align_pair(km, opt, ql - q_off, qseq + q_off, tl - t_off, tseq + t_off, mat, (int)(opt->bw * 1.5), -1, KSW_EZ_EXTZ_ONLY, ez);
mm_align_pair(km, opt, ql - q_off, qseq + q_off, tl - t_off, tseq + t_off, mat, (int)(opt->bw * 1.5), -1, opt->zdrop, KSW_EZ_EXTZ_ONLY, ez);
if (ez->n_cigar == 0) goto end_align1_inv; // should never be here
mm_append_cigar(r_inv, ez->n_cigar, ez->cigar);
r_inv->p->dp_score = ez->max;
@@ -582,9 +796,18 @@ static int mm_align1_inv(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, i
r_inv->inv = 1;
r_inv->rev = !r1->rev;
r_inv->rid = r1->rid;
r_inv->qs = r1->qe + q_off, r_inv->qe = r_inv->qs + ez->max_q + 1;
r_inv->rs = r1->re + t_off, r_inv->re = r_inv->rs + ez->max_t + 1;
mm_update_extra(r_inv, &qseq[q_off], qual? &qual[q_off] : 0, &tseq[t_off], mat, opt->q, opt->e);
r_inv->div = -1.0f;
if (r_inv->rev == 0) {
r_inv->qs = r2->qe + q_off;
r_inv->qe = r_inv->qs + ez->max_q + 1;
} else {
r_inv->qe = r2->qs - q_off;
r_inv->qs = r_inv->qe - (ez->max_q + 1);
}
r_inv->rs = r1->re + t_off;
r_inv->re = r_inv->rs + ez->max_t + 1;
mm_update_extra(r_inv, &qseq[q_off], &tseq[t_off], mat, opt->q, opt->e);
if (opt->flag & MM_F_EQX) mm_update_cigar_eqx(r_inv, &qseq[q_off], &tseq[t_off]);
ret = 1;
end_align1_inv:
kfree(km, tseq);
@@ -601,11 +824,11 @@ static inline mm_reg1_t *mm_insert_reg(const mm_reg1_t *r, int i, int *n_regs, m
return regs;
}
mm_reg1_t *mm_align_skeleton(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int qlen, const char *qstr, const char *qual, int *n_regs_, mm_reg1_t *regs, mm128_t *a)
mm_reg1_t *mm_align_skeleton(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int qlen, const char *qstr, int *n_regs_, mm_reg1_t *regs, mm128_t *a)
{
extern unsigned char seq_nt4_table[256];
int32_t i, n_regs = *n_regs_, n_a;
uint8_t *qseq0[2], *qual0[2];
uint8_t *qseq0[2];
ksw_extz_t ez;
// encode the query sequence
@@ -615,12 +838,6 @@ mm_reg1_t *mm_align_skeleton(void *km, const mm_mapopt_t *opt, const mm_idx_t *m
qseq0[0][i] = seq_nt4_table[(uint8_t)qstr[i]];
qseq0[1][qlen - 1 - i] = qseq0[0][i] < 4? 3 - qseq0[0][i] : 4;
}
if (qual) {
qual0[0] = (uint8_t*)kmalloc(km, qlen * 2);
qual0[1] = qual0[0] + qlen;
for (i = 0; i < qlen; ++i)
qual0[0][i] = qual0[1][qlen - 1 - i] = qual[i] - 33;
} else qual0[0] = qual0[1] = 0;
// align through seed hits
n_a = mm_squeeze_a(km, n_regs, regs, a);
@@ -631,8 +848,8 @@ mm_reg1_t *mm_align_skeleton(void *km, const mm_mapopt_t *opt, const mm_idx_t *m
mm_reg1_t s[2], s2[2];
int which, trans_strand;
s[0] = s[1] = regs[i];
mm_align1(km, opt, mi, qlen, qseq0, qual0, &s[0], &s2[0], n_a, a, &ez, MM_F_SPLICE_FOR);
mm_align1(km, opt, mi, qlen, qseq0, qual0, &s[1], &s2[1], n_a, a, &ez, MM_F_SPLICE_REV);
mm_align1(km, opt, mi, qlen, qseq0, &s[0], &s2[0], n_a, a, &ez, MM_F_SPLICE_FOR);
mm_align1(km, opt, mi, qlen, qseq0, &s[1], &s2[1], n_a, a, &ez, MM_F_SPLICE_REV);
if (s[0].p->dp_score > s[1].p->dp_score) which = 0, trans_strand = 1;
else if (s[0].p->dp_score < s[1].p->dp_score) which = 1, trans_strand = 2;
else trans_strand = 3, which = (qlen + s[0].p->dp_score) & 1; // randomly choose a strand, effectively
@@ -645,13 +862,13 @@ mm_reg1_t *mm_align_skeleton(void *km, const mm_mapopt_t *opt, const mm_idx_t *m
}
regs[i].p->trans_strand = trans_strand;
} else { // one round of alignment
mm_align1(km, opt, mi, qlen, qseq0, qual0, &regs[i], &r2, n_a, a, &ez, opt->flag);
mm_align1(km, opt, mi, qlen, qseq0, &regs[i], &r2, n_a, a, &ez, opt->flag);
if (opt->flag&MM_F_SPLICE)
regs[i].p->trans_strand = opt->flag&MM_F_SPLICE_FOR? 1 : 2;
}
if (r2.cnt > 0) regs = mm_insert_reg(&r2, i, &n_regs, regs);
if (!(opt->flag&MM_F_SPLICE) && !(opt->flag&MM_F_SR) && i > 0) { // don't try inversion alignment for -xsplice or -xsr
if (mm_align1_inv(km, opt, mi, qlen, qseq0, qual0, &regs[i-1], &regs[i], &r2, &ez)) {
if (i > 0 && regs[i].split_inv) {
if (mm_align1_inv(km, opt, mi, qlen, qseq0, &regs[i-1], &regs[i], &r2, &ez)) {
regs = mm_insert_reg(&r2, i, &n_regs, regs);
++i; // skip the inserted INV alignment
}
@@ -659,9 +876,8 @@ mm_reg1_t *mm_align_skeleton(void *km, const mm_mapopt_t *opt, const mm_idx_t *m
}
*n_regs_ = n_regs;
kfree(km, qseq0[0]);
if (qual0[0]) kfree(km, qual0[0]);
kfree(km, ez.cigar);
mm_filter_regs(km, opt, n_regs_, regs);
mm_hit_sort_by_dp(km, n_regs_, regs);
mm_filter_regs(opt, qlen, n_regs_, regs);
mm_hit_sort(km, n_regs_, regs);
return regs;
}
+38 -13
View File
@@ -2,6 +2,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define __STDC_LIMIT_MACROS
#include "bseq.h"
#include "kvec.h"
#include "kseq.h"
@@ -53,19 +54,28 @@ void mm_bseq_close(mm_bseq_file_t *fp)
free(fp);
}
static inline void kseq2bseq(kseq_t *ks, mm_bseq1_t *s, int with_qual)
static inline char *kstrdup(const kstring_t *s)
{
char *t;
t = (char*)malloc(s->l + 1);
memcpy(t, s->s, s->l + 1);
return t;
}
static inline void kseq2bseq(kseq_t *ks, mm_bseq1_t *s, int with_qual, int with_comment)
{
int i;
s->name = strdup(ks->name.s);
s->seq = strdup(ks->seq.s);
for (i = 0; i < ks->seq.l; ++i) // convert U to T
s->name = kstrdup(&ks->name);
s->seq = kstrdup(&ks->seq);
for (i = 0; i < (int)ks->seq.l; ++i) // convert U to T
if (s->seq[i] == 'u' || s->seq[i] == 'U')
--s->seq[i];
s->qual = with_qual && ks->qual.l? strdup(ks->qual.s) : 0;
s->qual = with_qual && ks->qual.l? kstrdup(&ks->qual) : 0;
s->comment = with_comment && ks->comment.l? kstrdup(&ks->comment) : 0;
s->l_seq = ks->seq.l;
}
mm_bseq1_t *mm_bseq_read2(mm_bseq_file_t *fp, int chunk_size, int with_qual, int frag_mode, int *n_)
mm_bseq1_t *mm_bseq_read3(mm_bseq_file_t *fp, int chunk_size, int with_qual, int with_comment, int frag_mode, int *n_)
{
int64_t size = 0;
kvec_t(mm_bseq1_t) a = {0,0,0};
@@ -82,12 +92,12 @@ mm_bseq1_t *mm_bseq_read2(mm_bseq_file_t *fp, int chunk_size, int with_qual, int
assert(ks->seq.l <= INT32_MAX);
if (a.m == 0) kv_resize(mm_bseq1_t, 0, a, 256);
kv_pushp(mm_bseq1_t, 0, a, &s);
kseq2bseq(ks, s, with_qual);
kseq2bseq(ks, s, with_qual, with_comment);
size += s->l_seq;
if (size >= chunk_size) {
if (frag_mode && a.a[a.n-1].l_seq < CHECK_PAIR_THRES) {
while (kseq_read(ks) >= 0) {
kseq2bseq(ks, &fp->s, with_qual);
kseq2bseq(ks, &fp->s, with_qual, with_comment);
if (mm_qname_same(fp->s.name, a.a[a.n-1].name)) {
kv_push(mm_bseq1_t, 0, a, fp->s);
memset(&fp->s, 0, sizeof(mm_bseq1_t));
@@ -101,12 +111,17 @@ mm_bseq1_t *mm_bseq_read2(mm_bseq_file_t *fp, int chunk_size, int with_qual, int
return a.a;
}
mm_bseq1_t *mm_bseq_read2(mm_bseq_file_t *fp, int chunk_size, int with_qual, int frag_mode, int *n_)
{
return mm_bseq_read3(fp, chunk_size, with_qual, 0, frag_mode, n_);
}
mm_bseq1_t *mm_bseq_read(mm_bseq_file_t *fp, int chunk_size, int with_qual, int *n_)
{
return mm_bseq_read2(fp, chunk_size, with_qual, 0, n_);
}
mm_bseq1_t *mm_bseq_read_frag(int n_fp, mm_bseq_file_t **fp, int chunk_size, int with_qual, int *n_)
mm_bseq1_t *mm_bseq_read_frag2(int n_fp, mm_bseq_file_t **fp, int chunk_size, int with_qual, int with_comment, int *n_)
{
int i;
int64_t size = 0;
@@ -114,15 +129,20 @@ mm_bseq1_t *mm_bseq_read_frag(int n_fp, mm_bseq_file_t **fp, int chunk_size, int
*n_ = 0;
if (n_fp < 1) return 0;
while (1) {
int n_read = 0;
for (i = 0; i < n_fp; ++i)
if (kseq_read(fp[i]->ks) < 0)
break;
if (i != n_fp) break; // some file reaches the end
if (kseq_read(fp[i]->ks) >= 0)
++n_read;
if (n_read < n_fp) {
if (n_read > 0)
fprintf(stderr, "[W::%s]\033[1;31m query files have different number of records; extra records skipped.\033[0m\n", __func__);
break; // some file reaches the end
}
if (a.m == 0) kv_resize(mm_bseq1_t, 0, a, 256);
for (i = 0; i < n_fp; ++i) {
mm_bseq1_t *s;
kv_pushp(mm_bseq1_t, 0, a, &s);
kseq2bseq(fp[i]->ks, s, with_qual);
kseq2bseq(fp[i]->ks, s, with_qual, with_comment);
size += s->l_seq;
}
if (size >= chunk_size) break;
@@ -131,6 +151,11 @@ mm_bseq1_t *mm_bseq_read_frag(int n_fp, mm_bseq_file_t **fp, int chunk_size, int
return a.a;
}
mm_bseq1_t *mm_bseq_read_frag(int n_fp, mm_bseq_file_t **fp, int chunk_size, int with_qual, int *n_)
{
return mm_bseq_read_frag2(n_fp, fp, chunk_size, with_qual, 0, n_);
}
int mm_bseq_eof(mm_bseq_file_t *fp)
{
return (ks_eof(fp->ks->f) && fp->s.seq == 0);
+3 -1
View File
@@ -13,13 +13,15 @@ typedef struct mm_bseq_file_s mm_bseq_file_t;
typedef struct {
int l_seq, rid;
char *name, *seq, *qual;
char *name, *seq, *qual, *comment;
} mm_bseq1_t;
mm_bseq_file_t *mm_bseq_open(const char *fn);
void mm_bseq_close(mm_bseq_file_t *fp);
mm_bseq1_t *mm_bseq_read3(mm_bseq_file_t *fp, int chunk_size, int with_qual, int with_comment, int frag_mode, int *n_);
mm_bseq1_t *mm_bseq_read2(mm_bseq_file_t *fp, int chunk_size, int with_qual, int frag_mode, int *n_);
mm_bseq1_t *mm_bseq_read(mm_bseq_file_t *fp, int chunk_size, int with_qual, int *n_);
mm_bseq1_t *mm_bseq_read_frag2(int n_fp, mm_bseq_file_t **fp, int chunk_size, int with_qual, int with_comment, int *n_);
mm_bseq1_t *mm_bseq_read_frag(int n_fp, mm_bseq_file_t **fp, int chunk_size, int with_qual, int *n_);
int mm_bseq_eof(mm_bseq_file_t *fp);
+1 -1
View File
@@ -44,7 +44,7 @@ mm128_t *mm_chain_dp(int max_dist_x, int max_dist_y, int bw, int max_skip, int m
int32_t qi = (int32_t)a[i].y, q_span = a[i].y>>32&0xff; // NB: only 8 bits of span is used!!!
int32_t max_f = q_span, n_skip = 0, min_d;
int32_t sidi = (a[i].y & MM_SEED_SEG_MASK) >> MM_SEED_SEG_SHIFT;
while (st < i && ri - a[st].x > max_dist_x) ++st;
while (st < i && ri > a[st].x + max_dist_x) ++st;
for (j = i - 1; j >= st; --j) {
int64_t dr = ri - a[j].x;
int32_t dq = qi - (int32_t)a[j].y, dd, sc, log_dd;
+243
View File
@@ -0,0 +1,243 @@
## Table of Contents
- [Introduction & Installation](#intro)
- [Mapping Genomic Reads](#map-reads)
* [Mapping long reads](#map-pb)
* [Mapping Illumina paired-end reads](#map-sr)
* [Evaluating mapping accuracy with simulated reads (for developers)](#mapeval)
- [Mapping Long RNA-seq Reads](#map-rna)
* [Mapping Nanopore 2D cDNA reads](#map-ont-cdna-2d)
* [Mapping Nanopore direct-RNA reads](#map-direct-rna)
* [Mapping PacBio Iso-seq reads](#map-iso-seq)
- [Full-Genome Alignment](#genome-aln)
* [Intra-species assembly alignment](#asm-to-ref)
* [Cross-species full-genome alignment](#x-species)
* [Eyeballing alignment](#view-aln)
* [Calling variants from assembly-to-reference alignment](#asm-var)
* [Constructing self-homology map](#hom-map)
* [Lift Over (for developers)](#liftover)
- [Read Overlap](#read-overlap)
* [Long-read overlap](#long-read-overlap)
* [Evaluating overlap sensitivity (for developers)](#ov-eval)
## <a name="intro"></a>Introduction & Installation
This cookbook walks you through a variety of applications of minimap2 and its
companion script `paftools.js`. All data here are freely available from the
minimap2 release page at version tag [v2.11][v2.11]. Some examples only work
with v2.11 or later.
To acquire the data used in this cookbook and to install minimap2 and paftools,
please follow the command lines below:
```sh
# install minimap2 executables
curl -L https://github.com/lh3/minimap2/releases/download/v2.11/minimap2-2.11_x64-linux.tar.bz2 | tar jxf -
cp minimap2-2.11_x64-linux/{minimap2,k8,paftools.js} . # copy executables
export PATH="$PATH:"`pwd` # put the current directory on PATH
# download example datasets
curl -L https://github.com/lh3/minimap2/releases/download/v2.11/cookbook-data.tgz | tar zxf -
```
## <a name="map-reads"></a>Mapping Genomic Reads
### <a name="map-pb"></a>Mapping long reads
```sh
minimap2 -ax map-pb -t4 ecoli_ref.fa ecoli_p6_25x_canu.fa > mapped.sam
```
Alternatively, you can create a minimap2 index first and then map:
```sh
minimap2 -x map-pb -d ecoli-pb.mmi ecoli_ref.fa # create an index
minimap2 -ax map-pb ecoli-pb.mmi ecoli_p6_25x_canu.fa > mapped.sam
```
This will save you a couple of minutes when you map against the human genome.
**HOWEVER**, key algorithm parameters such as the k-mer length and window
size can't be changed after indexing. Minimap2 will give you a warning if
parameters used in a pre-built index doesn't match parameters on the command
line. **Please always make sure you are using an intended pre-built index.**
### <a name="map-sr"></a>Mapping Illumina paired-end reads:
```sh
minimap2 -ax sr -t4 ecoli_ref.fa ecoli_mason_1.fq ecoli_mason_2.fq > mapped-sr.sam
```
### <a name="mapeval"></a>Evaluating mapping accuracy with simulated reads (for developers)
```sh
minimap2 -ax sr ecoli_ref.fa ecoli_mason_1.fq ecoli_mason_2.fq | paftools.js mapeval -
```
The output is:
```
Q 60 19712 0 0.000000000 19712
Q 0 282 219 0.010953286 19994
U 6
```
where a `U`-line gives the number of unmapped reads (for SAM input only); a
`Q`-line gives:
1. Mapping quality (mapQ) threshold
2. Number of mapped reads between this threshold and the previous mapQ threshold.
3. Number of wrong mappings in the same mapQ interval
4. Accumulative mapping error rate
5. Accumulative number of mappings
For `paftools.js mapeval` to work, you need to encode the true read positions
in read names in the right format. For [PBSIM][pbsim] and [mason2][mason2], we
provide scripts to generate the right format. Simulated reads in this cookbook
were created with the following command lines:
```sh
# in PBSIM source code directory:
src/pbsim ../ecoli_ref.fa --depth 1 --sample-fastq sample/sample.fastq
paftools.js pbsim2fq ../ecoli_ref.fa.fai sd_0001.maf > ../ecoli_pbsim.fa
# mason2 simulation
mason_simulator --illumina-prob-mismatch-scale 2.5 -ir ecoli_ref.fa -n 10000 -o tmp-l.fq -or tmp-r.fq -oa tmp.sam
paftools.js mason2fq tmp.sam | seqtk seq -1 > ecoli_mason_1.fq
paftools.js mason2fq tmp.sam | seqtk seq -2 > ecoli_mason_2.fq
```
## <a name="map-rna"></a>Mapping Long RNA-seq Reads
### <a name="map-ont-cdna-2d"></a>Mapping Nanopore 2D cDNA reads
```sh
minimap2 -ax splice SIRV_E2.fa SIRV_ont-cdna.fa > aln.sam
```
You can compare the alignment to the true annotations with:
```sh
paftools.js junceval SIRV_E2C.gtf aln.sam
```
It gives the percentage of introns found in the annotation. For SIRV data, it
is possible to achieve higher junction accuracy with
```sh
minimap2 -ax splice --splice-flank=no SIRV_E2.fa SIRV_ont-cdna.fa | paftools.js junceval SIRV_E2C.gtf
```
This is because minimap2 models one additional evolutionarily conserved base
around a canonical junction, but SIRV doesn't honor this signal. Option
`--splice-flank=no` asks minimap2 no to model this additional base.
In the output a tag `ts:A:+` indicates that the read strand is the same as the
transcript strand; `ts:A:-` indicates the read strand is opposite to the
transcript strand. This tag is inferred from the GT-AG signal and is thus only
available to spliced reads.
### <a name="map-direct-rna"></a>Mapping Nanopore direct-RNA reads
```sh
minimap2 -ax splice -k14 -uf SIRV_E2.fa SIRV_ont-drna.fa > aln.sam
```
Direct-RNA reads are noisier, so we use a shorter k-mer for improved
sensitivity. Here, option `-uf` forces minimap2 to map reads to the forward
transcript strand only because direct-RNA reads are stranded. Again, applying
`--splice-flank=no` helps junction accuracy for SIRV data.
### <a name="map-iso-seq"></a>Mapping PacBio Iso-seq reads
```sh
minimap2 -ax splice -uf -C5 SIRV_E2.fa SIRV_iso-seq.fq > aln.sam
```
Option `-C5` reduces the penalty on non-canonical splicing sites. It helps
to align such sites correctly for data with low error rate such as Iso-seq
reads and traditional cDNAs. On this example, minimap2 makes one junction
error. Applying `--splice-flank=no` fixes this alignment error.
Note that the command line above is optimized for the final Iso-seq reads.
PacBio's Iso-seq pipeline produces intermediate sequences at varying quality.
For example, some intermediate reads are not stranded. For these reads, option
`-uf` will lead to more errors. Please revise the minimap2 command line
accordingly.
## <a name="genome-aln"></a>Full-Genome Alignment
### <a name="asm-to-ref"></a>Intra-species assembly alignment
```sh
# option "--cs" is recommended as paftools.js may need it
minimap2 -cx asm5 --cs ecoli_ref.fa ecoli_canu.fa > ecoli_canu.paf
```
Here `ecoli_canu.fa` is the Canu assembly of `ecoli_p6_25x_canu.fa`. This
command line outputs alignments in the [PAF format][paf]. Use `-a` instead of
`-c` to get output in the SAM format.
### <a name="x-species"></a>Cross-species full-genome alignment
```sh
minimap2 -cx asm20 --cs ecoli_ref.fa ecoli_O104:H4.fa > ecoli_O104:H4.paf
sort -k6,6 -k8,8n ecoli_O104:H4.paf | paftools.js call -f ecoli_ref.fa -L10000 -l1000 - > out.vcf
```
Minimap2 has three presets for full-genome alignment: "asm5" for sequence
divergence below 1%, "asm10" for divergence around a couple of percent and
"asm20" for divergence not more than 10%. In theory, with the right setting,
minimap2 should work for sequence pairs with sequence divergence up to ~15%,
but this has not been carefully evaluated.
### <a name="view-aln"></a>Eyeballing alignment
```sh
# option "--cs" required; minimap2-r741 or higher required for the "asm20" preset
minimap2 -cx asm20 --cs ecoli_ref.fa ecoli_O104:H4.fa | paftools.js view - | less -S
```
This prints the alignment in a BLAST-like format.
### <a name="asm-var"></a>Calling variants from assembly-to-reference alignment
```sh
# don't forget the "--cs" option; otherwise it doesn't work
minimap2 -cx asm5 --cs ecoli_ref.fa ecoli_canu.fa \
| sort -k6,6 -k8,8n \
| paftools.js call -f ecoli_ref.fa - > out.vcf
```
Without option `-f`, `paftools.js call` outputs in a custom format. In this
format, lines starting with `R` give the regions covered by one contig only.
This information is not available in the VCF output.
### <a name="hom-map"></a>Constructing self-homology map
```sh
minimap2 -DP -k19 -w19 -m200 ecoli_ref.fa ecoli_ref.fa > out.paf
```
Option `-D` asks minimap2 to ignore anchors from perfect self match and `-P`
outputs all chains. For large nomes, we don't recommend to perform base-level
alignment (with `-c`, `-a` or `--cs`) when `-P` is applied. This is because
base-alignment is slow and occasionally gives wrong alignments close to the
diagonal of a dotter plot. For E. coli, though, base-alignment is still fast.
### <a name="liftover"></a>Lift over (for developers)
```sh
minimap2 -cx asm5 --cs ecoli_ref.fa ecoli_canu.fa > ecoli_canu.paf
echo -e 'tig00000001\t200000\t300000' | paftools.js liftover ecoli_canu.paf -
```
This lifts over a region on query sequences to one or multiple regions on
reference sequences. Note that this paftools.js command may not be efficient
enough to lift millions of regions.
## <a name="read-overlap"></a>Read Overlap
### <a name="long-read-overlap"></a>Long read overlap
```sh
# For pacbio reads:
minimap2 -x ava-pb ecoli_p6_25x_canu.fa ecoli_p6_25x_canu.fa > overlap.paf
# For Nanopore reads (ava-ont also works with PacBio but not as good):
minimap2 -x ava-ont -r 10000 ecoli_p6_25x_canu.fa ecoli_p6_25x_canu.fa > overlap.paf
# If you have miniasm installed:
miniasm -f ecoli_p6_25x_canu.fa overlap.paf > asm.gfa
```
Here we explicitly applied `-r 10000`. We are considering to set this as the
default for the `ava-ont` mode as this seems to improve the contiguity for
nanopore read assembly (Loman, personal communication).
*Minimap2 doesn't work well with short-read overlap.*
### <a name="ov-eval"></a>Evaluating overlap sensitivity (for developers)
```sh
# read to reference mapping
minimap2 -cx map-pb ecoli_ref.fa ecoli_p6_25x_canu.fa > to-ref.paf
# evaluate overlap sensitivity
sort -k6,6 -k8,8n to-ref.paf | paftools.js ov-eval - overlap.paf
```
You can see that for PacBio reads, minimap2 achieves higher overlap sensitivity
with `-x ava-pb` (99% vs 93% with `-x ava-ont`).
[pbsim]: https://github.com/pfaucon/PBSIM-PacBio-Simulator
[mason2]: https://github.com/seqan/seqan/tree/master/apps/mason2
[paf]: https://github.com/lh3/miniasm/blob/master/PAF.md
[v2.11]: https://github.com/lh3/minimap2/releases/tag/v2.11
+64
View File
@@ -0,0 +1,64 @@
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "mmpriv.h"
static inline int32_t get_for_qpos(int32_t qlen, const mm128_t *a)
{
int32_t x = (int32_t)a->y;
int32_t q_span = a->y>>32 & 0xff;
if (a->x>>63)
x = qlen - 1 - (x + 1 - q_span); // revert the position to the forward strand of query
return x;
}
static int get_mini_idx(int qlen, const mm128_t *a, int32_t n, const uint64_t *mini_pos)
{
int32_t x, L = 0, R = n - 1;
x = get_for_qpos(qlen, a);
while (L <= R) { // binary search
int32_t m = ((uint64_t)L + R) >> 1;
int32_t y = (int32_t)mini_pos[m];
if (y < x) L = m + 1;
else if (y > x) R = m - 1;
else return m;
}
return -1;
}
void mm_est_err(const mm_idx_t *mi, int qlen, int n_regs, mm_reg1_t *regs, const mm128_t *a, int32_t n, const uint64_t *mini_pos)
{
int i;
uint64_t sum_k = 0;
float avg_k;
if (n == 0) return;
for (i = 0; i < n; ++i)
sum_k += mini_pos[i] >> 32 & 0xff;
avg_k = (float)sum_k / n;
for (i = 0; i < n_regs; ++i) {
mm_reg1_t *r = &regs[i];
int32_t st, en, j, k, n_match, n_tot, l_ref;
r->div = -1.0f;
if (r->cnt == 0) continue;
st = en = get_mini_idx(qlen, r->rev? &a[r->as + r->cnt - 1] : &a[r->as], n, mini_pos);
if (st < 0) {
if (mm_verbose >= 2)
fprintf(stderr, "[WARNING] logic inconsistency in mm_est_err(). Please contact the developer.\n");
continue;
}
l_ref = mi->seq[r->rid].len;
for (k = 1, j = st + 1, n_match = 1; j < n && k < r->cnt; ++j) {
int32_t x;
x = get_for_qpos(qlen, r->rev? &a[r->as + r->cnt - 1 - k] : &a[r->as + k]);
if (x == (int32_t)mini_pos[j])
++k, en = j, ++n_match;
}
n_tot = en - st + 1;
if (r->qs > avg_k && r->rs > avg_k) ++n_tot;
if (qlen - r->qs > avg_k && l_ref - r->re > avg_k) ++n_tot;
r->div = logf((float)n_tot / n_match) / avg_k;
}
}
+85 -33
View File
@@ -118,7 +118,7 @@ void mm_write_sam_hdr(const mm_idx_t *idx, const char *rg, const char *ver, int
if (idx) {
uint32_t i;
for (i = 0; i < idx->n_seq; ++i)
printf("@SQ\tSN:%s\tLN:%d\n", idx->seq[i].name, idx->seq[i].len);
mm_sprintf_lite(&str, "@SQ\tSN:%s\tLN:%d\n", idx->seq[i].name, idx->seq[i].len);
}
if (rg) sam_write_rg_line(&str, rg);
mm_sprintf_lite(&str, "@PG\tID:minimap2\tPN:minimap2");
@@ -129,36 +129,18 @@ void mm_write_sam_hdr(const mm_idx_t *idx, const char *rg, const char *ver, int
for (i = 1; i < argc; ++i)
mm_sprintf_lite(&str, " %s", argv[i]);
}
mm_sprintf_lite(&str, "\n");
fputs(str.s, stdout);
mm_err_puts(str.s);
free(str.s);
}
static void write_cs(void *km, kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r, int no_iden)
static void write_cs_core(kstring_t *s, const uint8_t *tseq, const uint8_t *qseq, const mm_reg1_t *r, char *tmp, int no_iden)
{
extern unsigned char seq_nt4_table[256];
int i, q_off, t_off;
uint8_t *qseq, *tseq;
char *tmp;
if (r->p == 0) return;
mm_sprintf_lite(s, "\tcs:Z:");
qseq = (uint8_t*)kmalloc(km, r->qe - r->qs);
tseq = (uint8_t*)kmalloc(km, r->re - r->rs);
tmp = (char*)kmalloc(km, r->re - r->rs > r->qe - r->qs? r->re - r->rs + 1 : r->qe - r->qs + 1);
mm_idx_getseq(mi, r->rid, r->rs, r->re, tseq);
if (!r->rev) {
for (i = r->qs; i < r->qe; ++i)
qseq[i - r->qs] = seq_nt4_table[(uint8_t)t->seq[i]];
} else {
for (i = r->qs; i < r->qe; ++i) {
uint8_t c = seq_nt4_table[(uint8_t)t->seq[i]];
qseq[r->qe - i - 1] = c >= 4? 4 : 3 - c;
}
}
for (i = q_off = t_off = 0; i < r->p->n_cigar; ++i) {
for (i = q_off = t_off = 0; i < (int)r->p->n_cigar; ++i) {
int j, op = r->p->cigar[i]&0xf, len = r->p->cigar[i]>>4;
assert(op >= 0 && op <= 3);
if (op == 0) {
if (op == 0) { // match
int l_tmp = 0;
for (j = 0; j < len; ++j) {
if (qseq[q_off + j] != tseq[t_off + j]) {
@@ -179,17 +161,17 @@ static void write_cs(void *km, kstring_t *s, const mm_idx_t *mi, const mm_bseq1_
} else mm_sprintf_lite(s, ":%d", l_tmp);
}
q_off += len, t_off += len;
} else if (op == 1) {
} else if (op == 1) { // insertion to ref
for (j = 0, tmp[len] = 0; j < len; ++j)
tmp[j] = "acgtn"[qseq[q_off + j]];
mm_sprintf_lite(s, "+%s", tmp);
q_off += len;
} else if (op == 2) {
} else if (op == 2) { // deletion from ref
for (j = 0, tmp[len] = 0; j < len; ++j)
tmp[j] = "acgtn"[tseq[t_off + j]];
mm_sprintf_lite(s, "-%s", tmp);
t_off += len;
} else {
} else { // intron
assert(len >= 2);
mm_sprintf_lite(s, "~%c%c%d%c%c", "acgtn"[tseq[t_off]], "acgtn"[tseq[t_off+1]],
len, "acgtn"[tseq[t_off+len-2]], "acgtn"[tseq[t_off+len-1]]);
@@ -197,12 +179,67 @@ static void write_cs(void *km, kstring_t *s, const mm_idx_t *mi, const mm_bseq1_
}
}
assert(t_off == r->re - r->rs && q_off == r->qe - r->qs);
}
static void write_MD_core(kstring_t *s, const uint8_t *tseq, const uint8_t *qseq, const mm_reg1_t *r, char *tmp)
{
int i, q_off, t_off, l_MD = 0;
mm_sprintf_lite(s, "\tMD:Z:");
for (i = q_off = t_off = 0; i < (int)r->p->n_cigar; ++i) {
int j, op = r->p->cigar[i]&0xf, len = r->p->cigar[i]>>4;
assert(op >= 0 && op <= 2); // introns (aka reference skips) are not supported
if (op == 0) { // match
for (j = 0; j < len; ++j) {
if (qseq[q_off + j] != tseq[t_off + j]) {
mm_sprintf_lite(s, "%d%c", l_MD, "ACGTN"[tseq[t_off + j]]);
l_MD = 0;
} else ++l_MD;
}
q_off += len, t_off += len;
} else if (op == 1) { // insertion to ref
q_off += len;
} else if (op == 2) { // deletion from ref
for (j = 0, tmp[len] = 0; j < len; ++j)
tmp[j] = "ACGTN"[tseq[t_off + j]];
mm_sprintf_lite(s, "%d^%s", l_MD, tmp);
l_MD = 0;
t_off += len;
}
}
if (l_MD > 0) mm_sprintf_lite(s, "%d", l_MD);
assert(t_off == r->re - r->rs && q_off == r->qe - r->qs);
}
static void write_cs_or_MD(void *km, kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r, int no_iden, int is_MD)
{
extern unsigned char seq_nt4_table[256];
int i;
uint8_t *qseq, *tseq;
char *tmp;
if (r->p == 0) return;
qseq = (uint8_t*)kmalloc(km, r->qe - r->qs);
tseq = (uint8_t*)kmalloc(km, r->re - r->rs);
tmp = (char*)kmalloc(km, r->re - r->rs > r->qe - r->qs? r->re - r->rs + 1 : r->qe - r->qs + 1);
mm_idx_getseq(mi, r->rid, r->rs, r->re, tseq);
if (!r->rev) {
for (i = r->qs; i < r->qe; ++i)
qseq[i - r->qs] = seq_nt4_table[(uint8_t)t->seq[i]];
} else {
for (i = r->qs; i < r->qe; ++i) {
uint8_t c = seq_nt4_table[(uint8_t)t->seq[i]];
qseq[r->qe - i - 1] = c >= 4? 4 : 3 - c;
}
}
if (is_MD) write_MD_core(s, tseq, qseq, r, tmp);
else write_cs_core(s, tseq, qseq, r, tmp, no_iden);
kfree(km, qseq); kfree(km, tseq); kfree(km, tmp);
}
static inline void write_tags(kstring_t *s, const mm_reg1_t *r)
{
int type = r->inv? 'I' : r->id == r->parent? 'P' : 'S';
int type;
if (r->id == r->parent) type = r->inv? 'I' : 'P';
else type = r->inv? 'i' : 'S';
if (r->p) {
mm_sprintf_lite(s, "\tNM:i:%d\tms:i:%d\tAS:i:%d\tnn:i:%d", r->blen - r->mlen + r->p->n_ambi, r->p->dp_max, r->p->dp_score, r->p->n_ambi);
if (r->p->trans_strand == 1 || r->p->trans_strand == 2)
@@ -210,12 +247,22 @@ static inline void write_tags(kstring_t *s, const mm_reg1_t *r)
}
mm_sprintf_lite(s, "\ttp:A:%c\tcm:i:%d\ts1:i:%d", type, r->cnt, r->score);
if (r->parent == r->id) mm_sprintf_lite(s, "\ts2:i:%d", r->subsc);
if (r->div >= 0.0f && r->div <= 1.0f) {
char buf[8];
if (r->div == 0.0f) buf[0] = '0', buf[1] = 0;
else sprintf(buf, "%.4f", r->div);
mm_sprintf_lite(s, "\tdv:f:%s", buf);
}
if (r->split) mm_sprintf_lite(s, "\tzd:i:%d", r->split);
}
void mm_write_paf(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r, void *km, int opt_flag)
{
s->l = 0;
if (r == 0) {
mm_sprintf_lite(s, "%s\t%d", t->name, t->l_seq);
return;
}
mm_sprintf_lite(s, "%s\t%d\t%d\t%d\t%c\t", t->name, t->l_seq, r->qs, r->qe, "+-"[r->rev]);
if (mi->seq[r->rid].name) mm_sprintf_lite(s, "%s", mi->seq[r->rid].name);
else mm_sprintf_lite(s, "%d", r->rid);
@@ -227,10 +274,12 @@ void mm_write_paf(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const m
uint32_t k;
mm_sprintf_lite(s, "\tcg:Z:");
for (k = 0; k < r->p->n_cigar; ++k)
mm_sprintf_lite(s, "%d%c", r->p->cigar[k]>>4, "MIDN"[r->p->cigar[k]&0xf]);
mm_sprintf_lite(s, "%d%c", r->p->cigar[k]>>4, "MIDNSHP=XB"[r->p->cigar[k]&0xf]);
}
if (r->p && (opt_flag & MM_F_OUT_CS))
write_cs(km, s, mi, t, r, !(opt_flag&MM_F_OUT_CS_LONG));
if (r->p && (opt_flag & (MM_F_OUT_CS|MM_F_OUT_MD)))
write_cs_or_MD(km, s, mi, t, r, !(opt_flag&MM_F_OUT_CS_LONG), opt_flag&MM_F_OUT_MD);
if ((opt_flag & MM_F_COPY_COMMENT) && t->comment)
mm_sprintf_lite(s, "\t%s", t->comment);
}
static void sam_write_sq(kstring_t *s, char *seq, int l, int rev, int comp)
@@ -276,7 +325,7 @@ static void write_sam_cigar(kstring_t *s, int sam_flag, int in_tag, int qlen, co
int clip_char = (sam_flag&0x800) && !(opt_flag&MM_F_SOFTCLIP)? 'H' : 'S';
if (clip_len[0]) mm_sprintf_lite(s, "%d%c", clip_len[0], clip_char);
for (k = 0; k < r->p->n_cigar; ++k)
mm_sprintf_lite(s, "%d%c", r->p->cigar[k]>>4, "MIDN"[r->p->cigar[k]&0xf]);
mm_sprintf_lite(s, "%d%c", r->p->cigar[k]>>4, "MIDNSHP=XB"[r->p->cigar[k]&0xf]);
if (clip_len[1]) mm_sprintf_lite(s, "%d%c", clip_len[1], clip_char);
}
}
@@ -426,12 +475,15 @@ void mm_write_sam2(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, int se
}
}
}
if (r->p && (opt_flag & MM_F_OUT_CS))
write_cs(km, s, mi, t, r, !(opt_flag&MM_F_OUT_CS_LONG));
if (r->p && (opt_flag & (MM_F_OUT_CS|MM_F_OUT_MD)))
write_cs_or_MD(km, s, mi, t, r, !(opt_flag&MM_F_OUT_CS_LONG), opt_flag&MM_F_OUT_MD);
if (cigar_in_tag)
write_sam_cigar(s, flag, 1, t->l_seq, r, opt_flag);
}
if ((opt_flag & MM_F_COPY_COMMENT) && t->comment)
mm_sprintf_lite(s, "\t%s", t->comment);
s->s[s->l] = 0; // we always have room for an extra byte (see str_enlarge)
}
+57 -18
View File
@@ -80,6 +80,7 @@ mm_reg1_t *mm_gen_regs(void *km, uint32_t hash, int qlen, int n_u, uint64_t *u,
ri->hash = (uint32_t)z[i].x;
ri->cnt = (int32_t)z[i].y;
ri->as = z[i].y >> 32;
ri->div = -1.0f;
mm_reg_set_coor(ri, qlen, a);
}
kfree(km, z);
@@ -93,6 +94,7 @@ void mm_split_reg(mm_reg1_t *r, mm_reg1_t *r2, int n, int qlen, mm128_t *a)
r2->id = -1;
r2->sam_pri = 0;
r2->p = 0;
r2->split_inv = 0;
r2->cnt = r->cnt - n;
r2->score = (int32_t)(r->score * ((float)r2->cnt / r->cnt) + .499);
r2->as = r->as + n;
@@ -130,7 +132,7 @@ void mm_set_parent(void *km, float mask_level, int n, mm_reg1_t *r, int sub_diff
int j, x = si;
radix_sort_64(cov, cov + n_cov);
for (j = 0; j < n_cov; ++j) {
if (cov[j]>>32 > x) uncov_len += (cov[j]>>32) - x;
if ((int)(cov[j]>>32) > x) uncov_len += (cov[j]>>32) - x;
x = (int32_t)cov[j] > x? (int32_t)cov[j] : x;
}
if (ei > x) uncov_len += ei - x;
@@ -141,13 +143,13 @@ void mm_set_parent(void *km, float mask_level, int n, mm_reg1_t *r, int sub_diff
if (ej <= si || sj >= ei) continue; // no overlap
min = ej - sj < ei - si? ej - sj : ei - si;
max = ej - sj > ei - si? ej - sj : ei - si;
ol = si < sj? (ei < sj? 0 : ei < ej? ei - sj : ej - sj) : (ej < si? 0 : ej < ei? ej - si : ei - si); // overlap length
ol = si < sj? (ei < sj? 0 : ei < ej? ei - sj : ej - sj) : (ej < si? 0 : ej < ei? ej - si : ei - si); // overlap length; TODO: this can be simplified
if ((float)ol / min - (float)uncov_len / max > mask_level) {
int cnt_sub = 0;
ri->parent = rp->parent;
rp->subsc = rp->subsc > ri->score? rp->subsc : ri->score;
if (ri->cnt >= rp->cnt) cnt_sub = 1;
if (rp->p && ri->p && (rp->rs != ri->rs || rp->re != ri->re || ol != min)) { // the last condition excludes identical hits after DP
if (rp->p && ri->p && (rp->rid != ri->rid || rp->rs != ri->rs || rp->re != ri->re || ol != min)) { // the last condition excludes identical hits after DP
rp->p->dp_max2 = rp->p->dp_max2 > ri->p->dp_max? rp->p->dp_max2 : ri->p->dp_max;
if (rp->p->dp_max - ri->p->dp_max <= sub_diff) cnt_sub = 1;
}
@@ -162,9 +164,9 @@ set_parent_test:
kfree(km, w);
}
void mm_hit_sort_by_dp(void *km, int *n_regs, mm_reg1_t *r)
void mm_hit_sort(void *km, int *n_regs, mm_reg1_t *r)
{
int32_t i, n_aux, n = *n_regs;
int32_t i, n_aux, n = *n_regs, has_cigar = 0, no_cigar = 0;
mm128_t *aux;
mm_reg1_t *t;
@@ -173,14 +175,20 @@ void mm_hit_sort_by_dp(void *km, int *n_regs, mm_reg1_t *r)
t = (mm_reg1_t*)kmalloc(km, n * sizeof(mm_reg1_t));
for (i = n_aux = 0; i < n; ++i) {
if (r[i].inv || r[i].cnt > 0) { // squeeze out elements with cnt==0 (soft deleted)
assert(r[i].p);
aux[n_aux].x = (uint64_t)r[i].p->dp_max << 32 | r[i].hash;
if (r[i].p) {
aux[n_aux].x = (uint64_t)r[i].p->dp_max << 32 | r[i].hash;
has_cigar = 1;
} else {
aux[n_aux].x = (uint64_t)r[i].score << 32 | r[i].hash;
no_cigar = 1;
}
aux[n_aux++].y = i;
} else if (r[i].p) {
free(r[i].p);
r[i].p = 0;
}
}
assert(has_cigar + no_cigar == 1);
radix_sort_128x(aux, aux + n_aux);
for (i = n_aux - 1; i >= 0; --i)
t[n_aux - 1 - i] = r[aux[i].y];
@@ -234,7 +242,7 @@ void mm_select_sub(void *km, float pri_ratio, int min_diff, int best_n, int *n_,
if (p == i || r[i].inv) { // primary or inversion
r[k++] = r[i];
} else if ((r[i].score >= r[p].score * pri_ratio || r[i].score + min_diff >= r[p].score) && n_2nd < best_n) {
if (!(r[i].qs == r[p].qs && r[i].qe == r[p].qe && r[i].rs == r[p].rs && r[i].re == r[p].re)) // not identical hits
if (!(r[i].qs == r[p].qs && r[i].qe == r[p].qe && r[i].rid == r[p].rid && r[i].rs == r[p].rs && r[i].re == r[p].re)) // not identical hits
r[k++] = r[i], ++n_2nd;
else if (r[i].p) free(r[i].p);
} else if (r[i].p) free(r[i].p);
@@ -244,16 +252,17 @@ void mm_select_sub(void *km, float pri_ratio, int min_diff, int best_n, int *n_,
}
}
void mm_filter_regs(void *km, const mm_mapopt_t *opt, int *n_regs, mm_reg1_t *regs)
void mm_filter_regs(const mm_mapopt_t *opt, int qlen, int *n_regs, mm_reg1_t *regs)
{ // NB: after this call, mm_reg1_t::parent can be -1 if its parent filtered out
int i, k;
for (i = k = 0; i < *n_regs; ++i) {
mm_reg1_t *r = &regs[i];
int flt = 0;
if (!r->inv && !r->seg_split && r->cnt < opt->min_cnt) flt = 1;
if (r->p) {
if (r->p) { // these filters are only applied when base-alignment is available
if (r->mlen < opt->min_chain_score) flt = 1;
else if (r->p->dp_max < opt->min_dp_max) flt = 1;
else if (r->qs > qlen * opt->max_clip_ratio && qlen - r->qe > qlen * opt->max_clip_ratio) flt = 1;
if (flt) free(r->p);
}
if (!flt) {
@@ -301,7 +310,7 @@ void mm_join_long(void *km, const mm_mapopt_t *opt, int qlen, int *n_regs_, mm_r
for (i = n_aux - 1; i >= 1; --i) {
mm_reg1_t *r0 = &regs[(int32_t)aux[i-1]], *r1 = &regs[(int32_t)aux[i]];
mm128_t *a0e, *a1s;
int max_gap, min_gap, sc_thres;
int max_gap, min_gap, sc_thres, min_flank_len;
// test
if (r0->as + r0->cnt != r1->as) continue; // not adjacent in a[]
@@ -310,13 +319,14 @@ void mm_join_long(void *km, const mm_mapopt_t *opt, int qlen, int *n_regs_, mm_r
a1s = &a[r1->as];
if (a1s->x <= a0e->x || (int32_t)a1s->y <= (int32_t)a0e->y) continue; // keep colinearity
max_gap = min_gap = (int32_t)a1s->y - (int32_t)a0e->y;
max_gap = max_gap > a1s->x - a0e->x? max_gap : a1s->x - a0e->x;
min_gap = min_gap < a1s->x - a0e->x? min_gap : a1s->x - a0e->x;
max_gap = a0e->x + max_gap > a1s->x? max_gap : a1s->x - a0e->x;
min_gap = a0e->x + min_gap < a1s->x? min_gap : a1s->x - a0e->x;
if (max_gap > opt->max_join_long || min_gap > opt->max_join_short) continue;
sc_thres = (int)((float)opt->min_join_flank_sc / opt->max_join_long * max_gap + .499);
if (r0->score < sc_thres || r1->score < sc_thres) continue; // require good flanking chains
if (r0->re - r0->rs < max_gap>>1 || r0->qe - r0->qs < max_gap>>1) continue; // require enough flanking length
if (r1->re - r1->rs < max_gap>>1 || r1->qe - r1->qs < max_gap>>1) continue;
min_flank_len = (int)(max_gap * opt->min_join_flank_ratio);
if (r0->re - r0->rs < min_flank_len || r0->qe - r0->qs < min_flank_len) continue; // require enough flanking length
if (r1->re - r1->rs < min_flank_len || r1->qe - r1->qs < min_flank_len) continue;
// all conditions satisfied; join
a[r1->as].y |= MM_SEED_LONG_JOIN;
@@ -336,7 +346,7 @@ void mm_join_long(void *km, const mm_mapopt_t *opt, int qlen, int *n_regs_, mm_r
r->parent = regs[r->parent].parent;
}
}
mm_filter_regs(km, opt, n_regs_, regs);
mm_filter_regs(opt, qlen, n_regs_, regs);
mm_sync_regs(km, *n_regs_, regs);
}
}
@@ -389,8 +399,10 @@ mm_seg_t *mm_seg_gen(void *km, uint32_t hash, int n_segs, const int *qlens, int
for (s = 0; s < n_segs; ++s) {
regs[s] = mm_gen_regs(km, hash, qlens[s], seg[s].n_u, seg[s].u, seg[s].a);
n_regs[s] = seg[s].n_u;
for (i = 0; i < n_regs[s]; ++i)
for (i = 0; i < n_regs[s]; ++i) {
regs[s][i].seg_split = 1;
regs[s][i].seg_id = s;
}
}
return seg;
}
@@ -403,7 +415,33 @@ void mm_seg_free(void *km, int n_segs, mm_seg_t *segs)
kfree(km, segs);
}
void mm_set_mapq(int n_regs, mm_reg1_t *regs, int min_chain_sc, int match_sc, int rep_len, int is_sr)
static void mm_set_inv_mapq(void *km, int n_regs, mm_reg1_t *regs)
{
int i, n_aux;
mm128_t *aux;
if (n_regs < 3) return;
for (i = 0; i < n_regs; ++i)
if (regs[i].inv) break;
if (i == n_regs) return; // no inversion hits
aux = (mm128_t*)kmalloc(km, n_regs * 16);
for (i = n_aux = 0; i < n_regs; ++i)
if (regs[i].parent == i || regs[i].parent < 0)
aux[n_aux].y = i, aux[n_aux++].x = (uint64_t)regs[i].rid << 32 | regs[i].rs;
radix_sort_128x(aux, aux + n_aux);
for (i = 1; i < n_aux - 1; ++i) {
mm_reg1_t *inv = &regs[aux[i].y];
if (inv->inv) {
mm_reg1_t *l = &regs[aux[i-1].y];
mm_reg1_t *r = &regs[aux[i+1].y];
inv->mapq = l->mapq < r->mapq? l->mapq : r->mapq;
}
}
kfree(km, aux);
}
void mm_set_mapq(void *km, int n_regs, mm_reg1_t *regs, int min_chain_sc, int match_sc, int rep_len, int is_sr)
{
static const float q_coef = 40.0f;
int64_t sum_sc = 0;
@@ -446,4 +484,5 @@ void mm_set_mapq(int n_regs, mm_reg1_t *regs, int min_chain_sc, int match_sc, in
if (r->p && r->p->dp_max > r->p->dp_max2 && r->mapq == 0) r->mapq = 1;
} else r->mapq = 0;
}
mm_set_inv_mapq(km, n_regs, regs);
}
+145 -62
View File
@@ -7,6 +7,7 @@
#endif
#include <fcntl.h>
#include <stdio.h>
#define __STDC_LIMIT_MACROS
#include "kthread.h"
#include "bseq.h"
#include "minimap.h"
@@ -19,6 +20,8 @@
KHASH_INIT(idx, uint64_t, uint64_t, 1, idx_hash, idx_eq)
typedef khash_t(idx) idxhash_t;
KHASH_MAP_INIT_STR(str, uint32_t)
#define kroundup64(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, (x)|=(x)>>32, ++(x))
typedef struct mm_idx_bucket_s {
@@ -28,22 +31,13 @@ typedef struct mm_idx_bucket_s {
void *h; // hash table indexing _p_ and minimizers appearing once
} mm_idx_bucket_t;
void mm_idxopt_init(mm_idxopt_t *opt)
{
memset(opt, 0, sizeof(mm_idxopt_t));
opt->k = 15, opt->w = 10, opt->is_hpc = 0;
opt->bucket_bits = 14;
opt->mini_batch_size = 50000000;
opt->batch_size = 4000000000ULL;
}
mm_idx_t *mm_idx_init(int w, int k, int b, int is_hpc)
mm_idx_t *mm_idx_init(int w, int k, int b, int flag)
{
mm_idx_t *mi;
if (k*2 < b) b = k * 2;
if (w < 1) w = 1;
mi = (mm_idx_t*)calloc(1, sizeof(mm_idx_t));
mi->w = w, mi->k = k, mi->b = b, mi->is_hpc = is_hpc;
mi->w = w, mi->k = k, mi->b = b, mi->flag = flag;
mi->B = (mm_idx_bucket_t*)calloc(1<<b, sizeof(mm_idx_bucket_t));
if (!(mm_dbg_flag & 1)) mi->km = km_init();
return mi;
@@ -51,12 +45,15 @@ mm_idx_t *mm_idx_init(int w, int k, int b, int is_hpc)
void mm_idx_destroy(mm_idx_t *mi)
{
int i;
uint32_t i;
if (mi == 0) return;
for (i = 0; i < 1<<mi->b; ++i) {
free(mi->B[i].p);
free(mi->B[i].a.a);
kh_destroy(idx, (idxhash_t*)mi->B[i].h);
if (mi->h) kh_destroy(str, (khash_t(str)*)mi->h);
if (mi->B) {
for (i = 0; i < 1U<<mi->b; ++i) {
free(mi->B[i].p);
free(mi->B[i].a.a);
kh_destroy(idx, (idxhash_t*)mi->B[i].h);
}
}
if (!mi->km) {
for (i = 0; i < mi->n_seq; ++i)
@@ -87,14 +84,15 @@ const uint64_t *mm_idx_get(const mm_idx_t *mi, uint64_t minier, int *n)
void mm_idx_stat(const mm_idx_t *mi)
{
int i, n = 0, n1 = 0;
int n = 0, n1 = 0;
uint32_t i;
uint64_t sum = 0, len = 0;
fprintf(stderr, "[M::%s] kmer size: %d; skip: %d; is_HPC: %d; #seq: %d\n", __func__, mi->k, mi->w, mi->is_hpc, mi->n_seq);
fprintf(stderr, "[M::%s] kmer size: %d; skip: %d; is_hpc: %d; #seq: %d\n", __func__, mi->k, mi->w, mi->flag&MM_I_HPC, mi->n_seq);
for (i = 0; i < mi->n_seq; ++i)
len += mi->seq[i].len;
for (i = 0; i < 1<<mi->b; ++i)
for (i = 0; i < 1U<<mi->b; ++i)
if (mi->B[i].h) n += kh_size((idxhash_t*)mi->B[i].h);
for (i = 0; i < 1<<mi->b; ++i) {
for (i = 0; i < 1U<<mi->b; ++i) {
idxhash_t *h = (idxhash_t*)mi->B[i].h;
khint_t k;
if (h == 0) continue;
@@ -108,6 +106,34 @@ void mm_idx_stat(const mm_idx_t *mi)
__func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0), n, 100.0*n1/n, (double)sum / n, (double)len / sum);
}
int mm_idx_index_name(mm_idx_t *mi)
{
khash_t(str) *h;
uint32_t i;
int has_dup = 0, absent;
if (mi->h) return 0;
h = kh_init(str);
for (i = 0; i < mi->n_seq; ++i) {
khint_t k;
k = kh_put(str, h, mi->seq[i].name, &absent);
if (absent) kh_val(h, k) = i;
else has_dup = 1;
}
mi->h = h;
if (has_dup && mm_verbose >= 2)
fprintf(stderr, "[WARNING] some database sequences have identical sequence names\n");
return has_dup;
}
int mm_idx_name2id(const mm_idx_t *mi, const char *name)
{
khash_t(str) *h = (khash_t(str)*)mi->h;
khint_t k;
if (h == 0) return -2;
k = kh_get(str, h, name);
return k == kh_end(h)? -1 : kh_val(h, k);
}
int mm_idx_getseq(const mm_idx_t *mi, uint32_t rid, uint32_t st, uint32_t en, uint8_t *seq)
{
uint64_t i, st1, en1;
@@ -149,7 +175,8 @@ int32_t mm_idx_cal_max_occ(const mm_idx_t *mi, float f)
static void worker_post(void *g, long i, int tid)
{
int j, start_a, start_p, n, n_keys;
int n, n_keys;
size_t j, start_a, start_p;
idxhash_t *h;
mm_idx_t *mi = (mm_idx_t*)g;
mm_idx_bucket_t *b = &mi->B[i];
@@ -177,7 +204,7 @@ static void worker_post(void *g, long i, int tid)
int absent;
mm128_t *p = &b->a.a[j-1];
itr = kh_put(idx, h, p->x>>8>>mi->b<<1, &absent);
assert(absent && j - start_a == n);
assert(absent && j == start_a + n);
if (n == 1) {
kh_key(h, itr) |= 1;
kh_val(h, itr) = p->y;
@@ -193,7 +220,7 @@ static void worker_post(void *g, long i, int tid)
} else ++n;
}
b->h = h;
assert(b->n == start_p);
assert(b->n == (int32_t)start_p);
// deallocate and clear b->a
kfree(0, b->a.a);
@@ -214,7 +241,7 @@ static void mm_idx_post(mm_idx_t *mi, int n_threads)
#include "bseq.h"
typedef struct {
int mini_batch_size, keep_name;
int mini_batch_size;
uint64_t batch_size, sum_len;
mm_bseq_file_t *fp;
mm_idx_t *mi;
@@ -246,7 +273,6 @@ static void *worker_pipeline(void *shared, int step, void *in)
s->seq = mm_bseq_read(p->fp, p->mini_batch_size, 0, &s->n_seq); // read a mini-batch
if (s->seq) {
uint32_t old_m, m;
uint64_t sum_len, old_max_len, max_len;
assert((uint64_t)p->mi->n_seq + s->n_seq <= UINT32_MAX); // to prevent integer overflow
// make room for p->mi->seq
old_m = p->mi->n_seq, m = p->mi->n_seq + s->n_seq;
@@ -254,29 +280,34 @@ static void *worker_pipeline(void *shared, int step, void *in)
if (old_m != m)
p->mi->seq = (mm_idx_seq_t*)krealloc(p->mi->km, p->mi->seq, m * sizeof(mm_idx_seq_t));
// make room for p->mi->S
for (i = 0, sum_len = 0; i < s->n_seq; ++i) sum_len += s->seq[i].l_seq;
old_max_len = (p->sum_len + 7) / 8;
max_len = (p->sum_len + sum_len + 7) / 8;
kroundup64(old_max_len); kroundup64(max_len);
if (old_max_len != max_len) {
p->mi->S = (uint32_t*)realloc(p->mi->S, max_len * 4);
memset(&p->mi->S[old_max_len], 0, 4 * (max_len - old_max_len));
if (!(p->mi->flag & MM_I_NO_SEQ)) {
uint64_t sum_len, old_max_len, max_len;
for (i = 0, sum_len = 0; i < s->n_seq; ++i) sum_len += s->seq[i].l_seq;
old_max_len = (p->sum_len + 7) / 8;
max_len = (p->sum_len + sum_len + 7) / 8;
kroundup64(old_max_len); kroundup64(max_len);
if (old_max_len != max_len) {
p->mi->S = (uint32_t*)realloc(p->mi->S, max_len * 4);
memset(&p->mi->S[old_max_len], 0, 4 * (max_len - old_max_len));
}
}
// populate p->mi->seq
for (i = 0; i < s->n_seq; ++i) {
mm_idx_seq_t *seq = &p->mi->seq[p->mi->n_seq];
uint32_t j;
if (p->keep_name) {
if (!(p->mi->flag & MM_I_NO_NAME)) {
seq->name = (char*)kmalloc(p->mi->km, strlen(s->seq[i].name) + 1);
strcpy(seq->name, s->seq[i].name);
} else seq->name = 0;
seq->len = s->seq[i].l_seq;
seq->offset = p->sum_len;
// copy the sequence
for (j = 0; j < seq->len; ++j) { // TODO: this is not the fastest way, but let's first see if speed matters here
uint64_t o = p->sum_len + j;
int c = seq_nt4_table[(uint8_t)s->seq[i].seq[j]];
mm_seq4_set(p->mi->S, o, c);
if (!(p->mi->flag & MM_I_NO_SEQ)) {
for (j = 0; j < seq->len; ++j) { // TODO: this is not the fastest way, but let's first see if speed matters here
uint64_t o = p->sum_len + j;
int c = seq_nt4_table[(uint8_t)s->seq[i].seq[j]];
mm_seq4_set(p->mi->S, o, c);
}
}
// update p->sum_len and p->mi->n_seq
p->sum_len += seq->len;
@@ -288,7 +319,10 @@ static void *worker_pipeline(void *shared, int step, void *in)
step_t *s = (step_t*)in;
for (i = 0; i < s->n_seq; ++i) {
mm_bseq1_t *t = &s->seq[i];
mm_sketch(0, t->seq, t->l_seq, p->mi->w, p->mi->k, t->rid, p->mi->is_hpc, &s->a);
if (t->l_seq > 0)
mm_sketch(0, t->seq, t->l_seq, p->mi->w, p->mi->k, t->rid, p->mi->flag&MM_I_HPC, &s->a);
else if (mm_verbose >= 2)
fprintf(stderr, "[WARNING] the length database sequence '%s' is 0\n", t->name);
free(t->seq); free(t->name);
}
free(s->seq); s->seq = 0;
@@ -301,16 +335,15 @@ static void *worker_pipeline(void *shared, int step, void *in)
return 0;
}
mm_idx_t *mm_idx_gen(mm_bseq_file_t *fp, int w, int k, int b, int is_hpc, int mini_batch_size, int n_threads, uint64_t batch_size, int keep_name)
mm_idx_t *mm_idx_gen(mm_bseq_file_t *fp, int w, int k, int b, int flag, int mini_batch_size, int n_threads, uint64_t batch_size)
{
pipeline_t pl;
if (fp == 0 || mm_bseq_eof(fp)) return 0;
memset(&pl, 0, sizeof(pipeline_t));
pl.mini_batch_size = mini_batch_size < batch_size? mini_batch_size : batch_size;
pl.keep_name = keep_name;
pl.mini_batch_size = (uint64_t)mini_batch_size < batch_size? mini_batch_size : batch_size;
pl.batch_size = batch_size;
pl.fp = fp;
pl.mi = mm_idx_init(w, k, b, is_hpc);
pl.mi = mm_idx_init(w, k, b, flag);
kt_pipeline(n_threads < 3? n_threads : 3, worker_pipeline, &pl, 3);
if (mm_verbose >= 3)
@@ -323,17 +356,60 @@ mm_idx_t *mm_idx_gen(mm_bseq_file_t *fp, int w, int k, int b, int is_hpc, int mi
return pl.mi;
}
mm_idx_t *mm_idx_build(const char *fn, int w, int k, int is_hpc, int n_threads) // a simpler interface
mm_idx_t *mm_idx_build(const char *fn, int w, int k, int flag, int n_threads) // a simpler interface; deprecated
{
mm_bseq_file_t *fp;
mm_idx_t *mi;
fp = mm_bseq_open(fn);
if (fp == 0) return 0;
mi = mm_idx_gen(fp, w, k, 14, is_hpc, 1<<18, n_threads, UINT64_MAX, 1);
mi = mm_idx_gen(fp, w, k, 14, flag, 1<<18, n_threads, UINT64_MAX);
mm_bseq_close(fp);
return mi;
}
mm_idx_t *mm_idx_str(int w, int k, int is_hpc, int bucket_bits, int n, const char **seq, const char **name)
{
uint64_t sum_len = 0;
mm128_v a = {0,0,0};
mm_idx_t *mi;
int i, flag = 0;
if (n <= 0) return 0;
for (i = 0; i < n; ++i) // get the total length
sum_len += strlen(seq[i]);
if (is_hpc) flag |= MM_I_HPC;
if (name == 0) flag |= MM_I_NO_NAME;
if (bucket_bits < 0) bucket_bits = 14;
mi = mm_idx_init(w, k, bucket_bits, flag);
mi->n_seq = n;
mi->seq = (mm_idx_seq_t*)kcalloc(mi->km, n, sizeof(mm_idx_seq_t)); // ->seq is allocated from km
mi->S = (uint32_t*)calloc((sum_len + 7) / 8, 4);
for (i = 0, sum_len = 0; i < n; ++i) {
const char *s = seq[i];
mm_idx_seq_t *p = &mi->seq[i];
uint32_t j;
if (name && name[i]) {
p->name = (char*)kmalloc(mi->km, strlen(name[i]) + 1);
strcpy(p->name, name[i]);
}
p->offset = sum_len;
p->len = strlen(s);
for (j = 0; j < p->len; ++j) {
int c = seq_nt4_table[(uint8_t)s[j]];
uint64_t o = sum_len + j;
mm_seq4_set(mi->S, o, c);
}
sum_len += p->len;
if (p->len > 0) {
a.n = 0;
mm_sketch(0, s, p->len, w, k, i, is_hpc, &a);
mm_idx_add(mi, a.n, a.a);
}
}
free(a.a);
mm_idx_post(mi, 1);
return mi;
}
/*************
* index I/O *
*************/
@@ -341,17 +417,20 @@ mm_idx_t *mm_idx_build(const char *fn, int w, int k, int is_hpc, int n_threads)
void mm_idx_dump(FILE *fp, const mm_idx_t *mi)
{
uint64_t sum_len = 0;
uint32_t x[5];
int i;
uint32_t x[5], i;
x[0] = mi->w, x[1] = mi->k, x[2] = mi->b, x[3] = mi->n_seq, x[4] = mi->is_hpc;
x[0] = mi->w, x[1] = mi->k, x[2] = mi->b, x[3] = mi->n_seq, x[4] = mi->flag;
fwrite(MM_IDX_MAGIC, 1, 4, fp);
fwrite(x, 4, 5, fp);
for (i = 0; i < mi->n_seq; ++i) {
uint8_t l;
l = strlen(mi->seq[i].name);
fwrite(&l, 1, 1, fp);
fwrite(mi->seq[i].name, 1, l, fp);
if (mi->seq[i].name) {
uint8_t l = strlen(mi->seq[i].name);
fwrite(&l, 1, 1, fp);
fwrite(mi->seq[i].name, 1, l, fp);
} else {
uint8_t l = 0;
fwrite(&l, 1, 1, fp);
}
fwrite(&mi->seq[i].len, 4, 1, fp);
sum_len += mi->seq[i].len;
}
@@ -371,15 +450,15 @@ void mm_idx_dump(FILE *fp, const mm_idx_t *mi)
fwrite(x, 8, 2, fp);
}
}
fwrite(mi->S, 4, (sum_len + 7) / 8, fp);
if (!(mi->flag & MM_I_NO_SEQ))
fwrite(mi->S, 4, (sum_len + 7) / 8, fp);
fflush(fp);
}
mm_idx_t *mm_idx_load(FILE *fp)
{
int i;
char magic[4];
uint32_t x[5];
uint32_t x[5], i;
uint64_t sum_len = 0;
mm_idx_t *mi;
@@ -393,9 +472,11 @@ mm_idx_t *mm_idx_load(FILE *fp)
uint8_t l;
mm_idx_seq_t *s = &mi->seq[i];
fread(&l, 1, 1, fp);
s->name = (char*)kmalloc(mi->km, l + 1);
fread(s->name, 1, l, fp);
s->name[l] = 0;
if (l) {
s->name = (char*)kmalloc(mi->km, l + 1);
fread(s->name, 1, l, fp);
s->name[l] = 0;
}
fread(&s->len, 4, 1, fp);
s->offset = sum_len;
sum_len += s->len;
@@ -421,8 +502,10 @@ mm_idx_t *mm_idx_load(FILE *fp)
kh_val(h, k) = x[1];
}
}
mi->S = (uint32_t*)malloc((sum_len + 7) / 8 * 4);
fread(mi->S, 4, (sum_len + 7) / 8, fp);
if (!(mi->flag & MM_I_NO_SEQ)) {
mi->S = (uint32_t*)malloc((sum_len + 7) / 8 * 4);
fread(mi->S, 4, (sum_len + 7) / 8, fp);
}
return mi;
}
@@ -476,13 +559,13 @@ mm_idx_t *mm_idx_reader_read(mm_idx_reader_t *r, int n_threads)
mm_idx_t *mi;
if (r->is_idx) {
mi = mm_idx_load(r->fp.idx);
if (mi && mm_verbose >= 2 && (mi->k != r->opt.k || mi->w != r->opt.w || mi->is_hpc != r->opt.is_hpc))
if (mi && mm_verbose >= 2 && (mi->k != r->opt.k || mi->w != r->opt.w || (mi->flag&MM_I_HPC) != (r->opt.flag&MM_I_HPC)))
fprintf(stderr, "[WARNING]\033[1;31m Indexing parameters (-k, -w or -H) overridden by parameters used in the prebuilt index.\033[0m\n");
} else
mi = mm_idx_gen(r->fp.seq, r->opt.w, r->opt.k, r->opt.bucket_bits, r->opt.is_hpc, r->opt.mini_batch_size, n_threads, r->opt.batch_size, 1);
mi = mm_idx_gen(r->fp.seq, r->opt.w, r->opt.k, r->opt.bucket_bits, r->opt.flag, r->opt.mini_batch_size, n_threads, r->opt.batch_size);
if (mi) {
if (r->fp_out) mm_idx_dump(r->fp_out, mi);
++r->n_parts;
mi->index = r->n_parts++;
}
return mi;
}
+6 -2
View File
@@ -189,6 +189,10 @@ void km_stat(const void *_km, km_stat_t *s)
panic("[km_stat] The end of a free block enters another free block.");
if (p->ptr == km->loop_head) break;
}
for (p = km->core_head; p != NULL; p = p->ptr)
++s->n_cores, s->capacity += p->size * sizeof(header_t);
for (p = km->core_head; p != NULL; p = p->ptr) {
size_t size = p->size * sizeof(header_t);
++s->n_cores;
s->capacity += size;
s->largest = s->largest > size? s->largest : size;
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ extern "C" {
#endif
typedef struct {
size_t capacity, available, n_blocks, n_cores;
size_t capacity, available, n_blocks, n_cores, largest;
} km_stat_t;
void *kmalloc(void *km, size_t size);
+18 -1
View File
@@ -39,7 +39,24 @@ typedef struct {
#define KSORT_SWAP(type_t, a, b) { register type_t t=(a); (a)=(b); (b)=t; }
#define KSORT_INIT(name, type_t, __sort_lt) \
#define KSORT_INIT(name, type_t, __sort_lt) \
void ks_heapdown_##name(size_t i, size_t n, type_t l[]) \
{ \
size_t k = i; \
type_t tmp = l[i]; \
while ((k = (k << 1) + 1) < n) { \
if (k != n - 1 && __sort_lt(l[k], l[k+1])) ++k; \
if (__sort_lt(l[k], tmp)) break; \
l[i] = l[k]; i = k; \
} \
l[i] = tmp; \
} \
void ks_heapmake_##name(size_t lsize, type_t l[]) \
{ \
size_t i; \
for (i = (lsize >> 1) - 1; i != (size_t)(-1); --i) \
ks_heapdown_##name(i, lsize, l); \
} \
type_t ks_ksmall_##name(size_t n, type_t arr[], size_t kk) \
{ \
type_t *low, *high, *k, *ll, *hh, *mid; \
+6 -6
View File
@@ -116,7 +116,7 @@ static inline uint32_t *ksw_push_cigar(void *km, int *n_cigar, int *m_cigar, uin
// bit 0-2: which type gets the max - 0 for H, 1 for E, 2 for F, 3 for \tilde{E} and 4 for \tilde{F}
// bit 3/0x08: 1 if a continuation on the E state (bit 5/0x20 for a continuation on \tilde{E})
// bit 4/0x10: 1 if a continuation on the F state (bit 6/0x40 for a continuation on \tilde{F})
static inline void ksw_backtrack(void *km, int is_rot, int is_rev, int with_N, const uint8_t *p, const int *off, const int *off_end, int n_col, int i0, int j0,
static inline void ksw_backtrack(void *km, int is_rot, int is_rev, int min_intron_len, const uint8_t *p, const int *off, const int *off_end, int n_col, int i0, int j0,
int *m_cigar_, int *n_cigar_, uint32_t **cigar_)
{ // p[] - lower 3 bits: which type gets the max; bit
int n_cigar = 0, m_cigar = *m_cigar_, i = i0, j = j0, r, state = 0;
@@ -127,22 +127,22 @@ static inline void ksw_backtrack(void *km, int is_rot, int is_rev, int with_N, c
r = i + j;
if (i < off[r]) force_state = 2;
if (off_end && i > off_end[r]) force_state = 1;
tmp = force_state < 0? p[r * n_col + i - off[r]] : 0;
tmp = force_state < 0? p[(size_t)r * n_col + i - off[r]] : 0;
} else {
if (j < off[i]) force_state = 2;
if (off_end && j > off_end[i]) force_state = 1;
tmp = force_state < 0? p[i * n_col + j - off[i]] : 0;
tmp = force_state < 0? p[(size_t)i * n_col + j - off[i]] : 0;
}
if (state == 0) state = tmp & 7; // if requesting the H state, find state one maximizes it.
else if (!(tmp >> (state + 2) & 1)) state = 0; // if requesting other states, _state_ stays the same if it is a continuation; otherwise, set to H
if (state == 0) state = tmp & 7; // TODO: probably this line can be merged into the "else if" line right above; not 100% sure
if (force_state >= 0) state = force_state;
if (state == 0) cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, 0, 1), --i, --j; // match
else if (state == 1 || (state == 3 && !with_N)) cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, 2, 1), --i; // deletion
else if (state == 3 && with_N) cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, 3, 1), --i; // intron
else if (state == 1 || (state == 3 && min_intron_len <= 0)) cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, 2, 1), --i; // deletion
else if (state == 3 && min_intron_len > 0) cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, 3, 1), --i; // intron
else cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, 1, 1), --j; // insertion
}
if (i >= 0) cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, 2, i + 1); // first deletion
if (i >= 0) cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, min_intron_len > 0 && i >= min_intron_len? 3 : 2, i + 1); // first deletion
if (j >= 0) cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, 1, j + 1); // first insertion
if (!is_rev)
for (i = 0; i < n_cigar>>1; ++i) // reverse CIGAR
+5 -5
View File
@@ -76,7 +76,7 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
qe2_ = _mm_set1_epi8(q2 + e2);
sc_mch_ = _mm_set1_epi8(mat[0]);
sc_mis_ = _mm_set1_epi8(mat[1]);
sc_N_ = _mm_set1_epi8(-e2);
sc_N_ = mat[m*m-1] == 0? _mm_set1_epi8(-e2) : _mm_set1_epi8(mat[m*m-1]);
m1_ = _mm_set1_epi8(m - 1); // wildcard
if (w < 0) w = tlen > qlen? tlen : qlen;
@@ -111,7 +111,7 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
for (t = 0; t < tlen_ * 16; ++t) H[t] = KSW_NEG_INF;
}
if (with_cigar) {
mem2 = (uint8_t*)kmalloc(km, ((qlen + tlen - 1) * n_col_ + 1) * 16);
mem2 = (uint8_t*)kmalloc(km, ((size_t)(qlen + tlen - 1) * n_col_ + 1) * 16);
p = (__m128i*)(((size_t)mem2 + 15) >> 4 << 4);
off = (int*)kmalloc(km, (qlen + tlen - 1) * sizeof(int) * 2);
off_end = off + qlen + tlen - 1;
@@ -218,7 +218,7 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
#endif
}
} else if (!(flag&KSW_EZ_RIGHT)) { // gap left-alignment
__m128i *pr = p + r * n_col_ - st_;
__m128i *pr = p + (size_t)r * n_col_ - st_;
off[r] = st, off_end[r] = en;
for (t = st_; t <= en_; ++t) {
__m128i d, z, a, b, a2, b2, xt1, x2t1, vt1, ut, tmp;
@@ -265,7 +265,7 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
_mm_store_si128(&pr[t], d);
}
} else { // gap right-alignment
__m128i *pr = p + r * n_col_ - st_;
__m128i *pr = p + (size_t)r * n_col_ - st_;
off[r] = st, off_end[r] = en;
for (t = st_; t <= en_; ++t) {
__m128i d, z, a, b, a2, b2, xt1, x2t1, vt1, ut, tmp;
@@ -382,7 +382,7 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
int rev_cigar = !!(flag & KSW_EZ_REV_CIGAR);
if (!ez->zdropped && !(flag&KSW_EZ_EXTZ_ONLY)) {
ksw_backtrack(km, 1, rev_cigar, 0, (uint8_t*)p, off, off_end, n_col_*16, tlen-1, qlen-1, &ez->m_cigar, &ez->n_cigar, &ez->cigar);
} else if (!ez->zdropped && (flag&KSW_EZ_EXTZ_ONLY) && ez->mqe + end_bonus > ez->max) {
} else if (!ez->zdropped && (flag&KSW_EZ_EXTZ_ONLY) && ez->mqe + end_bonus > (int)ez->max) {
ez->reach_end = 1;
ksw_backtrack(km, 1, rev_cigar, 0, (uint8_t*)p, off, off_end, n_col_*16, ez->mqe_t, qlen-1, &ez->m_cigar, &ez->n_cigar, &ez->cigar);
} else if (ez->max_t >= 0 && ez->max_q >= 0) {
+3 -3
View File
@@ -71,7 +71,7 @@ void ksw_exts2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
qe_ = _mm_set1_epi8(q + e);
sc_mch_ = _mm_set1_epi8(mat[0]);
sc_mis_ = _mm_set1_epi8(mat[1]);
sc_N_ = _mm_set1_epi8(-e);
sc_N_ = mat[m*m-1] == 0? _mm_set1_epi8(-e) : _mm_set1_epi8(mat[m*m-1]);
m1_ = _mm_set1_epi8(m - 1); // wildcard
tlen_ = (tlen + 15) / 16;
@@ -367,9 +367,9 @@ void ksw_exts2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
if (with_cigar) { // backtrack
int rev_cigar = !!(flag & KSW_EZ_REV_CIGAR);
if (!ez->zdropped && !(flag&KSW_EZ_EXTZ_ONLY))
ksw_backtrack(km, 1, rev_cigar, 1, (uint8_t*)p, off, off_end, n_col_*16, tlen-1, qlen-1, &ez->m_cigar, &ez->n_cigar, &ez->cigar);
ksw_backtrack(km, 1, rev_cigar, long_thres, (uint8_t*)p, off, off_end, n_col_*16, tlen-1, qlen-1, &ez->m_cigar, &ez->n_cigar, &ez->cigar);
else if (ez->max_t >= 0 && ez->max_q >= 0)
ksw_backtrack(km, 1, rev_cigar, 1, (uint8_t*)p, off, off_end, n_col_*16, ez->max_t, ez->max_q, &ez->m_cigar, &ez->n_cigar, &ez->cigar);
ksw_backtrack(km, 1, rev_cigar, long_thres, (uint8_t*)p, off, off_end, n_col_*16, ez->max_t, ez->max_q, &ez->m_cigar, &ez->n_cigar, &ez->cigar);
kfree(km, mem2); kfree(km, off);
}
}
+5 -5
View File
@@ -65,7 +65,7 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
flag16_ = _mm_set1_epi8(0x10);
sc_mch_ = _mm_set1_epi8(mat[0]);
sc_mis_ = _mm_set1_epi8(mat[1]);
sc_N_ = _mm_set1_epi8(-e);
sc_N_ = mat[m*m-1] == 0? _mm_set1_epi8(-e) : _mm_set1_epi8(mat[m*m-1]);
m1_ = _mm_set1_epi8(m - 1); // wildcard
max_sc_ = _mm_set1_epi8(mat[0] + (q + e) * 2);
@@ -89,7 +89,7 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
for (t = 0; t < tlen_ * 16; ++t) H[t] = KSW_NEG_INF;
}
if (with_cigar) {
mem2 = (uint8_t*)kmalloc(km, ((qlen + tlen - 1) * n_col_ + 1) * 16);
mem2 = (uint8_t*)kmalloc(km, ((size_t)(qlen + tlen - 1) * n_col_ + 1) * 16);
p = (__m128i*)(((size_t)mem2 + 15) >> 4 << 4);
off = (int*)kmalloc(km, (qlen + tlen - 1) * sizeof(int) * 2);
off_end = off + qlen + tlen - 1;
@@ -169,7 +169,7 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
#endif
}
} else if (!(flag&KSW_EZ_RIGHT)) { // gap left-alignment
__m128i *pr = p + r * n_col_ - st_;
__m128i *pr = p + (size_t)r * n_col_ - st_;
off[r] = st, off_end[r] = en;
for (t = st_; t <= en_; ++t) {
__m128i d, z, a, b, xt1, vt1, ut, tmp;
@@ -195,7 +195,7 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
_mm_store_si128(&pr[t], d);
}
} else { // gap right-alignment
__m128i *pr = p + r * n_col_ - st_;
__m128i *pr = p + (size_t)r * n_col_ - st_;
off[r] = st, off_end[r] = en;
for (t = st_; t <= en_; ++t) {
__m128i d, z, a, b, xt1, vt1, ut, tmp;
@@ -293,7 +293,7 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
int rev_cigar = !!(flag & KSW_EZ_REV_CIGAR);
if (!ez->zdropped && !(flag&KSW_EZ_EXTZ_ONLY)) {
ksw_backtrack(km, 1, rev_cigar, 0, (uint8_t*)p, off, off_end, n_col_*16, tlen-1, qlen-1, &ez->m_cigar, &ez->n_cigar, &ez->cigar);
} else if (!ez->zdropped && (flag&KSW_EZ_EXTZ_ONLY) && ez->mqe + end_bonus > ez->max) {
} else if (!ez->zdropped && (flag&KSW_EZ_EXTZ_ONLY) && ez->mqe + end_bonus > (int)ez->max) {
ez->reach_end = 1;
ksw_backtrack(km, 1, rev_cigar, 0, (uint8_t*)p, off, off_end, n_col_*16, ez->mqe_t, qlen-1, &ez->m_cigar, &ez->n_cigar, &ez->cigar);
} else if (ez->max_t >= 0 && ez->max_q >= 0) {
+1
View File
@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <limits.h>
#include <stdint.h>
#include "kthread.h"
#if (defined(WIN32) || defined(_WIN32)) && defined(_MSC_VER)
#define __sync_fetch_and_add(ptr, addend) _InterlockedExchangeAdd((void*)ptr, addend)
+112 -41
View File
@@ -4,9 +4,13 @@
#include "bseq.h"
#include "minimap.h"
#include "mmpriv.h"
#ifdef HAVE_GETOPT
#include <getopt.h>
#else
#include "getopt.h"
#endif
#define MM_VERSION "2.5-r572"
#define MM_VERSION "2.11-r815-dirty"
#ifdef __linux__
#include <sys/resource.h>
@@ -28,7 +32,7 @@ static struct option long_options[] = {
{ "seed", required_argument, 0, 0 },
{ "no-kalloc", no_argument, 0, 0 },
{ "print-qname", no_argument, 0, 0 },
{ "no-self", no_argument, 0, 0 },
{ "no-self", no_argument, 0, 'D' },
{ "print-seeds", no_argument, 0, 0 },
{ "max-chain-skip", required_argument, 0, 0 },
{ "min-dp-len", required_argument, 0, 0 },
@@ -37,12 +41,27 @@ static struct option long_options[] = {
{ "cost-non-gt-ag", required_argument, 0, 'C' },
{ "no-long-join", no_argument, 0, 0 },
{ "sr", no_argument, 0, 0 },
{ "frag", optional_argument, 0, 0 },
{ "secondary", optional_argument, 0, 0 },
{ "frag", required_argument, 0, 0 },
{ "secondary", required_argument, 0, 0 },
{ "cs", optional_argument, 0, 0 },
{ "end-bonus", required_argument, 0, 0 },
{ "no-pairing", no_argument, 0, 0 },
{ "splice-flank", optional_argument, 0, 0 },
{ "splice-flank", required_argument, 0, 0 },
{ "idx-no-seq", no_argument, 0, 0 },
{ "end-seed-pen", required_argument, 0, 0 }, // 21
{ "for-only", no_argument, 0, 0 }, // 22
{ "rev-only", no_argument, 0, 0 }, // 23
{ "heap-sort", required_argument, 0, 0 }, // 24
{ "all-chain", no_argument, 0, 'P' },
{ "dual", required_argument, 0, 0 }, // 26
{ "max-clip-ratio", required_argument, 0, 0 }, // 27
{ "min-occ-floor", required_argument, 0, 0 }, // 28
{ "MD", no_argument, 0, 0 }, // 29
{ "lj-min-ratio", required_argument, 0, 0 }, // 30
{ "score-N", required_argument, 0, 0 }, // 31
{ "eqx", no_argument, 0, 0 }, // 32
{ "paf-no-hit", no_argument, 0, 0 }, // 33
{ "split-prefix", required_argument, 0, 0 }, // 34
{ "help", no_argument, 0, 'h' },
{ "max-intron-len", required_argument, 0, 'G' },
{ "version", no_argument, 0, 'V' },
@@ -58,19 +77,32 @@ static inline int64_t mm_parse_num(const char *str)
{
double x;
char *p;
x = strtod(optarg, &p);
x = strtod(str, &p);
if (*p == 'G' || *p == 'g') x *= 1e9;
else if (*p == 'M' || *p == 'm') x *= 1e6;
else if (*p == 'K' || *p == 'k') x *= 1e3;
return (int64_t)(x + .499);
}
static inline void yes_or_no(mm_mapopt_t *opt, int flag, int long_idx, const char *arg, int yes_to_set)
{
if (yes_to_set) {
if (strcmp(arg, "yes") == 0 || strcmp(arg, "y") == 0) opt->flag |= flag;
else if (strcmp(arg, "no") == 0 || strcmp(arg, "n") == 0) opt->flag &= ~flag;
else fprintf(stderr, "[WARNING]\033[1;31m option '--%s' only accepts 'yes' or 'no'.\033[0m\n", long_options[long_idx].name);
} else {
if (strcmp(arg, "yes") == 0 || strcmp(arg, "y") == 0) opt->flag &= ~flag;
else if (strcmp(arg, "no") == 0 || strcmp(arg, "n") == 0) opt->flag |= flag;
else fprintf(stderr, "[WARNING]\033[1;31m option '--%s' only accepts 'yes' or 'no'.\033[0m\n", long_options[long_idx].name);
}
}
int main(int argc, char *argv[])
{
const char *opt_str = "2aSw:k:K:t:r:f:Vv:g:G:I:d:XT:s:x:Hcp:M:n:z:A:B:O:E:m:N:Qu:R:hF:LC:";
const char *opt_str = "2aSDw:k:K:t:r:f:Vv:g:G:I:d:XT:s:x:Hcp:M:n:z:A:B:O:E:m:N:Qu:R:hF:LC:yY";
mm_mapopt_t opt;
mm_idxopt_t ipt;
int i, c, n_threads = 3, long_idx;
int i, c, n_threads = 3, n_parts, long_idx;
char *fnw = 0, *rg = 0, *s;
FILE *fp_help = stderr;
mm_idx_reader_t *idx_rdr;
@@ -89,12 +121,12 @@ int main(int argc, char *argv[])
}
break;
}
optreset = 1;
optind = 0; // for musl getopt, optind=0 has the same effect as optreset=1; older libc doesn't have optreset
while ((c = getopt_long(argc, argv, opt_str, long_options, &long_idx)) >= 0) {
if (c == 'w') ipt.w = atoi(optarg);
else if (c == 'k') ipt.k = atoi(optarg);
else if (c == 'H') ipt.is_hpc = 1;
else if (c == 'H') ipt.flag |= MM_I_HPC;
else if (c == 'd') fnw = optarg; // the above are indexing related options, except -I
else if (c == 'r') opt.bw = (int)mm_parse_num(optarg);
else if (c == 't') n_threads = atoi(optarg);
@@ -106,17 +138,19 @@ int main(int argc, char *argv[])
else if (c == 'p') opt.pri_ratio = atof(optarg);
else if (c == 'M') opt.mask_level = atof(optarg);
else if (c == 'c') opt.flag |= MM_F_OUT_CG | MM_F_CIGAR;
else if (c == 'X') opt.flag |= MM_F_AVA | MM_F_NO_SELF;
else if (c == 'D') opt.flag |= MM_F_NO_DIAG;
else if (c == 'P') opt.flag |= MM_F_ALL_CHAINS;
else if (c == 'X') opt.flag |= MM_F_ALL_CHAINS | MM_F_NO_DIAG | MM_F_NO_DUAL | MM_F_NO_LJOIN; // -D -P --no-long-join --dual=no
else if (c == 'a') opt.flag |= MM_F_OUT_SAM | MM_F_CIGAR;
else if (c == 'Q') opt.flag |= MM_F_NO_QUAL;
else if (c == 'Y') opt.flag |= MM_F_SOFTCLIP;
else if (c == 'L') opt.flag |= MM_F_LONG_CIGAR;
else if (c == 'y') opt.flag |= MM_F_COPY_COMMENT;
else if (c == 'T') opt.sdust_thres = atoi(optarg);
else if (c == 'n') opt.min_cnt = atoi(optarg);
else if (c == 'm') opt.min_chain_score = atoi(optarg);
else if (c == 'A') opt.a = atoi(optarg);
else if (c == 'B') opt.b = atoi(optarg);
else if (c == 'z') opt.zdrop = atoi(optarg);
else if (c == 's') opt.min_dp_max = atoi(optarg);
else if (c == 'C') opt.noncan = atoi(optarg);
else if (c == 'I') ipt.batch_size = mm_parse_num(optarg);
@@ -128,24 +162,31 @@ int main(int argc, char *argv[])
else if (c == 0 && long_idx == 2) opt.seed = atoi(optarg); // --seed
else if (c == 0 && long_idx == 3) mm_dbg_flag |= MM_DBG_NO_KALLOC; // --no-kalloc
else if (c == 0 && long_idx == 4) mm_dbg_flag |= MM_DBG_PRINT_QNAME; // --print-qname
else if (c == 0 && long_idx == 5) opt.flag |= MM_F_NO_SELF; // --no-self
else if (c == 0 && long_idx == 6) mm_dbg_flag |= MM_DBG_PRINT_QNAME | MM_DBG_PRINT_SEED, n_threads = 1; // --print-seed
else if (c == 0 && long_idx == 7) opt.max_chain_skip = atoi(optarg); // --max-chain-skip
else if (c == 0 && long_idx == 8) opt.min_ksw_len = atoi(optarg); // --min-dp-len
else if (c == 0 && long_idx == 9) mm_dbg_flag |= MM_DBG_PRINT_QNAME | MM_DBG_PRINT_ALN_SEQ; // --print-aln-seq
else if (c == 0 && long_idx == 9) mm_dbg_flag |= MM_DBG_PRINT_QNAME | MM_DBG_PRINT_ALN_SEQ, n_threads = 1; // --print-aln-seq
else if (c == 0 && long_idx ==10) opt.flag |= MM_F_SPLICE; // --splice
else if (c == 0 && long_idx ==12) opt.flag |= MM_F_NO_LJOIN; // --no-long-join
else if (c == 0 && long_idx ==13) opt.flag |= MM_F_SR; // --sr
else if (c == 0 && long_idx ==17) opt.end_bonus = atoi(optarg); // --end-bonus
else if (c == 0 && long_idx ==18) opt.flag |= MM_F_INDEPEND_SEG; // --no-pairing
else if (c == 0 && long_idx ==20) ipt.flag |= MM_I_NO_SEQ; // --idx-no-seq
else if (c == 0 && long_idx ==21) opt.anchor_ext_shift = atoi(optarg); // --end-seed-pen
else if (c == 0 && long_idx ==22) opt.flag |= MM_F_FOR_ONLY; // --for-only
else if (c == 0 && long_idx ==23) opt.flag |= MM_F_REV_ONLY; // --rev-only
else if (c == 0 && long_idx ==27) opt.max_clip_ratio = atof(optarg); // --max-clip-ratio
else if (c == 0 && long_idx ==28) opt.min_mid_occ = atoi(optarg); // --min-occ-floor
else if (c == 0 && long_idx ==29) opt.flag |= MM_F_OUT_MD; // --MD
else if (c == 0 && long_idx ==30) opt.min_join_flank_ratio = atof(optarg); // --lj-min-ratio
else if (c == 0 && long_idx ==31) opt.sc_ambi = atoi(optarg); // --score-N
else if (c == 0 && long_idx ==32) opt.flag |= MM_F_EQX; // --eqx
else if (c == 0 && long_idx ==33) opt.flag |= MM_F_PAF_NO_HIT; // --paf-no-hit
else if (c == 0 && long_idx ==34) opt.split_prefix = optarg; // --split-prefix
else if (c == 0 && long_idx == 14) { // --frag
if (optarg == 0 || strcmp(optarg, "yes") == 0 || strcmp(optarg, "y") == 0)
opt.flag |= MM_F_FRAG_MODE;
else opt.flag &= ~MM_F_FRAG_MODE;
yes_or_no(&opt, MM_F_FRAG_MODE, long_idx, optarg, 1);
} else if (c == 0 && long_idx == 15) { // --secondary
if (optarg == 0 || strcmp(optarg, "yes") == 0 || strcmp(optarg, "y") == 0)
opt.flag &= ~MM_F_NO_PRINT_2ND;
else opt.flag |= MM_F_NO_PRINT_2ND;
yes_or_no(&opt, MM_F_NO_PRINT_2ND, long_idx, optarg, 0);
} else if (c == 0 && long_idx == 16) { // --cs
opt.flag |= MM_F_OUT_CS | MM_F_CIGAR;
if (optarg == 0 || strcmp(optarg, "short") == 0) {
@@ -158,9 +199,11 @@ int main(int argc, char *argv[])
fprintf(stderr, "[WARNING]\033[1;31m --cs only takes 'short' or 'long'. Invalid values are assumed to be 'short'.\033[0m\n");
}
} else if (c == 0 && long_idx == 19) { // --splice-flank
if (optarg == 0 || strcmp(optarg, "yes") == 0 || strcmp(optarg, "y") == 0)
opt.flag |= MM_F_SPLICE_FLANK;
else opt.flag &= ~MM_F_SPLICE_FLANK;
yes_or_no(&opt, MM_F_SPLICE_FLANK, long_idx, optarg, 1);
} else if (c == 0 && long_idx == 24) { // --heap-sort
yes_or_no(&opt, MM_F_HEAP_SORT, long_idx, optarg, 1);
} else if (c == 0 && long_idx == 26) { // --dual
yes_or_no(&opt, MM_F_NO_DUAL, long_idx, optarg, 0);
} else if (c == 'S') {
opt.flag |= MM_F_OUT_CS | MM_F_CIGAR | MM_F_OUT_CS_LONG;
if (mm_verbose >= 2)
@@ -184,6 +227,9 @@ int main(int argc, char *argv[])
fprintf(stderr, "[ERROR]\033[1;31m unrecognized cDNA direction\033[0m\n");
return 1;
}
} else if (c == 'z') {
opt.zdrop = opt.zdrop_inv = strtol(optarg, &s, 10);
if (*s == ',') opt.zdrop_inv = strtol(s + 1, &s, 10);
} else if (c == 'O') {
opt.q = opt.q2 = strtol(optarg, &s, 10);
if (*s == ',') opt.q2 = strtol(s + 1, &s, 10);
@@ -196,12 +242,16 @@ int main(int argc, char *argv[])
fprintf(stderr, "[ERROR]\033[1;31m --splice and --frag should not be specified at the same time.\033[0m\n");
return 1;
}
if (!fnw && !(opt.flag&MM_F_CIGAR))
ipt.flag |= MM_I_NO_SEQ;
if (mm_check_opt(&ipt, &opt) < 0)
return 1;
if (argc == optind || fp_help == stdout) {
fprintf(fp_help, "Usage: minimap2 [options] <target.fa>|<target.idx> [query.fa] [...]\n");
fprintf(fp_help, "Options:\n");
fprintf(fp_help, " Indexing:\n");
fprintf(fp_help, " -H use homopolymer-compressed k-mer\n");
fprintf(fp_help, " -H use homopolymer-compressed k-mer (preferrable for PacBio)\n");
fprintf(fp_help, " -k INT k-mer size (no larger than 28) [%d]\n", ipt.k);
fprintf(fp_help, " -w INT minizer window size [%d]\n", ipt.w);
fprintf(fp_help, " -I NUM split index for every ~NUM input bases [4G]\n");
@@ -223,7 +273,7 @@ int main(int argc, char *argv[])
fprintf(fp_help, " -B INT mismatch penalty [%d]\n", opt.b);
fprintf(fp_help, " -O INT[,INT] gap open penalty [%d,%d]\n", opt.q, opt.q2);
fprintf(fp_help, " -E INT[,INT] gap extension penalty; a k-long gap costs min{O1+k*E1,O2+k*E2} [%d,%d]\n", opt.e, opt.e2);
fprintf(fp_help, " -z INT Z-drop score [%d]\n", opt.zdrop);
fprintf(fp_help, " -z INT[,INT] Z-drop score and inversion Z-drop score [%d,%d]\n", opt.zdrop, opt.zdrop_inv);
fprintf(fp_help, " -s INT minimal peak DP alignment score [%d]\n", opt.min_dp_max);
fprintf(fp_help, " -u CHAR how to find GT-AG. f:transcript strand, b:both strands, n:don't match GT-AG [n]\n");
fprintf(fp_help, " Input/Output:\n");
@@ -233,25 +283,28 @@ int main(int argc, char *argv[])
fprintf(fp_help, " -R STR SAM read group line in a format like '@RG\\tID:foo\\tSM:bar' []\n");
fprintf(fp_help, " -c output CIGAR in PAF\n");
fprintf(fp_help, " --cs[=STR] output the cs tag; STR is 'short' (if absent) or 'long' [none]\n");
fprintf(fp_help, " --MD output the MD tag\n");
fprintf(fp_help, " --eqx write =/X CIGAR operators\n");
fprintf(fp_help, " -Y use soft clipping for supplementary alignments\n");
fprintf(fp_help, " -t INT number of threads [%d]\n", n_threads);
fprintf(fp_help, " -K NUM minibatch size for mapping [500M]\n");
// fprintf(fp_help, " -v INT verbose level [%d]\n", mm_verbose);
fprintf(fp_help, " --version show version number\n");
fprintf(fp_help, " Preset:\n");
fprintf(fp_help, " -x STR preset (always applied before other options) []\n");
fprintf(fp_help, " map-pb: -Hk19 (PacBio vs reference mapping)\n");
fprintf(fp_help, " map-ont: -k15 (Oxford Nanopore vs reference mapping)\n");
fprintf(fp_help, " asm5: -k19 -w19 -A1 -B19 -O39,81 -E3,1 -s200 -z200 (asm to ref mapping; break at 5%% div.)\n");
fprintf(fp_help, " asm10: -k19 -w19 -A1 -B9 -O16,41 -E2,1 -s200 -z200 (asm to ref mapping; break at 10%% div.)\n");
fprintf(fp_help, " ava-pb: -Hk19 -w5 -Xp0 -m100 -g10000 --max-chain-skip 25 (PacBio read overlap)\n");
fprintf(fp_help, " ava-ont: -k15 -w5 -Xp0 -m100 -g10000 --max-chain-skip 25 (ONT read overlap)\n");
fprintf(fp_help, " splice: long-read spliced alignment (see minimap2.1 for details)\n");
fprintf(fp_help, " sr: short single-end reads without splicing (see minimap2.1 for details)\n");
fprintf(fp_help, " -x STR preset (always applied before other options; see minimap2.1 for details) []\n");
fprintf(fp_help, " - map-pb/map-ont: PacBio/Nanopore vs reference mapping\n");
fprintf(fp_help, " - ava-pb/ava-ont: PacBio/Nanopore read overlap\n");
fprintf(fp_help, " - asm5/asm10/asm20: asm-to-ref mapping, for ~0.1/1/5%% sequence divergence\n");
fprintf(fp_help, " - splice: long-read spliced alignment\n");
fprintf(fp_help, " - sr: genomic short-read mapping\n");
fprintf(fp_help, "\nSee `man ./minimap2.1' for detailed description of command-line options.\n");
return fp_help == stdout? 0 : 1;
}
if ((opt.flag & MM_F_SR) && argc - optind > 3) {
fprintf(stderr, "[ERROR] incorrect input: in the sr mode, please specify no more than two query files.\n");
return 1;
}
idx_rdr = mm_idx_reader_open(argv[optind], &ipt, fnw);
if (idx_rdr == 0) {
fprintf(stderr, "[ERROR] failed to open file '%s'\n", argv[optind]);
@@ -259,18 +312,25 @@ int main(int argc, char *argv[])
}
if (!idx_rdr->is_idx && fnw == 0 && argc - optind < 2) {
fprintf(stderr, "[ERROR] missing input: please specify a query file to map or option -d to keep the index\n");
mm_idx_reader_close(idx_rdr);
return 1;
}
if (opt.best_n == 0 && (opt.flag&MM_F_CIGAR) && mm_verbose >= 2)
fprintf(stderr, "[WARNING]\033[1;31m `-N 0' reduces alignment accuracy. Please use --secondary=no to suppress secondary alignments.\033[0m\n");
while ((mi = mm_idx_reader_read(idx_rdr, n_threads)) != 0) {
if ((opt.flag & MM_F_CIGAR) && (mi->flag & MM_I_NO_SEQ)) {
fprintf(stderr, "[ERROR] the prebuilt index doesn't contain sequences.\n");
mm_idx_destroy(mi);
mm_idx_reader_close(idx_rdr);
return 1;
}
if ((opt.flag & MM_F_OUT_SAM) && idx_rdr->n_parts == 1) {
if (mm_idx_reader_eof(idx_rdr)) {
mm_write_sam_hdr(mi, rg, MM_VERSION, argc, argv);
} else {
mm_write_sam_hdr(0, rg, MM_VERSION, argc, argv);
if (mm_verbose >= 2)
fprintf(stderr, "[WARNING]\033[1;31m For a multi-part index, no @SQ lines will be outputted.\033[0m\n");
if (opt.split_prefix == 0 && mm_verbose >= 2)
fprintf(stderr, "[WARNING]\033[1;31m For a multi-part index, no @SQ lines will be outputted. Please use --split-prefix.\033[0m\n");
}
}
if (mm_verbose >= 3)
@@ -286,12 +346,23 @@ int main(int argc, char *argv[])
}
mm_idx_destroy(mi);
}
n_parts = idx_rdr->n_parts;
mm_idx_reader_close(idx_rdr);
fprintf(stderr, "[M::%s] Version: %s\n", __func__, MM_VERSION);
fprintf(stderr, "[M::%s] CMD:", __func__);
for (i = 0; i < argc; ++i)
fprintf(stderr, " %s", argv[i]);
fprintf(stderr, "\n[M::%s] Real time: %.3f sec; CPU: %.3f sec\n", __func__, realtime() - mm_realtime0, cputime());
if (opt.split_prefix)
mm_split_merge(argc - (optind + 1), (const char**)&argv[optind + 1], &opt, n_parts);
if (fflush(stdout) == EOF) {
fprintf(stderr, "[ERROR] failed to write the results\n");
exit(EXIT_FAILURE);
}
if (mm_verbose >= 3) {
fprintf(stderr, "[M::%s] Version: %s\n", __func__, MM_VERSION);
fprintf(stderr, "[M::%s] CMD:", __func__);
for (i = 0; i < argc; ++i)
fprintf(stderr, " %s", argv[i]);
fprintf(stderr, "\n[M::%s] Real time: %.3f sec; CPU: %.3f sec\n", __func__, realtime() - mm_realtime0, cputime());
}
return 0;
}
+403 -249
View File
@@ -9,122 +9,9 @@
#include "bseq.h"
#include "khash.h"
void mm_mapopt_init(mm_mapopt_t *opt)
{
memset(opt, 0, sizeof(mm_mapopt_t));
opt->seed = 11;
opt->mid_occ_frac = 2e-4f;
opt->sdust_thres = 0; // no SDUST masking
opt->min_cnt = 3;
opt->min_chain_score = 40;
opt->bw = 500;
opt->max_gap = 5000;
opt->max_gap_ref = -1;
opt->max_chain_skip = 25;
opt->mask_level = 0.5f;
opt->pri_ratio = 0.8f;
opt->best_n = 5;
opt->max_join_long = 20000;
opt->max_join_short = 2000;
opt->min_join_flank_sc = 1000;
opt->a = 2, opt->b = 4, opt->q = 4, opt->e = 2, opt->q2 = 24, opt->e2 = 1;
opt->zdrop = 400;
opt->end_bonus = -1;
opt->min_dp_max = opt->min_chain_score * opt->a;
opt->min_ksw_len = 200;
opt->mini_batch_size = 500000000;
opt->pe_ori = 0; // FF
opt->pe_bonus = 33;
}
void mm_mapopt_update(mm_mapopt_t *opt, const mm_idx_t *mi)
{
if ((opt->flag & MM_F_SPLICE_FOR) && (opt->flag & MM_F_SPLICE_REV))
opt->flag |= MM_F_SPLICE;
if (opt->mid_occ <= 0)
opt->mid_occ = mm_idx_cal_max_occ(mi, opt->mid_occ_frac);
if (mm_verbose >= 3)
fprintf(stderr, "[M::%s::%.3f*%.2f] mid_occ = %d\n", __func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0), opt->mid_occ);
}
void mm_mapopt_max_intron_len(mm_mapopt_t *opt, int max_intron_len)
{
if ((opt->flag & MM_F_SPLICE) && max_intron_len > 0)
opt->max_gap_ref = opt->bw = max_intron_len;
}
int mm_set_opt(const char *preset, mm_idxopt_t *io, mm_mapopt_t *mo)
{
if (preset == 0) {
mm_idxopt_init(io);
mm_mapopt_init(mo);
} else if (strcmp(preset, "ava-ont") == 0) {
io->is_hpc = 0, io->k = 15, io->w = 5;
mo->flag |= MM_F_AVA | MM_F_NO_SELF;
mo->min_chain_score = 100, mo->pri_ratio = 0.0f, mo->max_gap = 10000, mo->max_chain_skip = 25;
} else if (strcmp(preset, "ava-pb") == 0) {
io->is_hpc = 1, io->k = 19, io->w = 5;
mo->flag |= MM_F_AVA | MM_F_NO_SELF;
mo->min_chain_score = 100, mo->pri_ratio = 0.0f, mo->max_gap = 10000, mo->max_chain_skip = 25;
} else if (strcmp(preset, "map10k") == 0 || strcmp(preset, "map-pb") == 0) {
io->is_hpc = 1, io->k = 19;
} else if (strcmp(preset, "map-ont") == 0) {
io->is_hpc = 0, io->k = 15;
} else if (strcmp(preset, "asm5") == 0) {
io->is_hpc = 0, io->k = 19, io->w = 19;
mo->a = 1, mo->b = 19, mo->q = 39, mo->q2 = 81, mo->e = 3, mo->e2 = 1, mo->zdrop = 200;
mo->min_dp_max = 200;
mo->best_n = 50;
} else if (strcmp(preset, "asm10") == 0) {
io->is_hpc = 0, io->k = 19, io->w = 19;
mo->a = 1, mo->b = 9, mo->q = 16, mo->q2 = 41, mo->e = 2, mo->e2 = 1, mo->zdrop = 200;
mo->min_dp_max = 200;
mo->best_n = 50;
} else if (strcmp(preset, "short") == 0 || strcmp(preset, "sr") == 0) {
io->is_hpc = 0, io->k = 21, io->w = 11;
mo->flag |= MM_F_SR | MM_F_FRAG_MODE | MM_F_NO_PRINT_2ND | MM_F_2_IO_THREADS;
mo->pe_ori = 0<<1|1; // FR
mo->a = 2, mo->b = 8, mo->q = 12, mo->e = 2, mo->q2 = 24, mo->e2 = 1;
mo->zdrop = 100;
mo->end_bonus = 10;
mo->max_frag_len = 800;
mo->max_gap = 100;
mo->bw = 100;
mo->pri_ratio = 0.5f;
mo->min_cnt = 2;
mo->min_chain_score = 25;
mo->min_dp_max = 40;
mo->best_n = 20;
mo->mid_occ = 1000;
mo->max_occ = 5000;
mo->mini_batch_size = 50000000;
} else if (strcmp(preset, "splice") == 0 || strcmp(preset, "cdna") == 0) {
io->is_hpc = 0, io->k = 15, io->w = 5;
mo->flag |= MM_F_SPLICE | MM_F_SPLICE_FOR | MM_F_SPLICE_REV | MM_F_SPLICE_FLANK;
mo->max_gap = 2000, mo->max_gap_ref = mo->bw = 200000;
mo->a = 1, mo->b = 2, mo->q = 2, mo->e = 1, mo->q2 = 32, mo->e2 = 0;
mo->noncan = 9;
mo->zdrop = 200;
} else return -1;
return 0;
}
typedef struct {
uint32_t n;
uint32_t qpos;
uint32_t seg_id;
const uint64_t *cr;
} mm_match_t;
struct mm_tbuf_s {
sdust_buf_t *sdb;
mm128_v mini;
void *km;
int rep_len, frag_gap;
};
mm_tbuf_t *mm_tbuf_init(void)
@@ -132,134 +19,243 @@ mm_tbuf_t *mm_tbuf_init(void)
mm_tbuf_t *b;
b = (mm_tbuf_t*)calloc(1, sizeof(mm_tbuf_t));
if (!(mm_dbg_flag & 1)) b->km = km_init();
b->sdb = sdust_buf_init(b->km);
return b;
}
void mm_tbuf_destroy(mm_tbuf_t *b)
{
if (b == 0) return;
kfree(b->km, b->mini.a);
sdust_buf_destroy(b->sdb);
km_destroy(b->km);
free(b);
}
static int mm_dust_minier(int n, mm128_t *a, int l_seq, const char *seq, int sdust_thres, sdust_buf_t *sdb)
static int mm_dust_minier(void *km, int n, mm128_t *a, int l_seq, const char *seq, int sdust_thres)
{
int n_dreg, j, k, u = 0;
const uint64_t *dreg;
if (sdust_thres <= 0 || sdb == 0) return n;
sdust_buf_t *sdb;
if (sdust_thres <= 0) return n;
sdb = sdust_buf_init(km);
dreg = sdust_core((const uint8_t*)seq, l_seq, sdust_thres, 64, &n_dreg, sdb);
for (j = k = 0; j < n; ++j) { // squeeze out minimizers that significantly overlap with LCRs
int32_t qpos = (uint32_t)a[j].y>>1, span = a[j].x&0xff;
int32_t s = qpos - (span - 1), e = s + span;
while (u < n_dreg && (uint32_t)dreg[u] <= s) ++u;
if (u < n_dreg && dreg[u]>>32 < e) {
while (u < n_dreg && (int32_t)dreg[u] <= s) ++u;
if (u < n_dreg && (int32_t)(dreg[u]>>32) < e) {
int v, l = 0;
for (v = u; v < n_dreg && dreg[v]>>32 < e; ++v) { // iterate over LCRs overlapping this minimizer
int ss = s > dreg[v]>>32? s : dreg[v]>>32;
int ee = e < (uint32_t)dreg[v]? e : (uint32_t)dreg[v];
for (v = u; v < n_dreg && (int32_t)(dreg[v]>>32) < e; ++v) { // iterate over LCRs overlapping this minimizer
int ss = s > (int32_t)(dreg[v]>>32)? s : dreg[v]>>32;
int ee = e < (int32_t)dreg[v]? e : (uint32_t)dreg[v];
l += ee - ss;
}
if (l <= span>>1) a[k++] = a[j]; // keep the minimizer if less than half of it falls in masked region
}
} else a[k++] = a[j];
}
sdust_buf_destroy(sdb);
return k; // the new size
}
static void collect_minimizers(const mm_mapopt_t *opt, const mm_idx_t *mi, int n_segs, const int *qlens, const char **seqs, mm_tbuf_t *b)
static void collect_minimizers(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int n_segs, const int *qlens, const char **seqs, mm128_v *mv)
{
int i, j, n, sum = 0;
b->mini.n = 0;
int i, n, sum = 0;
mv->n = 0;
for (i = n = 0; i < n_segs; ++i) {
mm_sketch(b->km, seqs[i], qlens[i], mi->w, mi->k, i, mi->is_hpc, &b->mini);
for (j = n; j < b->mini.n; ++j)
b->mini.a[j].y += sum << 1;
size_t j;
mm_sketch(km, seqs[i], qlens[i], mi->w, mi->k, i, mi->flag&MM_I_HPC, mv);
for (j = n; j < mv->n; ++j)
mv->a[j].y += sum << 1;
if (opt->sdust_thres > 0) // mask low-complexity minimizers
b->mini.n = n + mm_dust_minier(b->mini.n - n, b->mini.a + n, qlens[i], seqs[i], opt->sdust_thres, b->sdb);
sum += qlens[i], n = b->mini.n;
mv->n = n + mm_dust_minier(km, mv->n - n, mv->a + n, qlens[i], seqs[i], opt->sdust_thres);
sum += qlens[i], n = mv->n;
}
}
static mm128_t *collect_seed_hits(const mm_mapopt_t *opt, int max_occ, const mm_idx_t *mi, const char *qname, int qlen, int64_t *n_a, int *rep_len, mm_tbuf_t *b)
{
int rep_st = 0, rep_en = 0, i;
mm_match_t *m;
mm128_t *a;
#include "ksort.h"
#define heap_lt(a, b) ((a).x > (b).x)
KSORT_INIT(heap, mm128_t, heap_lt)
m = (mm_match_t*)kmalloc(b->km, b->mini.n * sizeof(mm_match_t));
for (i = 0; i < b->mini.n; ++i) {
typedef struct {
uint32_t n;
uint32_t q_pos, q_span;
uint32_t seg_id:31, is_tandem:1;
const uint64_t *cr;
} mm_match_t;
static mm_match_t *collect_matches(void *km, int *_n_m, int max_occ, const mm_idx_t *mi, const mm128_v *mv, int64_t *n_a, int *rep_len, int *n_mini_pos, uint64_t **mini_pos)
{
int rep_st = 0, rep_en = 0, n_m;
size_t i;
mm_match_t *m;
*n_mini_pos = 0;
*mini_pos = (uint64_t*)kmalloc(km, mv->n * sizeof(uint64_t));
m = (mm_match_t*)kmalloc(km, mv->n * sizeof(mm_match_t));
for (i = 0, n_m = 0, *rep_len = 0, *n_a = 0; i < mv->n; ++i) {
const uint64_t *cr;
mm128_t *p = &mv->a[i];
uint32_t q_pos = (uint32_t)p->y, q_span = p->x & 0xff;
int t;
mm128_t *p = &b->mini.a[i];
m[i].qpos = (uint32_t)p->y;
m[i].cr = mm_idx_get(mi, p->x>>8, &t);
m[i].n = t;
m[i].seg_id = p->y >> 32;
}
for (i = 0, *n_a = 0; i < b->mini.n; ++i) // find the length of a[]
if (m[i].n < max_occ) *n_a += m[i].n;
a = (mm128_t*)kmalloc(b->km, *n_a * sizeof(mm128_t));
for (i = *rep_len = 0, *n_a = 0; i < b->mini.n; ++i) {
mm128_t *p = &b->mini.a[i];
mm_match_t *q = &m[i];
const uint64_t *r = q->cr;
int k, q_span = p->x & 0xff, is_tandem = 0;
if (q->n >= max_occ) {
int en = (q->qpos>>1) + 1, st = en - q_span;
cr = mm_idx_get(mi, p->x>>8, &t);
if (t >= max_occ) {
int en = (q_pos >> 1) + 1, st = en - q_span;
if (st > rep_en) {
*rep_len += rep_en - rep_st;
rep_st = st, rep_en = en;
} else rep_en = en;
continue;
}
if (i > 0 && p->x>>8 == b->mini.a[i - 1].x>>8) is_tandem = 1;
if (i < b->mini.n - 1 && p->x>>8 == b->mini.a[i + 1].x>>8) is_tandem = 1;
for (k = 0; k < q->n; ++k) {
int32_t rpos = (uint32_t)r[k] >> 1;
mm128_t *p;
if (qname && (opt->flag&(MM_F_NO_SELF|MM_F_AVA))) {
const char *tname = mi->seq[r[k]>>32].name;
int cmp;
cmp = strcmp(qname, tname);
if ((opt->flag&MM_F_NO_SELF) && cmp == 0 && rpos == (q->qpos>>1)) // avoid the diagonal
continue;
if ((opt->flag&MM_F_AVA) && cmp > 0) // all-vs-all mode: map once
continue;
}
p = &a[(*n_a)++];
if ((r[k]&1) == (q->qpos&1)) { // forward strand
p->x = (r[k]&0xffffffff00000000ULL) | rpos;
p->y = (uint64_t)q_span << 32 | q->qpos >> 1;
} else { // reverse strand
p->x = 1ULL<<63 | (r[k]&0xffffffff00000000ULL) | rpos;
p->y = (uint64_t)q_span << 32 | (qlen - ((q->qpos>>1) + 1 - q_span) - 1);
}
p->y |= (uint64_t)q->seg_id << MM_SEED_SEG_SHIFT;
if (is_tandem) p->y |= MM_SEED_TANDEM;
} else {
mm_match_t *q = &m[n_m++];
q->q_pos = q_pos, q->q_span = q_span, q->cr = cr, q->n = t, q->seg_id = p->y >> 32;
q->is_tandem = 0;
if (i > 0 && p->x>>8 == mv->a[i - 1].x>>8) q->is_tandem = 1;
if (i < mv->n - 1 && p->x>>8 == mv->a[i + 1].x>>8) q->is_tandem = 1;
*n_a += q->n;
(*mini_pos)[(*n_mini_pos)++] = (uint64_t)q_span<<32 | q_pos>>1;
}
}
*rep_len += rep_en - rep_st;
kfree(b->km, m);
*_n_m = n_m;
return m;
}
static inline int skip_seed(int flag, uint64_t r, const mm_match_t *q, const char *qname, int qlen, const mm_idx_t *mi, int *is_self)
{
*is_self = 0;
if (qname && (flag & (MM_F_NO_DIAG|MM_F_NO_DUAL))) {
const mm_idx_seq_t *s = &mi->seq[r>>32];
int cmp;
cmp = strcmp(qname, s->name);
if ((flag&MM_F_NO_DIAG) && cmp == 0 && (int)s->len == qlen) {
if ((uint32_t)r>>1 == (q->q_pos>>1)) return 1; // avoid the diagnonal anchors
if ((r&1) == (q->q_pos&1)) *is_self = 1; // this flag is used to avoid spurious extension on self chain
}
if ((flag&MM_F_NO_DUAL) && cmp > 0) // all-vs-all mode: map once
return 1;
}
if (flag & (MM_F_FOR_ONLY|MM_F_REV_ONLY)) {
if ((r&1) == (q->q_pos&1)) { // forward strand
if (flag & MM_F_REV_ONLY) return 1;
} else {
if (flag & MM_F_FOR_ONLY) return 1;
}
}
return 0;
}
static mm128_t *collect_seed_hits_heap(void *km, const mm_mapopt_t *opt, int max_occ, const mm_idx_t *mi, const char *qname, const mm128_v *mv, int qlen, int64_t *n_a, int *rep_len,
int *n_mini_pos, uint64_t **mini_pos)
{
int i, n_m, heap_size = 0;
int64_t j, n_for = 0, n_rev = 0;
mm_match_t *m;
mm128_t *a, *heap;
m = collect_matches(km, &n_m, max_occ, mi, mv, n_a, rep_len, n_mini_pos, mini_pos);
heap = (mm128_t*)kmalloc(km, n_m * sizeof(mm128_t));
a = (mm128_t*)kmalloc(km, *n_a * sizeof(mm128_t));
for (i = 0, heap_size = 0; i < n_m; ++i) {
if (m[i].n > 0) {
heap[heap_size].x = m[i].cr[0];
heap[heap_size].y = (uint64_t)i<<32;
++heap_size;
}
}
ks_heapmake_heap(heap_size, heap);
while (heap_size > 0) {
mm_match_t *q = &m[heap->y>>32];
mm128_t *p;
uint64_t r = heap->x;
int32_t is_self, rpos = (uint32_t)r >> 1;
if (!skip_seed(opt->flag, r, q, qname, qlen, mi, &is_self)) {
if ((r&1) == (q->q_pos&1)) { // forward strand
p = &a[n_for++];
p->x = (r&0xffffffff00000000ULL) | rpos;
p->y = (uint64_t)q->q_span << 32 | q->q_pos >> 1;
} else { // reverse strand
p = &a[(*n_a) - (++n_rev)];
p->x = 1ULL<<63 | (r&0xffffffff00000000ULL) | rpos;
p->y = (uint64_t)q->q_span << 32 | (qlen - ((q->q_pos>>1) + 1 - q->q_span) - 1);
}
p->y |= (uint64_t)q->seg_id << MM_SEED_SEG_SHIFT;
if (q->is_tandem) p->y |= MM_SEED_TANDEM;
if (is_self) p->y |= MM_SEED_SELF;
}
// update the heap
if ((uint32_t)heap->y < q->n - 1) {
++heap[0].y;
heap[0].x = m[heap[0].y>>32].cr[(uint32_t)heap[0].y];
} else {
heap[0] = heap[heap_size - 1];
--heap_size;
}
ks_heapdown_heap(0, heap_size, heap);
}
kfree(km, m);
kfree(km, heap);
// reverse anchors on the reverse strand, as they are in the descending order
for (j = 0; j < n_rev>>1; ++j) {
mm128_t t = a[(*n_a) - 1 - j];
a[(*n_a) - 1 - j] = a[(*n_a) - (n_rev - j)];
a[(*n_a) - (n_rev - j)] = t;
}
if (*n_a > n_for + n_rev) {
memmove(a + n_for, a + (*n_a) - n_rev, n_rev * sizeof(mm128_t));
*n_a = n_for + n_rev;
}
return a;
}
static mm128_t *collect_seed_hits(void *km, const mm_mapopt_t *opt, int max_occ, const mm_idx_t *mi, const char *qname, const mm128_v *mv, int qlen, int64_t *n_a, int *rep_len,
int *n_mini_pos, uint64_t **mini_pos)
{
int i, n_m;
mm_match_t *m;
mm128_t *a;
m = collect_matches(km, &n_m, max_occ, mi, mv, n_a, rep_len, n_mini_pos, mini_pos);
a = (mm128_t*)kmalloc(km, *n_a * sizeof(mm128_t));
for (i = 0, *n_a = 0; i < n_m; ++i) {
mm_match_t *q = &m[i];
const uint64_t *r = q->cr;
uint32_t k;
for (k = 0; k < q->n; ++k) {
int32_t is_self, rpos = (uint32_t)r[k] >> 1;
mm128_t *p;
if (skip_seed(opt->flag, r[k], q, qname, qlen, mi, &is_self)) continue;
p = &a[(*n_a)++];
if ((r[k]&1) == (q->q_pos&1)) { // forward strand
p->x = (r[k]&0xffffffff00000000ULL) | rpos;
p->y = (uint64_t)q->q_span << 32 | q->q_pos >> 1;
} else { // reverse strand
p->x = 1ULL<<63 | (r[k]&0xffffffff00000000ULL) | rpos;
p->y = (uint64_t)q->q_span << 32 | (qlen - ((q->q_pos>>1) + 1 - q->q_span) - 1);
}
p->y |= (uint64_t)q->seg_id << MM_SEED_SEG_SHIFT;
if (q->is_tandem) p->y |= MM_SEED_TANDEM;
if (is_self) p->y |= MM_SEED_SELF;
}
}
kfree(km, m);
radix_sort_128x(a, a + (*n_a));
return a;
}
static void chain_post(const mm_mapopt_t *opt, int max_chain_gap_ref, const mm_idx_t *mi, void *km, int qlen, int n_segs, const int *qlens, int *n_regs, mm_reg1_t *regs, mm128_t *a)
{
if (!(opt->flag & MM_F_AVA)) { // don't choose primary mapping(s) for read overlap
if (!(opt->flag & MM_F_ALL_CHAINS)) { // don't choose primary mapping(s)
mm_set_parent(km, opt->mask_level, *n_regs, regs, opt->a * 2 + opt->b);
if (n_segs <= 1) mm_select_sub(km, opt->pri_ratio, mi->k*2, opt->best_n, n_regs, regs);
else mm_select_sub_multi(km, opt->pri_ratio, 0.2f, 0.7f, max_chain_gap_ref, mi->k*2, opt->best_n, n_segs, qlens, n_regs, regs);
if (!(opt->flag & MM_F_SPLICE) && !(opt->flag & MM_F_SR) && !(opt->flag & MM_F_NO_LJOIN))
if (!(opt->flag & (MM_F_SPLICE|MM_F_SR|MM_F_NO_LJOIN))) // long join not working well without primary chains
mm_join_long(km, opt, qlen, n_regs, regs, a);
}
}
static mm_reg1_t *align_regs(const mm_mapopt_t *opt, const mm_idx_t *mi, void *km, int qlen, const char *seq, const char *qual, int *n_regs, mm_reg1_t *regs, mm128_t *a)
static mm_reg1_t *align_regs(const mm_mapopt_t *opt, const mm_idx_t *mi, void *km, int qlen, const char *seq, int *n_regs, mm_reg1_t *regs, mm128_t *a)
{
if (!(opt->flag & MM_F_CIGAR)) return regs;
regs = mm_align_skeleton(km, opt, mi, qlen, seq, qual, n_regs, regs, a); // this calls mm_filter_regs()
if (!(opt->flag & MM_F_AVA)) {
regs = mm_align_skeleton(km, opt, mi, qlen, seq, n_regs, regs, a); // this calls mm_filter_regs()
if (!(opt->flag & MM_F_ALL_CHAINS)) { // don't choose primary mapping(s)
mm_set_parent(km, opt->mask_level, *n_regs, regs, opt->a * 2 + opt->b);
mm_select_sub(km, opt->pri_ratio, mi->k*2, opt->best_n, n_regs, regs);
mm_set_sam_pri(*n_regs, regs);
@@ -267,15 +263,17 @@ static mm_reg1_t *align_regs(const mm_mapopt_t *opt, const mm_idx_t *mi, void *k
return regs;
}
void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **seqs, const char **quals, int *n_regs, mm_reg1_t **regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *qname)
void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **seqs, int *n_regs, mm_reg1_t **regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *qname)
{
int i, j, rep_len, qlen_sum, n_regs0;
int i, j, rep_len, qlen_sum, n_regs0, n_mini_pos;
int max_chain_gap_qry, max_chain_gap_ref, is_splice = !!(opt->flag & MM_F_SPLICE), is_sr = !!(opt->flag & MM_F_SR);
uint32_t hash;
int64_t n_a;
uint64_t *u;
uint64_t *u, *mini_pos;
mm128_t *a;
mm128_v mv = {0,0,0};
mm_reg1_t *regs0;
km_stat_t kmst;
for (i = 0, qlen_sum = 0; i < n_segs; ++i)
qlen_sum += qlens[i], n_regs[i] = 0, regs[i] = 0;
@@ -286,9 +284,9 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **
hash ^= __ac_Wang_hash(qlen_sum) + __ac_Wang_hash(opt->seed);
hash = __ac_Wang_hash(hash);
collect_minimizers(opt, mi, n_segs, qlens, seqs, b);
a = collect_seed_hits(opt, opt->mid_occ, mi, qname, qlen_sum, &n_a, &rep_len, b);
radix_sort_128x(a, a + n_a);
collect_minimizers(b->km, opt, mi, n_segs, qlens, seqs, &mv);
if (opt->flag & MM_F_HEAP_SORT) a = collect_seed_hits_heap(b->km, opt, opt->mid_occ, mi, qname, &mv, qlen_sum, &n_a, &rep_len, &n_mini_pos, &mini_pos);
else a = collect_seed_hits(b->km, opt, opt->mid_occ, mi, qname, &mv, qlen_sum, &n_a, &rep_len, &n_mini_pos, &mini_pos);
if (mm_dbg_flag & MM_DBG_PRINT_SEED) {
fprintf(stderr, "RS\t%d\n", rep_len);
@@ -315,10 +313,10 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **
if (n_regs0 > 0) { // test if the best chain has all the segments
int n_chained_segs = 1, max = 0, max_i = -1, max_off = -1, off = 0;
for (i = 0; i < n_regs0; ++i) { // find the best chain
if (max < u[i]>>32) max = u[i]>>32, max_i = i, max_off = off;
if (max < (int)(u[i]>>32)) max = u[i]>>32, max_i = i, max_off = off;
off += (uint32_t)u[i];
}
for (i = 1; i < (uint32_t)u[max_i]; ++i) // count the number of segments in the best chain
for (i = 1; i < (int32_t)u[max_i]; ++i) // count the number of segments in the best chain
if ((a[max_off+i].y&MM_SEED_SEG_MASK) != (a[max_off+i-1].y&MM_SEED_SEG_MASK))
++n_chained_segs;
if (n_chained_segs < n_segs)
@@ -327,11 +325,14 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **
if (rechain) { // redo chaining with a higher max_occ threshold
kfree(b->km, a);
kfree(b->km, u);
a = collect_seed_hits(opt, opt->max_occ, mi, qname, qlen_sum, &n_a, &rep_len, b);
radix_sort_128x(a, a + n_a);
kfree(b->km, mini_pos);
if (opt->flag & MM_F_HEAP_SORT) a = collect_seed_hits_heap(b->km, opt, opt->max_occ, mi, qname, &mv, qlen_sum, &n_a, &rep_len, &n_mini_pos, &mini_pos);
else a = collect_seed_hits(b->km, opt, opt->max_occ, mi, qname, &mv, qlen_sum, &n_a, &rep_len, &n_mini_pos, &mini_pos);
a = mm_chain_dp(max_chain_gap_ref, max_chain_gap_qry, opt->bw, opt->max_chain_skip, opt->min_cnt, opt->min_chain_score, is_splice, n_segs, n_a, a, &n_regs0, &u, b->km);
}
}
b->frag_gap = max_chain_gap_ref;
b->rep_len = rep_len;
regs0 = mm_gen_regs(b->km, hash, qlen_sum, n_regs0, u, a);
@@ -342,10 +343,11 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **
i == regs0[j].as? 0 : ((int32_t)a[i].y - (int32_t)a[i-1].y) - ((int32_t)a[i].x - (int32_t)a[i-1].x));
chain_post(opt, max_chain_gap_ref, mi, b->km, qlen_sum, n_segs, qlens, &n_regs0, regs0, a);
if (!is_sr) mm_est_err(mi, qlen_sum, n_regs0, regs0, a, n_mini_pos, mini_pos);
if (n_segs == 1) { // uni-segment
regs0 = align_regs(opt, mi, b->km, qlens[0], seqs[0], quals? quals[0] : 0, &n_regs0, regs0, a);
mm_set_mapq(n_regs0, regs0, opt->min_chain_score, opt->a, rep_len, is_sr);
regs0 = align_regs(opt, mi, b->km, qlens[0], seqs[0], &n_regs0, regs0, a);
mm_set_mapq(b->km, n_regs0, regs0, opt->min_chain_score, opt->a, rep_len, is_sr);
n_regs[0] = n_regs0, regs[0] = regs0;
} else { // multi-segment
mm_seg_t *seg;
@@ -353,22 +355,35 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **
free(regs0);
for (i = 0; i < n_segs; ++i) {
mm_set_parent(b->km, opt->mask_level, n_regs[i], regs[i], opt->a * 2 + opt->b); // update mm_reg1_t::parent
regs[i] = align_regs(opt, mi, b->km, qlens[i], seqs[i], quals? quals[i] : 0, &n_regs[i], regs[i], seg[i].a);
mm_set_mapq(n_regs[i], regs[i], opt->min_chain_score, opt->a, rep_len, is_sr);
regs[i] = align_regs(opt, mi, b->km, qlens[i], seqs[i], &n_regs[i], regs[i], seg[i].a);
mm_set_mapq(b->km, n_regs[i], regs[i], opt->min_chain_score, opt->a, rep_len, is_sr);
}
mm_seg_free(b->km, n_segs, seg);
if (n_segs == 2 && opt->pe_ori >= 0 && (opt->flag&MM_F_CIGAR))
mm_pair(b->km, max_chain_gap_ref, opt->pe_bonus, opt->a * 2 + opt->b, opt->a, qlens, n_regs, regs); // pairing
}
kfree(b->km, mv.a);
kfree(b->km, a);
kfree(b->km, u);
kfree(b->km, mini_pos);
if (b->km) {
km_stat(b->km, &kmst);
if (mm_dbg_flag & MM_DBG_PRINT_QNAME)
fprintf(stderr, "QM\t%s\t%d\tcap=%ld,nCore=%ld,largest=%ld\n", qname, qlen_sum, kmst.capacity, kmst.n_cores, kmst.largest);
assert(kmst.n_blocks == kmst.n_cores); // otherwise, there is a memory leak
if (kmst.largest > 1U<<28) {
km_destroy(b->km);
b->km = km_init();
}
}
}
mm_reg1_t *mm_map(const mm_idx_t *mi, int qlen, const char *seq, int *n_regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *qname)
{
mm_reg1_t *regs;
mm_map_frag(mi, 1, &qlen, &seq, 0, n_regs, &regs, b, opt, qname);
mm_map_frag(mi, 1, &qlen, &seq, n_regs, &regs, b, opt, qname);
return regs;
}
@@ -382,13 +397,17 @@ typedef struct {
mm_bseq_file_t **fp;
const mm_idx_t *mi;
kstring_t str;
int n_parts;
uint32_t *rid_shift;
FILE *fp_split, **fp_parts;
} pipeline_t;
typedef struct {
const pipeline_t *p;
int n_seq, n_frag;
mm_bseq1_t *seq;
int *n_reg, *seg_off, *n_seg;
int *n_reg, *seg_off, *n_seg, *rep_len, *frag_gap;
mm_reg1_t **reg;
mm_tbuf_t **buf;
} step_t;
@@ -396,26 +415,30 @@ typedef struct {
static void worker_for(void *_data, long i, int tid) // kt_for() callback
{
step_t *s = (step_t*)_data;
int *qlens, j, off = s->seg_off[i], pe_ori = s->p->opt->pe_ori, is_sr = !!(s->p->opt->flag & MM_F_SR);
const char **qseqs, **quals = 0;
int qlens[MM_MAX_SEG], j, off = s->seg_off[i], pe_ori = s->p->opt->pe_ori;
const char *qseqs[MM_MAX_SEG];
mm_tbuf_t *b = s->buf[tid];
assert(s->n_seg[i] <= MM_MAX_SEG);
if (mm_dbg_flag & MM_DBG_PRINT_QNAME)
fprintf(stderr, "QR\t%s\t%d\n", s->seq[off].name, tid);
qlens = (int*)kmalloc(b->km, s->n_seg[i] * sizeof(int));
qseqs = (const char**)kmalloc(b->km, s->n_seg[i] * sizeof(const char**));
quals = (const char**)kmalloc(b->km, s->n_seg[i] * sizeof(const char**));
fprintf(stderr, "QR\t%s\t%d\t%d\n", s->seq[off].name, tid, s->seq[off].l_seq);
for (j = 0; j < s->n_seg[i]; ++j) {
if (s->n_seg[i] == 2 && ((j == 0 && (pe_ori>>1&1)) || (j == 1 && (pe_ori&1))))
mm_revcomp_bseq(&s->seq[off + j]);
qlens[j] = s->seq[off + j].l_seq;
qseqs[j] = s->seq[off + j].seq;
quals[j] = is_sr? s->seq[off + j].qual : 0;
}
if (s->p->opt->flag & MM_F_INDEPEND_SEG) {
for (j = 0; j < s->n_seg[i]; ++j)
mm_map_frag(s->p->mi, 1, &qlens[j], &qseqs[j], &quals[j], &s->n_reg[off+j], &s->reg[off+j], b, s->p->opt, s->seq[off+j].name);
for (j = 0; j < s->n_seg[i]; ++j) {
mm_map_frag(s->p->mi, 1, &qlens[j], &qseqs[j], &s->n_reg[off+j], &s->reg[off+j], b, s->p->opt, s->seq[off+j].name);
s->rep_len[off + j] = b->rep_len;
s->frag_gap[off + j] = b->frag_gap;
}
} else {
mm_map_frag(s->p->mi, s->n_seg[i], qlens, qseqs, quals, &s->n_reg[off], &s->reg[off], b, s->p->opt, s->seq[off].name);
mm_map_frag(s->p->mi, s->n_seg[i], qlens, qseqs, &s->n_reg[off], &s->reg[off], b, s->p->opt, s->seq[off].name);
for (j = 0; j < s->n_seg[i]; ++j) {
s->rep_len[off + j] = b->rep_len;
s->frag_gap[off + j] = b->frag_gap;
}
}
for (j = 0; j < s->n_seg[i]; ++j) // flip the query strand and coordinate to the original read strand
if (s->n_seg[i] == 2 && ((j == 0 && (pe_ori>>1&1)) || (j == 1 && (pe_ori&1)))) {
@@ -429,9 +452,63 @@ static void worker_for(void *_data, long i, int tid) // kt_for() callback
r->rev = !r->rev;
}
}
kfree(b->km, qlens);
kfree(b->km, qseqs);
kfree(b->km, quals);
}
static void merge_hits(step_t *s)
{
int f, i, k0, k, max_seg = 0, *n_reg_part, *rep_len_part, *frag_gap_part, *qlens;
void *km;
FILE **fp = s->p->fp_parts;
const mm_mapopt_t *opt = s->p->opt;
km = km_init();
for (f = 0; f < s->n_frag; ++f)
max_seg = max_seg > s->n_seg[f]? max_seg : s->n_seg[f];
qlens = CALLOC(int, max_seg + s->p->n_parts * 3);
n_reg_part = qlens + max_seg;
rep_len_part = n_reg_part + s->p->n_parts;
frag_gap_part = rep_len_part + s->p->n_parts;
for (f = 0, k = k0 = 0; f < s->n_frag; ++f) {
k0 = k;
for (i = 0; i < s->n_seg[f]; ++i, ++k) {
int j, l, t, rep_len = 0;
qlens[i] = s->seq[k].l_seq;
for (j = 0, s->n_reg[k] = 0; j < s->p->n_parts; ++j) {
mm_err_fread(&n_reg_part[j], sizeof(int), 1, fp[j]);
mm_err_fread(&rep_len_part[j], sizeof(int), 1, fp[j]);
mm_err_fread(&frag_gap_part[j], sizeof(int), 1, fp[j]);
s->n_reg[k] += n_reg_part[j];
if (rep_len < rep_len_part[j])
rep_len = rep_len_part[j];
}
s->reg[k] = CALLOC(mm_reg1_t, s->n_reg[k]);
for (j = 0, l = 0; j < s->p->n_parts; ++j) {
for (t = 0; t < n_reg_part[j]; ++t, ++l) {
mm_reg1_t *r = &s->reg[k][l];
uint32_t capacity;
mm_err_fread(r, sizeof(mm_reg1_t), 1, fp[j]);
r->rid += s->p->rid_shift[j];
if (opt->flag & MM_F_CIGAR) {
mm_err_fread(&capacity, 4, 1, fp[j]);
r->p = (mm_extra_t*)calloc(capacity, 4);
r->p->capacity = capacity;
mm_err_fread(r->p, r->p->capacity, 4, fp[j]);
}
}
}
mm_hit_sort(km, &s->n_reg[k], s->reg[k]);
mm_set_parent(km, opt->mask_level, s->n_reg[k], s->reg[k], opt->a * 2 + opt->b);
if (!(opt->flag & MM_F_ALL_CHAINS)) {
mm_select_sub(km, opt->pri_ratio, s->p->mi->k*2, opt->best_n, &s->n_reg[k], s->reg[k]);
mm_set_sam_pri(s->n_reg[k], s->reg[k]);
}
mm_set_mapq(km, s->n_reg[k], s->reg[k], opt->min_chain_score, opt->a, rep_len, !!(opt->flag & MM_F_SR));
}
if (s->n_seg[f] == 2 && opt->pe_ori >= 0 && (opt->flag&MM_F_CIGAR))
mm_pair(km, frag_gap_part[0], opt->pe_bonus, opt->a * 2 + opt->b, opt->a, qlens, &s->n_reg[k0], &s->reg[k0]);
}
free(qlens);
km_destroy(km);
}
static void *worker_pipeline(void *shared, int step, void *in)
@@ -440,11 +517,12 @@ static void *worker_pipeline(void *shared, int step, void *in)
pipeline_t *p = (pipeline_t*)shared;
if (step == 0) { // step 0: read sequences
int with_qual = (!!(p->opt->flag & MM_F_OUT_SAM) && !(p->opt->flag & MM_F_NO_QUAL));
int with_comment = !!(p->opt->flag & MM_F_COPY_COMMENT);
int frag_mode = (p->n_fp > 1 || !!(p->opt->flag & MM_F_FRAG_MODE));
step_t *s;
s = (step_t*)calloc(1, sizeof(step_t));
if (p->n_fp > 1) s->seq = mm_bseq_read_frag(p->n_fp, p->fp, p->mini_batch_size, with_qual, &s->n_seq);
else s->seq = mm_bseq_read2(p->fp[0], p->mini_batch_size, with_qual, frag_mode, &s->n_seq);
if (p->n_fp > 1) s->seq = mm_bseq_read_frag2(p->n_fp, p->fp, p->mini_batch_size, with_qual, with_comment, &s->n_seq);
else s->seq = mm_bseq_read3(p->fp[0], p->mini_batch_size, with_qual, with_comment, frag_mode, &s->n_seq);
if (s->seq) {
s->p = p;
for (i = 0; i < s->n_seq; ++i)
@@ -452,9 +530,11 @@ static void *worker_pipeline(void *shared, int step, void *in)
s->buf = (mm_tbuf_t**)calloc(p->n_threads, sizeof(mm_tbuf_t*));
for (i = 0; i < p->n_threads; ++i)
s->buf[i] = mm_tbuf_init();
s->n_reg = (int*)calloc(3 * s->n_seq, sizeof(int));
s->seg_off = s->n_reg + s->n_seq; // seg_off and n_seg are allocated together with n_reg
s->n_reg = (int*)calloc(5 * s->n_seq, sizeof(int));
s->seg_off = s->n_reg + s->n_seq; // seg_off, n_seg, rep_len and frag_gap are allocated together with n_reg
s->n_seg = s->seg_off + s->n_seq;
s->rep_len = s->n_seg + s->n_seq;
s->frag_gap = s->rep_len + s->n_seq;
s->reg = (mm_reg1_t**)calloc(s->n_seq, sizeof(mm_reg1_t*));
for (i = 1, j = 0; i <= s->n_seq; ++i)
if (i == s->n_seq || !frag_mode || !mm_qname_same(s->seq[i-1].name, s->seq[i].name)) {
@@ -465,7 +545,8 @@ static void *worker_pipeline(void *shared, int step, void *in)
return s;
} else free(s);
} else if (step == 1) { // step 1: map
kt_for(p->n_threads, worker_for, in, ((step_t*)in)->n_frag);
if (p->n_parts > 0) merge_hits((step_t*)in);
else kt_for(p->n_threads, worker_for, in, ((step_t*)in)->n_frag);
return in;
} else if (step == 2) { // step 2: output
void *km = 0;
@@ -478,20 +559,36 @@ static void *worker_pipeline(void *shared, int step, void *in)
int seg_st = s->seg_off[k], seg_en = s->seg_off[k] + s->n_seg[k];
for (i = seg_st; i < seg_en; ++i) {
mm_bseq1_t *t = &s->seq[i];
for (j = 0; j < s->n_reg[i]; ++j) {
mm_reg1_t *r = &s->reg[i][j];
assert(!r->sam_pri || r->id == r->parent);
if ((p->opt->flag & MM_F_NO_PRINT_2ND) && r->id != r->parent)
continue;
if (p->opt->split_prefix && p->n_parts == 0) { // then write to temporary files
mm_err_fwrite(&s->n_reg[i], sizeof(int), 1, p->fp_split);
mm_err_fwrite(&s->rep_len[i], sizeof(int), 1, p->fp_split);
mm_err_fwrite(&s->frag_gap[i], sizeof(int), 1, p->fp_split);
for (j = 0; j < s->n_reg[i]; ++j) {
mm_reg1_t *r = &s->reg[i][j];
mm_err_fwrite(r, sizeof(mm_reg1_t), 1, p->fp_split);
if (p->opt->flag & MM_F_CIGAR) {
mm_err_fwrite(&r->p->capacity, 4, 1, p->fp_split);
mm_err_fwrite(r->p, r->p->capacity, 4, p->fp_split);
}
}
} else if (s->n_reg[i] > 0) { // the query has at least one hit
for (j = 0; j < s->n_reg[i]; ++j) {
mm_reg1_t *r = &s->reg[i][j];
assert(!r->sam_pri || r->id == r->parent);
if ((p->opt->flag & MM_F_NO_PRINT_2ND) && r->id != r->parent)
continue;
if (p->opt->flag & MM_F_OUT_SAM)
mm_write_sam2(&p->str, mi, t, i - seg_st, j, s->n_seg[k], &s->n_reg[seg_st], (const mm_reg1_t*const*)&s->reg[seg_st], km, p->opt->flag);
else
mm_write_paf(&p->str, mi, t, r, km, p->opt->flag);
mm_err_puts(p->str.s);
}
} else if (p->opt->flag & (MM_F_OUT_SAM|MM_F_PAF_NO_HIT)) { // output an empty hit, if requested
if (p->opt->flag & MM_F_OUT_SAM)
mm_write_sam2(&p->str, mi, t, i - seg_st, j, s->n_seg[k], &s->n_reg[seg_st], (const mm_reg1_t*const*)&s->reg[seg_st], km, p->opt->flag);
mm_write_sam2(&p->str, mi, t, i - seg_st, -1, s->n_seg[k], &s->n_reg[seg_st], (const mm_reg1_t*const*)&s->reg[seg_st], km, p->opt->flag);
else
mm_write_paf(&p->str, mi, t, r, km, p->opt->flag);
puts(p->str.s);
}
if (s->n_reg[i] == 0 && (p->opt->flag & MM_F_OUT_SAM)) {
mm_write_sam2(&p->str, mi, t, i - seg_st, -1, s->n_seg[k], &s->n_reg[seg_st], (const mm_reg1_t*const*)&s->reg[seg_st], km, p->opt->flag);
puts(p->str.s);
mm_write_paf(&p->str, mi, t, 0, 0, p->opt->flag);
mm_err_puts(p->str.s);
}
}
for (i = seg_st; i < seg_en; ++i) {
@@ -501,7 +598,7 @@ static void *worker_pipeline(void *shared, int step, void *in)
if (s->seq[i].qual) free(s->seq[i].qual);
}
}
free(s->reg); free(s->n_reg); free(s->seq); // seg_off and n_seg were allocated with reg; no memory leak here
free(s->reg); free(s->n_reg); free(s->seq); // seg_off, n_seg, rep_len and frag_gap were allocated with reg; no memory leak here
km_destroy(km);
if (mm_verbose >= 3)
fprintf(stderr, "[M::%s::%.3f*%.2f] mapped %d sequences\n", __func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0), s->n_seq);
@@ -510,32 +607,44 @@ static void *worker_pipeline(void *shared, int step, void *in)
return 0;
}
static mm_bseq_file_t **open_bseqs(int n, const char **fn)
{
mm_bseq_file_t **fp;
int i, j;
fp = (mm_bseq_file_t**)calloc(n, sizeof(mm_bseq_file_t*));
for (i = 0; i < n; ++i) {
if ((fp[i] = mm_bseq_open(fn[i])) == 0) {
if (mm_verbose >= 1)
fprintf(stderr, "ERROR: failed to open file '%s'\n", fn[i]);
for (j = 0; j < i; ++j)
mm_bseq_close(fp[j]);
free(fp);
return 0;
}
}
return fp;
}
int mm_map_file_frag(const mm_idx_t *idx, int n_segs, const char **fn, const mm_mapopt_t *opt, int n_threads)
{
int i, j, pl_threads;
int i, pl_threads;
pipeline_t pl;
if (n_segs < 1) return -1;
memset(&pl, 0, sizeof(pipeline_t));
pl.n_fp = n_segs;
pl.fp = (mm_bseq_file_t**)calloc(n_segs, sizeof(mm_bseq_file_t*));
for (i = 0; i < n_segs; ++i) {
pl.fp[i] = mm_bseq_open(fn[i]);
if (pl.fp[i] == 0) {
if (mm_verbose >= 1)
fprintf(stderr, "ERROR: failed to open file '%s'\n", fn[i]);
for (j = 0; j < i; ++j)
mm_bseq_close(pl.fp[j]);
free(pl.fp);
return -1;
}
}
pl.fp = open_bseqs(pl.n_fp, fn);
if (pl.fp == 0) return -1;
pl.opt = opt, pl.mi = idx;
pl.n_threads = n_threads > 1? n_threads : 1;
pl.mini_batch_size = opt->mini_batch_size;
if (opt->split_prefix)
pl.fp_split = mm_split_init(opt->split_prefix, idx);
pl_threads = n_threads == 1? 1 : (opt->flag&MM_F_2_IO_THREADS)? 3 : 2;
kt_pipeline(pl_threads, worker_pipeline, &pl, 3);
free(pl.str.s);
for (i = 0; i < n_segs; ++i)
if (pl.fp_split) fclose(pl.fp_split);
for (i = 0; i < pl.n_fp; ++i)
mm_bseq_close(pl.fp[i]);
free(pl.fp);
return 0;
@@ -545,3 +654,48 @@ int mm_map_file(const mm_idx_t *idx, const char *fn, const mm_mapopt_t *opt, int
{
return mm_map_file_frag(idx, 1, &fn, opt, n_threads);
}
int mm_split_merge(int n_segs, const char **fn, const mm_mapopt_t *opt, int n_split_idx)
{
int i;
pipeline_t pl;
mm_idx_t *mi;
if (n_segs < 1 || n_split_idx < 1) return -1;
memset(&pl, 0, sizeof(pipeline_t));
pl.n_fp = n_segs;
pl.fp = open_bseqs(pl.n_fp, fn);
if (pl.fp == 0) return -1;
pl.opt = opt;
pl.mini_batch_size = opt->mini_batch_size;
pl.n_parts = n_split_idx;
pl.fp_parts = CALLOC(FILE*, pl.n_parts);
pl.rid_shift = CALLOC(uint32_t, pl.n_parts);
pl.mi = mi = mm_split_merge_prep(opt->split_prefix, n_split_idx, pl.fp_parts, pl.rid_shift);
if (pl.mi == 0) {
free(pl.fp_parts);
free(pl.rid_shift);
return -1;
}
for (i = n_split_idx - 1; i > 0; --i)
pl.rid_shift[i] = pl.rid_shift[i - 1];
for (pl.rid_shift[0] = 0, i = 1; i < n_split_idx; ++i)
pl.rid_shift[i] += pl.rid_shift[i - 1];
if (opt->flag & MM_F_OUT_SAM)
for (i = 0; i < pl.mi->n_seq; ++i)
printf("@SQ\tSN:%s\tLN:%d\n", pl.mi->seq[i].name, pl.mi->seq[i].len);
kt_pipeline(2, worker_pipeline, &pl, 3);
free(pl.str.s);
mm_idx_destroy(mi);
free(pl.rid_shift);
for (i = 0; i < n_split_idx; ++i)
fclose(pl.fp_parts[i]);
free(pl.fp_parts);
for (i = 0; i < pl.n_fp; ++i)
mm_bseq_close(pl.fp[i]);
free(pl.fp);
mm_split_rm_tmp(opt->split_prefix, n_split_idx);
return 0;
}
+104 -29
View File
@@ -5,26 +5,38 @@
#include <stdio.h>
#include <sys/types.h>
#define MM_F_NO_SELF 0x001
#define MM_F_AVA 0x002
#define MM_F_CIGAR 0x004
#define MM_F_OUT_SAM 0x008
#define MM_F_NO_QUAL 0x010
#define MM_F_OUT_CG 0x020
#define MM_F_OUT_CS 0x040
#define MM_F_SPLICE 0x080 // splice mode
#define MM_F_SPLICE_FOR 0x100 // match GT-AG
#define MM_F_SPLICE_REV 0x200 // match CT-AC, the reverse complement of GT-AG
#define MM_F_NO_LJOIN 0x400
#define MM_F_OUT_CS_LONG 0x800
#define MM_F_SR 0x1000
#define MM_F_FRAG_MODE 0x2000
#define MM_F_NO_DIAG 0x001 // no exact diagonal hit
#define MM_F_NO_DUAL 0x002 // skip pairs where query name is lexicographically larger than target name
#define MM_F_CIGAR 0x004
#define MM_F_OUT_SAM 0x008
#define MM_F_NO_QUAL 0x010
#define MM_F_OUT_CG 0x020
#define MM_F_OUT_CS 0x040
#define MM_F_SPLICE 0x080 // splice mode
#define MM_F_SPLICE_FOR 0x100 // match GT-AG
#define MM_F_SPLICE_REV 0x200 // match CT-AC, the reverse complement of GT-AG
#define MM_F_NO_LJOIN 0x400
#define MM_F_OUT_CS_LONG 0x800
#define MM_F_SR 0x1000
#define MM_F_FRAG_MODE 0x2000
#define MM_F_NO_PRINT_2ND 0x4000
#define MM_F_2_IO_THREADS 0x8000
#define MM_F_LONG_CIGAR 0x10000
#define MM_F_INDEPEND_SEG 0x20000
#define MM_F_SPLICE_FLANK 0x40000
#define MM_F_SOFTCLIP 0x80000
#define MM_F_FOR_ONLY 0x100000
#define MM_F_REV_ONLY 0x200000
#define MM_F_HEAP_SORT 0x400000
#define MM_F_ALL_CHAINS 0x800000
#define MM_F_OUT_MD 0x1000000
#define MM_F_COPY_COMMENT 0x2000000
#define MM_F_EQX 0x4000000 // use =/X instead of M
#define MM_F_PAF_NO_HIT 0x8000000 // output unmapped reads to PAF
#define MM_I_HPC 0x1
#define MM_I_NO_SEQ 0x2
#define MM_I_NO_NAME 0x4
#define MM_IDX_MAGIC "MMI\2"
@@ -46,12 +58,13 @@ typedef struct {
} mm_idx_seq_t;
typedef struct {
int32_t b, w, k, is_hpc;
int32_t b, w, k, flag;
uint32_t n_seq; // number of reference sequences
int32_t index;
mm_idx_seq_t *seq; // sequence name, length and offset
uint32_t *S; // 4-bit packed sequence
struct mm_idx_bucket_s *B; // index (hidden)
void *km;
void *km, *h;
} mm_idx_t;
// minimap2 alignment
@@ -64,23 +77,25 @@ typedef struct {
} mm_extra_t;
typedef struct {
int32_t id; // ID for internal uses (see also parent below)
uint32_t cnt:28, rev:1, seg_split:1, sam_pri:1, proper_frag:1; // number of minimizers; if on the reverse strand
uint32_t rid:31, inv:1; // reference index; if this is an alignment from inversion rescue
int32_t score; // DP alignment score
int32_t qs, qe, rs, re; // query start and end; reference start and end
int32_t parent, subsc; // parent==id if primary; best alternate mapping score
int32_t as; // offset in the a[] array (for internal uses only)
int32_t mlen, blen; // seeded exact match length; seeded alignment block length
uint32_t mapq:8, split:2, n_sub:22; // mapQ; split pattern; number of suboptimal mappings
uint32_t pe_thru:1, score0:31;
int32_t id; // ID for internal uses (see also parent below)
int32_t cnt; // number of minimizers; if on the reverse strand
int32_t rid; // reference index; if this is an alignment from inversion rescue
int32_t score; // DP alignment score
int32_t qs, qe, rs, re; // query start and end; reference start and end
int32_t parent, subsc; // parent==id if primary; best alternate mapping score
int32_t as; // offset in the a[] array (for internal uses only)
int32_t mlen, blen; // seeded exact match length; seeded alignment block length
int32_t n_sub; // number of suboptimal mappings
int32_t score0; // initial chaining score (before chain merging/spliting)
uint32_t mapq:8, split:2, rev:1, inv:1, sam_pri:1, proper_frag:1, pe_thru:1, seg_split:1, seg_id:8, split_inv:1, dummy:7;
uint32_t hash;
float div;
mm_extra_t *p;
} mm_reg1_t;
// indexing and mapping options
typedef struct {
short k, w, is_hpc, bucket_bits;
short k, w, flag, bucket_bits;
int mini_batch_size;
uint64_t batch_size;
} mm_idxopt_t;
@@ -103,20 +118,27 @@ typedef struct {
int max_join_long, max_join_short;
int min_join_flank_sc;
float min_join_flank_ratio;
int a, b, q, e, q2, e2; // matching score, mismatch, gap-open and gap-ext penalties
int sc_ambi; // score when one or both bases are "N"
int noncan; // cost of non-canonical splicing sites
int zdrop; // break alignment if alignment score drops too fast along the diagonal
int zdrop, zdrop_inv; // break alignment if alignment score drops too fast along the diagonal
int end_bonus;
int min_dp_max; // drop an alignment if the score of the max scoring segment is below this threshold
int min_ksw_len;
int anchor_ext_len, anchor_ext_shift;
float max_clip_ratio; // drop an alignment if BOTH ends are clipped above this ratio
int pe_ori, pe_bonus;
float mid_occ_frac; // only used by mm_mapopt_update(); see below
int32_t min_mid_occ;
int32_t mid_occ; // ignore seeds with occurrences above this threshold
int32_t max_occ;
int mini_batch_size; // size of a batch of query bases to process in parallel
const char *split_prefix;
} mm_mapopt_t;
// index reader
@@ -148,6 +170,7 @@ extern double mm_realtime0; // wall-clock timer
* @return 0 if success; -1 if _present_ unknown
*/
int mm_set_opt(const char *preset, mm_idxopt_t *io, mm_mapopt_t *mo);
int mm_check_opt(const mm_idxopt_t *io, const mm_mapopt_t *mo);
/**
* Update mm_mapopt_t::mid_occ via mm_mapopt_t::mid_occ_frac
@@ -200,6 +223,51 @@ void mm_idx_reader_close(mm_idx_reader_t *r);
int mm_idx_reader_eof(const mm_idx_reader_t *r);
/**
* Check whether the file contains a minimap2 index
*
* @param fn file name
*
* @return the file size if fn is an index file; 0 if fn is not.
*/
int64_t mm_idx_is_idx(const char *fn);
/**
* Load a part of an index
*
* Given a uni-part index, this function loads the entire index into memory.
* Given a multi-part index, it loads one part only and places the file pointer
* at the end of that part.
*
* @param fp pointer to FILE object
*
* @return minimap2 index read from fp
*/
mm_idx_t *mm_idx_load(FILE *fp);
/**
* Append an index (or one part of a full index) to file
*
* @param fp pointer to FILE object
* @param mi minimap2 index
*/
void mm_idx_dump(FILE *fp, const mm_idx_t *mi);
/**
* Create an index from strings in memory
*
* @param w minimizer window size
* @param k minimizer k-mer size
* @param is_hpc use HPC k-mer if true
* @param bucket_bits number of bits for the first level of the hash table
* @param n number of sequences
* @param seq sequences in A/C/G/T
* @param name sequence names; could be NULL
*
* @return minimap2 index
*/
mm_idx_t *mm_idx_str(int w, int k, int is_hpc, int bucket_bits, int n, const char **seq, const char **name);
/**
* Print index statistics to stderr
*
@@ -253,6 +321,8 @@ void mm_tbuf_destroy(mm_tbuf_t *b);
*/
mm_reg1_t *mm_map(const mm_idx_t *mi, int l_seq, const char *seq, int *n_regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *name);
void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **seqs, int *n_regs, mm_reg1_t **regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *qname);
/**
* Align a fasta/fastq file and print alignments to stdout
*
@@ -267,9 +337,14 @@ int mm_map_file(const mm_idx_t *idx, const char *fn, const mm_mapopt_t *opt, int
int mm_map_file_frag(const mm_idx_t *idx, int n_segs, const char **fn, const mm_mapopt_t *opt, int n_threads);
// query sequence name and sequence in the minimap2 index
int mm_idx_index_name(mm_idx_t *mi);
int mm_idx_name2id(const mm_idx_t *mi, const char *name);
int mm_idx_getseq(const mm_idx_t *mi, uint32_t rid, uint32_t st, uint32_t en, uint8_t *seq);
// deprecated APIs for backward compatibility
void mm_mapopt_init(mm_mapopt_t *opt);
mm_idx_t *mm_idx_build(const char *fn, int w, int k, int is_hpc, int n_threads);
mm_idx_t *mm_idx_build(const char *fn, int w, int k, int flag, int n_threads);
#ifdef __cplusplus
}
+145 -31
View File
@@ -1,4 +1,4 @@
.TH minimap2 1 "11 November 2017" "minimap2-2.5 (r572)" "Bioinformatics tools"
.TH minimap2 1 "20 June 2018" "minimap2-2.11 (r797)" "Bioinformatics tools"
.SH NAME
.PP
minimap2 - mapping and alignment between collections of DNA sequences
@@ -99,6 +99,14 @@ multiple times to map it against each batch of target sequences.
may be ending with k/K/m/M/g/G. NB: mapping quality is incorrect given a
multi-part index.
.TP
.B --idx-no-seq
Don't store target sequences in the index. It saves disk space and memory but
the index generated with this option will not work with
.B -a
or
.BR -c .
When base-level alignment is not requested, this option is automatically applied.
.TP
.BI -d \ FILE
Save the minimizer index of
.I target.fa
@@ -115,13 +123,30 @@ provided as the target sequences, options
will be effectively overridden by the options stored in the index file.
.SS Mapping options
.TP 10
.BI -f \ FLOAT
Ignore top
.BI -f \ FLOAT | INT1 [, INT2 ]
If fraction, ignore top
.I FLOAT
fraction of most frequent minimizers [0.0002]
fraction of most frequent minimizers [0.0002]. If integer,
ignore minimizers occuring more than
.I INT1
times.
.I INT2
is only effective in the
.B --sr
or
.B -xsr
mode, which sets the threshold for a second round of seeding.
.TP
.BI --min-occ-floor \ INT
Force minimap2 to always use k-mers occurring
.I INT
times or less [0]. In effect, the max occurrence threshold is set to
the
.RI max{ INT ,
.BR -f }.
.TP
.BI -g \ INT
Stop chain enlongation if there are no minimizers in
Stop chain enlongation if there are no minimizers within
.IR INT -bp
[10000].
.TP
@@ -140,11 +165,30 @@ Discard chains with chaining score
[40]. Chaining score equals the approximate number of matching bases minus a
concave gap penalty. It is computed with dynamic programming.
.TP
.B -D
If query sequence name/length are identical to the target name/length, ignore
diagonal anchors. This option also reduces DP-based extension along the
diagonal.
.TP
.B -P
Retain all chains and don't attempt to set primary chains. Options
.B -p
and
.B -N
have no effect when this option is in use.
.TP
.BR --dual = yes | no
If
.BR no ,
skip query-target pairs wherein the query name is lexicographically greater
than the target name [yes]
.TP
.B -X
Perform all-vs-all mapping. In this mode, if the query sequence name is
lexicographically larger than the target sequence name, the hits between them
will be suppressed; if the query sequence name is the same as the target name,
diagonal minimizer hits will also be suppressed.
Equivalent to
.RB ' -DP
.BR --dual = no
.BR --no-long-join '.
Primarily used for all-vs-all read overlapping.
.TP
.BI -p \ FLOAT
Minimal secondary-to-primary score ratio to output secondary mappings [0.8].
@@ -154,6 +198,9 @@ the chain with a lower score is secondary to the chain with a higher score.
If the ratio of the scores is below
.IR FLOAT ,
the secondary chain will not be outputted or extended with DP alignment later.
This option has no effect when
.B -X
is applied.
.TP
.BI -N \ INT
Output at most
@@ -171,9 +218,14 @@ Increasing this option slows down spliced alignment. [200k]
.TP
.BI -F \ NUM
Maximum fragment length (aka insert size; effective with
.BR -xsr / --frag)
.BR -xsr / --frag = yes )
[800]
.TP
.BI -M \ FLOAT
Mark as secondary a chain that overlaps with a better chain by
.I FLOAT
or more of the shorter chain [0.5]
.TP
.BI --max-chain-skip \ INT
A heuristics that stops chaining early [50]. Minimap2 uses dynamic programming
for chaining. The time complexity is quadratic in the number of seeds. This
@@ -187,6 +239,10 @@ Disable the long gap patching heuristic. When this option is applied, the
maximum alignment gap is mostly controlled by
.BR -r .
.TP
.BI --lj-min-ratio \ FLOAT
Fraction of query sequence length required to bridge a long gap [0.5]. A
smaller value helps to recover longer gaps, at the cost of more false gaps.
.TP
.B --splice
Enable the splice alignment mode.
.TP
@@ -196,8 +252,23 @@ applies a second round of chaining with a higher minimizer occurrence threshold
if no good chain is found. In addition, minimap2 attempts to patch gaps between
seeds with ungapped alignment.
.TP
.BR --frag [= no | yes ]
.BI --split-prefix \ STR
Prefix to create temporary files. Typically used for a multi-part index.
.TP
.BR --frag = no | yes
Whether to enable the fragment mode [no]
.TP
.B --for-only
Only map to the forward strand of the reference sequences. For paired-end
reads in the forward-reverse orientation, the first read is mapped to forward
strand of the reference and the second read to the reverse stand.
.TP
.B --rev-only
Only map to the reverse complement strand of the reference sequences.
.TP
.BR --heap-sort = no | yes
If yes, sort anchors with heap merge, instead of radix sort. Heap merge is
faster for short reads, but slower for long reads. [no]
.SS Alignment options
.TP 10
.BI -A \ INT
@@ -224,11 +295,22 @@ Cost for a non-canonical GT-AG splicing (effective with
.BR --splice )
[0]
.TP
.BI -z \ INT
Break an alignment if the running score drops too quickly along the diagonal of
the DP matrix (diagonal X-drop, or Z-drop) [400]. Increasing the value improves
the contiguity of the alignment at the cost of poor alignment in the middle
(e.g. caused by a long inversion).
.BI -z \ INT1[,INT2]
Truncate an alignment if the running alignment score drops too quickly along
the diagonal of the DP matrix (diagonal X-drop, or Z-drop) [400,200]. If the
drop of score is above
.IR INT2 ,
minimap2 will reverse complement the query in the related region and align
again to test small inversions. Minimap2 truncates alignment if there is an
inversion or the drop of score is greater than
.IR INT1 .
Decrease
.I INT2
to find small inversions at the cost of performance and false positives.
Increase
.I INT1
to improves the contiguity of alignment at the cost of poor alignment in the
middle.
.TP
.BI -s \ INT
Minimal peak DP alignment score to output [40]. The peak score is computed from
@@ -247,14 +329,16 @@ no attempt to match GT-AG [n]
.BI --end-bonus \ INT
Score bonus when alignment extends to the end of the query sequence [0].
.TP
.BR --splice-flank [= yes | no ]
.BI --score-N \ INT
Score of a mismatch involving ambiguous bases [1].
.TP
.BR --splice-flank = yes | no
Assume the next base to a
.B GT
donor site tends to be A/G (91% in human and 92% in mouse) and the preceding
base to a
.B AG
acceptor tends to be C/T [yes with
.BR --splice ].
acceptor tends to be C/T [no].
This trend is evolutionarily conservative, all the way to S. cerevisiae
(PMID:18688272). Specifying this option generally leads to higher junction
accuracy by several percents, so it is applied by default with
@@ -264,6 +348,18 @@ However, the SIRV control does not honor this trend
on SIRV data, please add
.B --splice-flank=no
to the command line.
.TP
.BI --end-seed-pen \ INT
Drop a terminal anchor if
.IR s <log( g )+ INT ,
where
.I s
is the local alignment score around the anchor and
.I g
the length of the terminal gap in the chain. This option is only effective
with
.BR --splice .
It helps to avoid tiny terminal exons. [6]
.SS Input/output options
.TP 10
.B -a
@@ -284,6 +380,9 @@ SAM read group line in a format like
.B @RG\\\\tID:foo\\\\tSM:bar
[].
.TP
.B -y
Copy input FASTA/Q comments to output.
.TP
.B -c
Generate CIGAR. In PAF, the CIGAR is written to the `cg' custom tag.
.TP
@@ -302,6 +401,12 @@ is given,
.I short
is assumed. [none]
.TP
.B --MD
Output the MD tag (see the SAM spec).
.TP
.B --eqx
Output =/X CIGAR operators for sequence match/mismatch.
.TP
.B -Y
In SAM output, use soft clipping for supplementary alignments.
.TP
@@ -332,7 +437,7 @@ K/M/G/k/m/g suffix is accepted. A large
helps load balancing in the multi-threading mode, at the cost of increased
memory.
.TP
.BR --secondary [= yes | no ]
.BR --secondary = yes | no
Whether to output secondary alignments [yes]
.TP
.B --version
@@ -364,28 +469,35 @@ is determined by the sequencing error mode.
.B asm5
Long assembly to reference mapping
.RB ( -k19
.B -w19 -A1 -B19 -O39,81 -E3,1 -s200
.BR -z200 ).
.B -w19 -A1 -B19 -O39,81 -E3,1 -s200 -z200
.BR --min-occ-floor=100 ).
Typically, the alignment will not extend to regions with 5% or higher sequence
divergence. Only use this preset if the average divergence is far below 5%.
.TP
.B asm10
Long assembly to reference mapping
.RB ( -k19
.B -w19 -A1 -B9 -O16,41 -E2,1 -s200
.BR -z200 ).
.B -w19 -A1 -B9 -O16,41 -E2,1 -s200 -z200
.BR --min-occ-floor=100 ).
Up to 10% sequence divergence.
.TP
.B asm20
Long assembly to reference mapping
.RB ( -k19
.B -w10 -A1 -B6 -O6,26 -E2,1 -s200 -z200
.BR --min-occ-floor=100 ).
Up to 20% sequence divergence.
.TP
.B ava-pb
PacBio all-vs-all overlap mapping
.RB ( -Hk19
.B -w5 -Xp0 -m100 -g10000 --max-chain-skip
.B -Xw5 -m100 -g10000 --max-chain-skip
.BR 25 ).
.TP
.B ava-ont
Oxford Nanopore all-vs-all overlap mapping
.RB ( -k15
.B -w5 -Xp0 -m100 -g10000 --max-chain-skip
.B -Xw5 -m100 -g10000 -r2000 --max-chain-skip
.BR 25 ).
Similarly, the major difference from
.B ava-pb
@@ -407,8 +519,8 @@ tag ignores introns to demote hits to pseudogenes.
.B sr
Short single-end reads without splicing
.RB ( -k21
.B -w11 --sr --frag -A2 -B8 -O12,32 -E2,1 -r50 -p.5 -N20 -f1000,5000 -n2 -m20
.B -s40 -g200 -2K50m
.B -w11 --sr --frag=yes -A2 -B8 -O12,32 -E2,1 -r50 -p.5 -N20 -f1000,5000 -n2 -m20
.B -s40 -g200 -2K50m --heap-sort=yes
.BR --secondary=no ).
.RE
.SS Miscellaneous options
@@ -461,17 +573,19 @@ cb | cb | cb
r | c | l .
Tag Type Description
_
tp A Type of aln: P/primary, S/secondary and I/inversion
tp A Type of aln: P/primary, S/secondary and I,i/inversion
cm i Number of minimizers on the chain
s1 i Chaining score
s2 i Chaining score of the best secondary chain
NM i Total number of mismatches and gaps in the alignment
MD Z To generate the ref sequence in the alignment
AS i DP alignment score
ms i DP score of the max scoring segment in the alignment
nn i Number of ambiguous bases in the alignment
ts A Transcript strand (splice mode only)
cg Z CIGAR string (only in PAF)
cs Z Difference string
dv f Approximate per-base sequence divergence
.TE
.PP
@@ -502,8 +616,8 @@ where seed positions may be suboptimal. This should not be a big concern
because even the optimal alignment may be wrong in such regions.
.TP
*
Minimap2 requires SSE2 instructions to compile. It is possible to add
non-SSE2 support, but it would make minimap2 slower by several times.
Minimap2 requires SSE2 or NEON instructions to compile. It is possible to add
non-SSE2/NEON support, but it would make minimap2 slower by several times.
.SH SEE ALSO
.PP
miniasm(1), minimap(1), bwa(1).
+46 -1
View File
@@ -1,4 +1,5 @@
#include "minimap.h"
#include <stdlib.h>
#include "mmpriv.h"
int mm_verbose = 1;
int mm_dbg_flag = 0;
@@ -86,6 +87,8 @@ double cputime()
return kernelModeTime + userModeTime;
}
long peakrss(void) { return 0; }
#else
#include <sys/resource.h>
#include <sys/time.h>
@@ -96,6 +99,18 @@ double cputime(void)
getrusage(RUSAGE_SELF, &r);
return r.ru_utime.tv_sec + r.ru_stime.tv_sec + 1e-6 * (r.ru_utime.tv_usec + r.ru_stime.tv_usec);
}
long peakrss(void)
{
struct rusage r;
getrusage(RUSAGE_SELF, &r);
#ifdef __linux__
return r.ru_maxrss * 1024;
#else
return r.ru_maxrss;
#endif
}
#endif /* WIN32 || _WIN32 */
double realtime(void)
@@ -106,6 +121,36 @@ double realtime(void)
return tp.tv_sec + tp.tv_usec * 1e-6;
}
void mm_err_puts(const char *str)
{
int ret;
ret = puts(str);
if (ret == EOF) {
fprintf(stderr, "[ERROR] failed to write the results\n");
exit(EXIT_FAILURE);
}
}
void mm_err_fwrite(const void *p, size_t size, size_t nitems, FILE *fp)
{
int ret;
ret = fwrite(p, size, nitems, fp);
if (ret == EOF) {
fprintf(stderr, "[ERROR] failed to write data\n");
exit(EXIT_FAILURE);
}
}
void mm_err_fread(void *p, size_t size, size_t nitems, FILE *fp)
{
int ret;
ret = fread(p, size, nitems, fp);
if (ret == EOF) {
fprintf(stderr, "[ERROR] failed to read data\n");
exit(EXIT_FAILURE);
}
}
#include "ksort.h"
#define sort_key_128x(a) ((a).x)
+170 -19
View File
@@ -1,28 +1,179 @@
The [K8 Javascript shell][k8] is needed to run Javascripts in this directory.
Precompiled k8 binaries for Mac and Linux can be found at the [K8 release
page][k8bin].
## <a name="started"></a>Getting Started
* [paf2aln.js](paf2aln.js): convert PAF to [MAF][maf] or BLAST-like output for
eyeballing. PAF has to be generated with minimap2 option `-S`, which writes
the aligned sequences to the `cs` tag. An example:
```sh
../minimap2 -S ../test/MT-*.fa | k8 paf2aln.js /dev/stdin
```
```sh
# install minimap2
git clone https://github.com/lh3/minimap2
cd minimap2 && make
# install the k8 javascript shell
curl -L https://github.com/attractivechaos/k8/releases/download/v0.2.4/k8-0.2.4.tar.bz2 | tar -jxf -
cp k8-0.2.4/k8-`uname -s` k8 # or copy it to a directory on your $PATH
# export PATH="$PATH:`pwd`:`pwd`/misc" # run this if k8, minimap2 or paftools.js not on your $PATH
minimap2 --cs test/MT-human.fa test/MT-orang.fa | paftools.js view - # view alignment
minimap2 -c test/MT-human.fa test/MT-orang.fa | paftools.js stat - # basic alignment statistics
minimap2 -c --cs test/MT-human.fa test/MT-orang.fa \
| sort -k6,6 -k8,8n | paftools.js call -L15000 - # calling variants from asm-to-ref alignment
minimap2 -c test/MT-human.fa test/MT-orang.fa \
| paftools.js liftover -l10000 - <(echo -e "MT_orang\t2000\t5000") # liftOver
# no test data for the following examples
paftools.js junceval -e anno.gtf splice.sam > out.txt # compare splice junctions to annotations
paftools.js splice2bed anno.gtf > anno.bed # convert GTF/GFF3 to BED12
```
* [mapstat.js](mapstat.js): output basic statistics such as the number of
non-redundant mapped bases, number of split and secondary alignments and
number of long gaps. This scripts seamlessly works with both SAM and PAF.
## Table of Contents
* [sim-pbsim.js](sim-pbsim.js): convert reads simulated with [PBSIM][pbsim] to
FASTA and encode the true mapping positions to read names in a format like
`S1_33!chr1!225258409!225267761!-`.
- [Getting Started](#started)
- [Introduction](#intro)
- [Evaluation](#eval)
- [Evaluating mapping accuracy with simulated reads](#mapeval)
- [Evaluating read overlap sensitivity](#oveval)
- [Calling Variants from Assemblies](#asmvar)
* [sim-eval.js](sim-eval.js): evaluate mapping accuracy for FASTA generated
with [sim-pbsim.js](sim-pbsim.js) or [sim-mason2.js](sim-mason2.js).
## <a name="intro"></a>Introduction
* [sam2paf.js](sam2paf.js): convert SAM to PAF.
paftools.js is a script that processes alignments in the [PAF format][paf],
such as converting between formats, evaluating mapping accuracy, lifting over
BED files based on alignment, and calling variants from assembly-to-assembly
alignment. This script *requires* the [k8 Javascript shell][k8] to run. On
Linux or Mac, you can download the precompiled k8 binary with:
```sh
curl -L https://github.com/attractivechaos/k8/releases/download/v0.2.4/k8-0.2.4.tar.bz2 | tar -jxf -
cp k8-0.2.4/k8-`uname -s` $HOME/bin/k8 # assuming $HOME/bin in your $PATH
```
It is highly recommended to copy the executable `k8` to a directory on your
`$PATH` such as `/usr/bin/env` can find it. Like python scripts, once you
install `k8`, you can launch paftools.js in one of the two ways:
```sh
path/to/paftools.js # only if k8 is on your $PATH
k8 path/to/paftools.js
```
In a nutshell, paftools.js has the following commands:
```
Usage: paftools.js <command> [arguments]
Commands:
view convert PAF to BLAST-like (for eyeballing) or MAF
splice2bed convert spliced alignment in PAF/SAM to BED12
sam2paf convert SAM to PAF
delta2paf convert MUMmer's delta to PAF
gff2bed convert GTF/GFF3 to BED12
stat collect basic mapping information in PAF/SAM
liftover simplistic liftOver
call call variants from asm-to-ref alignment with the cs tag
bedcov compute the number of bases covered
mapeval evaluate mapping accuracy using mason2/PBSIM-simulated FASTQ
mason2fq convert mason2-simulated SAM to FASTQ
pbsim2fq convert PBSIM-simulated MAF to FASTQ
junceval evaluate splice junction consistency with known annotations
ov-eval evaluate read overlap sensitivity using read-to-ref mapping
```
paftools.js seamlessly reads both plain text files and gzip'd text files.
## <a name="eval"></a>Evaluation
### <a name="mapeval"></a>Evaluating mapping accuracy with simulated reads
The **pbsim2fq** command of paftools.js converts the MAF output of [pbsim][pbsim]
to FASTQ and encodes the true mapping position in the read name in a format like
`S1_33!chr1!225258409!225267761!-`. Similarly, the **mason2fq** command
converts [mason2][mason2] simulated SAM to FASTQ.
Command **mapeval** evaluates mapped SAM/PAF. Here is example output:
```
Q 60 32478 0 0.000000000 32478
Q 22 16 1 0.000030775 32494
Q 21 43 1 0.000061468 32537
Q 19 73 1 0.000091996 32610
Q 14 66 1 0.000122414 32676
Q 10 27 3 0.000214048 32703
Q 8 14 1 0.000244521 32717
Q 7 13 2 0.000305530 32730
Q 6 46 1 0.000335611 32776
Q 3 10 1 0.000366010 32786
Q 2 20 2 0.000426751 32806
Q 1 248 94 0.003267381 33054
Q 0 31 17 0.003778147 33085
U 3
```
where each Q-line gives the quality threshold, the number of reads mapped with
mapping quality equal to or greater than the threshold, number of wrong
mappings, accumulative mapping error rate and the accumulative number of
mapped reads. The U-line, if present, gives the number of unmapped reads if
they are present in the SAM file.
Suppose the reported mapping coordinate overlap with the true coordinate like
the following:
```
truth: --------------------
mapper: ----------------------
|<- l1 ->|<-- o -->|<-- l2 -->|
```
Let `r=o/(l1+o+l2)`. The reported mapping is considered correct if `r>0.1` by
default.
### <a name="oveval"></a>Evaluating read overlap sensitivity
Command **ov-eval** takes *sorted* read-to-reference alignment and read
overlaps in PAF as input, and evaluates the sensitivity. For example:
```sh
minimap2 -cx map-pb ref.fa reads.fq.gz | sort -k6,6 -k8,8n > reads-to-ref.paf
minimap2 -x ava-pb reads.fq.gz reads.fq.gz > ovlp.paf
k8 ov-eval.js reads-to-ref.paf ovlp.paf
```
## <a name="asmvar"></a>Calling Variants from Haploid Assemblies
The **call** command of paftools.js calls variants from coordinate-sorted
assembly-to-reference alignment. It calls variants from the [cs tag][cs] and
identifies confident/callable regions as those covered by exactly one contig.
Here are example command lines:
```sh
minimap2 -cx asm5 -t8 --cs ref.fa asm.fa > asm.paf # keeping this file is recommended; --cs required!
sort -k6,6 -k8,8n asm.paf > asm.srt.paf # sort by reference start coordinate
k8 paftools.js call asm.srt.paf > asm.var.txt
```
Here is sample output:
```
V chr1 2276040 2276041 1 60 c g LJII01000171.1 1217409 1217410 +
V chr1 2280409 2280410 1 60 a g LJII01000171.1 1221778 1221779 +
V chr1 2280504 2280505 1 60 a g LJII01000171.1 1221873 1221874 +
R chr1 2325140 2436340
V chr1 2325287 2325287 1 60 - ct LJII01000171.1 1272894 1272896 +
V chr1 2325642 2325644 1 60 tt - LJII01000171.1 1273251 1273251 +
V chr1 2326051 2326052 1 60 c t LJII01000171.1 1273658 1273659 +
V chr1 2326287 2326288 1 60 c t LJII01000171.1 1273894 1273895 +
```
where a line starting with `R` gives regions covered by one query contig, and a
V-line encodes a variant in the following format: chr, start, end, query depth,
mapping quality, REF allele, ALT allele, query name, query start, end and the
query orientation. Generally, you should only look at variants where column 5
is one.
By default, when calling variants, "paftools.js call" ignores alignments 50kb
or shorter; when deriving callable regions, it ignores alignments 10kb or
shorter. It uses two thresholds to avoid edge effects. These defaults are
designed for long-read assemblies. For short reads, both should be reduced.
[paf]: https://github.com/lh3/miniasm/blob/master/PAF.md
[cs]: https://github.com/lh3/minimap2#cs
[k8]: https://github.com/attractivechaos/k8
[k8bin]: https://github.com/attractivechaos/k8/releases
[maf]: https://genome.ucsc.edu/FAQ/FAQformat#format5
[pbsim]: https://github.com/pfaucon/PBSIM-PacBio-Simulator
[mason2]: https://github.com/seqan/seqan/tree/master/apps/mason2
-266
View File
@@ -1,266 +0,0 @@
/*******************************
* Command line option parsing *
*******************************/
var getopt = function(args, ostr) {
var oli; // option letter list index
if (typeof(getopt.place) == 'undefined')
getopt.ind = 0, getopt.arg = null, getopt.place = -1;
if (getopt.place == -1) { // update scanning pointer
if (getopt.ind >= args.length || args[getopt.ind].charAt(getopt.place = 0) != '-') {
getopt.place = -1;
return null;
}
if (getopt.place + 1 < args[getopt.ind].length && args[getopt.ind].charAt(++getopt.place) == '-') { // found "--"
++getopt.ind;
getopt.place = -1;
return null;
}
}
var optopt = args[getopt.ind].charAt(getopt.place++); // character checked for validity
if (optopt == ':' || (oli = ostr.indexOf(optopt)) < 0) {
if (optopt == '-') return null; // if the user didn't specify '-' as an option, assume it means null.
if (getopt.place < 0) ++getopt.ind;
return '?';
}
if (oli+1 >= ostr.length || ostr.charAt(++oli) != ':') { // don't need argument
getopt.arg = null;
if (getopt.place < 0 || getopt.place >= args[getopt.ind].length) ++getopt.ind, getopt.place = -1;
} else { // need an argument
if (getopt.place >= 0 && getopt.place < args[getopt.ind].length)
getopt.arg = args[getopt.ind].substr(getopt.place);
else if (args.length <= ++getopt.ind) { // no arg
getopt.place = -1;
if (ostr.length > 0 && ostr.charAt(0) == ':') return ':';
return '?';
} else getopt.arg = args[getopt.ind]; // white space
getopt.place = -1;
++getopt.ind;
}
return optopt;
}
/***********************
* Interval operations *
***********************/
Interval = {};
Interval.sort = function(a)
{
if (typeof a[0] == 'number')
a.sort(function(x, y) { return x - y });
else a.sort(function(x, y) { return x[0] != y[0]? x[0] - y[0] : x[1] - y[1] });
}
Interval.merge = function(a, sorted)
{
if (typeof sorted == 'undefined') sorted = true;
if (!sorted) Interval.sort(a);
var k = 0;
for (var i = 1; i < a.length; ++i) {
if (a[k][1] >= a[i][0])
a[k][1] = a[k][1] > a[i][1]? a[k][1] : a[i][1];
else a[++k] = a[i].slice(0);
}
a.length = k + 1;
}
Interval.index_end = function(a, sorted)
{
if (a.length == 0) return;
if (typeof sorted == 'undefined') sorted = true;
if (!sorted) Interval.sort(a);
a[0].push(0);
var k = 0, k_en = a[0][1];
for (var i = 1; i < a.length; ++i) {
if (k_en <= a[i][0]) {
for (++k; k < i; ++k)
if (a[k][1] > a[i][0])
break;
k_en = a[k][1];
}
a[i].push(k);
}
}
Interval.find_intv = function(a, x)
{
var left = -1, right = a.length;
if (typeof a[0] == 'number') {
while (right - left > 1) {
var mid = left + ((right - left) >> 1);
if (a[mid] > x) right = mid;
else if (a[mid] < x) left = mid;
else return mid;
}
} else {
while (right - left > 1) {
var mid = left + ((right - left) >> 1);
if (a[mid][0] > x) right = mid;
else if (a[mid][0] < x) left = mid;
else return mid;
}
}
return left;
}
Interval.find_ovlp = function(a, st, en)
{
if (a.length == 0 || st >= en) return [];
var l = Interval.find_intv(a, st);
var k = l < 0? 0 : a[l][a[l].length - 1];
var b = [];
for (var i = k; i < a.length; ++i) {
if (a[i][0] >= en) break;
else if (st < a[i][1])
b.push(a[i]);
}
return b;
}
/*****************
* Main function *
*****************/
var c, l_fuzzy = 0, print_ovlp = false, print_err_only = false, first_only = false;
while ((c = getopt(arguments, "l:ep")) != null) {
if (c == 'l') l_fuzzy = parseInt(getopt.arg);
else if (c == 'e') print_err_only = print_ovlp = true;
else if (c == 'p') print_ovlp = true;
}
if (arguments.length - getopt.ind < 2) {
print("Usage: k8 intron-eval.js [options] <gene.gtf> <aln.sam>");
exit(1);
}
var file, buf = new Bytes();
var tr = {};
file = new File(arguments[getopt.ind]);
while (file.readline(buf) >= 0) {
var m, t = buf.toString().split("\t");
if (t[0].charAt(0) == '#') continue;
if (t[2] != 'exon') continue;
var st = parseInt(t[3]) - 1;
var en = parseInt(t[4]);
if ((m = /transcript_id "(\S+)"/.exec(t[8])) == null) continue;
var tid = m[1];
if (tr[tid] == null) tr[tid] = [t[0], t[6], 0, 0, []];
tr[tid][4].push([st, en]);
}
file.close();
var anno = {};
for (var tid in tr) {
var t = tr[tid];
Interval.sort(t[4]);
t[2] = t[4][0][0];
t[3] = t[4][t[4].length - 1][1];
if (anno[t[0]] == null) anno[t[0]] = [];
var s = t[4];
for (var i = 0; i < s.length - 1; ++i) {
if (s[i][1] >= s[i+1][0]) throw Error("ERROR: wrong annotation!");
anno[t[0]].push([s[i][1], s[i+1][0]]);
}
}
tr = null;
for (var chr in anno) {
var e = anno[chr];
if (e.length == 0) continue;
Interval.sort(e);
var k = 0;
for (var i = 1; i < e.length; ++i) // dedup
if (e[i][0] != e[k][0] || e[i][1] != e[k][1])
e[++k] = e[i].slice(0);
e.length = k + 1;
Interval.index_end(e);
}
var n_pri = 0, n_unmapped = 0, n_mapped = 0;
var n_sgl = 0, n_splice = 0, n_splice_hit = 0, n_splice_novel = 0;
file = new File(arguments[getopt.ind+1]);
var last_qname = null;
var re_cigar = /(\d+)([MIDNSHX=])/g;
while (file.readline(buf) >= 0) {
var m, t = buf.toString().split("\t");
if (t[0].charAt(0) == '@') continue;
var flag = parseInt(t[1]);
if (flag&0x100) continue;
if (first_only && last_qname == t[0]) continue;
if (t[2] == '*') {
++n_unmapped;
continue;
} else {
++n_pri;
if (last_qname != t[0]) ++n_mapped;
}
var pos = parseInt(t[3]) - 1, intron = [];
while ((m = re_cigar.exec(t[5])) != null) {
var len = parseInt(m[1]), op = m[2];
if (op == 'N') {
intron.push([pos, pos + len]);
pos += len;
} else if (op == 'M' || op == 'X' || op == '=' || op == 'D') pos += len;
}
if (intron.length == 0) {
++n_sgl;
continue;
}
n_splice += intron.length;
var chr = anno[t[2]];
if (chr != null) {
for (var i = 0; i < intron.length; ++i) {
var o = Interval.find_ovlp(chr, intron[i][0], intron[i][1]);
if (o.length > 0) {
var hit = false;
for (var j = 0; j < o.length; ++j) {
var st_diff = intron[i][0] - o[j][0];
var en_diff = intron[i][1] - o[j][1];
if (st_diff < 0) st_diff = -st_diff;
if (en_diff < 0) en_diff = -en_diff;
if (st_diff <= l_fuzzy && en_diff <= l_fuzzy)
++n_splice_hit, hit = true;
if (hit) break;
}
if (print_ovlp) {
var type = hit? 'C' : 'P';
if (hit && print_err_only) continue;
var x = '[';
for (var j = 0; j < o.length; ++j) {
if (j) x += ', ';
x += '(' + o[j][0] + "," + o[j][1] + ')';
}
x += ']';
print(type, t[0], i+1, t[2], intron[i][0], intron[i][1], x);
}
} else {
++n_splice_novel;
if (print_ovlp)
print('N', t[0], i+1, t[2], intron[i][0], intron[i][1]);
}
}
} else {
n_splice_novel += intron.length;
}
last_qname = t[0];
}
file.close();
buf.destroy();
if (!print_ovlp) {
print("# unmapped reads: " + n_unmapped);
print("# mapped reads: " + n_mapped);
print("# primary alignments: " + n_pri);
print("# singletons: " + n_sgl);
print("# predicted introns: " + n_splice);
print("# non-overlapping introns: " + n_splice_novel);
print("# correct introns: " + n_splice_hit + " (" + (n_splice_hit / n_splice * 100).toFixed(2) + "%)");
}
-183
View File
@@ -1,183 +0,0 @@
var getopt = function(args, ostr) {
var oli; // option letter list index
if (typeof(getopt.place) == 'undefined')
getopt.ind = 0, getopt.arg = null, getopt.place = -1;
if (getopt.place == -1) { // update scanning pointer
if (getopt.ind >= args.length || args[getopt.ind].charAt(getopt.place = 0) != '-') {
getopt.place = -1;
return null;
}
if (getopt.place + 1 < args[getopt.ind].length && args[getopt.ind].charAt(++getopt.place) == '-') { // found "--"
++getopt.ind;
getopt.place = -1;
return null;
}
}
var optopt = args[getopt.ind].charAt(getopt.place++); // character checked for validity
if (optopt == ':' || (oli = ostr.indexOf(optopt)) < 0) {
if (optopt == '-') return null; // if the user didn't specify '-' as an option, assume it means null.
if (getopt.place < 0) ++getopt.ind;
return '?';
}
if (oli+1 >= ostr.length || ostr.charAt(++oli) != ':') { // don't need argument
getopt.arg = null;
if (getopt.place < 0 || getopt.place >= args[getopt.ind].length) ++getopt.ind, getopt.place = -1;
} else { // need an argument
if (getopt.place >= 0 && getopt.place < args[getopt.ind].length)
getopt.arg = args[getopt.ind].substr(getopt.place);
else if (args.length <= ++getopt.ind) { // no arg
getopt.place = -1;
if (ostr.length > 0 && ostr.charAt(0) == ':') return ':';
return '?';
} else getopt.arg = args[getopt.ind]; // white space
getopt.place = -1;
++getopt.ind;
}
return optopt;
}
var c, gap_out_len = null;
while ((c = getopt(arguments, "l:")) != null)
if (c == 'l') gap_out_len = parseInt(getopt.arg);
if (getopt.ind == arguments.length) {
print("Usage: k8 mapstat.js [-l gapOutLen] <in.sam>|<in.paf>");
exit(1);
}
var buf = new Bytes();
var file = new File(arguments[getopt.ind]);
var re = /(\d+)([MIDSHNX=])/g;
var lineno = 0, n_pri = 0, n_2nd = 0, n_seq = 0, n_cigar_64k = 0, l_tot = 0, l_cov = 0;
var n_gap = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]];
function cov_len(regs)
{
regs.sort(function(a,b) {return a[0]-b[0]});
var st = regs[0][0], en = regs[0][1], l = 0;
for (var i = 1; i < regs.length; ++i) {
if (regs[i][0] < en)
en = en > regs[i][1]? en : regs[i][1];
else l += en - st, st = regs[i][0], en = regs[i][1];
}
l += en - st;
return l;
}
var last = null, last_qlen = null, regs = [];
while (file.readline(buf) >= 0) {
var line = buf.toString();
++lineno;
if (line.charAt(0) != '@') {
var t = line.split("\t", 12);
var m, rs, cigar = null, is_pri = false, is_sam = false, is_rev = false, tname = null;
var atlen = null, aqlen, qs, qe, mapq, ori_qlen;
if (t[4] == '+' || t[4] == '-') { // PAF
if (!/\ts2:i:\d+/.test(line)) {
++n_2nd;
continue;
}
if ((m = /\tcg:Z:(\S+)/.exec(line)) != null)
cigar = m[1];
if (cigar == null) {
warn("WARNING: no CIGAR at line " + lineno);
continue;
}
tname = t[5];
qs = parseInt(t[2]), qe = parseInt(t[3]);
aqlen = qe - qs;
is_rev = t[4] == '+'? false : true;
rs = parseInt(t[7]);
atlen = parseInt(t[8]) - rs;
mapq = parseInt(t[11]);
ori_qlen = parseInt(t[1]);
} else { // SAM
var flag = parseInt(t[1]);
if ((flag & 4) || t[2] == '*' || t[5] == '*') continue;
if (flag & 0x100) {
++n_2nd;
continue;
}
cigar = t[5];
tname = t[2];
rs = parseInt(t[3]) - 1;
mapq = parseInt(t[4]);
aqlen = t[9].length;
is_sam = true;
is_rev = !!(flag&0x10);
}
++n_pri;
if (last != t[0]) {
if (last != null) {
l_tot += last_qlen;
l_cov += cov_len(regs);
}
regs = [];
++n_seq, last = t[0];
}
var M = 0, tl = 0, ql = 0, clip = [0, 0], n_cigar = 0, sclip = 0;
while ((m = re.exec(cigar)) != null) {
var l = parseInt(m[1]);
++n_cigar;
if (m[2] == 'M' || m[2] == '=' || m[2] == 'X') {
tl += l, ql += l, M += l;
} else if (m[2] == 'I' || m[2] == 'D') {
var type;
if (l < 50) type = 0;
else if (l < 100) type = 1;
else if (l < 300) type = 2;
else if (l < 400) type = 3;
else if (l < 1000) type = 4;
else type = 5;
if (m[2] == 'I') ql += l, ++n_gap[0][type];
else tl += l, ++n_gap[1][type];
if (gap_out_len != null && l >= gap_out_len)
print(t[0], ql, is_rev? '-' : '+', tname, rs + tl, m[2], l);
} else if (m[2] == 'N') {
tl += l;
} else if (m[2] == 'S') {
clip[M == 0? 0 : 1] = l, sclip += l;
} else if (m[2] == 'H') {
clip[M == 0? 0 : 1] = l;
}
}
if (n_cigar > 65535) ++n_cigar_64k;
if (ql + sclip != aqlen)
warn("WARNING: aligned query length is inconsistent with CIGAR at line " + lineno + " (" + (ql+sclip) + " != " + aqlen + ")");
if (atlen != null && atlen != tl)
warn("WARNING: aligned reference length is inconsistent with CIGAR at line " + lineno);
if (is_sam) {
qs = clip[is_rev? 1 : 0], qe = qs + ql;
ori_qlen = clip[0] + ql + clip[1];
}
regs.push([qs, qe]);
last_qlen = ori_qlen;
}
}
l_tot += last_qlen;
l_cov += cov_len(regs);
file.close();
buf.destroy();
if (gap_out_len == null) {
print("Number of mapped sequences: " + n_seq);
print("Number of primary alignments: " + n_pri);
print("Number of secondary alignments: " + n_2nd);
print("Number of primary alignments with >65535 CIGAR operations: " + n_cigar_64k);
print("Number of bases in mapped sequences: " + l_tot);
print("Number of mapped bases: " + l_cov);
print("Number of insertions in [0,50): " + n_gap[0][0]);
print("Number of insertions in [50,100): " + n_gap[0][1]);
print("Number of insertions in [100,300): " + n_gap[0][2]);
print("Number of insertions in [300,400): " + n_gap[0][3]);
print("Number of insertions in [400,1000): " + n_gap[0][4]);
print("Number of insertions in [1000,inf): " + n_gap[0][5]);
print("Number of deletions in [0,50): " + n_gap[1][0]);
print("Number of deletions in [50,100): " + n_gap[1][1]);
print("Number of deletions in [100,300): " + n_gap[1][2]);
print("Number of deletions in [300,400): " + n_gap[1][3]);
print("Number of deletions in [400,1000): " + n_gap[1][4]);
print("Number of deletions in [1000,inf): " + n_gap[1][5]);
}
-171
View File
@@ -1,171 +0,0 @@
var getopt = function(args, ostr) {
var oli; // option letter list index
if (typeof(getopt.place) == 'undefined')
getopt.ind = 0, getopt.arg = null, getopt.place = -1;
if (getopt.place == -1) { // update scanning pointer
if (getopt.ind >= args.length || args[getopt.ind].charAt(getopt.place = 0) != '-') {
getopt.place = -1;
return null;
}
if (getopt.place + 1 < args[getopt.ind].length && args[getopt.ind].charAt(++getopt.place) == '-') { // found "--"
++getopt.ind;
getopt.place = -1;
return null;
}
}
var optopt = args[getopt.ind].charAt(getopt.place++); // character checked for validity
if (optopt == ':' || (oli = ostr.indexOf(optopt)) < 0) {
if (optopt == '-') return null; // if the user didn't specify '-' as an option, assume it means null.
if (getopt.place < 0) ++getopt.ind;
return '?';
}
if (oli+1 >= ostr.length || ostr.charAt(++oli) != ':') { // don't need argument
getopt.arg = null;
if (getopt.place < 0 || getopt.place >= args[getopt.ind].length) ++getopt.ind, getopt.place = -1;
} else { // need an argument
if (getopt.place >= 0 && getopt.place < args[getopt.ind].length)
getopt.arg = args[getopt.ind].substr(getopt.place);
else if (args.length <= ++getopt.ind) { // no arg
getopt.place = -1;
if (ostr.length > 0 && ostr.charAt(0) == ':') return ':';
return '?';
} else getopt.arg = args[getopt.ind]; // white space
getopt.place = -1;
++getopt.ind;
}
return optopt;
}
var c, maf_out = false, line_len = 80;
while ((c = getopt(arguments, "ml:")) != null) {
if (c == 'm') maf_out = true;
else if (c == 'l') line_len = parseInt(getopt.arg); // TODO: not implemented yet
}
if (line_len == 0) line_len = 0x7fffffff;
if (getopt.ind == arguments.length) {
print("Usage: k8 paf2aln.js [options] <with-cs.paf>");
print("Options:");
print(" -m MAF output (BLAST-like output by default)");
print(" -l INT line length in BLAST-like output [80]");
print("");
print("Note: this script only works when minimap2 is run with option '-S'");
exit(1);
}
function padding_str(x, len, right)
{
var s = x.toString();
if (s.length < len) {
if (right) s += Array(len - s.length + 1).join(" ");
else s = Array(len - s.length + 1).join(" ") + s;
}
return s;
}
function update_aln(s_ref, s_qry, s_mid, type, seq, slen)
{
var l = type == '*'? 1 : seq.length;
if (type == '=') {
s_ref.set(seq);
s_qry.set(seq);
s_mid.set(Array(l+1).join("|"));
slen[0] += l, slen[1] += l;
} else if (type == '*') {
s_ref.set(seq.charAt(0));
s_qry.set(seq.charAt(1));
s_mid.set(' ');
slen[0] += 1, slen[1] += 1;
} else if (type == '+') {
s_ref.set(Array(l+1).join("-"));
s_qry.set(seq);
s_mid.set(Array(l+1).join(" "));
slen[1] += l;
} else if (type == '-') {
s_ref.set(seq);
s_qry.set(Array(l+1).join("-"));
s_mid.set(Array(l+1).join(" "));
slen[0] += l;
}
}
function print_aln(rs, qs, strand, slen, elen, s_ref, s_qry, s_mid)
{
print(["Ref+:", padding_str(rs + slen[0] + 1, 10, false), s_ref.toString(), padding_str(rs + elen[0], 10, true)].join(" "));
print(" " + s_mid.toString());
var st, en;
if (strand == '+') st = qs + slen[1] + 1, en = qs + elen[1];
else st = qs - slen[1], en = qs - elen[1] + 1;
print(["Qry" + strand + ":", padding_str(st, 10, false), s_qry.toString(), padding_str(en , 10, true)].join(" "));
}
var s_ref = new Bytes(), s_qry = new Bytes(), s_mid = new Bytes();
var re = /([=\-\+\*])([A-Za-z]+)/g;
var buf = new Bytes();
var file = new File(arguments[getopt.ind]);
if (maf_out) print("##maf version=1\n");
while (file.readline(buf) >= 0) {
var m, line = buf.toString();
var t = line.split("\t", 12);
if ((m = /\tcs:Z:(\S+)/.exec(line)) == null) continue;
var cs = m[1];
s_ref.length = s_qry.length = s_mid.length = 0;
var slen = [0, 0], elen = [0, 0];
if (maf_out) {
while ((m = re.exec(cs)) != null)
update_aln(s_ref, s_qry, s_mid, m[1], m[2], elen);
if (maf_out) {
var score = (m = /\tAS:i:(\d+)/.exec(line)) != null? parseInt(m[1]) : 0;
var len = t[0].length > t[5].length? t[0].length : t[5].length;
print("a " + score);
print(["s", padding_str(t[5], len, true), padding_str(t[7], 10, false), padding_str(parseInt(t[8]) - parseInt(t[7]), 10, false),
"+", padding_str(t[6], 10, false), s_ref.toString()].join(" "));
var qs, qe, ql = parseInt(t[1]);
if (t[4] == '+') {
qs = parseInt(t[2]);
qe = parseInt(t[3]);
} else {
qs = ql - parseInt(t[3]);
qe = ql - parseInt(t[2]);
}
print(["s", padding_str(t[0], len, true), padding_str(qs, 10, false), padding_str(qe - qs, 10, false),
t[4], padding_str(ql, 10, false), s_qry.toString()].join(" "));
print("");
}
} else {
line = line.replace(/\tc[sg]:Z:\S+/g, "");
print('>' + line);
var rs = parseInt(t[7]), qs = t[4] == '+'? parseInt(t[2]) : parseInt(t[3]);
var n_blocks = 0;
while ((m = re.exec(cs)) != null) {
var start = 0, rest = m[1] == '*'? 1 : m[2].length;
while (rest > 0) {
var l_proc;
if (s_ref.length + rest >= line_len) {
l_proc = line_len - s_ref.length;
update_aln(s_ref, s_qry, s_mid, m[1], m[1] == '*'? m[2] : m[2].substr(start, l_proc), elen);
if (n_blocks > 0) print("");
print_aln(rs, qs, t[4], slen, elen, s_ref, s_qry, s_mid);
++n_blocks;
s_ref.length = s_qry.length = s_mid.length = 0;
slen[0] = elen[0], slen[1] = elen[1];
} else {
l_proc = rest;
update_aln(s_ref, s_qry, s_mid, m[1], m[1] == '*'? m[2] : m[2].substr(start, l_proc), elen);
}
rest -= l_proc, start += l_proc;
}
}
if (s_ref.length > 0) {
if (n_blocks > 0) print("");
print_aln(rs, qs, t[4], slen, elen, s_ref, s_qry, s_mid);
++n_blocks;
}
print("//");
}
}
file.close();
buf.destroy();
s_ref.destroy(); s_qry.destroy(); s_mid.destroy();
+2223
View File
File diff suppressed because it is too large Load Diff
-114
View File
@@ -1,114 +0,0 @@
var getopt = function(args, ostr) {
var oli; // option letter list index
if (typeof(getopt.place) == 'undefined')
getopt.ind = 0, getopt.arg = null, getopt.place = -1;
if (getopt.place == -1) { // update scanning pointer
if (getopt.ind >= args.length || args[getopt.ind].charAt(getopt.place = 0) != '-') {
getopt.place = -1;
return null;
}
if (getopt.place + 1 < args[getopt.ind].length && args[getopt.ind].charAt(++getopt.place) == '-') { // found "--"
++getopt.ind;
getopt.place = -1;
return null;
}
}
var optopt = args[getopt.ind].charAt(getopt.place++); // character checked for validity
if (optopt == ':' || (oli = ostr.indexOf(optopt)) < 0) {
if (optopt == '-') return null; // if the user didn't specify '-' as an option, assume it means null.
if (getopt.place < 0) ++getopt.ind;
return '?';
}
if (oli+1 >= ostr.length || ostr.charAt(++oli) != ':') { // don't need argument
getopt.arg = null;
if (getopt.place < 0 || getopt.place >= args[getopt.ind].length) ++getopt.ind, getopt.place = -1;
} else { // need an argument
if (getopt.place >= 0 && getopt.place < args[getopt.ind].length)
getopt.arg = args[getopt.ind].substr(getopt.place);
else if (args.length <= ++getopt.ind) { // no arg
getopt.place = -1;
if (ostr.length > 0 && ostr.charAt(0) == ':') return ':';
return '?';
} else getopt.arg = args[getopt.ind]; // white space
getopt.place = -1;
++getopt.ind;
}
return optopt;
}
var c, pri_only = false;
while ((c = getopt(arguments, "p")) != null)
if (c == 'p') pri_only = true;
var file = arguments.length == getopt.ind? new File() : new File(arguments[getopt.ind]);
var buf = new Bytes();
var re = /(\d+)([MIDSHNX=])/g;
var len = {}, lineno = 0;
while (file.readline(buf) >= 0) {
var m, n_cigar = 0, line = buf.toString();
++lineno;
if (line.charAt(0) == '@') {
if (/^@SQ/.test(line)) {
var name = (m = /\tSN:(\S+)/.exec(line)) != null? m[1] : null;
var l = (m = /\tLN:(\d+)/.exec(line)) != null? parseInt(m[1]) : null;
if (name != null && l != null) len[name] = l;
}
continue;
}
var t = line.split("\t");
var flag = parseInt(t[1]);
if (t[9] != '*' && t[10] != '*' && t[9].length != t[10].length) throw Error("ERROR at line " + lineno + ": inconsistent SEQ and QUAL lengths - " + t[9].length + " != " + t[10].length);
if (t[2] == '*' || (flag&4)) continue;
if (pri_only && (flag&0x100)) continue;
var tlen = len[t[2]];
if (tlen == null) throw Error("ERROR at line " + lineno + ": can't find the length of contig " + t[2]);
var nn = (m = /\tnn:i:(\d+)/.exec(line)) != null? parseInt(m[1]) : 0;
var NM = (m = /\tNM:i:(\d+)/.exec(line)) != null? parseInt(m[1]) : null;
var have_NM = NM == null? false : true;
NM += nn;
var clip = [0, 0], I = [0, 0], D = [0, 0], M = 0, N = 0, ql = 0, tl = 0, mm = 0, ext_cigar = false;
while ((m = re.exec(t[5])) != null) {
var l = parseInt(m[1]);
if (m[2] == 'M') M += l, ql += l, tl += l, ext_cigar = false;
else if (m[2] == 'I') ++I[0], I[1] += l, ql += l;
else if (m[2] == 'D') ++D[0], D[1] += l, tl += l;
else if (m[2] == 'N') N += l, tl += l;
else if (m[2] == 'S') clip[M == 0? 0 : 1] = l, ql += l;
else if (m[2] == 'H') clip[M == 0? 0 : 1] = l;
else if (m[2] == '=') M += l, ql += l, tl += l, ext_cigar = true;
else if (m[2] == 'X') M += l, ql += l, tl += l, mm += l, ext_cigar = true;
++n_cigar;
}
if (n_cigar > 65535)
warn("WARNING at line " + lineno + ": " + n_cigar + " CIGAR operations");
if (tl + parseInt(t[3]) - 1 > tlen) {
warn("WARNING at line " + lineno + ": alignment end position larger than ref length; skipped");
continue;
}
if (t[9] != '*' && t[9].length != ql) {
warn("WARNING at line " + lineno + ": SEQ length inconsistent with CIGAR (" + t[9].length + " != " + ql + "); skipped");
continue;
}
if (!have_NM || ext_cigar) NM = I[1] + D[1] + mm;
if (NM < I[1] + D[1] + mm) {
warn("WARNING at line " + lineno + ": NM is less than the total number of gaps (" + NM + " < " + (I[1]+D[1]+mm) + ")");
NM = I[1] + D[1] + mm;
}
var extra = ["mm:i:"+(NM-I[1]-D[1]), "io:i:"+I[0], "in:i:"+I[1], "do:i:"+D[0], "dn:i:"+D[1]];
var match = M - (NM - I[1] - D[1]);
var blen = M + I[1] + D[1];
var qlen = M + I[1] + clip[0] + clip[1];
var qs, qe;
if (flag&16) qs = clip[1], qe = qlen - clip[0];
else qs = clip[0], qe = qlen - clip[1];
var ts = parseInt(t[3]) - 1, te = ts + M + D[1] + N;
var qname = t[0];
if ((flag&1) && (flag&0x40)) qname += '/1';
if ((flag&1) && (flag&0x80)) qname += '/2';
var a = [qname, qlen, qs, qe, flag&16? '-' : '+', t[2], tlen, ts, te, match, blen, t[4]];
print(a.join("\t"), extra.join("\t"));
}
buf.destroy();
file.close();
-193
View File
@@ -1,193 +0,0 @@
var getopt = function(args, ostr) {
var oli; // option letter list index
if (typeof(getopt.place) == 'undefined')
getopt.ind = 0, getopt.arg = null, getopt.place = -1;
if (getopt.place == -1) { // update scanning pointer
if (getopt.ind >= args.length || args[getopt.ind].charAt(getopt.place = 0) != '-') {
getopt.place = -1;
return null;
}
if (getopt.place + 1 < args[getopt.ind].length && args[getopt.ind].charAt(++getopt.place) == '-') { // found "--"
++getopt.ind;
getopt.place = -1;
return null;
}
}
var optopt = args[getopt.ind].charAt(getopt.place++); // character checked for validity
if (optopt == ':' || (oli = ostr.indexOf(optopt)) < 0) {
if (optopt == '-') return null; // if the user didn't specify '-' as an option, assume it means null.
if (getopt.place < 0) ++getopt.ind;
return '?';
}
if (oli+1 >= ostr.length || ostr.charAt(++oli) != ':') { // don't need argument
getopt.arg = null;
if (getopt.place < 0 || getopt.place >= args[getopt.ind].length) ++getopt.ind, getopt.place = -1;
} else { // need an argument
if (getopt.place >= 0 && getopt.place < args[getopt.ind].length)
getopt.arg = args[getopt.ind].substr(getopt.place);
else if (args.length <= ++getopt.ind) { // no arg
getopt.place = -1;
if (ostr.length > 0 && ostr.charAt(0) == ':') return ':';
return '?';
} else getopt.arg = args[getopt.ind]; // white space
getopt.place = -1;
++getopt.ind;
}
return optopt;
}
var c, max_mapq = 60, mode = 0, err_out_q = 256, print_err = false, ovlp_ratio = 0.1, cap_short_mapq = false;
while ((c = getopt(arguments, "Q:r:m:c")) != null) {
if (c == 'Q') err_out_q = parseInt(getopt.arg), print_err = true;
else if (c == 'r') ovlp_ratio = parseFloat(getopt.arg);
else if (c == 'm') mode = parseInt(getopt.arg);
else if (c == 'c') cap_short_mapq = true;
}
var file = arguments.length == getopt.ind? new File() : new File(arguments[getopt.ind]);
var buf = new Bytes();
var tot = [], err = [];
for (var q = 0; q <= max_mapq; ++q)
tot[q] = err[q] = 0;
function is_correct(s, b)
{
if (s[0] != b[0] || s[3] != b[3]) return false;
var o, l;
if (s[1] < b[1]) {
if (s[2] <= b[1]) return false;
o = (s[2] < b[2]? s[2] : b[2]) - b[1];
l = (s[2] > b[2]? s[2] : b[2]) - s[1];
} else {
if (b[2] <= s[1]) return false;
o = (s[2] < b[2]? s[2] : b[2]) - s[1];
l = (s[2] > b[2]? s[2] : b[2]) - b[1];
}
return o/l > ovlp_ratio? true : false;
}
function count_err(qname, a, tot, err, mode)
{
if (a.length == 0) return;
var m, s;
if ((m = /^(\S+)!(\S+)!(\d+)!(\d+)!([\+\-])$/.exec(qname)) != null) { // pbsim single-end reads
s = [m[1], m[2], parseInt(m[3]), parseInt(m[4]), m[5]];
} else if ((m = /^(\S+)!(\S+)!(\d+)_(\d+)!(\d+)_(\d+)!([\+\-])([\+\-])\/([12])$/.exec(qname)) != null) { // mason2 paired-end reads
if (m[9] == '1') {
s = [m[1], m[2], parseInt(m[3]), parseInt(m[5]), m[7]];
} else {
s = [m[1], m[2], parseInt(m[4]), parseInt(m[6]), m[8]];
}
} else throw Error("Failed to parse simulated read names '" + qname + "'");
s.shift(); // skip the orginal read name
if (mode == 0 || mode == 1) { // longest only or first only
var max_i = 0;
if (mode == 0) { // longest only
var max = 0;
for (var i = 0; i < a.length; ++i)
if (a[i][5] > max)
max = a[i][5], max_i = i;
}
var mapq = a[max_i][4];
++tot[mapq];
if (!is_correct(s, a[max_i])) {
if (mapq >= err_out_q)
print('E', qname, a[max_i].join("\t"));
++err[mapq];
}
} else if (mode == 2) { // all primary mode
var max_err_mapq = -1, max_mapq = 0, max_err_i = -1;
if (cap_short_mapq) {
var max = 0, max_q = 0;
for (var i = 0; i < a.length; ++i)
if (a[i][5] > max)
max = a[i][5], max_q = a[i][4];
for (var i = 0; i < a.length; ++i)
a[i][4] = max_q < a[i][4]? max_q : a[i][4];
}
for (var i = 0; i < a.length; ++i) {
max_mapq = max_mapq > a[i][4]? max_mapq : a[i][4];
if (!is_correct(s, a[i]))
if (a[i][4] > max_err_mapq)
max_err_mapq = a[i][4], max_err_i = i;
}
if (max_err_mapq >= 0) {
++tot[max_err_mapq], ++err[max_err_mapq];
if (max_err_mapq >= err_out_q)
print('E', qname, a[max_err_i].join("\t"));
} else ++tot[max_mapq];
}
}
var lineno = 0, last = null, a = [], n_unmapped = null;
var re_cigar = /(\d+)([MIDSHN])/g;
while (file.readline(buf) >= 0) {
var m, line = buf.toString();
++lineno;
if (line[0] != '@') {
var t = line.split("\t");
if (t[4] == '+' || t[4] == '-') { // PAF
if (last != t[0]) {
if (last != null) count_err(last, a, tot, err, mode);
a = [], last = t[0];
}
if (/\ts1:i:\d+/.test(line) && !/\ts2:i:\d+/.test(line)) // secondary alignment in minimap2 PAF
continue;
var mapq = parseInt(t[11]);
if (mapq > max_mapq) mapq = max_mapq;
a.push([t[5], parseInt(t[7]), parseInt(t[8]), t[4], mapq, parseInt(t[9])]);
} else { // SAM
var flag = parseInt(t[1]);
var read_no = flag>>6&0x3;
var qname = t[0];
if (!/\/[12]$/.test(qname))
qname = read_no == 1 || read_no == 2? t[0] + '/' + read_no : t[0];
if (last != qname) {
if (last != null) count_err(last, a, tot, err, mode);
a = [], last = qname;
}
if (flag&0x100) continue; // secondary alignment
if ((flag&0x4) || t[2] == '*') { // unmapped
if (n_unmapped == null) n_unmapped = 0;
++n_unmapped;
continue;
}
var mapq = parseInt(t[4]);
if (mapq > max_mapq) mapq = max_mapq;
var pos = parseInt(t[3]) - 1, pos_end = pos;
var n_gap = 0, mlen = 0;
while ((m = re_cigar.exec(t[5])) != null) {
var len = parseInt(m[1]);
if (m[2] == 'M') pos_end += len, mlen += len;
else if (m[2] == 'I') n_gap += len;
else if (m[2] == 'D') n_gap += len, pos_end += len;
}
var score = pos_end - pos;
if ((m = /\tNM:i:(\d+)/.exec(line)) != null) {
var NM = parseInt(m[1]);
if (NM >= n_gap) score = mlen - (NM - n_gap);
}
a.push([t[2], pos, pos_end, (flag&16)? '-' : '+', mapq, score]);
}
}
}
if (last != null) count_err(last, a, tot, err, mode);
buf.destroy();
file.close();
var sum_tot = 0, sum_err = 0, q_out = -1, sum_tot2 = 0, sum_err2 = 0;
for (var q = max_mapq; q >= 0; --q) {
if (tot[q] == 0) continue;
if (q_out < 0 || err[q] > 0) {
if (q_out >= 0) print('Q', q_out, sum_tot, sum_err, (sum_err2/sum_tot2).toFixed(9), sum_tot2);
sum_tot = sum_err = 0, q_out = q;
}
sum_tot += tot[q], sum_err += err[q];
sum_tot2 += tot[q], sum_err2 += err[q];
}
print('Q', q_out, sum_tot, sum_err, (sum_err2/sum_tot2).toFixed(9), sum_tot2);
if (n_unmapped != null) print('U', n_unmapped);
-105
View File
@@ -1,105 +0,0 @@
Bytes.prototype.reverse = function()
{
for (var i = 0; i < this.length>>1; ++i) {
var tmp = this[i];
this[i] = this[this.length - i - 1];
this[this.length - i - 1] = tmp;
}
}
// reverse complement a DNA string
Bytes.prototype.revcomp = function()
{
if (Bytes.rctab == null) {
var s1 = 'WSATUGCYRKMBDHVNwsatugcyrkmbdhvn';
var s2 = 'WSTAACGRYMKVHDBNwstaacgrymkvhdbn';
Bytes.rctab = [];
for (var i = 0; i < 256; ++i) Bytes.rctab[i] = 0;
for (var i = 0; i < s1.length; ++i)
Bytes.rctab[s1.charCodeAt(i)] = s2.charCodeAt(i);
}
for (var i = 0; i < this.length>>1; ++i) {
var tmp = this[this.length - i - 1];
this[this.length - i - 1] = Bytes.rctab[this[i]];
this[i] = Bytes.rctab[tmp];
}
if (this.length&1)
this[this.length>>1] = Bytes.rctab[this[this.length>>1]];
}
if (arguments.length == 0) {
print("Usage: k8 sim-mason2.js <mason.sam>");
exit(1);
}
function print_se(a)
{
print('@' + a.slice(0, 5).join("!") + " " + a[8]);
print(a[5]);
print("+");
print(a[6]);
}
var buf = new Bytes(), buf2 = new Bytes();
var file = new File(arguments[0]);
var re = /(\d+)([MIDSHN])/g;
var last = null;
while (file.readline(buf) >= 0) {
var t = buf.toString().split("\t");
if (t[0].charAt(0) == '@') continue;
var m, l_ref = 0;
while ((m = re.exec(t[5])) != null)
if (m[2] == 'D' || m[2] == 'M' || m[2] == 'N')
l_ref += parseInt(m[1]);
var flag = parseInt(t[1]);
var rev = !!(flag&16);
var seq, qual;
if (rev) {
buf2.length = 0;
buf2.set(t[9], 0);
buf2.revcomp();
seq = buf2.toString();
buf2.set(t[10], 0);
buf2.reverse();
qual = buf2.toString();
} else seq = t[9], qual = t[10];
var qname = t[0];
qname = qname.replace(/^simulated./, "");
var chr = t[2];
var pos = parseInt(t[3]) - 1;
var strand = (flag&16)? '-' : '+';
var read_no = flag&0xc0;
if (read_no == 0x40) read_no = 1;
else if (read_no == 0x80) read_no = 2;
else read_no = 0;
var err = 0, snp = 0, indel = 0;
for (var i = 11; i < t.length; ++i) {
if ((m = /^XE:i:(\d+)/.exec(t[i])) != null) err = m[1];
else if ((m = /^XS:i:(\d+)/.exec(t[i])) != null) snp = m[1];
else if ((m = /^XI:i:(\d+)/.exec(t[i])) != null) indel = m[1];
}
var comment = [err, snp, indel].join(":");
if (last == null) {
last = [qname, chr, pos, pos + l_ref, strand, seq, qual, read_no, comment];
} else if (last[0] != qname) {
print_se(last);
last = [qname, chr, pos, pos + l_ref, strand, seq, qual, read_no, comment];
} else {
if (read_no == 2) { // last[] is the first read
if (last[7] != 1) throw Error("ERROR: can't find read1");
var name = [qname, chr, last[2] + "_" + pos, last[3] + "_" + (pos + l_ref), last[4] + strand].join("!");
print('@' + name + '/1' + ' ' + last[8]); print(last[5]); print("+"); print(last[6]);
print('@' + name + '/2' + ' ' + comment); print(seq); print("+"); print(qual);
} else {
if (last[7] != 2) throw Error("ERROR: can't find read2");
var name = [qname, chr, pos + "_" + last[2], (pos + l_ref) + "_" + last[3], strand + last[4]].join("!");
print('@' + name + '/1' + ' ' + comment); print(seq); print("+"); print(qual);
print('@' + name + '/2' + ' ' + last[8]); print(last[5]); print("+"); print(last[6]);
}
last = null;
}
}
if (last != null) print_se(last);
file.close();
buf.destroy();
buf2.destroy();
-81
View File
@@ -1,81 +0,0 @@
Bytes.prototype.reverse = function()
{
for (var i = 0; i < this.length>>1; ++i) {
var tmp = this[i];
this[i] = this[this.length - i - 1];
this[this.length - i - 1] = tmp;
}
}
// reverse complement a DNA string
Bytes.prototype.revcomp = function()
{
if (Bytes.rctab == null) {
var s1 = 'WSATUGCYRKMBDHVNwsatugcyrkmbdhvn';
var s2 = 'WSTAACGRYMKVHDBNwstaacgrymkvhdbn';
Bytes.rctab = [];
for (var i = 0; i < 256; ++i) Bytes.rctab[i] = 0;
for (var i = 0; i < s1.length; ++i)
Bytes.rctab[s1.charCodeAt(i)] = s2.charCodeAt(i);
}
for (var i = 0; i < this.length>>1; ++i) {
var tmp = this[this.length - i - 1];
this[this.length - i - 1] = Bytes.rctab[this[i]];
this[i] = Bytes.rctab[tmp];
}
if (this.length&1)
this[this.length>>1] = Bytes.rctab[this[this.length>>1]];
}
if (arguments.length < 2) {
print("Usage: k8 sim-pbsim.js <ref.fa.fai> <pbsim1.maf> [[pbsim2.maf] ...]");
exit(1);
}
var file, buf = new Bytes(), buf2 = new Bytes();
file = new File(arguments[0]);
var chr_list = [];
while (file.readline(buf) >= 0) {
var t = buf.toString().split(/\s+/);
chr_list.push(t[0]);
}
file.close();
for (var k = 1; k < arguments.length; ++k) {
var fn = arguments[k];
file = new File(fn);
var state = 0, reg;
while (file.readline(buf) >= 0) {
var line = buf.toString();
if (state == 0 && line.charAt(0) == 'a') {
state = 1;
} else if (state == 1 && line.charAt(0) == 's') {
var t = line.split(/\s+/);
var st = parseInt(t[2]);
reg = [st, st + parseInt(t[3])];
state = 2;
} else if (state == 2 && line.charAt(0) == 's') {
var m, t = line.split(/\s+/);
if ((m = /S(\d+)_\d+/.exec(t[1])) == null) throw Error("Failed to parse the read name");
var chr_id = parseInt(m[1]) - 1;
if (chr_id >= chr_list.length) throw Error("Index outside the chr list");
var name = [t[1], chr_list[chr_id], reg[0], reg[1], t[4]].join("!");
var seq = t[6].replace(/\-/g, "");
if (seq.length != parseInt(t[5])) throw Error("Inconsistent read length");
if (seq.indexOf("NN") < 0) {
if (t[4] == '-') {
buf2.set(seq, 0);
buf2.length = seq.length;
buf2.revcomp();
seq = buf2.toString();
}
print(">" + name);
print(seq);
}
state = 0;
}
}
file.close();
}
buf.destroy();
buf2.destroy();
+20 -5
View File
@@ -16,6 +16,7 @@
#define MM_SEED_LONG_JOIN (1ULL<<40)
#define MM_SEED_IGNORE (1ULL<<41)
#define MM_SEED_TANDEM (1ULL<<42)
#define MM_SEED_SELF (1ULL<<43)
#define MM_SEED_SEG_SHIFT 48
#define MM_SEED_SEG_MASK (0xffULL<<(MM_SEED_SEG_SHIFT))
@@ -27,6 +28,9 @@
#define mm_seq4_set(s, i, c) ((s)[(i)>>3] |= (uint32_t)(c) << (((i)&7)<<2))
#define mm_seq4_get(s, i) ((s)[(i)>>3] >> (((i)&7)<<2) & 0xf)
#define MALLOC(type, len) ((type*)malloc((len) * sizeof(type)))
#define CALLOC(type, len) ((type*)calloc((len), sizeof(type)))
#ifdef __cplusplus
extern "C" {
#endif
@@ -47,6 +51,7 @@ typedef struct {
double cputime(void);
double realtime(void);
long peakrss(void);
void radix_sort_128x(mm128_t *beg, mm128_t *end);
void radix_sort_64(uint64_t *beg, uint64_t *end);
@@ -61,10 +66,9 @@ void mm_write_sam2(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, int se
void mm_idxopt_init(mm_idxopt_t *opt);
const uint64_t *mm_idx_get(const mm_idx_t *mi, uint64_t minier, int *n);
int mm_idx_getseq(const mm_idx_t *mi, uint32_t rid, uint32_t st, uint32_t en, uint8_t *seq);
int32_t mm_idx_cal_max_occ(const mm_idx_t *mi, float f);
mm128_t *mm_chain_dp(int max_dist_x, int max_dist_y, int bw, int max_skip, int min_cnt, int min_sc, int is_cdna, int n_segs, int64_t n, mm128_t *a, int *n_u_, uint64_t **_u, void *km);
mm_reg1_t *mm_align_skeleton(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int qlen, const char *qstr, const char *qual, int *n_regs_, mm_reg1_t *regs, mm128_t *a);
mm_reg1_t *mm_align_skeleton(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int qlen, const char *qstr, int *n_regs_, mm_reg1_t *regs, mm128_t *a);
mm_reg1_t *mm_gen_regs(void *km, uint32_t hash, int qlen, int n_u, uint64_t *u, mm128_t *a);
void mm_split_reg(mm_reg1_t *r, mm_reg1_t *r2, int n, int qlen, mm128_t *a);
@@ -74,15 +78,26 @@ int mm_set_sam_pri(int n, mm_reg1_t *r);
void mm_set_parent(void *km, float mask_level, int n, mm_reg1_t *r, int sub_diff);
void mm_select_sub(void *km, float pri_ratio, int min_diff, int best_n, int *n_, mm_reg1_t *r);
void mm_select_sub_multi(void *km, float pri_ratio, float pri1, float pri2, int max_gap_ref, int min_diff, int best_n, int n_segs, const int *qlens, int *n_, mm_reg1_t *r);
void mm_filter_regs(void *km, const mm_mapopt_t *opt, int *n_regs, mm_reg1_t *regs);
void mm_filter_regs(const mm_mapopt_t *opt, int qlen, int *n_regs, mm_reg1_t *regs);
void mm_join_long(void *km, const mm_mapopt_t *opt, int qlen, int *n_regs, mm_reg1_t *regs, mm128_t *a);
void mm_hit_sort_by_dp(void *km, int *n_regs, mm_reg1_t *r);
void mm_set_mapq(int n_regs, mm_reg1_t *regs, int min_chain_sc, int match_sc, int rep_len, int is_sr);
void mm_hit_sort(void *km, int *n_regs, mm_reg1_t *r);
void mm_set_mapq(void *km, int n_regs, mm_reg1_t *regs, int min_chain_sc, int match_sc, int rep_len, int is_sr);
void mm_est_err(const mm_idx_t *mi, int qlen, int n_regs, mm_reg1_t *regs, const mm128_t *a, int32_t n, const uint64_t *mini_pos);
mm_seg_t *mm_seg_gen(void *km, uint32_t hash, int n_segs, const int *qlens, int n_regs0, const mm_reg1_t *regs0, int *n_regs, mm_reg1_t **regs, const mm128_t *a);
void mm_seg_free(void *km, int n_segs, mm_seg_t *segs);
void mm_pair(void *km, int max_gap_ref, int dp_bonus, int sub_diff, int match_sc, const int *qlens, int *n_regs, mm_reg1_t **regs);
FILE *mm_split_init(const char *prefix, const mm_idx_t *mi);
mm_idx_t *mm_split_merge_prep(const char *prefix, int n_splits, FILE **fp, uint32_t *n_seq_part);
int mm_split_merge(int n_segs, const char **fn, const mm_mapopt_t *opt, int n_split_idx);
void mm_split_rm_tmp(const char *prefix, int n_splits);
void mm_err_puts(const char *str);
void mm_err_fwrite(const void *p, size_t size, size_t nitems, FILE *fp);
void mm_err_fread(void *p, size_t size, size_t nitems, FILE *fp);
#ifdef __cplusplus
}
#endif
+173
View File
@@ -0,0 +1,173 @@
#include <stdio.h>
#include "mmpriv.h"
void mm_idxopt_init(mm_idxopt_t *opt)
{
memset(opt, 0, sizeof(mm_idxopt_t));
opt->k = 15, opt->w = 10, opt->flag = 0;
opt->bucket_bits = 14;
opt->mini_batch_size = 50000000;
opt->batch_size = 4000000000ULL;
}
void mm_mapopt_init(mm_mapopt_t *opt)
{
memset(opt, 0, sizeof(mm_mapopt_t));
opt->seed = 11;
opt->mid_occ_frac = 2e-4f;
opt->sdust_thres = 0; // no SDUST masking
opt->min_cnt = 3;
opt->min_chain_score = 40;
opt->bw = 500;
opt->max_gap = 5000;
opt->max_gap_ref = -1;
opt->max_chain_skip = 25;
opt->mask_level = 0.5f;
opt->pri_ratio = 0.8f;
opt->best_n = 5;
opt->max_join_long = 20000;
opt->max_join_short = 2000;
opt->min_join_flank_sc = 1000;
opt->min_join_flank_ratio = 0.5f;
opt->a = 2, opt->b = 4, opt->q = 4, opt->e = 2, opt->q2 = 24, opt->e2 = 1;
opt->sc_ambi = 1;
opt->zdrop = 400, opt->zdrop_inv = 200;
opt->end_bonus = -1;
opt->min_dp_max = opt->min_chain_score * opt->a;
opt->min_ksw_len = 200;
opt->anchor_ext_len = 20, opt->anchor_ext_shift = 6;
opt->max_clip_ratio = 1.0f;
opt->mini_batch_size = 500000000;
opt->pe_ori = 0; // FF
opt->pe_bonus = 33;
}
void mm_mapopt_update(mm_mapopt_t *opt, const mm_idx_t *mi)
{
if ((opt->flag & MM_F_SPLICE_FOR) || (opt->flag & MM_F_SPLICE_REV))
opt->flag |= MM_F_SPLICE;
if (opt->mid_occ <= 0)
opt->mid_occ = mm_idx_cal_max_occ(mi, opt->mid_occ_frac);
if (opt->mid_occ < opt->min_mid_occ)
opt->mid_occ = opt->min_mid_occ;
if (mm_verbose >= 3)
fprintf(stderr, "[M::%s::%.3f*%.2f] mid_occ = %d\n", __func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0), opt->mid_occ);
}
void mm_mapopt_max_intron_len(mm_mapopt_t *opt, int max_intron_len)
{
if ((opt->flag & MM_F_SPLICE) && max_intron_len > 0)
opt->max_gap_ref = opt->bw = max_intron_len;
}
int mm_set_opt(const char *preset, mm_idxopt_t *io, mm_mapopt_t *mo)
{
if (preset == 0) {
mm_idxopt_init(io);
mm_mapopt_init(mo);
} else if (strcmp(preset, "ava-ont") == 0) {
io->flag = 0, io->k = 15, io->w = 5;
mo->flag |= MM_F_ALL_CHAINS | MM_F_NO_DIAG | MM_F_NO_DUAL | MM_F_NO_LJOIN;
mo->min_chain_score = 100, mo->pri_ratio = 0.0f, mo->max_gap = 10000, mo->max_chain_skip = 25;
mo->bw = 2000;
} else if (strcmp(preset, "ava-pb") == 0) {
io->flag |= MM_I_HPC, io->k = 19, io->w = 5;
mo->flag |= MM_F_ALL_CHAINS | MM_F_NO_DIAG | MM_F_NO_DUAL | MM_F_NO_LJOIN;
mo->min_chain_score = 100, mo->pri_ratio = 0.0f, mo->max_gap = 10000, mo->max_chain_skip = 25;
} else if (strcmp(preset, "map10k") == 0 || strcmp(preset, "map-pb") == 0) {
io->flag |= MM_I_HPC, io->k = 19;
} else if (strcmp(preset, "map-ont") == 0) {
io->flag = 0, io->k = 15;
} else if (strcmp(preset, "asm5") == 0) {
io->flag = 0, io->k = 19, io->w = 19;
mo->a = 1, mo->b = 19, mo->q = 39, mo->q2 = 81, mo->e = 3, mo->e2 = 1, mo->zdrop = mo->zdrop_inv = 200;
mo->min_mid_occ = 100;
mo->min_dp_max = 200;
mo->best_n = 50;
} else if (strcmp(preset, "asm10") == 0) {
io->flag = 0, io->k = 19, io->w = 19;
mo->a = 1, mo->b = 9, mo->q = 16, mo->q2 = 41, mo->e = 2, mo->e2 = 1, mo->zdrop = mo->zdrop_inv = 200;
mo->min_mid_occ = 100;
mo->min_dp_max = 200;
mo->best_n = 50;
} else if (strcmp(preset, "asm20") == 0) {
io->flag = 0, io->k = 19, io->w = 10;
mo->a = 1, mo->b = 4, mo->q = 6, mo->q2 = 26, mo->e = 2, mo->e2 = 1, mo->zdrop = mo->zdrop_inv = 200;
mo->min_mid_occ = 100;
mo->min_dp_max = 200;
mo->best_n = 50;
} else if (strcmp(preset, "short") == 0 || strcmp(preset, "sr") == 0) {
io->flag = 0, io->k = 21, io->w = 11;
mo->flag |= MM_F_SR | MM_F_FRAG_MODE | MM_F_NO_PRINT_2ND | MM_F_2_IO_THREADS | MM_F_HEAP_SORT;
mo->pe_ori = 0<<1|1; // FR
mo->a = 2, mo->b = 8, mo->q = 12, mo->e = 2, mo->q2 = 24, mo->e2 = 1;
mo->zdrop = mo->zdrop_inv = 100;
mo->end_bonus = 10;
mo->max_frag_len = 800;
mo->max_gap = 100;
mo->bw = 100;
mo->pri_ratio = 0.5f;
mo->min_cnt = 2;
mo->min_chain_score = 25;
mo->min_dp_max = 40;
mo->best_n = 20;
mo->mid_occ = 1000;
mo->max_occ = 5000;
mo->mini_batch_size = 50000000;
} else if (strcmp(preset, "splice") == 0 || strcmp(preset, "cdna") == 0) {
io->flag = 0, io->k = 15, io->w = 5;
mo->flag |= MM_F_SPLICE | MM_F_SPLICE_FOR | MM_F_SPLICE_REV | MM_F_SPLICE_FLANK;
mo->max_gap = 2000, mo->max_gap_ref = mo->bw = 200000;
mo->a = 1, mo->b = 2, mo->q = 2, mo->e = 1, mo->q2 = 32, mo->e2 = 0;
mo->noncan = 9;
mo->zdrop = 200, mo->zdrop_inv = 100; // because mo->a is halved
} else return -1;
return 0;
}
int mm_check_opt(const mm_idxopt_t *io, const mm_mapopt_t *mo)
{
if (io->k <= 0 || io->w <= 0) {
if (mm_verbose >= 1)
fprintf(stderr, "[ERROR]\033[1;31m -k and -w must be positive\033[0m\n");
return -5;
}
if (mo->best_n < 0) {
if (mm_verbose >= 1)
fprintf(stderr, "[ERROR]\033[1;31m -N must be no less than 0\033[0m\n");
return -4;
}
if (mo->best_n == 0 && mm_verbose >= 2)
fprintf(stderr, "[WARNING]\033[1;31m '-N 0' reduces mapping accuracy. Please use '--secondary=no' instead.\033[0m\n");
if (mo->pri_ratio < 0.0f || mo->pri_ratio > 1.0f) {
if (mm_verbose >= 1)
fprintf(stderr, "[ERROR]\033[1;31m -p must be within 0 and 1 (including 0 and 1)\033[0m\n");
return -4;
}
if ((mo->flag & MM_F_FOR_ONLY) && (mo->flag & MM_F_REV_ONLY)) {
if (mm_verbose >= 1)
fprintf(stderr, "[ERROR]\033[1;31m --for-only and --rev-only can't be applied at the same time\033[0m\n");
return -3;
}
if ((mo->q != mo->q2 || mo->e != mo->e2) && !(mo->e > mo->e2 && mo->q + mo->e < mo->q2 + mo->e2)) {
if (mm_verbose >= 1)
fprintf(stderr, "[ERROR]\033[1;31m dual gap penalties violating E1>E2 and O1+E1<O2+E2\033[0m\n");
return -2;
}
if ((mo->q + mo->e) + (mo->q2 + mo->e2) > 127) {
if (mm_verbose >= 1)
fprintf(stderr, "[ERROR]\033[1;31m scoring system violating ({-O}+{-E})+({-O2}+{-E2}) <= 127\033[0m\n");
return -1;
}
if (mo->zdrop < mo->zdrop_inv) {
if (mm_verbose >= 1)
fprintf(stderr, "[ERROR]\033[1;31m Z-drop should not be less than inversion-Z-drop\033[0m\n");
return -5;
}
return 0;
}
+4 -4
View File
@@ -105,7 +105,7 @@ void mm_pair(void *km, int max_gap_ref, int pe_bonus, int sub_diff, int match_sc
max = -1;
max_idx[0] = max_idx[1] = -1;
last[0] = last[1] = -1;
kv_resize(uint64_t, km, sc, n);
kv_resize(uint64_t, km, sc, (size_t)n);
for (i = 0; i < n; ++i) {
if (a[i].key & 1) { // reverse first read or forward second read
mm_reg1_t *q, *r;
@@ -151,8 +151,8 @@ void mm_pair(void *km, int max_gap_ref, int pe_bonus, int sub_diff, int match_sc
}
}
mapq_pe = r[0]->mapq > r[1]->mapq? r[0]->mapq : r[1]->mapq;
for (i = 0; i < sc.n; ++i)
if ((sc.a[i]>>32) + sub_diff >= max>>32)
for (i = 0; i < (int)sc.n; ++i)
if ((sc.a[i]>>32) + sub_diff >= (uint64_t)max>>32)
++n_sub;
if (sc.n > 1) {
int mapq_pe_alt;
@@ -164,7 +164,7 @@ void mm_pair(void *km, int max_gap_ref, int pe_bonus, int sub_diff, int match_sc
if (sc.n == 1) {
if (r[0]->mapq < 2) r[0]->mapq = 2;
if (r[1]->mapq < 2) r[1]->mapq = 2;
} else if (max>>32 > sc.a[sc.n - 2]>>32) {
} else if ((uint64_t)max>>32 > sc.a[sc.n - 2]>>32) {
if (r[0]->mapq < 1) r[0]->mapq = 1;
if (r[1]->mapq < 1) r[1]->mapq = 1;
}
+31 -6
View File
@@ -34,6 +34,8 @@ The following Python script demonstrates the key functionality of mappy:
import mappy as mp
a = mp.Aligner("test/MT-human.fa") # load or build index
if not a: raise Exception("ERROR: failed to load/build index")
s = a.seq("MT_human", 100, 200) # retrieve a subsequence from the index
print(mp.revcomp(s)) # reverse complement
for name, seq, qual in mp.fastx_read("test/MT-orang.fa"): # read a fasta/q sequence
for hit in a.map(seq): # traverse alignments
print("{}\t{}\t{}\t{}".format(hit.ctg, hit.r_st, hit.r_en, hit.cigar_str))
@@ -81,10 +83,21 @@ This constructor accepts the following arguments:
.. code:: python
mappy.Aligner.map(seq)
mappy.Aligner.map(seq, seq2=None)
This method aligns :code:`seq` against the index. It is a generator, *yielding*
a series of :code:`mappy.Alignment` objects.
a series of :code:`mappy.Alignment` objects. If :code:`seq2` is present, mappy
performs paired-end alignment, assuming the two ends are in the FR orientation.
Alignments of the two ends can be distinguished by the :code:`read_num` field
(see Class mappy.Alignment below).
.. code:: python
mappy.Aligner.seq(name, start=0, end=0x7fffffff)
This method retrieves a (sub)sequence from the index and returns it as a Python
string. :code:`None` is returned if :code:`name` is not present in the index or
the start/end coordinates are invalid.
Class mappy.Alignment
~~~~~~~~~~~~~~~~~~~~~
@@ -118,6 +131,9 @@ properties:
* **is_primary**: if the alignment is primary (typically the best and the first
to generate)
* **read_num**: read number that the alignment corresponds to; 1 for the first
read and 2 for the second read
* **cigar_str**: CIGAR string
* **cigar**: CIGAR returned as an array of shape :code:`(n_cigar,2)`. The two
@@ -133,13 +149,22 @@ the following format:
It is effectively the PAF format without the QueryName and QueryLength columns
(the first two columns in PAF).
Function mappy.fastx_read
~~~~~~~~~~~~~~~~~~~~~~~~~
Miscellaneous Functions
~~~~~~~~~~~~~~~~~~~~~~~
.. code:: python
mappy.fastx_read(fn)
mappy.fastx_read(fn, read_comment=False)
This generator function opens a FASTA/FASTQ file and *yields* a
:code:`(name,seq,qual)` tuple for each sequence entry. The input file may be
optionally gzip'd.
optionally gzip'd. If :code:`read_comment` is True, this generator yields
a :code:`(name,seq,qual,comment)` tuple instead.
.. code:: python
mappy.revcomp(seq)
Return the reverse complement of DNA string :code:`seq`. This function
recognizes IUB code and preserves the letter cases. Uracil :code:`U` is
complemented to :code:`A`.
+69
View File
@@ -15,6 +15,7 @@ typedef struct {
int32_t blen, mlen, NM, ctg_len;
uint8_t mapq, is_primary;
int8_t strand, trans_strand;
int32_t seg_id;
int32_t n_cigar32;
uint32_t *cigar32;
} mm_hitpy_t;
@@ -32,6 +33,7 @@ static inline void mm_reg2hitpy(const mm_idx_t *mi, mm_reg1_t *r, mm_hitpy_t *h)
h->NM = r->blen - r->mlen + r->p->n_ambi;
h->trans_strand = r->p->trans_strand == 1? 1 : r->p->trans_strand == 2? -1 : 0;
h->is_primary = (r->id == r->parent);
h->seg_id = r->seg_id;
h->n_cigar32 = r->p->n_cigar;
h->cigar32 = r->p->cigar;
}
@@ -68,4 +70,71 @@ static inline void mm_reset_timer(void)
mm_realtime0 = realtime();
}
extern unsigned char seq_comp_table[256];
static inline mm_reg1_t *mm_map_aux(const mm_idx_t *mi, const char *seq1, const char *seq2, int *n_regs, mm_tbuf_t *b, const mm_mapopt_t *opt)
{
mm_reg1_t *r;
Py_BEGIN_ALLOW_THREADS
if (seq2 == 0) {
r = mm_map(mi, strlen(seq1), seq1, n_regs, b, opt, NULL);
} else {
int _n_regs[2];
mm_reg1_t *regs[2];
char *seq[2];
int i, len[2];
len[0] = strlen(seq1);
len[1] = strlen(seq2);
seq[0] = (char*)seq1;
seq[1] = strdup(seq2);
for (i = 0; i < len[1]>>1; ++i) {
int t = seq[1][len[1] - i - 1];
seq[1][len[1] - i - 1] = seq_comp_table[(uint8_t)seq[1][i]];
seq[1][i] = seq_comp_table[t];
}
if (len[1]&1) seq[1][len[1]>>1] = seq_comp_table[(uint8_t)seq[1][len[1]>>1]];
mm_map_frag(mi, 2, len, (const char**)seq, _n_regs, regs, b, opt, NULL);
for (i = 0; i < _n_regs[1]; ++i)
regs[1][i].rev = !regs[1][i].rev;
*n_regs = _n_regs[0] + _n_regs[1];
regs[0] = (mm_reg1_t*)realloc(regs[0], sizeof(mm_reg1_t) * (*n_regs));
memcpy(&regs[0][_n_regs[0]], regs[1], _n_regs[1] * sizeof(mm_reg1_t));
free(regs[1]);
r = regs[0];
}
Py_END_ALLOW_THREADS
return r;
}
static inline char *mappy_revcomp(int len, const uint8_t *seq)
{
int i;
char *rev;
rev = (char*)malloc(len + 1);
for (i = 0; i < len; ++i)
rev[len - i - 1] = seq_comp_table[seq[i]];
rev[len] = 0;
return rev;
}
static char *mappy_fetch_seq(const mm_idx_t *mi, const char *name, int st, int en, int *len)
{
int i, rid;
char *s;
*len = 0;
rid = mm_idx_name2id(mi, name);
if (rid < 0) return 0;
if (st >= mi->seq[rid].len || st >= en) return 0;
if (en < 0 || en > mi->seq[rid].len)
en = mi->seq[rid].len;
s = (char*)malloc(en - st + 1);
*len = mm_idx_getseq(mi, rid, st, en, (uint8_t*)s);
for (i = 0; i < *len; ++i)
s[i] = "ACGTN"[(uint8_t)s[i]];
s[*len] = 0;
return s;
}
#endif
+15 -4
View File
@@ -5,7 +5,7 @@ cdef extern from "minimap.h":
# Options
#
ctypedef struct mm_idxopt_t:
short k, w, is_hpc, bucket_bits
short k, w, flag, bucket_bits
int mini_batch_size
uint64_t batch_size
@@ -24,14 +24,19 @@ cdef extern from "minimap.h":
int best_n
int max_join_long, max_join_short
int min_join_flank_sc
float min_join_flank_ratio;
int a, b, q, e, q2, e2
int sc_ambi
int noncan
int zdrop
int zdrop, zdrop_inv
int end_bonus
int min_dp_max
int min_ksw_len
int anchor_ext_len, anchor_ext_shift
float max_clip_ratio
int pe_ori, pe_bonus
float mid_occ_frac
int32_t min_mid_occ
int32_t mid_occ
int32_t max_occ
int mini_batch_size
@@ -51,12 +56,13 @@ cdef extern from "minimap.h":
pass
ctypedef struct mm_idx_t:
int32_t b, w, k, is_hpc
int32_t b, w, k, flag
uint32_t n_seq
mm_idx_seq_t *seq
uint32_t *S
mm_idx_bucket_t *B
void *km
void *h
ctypedef struct mm_idx_reader_t:
pass
@@ -67,6 +73,8 @@ cdef extern from "minimap.h":
void mm_idx_destroy(mm_idx_t *mi)
void mm_mapopt_update(mm_mapopt_t *opt, const mm_idx_t *mi)
int mm_idx_index_name(mm_idx_t *mi)
#
# Mapping (key struct defined in cmappy.h below)
#
@@ -78,7 +86,6 @@ cdef extern from "minimap.h":
mm_tbuf_t *mm_tbuf_init()
void mm_tbuf_destroy(mm_tbuf_t *b)
mm_reg1_t *mm_map(const mm_idx_t *mi, int l_seq, const char *seq, int *n_regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *name)
#
# Helper header (because it is hard to expose mm_reg1_t with Cython)
@@ -91,11 +98,14 @@ cdef extern from "cmappy.h":
int32_t blen, mlen, NM, ctg_len
uint8_t mapq, is_primary
int8_t strand, trans_strand
int32_t seg_id
int32_t n_cigar32
uint32_t *cigar32
void mm_reg2hitpy(const mm_idx_t *mi, mm_reg1_t *r, mm_hitpy_t *h)
void mm_free_reg1(mm_reg1_t *r)
mm_reg1_t *mm_map_aux(const mm_idx_t *mi, const char *seq1, const char *seq2, int *n_regs, mm_tbuf_t *b, const mm_mapopt_t *opt)
char *mappy_fetch_seq(const mm_idx_t *mi, const char *name, int st, int en, int *l)
ctypedef struct kstring_t:
unsigned l, m
@@ -113,5 +123,6 @@ cdef extern from "cmappy.h":
void mm_fastx_close(kseq_t *ks)
int kseq_read(kseq_t *seq)
char *mappy_revcomp(int l, const uint8_t *seq)
int mm_verbose_level(int v)
void mm_reset_timer()
+57 -8
View File
@@ -1,6 +1,9 @@
from libc.stdint cimport uint8_t, int8_t
from libc.stdlib cimport free
cimport cmappy
import sys
__version__ = '2.11'
cmappy.mm_reset_timer()
@@ -10,9 +13,10 @@ cdef class Alignment:
cdef int _NM, _mlen, _blen
cdef int8_t _strand, _trans_strand
cdef uint8_t _mapq, _is_primary
cdef int _seg_id
cdef _ctg, _cigar # these are python objects
def __cinit__(self, ctg, cl, cs, ce, strand, qs, qe, mapq, cigar, is_primary, mlen, blen, NM, trans_strand):
def __cinit__(self, ctg, cl, cs, ce, strand, qs, qe, mapq, cigar, is_primary, mlen, blen, NM, trans_strand, seg_id):
self._ctg = ctg if isinstance(ctg, str) else ctg.decode()
self._ctg_len, self._r_st, self._r_en = cl, cs, ce
self._strand, self._q_st, self._q_en = strand, qs, qe
@@ -21,6 +25,7 @@ cdef class Alignment:
self._cigar = cigar
self._is_primary = is_primary
self._trans_strand = trans_strand
self._seg_id = seg_id
@property
def ctg(self): return self._ctg
@@ -64,6 +69,9 @@ cdef class Alignment:
@property
def cigar(self): return self._cigar
@property
def read_num(self): return self._seg_id + 1
@property
def cigar_str(self):
return "".join(map(lambda x: str(x[0]) + 'MIDNSH'[x[1]], self._cigar))
@@ -94,7 +102,7 @@ cdef class Aligner:
cdef cmappy.mm_idxopt_t idx_opt
cdef cmappy.mm_mapopt_t map_opt
def __cinit__(self, fn_idx_in, preset=None, k=None, w=None, min_cnt=None, min_chain_score=None, min_dp_score=None, bw=None, best_n=None, n_threads=3, fn_idx_out=None):
def __cinit__(self, fn_idx_in, preset=None, k=None, w=None, min_cnt=None, min_chain_score=None, min_dp_score=None, bw=None, best_n=None, n_threads=3, fn_idx_out=None, max_frag_len=None):
cmappy.mm_set_opt(NULL, &self.idx_opt, &self.map_opt) # set the default options
if preset is not None:
cmappy.mm_set_opt(str.encode(preset), &self.idx_opt, &self.map_opt) # apply preset
@@ -106,7 +114,8 @@ cdef class Aligner:
if min_chain_score is not None: self.map_opt.min_chain_score = min_chain_score
if min_dp_score is not None: self.map_opt.min_dp_max = min_dp_score
if bw is not None: self.map_opt.bw = bw
if best_n is not None: self.best_n = best_n
if best_n is not None: self.map_opt.best_n = best_n
if max_frag_len is not None: self.map_opt.max_frag_len = max_frag_len
cdef cmappy.mm_idx_reader_t *r;
if fn_idx_out is None:
@@ -117,6 +126,7 @@ cdef class Aligner:
self._idx = cmappy.mm_idx_reader_read(r, n_threads) # NB: ONLY read the first part
cmappy.mm_idx_reader_close(r)
cmappy.mm_mapopt_update(&self.map_opt, self._idx)
cmappy.mm_idx_index_name(self._idx)
def __dealloc__(self):
if self._idx is not NULL:
@@ -125,16 +135,25 @@ cdef class Aligner:
def __bool__(self):
return (self._idx != NULL)
def map(self, seq, buf=None):
def map(self, seq, seq2=None, buf=None, max_frag_len=None):
cdef cmappy.mm_reg1_t *regs
cdef cmappy.mm_hitpy_t h
cdef ThreadBuffer b
cdef int n_regs
cdef cmappy.mm_mapopt_t map_opt
map_opt = self.map_opt
if max_frag_len is not None: map_opt.max_frag_len = max_frag_len
if self._idx is NULL: return None
if buf is None: b = ThreadBuffer()
else: b = buf
regs = cmappy.mm_map(self._idx, len(seq), str.encode(seq), &n_regs, b._b, &self.map_opt, NULL)
_seq = seq if isinstance(seq, bytes) else seq.encode()
if seq2 is None:
regs = cmappy.mm_map_aux(self._idx, _seq, NULL, &n_regs, b._b, &map_opt)
else:
_seq2 = seq2 if isinstance(seq2, bytes) else seq2.encode()
regs = cmappy.mm_map_aux(self._idx, _seq, _seq2, &n_regs, b._b, &map_opt)
for i in range(n_regs):
cmappy.mm_reg2hitpy(self._idx, &regs[i], &h)
@@ -142,11 +161,28 @@ cdef class Aligner:
for k in range(h.n_cigar32):
c = h.cigar32[k]
cigar.append([c>>4, c&0xf])
yield Alignment(h.ctg, h.ctg_len, h.ctg_start, h.ctg_end, h.strand, h.qry_start, h.qry_end, h.mapq, cigar, h.is_primary, h.mlen, h.blen, h.NM, h.trans_strand)
yield Alignment(h.ctg, h.ctg_len, h.ctg_start, h.ctg_end, h.strand, h.qry_start, h.qry_end, h.mapq, cigar, h.is_primary, h.mlen, h.blen, h.NM, h.trans_strand, h.seg_id)
cmappy.mm_free_reg1(&regs[i])
free(regs)
def fastx_read(fn):
def seq(self, str name, int start=0, int end=0x7fffffff):
cdef int l
cdef char *s = cmappy.mappy_fetch_seq(self._idx, name.encode(), start, end, &l)
if l == 0: return None
r = s[:l] if isinstance(s, str) else s[:l].decode()
free(s)
return r
@property
def k(self): return self._idx.k
@property
def w(self): return self._idx.w
@property
def n_seq(self): return self._idx.n_seq
def fastx_read(fn, read_comment=False):
cdef cmappy.kseq_t *ks
ks = cmappy.mm_fastx_open(str.encode(fn))
if ks is NULL: return None
@@ -155,9 +191,22 @@ def fastx_read(fn):
else: qual = None
name = ks.name.s if isinstance(ks.name.s, str) else ks.name.s.decode()
seq = ks.seq.s if isinstance(ks.seq.s, str) else ks.seq.s.decode()
yield name, seq, qual
if read_comment:
if ks.comment.l > 0: comment = ks.comment.s if isinstance(ks.comment.s, str) else ks.comment.s.decode()
else: comment = None
yield name, seq, qual, comment
else:
yield name, seq, qual
cmappy.mm_fastx_close(ks)
def revcomp(seq):
l = len(seq)
bseq = seq if isinstance(seq, bytes) else seq.encode()
cdef char *s = cmappy.mappy_revcomp(l, bseq)
r = s[:l] if isinstance(s, str) else s[:l].decode()
free(s)
return r
def verbose(v=None):
if v is None: v = -1
return cmappy.mm_verbose_level(v)
+4 -3
View File
@@ -56,6 +56,7 @@ sdust_buf_t *sdust_buf_init(void *km)
buf = (sdust_buf_t*)kcalloc(km, 1, sizeof(sdust_buf_t));
buf->km = km;
buf->w = kdq_init(int, buf->km);
kdq_resize(int, buf->w, 8);
return buf;
}
@@ -69,10 +70,10 @@ void sdust_buf_destroy(sdust_buf_t *buf)
static inline void shift_window(int t, kdq_t(int) *w, int T, int W, int *L, int *rw, int *rv, int *cw, int *cv)
{
int s;
if (kdq_size(w) >= W - SD_WLEN + 1) { // TODO: is this right for SD_WLEN!=3?
if ((int)kdq_size(w) >= W - SD_WLEN + 1) { // TODO: is this right for SD_WLEN!=3?
s = *kdq_shift(int, w);
*rw -= --cw[s];
if (*L > kdq_size(w))
if (*L > (int)kdq_size(w))
--*L, *rv -= --cv[s];
}
kdq_push(int, w, t);
@@ -113,7 +114,7 @@ static void find_perfect(void *km, perf_intv_v *P, const kdq_t(int) *w, int T, i
r += c[t]++;
new_r = r, new_l = kdq_size(w) - i - 1;
if (new_r * 10 > T * new_l) {
for (j = 0; j < P->n && P->a[j].start >= i + start; ++j) { // find insertion position
for (j = 0; j < (int)P->n && P->a[j].start >= i + start; ++j) { // find insertion position
perf_intv_t *p = &P->a[j];
if (max_r == 0 || p->r * max_l > max_r * p->l)
max_r = p->r, max_l = p->l;
+5 -5
View File
@@ -23,7 +23,7 @@ def readme():
setup(
name = 'mappy',
version = '2.5',
version = '2.11',
url = 'https://github.com/lh3/minimap2',
description = 'Minimap2 python binding',
long_description = readme(),
@@ -33,17 +33,17 @@ setup(
keywords = 'sequence-alignment',
scripts = ['python/minimap2.py'],
ext_modules = [Extension('mappy',
sources = [module_src, 'align.c', 'bseq.c', 'chain.c', 'format.c', 'hit.c', 'index.c', 'pe.c',
sources = [module_src, 'align.c', 'bseq.c', 'chain.c', 'format.c', 'hit.c', 'index.c', 'pe.c', 'options.c',
'ksw2_extd2_sse.c', 'ksw2_exts2_sse.c', 'ksw2_extz2_sse.c', 'ksw2_ll_sse.c',
'kalloc.c', 'kthread.c', 'map.c', 'misc.c', 'sdust.c', 'sketch.c'],
'kalloc.c', 'kthread.c', 'map.c', 'misc.c', 'sdust.c', 'sketch.c', 'esterr.c'],
depends = ['minimap.h', 'bseq.h', 'kalloc.h', 'kdq.h', 'khash.h', 'kseq.h', 'ksort.h',
'ksw2.h', 'kthread.h', 'kvec.h', 'mmpriv.h', 'sdust.h',
'python/cmappy.h', 'python/cmappy.pxd'],
extra_compile_args = ['-msse4'], # WARNING: ancient x86_64 CPUs don't have SSE4
extra_compile_args = ['-DHAVE_KALLOC', '-msse4.1'], # WARNING: ancient x86_64 CPUs don't have SSE4
include_dirs = ['.'],
libraries = ['z', 'm', 'pthread'])],
classifiers = [
'Development Status :: 4 - Beta',
'Development Status :: 5 - Production/Stable',
'License :: OSI Approved :: MIT License',
'Operating System :: POSIX',
'Programming Language :: C',
+6 -5
View File
@@ -2,8 +2,9 @@
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#define __STDC_LIMIT_MACROS
#include "kvec.h"
#include "minimap.h"
#include "mmpriv.h"
unsigned char seq_nt4_table[256] = {
0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
@@ -113,22 +114,22 @@ void mm_sketch(void *km, const char *str, int len, int w, int k, uint32_t rid, i
}
} else l = 0, tq.count = tq.front = 0, kmer_span = 0;
buf[buf_pos] = info; // need to do this here as appropriate buf_pos and buf[buf_pos] are needed below
if (l == w + k - 1) { // special case for the first window - because identical k-mers are not stored yet
if (l == w + k - 1 && min.x != UINT64_MAX) { // special case for the first window - because identical k-mers are not stored yet
for (j = buf_pos + 1; j < w; ++j)
if (min.x == buf[j].x && buf[j].y != min.y) kv_push(mm128_t, km, *p, buf[j]);
for (j = 0; j < buf_pos; ++j)
if (min.x == buf[j].x && buf[j].y != min.y) kv_push(mm128_t, km, *p, buf[j]);
}
if (info.x <= min.x) { // a new minimum; then write the old min
if (l >= w + k) kv_push(mm128_t, km, *p, min);
if (l >= w + k && min.x != UINT64_MAX) kv_push(mm128_t, km, *p, min);
min = info, min_pos = buf_pos;
} else if (buf_pos == min_pos) { // old min has moved outside the window
if (l >= w + k - 1) kv_push(mm128_t, km, *p, min);
if (l >= w + k - 1 && min.x != UINT64_MAX) kv_push(mm128_t, km, *p, min);
for (j = buf_pos + 1, min.x = UINT64_MAX; j < w; ++j) // the two loops are necessary when there are identical k-mers
if (min.x >= buf[j].x) min = buf[j], min_pos = j; // >= is important s.t. min is always the closest k-mer
for (j = 0; j <= buf_pos; ++j)
if (min.x >= buf[j].x) min = buf[j], min_pos = j;
if (l >= w + k - 1) { // write identical k-mers
if (l >= w + k - 1 && min.x != UINT64_MAX) { // write identical k-mers
for (j = buf_pos + 1; j < w; ++j) // these two loops make sure the output is sorted
if (min.x == buf[j].x && min.y != buf[j].y) kv_push(mm128_t, km, *p, buf[j]);
for (j = 0; j <= buf_pos; ++j)
+80
View File
@@ -0,0 +1,80 @@
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "mmpriv.h"
FILE *mm_split_init(const char *prefix, const mm_idx_t *mi)
{
char *fn;
FILE *fp;
uint32_t i, k = mi->k;
fn = (char*)calloc(strlen(prefix) + 10, 1);
sprintf(fn, "%s.%.4d.tmp", prefix, mi->index);
fp = fopen(fn, "wb");
assert(fp);
mm_err_fwrite(&k, 4, 1, fp);
mm_err_fwrite(&mi->n_seq, 4, 1, fp);
for (i = 0; i < mi->n_seq; ++i) {
uint8_t l;
l = strlen(mi->seq[i].name);
mm_err_fwrite(&l, 1, 1, fp);
mm_err_fwrite(mi->seq[i].name, 1, l, fp);
mm_err_fwrite(&mi->seq[i].len, 4, 1, fp);
}
free(fn);
return fp;
}
mm_idx_t *mm_split_merge_prep(const char *prefix, int n_splits, FILE **fp, uint32_t *n_seq_part)
{
mm_idx_t *mi = 0;
char *fn;
int i, j;
if (n_splits < 1) return 0;
fn = CALLOC(char, strlen(prefix) + 10);
for (i = 0; i < n_splits; ++i) {
sprintf(fn, "%s.%.4d.tmp", prefix, i);
if ((fp[i] = fopen(fn, "rb")) == 0) {
if (mm_verbose >= 1)
fprintf(stderr, "ERROR: failed to open temporary file '%s'\n", fn);
for (j = 0; j < i; ++j)
fclose(fp[j]);
free(fn);
return 0;
}
}
free(fn);
mi = CALLOC(mm_idx_t, 1);
for (i = 0; i < n_splits; ++i) {
mm_err_fread(&mi->k, 4, 1, fp[i]); // TODO: check if k is all the same
mm_err_fread(&n_seq_part[i], 4, 1, fp[i]);
mi->n_seq += n_seq_part[i];
}
mi->seq = CALLOC(mm_idx_seq_t, mi->n_seq);
for (i = j = 0; i < n_splits; ++i) {
uint32_t k;
for (k = 0; k < n_seq_part[i]; ++k, ++j) {
uint8_t l;
mm_err_fread(&l, 1, 1, fp[i]);
mi->seq[j].name = (char*)calloc(l + 1, 1);
mm_err_fread(mi->seq[j].name, 1, l, fp[i]);
mm_err_fread(&mi->seq[j].len, 4, 1, fp[i]);
}
}
return mi;
}
void mm_split_rm_tmp(const char *prefix, int n_splits)
{
int i;
char *fn;
fn = CALLOC(char, strlen(prefix) + 10);
for (i = 0; i < n_splits; ++i) {
sprintf(fn, "%s.%.4d.tmp", prefix, i);
remove(fn);
}
free(fn);
}
+1689
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,4 +1,4 @@
>MT_orang
>MT_orang co:Z:comment
GTTTATGTAGCTTATTCTATCCAAAGCAATGCACTGAAAATGTCTCGACGGGCCCACACG
CCCCATAAACAAATAGGTTTGGTCCTAGCCTTTCTATTAGCTCTTAGTGAGGTTACACAT
GCAAGCATCCCCGCCCCAGTGAGTCGCCCTCCAAGTCACTCTGACTAAGAGGAGCAAGCA
+4
View File
File diff suppressed because one or more lines are too long
+127
View File
@@ -0,0 +1,127 @@
>ref
TGCGGAGGCTGAAGCAACTCCATCTTGGAAGCTAATCTACCATGTTGGCTTCTGATTAAC
ATCAGTTCTGGGAAGGCTTGTAAGATTTCCTGTTTGTCTATTATTTCCTAGGTAAGAGCA
GATACTTACTGTAAATCCTGCCCCTAGATTAAACAACCTTGGTGTTATCGTACTTCCATT
GTCCTATACATCCCTTCGGAATCCCCCTTTCCCTATGGTCCTCAAGCCCTTGGTCTGGGG
AGTAACAGCATAGGGATCAACCATCTCGTCTTGCCACTGCCCGAAATACAGACATGGCTT
CTGTTCCTAAGTCCCTATTCAACTTTTCTTTCTAAGAAACTGGATTTGTCAGCCTCTTTC
TTCACCTCTCAGCTTCCTTGGACTTTGGGGGTAGGTTTGCGTAGACATGCTCACCACAGA
CACAATATCAGCTTCATTCTACAGATGAGGAAGGCAAGCCTTGGGGAGCTTAACCAACTT
GTCGAGACTCATGTATATACCAACACTGAAAAGCAGATATTCCAGACTCCCAGTCATGCC
ACAGGCACACCCCTCAGTGAGAGGTGGGGTTTGTAGTTGAGGCTATTTCCTGCCCAGGGA
GCAGGGAGGCACTCTAGCTTCCCTGAGCTAACGTGGTTCTGCTTGTGTCTGACTTCCAGG
TCTCTGCCCTTTCCAAGCTCACTAGGATGGGCTTCGGGTGTGTCAAATGCCTCAGACAGT
ACAGATCCACACAGAATGGGCATATGCAACCAATCAGTGTCATAAAAAAGAAGGAAATGA
CTCGGGCCCCCTGTGTGTTCAACATGTCGAAGGTATCTGTGCAGCAGAAGAAAGAGGGGC
AAAAGCCCCCAGTGCCACAGGCCAGAGGCAGCAGCTTGGGCCCATGTGGGAGGGTTTGCT
TTCCCCTGCCAAAGTGATGGGCTGCTGCAGCCTGGGGCTTGTGGGAATCCTTCCTGGGCC
TGTGTGGGAAGTGTAGGCAGGGAGAGTGCTGCTTTCCCAAGCTCATCCCAGCTACAGCTA
CCTTTGTGCTCTGGGATTCAGGACCCCCGAGGGGGCTGGCAGGAGAGTCTCTGTTCTCGG
ATGGGTTGTCACCAGGGCATACATGGGAAGTGGGCTCTCTGGAGTCACCCTCCAGGGGAC
AATGCCAATTCCAGACACATTTACTGGAACCCCTACACTGATGACCTTTTGTTGAGGGTT
GAATTATGTCCCCAAAAAAGATACATTGAAGTCCAAACCTCTGGTGTCTATAAATGTGAT
TTTATTTGAAAATGAGGTTTCTATGGACTAAATTGTGTCCCTCCCAAATTCATATTTTGA
AGCCCTAGCCCCCAGTGTGACTATACCTAGAGACAGAGATCTTTAGGAGGTAATTAAGGT
TCAATGAGGTCAGGTGGGTGGGGCCCTAAACCAACAGGAAGGACTGTGGCCTTACTAGAA
AAGGAAGAAAAAGCATTTCCTCTCTTCTAGTATAAAAGGACACAGAAAGAAGGCAGATAT
CTACAAGCCACGAAGAGAGACGTCACTGAGAACTGAATTTGTGTACATTGATCTGGAACT
TCCAGCCTCCAGAACTTGAGAAATACATTTCTGTTGTTTATTTTTTTTTCATGTAATCAA
TTCATTTATCATATATTTATTGAGTGCCTACTATGTGCCAGAGGATACAGCAGTAACAAA
ACTAGGCAAAAATTGTGCCTAAAAGAGGGAAGATGACTTTTCTTAAAGTGTGGAATAAAG
AAAAGTAAGATAGCGGATAGAAGCTTGAAGTGAAAGCAGGTTCACAGGAAGTTTCTTTGG
TCATTTGTTTTGTTTTTAAATAGTGGAAAGATGTATATGTTTATGGAGAAAGATTGCCTT
GAAGATGCAAGAGGAAGAGATGATCAAAATTCAAGAAGAAGCAGAAAGTGATAGAATAAA
GAGCACAAGTGGAGAATTAGTGTTAATGAAAAGAAGGATGCTTCCTTTGATATGAAGTGA
AGGAAGAGAGAATGAGTAAAGACCAAGACTTGAAGTCCCTAGTTTAATAGAGGGAGATTT
CTTCTTTTGATAGCAACAATGGTATTCTGAATTATTTGAAGACATGTCATATTTCTCTTG
TGCCATTTTCCTCCCAGTTTAAACATTCTCATAACCTCTATTCCTCACATGATGTTTTTC
CAGGTCCTTTATTCTTTGGCACTCTCTTCTCTGGACACATTGTATTCTGTCATTGGTCCT
AAAATTTAGATACCCACAATTGAACATACTCCTCTAGATATGGTCTAGCTAATGCAAAAG
AACTGCTGCCTTCCAACTTGTTCAGACATCATATGTTTGTTGTCAAACGCTAAGTTGAGT
TGTTATCTTTTAAGTTTTGTTTTTGTTTTTTTTTTTTTTTTTTAATTCCAAGAGGTGCCC
ACGTTGGCTAAGTACCAAACAGGGTACTAGGGAATTTTACTTCTGAGTTAAATGCCATTC
TAGTTGTTTTTTCTTCATCTCCAGTAAGGTTATCTTTATTCACCAGTTGTTACAATAGCT
GTGGGTCTTGCTTCTCACAGTTTTATGCTGTCTGTGCTATTTTCTCTACTGATCATCACC
ACAATCATTATTGCTTATCATAATTGTTATCTTTATTTTCTCCTTTAATCAAGAATCAGT
CTTCCTTTATCTCATTATTCTCTTTTGCAGGCTTCAGGATAATTATGGTTGGAGTGCACT
GGGGGAACCAGTGCAGCTAAGCTCTGACATCTTTGCATCCCTTTTCCATCTGCTGTTTTG
GCACTCTGGTAGAATAGATAACCTAAAAACGACTTTAAAACATCTAGAAATTTTGGATAA
AATATAACAAACATCCCTTTAAATGCACAACTGATCTTCCATGGAAGTCACAGAAATATA
TAACGCCAAAAAGAAGGGAAGCTGAAACCCAGGGCTGTAAACATGAACATCATCTTCTCT
CCCTTTTTCTTGTGACTTATCTTGTTTTTCTCAGCTTTGGTGCTACCAAGGCTTGACTTT
AATAGGCATTTCCAATCAATGAGAGAATTTCTTTTGCTTTCATCAACAATTCAGTTATTG
ATGTTAACATATATATCATTTGAGTACTTTTCTTTTTTTTATTATTATTATACTTTAAGT
TTTAGGGTCCATGTGCACAATGTGCAGGTTAGTTACGTATGTATACATGTGCCATGCTGG
TGTGCTGCACCCATTAACTCATCATTTAGCATTAGGTATATCTCCTAATGCTATCCCTTC
CCCCTCTCCCCACCCCACAACAGTCCCCAGAGTGTTCCCCTTCCTGTGTCCATGTGTTCT
CATTGTTCAATCCCCATCTATGAGTGAGAACATGCGGTGTTTGGTTTTTTGTCCTTGCAA
TAGTTTACTGAGAATGATGATTTCTAATTTCATCCATGTCCCTAAAGAGCTTCTGCACAG
CAAAAGAAACTACCATCAGAGTGAACAGGCAACCTACAAAATGGGAGAAAATTTTCACAA
CCTGCTCATCTGACAAAGGGCTAATATCCAGAATCTACAATGAACTCAAACAAATTTACA
AGAAAAAAACAAACAACCCCATCAAAAAGTGGGCAAAGGATATGAACAGACACTTCTCAA
AAGAAGACATTTATGCAGCCAAAAGACACATGAAAAAATGCTCATCATCACTGGCCATCA
GAGAAATGCAAACCAAAACCACAATGAGATACCATCTCACACCAGTTAAAATGGCAATCA
TTAAAAAGTCAGGAAACAACAGGTGCTGGAGAGGATGTGGAGAAACAGGAACACTTTTAC
ACTGTTGGTGGGACTGTAAACTAGTTCAACCATTGTGGAAGTCAGTGTGCTGATTCCTCA
GGGATCTAGAACTAGAAATACCATTTGACCCAGCCATCCCATTACTGGGTATATACCCAA
AGGACTATAAATCATGCTGCTATAAAGACACATGCACACGTATGTTTATTGCGGCACTAT
TCACAATAGCAAAGACTTGGAACCAACCCAAATGTCCAACAATGATAGACTGGATTAAGA
AAATGTGGCACATATACACCACGGAATACTGTGCAGCCATAAAAAATGATGAGTTCATGT
CCTTTGTAGGGACACGGATGAAATTGGAAATCATTTCTGTTGTTTAAACCACGAAGTCTA
TGGTATCTGGTTATGACAACCTGAGAATACTAACTCAAGGGTCTTTCGCAGATGTCATTA
AGTTGTTAAAGTGAGGTCATTATGGTGGGTCCTAATCCAAGAGAAGAGATGCATGGACAG
ACGTGCACAACGGGAGGACCAAGCCAAGACACACAGGGAGAATGGCCATGGGAAGATGGA
GGCAGAGATCAAAGTGAGGCACCCACAAGCCAAGAAATGGCAGGAGCTACCAGCAGCTGG
AAGATGCAGAGAAGCATTCCTTCTTAGAGGTTTCAGAGAGAGTATGGTGCTACTGACACC
TTGATTTTGAACTTCTAGTCTCCAGAACTATGAGAGAATAAATTTCTGTTGGTTAAGCCA
TCGAGTTTGTGTAAGTTTGTTATAAGAGCCCTAGGAAATAAACATATCCATTTATTCAGG
AAAGCCTGCTAGAGTGCAAATATTTGGAAAAGATACTACTATGCAAATGTTTGAAAAAGA
TATTGCTCTTGATTCTGCCTTATGGGTTTTTCATTTCTGTAAGCTATTCTCAAAGTTTTG
TTCTTGGACTACTATTGGTAATTAAGACTGCAACATGTTTGGCAACATCAGTTGAGAACT
GTTGCTCTGGGAACGTTTTCGGCAAGCCTCAGCCCTTCTTTTCCCTTGGCTTGCATTGAG
GAGTTAGGTGATACTCTGCTGCTCAGGCCCAGCACCTTTATGGACCGTATTCCCCTGGTG
GAATGACCATCTCTGCTTGCTCTGATTGGCTGTTGGGGTTTTCTAGCATGCCCTATTTAA
TATGTATGATTTATCTCTTACTTCAGTTGGAAGGTACAGTTGCTCTGTAGTTGGCATGCA
GTCATGGTGACTATGAAAATATAAAATAATGTTTTGGTTTACAGACACTTAGAAATAAGT
TGTGTCTCAAAATTGGGTGACTATTCTAGTTATCTGCTACTCAATATCCTTGTGCGAGCC
CTCTTTACCCAGAATCAAACTAAACCATGAGGGGCACTATAGAATGTCACCCCTGGGTCC
AGGATACTATGGGGACTCAGAAGCCAAGCTCCCACTGGGGGATCTAGGGCATGCCCCCAA
GGTAAGATTCCCACCTCTTTGTTCAGCAGGAAGCACCCATCACACAAGGAGGTAGGAATA
AACAAGCATTCGTCAAGAACAAAAGATACAGATGTTCTGCTGGAGCTTGGATACATAGCA
TAAGAGGGAACAGTTCTCACAGGTAAGAGTAAGTTTTCCTCTGGTGGTGACAGTGGGACC
TGTGGGGGAGAGAATTGGGAGTACTGACAGGAAGGCAGAGTGGCTGTCCAAATGAACGGA
TTGTTTGCACATGGCCTTTAGGGCACGTTGTGTTAGCCTTCCATTGCTGCTTATATTAGT
CTGTTTTCACACTGCCCATAAATGCATACCTGAGACTGGATAATTTATAAAGAAAAAGAG
CCTTAATGTACTCATAGTTGCATGTGGCTGGGGAGGCCTCACAATCATGGCAGAAGGTGA
AAGGCACATCTTACATGGAAGCAGACAAGAGAGAATTGAGGACCAAGTGAAAGGGGTTTC
CCCTTATAAAACCATCAGATCACATGAGACTTTTTCACCACCATGAGAACAGTAAGGGGA
AAACTATGCTCATGATTCAATTGTCTCCCACTGGATTCCTCCCACAACACATAGGAATTA
TGGGAGCTAAAATTCAAGATGAGATTTGGGTGAGGACACAGCCAAACCCTATCACTGCTG
TAATCAATTCCCACCAACTTAGTGGCTCGAAACATCACAGATTTATGATCTTATGACGGT
GGAGGTCCCCAAATGGATCTTCTAGGTCTAGAATCAAGGTATCAGCAGACCACTTCTTTT
GGAGGCTCTGGTGGAGAAACCATTTCCTCGCCTTTTCCAGCTTCTAGAGGCTGCCCTTCT
CATTCCTTGGTTCACGGCCACACTCATTTCCATCTCTGCTTCCACTGTGACAACTTCTCT
GCCTCAGACCCTCCTGCTTTGCCTTTGTAAGGACCCTTGTGATGAGATCAGGCCCATCCA
GGATTATCCCTCATCTCAAGACCTTTACCTTAATCACATTTGCAAGGTCTCTTCCACTGT
GTCAGGTAACATTTTCACAGGTTCCAGGGATTAGGGTGTGGACATCTTGGGGAGCTGGAG
GATATTATTTCATCTACCACACACATCTCTACCTTGTACAGGCAAGCACTTGCAAAGTGC
AATGTGATCCTCTGGAGCCACTGTCCTCCCAGAGCTTATATATACTCTGAAAGTCAACTC
TCAGACCACAGCCTCCTGTCCATGCACCACTCTCATCAACACCCCCACCCGAAACACTTT
CACTCCACCCTCTTTGTCCCCTAACTCATGGAGAAGAAAATCTAATTAGTAGGAGTGGAA
TTTGGCTTTCATCTTTACCAGTACTAGAAATATGGTGTGTGTCTTTTTGTAAAAATTCTC
TCAACTAAATTGTTTTTATTAATTTCTGCAAAATGTGAACATCAACTCCCTTCATGTGAA
TGTCAATAAGATTAAATGAGCTGTCTCAGCTCCTAGCCTGTGCAAGCTAACAGCTCAGGA
GATGTTTATTTCTTTCCCTCTTCTTTCCTTAATGAAGCCCTCTCCTTTGACATCTTCAAT
TCTGGAGCGCTTCTTTTCTGAGGCCTTGGCTCCCCCACATTGCCCACCCTTTTCCTGCTC
GTCCACATTTCTGGCTTCTATTCTCTTGTCTTTACCATCTCCCTGAACAATGTTATCCGT
TCCAATGACTTCAACAGTCTCTCCGCTTACATATGATGCCTCTCAAACTCTGATCTCCAA
CTCTTCCAAAGAGCTCTGGACCTTTGTTCCAATTACCTGAAAAACATCTTCTTGGATGTC
CCATTAGCACTGTTAAATCAAACAAGAATTTCCCTCCCTCCTGCCTTGCTGTAGTTCCCC
TAGGGATTCGGTTGTGTGGGAAGATGTGTGGAGAGCTCTTAGTTGACTCCCTTCTCTGCA
GTTCTACCTCTCTAGAGACTTGGAGGACCCACTGTTTCCGCCTCGCTTTTTCAGGCCTAG
AGATTGCTCGCTCCTGGGCTGGCTGCTTCATAATTCCTTATTAGTAGTTTCCCAAGCTTA
CATATCTGTAAATATTTACTTTAGTTAAATTCTCCCCAATTTCCACAATATGTTGGCTGC
ACATGCTTTCTACTAGGAGTCACACAACTATGATAAGAACCAAGAAATATTAGTAAACGT
TTTTTACCATTATTGGCCTATACCCTGGAATAGCCAACAATAACCTAGAACCTATGCAAC
AAGAATATCCAACAAGAACCTAGAGACCTGTCAGTCTATAGGTGGGAACTACAGGATGAG
A
+57 -24
View File
@@ -61,13 +61,6 @@
Volume = {32},
Year = {2016}}
@misc{Suzuki:2016,
title = {Fast and accurate alignment tool for PacBio and Nanopore long reads},
author = {Hajime Suzuki},
journal = {Unpublished},
howpublished = {\href{https://github.com/ocxtal/minialign}{https://github.com/ocxtal/minialign}},
year = {2016}}
@misc{Ruan:2016,
title = {Ultra-fast de novo assembler using long noisy reads},
author = {Jue Ruan},
@@ -172,14 +165,6 @@
Volume = {29},
Year = {2011}}
@article {Suzuki130633,
author = {Suzuki, Hajime and Kasahara, Masahiro},
title = {Acceleration Of Nucleotide Semi-Global Alignment With Adaptive Banded Dynamic Programming},
year = {2017},
note = {doi:10.1101/130633},
publisher = {Cold Spring Harbor Labs Journals},
journal = {bioRxiv}}
@article{Gotoh:1982aa,
Author = {Gotoh, O},
Journal = {J Mol Biol},
@@ -268,18 +253,10 @@
Title = {Mason -- a read simulator for second generation sequencing data},
Year = {2010}}
@article{Langmead:2012fk,
Author = {Langmead, Ben and Salzberg, Steven L},
Journal = {Nat Methods},
Pages = {357-9},
Title = {Fast gapped-read alignment with Bowtie 2},
Volume = {9},
Year = {2012}}
@article{Zaharia:2011aa,
Author = {Zaharia, Matei and others},
Journal = {arXiv:1111:5572},
Title = {Faster and More Accurate Sequence Alignment with SNAP},
Title = {Faster and More Accurate Sequence Alignment with {SNAP}},
Year = {2011}}
@article{Irimia:2008aa,
@@ -305,3 +282,59 @@
Title = {Versatile and open software for comparing large genomes},
Volume = {5},
Year = {2004}}
@article {Li223297,
author = {Li, Heng and others},
title = {New synthetic-diploid benchmark for accurate variant calling evaluation},
year = {2017},
note = {doi:10.1101/223297},
journal = {bioRxiv}
}
@article{Berlin:2015xy,
Author = {Berlin, Konstantin and others},
Journal = {Nat Biotechnol},
Pages = {623-30},
Title = {Assembling large genomes with single-molecule sequencing and locality-sensitive hashing},
Volume = {33},
Year = {2015}}
@article{Gurevich:2013aa,
Author = {Gurevich, Alexey and others},
Journal = {Bioinformatics},
Pages = {1072-5},
Title = {{QUAST}: quality assessment tool for genome assemblies},
Volume = {29},
Year = {2013}}
@article{Li:2010fk,
Author = {Li, Heng and Durbin, Richard},
Journal = {Bioinformatics},
Pages = {589-95},
Title = {Fast and accurate long-read alignment with {Burrows-Wheeler} transform},
Volume = {26},
Year = {2010}}
@article{Marcais:2018aa,
Author = {Mar{\c c}ais, Guillaume and others},
Journal = {PLoS Comput Biol},
Pages = {e1005944},
Title = {{MUMmer4}: A fast and versatile genome alignment system},
Volume = {14},
Year = {2018}}
@article{Li:2009ys,
Author = {Li, Heng and others},
Journal = {Bioinformatics},
Pages = {2078-9},
Title = {The {Sequence Alignment/Map format and SAMtools}},
Volume = {25},
Year = {2009}}
@article{Suzuki:2018aa,
Author = {Suzuki, Hajime and Kasahara, Masahiro},
Journal = {BMC Bioinformatics},
Pages = {45},
Title = {Introducing difference recurrence relations for faster semi-global alignment of long sequences},
Volume = {19},
Year = {2018}}
+185 -74
View File
@@ -1,6 +1,6 @@
\documentclass{bioinfo}
\copyrightyear{2017}
\pubyear{2017}
\copyrightyear{2018}
\pubyear{2018}
\usepackage{graphicx}
\usepackage{hyperref}
@@ -13,14 +13,13 @@
\usepackage{natbib}
\bibliographystyle{apalike}
\usepackage{hyperref}
\DeclareMathOperator*{\argmax}{argmax}
\begin{document}
\firstpage{1}
\title[Aligning nucleotide sequences with minimap2]{Minimap2: versatile pairwise alignment for nucleotide sequences}
\title[Aligning nucleotide sequences with minimap2]{Minimap2: pairwise alignment for nucleotide sequences}
\author[Li]{Heng Li}
\address{Broad Institute, 415 Main Street, Cambridge, MA 02142, USA}
@@ -41,9 +40,10 @@ full-length noisy Direct RNA or cDNA reads, and assembly contigs or closely
related full chromosomes of hundreds of megabases in length. Minimap2 does
split-read alignment, employs concave gap cost for long insertions and
deletions (INDELs) and introduces new heuristics to reduce spurious alignments.
It is 3--4 times faster than mainstream short-read mappers at comparable
accuracy and $\ge$30 times faster at higher accuracy for both genomic and mRNA
reads, surpassing most aligners specialized in one type of alignment.
It is 3--4 times as fast as mainstream short-read mappers at comparable
accuracy, and is $\ge$30 times faster than long-read genomic or cDNA
mappers at higher accuracy, surpassing most aligners specialized in one type of
alignment.
\section{Availability and implementation:}
\href{https://github.com/lh3/minimap2}{https://github.com/lh3/minimap2}
@@ -64,7 +64,7 @@ the thought that 10kb long sequences should be easier to map than 100bp reads
because we can more effectively skip repetitive regions, which are often the
bottleneck of short-read alignment. We confirmed our speculation by achieving
approximate mapping 50 times faster than BWA-MEM~\citep{Li:2016aa}.
\citet{Suzuki130633} extended our work with a fast and novel algorithm on
\citet{Suzuki:2018aa} extended our work with a fast and novel algorithm on
generating base-level alignment, which in turn inspired us to develop minimap2
with added functionality.
@@ -88,12 +88,14 @@ the versatility of minimap2.
Minimap2 follows a typical seed-chain-align procedure as is used by most
full-genome aligners. It collects minimizers~\citep{Roberts:2004fv} of the
reference sequences and indexes them in a hash table. Then for each query
sequence, minimap2 takes query minimizers as \emph{seeds}, finds matches to the
reference, and identifies sets of colinear seeds, which are called
reference sequences and indexes them in a hash table, with the key being the
hash of a minimizer and the value being a list of locations of the minimizer
copies. Then for each query
sequence, minimap2 takes query minimizers as \emph{seeds}, finds exact matches
(i.e. \emph{anchors}) to the reference, and identifies sets of colinear anchors as
\emph{chains}. If base-level alignment is requested, minimap2 applies dynamic
programming (DP) to extend from the ends of chains and to close unseeded
regions between adjacent seeds in chains.
programming (DP) to extend from the ends of chains and to close
regions between adjacent anchors in chains.
Minimap2 uses indexing and seeding algorithms similar to
minimap~\citep{Li:2016aa}, and furthers the predecessor with more accurate
@@ -120,10 +122,13 @@ distance between two anchors is too large); otherwise
\end{equation}
In implementation, a gap of length $l$ costs
\[
\gamma_c(l)=0.01\cdot \bar{w}\cdot|l|+0.5\log_2|l|
\gamma_c(l)=\left\{\begin{array}{ll}
0.01\cdot \bar{w}\cdot|l|+0.5\log_2|l| & (l\not=0) \\
0 & (l=0)
\end{array}\right.
\]
where $\bar{w}$ is the average seed length. For $m$ anchors, directly computing all $f(\cdot)$ with
Eq.~(\ref{eq:chain}) takes $O(m^2)$ time. Although theoretically faster
where $\bar{w}$ is the average seed length. For $N$ anchors, directly computing all $f(\cdot)$ with
Eq.~(\ref{eq:chain}) takes $O(N^2)$ time. Although theoretically faster
chaining algorithms exist~\citep{Abouelhoda:2005aa}, they
are inapplicable to generic gap cost, complex to implement and usually
associated with a large constant. We introduced a simple heuristic to
@@ -133,12 +138,12 @@ We note that if anchor $i$ is chained to $j$, chaining $i$ to a predecessor
of $j$ is likely to yield a lower score. When evaluating Eq.~(\ref{eq:chain}),
we start from anchor $i-1$ and stop the process if we cannot find a better
score after up to $h$ iterations. This approach reduces the average time to
$O(h\cdot m)$. In practice, we can almost always find the optimal chain with
$O(hN)$. In practice, we can almost always find the optimal chain with
$h=50$; even if the heuristic fails, the optimal chain is often close.
\subsubsection{Backtracking}
Let $P(i)$ be the index of the best predecessor of anchor $i$. It equals 0 if
$f(i)=w_i$ or $\argmax_j\{f(j)+\eta(j,i)-\gamma(j,i)\}$ otherwise. For each
$f(i)=w_i$ or $\argmax_j\{f(j)+\alpha(j,i)-\beta(j,i)\}$ otherwise. For each
anchor $i$ in the descending order of $f(i)$, we apply $P(\cdot)$ repeatedly to
find its predecessor and mark each visited $i$ as `used', until $P(i)=0$ or we
reach an already `used' $i$. This way we find all chains with no anchors used
@@ -147,9 +152,11 @@ in more than one chains.
\subsubsection{Identifying primary chains}\label{sec:primary}
In the absence of copy number changes, each query segment should not be mapped
to two places in the reference. However, chains found at the previous step may
have significant or complete overlaps due to repeats in the reference.
have significant or complete overlaps due to repeats in the reference~\citep{Li:2010fk}.
Minimap2 used the following procedure to identify \emph{primary chains} that do
not greatly overlap on the query. Let $Q$ be an empty set initially. For each
not greatly overlap on the query.
Let $Q$ be an empty set initially. For each
chain from the best to the worst according to their chaining scores: if on the
query, the chain overlaps with a chain in $Q$ by 50\% or higher percentage of
the shorter chain, mark the chain as secondary to the chain in $Q$; otherwise,
@@ -157,6 +164,63 @@ add the chain to $Q$. In the end, $Q$ contains all the primary chains. We did
not choose a more sophisticated data structure (e.g. range tree or k-d tree)
because this step is not the performance bottleneck.
For each primary chain, minimap2 estimates its mapping quality with an
empirical formula:
\[
{\rm mapQ}=40\cdot (1-f_2/f_1)\cdot\min\{1,m/10\}\cdot\log f_1
\]
where $\log$ denotes natural logarithm, $m$ is the number of anchors on the primary chain, $f_1$ is the chaining
score, and $f_2\le f_1$ is the score of the best chain that is secondary to the
primary chain. Intuitively, a chain is assigned to a higher mapping quality if
it is long and its best secondary chain is weak.
\subsubsection{Estimating per-base sequence divergence}
Suppose a query sequence harbors $n$ seeds of length $k$, $m$ of which are
present in a chain. We want to estimate the sequence divergence $\epsilon$
between the query and the reference sequences in the chain. This is useful
when base-level alignment is too expensive to perform.
If we model substitutions with a homogeneous Poisson process along the query
sequence, the probablity of seeing $k$ consecutive bases without substitutions
is $e^{-k\epsilon}$. On the assumption that all $k$-mers are independent of
each other, the likelihood function of $\epsilon$ is
\[
\mathcal{L}(\epsilon|n,m,k)=e^{-m\cdot k\epsilon}(1-e^{-k\epsilon})^{n-m}
\]
The maximum likelihood estimate of $\epsilon$ is
\[
\hat{\epsilon}=\frac{1}{k}\log\frac{n}{m}
\]
In reality, sequencing errors are sometimes clustered and $k$-mers are not
independent of each other, especially when we take minimizers as seeds. These
violate the assumptions in the derivation above. As a result, $\hat{\epsilon}$
is only approximate and can be biased. It also ignores long deletions from the
reference sequence. In practice, fortunately, $\hat{\epsilon}$ is often close
to and strongly correlated with the sequence divergence estimated from
base-level alignments. On the several datasets used in
Section~\ref{sec:long-genomic}, the Spearman correlation coefficient is around
$0.9$.
\subsubsection{Indexing with homopolymer compressed $k$-mers}
SmartDenovo
(\href{https://github.com/ruanjue/smartdenovo}{https://github.com/ruanjue/smartdenovo};
J. Ruan, personal communication) indexes reads with homopolymer-compressed (HPC)
$k$-mers and finds the strategy improves overlap sensitivity for SMRT reads.
Minimap2 adopts the same heuristic.
The HPC string of a string $s$, denoted by ${\rm HPC}(s)$, is constructed by
contracting homopolymers in $s$ to a single base. An HPC $k$-mer of $s$ is a
$k$-long substring of ${\rm HPC}(s)$. For example, suppose $s={\tt GGATTTTCCA}$,
${\rm HPC}(s)={\tt GATCA}$ and the first HPC 4-mer is ${\tt GATC}$.
To demonstrate the effectiveness of HPC $k$-mers, we performed read overlapping
for the example {\it E. coli} SMRT reads from PBcR~\citep{Berlin:2015xy}, using
different types of $k$-mers. With normal 15bp minimizers per 5bp window,
minimap2 finds 90.9\% of $\ge$2kb overlaps inferred from the read-to-reference
alignment. With HPC 19-mers per 5bp window, minimap2 finds 97.4\% of overlaps. It achieves this
higher sensitivity by indexing 1/3 fewer minimizers, which further helps
performance. HPC-based indexing reduces the sensitivity for current ONT reads, though.
\subsection{Aligning genomic DNA}\label{sec:genomic}
\subsubsection{Alignment with 2-piece affine gap cost}
@@ -194,14 +258,14 @@ performance of minimap2. Traditional SSE implementations~\citep{Farrar:2007hs}
based on Eq.~(\ref{eq:ae86}) can achieve 16-way parallelization for short
sequences, but only 4-way parallelization when the peak alignment score reaches
32767. Long sequence alignment may exceed this threshold. Inspired by
\citet{Wu:1996aa} and the following work, \citet{Suzuki130633} proposed a
\citet{Wu:1996aa} and the following work, \citet{Suzuki:2018aa} proposed a
difference-based formulation that lifted this limitation.
In case of 2-piece gap cost, define
\[
\left\{\begin{array}{ll}
u_{ij}\triangleq H_{ij}-H_{i-1,j} & v_{ij}\triangleq H_{ij}-H_{i,j-1} \\
x_{ij}\triangleq E_{i+1,j}-H_{ij} & \tilde{x}_{ij}\triangleq \tilde{E}_{i+1,j}-\tilde{H}_{ij} \\
y_{ij}\triangleq F_{i,j+1}-H_{ij} & \tilde{y}_{ij}\triangleq \tilde{F}_{i,j+1}-\tilde{H}_{ij}
x_{ij}\triangleq E_{i+1,j}-H_{ij} & \tilde{x}_{ij}\triangleq \tilde{E}_{i+1,j}-H_{ij} \\
y_{ij}\triangleq F_{i,j+1}-H_{ij} & \tilde{y}_{ij}\triangleq \tilde{F}_{i,j+1}-H_{ij}
\end{array}\right.
\]
We can transform Eq.~(\ref{eq:ae86}) to
@@ -261,11 +325,11 @@ y_{rt}&=&\max\{0,y_{r-1,t}+u_{r-1,t}-z_{rt}+q\}-q-e\\
\end{equation*}
In this formulation, cells with the same diagonal index $r$ are independent of
each other. This allows us to fully vectorize the computation of all cells on
the same anti-diagonal in one inner loop. It also simplifies banded alignment,
the same anti-diagonal in one inner loop. It also simplifies banded alignment (500bp band width by default),
which would be difficult with striped vectorization~\citep{Farrar:2007hs}.
On the condition that $q+e<\tilde{q}+\tilde{e}$ and $e>\tilde{e}$, the initial
values in the diagonal-antidiagonal formuation is
values in the diagonal-antidiagonal formuation are
\[
\left\{\begin{array}{l}
x_{r-1,-1}=y_{r-1,r}=-q-e\\
@@ -284,12 +348,19 @@ r\cdot(e-\tilde{e})-(\tilde{q}-q)-\tilde{e} & (r=\lceil\frac{\tilde{q}-q}{e-\til
\]
These can be derived from the initial values for Eq.~(\ref{eq:ae86}).
When performing global alignment, we do not need to compute $H_{rt}$ in each cell.
We use 16-way vectorization throughout the alignment process. When extending
alignments from ends of chains, we need to find the cell $(r,t)$ where $H_{rt}$
reaches the maximum. We resort to 4-way vectorization to compute
$H_{rt}=H_{r-1,t}+u_{rt}$. Because this computation is simple,
Eq.~(\ref{eq:suzuki}) is still the dominant performance bottleneck.
In practice, our 16-way vectorized implementation of global alignment is three
times as fast as Parasail's 4-way vectorization~\citep{Daily:2016aa}. Without
banding, our implementation is slower than Edlib~\citep{Sosic:2017aa}, but with
a 1000bp band, it is considerably faster. When performing global alignment
between anchors, we expect the alignment to stay close to the diagonal of the
DP matrix. Banding is applicable most of time.
DP matrix. Banding is applicable most of the time.
\subsubsection{The Z-drop heuristic}
@@ -313,6 +384,16 @@ alignment between the two subsequences involved in the global alignment, but
this time with the one subsequence reverse complemented. This additional
alignment step may identify short inversions that are missed during chaining.
\subsubsection{Filtering out misplaced anchors}
Due to sequencing errors and local homology, some anchors in a chain may be
wrong. If we blindly align regions between two misplaced anchors, we will
produce a suboptimal alignment. To reduce this artifact, we filter out
anchors that lead to a $>$10bp insertion and a $>$10bp deletion at the same
time, and filter out terminal anchors that lead to a long gap towards the ends
of a chain. These heuristics greatly alleviate the issues with misplaced
anchors, but they are unable to fix all such errors. Local misalignment is a
limitation of minimap2 which we hope to address in future.
\subsection{Aligning spliced sequences}
The algorithm described above can be adapted to spliced alignment. In this
@@ -351,7 +432,7 @@ p/2 & \mbox{if $T[i+1,i+3]$ is ${\tt GTC}$ or ${\tt GTT}$} \\
p & \mbox{otherwise}
\end{array}\right.\]
where $T[i,j]$ extracts a substring of $T$ between $i$ and $j$ inclusively.
$d(i)$ penalizes non-canonical donor sites with $p$ and less frequent Eukayotic
$d(i)$ penalizes non-canonical donor sites with $p$ and less frequent Eukaryotic
splicing signal ${\tt GT[C/T]}$ with $p/2$~\citep{Irimia:2008aa}. Similarly,
\[a(i)=\left\{\begin{array}{ll}
0 & \mbox{if $T[i-2,i]$ is ${\tt CAG}$ or ${\tt TAG}$} \\
@@ -378,13 +459,13 @@ alignment.
\subsection{Aligning short paired-end reads}
During chainging, minimap2 takes a pair of reads as one fragment with a gap of
During chaining, minimap2 takes a pair of reads as one fragment with a gap of
unknown length in the middle. It applies a normal gap cost between seeds on the
same read but is a more permissive gap cost between seeds on different reads.
More precisely, the gap cost during chaining is:
More precisely, the gap cost during chaining is ($l\not=0$):
\[
\gamma_c(l)=\left\{\begin{array}{ll}
0.01\cdot\bar{w}\cdot l+0.5\log_2 l & \mbox{if two seeds on the same read} \\
0.01\cdot\bar{w}\cdot |l|+0.5\log_2 |l| & \mbox{if two seeds on the same read} \\
\min\{0.01\cdot\bar{w}\cdot|l|,\log_2|l|\} & \mbox{otherwise}
\end{array}\right.
\]
@@ -397,26 +478,40 @@ consistent paired-end alignments.
\section{Results}
\subsection{Aligning long genomic reads}
Minimap2 is implemented in the C programming language and comes with APIs in
both C and Python. It is distributed under the MIT license, free to both
commercial and academic uses. Minimap2 uses the same base algorithm for all
applications, but it has to apply different sets of parameters depending on
input data types. Similar to BWA-MEM, minimap2 introduces `presets' that
modify multiple parameters with a simple invocation. Detailed settings
and command-line options can be found in the minimap2 manpage. In addition to
the applications evaluated in the following sections, minimap2 also retains
minimap's functionality to find overlaps between long reads and to search
against large multi-species databases such as \emph{nt} from NCBI.
\subsection{Aligning long genomic reads}\label{sec:long-genomic}
\begin{figure}[!tb]
\centering
\includegraphics[width=.5\textwidth]{roc-color.pdf}
\caption{Evaluation on aligning simulated reads. Simulated reads were mapped
to the primary assembly of human genome GRCh38. A read is considered correctly
mapped if the true position overlaps with the best mapping position by 10\% of
the read length. Read alignments are sorted by mapping quality in the
descending order. For each mapping quality threshold, the fraction of
alignments with mapping quality above the threshold and their error rate are
mapped if its longest alignment overlaps with the true interval, and the
overlap length is $\ge$10\% of the true interval length. Read alignments are
sorted by mapping quality in the descending order. For each mapping quality
threshold, the fraction of alignments (out of the number of input reads) with
mapping quality above the threshold and their error rate are
plotted along the curve. (a) long-read alignment evaluation. 33,088 $\ge$1000bp
reads were simulated using pbsim~\citep{Ono:2013aa} with error profile sampled
from file `m131017\_060208\_42213\_*.1.*' downloaded at
\href{http://bit.ly/chm1p5c3}{http://bit.ly/chm1p5c3}. The N50 read length is
11,628. Aligners were run under the default setting for SMRT reads.
(b) short-read alignment evaluation. 10 million pairs of 150bp reads were
simulated using mason2~\citep{Holtgrewe:2010aa} with option
`\mbox{--illumina-prob-mismatch-scale 2.5}'. Short-read aligners were run under the
default setting except for changing the maximum fragment length to
Kart outputted all alignments at mapping quality 60, so is not shown in the
figure. It mapped nearly all reads with 4.1\% of alignments being wrong, less
accurate than others. (b) short-read alignment evaluation. 10 million pairs of
150bp reads were simulated using mason2~\citep{Holtgrewe:2010aa} with option
`\mbox{--illumina-prob-mismatch-scale 2.5}'. Short-read aligners were run under
the default setting except for changing the maximum fragment length to
800bp.}\label{fig:eval}
\end{figure}
@@ -425,7 +520,7 @@ BLASR~(v1.MC.rc64; \citealp{Chaisson:2012aa}),
BWA-MEM~(v0.7.15; \citealp{Li:2013aa}),
GraphMap~(v0.5.2; \citealp{Sovic:2016aa}),
Kart~(v2.2.5; \citealp{Lin:2017aa}),
minialign~(v0.5.3; \citealp{Suzuki:2016}) and
minialign~(v0.5.3; \href{https://github.com/ocxtal/minialign}{https://github.com/ocxtal/minialign}) and
NGMLR~(v0.2.5; \citealp{Sedlazeck169557}). We excluded rHAT~\citep{Liu:2016ab}
and LAMSA~\citep{Liu:2017aa} because they either
crashed or produced malformatted output. In this evaluation, minimap2 has
@@ -434,11 +529,11 @@ higher mapping accuracy (Fig.~\ref{fig:eval}a). Minimap2 and
NGMLR provide better mapping quality estimate: they rarely give repetitive hits
high mapping quality. Apparently, other aligners may
occasionally miss close suboptimal hits and be overconfident in wrong mappings.
On run time, minialign is slightly faster than minimap2 and Kart. They are over
30 times faster than the rest. Minimap2 consumed 6.1GB memory at the peak,
more than BWA-MEM but less than others.
On run time, minimap2 took 200 CPU seconds, comparable to minialign and Kart, and is over
30 times faster than the rest. Minimap2 consumed 6.8GB memory at the peak,
more than BWA-MEM (5.4GB), similar to NGMLR and less than others.
On real human SMRT reads, the relative performance and sensitivity of
On real human SMRT reads, the relative performance and fraction of mapped reads reported by
these aligners are broadly similar to the metrics on simulated data. We are
unable to provide a good estimate of mapping error rate due to the lack of the
truth. On ONT $\sim$100kb human reads~\citep{Jain128835}, BWA-MEM failed.
@@ -480,7 +575,7 @@ Peak RAM (GByte) & 8.9 & 14.5 & 3.2 & 29.2\vspace{1em}\\
\% approx. introns & 91.8\% & 96.9\% & 92.5\% & 82.4\% \\
\botrule
\end{tabular}
}{Mouse reads (AC:SRR5286960) were mapped to the primary assembly of mouse
}{Mouse cDNA reads (AC:SRR5286960; R9.4 chemistry) were mapped to the primary assembly of mouse
genome GRCm38 with the following tools and command options: minimap2 (`-ax
splice'); GMAP (`-n 0 --min-intronlength 30 --cross-species'); SpAln (`-Q7 -LS
-S3'); STARlong (according to
@@ -489,7 +584,7 @@ compared to the EnsEMBL gene annotation, release 89. A predicted intron
is \emph{novel} if it has no overlaps with any annotated introns. An intron
is \emph{exact} if it is identical to an annotated intron. An intron is
\emph{approximate} if both its 5'- and 3'-end are within 10bp around the ends
of an annotated intron.}
of an annotated intron. Chimeric alignments are defined in the SAM spec~\citep{Li:2009ys}.}
\end{table}
We next aligned real mouse reads~\citep{Byrne:2017aa} with GMAP~(v2017-06-20;
@@ -500,14 +595,18 @@ more junctions with a higher percentage being exactly or approximately correct.
Minimap2 is over 40 times faster than GMAP and SpAln. While STAR is close to
minimap2 in speed, it does not work well with noisy reads.
We have also evaluated spliced aligners on public Iso-Seq data (human Alzheimer
brain from \href{http://bit.ly/isoseqpub}{http://bit.ly/isoseqpub}). The
observation is similar: minimap2 is faster at higher junction accuracy.
On a private Nanopore Direct RNA data set with $\sim$17\% sequencing error rate
(N. Loman, personal communication), minimap2 aligned 96\,467 introns
from 37\,068 mapped reads with 95.4\% of them consistent with human gene
annotations. In comparison, only 74.8\% of GMAP introns found in known gene
annotations.
We have also evaluated spliced aligners on a human Nanopore Direct RNA-seq
dataset (\href{http://bit.ly/na12878ont}{http://bit.ly/na12878ont}). Minimap2
aligned 10 million reads in $<$1 wall-clock hour using 16 CPU cores. 94.2\% of
aligned splice junctions consistent with gene annotations. In comparison,
GMAP under option `-k 14 -n 0 --min-intronlength 30 --cross-species' is 160
times slower; 68.7\% of GMAP junctions are found in known gene annotations. The
percentage increases to 84.1\% if an aligned junction within 10bp from an
annotated junction is considered to be correct. On a public Iso-Seq dataset
(human Alzheimer brain from
\href{http://bit.ly/isoseqpub}{http://bit.ly/isoseqpub}), minimap2 is also
faster at higher junction accuracy in comparison to other aligners in
Table~\ref{tab:intron}.
We noted that GMAP and SpAln have not been optimized for noisy reads. We are
showing the best setting we have experimented, but their developers should be
@@ -542,7 +641,7 @@ simulated data set than Bowtie2 and SNAP but less accurate than BWA-MEM
(Fig.~\ref{fig:eval}b). Closer investigation reveals that BWA-MEM achieves
a higher accuracy partly because it tries to locally align a read in a small
region close to its mate. If we disable this feature, BWA-MEM becomes slightly
less accurate than minimap2. We might consider to implement a similar heuristic
less accurate than minimap2. We might implement a similar heuristic
in minimap2 in future.
To evaluate the accuracy of minimap2 on real data, we aligned human reads
@@ -551,22 +650,31 @@ with GATK HaplotypeCaller v3.5~\citep{Depristo:2011vn}. This run was sequenced
from experimentally mixed CHM1 and CHM13 cell lines. Both of them are homozygous
across the whole genome and have been \emph{de novo} assembled with SMRT reads
to high quality. This allowed us to construct an independent truth variant
data set
(\href{https://github.com/lh3/CHM-eval}{https://github.com/lh3/CHM-eval}) for
dataset~\citep{Li223297} for
ERR1341796. In this evaluation, minimap2 has higher SNP false negative rate
(FNR; 2.5\% of minimap2 vs 2.2\% of BWA-MEM), but fewer false positive SNPs per
million bases (FPPM; 3.0 vs 3.9), lower 2--50bp INDEL FNR (7.3\% vs 7.5\%) and
similar INDEL FPPM (both 1.0). Minimap2 is broadly similar to BWA-MEM in the
(FNR; 2.6\% of minimap2 vs 2.3\% of BWA-MEM), but fewer false positive SNPs per
million bases (FPPM; 7.0 vs 8.8), similar INDEL FNR (11.2\% vs 11.3\%) and
similar INDEL FPPM (6.4 vs 6.5). Minimap2 is broadly comparable to BWA-MEM in the
context of small variant calling.
\subsection{Other applications}
\subsection{Aligning long-read assemblies}
Minimap2 retains minimap's functionality to find overlaps between long reads
and to search against large multi-species databases such as \emph{nt} from
NCBI. Minimap2 can also align similar genomes or different assemblies of the
same species. It took 7 wall-clock minutes over 8 CPU cores to align a human
SMRT assembly (AC:GCA\_001297185.1) to GRCh38, over 20 times faster
MUMmer4~\citep{Kurtz:2004zr}.
Minimap2 can align a SMRT assembly (AC:GCA\_001297185.1) against GRCh38 in 7
minutes using 8 CPU cores, over 20 times faster than nucmer from
MUMmer4~\citep{Marcais:2018aa}. With the paftools.js script from the minimap2
package, we called 2.67 million single-base substitutions out of 2.78Gbp
genomic regions. The transition-to-transversion ratio (ts/tv) is 2.01. In
comparison, using MUMmer4's dnadiff pipeline, we called 2.86 million
substitutions in 2.83Gbp at ts/tv=1.87. Given that ts/tv averaged across the
human genome is about 2 but ts/tv averaged over random errors is 0.5, the
minimap2 callset arguably has higher precision at lower sensitivity.
The sample being assembled is a female. Minimap2 still called 201 substitutions
on the Y chromosome. These substitutions all come from one contig aligned at
96.8\% sequence identity. The contig could be a segmental duplication
absent from GRCh38. In constrast, dnadiff called 9070 substitutions on the Y
chromosome across 73 SMRT contigs. This again implies our minimap2-based
pipeline has higher precision.
\section{Discussions}
@@ -584,9 +692,9 @@ involving $>$100kb introns, which was impractically slow ten years ago. The
minimap2 chaining algorithm is fast and highly accurate by itself. In fact,
chaining alone is more accurate than all the other long-read mappers in
Fig.~\ref{fig:eval}a (data not shown). This accuracy helps to reduce downstream
base-level alignment of candidate chains, which is still times slower than
base-level alignment of candidate chains, which is still several times slower than
chaining even with the Suzuki-Kasahara improvement. In addition, taking a
general form, minimap2 chaining can be adapted to non-typical data types such
general form, minimap2 chaining can be adapted to non-typical data types such as
spliced reads and multiple reads per fragment. This gives us the opportunity to
extend the same base algorithm to a variety of use cases.
@@ -597,16 +705,19 @@ uniqueness and reduce unsuccessful extensions. Minimap2 indexes reference
k-mers with a hash table instead. Such fixed-length seeds are inferior to
variable-length seeds in theory, but can be computed much more efficiently in
practice. When a query sequence has multiple seed hits, we can afford to skip
some highly repetitive seeds without affecting the final accuracy. This further
alleviates the concern with the uniqueness of seeds. Hash table is the ideal
data structure for mapping long query sequences.
highly repetitive seeds without affecting the final accuracy. This further
alleviates the concern with the seeding uniqueness. At the same time, at low
sequence identity, it is rare to see long seeds anyway. Hash table is the ideal
data structure for mapping long noisy sequences.
\section*{Acknowledgements}
We owe a debt of gratitude to H. Suzuki and M. Kasahara for releasing their
masterpiece and insightful notes before formal publication. We thank M.
Schatz, P. Rescheneder and F. Sedlazeck for pointing out the limitation of
BWA-MEM. We are also grateful to early minimap2 testers who have greatly helped
to suggest features and to fix various issues.
BWA-MEM. We are also grateful to minimap2 users who have greatly helped to
suggest features and to fix various issues.
\paragraph{Funding\textcolon} NHGRI 1R01HG010040-01
\bibliography{minimap2}