Compare commits

..
584 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
Heng Li 3b518271ee Release minimap2-2.5 (r572) 2017-11-11 11:29:28 -05:00
Heng Li 481d8239e9 Merge pull request #57 from cjw85/strand_typo
Fix typo in strand property
2017-11-10 20:36:19 -05:00
cwright 4f77b0c1ed Fix typo in strand property 2017-11-11 01:18:44 +00:00
Heng Li d7a31e40e6 r569: last commit is buggy 2017-11-09 23:20:41 -05:00
Heng Li dd18cd75de r568: revert - don't take max(dp_max, dp_score) 2017-11-09 23:12:48 -05:00
Heng Li 99a2709913 r567: minor change to #56 2017-11-09 19:17:45 -05:00
Heng Li 032068a747 Merge pull request #56 from mvdbeek/softclipping
Implement -Y for soft clipping of supp. alignments
2017-11-09 19:12:47 -05:00
mvdbeek 1cb0bf4bef Implement -Y for soft clipping of supp. alignments
I tried to base this on bwa-mem and it seems to work for sam alignments.
2017-11-09 19:22:36 +01:00
Heng Li 422b43374e Merge pull request #55 from martinghunt/fix_python3_strings
Bug fix with byte strings in Python3
2017-11-09 09:37:41 -05:00
martinghunt 29a26e3eea Bug fix with byte strings in Python3 2017-11-09 13:57:15 +00:00
Heng Li a7b38f6900 r562: fixed a severe bug: wrong query start 2017-11-08 22:31:05 -05:00
Heng Li e896c9ec05 r559: prefer a chain involving more segments 2017-11-08 13:22:16 -05:00
Heng Li 98ba8928c6 r558: dp_max no less than dp_score 2017-11-08 10:06:10 -05:00
Heng Li bcf8462d20 Merge pull request #52 from cvdelannoy/patch-1
Update README.md
2017-11-08 07:50:31 -05:00
Carlos de Lannoy c047c852ce Update README.md
25: changed -x ava-one to -x ava-ont
2017-11-08 13:07:01 +01:00
Heng Li b24d68ae9f r557: fixed another mapq underestimate
When a chain is split during base-level alignment, its chaining score is
reduced. However, the chaining score of its suboptimal chain remains the same.
This leads to underestimated mapping quality.
2017-11-07 23:20:49 -05:00
Heng Li 65deedfa96 r556: bugfix - underestimate mapq for split aln 2017-11-07 22:37:12 -05:00
Heng Li 21a46ba652 Release minimap2-2.4 (r555) 2017-11-06 12:54:02 -05:00
Heng Li 1617b87ee1 this will become version 3 at arXiv 2017-11-06 10:57:12 -05:00
Heng Li 2191ac58ad two discussion paragraphs; need one more 2017-11-05 12:27:52 -05:00
Heng Li fa5a645ca5 r552: fixed a tiny typo on struct packing
The old packing wastes memory, thought very small.
2017-11-05 08:27:26 -05:00
Heng Li c2b09356b8 moved conclusion to result; need new discussion 2017-11-04 22:19:46 -04:00
Heng Li a3f0aa1d5b r550: fixed -L issues with secondary and supp aln 2017-11-04 12:13:38 -04:00
Heng Li 52ffbc9e0c added bowtie2 and snap versions 2017-11-02 15:51:23 -04:00
Heng Li a9790c0f1d added two bowtie2 numbers for comparison 2017-11-02 15:44:48 -04:00
Heng Li d0ac78ac08 updated the tech note 2017-11-02 15:37:24 -04:00
Heng Li 22290db3e4 r546: minor mapQ tuning 2017-11-01 13:20:39 -04:00
Heng Li cd24dc8834 r545: removed option -i, not working well 2017-10-31 22:23:27 -04:00
Heng Li b8e758df0f r544: increased PE mapQ 2017-10-31 16:55:02 -04:00
Heng Li 311fa90030 r543: applied some sr mapq changes to long reads 2017-10-31 15:24:05 -04:00
Heng Li fb8a1b5536 r542: tuning mapQ calculation 2017-10-31 14:25:09 -04:00
Heng Li 7f11f4c4d4 Instructions on different long RNA-seq techs 2017-10-29 13:58:25 -04:00
Heng Li 285eb0da05 r540: removed a buggy debugging line 2017-10-29 00:02:41 -04:00
Heng Li 192217a10c r539: use --splice-flank=yes by default
In human/mouse, the GTr..yAG pattern occurs to 91/92% of all GT-AG introns.
Modeling r..y clearly leads to higher accuracy. However, in SIRV, this
percentage is reduced to ~60%. The default "--splice --splice-flank=yes"
leads to lower accuracy. If someone benchmark minimap2 on SIRV, this would be
bad, but minimap2 is developed for practical applications, not for benchmarks.
I will live with that.
2017-10-28 22:29:55 -04:00
Heng Li f22a94e868 r538: fixed a long existing bug in HPC k-mer (#47)
This bug may lead to a wrong minimizer when a HPC k-mer is longer than 256bp.
When there is a seed match involving this wrong HPC k-mer, the correct seed
sequences do not match in fact. This violates the assumption in align.c and
subsequently causes a segfault, which is what #47 has caught. This bug lurked
in the earliest piece of code and affected all released minimap2 versions so
far. It is extremely rare and does not affect the prebuilt GRCh37/38 indices.
2017-10-28 19:21:10 -04:00
Heng Li 79b0caca95 r537: model the next base to GT/AG
[PMID:18688272] shows that the base following GT tends to be A or G (i.e. R) in
both human and yeast, and that the base preceeding AG tends to be C or T (i.e.
Y). In the new model, we pay no cost to GTr..yAG, but we pay half of the cost
if there is no r or y. This improves the junction accuracy when mapping to
human and mouse and decreases the accuacy when mapping to SIRV. My guess is
that SIRV does not honor this trend. Need to investigate in future.

Also in this commit, --cost-non-gt-ag is aliased to -C. The default is changed
to 9 instead of 5. I also added --splice-flank to enable the above model. This
may become the default once I confirm my hypothesis on SIRV.
2017-10-28 00:25:01 -04:00
Heng Li afc2f2e84b r536: removed an unnecessary assert() 2017-10-24 21:08:54 -04:00
Heng Li e6f66f2f3b disabled download counts
seems not working any more
2017-10-24 14:40:08 -04:00
Heng Li 70735098e2 fixed a typo in README 2017-10-24 14:39:33 -04:00
Heng Li d4b5dfc297 r533: added --no-pairing
to prevent the use of any pairing information for paired-end reads.
2017-10-23 14:09:32 -04:00
Heng Li 5acd709524 updated the download link to v2.3 2017-10-23 13:43:31 -04:00
Heng Li 306e4541f8 Released minimap2-2.3 (r531) 2017-10-22 23:13:35 -04:00
Heng Li 1dd221ad82 a bit more on short read mapping
The tech note still needs improvement. Will do that after the release of v2.3.
2017-10-22 18:38:35 -04:00
Heng Li c6b6392b70 minor wording changes 2017-10-21 23:46:36 -04:00
Heng Li dc37aee881 minor wording changes 2017-10-21 23:38:05 -04:00
Heng Li 37e627aa98 note on long cigar in README 2017-10-21 22:28:06 -04:00
Heng Li beeb806829 r526: fixed a bug when HPC is in use
It happened when the query HPC minimizer is longer than the reference HPC
minimizer close to the beginning of a contig. We may get a negative coordinate,
which causes an assertion failure.
2017-10-21 19:54:04 -04:00
Heng Li be7f3c4ffe r525: fixed a bug in chaining; handle ovlp ends 2017-10-20 21:34:52 -04:00
Heng Li bd04372873 r524: reverted to bwa-mem end bonus
and reduced the cost of clipping when filtering by identity
2017-10-20 16:57:31 -04:00
Heng Li 15ed0712c2 r523: fixed a performance bug in ksw2_ll
Wont' affect accuracy.
2017-10-20 13:00:10 -04:00
Heng Li 55dcbefe87 updated text (unfinished) 2017-10-20 12:44:54 -04:00
Heng Li 8abba332ad replaced mapQ plot with sr roc
figure legend and text to be updated later
2017-10-19 23:43:17 -04:00
Heng Li 4683da2455 r520: added option -L to write long cigar to CG 2017-10-17 17:32:44 -04:00
Heng Li ffd953029f r519: fixed a severe bug that misses long alns 2017-10-17 15:52:36 -04:00
Heng Li 04cf4ebf5e r518: increased the default -K to 500M
This helps multi-thread performance for ultra-long reads.
2017-10-17 13:21:29 -04:00
Heng Li 25ffd72690 r517: replaced --print-2nd with --secondary 2017-10-17 11:41:56 -04:00
Heng Li aa2d9d4e1b r516: throw a warning if -N0 is used 2017-10-16 14:55:35 -04:00
Heng Li addb61bcb2 r515: more conservative hit exclusion
When a hit covers a long query subsequence that has not been covered by better
primary hits, this hit is more likely to become a new primary hit.
2017-10-16 13:58:01 -04:00
Heng Li b24c9c90c7 updated mappy and example.c 2017-10-16 11:15:07 -04:00
Heng Li adf6cd7f52 r513: merged pre- and post-cigar blen and mlen
This saves a bit memory and is cleaner.
2017-10-16 10:55:18 -04:00
Heng Li e6f525edaf r512: option to filter poorly aligned reads 2017-10-16 10:38:22 -04:00
Heng Li 858213d513 r511: fixed wrong primary sam record 2017-10-12 23:02:18 -04:00
Heng Li dea3b60918 r510: fixed an off-by-1 bug for unmapped mate 2017-10-12 17:31:13 -04:00
Heng Li 7c555f9b7e r508: use two I/O threads for mapping
-x sr applies this option by default
2017-10-12 14:56:01 -04:00
Heng Li 2801ed9b4b r507: -K not working as is intended (#36) 2017-10-12 14:16:05 -04:00
Heng Li 27025c70a7 added evaluation scripts to README 2017-10-12 12:55:10 -04:00
Heng Li 9bafbe4e70 added Getting help and cs 2017-10-12 12:40:52 -04:00
Heng Li ce06188203 r506: fixed a memory leak 2017-10-12 10:12:22 -04:00
Heng Li 9862a75cd3 r505: a bit code simplification 2017-10-11 21:54:32 -04:00
Heng Li 3073f4a758 r504: better heuristics to reduce excessive ext 2017-10-11 21:42:11 -04:00
Heng Li ba6ddda6b0 Merge pull request #35 from mcshane/sam_fix
fix sam output for some unmapped queries
2017-10-11 20:09:16 -04:00
Heng Li 9364bc64d7 r501: added end_bonus to extz2 2017-10-11 09:39:41 -04:00
Shane McCarthy 5498565157 fix sam output for some unmapped queries 2017-10-11 08:46:24 +01:00
Heng Li 65abdb8f3c r500: temporarily disabled region trunc
because it is causing other problems.
2017-10-11 00:16:04 -04:00
Heng Li 7345621759 r499: end bonus working; DP region needs improve! 2017-10-11 00:14:25 -04:00
Heng Li ca632f907b r498: fixed a bug when merging like "4I5I" 2017-10-10 21:22:37 -04:00
Heng Li 6c78a980b6 r497: the previous change not working at the ends 2017-10-10 17:32:28 -04:00
Heng Li c217eecdb7 r496: avoid DP extending into another chain
When deciding the region for DP, exclude regions in the adjacent chain
2017-10-10 17:25:12 -04:00
Heng Li 13b66aad4d r495: fix impropriate CIGAR
1. Not left aligned
2. In one case, 50M24D50M becomes 24D100M. The leading D needs to be removed.
3. Avoid identical hits after DP
2017-10-10 11:59:44 -04:00
Heng Li 46fa520db9 r494: simpler and better SR gap filling
Still one thing to do: left alignment
2017-10-09 22:02:30 -04:00
Heng Li 1e53610fb4 r493: reduced calling extd2 for ungapped aln
Still need to improve in case of 3I5M3D
2017-10-09 21:13:34 -04:00
Heng Li 9396d9e11b r452: typo in the last commit 2017-10-09 10:05:32 -04:00
Heng Li 198849a716 r491: an ambiguous base costs the same as gap ext 2017-10-09 09:59:42 -04:00
Heng Li 9fea4d16b3 r490: improved short-read extension heuristic
Now we find the best scoring ungapped seeded segment and then extend from it.
There is no gap filling for short reads.
2017-10-08 21:36:34 -04:00
Heng Li f9415628a8 r489: don't use approximate zdrop
it doesn't work well
2017-10-08 19:29:09 -04:00
Heng Li 61e56c941d r488: parameter to control max fragment length 2017-10-07 23:54:32 -04:00
Heng Li f150257a0d r487: demote "map10k"; improved README 2017-10-07 19:19:40 -04:00
Heng Li bf2d4f7aec r486: treat "U" as "T" for RNA reads (#33) 2017-10-07 18:53:25 -04:00
Heng Li 47b86e765b minor wording changes 2017-10-06 15:34:20 -04:00
Heng Li 56acf6ee28 completed README rewrite 2017-10-06 14:26:02 -04:00
Heng Li 4f6244bd4a revamped README; not finished yet 2017-10-06 13:02:25 -04:00
Heng Li c6384ed2c8 r482: increased short-read bandwidth to 100
This has very minor effect on speed.
2017-10-06 10:20:32 -04:00
Heng Li 2833a9c255 150bp mason2 simulation 2017-10-06 09:46:59 -04:00
Heng Li de2fcc1bf3 updated the mappy 2017-10-05 17:10:07 -04:00
Heng Li e0baf1ad54 r479: a bit code cleanup 2017-10-05 16:15:14 -04:00
Heng Li f266092699 r478: simplied useless code, a tiny bit 2017-10-05 15:56:00 -04:00
Heng Li 9c5767f9ed r477: renamed multi_seg to frag_mode 2017-10-05 15:48:17 -04:00
Heng Li ae2adf04d4 r476: multi-file fragment mode working 2017-10-05 15:39:26 -04:00
Heng Li b839758335 r475: added --cs=none; updated manpage 2017-10-05 15:27:37 -04:00
Heng Li f4a5d3a692 r474: replaced -S and --cs-no-equal with --cs 2017-10-05 15:03:03 -04:00
Heng Li 3ff6eda3a4 r473: don't count introns into blen 2017-10-05 14:37:21 -04:00
Heng Li 1a90bc8603 r472: fixed a bug when printing MAPQ/CIGAR 2017-10-05 12:46:11 -04:00
Heng Li abf2a90363 r471: all SAM features implemented; more tests! 2017-10-05 12:37:30 -04:00
Heng Li 5ab99eb26e more accurate SAM flag 2017-10-05 10:59:38 -04:00
Heng Li 7cc4f6f965 r469: first step towards PE SAM 2017-10-05 10:38:09 -04:00
Heng Li 16e6e589a8 r468: replaced ^ with ~ in cs 2017-10-04 22:17:12 -04:00
Heng Li 9aba11769c r467: added : (equal length) and ^ (intron) ops 2017-10-04 21:55:37 -04:00
Heng Li 7d50e646dd r466: detect multi-part index more smartly
though it might not work in an extremely rare case: the end of a sequence ends
at X*16384 and it is the last sequence in a batch. This can be resolved by
never letting the kstream_t buffer empty.
2017-10-04 17:32:58 -04:00
Heng Li 1554149158 r465: apply option -x before other options 2017-10-04 13:52:28 -04:00
Heng Li 19c39e704f r464: fixed a bug in pairing, due to randomization 2017-10-04 13:37:40 -04:00
Heng Li 2581c44a21 r463: optionally disable secondary hits 2017-10-04 13:24:41 -04:00
Heng Li 5babf41a38 r462: SAM primary flag not properly set 2017-10-04 13:11:29 -04:00
Heng Li 2a1e738a94 r461: randomize repetitive hits 2017-10-04 13:05:18 -04:00
Heng Li cf55c84056 r460: added option --no-long-join 2017-10-04 12:08:44 -04:00
Heng Li 841763ec24 Merge branch 'master' into sr 2017-10-04 11:42:44 -04:00
Heng Li 95eb1dec36 r458: fixed wrong chr for inversion aln (#30) 2017-10-04 11:32:06 -04:00
Heng Li 0fd0f2aed1 r457: fixed a bug on parsing -f 2017-09-30 00:00:44 -04:00
Heng Li ee9b2773a8 r456: min chain score should >k-mer length
or chain_dp() wastes time on unnecessarily sorting chains with one k-mer.
2017-09-29 22:33:55 -04:00
Heng Li 340483821e r455: set max_occ on command line 2017-09-29 22:18:43 -04:00
Heng Li 04fb2c2ec0 r454: rechain with higher max_occ if no good chain 2017-09-29 19:24:32 -04:00
Heng Li 0d4ecd19ee r453: avoid duplicated strcmp() for ava 2017-09-28 15:52:05 -04:00
Heng Li 0c63325985 r452: fixed - -G not working with -x sr 2017-09-28 14:28:12 -04:00
Heng Li 2a554a92e9 r451: changed rep_len mapq heuristic 2017-09-28 14:23:14 -04:00
Heng Li 935a6e6064 r450: differentiate exact repeats via mapq 2017-09-27 23:51:05 -04:00
Heng Li a13691d00d eval script works with /[12] in SAM 2017-09-27 23:33:59 -04:00
Heng Li 8301222174 r448: fixed a bug when computing PE quality 2017-09-27 21:54:07 -04:00
Heng Li 9541052564 r447: paired-end mapping quality
not as good as I would hope...
2017-09-27 15:39:25 -04:00
Heng Li 7e0d70bfd3 r445: pair coordinate adjustment working
Next: mapq adjustment, which will be tricky...
2017-09-27 15:38:18 -04:00
Heng Li a349d85280 r444: changed the way orientation is specified
The old model doesn't work with RF or RR orientation. The new model only works
with paired-end reads. For >2 segments, only FF is supported.
2017-09-27 12:33:10 -04:00
Heng Li f611edf6f2 r443: don't filter small cm for split seg 2017-09-26 16:17:58 -04:00
Heng Li 1b1dd0cd57 r442: default max_gap to 200 in the sr mode 2017-09-26 13:31:01 -04:00
Heng Li 92ec8bd859 added the /1 or /2 suffix 2017-09-26 12:04:35 -04:00
Heng Li 55d1e4f638 r440: better chain filtering for PE reads 2017-09-26 11:03:36 -04:00
Heng Li 64c0ad6b35 r439: use splice-like chain gap cost between segs
This improves accuracy
2017-09-25 16:04:38 -04:00
Heng Li 9538c985aa r438: fixed a rare case that leads to missing hits
It is a bug in chaining.
2017-09-25 14:59:34 -04:00
Heng Li 8f25cfa36e r437: fixed uninialized memory on rep_len 2017-09-25 14:22:45 -04:00
Heng Li 81008dd371 r436: working on short reads
The result is mixed - lots of room for tuning
2017-09-25 14:06:29 -04:00
Heng Li 3bb66e1ed3 multi-seg working on toy examples 2017-09-25 13:42:04 -04:00
Heng Li a742f10164 get multi-seg code ready; probably not working yet 2017-09-24 15:17:17 -04:00
Heng Li f0951141a1 allow to read multiple files interleaved 2017-09-24 14:33:05 -04:00
Heng Li 84bbc47152 two arrays should be freed with kfree(0,)
though in the current code, they are strictly equivalent.
2017-09-23 10:43:22 -04:00
Heng Li 5400191097 get batch sequence reader ready for paired-end 2017-09-22 09:56:31 -04:00
Heng Li ef84e8b4e7 Merge branch 'master' into sr 2017-09-20 23:56:06 -04:00
Heng Li 1c948e0d1d added GMAP iso-seq numbers 2017-09-20 23:54:02 -04:00
Heng Li 997011458c fixed uninitialized value due to last commit 2017-09-20 15:10:48 -04:00
Heng Li 19d8eca3a1 moved array shrinking into chain_dp() 2017-09-20 14:58:57 -04:00
Heng Li 9943e5fdd0 backup 2017-09-20 14:35:46 -04:00
Heng Li 5b39a1b34b Merge branch 'master' into sr 2017-09-20 12:24:08 -04:00
Heng Li e3b5802b2e r424: reduce memory for long query seqs 2017-09-20 12:22:13 -04:00
Heng Li 03d6894517 backup 2017-09-20 11:47:46 -04:00
Heng Li 645db3350e Merge branch 'master' into sr 2017-09-20 11:15:14 -04:00
Heng Li 75e6bbc9f6 r421: removed the MM_F_SPLICE_BOTH mode
In the default splice mode, minimap2 applies two rounds of spliced alignment:
first assuming GT-AG to be the splice signal across all splicing sites and then
assuming CT-AC to be the signal. This is the idea strategy.

In the MM_F_SPLICE_BOTH mode, minimap2 applies one round of spliced alignment,
assuming GT-AG and CT-AC to be the splice signals AT THE SAME TIME. This will
be faster but less accurate. I don't think anyone would like to run minimap2 in
this mode, so I am removing it for clarity.
2017-09-20 11:11:53 -04:00
Heng Li 7a9b4db874 replaced --approx-ext with --sr
--sr disables Z-drop and may come with other heurstics
2017-09-20 10:51:18 -04:00
Heng Li 4979e66ff0 Merge branch 'master' into sr 2017-09-20 10:20:31 -04:00
Heng Li a686461e83 added github downloads counts 2017-09-20 10:11:58 -04:00
Heng Li fd14618e61 no effective changes 2017-09-20 10:11:05 -04:00
Heng Li 5caadc28b4 Merge branch 'master' into sr 2017-09-19 22:31:14 -04:00
Heng Li 56014ba3db avoid assertion failure given 0-length reads 2017-09-19 22:30:32 -04:00
Heng Li b99c22840f r414: avoid assertion failure for 0-length reads 2017-09-19 22:21:27 -04:00
Heng Li c04420698e fixed an uninitialized value 2017-09-19 16:21:21 -04:00
Heng Li fb1bcc0084 early exploration 2017-09-19 16:18:28 -04:00
Heng Li 11081c6c27 r411: refactored kalloc for clarity
The new version is closer to K&R's original implementation.
2017-09-18 19:49:15 -04:00
Heng Li 60485790b4 updated version number in README 2017-09-17 20:25:54 -04:00
Heng Li ea5a0cd17d Release minimap2-2.2 (r409) 2017-09-17 20:08:47 -04:00
Heng Li ffff953e2c added python version badge 2017-09-17 17:15:55 -04:00
Heng Li 48705e9bfa don't build for python-3.0 (unavailable in travis) 2017-09-17 17:07:42 -04:00
Heng Li cf93e5c0a1 more functional minimap2.py; added categories 2017-09-17 17:06:39 -04:00
Heng Li 0b660c70e2 syntax error 2017-09-17 16:02:06 -04:00
Heng Li 5715c423ff exposed timer and verbose-level to mappy 2017-09-17 15:21:36 -04:00
Heng Li c8a019fae8 exposed fasta/q reader to mappy 2017-09-17 14:41:59 -04:00
Heng Li e9c57f6d8b r402: exposed kseq (for API in mappy later) 2017-09-17 13:09:16 -04:00
Heng Li 3edf2a9130 renamed mm2-lite.py to minimap2.py 2017-09-17 09:41:37 -04:00
Heng Li 89151b2588 added python 3.6 test 2017-09-17 00:55:31 -04:00
Heng Li cc0a538bd3 try python3 in travis 2017-09-17 00:49:16 -04:00
Heng Li e9e86f5a48 try python build again 2017-09-17 00:46:39 -04:00
Heng Li c0779f0359 revert to the old travis
can't get cython working...
2017-09-17 00:40:18 -04:00
Heng Li 875ea06302 install cython with travis 2017-09-17 00:37:09 -04:00
Heng Li 2c7007a11b try again 2017-09-17 00:31:15 -04:00
Heng Li fc87b767ba travis for python (test) 2017-09-17 00:28:34 -04:00
Heng Li dba8b50ee9 change python version to rc1 2017-09-17 00:08:54 -04:00
Heng Li d5012a1b17 this is embarrassing: rename again to mappy 2017-09-17 00:05:30 -04:00
Heng Li eaaf53c9b8 bumped version number
due to conflict with PyPI (already uploaded)
2017-09-16 23:51:49 -04:00
Heng Li ef46a8aed4 remaining minimap2=>mmappy in doc 2017-09-16 23:49:02 -04:00
Heng Li 28fd3d63fd renamed module name from minimap2 to mmappy
minimap2 clashed with minimap2 from conda
2017-09-16 23:29:41 -04:00
Heng Li 06b79c4a52 Merge branch 'master' of github.com:lh3/minimap2 2017-09-16 22:53:34 -04:00
Heng Li 8cdaae0935 fixed a few typos
eh... a missing fix
2017-09-16 22:53:24 -04:00
Heng Li 38aa9aa9a7 eh... a missing fix 2017-09-16 22:52:52 -04:00
Heng Li f8cb865ec5 fixed a few typos 2017-09-16 22:52:26 -04:00
Heng Li 1d90742b35 fixed hyperlink 2017-09-16 22:46:30 -04:00
Heng Li 5103cea7d3 load python README.rst into setup.py 2017-09-16 22:43:52 -04:00
Heng Li 7da9a08a6f reformat 2017-09-16 22:36:19 -04:00
Heng Li ddc2c6f279 change to rst for PyPI 2017-09-16 22:29:52 -04:00
Heng Li 7e98b18ba2 for python3 compatibility 2017-09-16 20:37:49 -04:00
Heng Li 3544c60c71 allow to test if index is present 2017-09-16 20:09:17 -04:00
Heng Li 6b66ec6167 minor 2017-09-16 19:55:33 -04:00
Heng Li cb7fb77bb9 python documentation 2017-09-16 19:50:52 -04:00
Heng Li 322e5a16e5 minor tweaks to python 2017-09-16 18:11:43 -04:00
Heng Li 10bd4079d1 fixed a bug on rev strand; added example 2017-09-16 17:51:13 -04:00
Heng Li b22703a354 improvement to the python binding 2017-09-16 11:14:01 -04:00
Heng Li 7e34bea7ab minor 2017-09-16 09:30:00 -04:00
Heng Li c07f9f9a49 r372: default mm_verbose to 1, and change in main 2017-09-16 09:14:34 -04:00
Heng Li 446bde214d first python version 2017-09-16 08:44:47 -04:00
Heng Li 5966e5d6e4 make reader_open() work even if idxopt is NULL 2017-09-15 11:29:49 -04:00
Heng Li 14b853499f r369: updated example with the latest API 2017-09-14 22:44:10 -04:00
Heng Li 75ff7ceec5 r368: API documentation 2017-09-14 22:23:04 -04:00
Heng Li e2823d4aee r367: index reader optionally writes index 2017-09-14 21:18:13 -04:00
Heng Li eb00521d9b redesigned indexing and option APIs 2017-09-14 17:02:01 -04:00
Heng Li 0f7455cefa r365: documented the "sr" preset 2017-09-14 12:57:21 -04:00
Heng Li 4d3768bf26 r364: improved the mapq heuristics
* use repetitive seed lengths, not counts
* compute n_sub to higher accuracy
* use bwa-mem mapq heuristic as a backup

For short single-end reads, minimap2's ROC is not as good as bwa-mem's, but is
close.
2017-09-14 12:37:03 -04:00
Heng Li 47e9d76ca1 further mapq tuning 2017-09-14 10:46:14 -04:00
Heng Li f4a8766283 r362: fixed overestimated chaining score
Caused by ilog2_32(0)=-1. This bug was fixed once and reoccurred as I was
tuning the score function but forgot to apply the fix.
2017-09-14 10:15:22 -04:00
Heng Li 6a82a21dee r361: improved mapq for short reads 2017-09-13 15:32:39 -04:00
Heng Li 3c91d652dd r360: allow to set integer max occ 2017-09-13 11:37:00 -04:00
Heng Li 1b44275802 Merge branch 'master' into sr 2017-09-13 11:10:23 -04:00
Heng Li 2f2b11624a reverted to the Travis badge as it is faster 2017-09-13 10:50:03 -04:00
Heng Li cb57bd6146 updated badges 2017-09-13 10:46:25 -04:00
Heng Li 885db1233d updated badges
for fun
2017-09-13 10:29:41 -04:00
Heng Li 2bf2f137dd added a license badge 2017-09-13 10:03:15 -04:00
Heng Li 8706f6bdf8 Merge branch 'master' into sr 2017-09-12 22:37:46 -04:00
Heng Li 2028e8c266 show bioconda version 2017-09-12 16:21:49 -04:00
Heng Li 0cc8d277ba changed to the "install with conda" badge 2017-09-12 16:20:01 -04:00
Heng Li 14f0cce4e2 malformatted conda badge 2017-09-12 16:17:07 -04:00
Heng Li 8ddbf7169f added bioconda download link 2017-09-12 16:16:26 -04:00
Heng Li d7f2ac1d4f better parameters for short reads
It turns out the key problem is not the minimizer density. It is the max
occurrence that tends to affect results more, especially sensitivity. There is
still lots of work to do, but for now, it seems a good start.
2017-09-12 16:11:23 -04:00
Heng Li eea9e851d8 Merge branch 'dev' into short 2017-09-11 09:32:28 -04:00
Heng Li c7c3585531 r347: merged mm_map_frag() into mm_map()
mm_map_frag() was separated due to an earlier design that has been rejected.
2017-09-10 15:02:55 -04:00
Heng Li 87a278d06a Merge branch 'dev' into short 2017-09-09 08:49:58 -04:00
Heng Li 59c822b722 removed some commented code
which *might* return at some time later
2017-09-09 08:38:39 -04:00
Heng Li f422175e4e r344: avoid unnecessary refName retrieval 2017-09-08 22:44:14 -04:00
Heng Li 709b6ec1f1 increase seed occurrences 2017-09-08 22:42:39 -04:00
Heng Li 0031158936 Merge branch 'master' into short 2017-09-07 11:41:32 -04:00
Heng Li ef3f7ea2f2 Release minimap2-2.1.1 (r341) 2017-09-06 13:46:51 -04:00
Heng Li 8b9f2aaf04 r339: improved SIMD detection
old code does not check AVX2
2017-09-05 13:10:30 -04:00
Heng Li 46e8b6a4f9 r338: portable CPU dispatch, which is the default
working with gcc, icc, clang and msvc.
2017-09-03 20:29:24 -04:00
Heng Li 3c997ca016 r337: support CPU dispatch for gcc-4.8+
using __builtin_cpu_supports()
2017-09-03 14:29:49 -04:00
Heng Li f9ccc522cd Merge branch 'master' into short 2017-09-03 11:58:15 -04:00
Heng Li 101b8bb97d r335: report an error if query can't be opened 2017-09-03 11:54:38 -04:00
Heng Li f4a71d447f Merge branch 'master' of github.com:lh3/minimap2 2017-09-03 11:07:32 -04:00
Heng Li 6db9b7579c Better MSVC support
* get rid of alloca()
* no variable-sized arrays
2017-09-03 11:05:55 -04:00
Heng Li f50b9a14a7 last commit can't be compiled 2017-09-02 21:07:19 -04:00
Heng Li 33423e1568 get rid of the last var-sized array in ksort
for MSVC
2017-09-02 21:05:03 -04:00
Heng Li aeb6b5eeb1 fixed an issue due to the 64-bit assumption 2017-09-02 19:29:52 -04:00
Heng Li 3d3fde8224 replaced remaining alloca() 2017-09-02 18:50:42 -04:00
Heng Li 6f4cbf4f12 ok, %zu is a C99 feature... 2017-09-02 18:48:14 -04:00
Heng Li 2a5d5b6f12 print size_t with %zu instead of %lu 2017-09-02 18:37:48 -04:00
Heng Li 3d48516885 reduced a heap allocation
which *might* be frequent for short reads
2017-09-02 18:32:45 -04:00
Heng Li 00416c76d1 for MinGW32 compatibility
get rid of alloca()
2017-09-02 18:23:29 -04:00
Heng Li 2b8681ead7 fixed memory leak in example 2017-09-02 17:55:25 -04:00
Heng Li 0a3ebdc916 for better windows compatibility 2017-09-02 17:52:33 -04:00
Heng Li b97620afed moved gettimeofday() to misc.c 2017-09-02 17:40:10 -04:00
Heng Li 743d26eab0 Merge pull request #20 from nanoporetech/msvc14
ONT source code changes to compile with MSVC 14
2017-09-02 14:35:02 -07:00
Heng Li 62535ecd7f Merge branch 'dev' 2017-09-01 10:06:21 -07:00
Heng Li 40665d1083 Merge pull request #23 from simonrharris/segfaultfix
Fixed segfault caused when reading from index file
2017-09-02 00:46:55 +08:00
Simon Harris 4db1c0295c Fixed segfault caused when reading from index file 2017-09-01 15:48:59 +01:00
Heng Li d4074874ee r316: get rid of a harmless gcc warning 2017-09-01 20:25:27 +08:00
Heng Li eccdb3a1ca r315: added getopt from musl 2017-09-01 20:20:34 +08:00
Stefan von Deylen a3c3db6b9b ONT source code changes to compile with MSVC 14 2017-08-30 16:25:20 +01:00
Heng Li c4080aaf7e Merge branch 'master' into short 2017-08-28 07:02:22 +08:00
Heng Li 2641613686 various minor improvements 2017-08-25 17:56:16 +08:00
Heng Li 5f96d851a8 added spliced alignment example 2017-08-25 14:11:54 +08:00
Heng Li bf8246f872 Release minimap2-2.1-r311 2017-08-25 13:35:55 +08:00
Heng Li 5cbd776651 test data for spliced alignment 2017-08-25 13:17:56 +08:00
Heng Li 0fe1a224ab r309: improved SAM header output 2017-08-25 10:35:58 +08:00
Heng Li 240f6caaff backup manuscript 2017-08-24 22:05:14 +08:00
Heng Li b8bdac7e64 backup 2017-08-22 17:45:01 +08:00
Heng Li 19e4b2aab0 backup 2017-08-20 22:10:03 +08:00
Heng Li ce859dbe1c evaluate introns
this simplies the metrics
2017-08-20 21:54:59 +08:00
Heng Li a4c41f64a5 the final version
to be replaced by intron-eval
2017-08-20 21:54:35 +08:00
Heng Li 5ed2faf270 minor tuning to the matching rules 2017-08-19 16:21:49 +08:00
Heng Li 8058c85b72 moved appendix to methods; detailed splice aln 2017-08-18 23:33:57 +08:00
Heng Li 993a2bb521 r301: separate introns from deletions
When an intron is adjacent to a deletion, the old code count both as introns,
which lead to an inaccurate exon boundary.
2017-08-18 15:31:15 +08:00
Heng Li 64c1389e1a Merge branch 'master' into splice 2017-08-17 23:39:27 +08:00
Heng Li 81cff97208 r299: support -h: output to stdout; return 0 2017-08-17 23:38:31 +08:00
Heng Li bbb37d95f2 support inserting RG lines 2017-08-17 23:34:09 +08:00
Heng Li 2cde8d257c r297: bidirectional RNA alignment 2017-08-17 06:02:44 -04:00
Heng Li b5f5929bf9 r296: expose splicing related options to CLI 2017-08-13 21:37:51 -04:00
Heng Li 28f86688ab r295: gap closure from the middle of non-HPC k
This WILL slightly affect the result of genomic mapping, but hopefully
in the good direction.
2017-08-12 23:48:43 -04:00
Heng Li 43506edbc5 backup: preliminary boundary alignment 2017-08-12 23:10:14 -04:00
Heng Li bcfe00d2ad minor formatting changes 2017-08-12 19:09:33 -04:00
Heng Li 17c19e5819 exts2 working 2017-08-12 19:06:59 -04:00
Heng Li 61eef0575c separate out spliced alignment; not right yet 2017-08-12 18:54:32 -04:00
Heng Li 53b3265d84 r290: in techrep, explain spliced alignment 2017-08-12 15:40:49 -04:00
Heng Li a23df2dc91 r289: changed CLI help only 2017-08-12 12:40:07 -04:00
Heng Li 5a74088b74 r288: changed max intron length to 200k 2017-08-12 12:39:21 -04:00
Heng Li d240318741 r287: refined CLI options and manpage 2017-08-12 12:26:04 -04:00
Heng Li 0f4c823b0c r286: ignore introns when computing max seg score 2017-08-12 10:58:16 -04:00
Heng Li 2d11aaa830 option to print all exons for debugging 2017-08-12 10:04:16 -04:00
Heng Li 30b8cb4672 option to print correct exons 2017-08-11 17:50:21 -04:00
Heng Li 0c1760bc86 bugfix; print errors; better rules 2017-08-11 16:28:52 -04:00
Heng Li a99358bc3d r282: reduced intron cost; added eval script 2017-08-11 00:06:01 -04:00
Heng Li 163fa36ee6 r281: don't open long gaps on query 2017-08-10 15:04:59 -04:00
Heng Li c59b0781bc r280: output introns as "N" in the cdna mode 2017-08-09 11:45:02 -04:00
Heng Li 7429b12164 Merge branch 'master' into cdna 2017-08-08 22:00:24 -04:00
Heng Li 9e1125edda r277: abort if query/-d missing (#11) 2017-08-08 21:46:15 -04:00
Heng Li 4db0d1034b Show travis CI status 2017-08-08 21:32:56 -04:00
Heng Li 3dbe23b34e Merge branch 'dev' 2017-08-08 21:30:32 -04:00
Heng Li 6840370f3c Release minimap2-2.0 (r275) 2017-08-08 21:16:25 -04:00
Heng Li 7f9f659b6a r274: CLI option to change max ref gap 2017-08-08 11:39:23 -04:00
Heng Li 1a7d782131 r273: cdna mapping mode for testing
Differences from the typical mapping mode:

* banded alignment disabled
* log gap cost during chaining
* zero long-gap extension during alignment
* up to 100kb (by default) reference gap
* bad seeding not filtered (to tune later)
2017-08-08 11:31:49 -04:00
Heng Li 4badf2fcbf avoid wasted memory allocation for backtrack 2017-08-08 11:29:46 -04:00
Heng Li 079ec0d283 r271: added "short" preset; for testing only 2017-08-07 15:30:05 -04:00
Heng Li 0137d0dfab command lines used for read simulation 2017-08-07 13:41:40 -04:00
Heng Li 5b10667967 sim-eval.js to support mason2 read names and SAM 2017-08-07 13:28:33 -04:00
Heng Li bd1541dce1 renamed simulation-related scripts 2017-08-07 13:26:25 -04:00
Heng Li 1597f2b030 convert mason2 SAM to fastq 2017-08-07 12:45:34 -04:00
Heng Li ce927d2aee Merge branch 'master' into dev 2017-08-07 12:44:07 -04:00
Heng Li 8c2917b391 Added citation. 2017-08-06 21:30:47 -04:00
Heng Li 9fcf8b6315 added basic travis CI 2017-08-04 15:33:40 -04:00
Heng Li 3217178d4a fixed k8 download page 2017-08-04 14:53:00 -04:00
Heng Li b40598fa91 added paf2aln.js example 2017-08-04 14:51:33 -04:00
Heng Li 269b8661b9 minor clarification 2017-08-04 14:49:25 -04:00
Heng Li 890d7bbd5b added README for misc scripts 2017-08-04 14:46:31 -04:00
Heng Li 1aca0d5098 copied sam2paf.js here for completeness 2017-08-04 14:44:37 -04:00
Heng Li 68d99f0edb Merge branch 'cs-tag' into dev 2017-08-04 14:31:46 -04:00
Heng Li fd66ab5413 blast-like output working 2017-08-04 14:31:21 -04:00
Heng Li 71f82759af convert paf to maf 2017-08-04 13:05:46 -04:00
Heng Li 648cdd8a3b minor wording changes 2017-08-04 10:21:10 -04:00
Heng Li 7e231cfd3a minor wording improvement 2017-08-03 21:28:01 -04:00
Heng Li 95d3c30ae3 rephrasing a bit to avoid the apparent TeX bug
Several hours sank into this. Damned...
2017-08-03 20:19:00 -04:00
Heng Li f57fd8c790 added one transition phrase
as there are no subsubsections now
2017-08-03 18:01:48 -04:00
Heng Li c70b20ee6d removed subsubsection
TeX Live 2016 doesn't work with section and content on the same line.
2017-08-03 17:58:36 -04:00
Heng Li e8d00a8c8c removed trailing ^M 2017-08-03 17:20:43 -04:00
Heng Li ed4efd77bc minor modifications
I will submit this version arXiv. There is definitely room for improvement, but
as a semi-formal technical report, it should be good for now.
2017-08-03 12:47:36 -04:00
Heng Li 295874ea30 spell check 2017-08-03 12:17:58 -04:00
Heng Li 9ec5f51afb the first draft 2017-08-03 12:12:55 -04:00
Heng Li 28ab4d1f72 backup 2017-08-02 22:59:58 -04:00
Heng Li 6c9390b54a backup 2017-08-02 19:52:31 -04:00
Heng Li 9306299e4d added benchmark results 2017-08-01 15:12:49 -04:00
Heng Li 0c563e1868 Merge branch 'dev' into tex 2017-08-01 13:53:16 -04:00
Heng Li 8b1e36c609 minor tweaks to the evaluation script 2017-08-01 13:52:29 -04:00
Heng Li d933a263d7 backup 2017-08-01 13:29:02 -04:00
Heng Li 7542b4b40a Makefile for TeX 2017-08-01 10:42:33 -04:00
Heng Li d6181de844 added tech notes 2017-08-01 10:38:33 -04:00
Heng Li 12cea727b8 r238: bugfix to cs - rev sequence not complemented 2017-08-01 10:33:21 -04:00
Heng Li cd105b47f2 r237: fixed a bug in outputting cs:Z 2017-07-31 14:49:39 -04:00
Heng Li 35f232c3fa r236: in cs tag, output differences in lowercase
for easy eyeballing
2017-07-31 12:17:48 -04:00
Heng Li 4c0713ee14 r235: optionally output tag cs in PAF
cs encodes the query, the reference sequence and CIGAR.
2017-07-31 12:06:49 -04:00
Heng Li 46de0fbdad Merge pull request #8 from rikuu/master
fix self-comparison in index parameter override check
2017-07-30 15:10:29 -04:00
Riku Walve 9e09c1ae72 fix self-comparison in index parameter override check 2017-07-30 21:46:25 +03:00
Heng Li d8d4d29b68 Release minimap2-2.0rc1-r232 2017-07-30 14:32:40 -04:00
Heng Li e51dfd0330 added NEWS 2017-07-30 14:20:03 -04:00
Heng Li 1f78e1ee53 r230: code formatting changes only 2017-07-30 12:31:40 -04:00
Heng Li 5934d68772 r229: a new way to prevent out-of-band backtrack 2017-07-29 23:52:30 -04:00
Heng Li fa99d28d34 r228: reduced unnecessary INV alignment 2017-07-29 20:21:53 -04:00
Heng Li d08b7a0c51 r227: use local alignment for INV alignment 2017-07-29 17:40:53 -04:00
Heng Li da3db3c095 r226: only try inv alignment for primary 2017-07-29 14:09:35 -04:00
Heng Li 783ead6f47 r225: removed a debugging line 2017-07-29 13:21:38 -04:00
Heng Li 19d6ec885e r224: inversion alignment around Z-drop break 2017-07-29 13:09:10 -04:00
Heng Li 120bebc290 ake 2017-07-29 11:01:49 -04:00
Heng Li 5e3eecd6d4 r222: no effective changes 2017-07-29 10:31:46 -04:00
Heng Li 2179e9e24b r221: output SA in the SAM output 2017-07-28 23:08:39 -04:00
Heng Li 84922cfe41 clarify that wrong seeding mainly occurs in LCRs 2017-07-28 14:33:15 -04:00
Heng Li ebbe9c1eb8 r219: fixed a bug caused by skipping tandem seeds 2017-07-28 14:06:56 -04:00
Heng Li c672690564 r218: increase the frequency of SW slightly 2017-07-28 13:30:42 -04:00
Heng Li f4fee60188 r217: ignore tandem seeds during alignment
This helps a tiny bit.
2017-07-28 12:26:56 -04:00
Heng Li 254280b8af r216: a bit cleanup; identical output to r215 2017-07-28 11:54:18 -04:00
Heng Li fc965805f7 r215: bring back a log gap component
Otherwise chaining may more often break a long gap into several gaps.
2017-07-28 00:17:19 -04:00
Heng Li 667b32a516 added algorithm overview 2017-07-27 18:50:39 -04:00
Heng Li 2c79580649 r213: more careful solution to wrong seeds
a little better, but not good enough!
2017-07-27 13:19:11 -04:00
Heng Li b927838495 r212: better heuristic to fix wrong seeding
but not good enough. Will explore more.
2017-07-27 11:24:51 -04:00
Heng Li 371e20cc7c r211: a better heurstic to reduce false seeds 2017-07-26 23:56:38 -04:00
Heng Li 8b08a2ec41 added first-only and all-primary modes to eval 2017-07-26 19:32:26 -04:00
Heng Li 9f728dd96a tunable overlap ratio 2017-07-26 18:30:29 -04:00
Heng Li 323293fbda changed the way mapq is counted 2017-07-26 14:20:31 -04:00
Heng Li bd33bed455 make output better 2017-07-26 12:03:42 -04:00
Heng Li a01d758af6 r206: mapq penalize short chains further
The old code penalized at the log() scale. Now added a linear-scaled factor. If
the chain consists of few minimizers, its quality is really not good.
2017-07-26 11:50:04 -04:00
Heng Li e9dc1ce2b6 r205: when computing mapq, consider min_chain_sc
Not doing this was a mistake.
2017-07-26 11:34:14 -04:00
Heng Li a8ad53ee81 evaluating read mapping for pbsim2fa reads
More functionality to be added later
2017-07-26 11:17:34 -04:00
Heng Li 00c6db5073 r203: check more subopt aln if score small 2017-07-25 20:02:44 -04:00
Heng Li f2ef48878a r202: trim bad chain ends before extension
This fixes a few more FP long INDELs towards the end of alignments.
2017-07-25 19:53:19 -04:00
Heng Li 21ca564112 r201: fixed a minor chaining issue
Chaining looked at the end of a chain, but the end may not be the best. We now
go back to find the max.
2017-07-25 18:26:51 -04:00
Heng Li 215e92ed7b r200: reduce long gaps in chaining
Every seed can initiate a chain.
2017-07-25 17:32:54 -04:00
Heng Li b530ade333 r199: changed to linear gap cost for chaining
The old cost doesn't penalize long gaps enough. Will also drop seeds close to
the edge in the next commit.
2017-07-25 15:35:10 -04:00
Heng Li 4bd7ebc39c convert pbsim .maf to fasta 2017-07-25 13:54:58 -04:00
Heng Li f81f37fef1 r197: allocate index seq names from kalloc
to reduce malloc() overhead.
2017-07-24 19:36:05 -04:00
75 changed files with 15789 additions and 1399 deletions
+4
View File
@@ -1,4 +1,8 @@
.cproject
.project
.*.swp .*.swp
*.a *.a
*.o *.o
*.dSYM *.dSYM
minimap2
mappy.c
+20
View File
@@ -0,0 +1,20 @@
matrix:
include:
- language: c
compiler: gcc
script: make
- language: c
compiler: clang
script: make
- language: python
python: "2.7"
before_install: pip install cython
script: python setup.py build_ext
- language: python
python: "3.5"
before_install: pip install cython
script: python setup.py build_ext
- language: python
python: "3.6"
before_install: pip install cython
script: python setup.py build_ext
+11
View File
@@ -0,0 +1,11 @@
include *.h
include Makefile
include ksw2_dispatch.c
include getopt.c
include main.c
include README.md
include python/mappy.c
include python/cmappy.h
include python/cmappy.pxd
include python/mappy.pyx
include python/README.rst
+79 -19
View File
@@ -1,17 +1,28 @@
CC= gcc CFLAGS= -g -Wall -O2 -Wc++-compat #-Wextra
CFLAGS= -g -Wall -O2 -Wc++-compat
CPPFLAGS= -DHAVE_KALLOC CPPFLAGS= -DHAVE_KALLOC
INCLUDES= -I. INCLUDES=
OBJS= kthread.o kalloc.o ksw2_extz2_sse.o ksw2_extd2_sse.o misc.o bseq.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
sketch.o sdust.o index.o chain.o align.o hit.o map.o format.o
PROG= minimap2 PROG= minimap2
PROG_EXTRA= sdust minimap2-lite PROG_EXTRA= sdust minimap2-lite
LIBS= -lm -lz -lpthread LIBS= -lm -lz -lpthread
ifeq ($(sse2only),) ifeq ($(arm_neon),) # if arm_neon is not defined
CFLAGS+=-msse4 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 # 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 endif
.PHONY:all extra clean depend
.SUFFIXES:.c .o .SUFFIXES:.c .o
.c.o: .c.o:
@@ -21,8 +32,8 @@ all:$(PROG)
extra:all $(PROG_EXTRA) extra:all $(PROG_EXTRA)
minimap2:main.o libminimap2.a minimap2:main.o getopt.o libminimap2.a
$(CC) $(CFLAGS) $< -o $@ -L. -lminimap2 $(LIBS) $(CC) $(CFLAGS) main.o getopt.o -o $@ -L. -lminimap2 $(LIBS)
minimap2-lite:example.o libminimap2.a minimap2-lite:example.o libminimap2.a
$(CC) $(CFLAGS) $< -o $@ -L. -lminimap2 $(LIBS) $(CC) $(CFLAGS) $< -o $@ -L. -lminimap2 $(LIBS)
@@ -30,11 +41,52 @@ minimap2-lite:example.o libminimap2.a
libminimap2.a:$(OBJS) libminimap2.a:$(OBJS)
$(AR) -csru $@ $(OBJS) $(AR) -csru $@ $(OBJS)
sdust:sdust.c kalloc.o kalloc.h kdq.h kvec.h kseq.h sdust.h sdust:sdust.c getopt.o kalloc.o kalloc.h kdq.h kvec.h kseq.h sdust.h
$(CC) -D_SDUST_MAIN $(CFLAGS) $< kalloc.o -o $@ -lz $(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 $(CFLAGS) -msse4.1 $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@
ksw2_extz2_sse2.o:ksw2_extz2_sse.c ksw2.h kalloc.h
$(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 $(CFLAGS) -msse4.1 $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@
ksw2_extd2_sse2.o:ksw2_extd2_sse.c ksw2.h kalloc.h
$(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 $(CFLAGS) -msse4.1 $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@
ksw2_exts2_sse2.o:ksw2_exts2_sse.c ksw2.h kalloc.h
$(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) -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: clean:
rm -fr gmon.out *.o a.out $(PROG) $(PROG_EXTRA) *~ *.a *.dSYM session* rm -fr gmon.out *.o a.out $(PROG) $(PROG_EXTRA) *~ *.a *.dSYM build dist mappy*.so mappy.c python/mappy.c mappy.egg*
depend: depend:
(LC_ALL=C; export LC_ALL; makedepend -Y -- $(CFLAGS) $(CPPFLAGS) -- *.c) (LC_ALL=C; export LC_ALL; makedepend -Y -- $(CFLAGS) $(CPPFLAGS) -- *.c)
@@ -42,17 +94,25 @@ depend:
# DO NOT DELETE # DO NOT DELETE
align.o: minimap.h mmpriv.h bseq.h ksw2.h kalloc.h align.o: minimap.h mmpriv.h bseq.h ksw2.h kalloc.h
bseq.o: bseq.h kseq.h bseq.o: bseq.h kvec.h kalloc.h kseq.h
chain.o: minimap.h mmpriv.h bseq.h kalloc.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 example.o: minimap.h kseq.h
format.o: mmpriv.h minimap.h bseq.h format.o: kalloc.h mmpriv.h minimap.h bseq.h
hit.o: mmpriv.h minimap.h bseq.h kalloc.h getopt.o: getopt.h
hit.o: mmpriv.h minimap.h bseq.h kalloc.h khash.h
index.o: kthread.h bseq.h minimap.h mmpriv.h kvec.h kalloc.h khash.h index.o: kthread.h bseq.h minimap.h mmpriv.h kvec.h kalloc.h khash.h
kalloc.o: kalloc.h kalloc.o: kalloc.h
ksw2_extd2_sse.o: ksw2.h kalloc.h 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_extz2_sse.o: ksw2.h kalloc.h
main.o: bseq.h minimap.h mmpriv.h ksw2_ll_sse.o: ksw2.h kalloc.h
map.o: kthread.h kvec.h kalloc.h sdust.h mmpriv.h minimap.h bseq.h kthread.o: kthread.h
misc.o: minimap.h ksort.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
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 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
+430
View File
@@ -0,0 +1,430 @@
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)
-----------------------------------
This release fixes several bugs and brings a couple of minor improvements:
* Fixed a severe bug that leads to incorrect mapping coordinates in rare
corner cases.
* Fixed underestimated mapping quality for chimeric alignments when the whole
query sequence contain many repetitive minimizers, and for chimeric
alignments caused by Z-drop.
* Fixed two bugs in Python binding: incorrect strand field (#57) and incorrect
sequence names for Python3 (#55).
* Improved mapping accuracy for highly overlapping paired ends.
* Added option -Y to use soft clipping for supplementary alignments (#56).
(2.5: 11 November 2017, r572)
Release 2.4-r555 (6 November 2017)
----------------------------------
As is planned, this release focuses on fine tuning the base algorithm. Notable
changes include
* Changed the mapping quality scale to match the scale of BWA-MEM. This makes
minimap2 and BWA-MEM achieve similar sensitivity-specificity balance on real
short-read data.
* Improved the accuracy of splice alignment by modeling one additional base
close to the GT-AG signal. This model is used by default with `-x splice`.
For SIRV control data, however, it is recommended to add `--splice-flank=no`
to disable this feature as the SIRV splice signals are slightly different.
* Tuned the parameters for Nanopore Direct RNA reads. The recommended command
line is `-axsplice -k14 -uf` (#46).
* Fixed a segmentation fault when aligning PacBio reads (#47 and #48). This
bug is very rare but it affects all versions of minimap2. It is also
recommended to re-index reference genomes created with `map-pb`. For human,
two minimizers in an old index are wrong.
* Changed option `-L` in sync with the final decision of hts-specs: a fake
CIGAR takes the form of `<readLen>S<refLen>N`. Note that `-L` only enables
future tools to recognize long CIGARs. It is not possible for older tools to
work with such alignments in BAM (#43 and #51).
* Fixed a tiny issue whereby minimap2 may waste 8 bytes per candidate
alignment.
The minimap2 technical note hosted at arXiv has also been updated to reflect
recent changes.
(2.4: 6 November 2017, r555)
Release 2.3-r531 (22 October 2017)
----------------------------------
This release come with many improvements and bug fixes:
* The **sr** preset now supports paired-end short-read alignment. Minimap2 is
3-4 times as fast as BWA-MEM, but is slightly less accurate on simulated
reads.
* Meticulous improvements to assembly-to-assembly alignment (special thanks to
Alexey Gurevich from the QUAST team): a) apply a small penalty to matches
between ambiguous bases; b) reduce missing alignments due to spurious
overlaps; c) introduce the short form of the `cs` tag, an improvement to the
SAM MD tag.
* Make sure gaps are always left-aligned.
* Recognize `U` bases from Oxford Nanopore Direct RNA-seq (#33).
* Fixed slightly wrong chaining score. Fixed slightly inaccurate coordinates
for split alignment.
* Fixed multiple reported bugs: 1) wrong reference name for inversion
alignment (#30); 2) redundant SQ lines when multiple query files are
specified (#39); 3) non-functioning option `-K` (#36).
This release has implemented all the major features I planned five months ago,
with the addition of spliced long-read alignment. The next couple of releases
will focus on fine tuning of the base algorithms.
(2.3: 22 October 2017, r531)
Release 2.2-r409 (17 September 2017)
------------------------------------
This is a feature release. It improves single-end short-read alignment and
comes with Python bindings. Detailed changes include:
* Added the **sr** preset for single-end short-read alignment. In this mode,
minimap2 runs faster than BWA-MEM, but is slightly less accurate on
simulated data sets. Paired-end alignment is not supported as of now.
* Improved mapping quality estimate with more accurate identification of
repetitive hits. This mainly helps short-read alignment.
* Implemented **mappy**, a Python binding for minimap2, which is available
from PyPI and can be installed with `pip install --user mappy`. Python users
can perform read alignment without the minimap2 executable.
* Restructured the indexing APIs and documented key minimap2 APIs in the
header file minimap.h. Updated example.c with the new APIs. Old APIs still
work but may become deprecated in future.
This release may output alignments different from the previous version, though
the overall alignment statistics, such as the number of aligned bases and long
gaps, remain close.
(2.2: 17 September 2017, r409)
Release 2.1.1-r341 (6 September 2017)
-------------------------------------
This is a maintenance release that is expected to output identical alignment to
v2.1. Detailed changes include:
* Support CPU dispatch. By default, minimap2 is compiled with both SSE2 and
SSE4 based implementation of alignment and automatically chooses the right
one at runtime. This avoids unexpected errors on older CPUs (#21).
* Improved Windows support as is requested by Oxford Nanopore (#19). Minimap2
now avoids variable-length stacked arrays, eliminates alloca(), ships with
getopt_long() and provides timing functions implemented with Windows APIs.
* Fixed a potential segmentation fault when specifying -k/-w/-H with
multi-part index (#23).
* Fixed two memory leaks in example.c
(2.1.1: 6 September 2017, r341)
Release 2.1-r311 (25 August 2017)
---------------------------------
This release adds spliced alignment for long noisy RNA-seq reads. On a SMRT
Iso-Seq and a Oxford Nanopore data sets, minimap2 appears to outperform
traditional mRNA aligners. For DNA alignment, this release gives almost
identical output to v2.0. Other changes include:
* Added option `-R` to set the read group header line in SAM.
* Optionally output the `cs:Z` tag in PAF to encode both the query and the
reference sequences in the alignment.
* Fixed an issue where DP alignment uses excessive memory.
The minimap2 technical report has been updated with more details and the
evaluation of spliced alignment:
* Li, H. (2017). Minimap2: fast pairwise alignment for long nucleotide
sequences. [arXiv:1708.01492v2](https://arxiv.org/abs/1708.01492v2).
(2.1: 25 August 2017, r311)
Release 2.0-r275 (8 August 2017)
--------------------------------
This release is identical to version 2.0rc1, except the version number. It is
described and evaluated in the following technical report:
* Li, H. (2017). Minimap2: fast pairwise alignment for long DNA sequences.
[arXiv:1708.01492v1](https://arxiv.org/abs/1708.01492v1).
(2.0: 8 August 2017, r275)
Release 2.0rc1-r232 (30 July 2017)
----------------------------------
This release improves the accuracy of long-read alignment and added several
minor features.
* Improved mapping quality estimate for short alignments containing few seed
hits.
* Fixed a minor bug that affects the chaining accuracy towards the ends of a
chain. Changed the gap cost for chaining to reduce false seeding.
* Skip potentially wrong seeding and apply dynamic programming more frequently.
This slightly increases run time, but greatly reduces false long gaps.
* Perform local alignment at Z-drop break point to recover potential inversion
alignment. Output the SA tag in the SAM format. Added scripts to evaluate
mapping accuracy for reads simulated with pbsim.
This release completes features intended for v2.0. No major features will be
added to the master branch before the final v2.0.
(2.0rc1: 30 July 2017, r232)
Release r191 (19 July 2017)
---------------------------
This is the first public release of minimap2, an aligner for long reads and
assemblies. This release has a few issues and is generally not recommended for
production uses.
(19 July 2017, r191)
+346 -42
View File
@@ -1,62 +1,357 @@
## Getting Started [![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)
[![Build Status](https://travis-ci.org/lh3/minimap2.svg?branch=master)](https://travis-ci.org/lh3/minimap2)
## <a name="started"></a>Getting Started
```sh ```sh
git clone https://github.com/lh3/minimap2 git clone https://github.com/lh3/minimap2
cd minimap2 && make cd minimap2 && make
# long reads against a reference genome # long sequences against a reference genome
./minimap2 -ax map10k test/MT-human.fa test/MT-orang.fa > test.sam ./minimap2 -a test/MT-human.fa test/MT-orang.fa > test.sam
# create an index first and then map # create an index first and then map
./minimap2 -x map10k -d MT-human.mmi test/MT-human.fa ./minimap2 -d MT-human.mmi test/MT-human.fa
./minimap2 -ax map10k MT-human.mmi test/MT-orang.fa > test.sam ./minimap2 -a MT-human.mmi test/MT-orang.fa > test.sam
# long-read overlap (no test data) # use presets (no test data)
./minimap2 -x ava-pb your-reads.fa your-reads.fa > overlaps.paf ./minimap2 -ax map-pb ref.fa pacbio.fq.gz > aln.sam # PacBio genomic reads
# man page ./minimap2 -ax map-ont ref.fa ont.fq.gz > aln.sam # Oxford Nanopore genomic reads
./minimap2 -ax sr ref.fa read1.fa read2.fa > aln.sam # short genomic paired-end reads
./minimap2 -ax splice ref.fa rna-reads.fa > aln.sam # spliced long reads
./minimap2 -ax splice -k14 -uf ref.fa reads.fa > aln.sam # Nanopore Direct RNA-seq
./minimap2 -cx asm5 asm1.fa asm2.fa > aln.paf # intra-species asm-to-asm alignment
./minimap2 -x ava-pb reads.fa reads.fa > overlaps.paf # PacBio read overlap
./minimap2 -x ava-ont reads.fa reads.fa > overlaps.paf # Nanopore read overlap
# man page for detailed command line options
man ./minimap2.1 man ./minimap2.1
``` ```
## Table of Contents
## Introduction - [Getting Started](#started)
- [Users' Guide](#uguide)
- [Installation](#install)
- [General usage](#general)
- [Use cases](#cases)
- [Map long noisy genomic reads](#map-long-genomic)
- [Map long mRNA/cDNA reads](#map-long-splice)
- [Find overlaps between long reads](#long-overlap)
- [Map short accurate genomic reads](#short-genomic)
- [Full genome/assembly alignment](#full-genome)
- [Advanced features](#advanced)
- [Working with >65535 CIGAR operations](#long-cigar)
- [The cs optional tag](#cs)
- [Working with the PAF format](#paftools)
- [Algorithm overview](#algo)
- [Getting help](#help)
- [Citing minimap2](#cite)
- [Developers' Guide](#dguide)
- [Limitations](#limit)
Minimap2 is a fast sequence mapping and alignment program that can find ## <a name="uguide"></a>Users' Guide
overlaps between long noisy reads, or map long reads or their assemblies to a
reference genome optionally with detailed alignment (i.e. CIGAR). At present,
it works efficiently with query sequences from a few kilobases to ~100
megabases in length at an error rate ~15%. Minimap2 outputs in the [PAF][paf] or
the [SAM format][sam]. On limited test data sets, minimap2 is over 20 times
faster than most other long-read aligners. It will replace BWA-MEM for long
reads and contig alignment.
Minimap2 is the successor of [minimap][minimap]. It uses a similar Minimap2 is a versatile sequence alignment program that aligns DNA or mRNA
minimizer-based indexing and seeding algorithm, and improves the original sequences against a large reference database. Typical use cases include: (1)
minimap with homopolyer-compressed k-mers (see also [SMARTdenovo][smartdenovo] mapping PacBio or Oxford Nanopore genomic reads to the human genome; (2)
and [longISLND][longislnd]), better chaining and the ability to produce CIGAR finding overlaps between long reads with error rate up to ~15%; (3)
with fast extension alignment (see also [libgaba][gaba] and [ksw2][ksw2]) and splice-aware alignment of PacBio Iso-Seq or Nanopore cDNA or Direct RNA reads
piece-wise affine gap cost. against a reference genome; (4) aligning Illumina single- or paired-end reads;
(5) assembly-to-assembly alignment; (6) full-genome alignment between two
closely related species with divergence below ~15%.
## Installation For ~10kb noisy reads sequences, minimap2 is tens of times faster than
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 paper][doi] or the
[preprint][preprint].
For modern x86-64 CPUs, just type `make` in the source code directory. This ### <a name="install"></a>Installation
will compile a binary `minimap2` which you can copy to your desired location.
If you see compilation errors, try `make sse2only=1` to disable SSE4. Minimap2
will run a little slower. At present, minimap2 does not work with non-x86 CPUs
or ancient CPUs that do not support SSE2. SSE2 is critical to the performance
of minimap2.
## Limitations 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.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.
* At the alignment phase, minimap2 performs global alignments between minimizer Minimap2 also works with ARM CPUs supporting the NEON instruction sets. To
hits. If the positions of these minimizer hits are incorrect, the final 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`.
alignment may be suboptimal or unnecessarily fragmented.
* Minimap2 may produce poor alignments that may need post-filtering. We are ### <a name="general"></a>General usage
still exploring a reliable and consistent way to report good alignments.
* Minimap2 does not work well with Illumina short reads as of now. Without any options, minimap2 takes a reference database and a query sequence
file as input and produce approximate mapping, without base-level alignment
(i.e. no CIGAR), in the [PAF format][paf]:
```sh
minimap2 ref.fa query.fq > approx-mapping.paf
```
You can ask minimap2 to generate CIGAR at the `cg` tag of PAF with:
```sh
minimap2 -c ref.fa query.fq > alignment.paf
```
or to output alignments in the [SAM format][sam]:
```sh
minimap2 -a ref.fa query.fq > alignment.sam
```
Minimap2 seamlessly works with gzip'd FASTA and FASTQ formats as input. You
don't need to convert between FASTA and FASTQ or decompress gzip'd files first.
* Minimap2 requires SSE2 instructions to compile. It is possible to add For the human reference genome, minimap2 takes a few minutes to generate a
non-SSE2 support, but it would make minimap2 slower by several times. minimizer index for the reference before mapping. To reduce indexing time, you
can optionally save the index with option **-d** and replace the reference
sequence file with the index file on the minimap2 command line:
```sh
minimap2 -d ref.mmi ref.fa # indexing
minimap2 -a ref.mmi reads.fq > alignment.sam # alignment
```
***Importantly***, it should be noted that once you build the index, indexing
parameters such as **-k**, **-w**, **-H** and **-I** can't be changed during
mapping. If you are running minimap2 for different data types, you will
probably need to keep multiple indexes generated with different parameters.
This makes minimap2 different from BWA which always uses the same index
regardless of query data types.
In general, minimap2 is a young project with most code written since June, ### <a name="cases"></a>Use cases
2017. It may have bugs and room for improvements. Bug reports and suggestions
are warmly welcomed. Minimap2 uses the same base algorithm for all applications. However, due to the
different data types it supports (e.g. short vs long reads; DNA vs mRNA reads),
minimap2 needs to be tuned for optimal performance and accuracy. It is usually
recommended to choose a preset with option **-x**, which sets multiple
parameters at the same time. The default setting is the same as `map-ont`.
#### <a name="map-long-genomic"></a>Map long noisy genomic reads
```sh
minimap2 -ax map-pb ref.fa pacbio-reads.fq > aln.sam # for PacBio subreads
minimap2 -ax map-ont ref.fa ont-reads.fq > aln.sam # for Oxford Nanopore reads
```
The difference between `map-pb` and `map-ont` is that `map-pb` uses
homopolymer-compressed (HPC) minimizers as seeds, while `map-ont` uses ordinary
minimizers as seeds. Emperical evaluation suggests HPC minimizers improve
performance and sensitivity when aligning PacBio reads, but hurt when aligning
Nanopore reads.
#### <a name="map-long-splice"></a>Map long mRNA/cDNA reads
```sh
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
```
There are different long-read RNA-seq technologies, including tranditional
full-length cDNA, EST, PacBio Iso-seq, Nanopore 2D cDNA-seq and Direct RNA-seq.
They produce data of varying quality and properties. By default, `-x splice`
assumes the read orientation relative to the transcript strand is unknown. It
tries two rounds of alignment to infer the orientation and write the strand to
the `ts` SAM/PAF tag if possible. For Iso-seq, Direct RNA-seq and tranditional
full-length cDNAs, it would be desired to apply `-u f` to force minimap2 to
consider the forward transcript strand only. This speeds up alignment with
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
reduces the accuracy when aligning against the widely used SIRV control data.
This is because SIRV does not honor the evolutionarily conservative splicing
signal. If you are studying SIRV, you may apply `--splice-flank=no` to let
minimap2 only model GT..AG, ignoring the additional base.
#### <a name="long-overlap"></a>Find overlaps between long reads
```sh
minimap2 -x ava-pb reads.fq reads.fq > ovlp.paf # PacBio read overlap
minimap2 -x ava-ont reads.fq reads.fq > ovlp.paf # Oxford Nanopore read overlap
```
Similarly, `ava-pb` uses HPC minimizers while `ava-ont` uses ordinary
minimizers. It is usually not recommended to perform base-level alignment in
the overlapping mode because it is slow and may produce false positive
overlaps. However, if performance is not a concern, you may try to add `-a` or
`-c` anyway.
#### <a name="short-genomic"></a>Map short accurate genomic reads
```sh
minimap2 -ax sr ref.fa reads-se.fq > aln.sam # single-end alignment
minimap2 -ax sr ref.fa read1.fq read2.fq > aln.sam # paired-end alignment
minimap2 -ax sr ref.fa reads-interleaved.fq > aln.sam # paired-end alignment
```
When two read files are specified, minimap2 reads from each file in turn and
merge them into an interleaved stream internally. Two reads are considered to
be paired if they are adjacent in the input stream and have the same name (with
the `/[0-9]` suffix trimmed if present). Single- and paired-end reads can be
mixed.
Minimap2 does not work well with short spliced reads. There are many capable
RNA-seq mappers for short reads.
#### <a name="full-genome"></a>Full genome/assembly alignment
```sh
minimap2 -ax asm5 ref.fa asm.fa > aln.sam # assembly to assembly/ref alignment
```
For cross-species full-genome alignment, the scoring system needs to be tuned
according to the sequence divergence.
### <a name="advanced"></a>Advanced features
#### <a name="long-cigar"></a>Working with >65535 CIGAR operations
Due to a design flaw, BAM does not work with CIGAR strings with >65535
operations (SAM and CRAM work). However, for ultra-long nanopore reads minimap2
may align ~1% of read bases with long CIGARs beyond the capability of BAM. If
you convert such SAM/CRAM to BAM, Picard and recent samtools will throw an
error and abort. Older samtools and other tools may create corrupted BAM.
To avoid this issue, you can add option `-L` at the minimap2 command line.
This option moves a long CIGAR to the `CG` tag and leaves a fully clipped CIGAR
at the SAM CIGAR column. Current tools that don't read CIGAR (e.g. merging and
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`.
**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
The `cs` SAM/PAF tag encodes bases at mismatches and INDELs. It matches regular
expression `/(:[0-9]+|\*[a-z][a-z]|[=\+\-][A-Za-z]+)+/`. Like CIGAR, `cs`
consists of series of operations. Each leading character specifies the
operation; the following sequence is the one involved in the operation.
The `cs` tag is enabled by command line option `--cs`. The following alignment,
for example:
```txt
CGATCGATAAATAGAGTAG---GAATAGCA
|||||| |||||||||| |||| |||
CGATCG---AATAGAGTAGGTCGAATtGCA
```
is represented as `:6-ata:10+gtc:4*at:3`, where `:[0-9]+` represents an
identical block, `-ata` represents a deltion, `+gtc` an insertion and `*at`
indicates reference base `a` is substituted with a query base `t`. It is
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. The `cs` tag also encodes intron
positions and splicing signals (see the [minimap2 manpage][manpage-cs] for
details).
#### <a name="paftools"></a>Working with the PAF format
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
In the following, minimap2 command line options have a dash ahead and are
highlighted in bold. The description may help to tune minimap2 parameters.
1. Read **-I** [=*4G*] reference bases, extract (**-k**,**-w**)-minimizers and
index them in a hash table.
2. Read **-K** [=*200M*] query bases. For each query sequence, do step 3
through 7:
3. For each (**-k**,**-w**)-minimizer on the query, check against the reference
index. If a reference minimizer is not among the top **-f** [=*2e-4*] most
frequent, collect its the occurrences in the reference, which are called
*seeds*.
4. Sort seeds by position in the reference. Chain them with dynamic
programming. Each chain represents a potential mapping. For read
overlapping, report all chains and then go to step 8. For reference mapping,
do step 5 through 7:
5. Let *P* be the set of primary mappings, which is 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 *P* by **--mask-level**
[=*0.5*] or higher fraction of the shorter chain, mark the chain as
*secondary* to the chain in *P*; otherwise, add the chain to *P*.
6. Retain all primary mappings. Also retain up to **-N** [=*5*] top secondary
mappings if their chaining scores are higher than **-p** [=*0.8*] of their
corresponding primary mappings.
7. If alignment is requested, filter out an internal seed if it potentially
leads to both a long insertion and a long deletion. Extend from the
left-most seed. Perform global alignments between internal seeds. Split the
chain if the accumulative score along the global alignment drops by **-z**
[=*400*], disregarding long gaps. Extend from the right-most seed. Output
chains and their alignments.
8. If there are more query sequences in the input, go to step 2 until no more
queries are left.
9. If there are more reference sequences, reopen the query file from the start
and go to step 1; otherwise stop.
### <a name="help"></a>Getting help
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 cite:
> Li, H. (2018). Minimap2: pairwise alignment for nucleotide sequences.
> Bioinformatics. [doi:10.1093/bioinformatics/bty191][doi]
## <a name="dguide"></a>Developers' Guide
Minimap2 is not only a command line tool, but also a programming library.
It provides C APIs to build/load index and to align sequences against the
index. File [example.c](example.c) demonstrates typical uses of C APIs. Header
file [minimap.h](minimap.h) gives more detailed API documentation. Minimap2
aims to keep APIs in this header stable. File [mmpriv.h](mmpriv.h) contains
additional private APIs which may be subjected to changes frequently.
This repository also provides Python bindings to a subset of C APIs. File
[python/README.rst](python/README.rst) gives the full documentation;
[python/minimap2.py](python/minimap2.py) shows an example. This Python
extension, mappy, is also [available from PyPI][mappypypi] via `pip install
mappy` or [from BioConda][mappyconda] via `conda install -c bioconda mappy`.
## <a name="limit"></a>Limitations
* Minimap2 may produce suboptimal alignments through long low-complexity
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 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.
* 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.
@@ -67,3 +362,12 @@ are warmly welcomed.
[longislnd]: https://www.ncbi.nlm.nih.gov/pubmed/27667791 [longislnd]: https://www.ncbi.nlm.nih.gov/pubmed/27667791
[gaba]: https://github.com/ocxtal/libgaba [gaba]: https://github.com/ocxtal/libgaba
[ksw2]: https://github.com/lh3/ksw2 [ksw2]: https://github.com/lh3/ksw2
[preprint]: https://arxiv.org/abs/1708.01492
[release]: https://github.com/lh3/minimap2/releases
[mappypypi]: https://pypi.python.org/pypi/mappy
[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
+661 -111
View File
@@ -1,21 +1,24 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <math.h>
#include "minimap.h" #include "minimap.h"
#include "mmpriv.h" #include "mmpriv.h"
#include "ksw2.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; int i, j;
a = a < 0? -a : a; a = a < 0? -a : a;
b = b > 0? -b : b; b = b > 0? -b : b;
sc_ambi = sc_ambi > 0? -sc_ambi : sc_ambi;
for (i = 0; i < m - 1; ++i) { for (i = 0; i < m - 1; ++i) {
for (j = 0; j < m - 1; ++j) for (j = 0; j < m - 1; ++j)
mat[i * m + j] = i == j? a : b; 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) 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) static inline void mm_seq_rev(uint32_t len, uint8_t *seq)
@@ -26,77 +29,169 @@ 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; 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) { if (score < *max) {
int li = i - *max_i; int li = i - *max_i;
int lj = j - *max_j; int lj = j - *max_j;
int diff = li > lj? li - lj : lj - li; int diff = li > lj? li - lj : lj - li;
if (*max - score > zdrop + diff * e) int z = *max - score - diff * e;
return 1; 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; } 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; uint32_t k;
int32_t score = 0, max = 0, max_i = -1, max_j = -1, i = 0, j = 0; int32_t score = 0, max = INT32_MIN, max_i = -1, max_j = -1, i = 0, j = 0, max_zdrop = 0;
for (k = 0; k < n_cigar; ++k) { 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; uint32_t l, op = cigar[k]&0xf, len = cigar[k]>>4;
if (op == 0) { if (op == 0) {
for (l = 0; l < len; ++l) { for (l = 0; l < len; ++l) {
score += mat[tseq[i + l] * 5 + qseq[j + 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; i += len, j += len;
} else if (op == 1) { } else if (op == 1 || op == 2 || op == 3) {
score -= q + e * len, j += len; score -= opt->q + opt->e * len;
if (test_zdrop_aux(score, i, j, &max, &max_i, &max_j, e, zdrop)) return 1; if (op == 1) j += len; // insertion
} else if (op == 2) { else i += len; // deletion
score -= q + e * len, i += len; update_max_zdrop(score, i, j, &max, &max_i, &max_j, opt->e, &max_zdrop, pos);
if (test_zdrop_aux(score, i, j, &max, &max_i, &max_j, e, zdrop)) return 1;
} }
} }
return 0;
}
static void mm_update_extra(mm_extra_t *p, const uint8_t *qseq, const uint8_t *tseq, const int8_t *mat, int8_t q, int8_t e) // 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)
{ {
uint32_t k, l, toff = 0, qoff = 0; mm_extra_t *p = r->p;
int32_t s = 0, max = 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
uint32_t op = p->cigar[k]&0xf, len = p->cigar[k]>>4;
if (len == 0) to_shrink = 1;
if (op == 0) {
toff += len, qoff += len;
} else if (op == 1 || op == 2) { // insertion or deletion
if (k > 0 && k < p->n_cigar - 1 && (p->cigar[k-1]&0xf) == 0 && (p->cigar[k+1]&0xf) == 0) {
int l, prev_len = p->cigar[k-1] >> 4;
if (op == 1) {
for (l = 0; l < prev_len; ++l)
if (qseq[qoff - 1 - l] != qseq[qoff + len - 1 - l])
break;
} else {
for (l = 0; l < prev_len; ++l)
if (tseq[toff - 1 - l] != tseq[toff + len - 1 - l])
break;
}
if (l > 0)
p->cigar[k-1] -= l<<4, p->cigar[k+1] += l<<4, qoff -= l, toff -= l;
if (l == prev_len) to_shrink = 1;
}
if (op == 1) qoff += len;
else toff += len;
} else if (op == 3) {
toff += len;
}
}
assert(qoff == r->qe - r->qs && toff == r->re - r->rs);
if (to_shrink) { // squeeze out zero-length operations
int32_t l = 0;
for (k = 0; k < p->n_cigar; ++k) // squeeze out zero-length operations
if (p->cigar[k]>>4 != 0)
p->cigar[l++] = p->cigar[k];
p->n_cigar = l;
for (k = l = 0; k < p->n_cigar; ++k) // merge two adjacent operations if they are the same
if (k == p->n_cigar - 1 || (p->cigar[k]&0xf) != (p->cigar[k+1]&0xf))
p->cigar[l++] = p->cigar[k];
else p->cigar[k+1] += p->cigar[k]>>4<<4; // add length to the next CIGAR operator
p->n_cigar = l;
}
if ((p->cigar[0]&0xf) == 1 || (p->cigar[0]&0xf) == 2) { // get rid of leading I or D
int32_t l = p->cigar[0] >> 4;
if ((p->cigar[0]&0xf) == 1) {
if (r->rev) r->qe -= l;
else r->qs += l;
*qshift = l;
} else r->rs += l, *tshift = l;
--p->n_cigar;
memmove(p->cigar, p->cigar + 1, p->n_cigar * 4);
}
}
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;
int32_t s = 0, max = 0, qshift, tshift, toff = 0, qoff = 0;
mm_extra_t *p = r->p;
if (p == 0) return; if (p == 0) return;
mm_fix_cigar(r, qseq, tseq, &qshift, &tshift);
qseq += qshift, tseq += tshift; // qseq and tseq may be shifted due to the removal of leading I/D
r->blen = r->mlen = 0;
for (k = 0; k < p->n_cigar; ++k) { for (k = 0; k < p->n_cigar; ++k) {
uint32_t op = p->cigar[k]&0xf, len = p->cigar[k]>>4; uint32_t op = p->cigar[k]&0xf, len = p->cigar[k]>>4;
if (op == 0) { if (op == 0) { // match/mismatch
int n_ambi = 0, n_diff = 0;
for (l = 0; l < len; ++l) { for (l = 0; l < len; ++l) {
int cq = qseq[qoff + l], ct = tseq[toff + l]; int cq = qseq[qoff + l], ct = tseq[toff + l];
if (ct > 3 || cq > 3) ++p->n_ambi; if (ct > 3 || cq > 3) ++n_ambi;
else if (ct != cq) ++p->n_diff; else if (ct != cq) ++n_diff;
s += mat[ct * 5 + cq]; s += mat[ct * 5 + cq];
if (s < 0) s = 0; if (s < 0) s = 0;
else max = max > s? max : s; else max = max > s? max : s;
} }
toff += len, qoff += len, p->blen += len; r->blen += len - n_ambi, r->mlen += len - (n_ambi + n_diff), p->n_ambi += n_ambi;
} else if (op == 1) { toff += len, qoff += len;
} else if (op == 1) { // insertion
int n_ambi = 0; int n_ambi = 0;
for (l = 0; l < len; ++l) for (l = 0; l < len; ++l)
if (qseq[qoff + l] > 3) ++n_ambi; if (qseq[qoff + l] > 3) ++n_ambi;
qoff += len, p->blen += len; r->blen += len - n_ambi, p->n_ambi += n_ambi;
p->n_ambi += n_ambi, p->n_diff += len - n_ambi;
s -= q + e * len; s -= q + e * len;
if (s < 0) s = 0; if (s < 0) s = 0;
} else if (op == 2) { qoff += len;
} else if (op == 2) { // deletion
int n_ambi = 0; int n_ambi = 0;
for (l = 0; l < len; ++l) for (l = 0; l < len; ++l)
if (tseq[toff + l] > 3) ++n_ambi; if (tseq[toff + l] > 3) ++n_ambi;
toff += len, p->blen += len; r->blen += len - n_ambi, p->n_ambi += n_ambi;
p->n_ambi += n_ambi, p->n_diff += len - n_ambi;
s -= q + e * len; s -= q + e * len;
if (s < 0) s = 0; if (s < 0) s = 0;
toff += len;
} else if (op == 3) { // intron
toff += len;
} }
} }
p->dp_max = max; p->dp_max = max;
assert(qoff == r->qe - r->qs && toff == r->re - r->rs);
} }
static void mm_append_cigar(mm_reg1_t *r, uint32_t n_cigar, uint32_t *cigar) // TODO: this calls the libc realloc() static void mm_append_cigar(mm_reg1_t *r, uint32_t n_cigar, uint32_t *cigar) // TODO: this calls the libc realloc()
@@ -104,12 +199,12 @@ static void mm_append_cigar(mm_reg1_t *r, uint32_t n_cigar, uint32_t *cigar) //
mm_extra_t *p; mm_extra_t *p;
if (n_cigar == 0) return; if (n_cigar == 0) return;
if (r->p == 0) { 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); kroundup32(capacity);
r->p = (mm_extra_t*)calloc(capacity, 4); r->p = (mm_extra_t*)calloc(capacity, 4);
r->p->capacity = capacity; r->p->capacity = capacity;
} else if (r->p->n_cigar + n_cigar + sizeof(mm_extra_t) > r->p->capacity) { } 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); r->p->capacity = r->p->n_cigar + n_cigar + sizeof(mm_extra_t)/4;
kroundup32(r->p->capacity); kroundup32(r->p->capacity);
r->p = (mm_extra_t*)realloc(r->p, r->p->capacity * 4); r->p = (mm_extra_t*)realloc(r->p, r->p->capacity * 4);
} }
@@ -124,12 +219,100 @@ 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 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
{ {
if (opt->q == opt->q2 && opt->e == opt->e2) uint32_t n_EQX = 0;
ksw_extz2_sse(km, qlen, qseq, tlen, tseq, 5, mat, opt->q, opt->e, w, opt->zdrop, flag, ez); 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)
{
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);
for (i = 0; i < tlen; ++i) fputc("ACGTN"[tseq[i]], stderr);
fputc('\n', stderr);
for (i = 0; i < qlen; ++i) fputc("ACGTN"[qseq[i]], stderr);
fputc('\n', stderr);
}
if (opt->flag & MM_F_SPLICE)
ksw_exts2_sse(km, qlen, qseq, tlen, tseq, 5, mat, opt->q, opt->e, opt->q2, opt->noncan, zdrop, flag, ez);
else if (opt->q == opt->q2 && opt->e == opt->e2)
ksw_extz2_sse(km, qlen, qseq, tlen, tseq, 5, mat, opt->q, opt->e, w, zdrop, end_bonus, flag, ez);
else else
ksw_extd2_sse(km, qlen, qseq, tlen, tseq, 5, mat, opt->q, opt->e, opt->q2, opt->e2, w, opt->zdrop, flag, ez); 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) static inline int mm_get_hplen_back(const mm_idx_t *mi, uint32_t rid, uint32_t x)
@@ -143,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) 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]; const uint8_t *qseq = qseq0[a->x>>63];
int i, c; int i, c;
*q = (int32_t)a->y; *q = (int32_t)a->y;
@@ -153,70 +336,326 @@ static inline void mm_adjust_minier(const mm_idx_t *mi, uint8_t *const qseq0[2],
c = mm_get_hplen_back(mi, a->x<<1>>33, (int32_t)a->x); c = mm_get_hplen_back(mi, a->x<<1>>33, (int32_t)a->x);
*r = (int32_t)a->x + 1 - c; *r = (int32_t)a->x + 1 - c;
} else { } else {
*r = (int32_t)a->x + 1; *r = (int32_t)a->x - (mi->k>>1);
*q = (int32_t)a->y + 1; *q = (int32_t)a->y - (mi->k>>1);
} }
} }
static void mm_filter_bad_seeds(int n, mm128_t *a, int max_space, int max_diff) static int *collect_long_gaps(void *km, int as1, int cnt1, mm128_t *a, int min_gap, int *n_)
{ {
int i; int i, n, *K;
if (n < 3) return; *n_ = 0;
for (i = 0; i < n - 2; ++i) { for (i = 1, n = 0; i < cnt1; ++i) { // count the number of gaps longer than min_gap
int32_t q[3], t[3], gap01, gap12, gap02; int gap = ((int32_t)a[as1 + i].y - a[as1 + i - 1].y) - ((int32_t)a[as1 + i].x - a[as1 + i - 1].x);
t[0] = (int32_t)a[i].x, q[0] = (int32_t)a[i].y; if (gap < -min_gap || gap > min_gap) ++n;
t[1] = (int32_t)a[i+1].x, q[1] = (int32_t)a[i+1].y; }
t[2] = (int32_t)a[i+2].x, q[2] = (int32_t)a[i+2].y; if (n <= 1) return 0;
if (t[2] - t[0] > max_space || q[2] - q[0] > max_space) continue; K = (int*)kmalloc(km, n * sizeof(int));
gap01 = (t[1] - t[0]) - (q[1] - q[0]), gap01 = gap01 > 0? gap01 : -gap01; for (i = 1, n = 0; i < cnt1; ++i) { // store the positions of long gaps
gap12 = (t[2] - t[1]) - (q[2] - q[1]), gap12 = gap12 > 0? gap12 : -gap12; int gap = ((int32_t)a[as1 + i].y - a[as1 + i - 1].y) - ((int32_t)a[as1 + i].x - a[as1 + i - 1].x);
gap02 = (t[2] - t[0]) - (q[2] - q[0]), gap02 = gap02 > 0? gap02 : -gap02; if (gap < -min_gap || gap > min_gap)
if (gap01 + gap12 - gap02 > max_diff) K[n++] = i;
a[++i].y |= 1ULL << 41; }
*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;
if (k == n || k >= max_en) {
if (max_en > 0)
for (i = K[max_st]; i < K[max_en]; ++i)
a[as1 + i].y |= MM_SEED_IGNORE;
max = 0, max_st = max_en = -1;
if (k == n) break;
}
i = K[k];
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;
rs = (int32_t)a[as1 + i - 1].x;
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) - (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);
if (max_diff < diff)
max_diff = diff, max_diff_l = l;
}
if (max_diff > diff_thres && max_diff > max)
max = max_diff, max_st = k, max_en = max_diff_l;
}
kfree(km, K);
}
static void mm_filter_bad_seeds_alt(void *km, int as1, int cnt1, mm128_t *a, int min_gap, int max_ext)
{
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;
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;
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;
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;
m += min < q_span? min : q_span;
if (l >= bw << 1 || (m >= min_match && m >= bw) || m >= r->mlen >> 1) break;
} }
} }
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, mm128_t *a, ksw_extz_t *ez) static void mm_max_stretch(const mm_reg1_t *r, const mm128_t *a, int32_t *as, int32_t *cnt)
{ {
int32_t rid = a[r->as].x<<1>>33, rev = a[r->as].x>>63; int32_t i, score, max_score, len, max_i, max_len;
*as = r->as, *cnt = r->cnt;
if (r->cnt < 2) return;
max_score = -1, max_i = -1, max_len = 0;
score = a[r->as].y >> 32 & 0xff, len = 1;
for (i = r->as + 1; i < r->as + r->cnt; ++i) {
int32_t lq, lr, q_span;
q_span = a[i].y >> 32 & 0xff;
lr = (int32_t)a[i].x - (int32_t)a[i-1].x;
lq = (int32_t)a[i].y - (int32_t)a[i-1].y;
if (lq == lr) {
score += lq < q_span? lq : q_span;
++len;
} else {
if (score > max_score)
max_score = score, max_len = len, max_i = i - len;
score = q_span, len = 1;
}
}
if (score > max_score)
max_score = score, max_len = len, max_i = i - len;
*as = max_i, *cnt = max_len;
}
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;
uint8_t *tseq, *qseq; uint8_t *tseq, *qseq;
int32_t i, l, bw, dropped = 0, rs0, re0, qs0, qe0; int32_t i, l, bw, dropped = 0, extra_flag = 0, rs0, re0, qs0, qe0;
int32_t rs, re, qs, qe; int32_t rs, re, qs, qe;
int32_t rs1, qs1, re1, qe1; int32_t rs1, qs1, re1, qe1;
int8_t mat[25]; int8_t mat[25];
if (r->cnt == 0) return; 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
ksw_gen_simple_mat(5, mat, opt->a, opt->b);
bw = (int)(opt->bw * 1.5 + 1.);
r2->cnt = 0; r2->cnt = 0;
mm_adjust_minier(mi, qseq0, &a[r->as], &rs, &qs); if (r->cnt == 0) return;
mm_adjust_minier(mi, qseq0, &a[r->as + r->cnt - 1], &re, &qe); ksw_gen_simple_mat(5, mat, opt->a, opt->b, opt->sc_ambi);
mm_filter_bad_seeds(r->cnt, &a[r->as], opt->max_gap, 100); bw = (int)(opt->bw * 1.5 + 1.);
// compute rs0 and qs0 if (is_sr && !(mi->flag & MM_I_HPC)) {
if (r->split && r->as > 0) { mm_max_stretch(r, a, &as1, &cnt1);
mm_adjust_minier(mi, qseq0, &a[r->as-1], &rs0, &qs0); 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 { } else {
if (qs > 0 && rs > 0) { // actually this is always true 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);
}
assert(cnt1 > 0);
if (is_splice) {
if (splice_flag & MM_F_SPLICE_FOR) extra_flag |= rev? KSW_EZ_SPLICE_REV : KSW_EZ_SPLICE_FOR;
if (splice_flag & MM_F_SPLICE_REV) extra_flag |= rev? KSW_EZ_SPLICE_FOR : KSW_EZ_SPLICE_REV;
if (opt->flag & MM_F_SPLICE_FLANK) extra_flag |= KSW_EZ_SPLICE_FLANK;
}
/* Look for the start and end of regions to perform DP. This sounds easy
* but is in fact tricky. Excessively small regions lead to unnecessary
* clippings and lose alignable sequences. Excessively large regions
* occasionally lead to large overlaps between two chains and may cause
* loss of alignments in corner cases. */
if (is_sr) {
qs0 = 0, qe0 = qlen;
l = qs;
l += l * opt->a + opt->end_bonus > opt->q? (l * opt->a + opt->end_bonus - opt->q) / opt->e : 0;
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 < (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);
qs0 = (int32_t)a[r->as].y + 1 - (int32_t)(a[r->as].y>>32&0xff);
if (rs0 < 0) rs0 = 0; // this may happen when HPC is in use
assert(qs0 >= 0); // this should never happen, or it is logic error
rs1 = qs1 = 0;
for (i = r->as - 1, l = 0; i >= 0 && a[i].x>>32 == a[r->as].x>>32; --i) { // inspect nearby seeds
int32_t x = (int32_t)a[i].x + 1 - (int32_t)(a[i].y>>32&0xff);
int32_t y = (int32_t)a[i].y + 1 - (int32_t)(a[i].y>>32&0xff);
if (x < rs0 && y < qs0) {
if (++l > opt->min_cnt) {
l = rs0 - x > qs0 - y? rs0 - x : qs0 - y;
rs1 = rs0 - l, qs1 = qs0 - l;
break;
}
}
}
if (qs > 0 && rs > 0) {
l = qs < opt->max_gap? qs : opt->max_gap; l = qs < opt->max_gap? qs : opt->max_gap;
qs0 = qs - l; qs1 = qs1 > qs - l? qs1 : qs - l;
qs0 = qs0 < qs1? qs0 : qs1; // at least include qs0
l += l * opt->a > opt->q? (l * opt->a - opt->q) / opt->e : 0; 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 < opt->max_gap? l : opt->max_gap;
l = l < rs? l : rs; l = l < rs? l : rs;
rs0 = rs - l; rs1 = rs1 > rs - l? rs1 : rs - l;
rs0 = rs0 < rs1? rs0 : rs1;
} else rs0 = rs, qs0 = qs; } else rs0 = rs, qs0 = qs;
}
// compute re0 and qe0 // compute re0 and qe0
if (qe < qlen && re < mi->seq[rid].len) { re0 = (int32_t)a[r->as + r->cnt - 1].x + 1;
qe0 = (int32_t)a[r->as + r->cnt - 1].y + 1;
re1 = mi->seq[rid].len, qe1 = qlen;
for (i = r->as + r->cnt, l = 0; i < n_a && a[i].x>>32 == a[r->as].x>>32; ++i) { // inspect nearby seeds
int32_t x = (int32_t)a[i].x + 1;
int32_t y = (int32_t)a[i].y + 1;
if (x > re0 && y > qe0) {
if (++l > opt->min_cnt) {
l = x - re0 > y - qe0? x - re0 : y - qe0;
re1 = re0 + l, qe1 = qe0 + l;
break;
}
}
}
if (qe < qlen && re < (int32_t)mi->seq[rid].len) {
l = qlen - qe < opt->max_gap? qlen - qe : opt->max_gap; l = qlen - qe < opt->max_gap? qlen - qe : opt->max_gap;
qe0 = qe + l; 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->a > opt->q? (l * opt->a - opt->q) / opt->e : 0;
l = l < opt->max_gap? l : opt->max_gap; 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;
re0 = re + l; re1 = re1 < re + l? re1 : re + l;
re0 = re0 > re1? re0 : re1;
} else re0 = re, qe0 = qe; } 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); assert(re0 > rs0);
tseq = (uint8_t*)kmalloc(km, re0 - rs0); tseq = (uint8_t*)kmalloc(km, re0 - rs0);
@@ -226,44 +665,62 @@ 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_idx_getseq(mi, rid, rs0, rs, tseq);
mm_seq_rev(qs - qs0, qseq); mm_seq_rev(qs - qs0, qseq);
mm_seq_rev(rs - rs0, tseq); mm_seq_rev(rs - rs0, tseq);
mm_align_pair(km, opt, qs - qs0, qseq, rs - rs0, tseq, mat, bw, 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) { if (ez->n_cigar > 0) {
mm_append_cigar(r, ez->n_cigar, ez->cigar); mm_append_cigar(r, ez->n_cigar, ez->cigar);
r->p->dp_score += ez->max; r->p->dp_score += ez->max;
} }
rs1 = rs - (ez->max_t + 1); rs1 = rs - (ez->reach_end? ez->mqe_t + 1 : ez->max_t + 1);
qs1 = qs - (ez->max_q + 1); qs1 = qs - (ez->reach_end? qs - qs0 : ez->max_q + 1);
mm_seq_rev(qs - qs0, qseq); mm_seq_rev(qs - qs0, qseq);
} else rs1 = rs, qs1 = qs; } else rs1 = rs, qs1 = qs;
re1 = rs, qe1 = qs; re1 = rs, qe1 = qs;
assert(qs1 >= 0 && rs1 >= 0); assert(qs1 >= 0 && rs1 >= 0);
for (i = 1; i < r->cnt; ++i) { // gap filling for (i = is_sr? cnt1 - 1 : 1; i < cnt1; ++i) { // gap filling
if (a[r->as+i].y>>41&1) continue; if ((a[as1+i].y & (MM_SEED_IGNORE|MM_SEED_TANDEM)) && i != cnt1 - 1) continue;
mm_adjust_minier(mi, qseq0, &a[r->as + i], &re, &qe); 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; re1 = re, qe1 = qe;
if (i == r->cnt - 1 || (a[r->as+i].y>>40&1) || qe - qs >= opt->min_ksw_len || re - rs >= opt->min_ksw_len) { 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 bw1 = bw; int j, bw1 = bw, zdrop_code;
if (a[r->as+i].y>>40&1) if (a[as1+i].y & MM_SEED_LONG_JOIN)
bw1 = qe - qs > re - rs? qe - qs : re - rs; bw1 = qe - qs > re - rs? qe - qs : re - rs;
// perform alignment
qseq = &qseq0[rev][qs]; qseq = &qseq0[rev][qs];
mm_idx_getseq(mi, rid, rs, re, tseq); mm_idx_getseq(mi, rid, rs, re, tseq);
mm_align_pair(km, opt, qe - qs, qseq, re - rs, tseq, mat, bw1, KSW_EZ_APPROX_MAX, ez); if (is_sr) { // perform ungapped alignment
if (mm_check_zdrop(qseq, tseq, ez->n_cigar, ez->cigar, mat, opt->q, opt->e, opt->zdrop)) assert(qe - qs == re - rs);
mm_align_pair(km, opt, qe - qs, qseq, re - rs, tseq, mat, bw1, 0, ez); ksw_reset_extz(ez);
for (j = 0, ez->score = 0; j < qe - qs; ++j) {
if (qseq[j] >= 4 || tseq[j] >= 4) ez->score += opt->e2;
else ez->score += qseq[j] == tseq[j]? opt->a : -opt->b;
}
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, opt->zdrop, extra_flag|KSW_EZ_APPROX_MAX, ez); // first pass: with approximate Z-drop
}
// 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) if (ez->n_cigar > 0)
mm_append_cigar(r, ez->n_cigar, ez->cigar); 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. 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.
int j;
for (j = i - 1; j >= 0; --j) for (j = i - 1; j >= 0; --j)
if ((int32_t)a[r->as + j].x < re + ez->max_t) if ((int32_t)a[as1 + j].x <= rs + ez->max_t)
break; break;
dropped = 1; dropped = 1;
if (j < 0) j = 0;
r->p->dp_score += ez->max; r->p->dp_score += ez->max;
re1 = rs + (ez->max_t + 1); re1 = rs + (ez->max_t + 1);
qe1 = qs + (ez->max_q + 1); qe1 = qs + (ez->max_q + 1);
if (r->cnt - (j + 1) >= opt->min_cnt) if (cnt1 - (j + 1) >= opt->min_cnt) {
mm_split_reg(r, r2, j + 1, qlen, a); mm_split_reg(r, r2, as1 + j + 1 - r->as, qlen, a);
if (zdrop_code == 2) r2->split_inv = 1;
}
break; break;
} else r->p->dp_score += ez->score; } else r->p->dp_score += ez->score;
rs = re, qs = qe; rs = re, qs = qe;
@@ -273,13 +730,13 @@ 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 if (!dropped && qe < qe0 && re < re0) { // right extension
qseq = &qseq0[rev][qe]; qseq = &qseq0[rev][qe];
mm_idx_getseq(mi, rid, re, re0, tseq); mm_idx_getseq(mi, rid, re, re0, tseq);
mm_align_pair(km, opt, qe0 - qe, qseq, re0 - re, tseq, mat, bw, 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) { if (ez->n_cigar > 0) {
mm_append_cigar(r, ez->n_cigar, ez->cigar); mm_append_cigar(r, ez->n_cigar, ez->cigar);
r->p->dp_score += ez->max; r->p->dp_score += ez->max;
} }
re1 = re + (ez->max_t + 1); re1 = re + (ez->reach_end? ez->mqe_t + 1 : ez->max_t + 1);
qe1 = qe + (ez->max_q + 1); qe1 = qe + (ez->reach_end? qe0 - qe : ez->max_q + 1);
} }
assert(qe1 <= qlen); assert(qe1 <= qlen);
@@ -290,44 +747,137 @@ static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int
assert(re1 - rs1 <= re0 - rs0); assert(re1 - rs1 <= re0 - rs0);
if (r->p) { if (r->p) {
mm_idx_getseq(mi, rid, rs1, re1, tseq); mm_idx_getseq(mi, rid, rs1, re1, tseq);
mm_update_extra(r->p, &qseq0[r->rev][qs1], 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
} }
kfree(km, tseq); 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], 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;
int8_t mat[25];
void *qp;
memset(r_inv, 0, sizeof(mm_reg1_t));
if (!(r1->split&1) || !(r2->split&2)) return 0;
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 = 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, opt->sc_ambi);
tseq = (uint8_t*)kmalloc(km, tl);
mm_idx_getseq(mi, r1->rid, r1->re, r2->rs, tseq);
qseq = r1->rev? &qseq0[0][r2->qe] : &qseq0[1][qlen - r2->qs];
mm_seq_rev(ql, qseq);
mm_seq_rev(tl, tseq);
qp = ksw_ll_qinit(km, 2, ql, qseq, 5, mat);
score = ksw_ll_i16(qp, tl, tseq, opt->q, opt->e, &q_off, &t_off);
kfree(km, qp);
mm_seq_rev(ql, qseq);
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, 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;
r_inv->id = -1;
r_inv->parent = MM_PARENT_UNSET;
r_inv->inv = 1;
r_inv->rev = !r1->rev;
r_inv->rid = r1->rid;
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);
return ret;
}
static inline mm_reg1_t *mm_insert_reg(const mm_reg1_t *r, int i, int *n_regs, mm_reg1_t *regs)
{
regs = (mm_reg1_t*)realloc(regs, (*n_regs + 1) * sizeof(mm_reg1_t));
if (i + 1 != *n_regs)
memmove(&regs[i + 2], &regs[i + 1], sizeof(mm_reg1_t) * (*n_regs - i - 1));
regs[i + 1] = *r;
++*n_regs;
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, 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]; extern unsigned char seq_nt4_table[256];
int32_t i, r, n_regs = *n_regs_; int32_t i, n_regs = *n_regs_, n_a;
uint8_t *qseq0[2]; uint8_t *qseq0[2];
ksw_extz_t ez; ksw_extz_t ez;
// encode the query sequence // encode the query sequence
qseq0[0] = (uint8_t*)kmalloc(km, qlen); qseq0[0] = (uint8_t*)kmalloc(km, qlen * 2);
qseq0[1] = (uint8_t*)kmalloc(km, qlen); qseq0[1] = qseq0[0] + qlen;
for (i = 0; i < qlen; ++i) { for (i = 0; i < qlen; ++i) {
qseq0[0][i] = seq_nt4_table[(uint8_t)qstr[i]]; qseq0[0][i] = seq_nt4_table[(uint8_t)qstr[i]];
qseq0[1][qlen - 1 - i] = qseq0[0][i] < 4? 3 - qseq0[0][i] : 4; qseq0[1][qlen - 1 - i] = qseq0[0][i] < 4? 3 - qseq0[0][i] : 4;
} }
// align through seed hits // align through seed hits
n_a = mm_squeeze_a(km, n_regs, regs, a);
memset(&ez, 0, sizeof(ksw_extz_t)); memset(&ez, 0, sizeof(ksw_extz_t));
for (r = 0; r < n_regs; ++r) { for (i = 0; i < n_regs; ++i) {
mm_reg1_t r2; mm_reg1_t r2;
mm_align1(km, opt, mi, qlen, qseq0, &regs[r], &r2, a, &ez); if ((opt->flag&MM_F_SPLICE) && (opt->flag&MM_F_SPLICE_FOR) && (opt->flag&MM_F_SPLICE_REV)) { // then do two rounds of alignments for both strands
if (r2.cnt > 0) { mm_reg1_t s[2], s2[2];
regs = (mm_reg1_t*)realloc(regs, (n_regs + 1) * sizeof(mm_reg1_t)); // this should be very rare int which, trans_strand;
if (r + 1 != n_regs) s[0] = s[1] = regs[i];
memmove(&regs[r + 2], &regs[r + 1], sizeof(mm_reg1_t) * (n_regs - r - 1)); mm_align1(km, opt, mi, qlen, qseq0, &s[0], &s2[0], n_a, a, &ez, MM_F_SPLICE_FOR);
regs[r + 1] = r2; mm_align1(km, opt, mi, qlen, qseq0, &s[1], &s2[1], n_a, a, &ez, MM_F_SPLICE_REV);
++n_regs; 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
if (which == 0) {
regs[i] = s[0], r2 = s2[0];
free(s[1].p);
} else {
regs[i] = s[1], r2 = s2[1];
free(s[0].p);
}
regs[i].p->trans_strand = trans_strand;
} else { // one round of alignment
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 (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
}
} }
} }
*n_regs_ = n_regs; *n_regs_ = n_regs;
kfree(km, qseq0[0]); kfree(km, qseq0[1]); kfree(km, qseq0[0]);
kfree(km, ez.cigar); kfree(km, ez.cigar);
mm_filter_regs(km, opt, n_regs_, regs); mm_filter_regs(opt, qlen, n_regs_, regs);
mm_hit_sort_by_dp(km, n_regs_, regs); mm_hit_sort(km, n_regs_, regs);
return regs; return regs;
} }
+118 -20
View File
@@ -1,16 +1,38 @@
#include <zlib.h> #include <zlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <assert.h> #include <assert.h>
#define __STDC_LIMIT_MACROS
#include "bseq.h" #include "bseq.h"
#include "kvec.h"
#include "kseq.h" #include "kseq.h"
KSEQ_INIT(gzFile, gzread) KSEQ_INIT2(, gzFile, gzread)
unsigned char seq_comp_table[256] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 'T', 'V', 'G', 'H', 'E', 'F', 'C', 'D', 'I', 'J', 'M', 'L', 'K', 'N', 'O',
'P', 'Q', 'Y', 'S', 'A', 'A', 'B', 'W', 'X', 'R', 'Z', 91, 92, 93, 94, 95,
64, 't', 'v', 'g', 'h', 'e', 'f', 'c', 'd', 'i', 'j', 'm', 'l', 'k', 'n', 'o',
'p', 'q', 'y', 's', 'a', 'a', 'b', 'w', 'x', 'r', 'z', 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
};
#define CHECK_PAIR_THRES 1000000
struct mm_bseq_file_s { struct mm_bseq_file_s {
int is_eof;
gzFile fp; gzFile fp;
kseq_t *ks; kseq_t *ks;
mm_bseq1_t s;
}; };
mm_bseq_file_t *mm_bseq_open(const char *fn) mm_bseq_file_t *mm_bseq_open(const char *fn)
@@ -32,33 +54,109 @@ void mm_bseq_close(mm_bseq_file_t *fp)
free(fp); free(fp);
} }
mm_bseq1_t *mm_bseq_read(mm_bseq_file_t *fp, int chunk_size, int with_qual, int *n_) static inline char *kstrdup(const kstring_t *s)
{ {
int size = 0, m, n; char *t;
mm_bseq1_t *seqs; 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 = 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? 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_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};
kseq_t *ks = fp->ks; kseq_t *ks = fp->ks;
m = n = 0; seqs = 0; *n_ = 0;
if (fp->s.seq) {
kv_resize(mm_bseq1_t, 0, a, 256);
kv_push(mm_bseq1_t, 0, a, fp->s);
size = fp->s.l_seq;
memset(&fp->s, 0, sizeof(mm_bseq1_t));
}
while (kseq_read(ks) >= 0) { while (kseq_read(ks) >= 0) {
mm_bseq1_t *s; mm_bseq1_t *s;
assert(ks->seq.l <= INT32_MAX); assert(ks->seq.l <= INT32_MAX);
if (n >= m) { if (a.m == 0) kv_resize(mm_bseq1_t, 0, a, 256);
m = m? m<<1 : 256; kv_pushp(mm_bseq1_t, 0, a, &s);
seqs = (mm_bseq1_t*)realloc(seqs, m * sizeof(mm_bseq1_t)); 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, 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));
} else break;
}
}
break;
}
}
*n_ = a.n;
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_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;
kvec_t(mm_bseq1_t) a = {0,0,0};
*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)
++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, with_comment);
size += s->l_seq;
} }
s = &seqs[n];
s->name = strdup(ks->name.s);
s->seq = strdup(ks->seq.s);
s->qual = with_qual && ks->qual.l? strdup(ks->qual.s) : 0;
s->l_seq = ks->seq.l;
size += seqs[n++].l_seq;
if (size >= chunk_size) break; if (size >= chunk_size) break;
} }
if (size < chunk_size) fp->is_eof = 1; *n_ = a.n;
*n_ = n; return a.a;
return seqs; }
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) int mm_bseq_eof(mm_bseq_file_t *fp)
{ {
return fp->is_eof; return (ks_eof(fp->ks->f) && fp->s.seq == 0);
} }
+36 -1
View File
@@ -2,6 +2,7 @@
#define MM_BSEQ_H #define MM_BSEQ_H
#include <stdint.h> #include <stdint.h>
#include <string.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -12,15 +13,49 @@ typedef struct mm_bseq_file_s mm_bseq_file_t;
typedef struct { typedef struct {
int l_seq, rid; int l_seq, rid;
char *name, *seq, *qual; char *name, *seq, *qual, *comment;
} mm_bseq1_t; } mm_bseq1_t;
mm_bseq_file_t *mm_bseq_open(const char *fn); mm_bseq_file_t *mm_bseq_open(const char *fn);
void mm_bseq_close(mm_bseq_file_t *fp); 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(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); int mm_bseq_eof(mm_bseq_file_t *fp);
extern unsigned char seq_nt4_table[256]; extern unsigned char seq_nt4_table[256];
extern unsigned char seq_comp_table[256];
static inline int mm_qname_len(const char *s)
{
int l;
l = strlen(s);
return l >= 3 && s[l-1] >= '0' && s[l-1] <= '9' && s[l-2] == '/'? l - 2 : l;
}
static inline int mm_qname_same(const char *s1, const char *s2)
{
int l1, l2;
l1 = mm_qname_len(s1);
l2 = mm_qname_len(s2);
return (l1 == l2 && strncmp(s1, s2, l1) == 0);
}
static inline void mm_revcomp_bseq(mm_bseq1_t *s)
{
int i, t, l = s->l_seq;
for (i = 0; i < l>>1; ++i) {
t = s->seq[l - i - 1];
s->seq[l - i - 1] = seq_comp_table[(uint8_t)s->seq[i]];
s->seq[i] = seq_comp_table[t];
}
if (l&1) s->seq[l>>1] = seq_comp_table[(uint8_t)s->seq[l>>1]];
if (s->qual)
for (i = 0; i < l>>1; ++i)
t = s->qual[l - i - 1], s->qual[l - i - 1] = s->qual[i], s->qual[i] = t;
}
#ifdef __cplusplus #ifdef __cplusplus
} }
+46 -24
View File
@@ -19,35 +19,52 @@ static inline int ilog2_32(uint32_t v)
return (t = v>>8) ? 8 + LogTable256[t] : LogTable256[v]; return (t = v>>8) ? 8 + LogTable256[t] : LogTable256[v];
} }
int mm_chain_dp(int max_dist, int bw, int max_skip, int min_cnt, int min_sc, int64_t n, mm128_t *a, uint64_t **_u, void *km) 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)
{ // TODO: make sure this works when n has more than 32 bits { // TODO: make sure this works when n has more than 32 bits
int32_t st = 0, j, k, *f, *p, *t, *v, n_u, n_v; int32_t k, *f, *p, *t, *v, n_u, n_v;
int64_t i; int64_t i, j, st = 0;
uint64_t *u, *u2; uint64_t *u, *u2, sum_qspan = 0;
float avg_qspan;
mm128_t *b, *w; mm128_t *b, *w;
if (_u) *_u = 0; if (_u) *_u = 0, *n_u_ = 0;
f = (int32_t*)kmalloc(km, n * 4); f = (int32_t*)kmalloc(km, n * 4);
p = (int32_t*)kmalloc(km, n * 4); p = (int32_t*)kmalloc(km, n * 4);
t = (int32_t*)kmalloc(km, n * 4); t = (int32_t*)kmalloc(km, n * 4);
v = (int32_t*)kmalloc(km, n * 4);
memset(t, 0, n * 4); memset(t, 0, n * 4);
for (i = 0; i < n; ++i) sum_qspan += a[i].y>>32&0xff;
avg_qspan = (float)sum_qspan / n;
// fill the score and backtrack arrays // fill the score and backtrack arrays
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
uint64_t ri = a[i].x; uint64_t ri = a[i].x;
int64_t max_j = -1;
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 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 = -INT32_MAX, max_j = -1, n_skip = 0, min_d; int32_t max_f = q_span, n_skip = 0, min_d;
while (st < i && ri - a[st].x > max_dist) ++st; 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;
for (j = i - 1; j >= st; --j) { for (j = i - 1; j >= st; --j) {
int64_t dr = ri - a[j].x; int64_t dr = ri - a[j].x;
int32_t dq = qi - (int32_t)a[j].y, dd, sc; int32_t dq = qi - (int32_t)a[j].y, dd, sc, log_dd;
if (dr == 0 || dq <= 0 || dq > max_dist) continue; int32_t sidj = (a[j].y & MM_SEED_SEG_MASK) >> MM_SEED_SEG_SHIFT;
if ((sidi == sidj && dr == 0) || dq <= 0) continue; // don't skip if an anchor is used by multiple segments; see below
if ((sidi == sidj && dq > max_dist_y) || dq > max_dist_x) continue;
dd = dr > dq? dr - dq : dq - dr; dd = dr > dq? dr - dq : dq - dr;
if (dd > bw) continue; if (sidi == sidj && dd > bw) continue;
if (n_segs > 1 && !is_cdna && sidi == sidj && dr > max_dist_y) continue;
min_d = dq < dr? dq : dr; min_d = dq < dr? dq : dr;
sc = min_d > q_span? q_span : dq < dr? dq : dr; sc = min_d > q_span? q_span : dq < dr? dq : dr;
sc -= dd? ilog2_32(dd) * 2 : 0; log_dd = dd? ilog2_32(dd) : 0;
if (min_d > q_span) sc -= ilog2_32(min_d) / 2; if (is_cdna || sidi != sidj) {
int c_log, c_lin;
c_lin = (int)(dd * .01 * avg_qspan);
c_log = log_dd;
if (sidi != sidj && dr == 0) ++sc; // possibly due to overlapping paired ends; give a minor bonus
else if (dr > dq || sidi != sidj) sc -= c_lin < c_log? c_lin : c_log;
else sc -= c_lin + (c_log>>1);
} else sc -= (int)(dd * .01 * avg_qspan) + (log_dd>>1);
sc += f[j]; sc += f[j];
if (sc > max_f) { if (sc > max_f) {
max_f = sc, max_j = j; max_f = sc, max_j = j;
@@ -58,8 +75,8 @@ int mm_chain_dp(int max_dist, int bw, int max_skip, int min_cnt, int min_sc, int
} }
if (p[j] >= 0) t[p[j]] = i; if (p[j] >= 0) t[p[j]] = i;
} }
if (max_j >= 0) f[i] = max_f, p[i] = max_j; f[i] = max_f, p[i] = max_j;
else f[i] = q_span, p[i] = -1; v[i] = max_j >= 0 && v[max_j] > max_f? v[max_j] : max_f; // v[] keeps the peak score up to i; f[] is the score ending at i, not always the peak
} }
// find the ending positions of chains // find the ending positions of chains
@@ -67,16 +84,21 @@ int mm_chain_dp(int max_dist, int bw, int max_skip, int min_cnt, int min_sc, int
for (i = 0; i < n; ++i) for (i = 0; i < n; ++i)
if (p[i] >= 0) t[p[i]] = 1; if (p[i] >= 0) t[p[i]] = 1;
for (i = n_u = 0; i < n; ++i) for (i = n_u = 0; i < n; ++i)
if (t[i] == 0 && f[i] >= min_sc) if (t[i] == 0 && v[i] >= min_sc)
++n_u; ++n_u;
if (n_u == 0) { if (n_u == 0) {
kfree(km, f); kfree(km, p); kfree(km, t); kfree(km, a); kfree(km, f); kfree(km, p); kfree(km, t); kfree(km, v);
return 0; return 0;
} }
u = (uint64_t*)kmalloc(km, n_u * 8); u = (uint64_t*)kmalloc(km, n_u * 8);
for (i = n_u = 0; i < n; ++i) for (i = n_u = 0; i < n; ++i) {
if (t[i] == 0 && f[i] >= min_sc) if (t[i] == 0 && v[i] >= min_sc) {
u[n_u++] = (uint64_t)f[i] << 32 | i; j = i;
while (j >= 0 && f[j] < v[j]) j = p[j]; // find the peak that maximizes f[]
if (j < 0) j = i; // TODO: this should really be assert(j>=0)
u[n_u++] = (uint64_t)f[j] << 32 | j;
}
}
radix_sort_64(u, u + n_u); radix_sort_64(u, u + n_u);
for (i = 0; i < n_u>>1; ++i) { // reverse, s.t. the highest scoring chain is the first for (i = 0; i < n_u>>1; ++i) { // reverse, s.t. the highest scoring chain is the first
uint64_t t = u[i]; uint64_t t = u[i];
@@ -85,7 +107,6 @@ int mm_chain_dp(int max_dist, int bw, int max_skip, int min_cnt, int min_sc, int
// backtrack // backtrack
memset(t, 0, n * 4); memset(t, 0, n * 4);
v = (int32_t*)kmalloc(km, n * 4);
for (i = n_v = k = 0; i < n_u; ++i) { // starting from the highest score for (i = n_v = k = 0; i < n_u; ++i) { // starting from the highest score
int32_t n_v0 = n_v, k0 = k; int32_t n_v0 = n_v, k0 = k;
j = (int32_t)u[i]; j = (int32_t)u[i];
@@ -101,9 +122,9 @@ int mm_chain_dp(int max_dist, int bw, int max_skip, int min_cnt, int min_sc, int
} }
if (k0 == k) n_v = n_v0; // no new chain added, reset if (k0 == k) n_v = n_v0; // no new chain added, reset
} }
n_u = k, *_u = u; // NB: note that u[] may not be sorted by score here *n_u_ = n_u = k, *_u = u; // NB: note that u[] may not be sorted by score here
// free // free temporary arrays
kfree(km, f); kfree(km, p); kfree(km, t); kfree(km, f); kfree(km, p); kfree(km, t);
// write the result to b[] // write the result to b[]
@@ -130,6 +151,7 @@ int mm_chain_dp(int max_dist, int bw, int max_skip, int min_cnt, int min_sc, int
k += n; k += n;
} }
memcpy(u, u2, n_u * 8); memcpy(u, u2, n_u * 8);
kfree(km, b); kfree(km, w); kfree(km, u2); memcpy(b, a, k * sizeof(mm128_t)); // write _a_ to _b_ and deallocate _a_ because _a_ is oversized, sometimes a lot
return n_u; kfree(km, a); kfree(km, w); kfree(km, u2);
return b;
} }
+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;
}
}
+21 -21
View File
@@ -11,7 +11,13 @@ KSEQ_INIT(gzFile, gzread)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
mm_idxopt_t iopt;
mm_mapopt_t mopt;
int n_threads = 3;
mm_verbose = 2; // disable message output to stderr mm_verbose = 2; // disable message output to stderr
mm_set_opt(0, &iopt, &mopt);
mopt.flag |= MM_F_CIGAR; // perform alignment
if (argc < 3) { if (argc < 3) {
fprintf(stderr, "Usage: minimap2-lite <target.fa> <query.fa>\n"); fprintf(stderr, "Usage: minimap2-lite <target.fa> <query.fa>\n");
@@ -23,39 +29,33 @@ int main(int argc, char *argv[])
assert(f); assert(f);
kseq_t *ks = kseq_init(f); kseq_t *ks = kseq_init(f);
// create index for target; we are creating one index for all target sequence // open index reader
int n_threads = 4, w = 10, k = 15, is_hpc = 0; mm_idx_reader_t *r = mm_idx_reader_open(argv[1], &iopt, 0);
mm_idx_t *mi = mm_idx_build(argv[1], w, k, is_hpc, n_threads); mm_idx_t *mi;
assert(mi); while ((mi = mm_idx_reader_read(r, n_threads)) != 0) { // traverse each part of the index
mm_mapopt_update(&mopt, mi); // this sets the maximum minimizer occurrence; TODO: set a better default in mm_mapopt_init()!
// mapping
mm_mapopt_t opt;
mm_mapopt_init(&opt); // initialize mapping parameters
mm_mapopt_update(&opt, mi); // this sets the maximum minimizer occurrence; TODO: set a better default in mm_mapopt_init()!
opt.flag |= MM_F_CIGAR; // perform alignment
mm_tbuf_t *tbuf = mm_tbuf_init(); // thread buffer; for multi-threading, allocate one tbuf for each thread mm_tbuf_t *tbuf = mm_tbuf_init(); // thread buffer; for multi-threading, allocate one tbuf for each thread
while (kseq_read(ks) >= 0) { // each kseq_read() call reads one query sequence while (kseq_read(ks) >= 0) { // each kseq_read() call reads one query sequence
const mm_reg1_t *reg; mm_reg1_t *reg;
int j, i, n_reg; int j, i, n_reg;
// get all hits for the query reg = mm_map(mi, ks->seq.l, ks->seq.s, &n_reg, tbuf, &mopt, 0); // get all hits for the query
reg = mm_map(mi, ks->seq.l, ks->seq.s, &n_reg, tbuf, &opt, 0); for (j = 0; j < n_reg; ++j) { // traverse hits and print them out
// traverse hits and print them out mm_reg1_t *r = &reg[j];
for (j = 0; j < n_reg; ++j) {
const mm_reg1_t *r = &reg[j];
assert(r->p); // with MM_F_CIGAR, this should not be NULL assert(r->p); // with MM_F_CIGAR, this should not be NULL
printf("%s\t%d\t%d\t%d\t%c\t", ks->name.s, ks->seq.l, r->qs, r->qe, "+-"[r->rev]); printf("%s\t%d\t%d\t%d\t%c\t", ks->name.s, ks->seq.l, r->qs, r->qe, "+-"[r->rev]);
printf("%s\t%d\t%d\t%d\t%d\t%d\t%d\tcg:Z:", mi->seq[r->rid].name, mi->seq[r->rid].len, r->rs, r->re, printf("%s\t%d\t%d\t%d\t%d\t%d\t%d\tcg:Z:", mi->seq[r->rid].name, mi->seq[r->rid].len, r->rs, r->re, r->mlen, r->blen, r->mapq);
r->p->blen - r->p->n_ambi - r->p->n_diff, r->p->blen, r->mapq);
for (i = 0; i < r->p->n_cigar; ++i) // IMPORTANT: this gives the CIGAR in the aligned regions. NO soft/hard clippings! for (i = 0; i < r->p->n_cigar; ++i) // IMPORTANT: this gives the CIGAR in the aligned regions. NO soft/hard clippings!
printf("%d%c", r->p->cigar[i]>>4, "MIDSHN"[r->p->cigar[i]&0xf]); printf("%d%c", r->p->cigar[i]>>4, "MIDSHN"[r->p->cigar[i]&0xf]);
putchar('\n'); putchar('\n');
free(r->p);
} }
free(reg);
} }
mm_tbuf_destroy(tbuf); mm_tbuf_destroy(tbuf);
// deallocate index and close the query file
mm_idx_destroy(mi); mm_idx_destroy(mi);
kseq_destroy(ks); }
mm_idx_reader_close(r); // close the index reader
kseq_destroy(ks); // close the query file
gzclose(f); gzclose(f);
return 0; return 0;
} }
+388 -41
View File
@@ -1,9 +1,13 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include "kalloc.h"
#include "mmpriv.h" #include "mmpriv.h"
static char mm_rg_id[256];
static inline void str_enlarge(kstring_t *s, int l) static inline void str_enlarge(kstring_t *s, int l)
{ {
if (s->l + l + 1 > s->m) { if (s->l + l + 1 > s->m) {
@@ -39,6 +43,13 @@ static void mm_sprintf_lite(kstring_t *s, const char *fmt, ...)
if (c < 0) buf[l++] = '-'; if (c < 0) buf[l++] = '-';
str_enlarge(s, l); str_enlarge(s, l);
for (i = l - 1; i >= 0; --i) s->s[s->l++] = buf[i]; for (i = l - 1; i >= 0; --i) s->s[s->l++] = buf[i];
} else if (*p == 'u') {
int i, l = 0;
uint32_t x;
x = va_arg(ap, uint32_t);
do { buf[l++] = x%10 + '0'; x /= 10; } while (x > 0);
str_enlarge(s, l);
for (i = l - 1; i >= 0; --i) s->s[s->l++] = buf[i];
} else if (*p == 's') { } else if (*p == 's') {
char *r = va_arg(ap, char*); char *r = va_arg(ap, char*);
str_copy(s, r, r + strlen(r)); str_copy(s, r, r + strlen(r));
@@ -54,83 +65,372 @@ static void mm_sprintf_lite(kstring_t *s, const char *fmt, ...)
s->s[s->l] = 0; s->s[s->l] = 0;
} }
static inline void write_tags(kstring_t *s, const mm_reg1_t *r) static char *mm_escape(char *s)
{ {
mm_sprintf_lite(s, "\tcm:i:%d\ts1:i:%d", r->cnt, r->score); char *p, *q;
if (r->parent == r->id) mm_sprintf_lite(s, "\ts2:i:%d", r->subsc); for (p = q = s; *p; ++p) {
if (r->split) mm_sprintf_lite(s, "\tzd:i:%d", r->split); if (*p == '\\') {
if (r->p) mm_sprintf_lite(s, "\tNM:i:%d\tms:i:%d\tAS:i:%d\tnn:i:%d", r->p->n_diff, r->p->dp_max, r->p->dp_score, r->p->n_ambi); ++p;
if (*p == 't') *q++ = '\t';
else if (*p == '\\') *q++ = '\\';
} else *q++ = *p;
}
*q = '\0';
return s;
} }
void mm_write_paf(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r) static void sam_write_rg_line(kstring_t *str, const char *s)
{
char *p, *q, *r, *rg_line = 0;
memset(mm_rg_id, 0, 256);
if (s == 0) return;
if (strstr(s, "@RG") != s) {
if (mm_verbose >= 1) fprintf(stderr, "[ERROR] the read group line is not started with @RG\n");
goto err_set_rg;
}
if (strstr(s, "\t") != NULL) {
if (mm_verbose >= 1) fprintf(stderr, "[ERROR] the read group line contained literal <tab> characters -- replace with escaped tabs: \\t\n");
goto err_set_rg;
}
rg_line = strdup(s);
mm_escape(rg_line);
if ((p = strstr(rg_line, "\tID:")) == 0) {
if (mm_verbose >= 1) fprintf(stderr, "[ERROR] no ID within the read group line\n");
goto err_set_rg;
}
p += 4;
for (q = p; *q && *q != '\t' && *q != '\n'; ++q);
if (q - p + 1 > 256) {
if (mm_verbose >= 1) fprintf(stderr, "[ERROR] @RG:ID is longer than 255 characters\n");
goto err_set_rg;
}
for (q = p, r = mm_rg_id; *q && *q != '\t' && *q != '\n'; ++q)
*r++ = *q;
mm_sprintf_lite(str, "%s\n", rg_line);
err_set_rg:
free(rg_line);
}
void mm_write_sam_hdr(const mm_idx_t *idx, const char *rg, const char *ver, int argc, char *argv[])
{
kstring_t str = {0,0,0};
if (idx) {
uint32_t i;
for (i = 0; i < idx->n_seq; ++i)
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");
if (ver) mm_sprintf_lite(&str, "\tVN:%s", ver);
if (argc > 1) {
int i;
mm_sprintf_lite(&str, "\tCL:minimap2");
for (i = 1; i < argc; ++i)
mm_sprintf_lite(&str, " %s", argv[i]);
}
mm_err_puts(str.s);
free(str.s);
}
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)
{
int i, q_off, t_off;
mm_sprintf_lite(s, "\tcs: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 <= 3);
if (op == 0) { // match
int l_tmp = 0;
for (j = 0; j < len; ++j) {
if (qseq[q_off + j] != tseq[t_off + j]) {
if (l_tmp > 0) {
if (!no_iden) {
tmp[l_tmp] = 0;
mm_sprintf_lite(s, "=%s", tmp);
} else mm_sprintf_lite(s, ":%d", l_tmp);
l_tmp = 0;
}
mm_sprintf_lite(s, "*%c%c", "acgtn"[tseq[t_off + j]], "acgtn"[qseq[q_off + j]]);
} else tmp[l_tmp++] = "ACGTN"[qseq[q_off + j]];
}
if (l_tmp > 0) {
if (!no_iden) {
tmp[l_tmp] = 0;
mm_sprintf_lite(s, "=%s", tmp);
} else mm_sprintf_lite(s, ":%d", l_tmp);
}
q_off += len, t_off += len;
} 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) { // 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 { // 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]]);
t_off += len;
}
}
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;
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)
mm_sprintf_lite(s, "\tts:A:%c", "?+-?"[r->p->trans_strand]);
}
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; 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]); 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); if (mi->seq[r->rid].name) mm_sprintf_lite(s, "%s", mi->seq[r->rid].name);
else mm_sprintf_lite(s, "%d", r->rid); else mm_sprintf_lite(s, "%d", r->rid);
mm_sprintf_lite(s, "\t%d\t%d\t%d", mi->seq[r->rid].len, r->rs, r->re); mm_sprintf_lite(s, "\t%d\t%d\t%d", mi->seq[r->rid].len, r->rs, r->re);
if (r->p) mm_sprintf_lite(s, "\t%d\t%d", r->p->blen - r->p->n_ambi - r->p->n_diff, r->p->blen); mm_sprintf_lite(s, "\t%d\t%d", r->mlen, r->blen);
else mm_sprintf_lite(s, "\t%d\t%d", r->fuzzy_mlen, r->fuzzy_blen);
mm_sprintf_lite(s, "\t%d", r->mapq); mm_sprintf_lite(s, "\t%d", r->mapq);
write_tags(s, r); write_tags(s, r);
if (r->p) { if (r->p && (opt_flag & MM_F_OUT_CG)) {
uint32_t k; uint32_t k;
mm_sprintf_lite(s, "\tcg:Z:"); mm_sprintf_lite(s, "\tcg:Z:");
for (k = 0; k < r->p->n_cigar; ++k) for (k = 0; k < r->p->n_cigar; ++k)
mm_sprintf_lite(s, "%d%c", r->p->cigar[k]>>4, "MID"[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|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 char comp_tab[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 'T', 'V', 'G', 'H', 'E', 'F', 'C', 'D', 'I', 'J', 'M', 'L', 'K', 'N', 'O',
'P', 'Q', 'Y', 'S', 'A', 'A', 'B', 'W', 'X', 'R', 'Z', 91, 92, 93, 94, 95,
64, 't', 'v', 'g', 'h', 'e', 'f', 'c', 'd', 'i', 'j', 'm', 'l', 'k', 'n', 'o',
'p', 'q', 'y', 's', 'a', 'a', 'b', 'w', 'x', 'r', 'z', 123, 124, 125, 126, 127
};
static void sam_write_sq(kstring_t *s, char *seq, int l, int rev, int comp) static void sam_write_sq(kstring_t *s, char *seq, int l, int rev, int comp)
{ {
extern unsigned char seq_comp_table[256];
if (rev) { if (rev) {
int i; int i;
str_enlarge(s, l); str_enlarge(s, l);
for (i = 0; i < l; ++i) { for (i = 0; i < l; ++i) {
int c = seq[l - 1 - i]; int c = seq[l - 1 - i];
s->s[s->l + i] = c < 128 && comp? comp_tab[c] : c; s->s[s->l + i] = c < 128 && comp? seq_comp_table[c] : c;
} }
s->l += l; s->l += l;
} else str_copy(s, seq, seq + l); } else str_copy(s, seq, seq + l);
} }
void mm_write_sam(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r) static inline const mm_reg1_t *get_sam_pri(int n_regs, const mm_reg1_t *regs)
{ {
int flag = 0; int i;
for (i = 0; i < n_regs; ++i)
if (regs[i].sam_pri)
return &regs[i];
assert(n_regs == 0);
return NULL;
}
static void write_sam_cigar(kstring_t *s, int sam_flag, int in_tag, int qlen, const mm_reg1_t *r, int opt_flag)
{
if (r->p == 0) {
mm_sprintf_lite(s, "*");
} else {
uint32_t k, clip_len[2];
clip_len[0] = r->rev? qlen - r->qe : r->qs;
clip_len[1] = r->rev? r->qs : qlen - r->qe;
if (in_tag) {
int clip_char = (sam_flag&0x800) && !(opt_flag&MM_F_SOFTCLIP)? 5 : 4;
mm_sprintf_lite(s, "\tCG:B:I");
if (clip_len[0]) mm_sprintf_lite(s, ",%u", clip_len[0]<<4|clip_char);
for (k = 0; k < r->p->n_cigar; ++k)
mm_sprintf_lite(s, ",%u", r->p->cigar[k]);
if (clip_len[1]) mm_sprintf_lite(s, ",%u", clip_len[1]<<4|clip_char);
} else {
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, "MIDNSHP=XB"[r->p->cigar[k]&0xf]);
if (clip_len[1]) mm_sprintf_lite(s, "%d%c", clip_len[1], clip_char);
}
}
}
void mm_write_sam2(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, int seg_idx, int reg_idx, int n_seg, const int *n_regss, const mm_reg1_t *const* regss, void *km, int opt_flag)
{
const int max_bam_cigar_op = 65535;
int flag, n_regs = n_regss[seg_idx], cigar_in_tag = 0;
int this_rid = -1, this_pos = -1, this_rev = 0;
const mm_reg1_t *regs = regss[seg_idx], *r_prev = NULL, *r_next;
const mm_reg1_t *r = n_regs > 0 && reg_idx < n_regs && reg_idx >= 0? &regs[reg_idx] : NULL;
// find the primary of the previous and the next segments, if they are mapped
if (n_seg > 1) {
int i, next_sid = (seg_idx + 1) % n_seg;
r_next = get_sam_pri(n_regss[next_sid], regss[next_sid]);
if (n_seg > 2) {
for (i = 1; i <= n_seg - 1; ++i) {
int prev_sid = (seg_idx + n_seg - i) % n_seg;
if (n_regss[prev_sid] > 0) {
r_prev = get_sam_pri(n_regss[prev_sid], regss[prev_sid]);
break;
}
}
} else r_prev = r_next;
} else r_prev = r_next = NULL;
// write QNAME
s->l = 0; s->l = 0;
mm_sprintf_lite(s, "%s", t->name);
if (n_seg > 1) s->l = mm_qname_len(t->name); // trim the suffix like /1 or /2
// write flag
flag = n_seg > 1? 0x1 : 0x0;
if (r == 0) {
flag |= 0x4;
} else {
if (r->rev) flag |= 0x10;
if (r->parent != r->id) flag |= 0x100;
else if (!r->sam_pri) flag |= 0x800;
}
if (n_seg > 1) {
if (r && r->proper_frag) flag |= 0x2; // TODO: this doesn't work when there are more than 2 segments
if (seg_idx == 0) flag |= 0x40;
else if (seg_idx == n_seg - 1) flag |= 0x80;
if (r_next == NULL) flag |= 0x8;
else if (r_next->rev) flag |= 0x20;
}
mm_sprintf_lite(s, "\t%d", flag);
// write coordinate, MAPQ and CIGAR
if (r == 0) {
if (r_prev) {
this_rid = r_prev->rid, this_pos = r_prev->rs;
mm_sprintf_lite(s, "\t%s\t%d\t0\t*", mi->seq[this_rid].name, this_pos+1);
} else mm_sprintf_lite(s, "\t*\t0\t0\t*");
} else {
this_rid = r->rid, this_pos = r->rs, this_rev = r->rev;
mm_sprintf_lite(s, "\t%s\t%d\t%d\t", mi->seq[r->rid].name, r->rs+1, r->mapq);
if ((opt_flag & MM_F_LONG_CIGAR) && r->p && r->p->n_cigar > max_bam_cigar_op - 2) {
int n_cigar = r->p->n_cigar;
if (r->qs != 0) ++n_cigar;
if (r->qe != t->l_seq) ++n_cigar;
if (n_cigar > max_bam_cigar_op)
cigar_in_tag = 1;
}
if (cigar_in_tag) {
if (flag & 0x100) mm_sprintf_lite(s, "0S"); // secondary alignment
else if (flag & 0x800) mm_sprintf_lite(s, "%dS", r->re - r->rs); // supplementary alignment
else mm_sprintf_lite(s, "%dS", t->l_seq);
} else write_sam_cigar(s, flag, 0, t->l_seq, r, opt_flag);
}
// write mate positions
if (n_seg > 1) {
int tlen = 0;
if (this_rid >= 0 && r_next) {
if (this_rid == r_next->rid) {
int this_pos5 = r && r->rev? r->re - 1 : this_pos;
int next_pos5 = r_next->rev? r_next->re - 1 : r_next->rs;
tlen = next_pos5 - this_pos5;
mm_sprintf_lite(s, "\t=\t");
} else mm_sprintf_lite(s, "\t%s\t", mi->seq[r_next->rid].name);
mm_sprintf_lite(s, "%d\t", r_next->rs + 1);
} else if (r_next) { // && this_rid < 0
mm_sprintf_lite(s, "\t%s\t%d\t", mi->seq[r_next->rid].name, r_next->rs + 1);
} else if (this_rid >= 0) { // && r_next == NULL
int this_pos5 = this_rev? r->re - 1 : this_pos; // this_rev is only true when r != NULL
tlen = this_pos - this_pos5; // next_pos5 will be this_pos
mm_sprintf_lite(s, "\t=\t%d\t", this_pos + 1); // next segment will take r's coordinate
} else mm_sprintf_lite(s, "\t*\t0\t"); // neither has coordinates
if (tlen > 0) ++tlen;
else if (tlen < 0) --tlen;
mm_sprintf_lite(s, "%d\t", tlen);
} else mm_sprintf_lite(s, "\t*\t0\t0\t");
// write SEQ and QUAL
if (r == 0) { if (r == 0) {
mm_sprintf_lite(s, "%s\t4\t*\t0\t0\t*\t*\t0\t0\t", t->name);
sam_write_sq(s, t->seq, t->l_seq, 0, 0); sam_write_sq(s, t->seq, t->l_seq, 0, 0);
mm_sprintf_lite(s, "\t"); mm_sprintf_lite(s, "\t");
if (t->qual) sam_write_sq(s, t->qual, t->l_seq, 0, 0); if (t->qual) sam_write_sq(s, t->qual, t->l_seq, 0, 0);
else mm_sprintf_lite(s, "*"); else mm_sprintf_lite(s, "*");
} else { } else {
if (r->rev) flag |= 0x10; if ((flag & 0x900) == 0 || (opt_flag & MM_F_SOFTCLIP)) {
if (r->parent != r->id) flag |= 0x100;
else if (!r->sam_pri) flag |= 0x800;
mm_sprintf_lite(s, "%s\t%d\t%s\t%d\t%d\t", t->name, flag, mi->seq[r->rid].name, r->rs+1, r->mapq);
if (r->p) { // TODO: using hard clippings
uint32_t k, clip_len = r->rev? t->l_seq - r->qe : r->qs;
int clip_char = (flag&0x800)? 'H' : 'S';
if (clip_len) mm_sprintf_lite(s, "%d%c", clip_len, clip_char);
for (k = 0; k < r->p->n_cigar; ++k)
mm_sprintf_lite(s, "%d%c", r->p->cigar[k]>>4, "MID"[r->p->cigar[k]&0xf]);
clip_len = r->rev? r->qs : t->l_seq - r->qe;
if (clip_len) mm_sprintf_lite(s, "%d%c", clip_len, clip_char);
} else mm_sprintf_lite(s, "*");
mm_sprintf_lite(s, "\t*\t0\t0\t");
if ((flag & 0x900) == 0) {
sam_write_sq(s, t->seq, t->l_seq, r->rev, r->rev); sam_write_sq(s, t->seq, t->l_seq, r->rev, r->rev);
mm_sprintf_lite(s, "\t"); mm_sprintf_lite(s, "\t");
if (t->qual) sam_write_sq(s, t->qual, t->l_seq, r->rev, 0); if (t->qual) sam_write_sq(s, t->qual, t->l_seq, r->rev, 0);
@@ -143,7 +443,54 @@ void mm_write_sam(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const m
if (t->qual) sam_write_sq(s, t->qual + r->qs, r->qe - r->qs, r->rev, 0); if (t->qual) sam_write_sq(s, t->qual + r->qs, r->qe - r->qs, r->rev, 0);
else mm_sprintf_lite(s, "*"); else mm_sprintf_lite(s, "*");
} }
write_tags(s, r);
} }
// write tags
if (mm_rg_id[0]) mm_sprintf_lite(s, "\tRG:Z:%s", mm_rg_id);
if (n_seg > 2) mm_sprintf_lite(s, "\tFI:i:%d", seg_idx);
if (r) {
write_tags(s, r);
if (r->parent == r->id && r->p && n_regs > 1 && regs && r >= regs && r - regs < n_regs) { // supplementary aln may exist
int i, n_sa = 0; // n_sa: number of SA fields
for (i = 0; i < n_regs; ++i)
if (i != r - regs && regs[i].parent == regs[i].id && regs[i].p)
++n_sa;
if (n_sa > 0) {
mm_sprintf_lite(s, "\tSA:Z:");
for (i = 0; i < n_regs; ++i) {
const mm_reg1_t *q = &regs[i];
int l_M, l_I = 0, l_D = 0, clip5 = 0, clip3 = 0;
if (r == q || q->parent != q->id || q->p == 0) continue;
if (q->qe - q->qs < q->re - q->rs) l_M = q->qe - q->qs, l_D = (q->re - q->rs) - l_M;
else l_M = q->re - q->rs, l_I = (q->qe - q->qs) - l_M;
clip5 = q->rev? t->l_seq - q->qe : q->qs;
clip3 = q->rev? q->qs : t->l_seq - q->qe;
mm_sprintf_lite(s, "%s,%d,%c,", mi->seq[q->rid].name, q->rs+1, "+-"[q->rev]);
if (clip5) mm_sprintf_lite(s, "%dS", clip5);
if (l_M) mm_sprintf_lite(s, "%dM", l_M);
if (l_I) mm_sprintf_lite(s, "%dI", l_I);
if (l_D) mm_sprintf_lite(s, "%dD", l_D);
if (clip3) mm_sprintf_lite(s, "%dS", clip3);
mm_sprintf_lite(s, ",%d,%d;", q->mapq, q->blen - q->mlen + q->p->n_ambi);
}
}
}
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) s->s[s->l] = 0; // we always have room for an extra byte (see str_enlarge)
} }
void mm_write_sam(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r, int n_regs, const mm_reg1_t *regs)
{
int i;
for (i = 0; i < n_regs; ++i)
if (r == &regs[i]) break;
mm_write_sam2(s, mi, t, 0, i, 1, &n_regs, &regs, NULL, 0);
}
+216
View File
@@ -0,0 +1,216 @@
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "getopt.h"
char *optarg;
int optind=1, opterr=1, optopt, __optpos, optreset=0;
#define optpos __optpos
static void __getopt_msg(const char *a, const char *b, const char *c, size_t l)
{
FILE *f = stderr;
#if !defined(WIN32) && !defined(_WIN32)
flockfile(f);
#endif
fputs(a, f);
fwrite(b, strlen(b), 1, f);
fwrite(c, 1, l, f);
fputc('\n', f);
#if !defined(WIN32) && !defined(_WIN32)
funlockfile(f);
#endif
}
int getopt(int argc, char * const argv[], const char *optstring)
{
int i, c, d;
int k, l;
char *optchar;
if (!optind || optreset) {
optreset = 0;
__optpos = 0;
optind = 1;
}
if (optind >= argc || !argv[optind])
return -1;
if (argv[optind][0] != '-') {
if (optstring[0] == '-') {
optarg = argv[optind++];
return 1;
}
return -1;
}
if (!argv[optind][1])
return -1;
if (argv[optind][1] == '-' && !argv[optind][2])
return optind++, -1;
if (!optpos) optpos++;
c = argv[optind][optpos], k = 1;
optchar = argv[optind]+optpos;
optopt = c;
optpos += k;
if (!argv[optind][optpos]) {
optind++;
optpos = 0;
}
if (optstring[0] == '-' || optstring[0] == '+')
optstring++;
i = 0;
d = 0;
do {
d = optstring[i], l = 1;
if (l>0) i+=l; else i++;
} while (l && d != c);
if (d != c) {
if (optstring[0] != ':' && opterr)
__getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
return '?';
}
if (optstring[i] == ':') {
if (optstring[i+1] == ':') optarg = 0;
else if (optind >= argc) {
if (optstring[0] == ':') return ':';
if (opterr) __getopt_msg(argv[0],
": option requires an argument: ",
optchar, k);
return '?';
}
if (optstring[i+1] != ':' || optpos) {
optarg = argv[optind++] + optpos;
optpos = 0;
}
}
return c;
}
static void permute(char *const *argv, int dest, int src)
{
char **av = (char **)argv;
char *tmp = av[src];
int i;
for (i=src; i>dest; i--)
av[i] = av[i-1];
av[dest] = tmp;
}
static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
{
optarg = 0;
if (longopts && argv[optind][0] == '-' &&
((longonly && argv[optind][1] && argv[optind][1] != '-') ||
(argv[optind][1] == '-' && argv[optind][2])))
{
int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':';
int i, cnt, match = -1;
char *opt;
for (cnt=i=0; longopts[i].name; i++) {
const char *name = longopts[i].name;
opt = argv[optind]+1;
if (*opt == '-') opt++;
for (; *name && *name == *opt; name++, opt++);
if (*opt && *opt != '=') continue;
match = i;
if (!*name) {
cnt = 1;
break;
}
cnt++;
}
if (cnt==1) {
i = match;
optind++;
optopt = longopts[i].val;
if (*opt == '=') {
if (!longopts[i].has_arg) {
if (colon || !opterr)
return '?';
__getopt_msg(argv[0],
": option does not take an argument: ",
longopts[i].name,
strlen(longopts[i].name));
return '?';
}
optarg = opt+1;
} else if (longopts[i].has_arg == required_argument) {
if (!(optarg = argv[optind])) {
if (colon) return ':';
if (!opterr) return '?';
__getopt_msg(argv[0],
": option requires an argument: ",
longopts[i].name,
strlen(longopts[i].name));
return '?';
}
optind++;
}
if (idx) *idx = i;
if (longopts[i].flag) {
*longopts[i].flag = longopts[i].val;
return 0;
}
return longopts[i].val;
}
if (argv[optind][1] == '-') {
if (!colon && opterr)
__getopt_msg(argv[0], cnt ?
": option is ambiguous: " :
": unrecognized option: ",
argv[optind]+2,
strlen(argv[optind]+2));
optind++;
return '?';
}
}
return getopt(argc, argv, optstring);
}
static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
{
int ret, skipped, resumed;
if (!optind || optreset) {
optreset = 0;
__optpos = 0;
optind = 1;
}
if (optind >= argc || !argv[optind]) return -1;
skipped = optind;
if (optstring[0] != '+' && optstring[0] != '-') {
int i;
for (i=optind; ; i++) {
if (i >= argc || !argv[i]) return -1;
if (argv[i][0] == '-' && argv[i][1]) break;
}
optind = i;
}
resumed = optind;
ret = __getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
if (resumed > skipped) {
int i, cnt = optind-resumed;
for (i=0; i<cnt; i++)
permute(argv, skipped, optind-1);
optind = skipped + cnt;
}
return ret;
}
int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx)
{
return __getopt_long(argc, argv, optstring, longopts, idx, 0);
}
int getopt_long_only(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx)
{
return __getopt_long(argc, argv, optstring, longopts, idx, 1);
}
+53
View File
@@ -0,0 +1,53 @@
/*
Copyright 2005-2014 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _GETOPT_H
#define _GETOPT_H
#ifdef __cplusplus
extern "C" {
#endif
int getopt(int, char * const [], const char *);
extern char *optarg;
extern int optind, opterr, optopt, optreset;
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
int getopt_long(int, char *const *, const char *, const struct option *, int *);
int getopt_long_only(int, char *const *, const char *, const struct option *, int *);
#define no_argument 0
#define required_argument 1
#define optional_argument 2
#ifdef __cplusplus
}
#endif
#endif
+229 -46
View File
@@ -1,20 +1,22 @@
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <math.h> #include <math.h>
#include "mmpriv.h" #include "mmpriv.h"
#include "kalloc.h" #include "kalloc.h"
#include "khash.h"
static inline void mm_cal_fuzzy_len(mm_reg1_t *r, const mm128_t *a) static inline void mm_cal_fuzzy_len(mm_reg1_t *r, const mm128_t *a)
{ {
int i; int i;
r->fuzzy_mlen = r->fuzzy_blen = 0; r->mlen = r->blen = 0;
if (r->cnt <= 0) return; if (r->cnt <= 0) return;
r->fuzzy_mlen = r->fuzzy_blen = a[r->as].y>>32&0xff; r->mlen = r->blen = a[r->as].y>>32&0xff;
for (i = r->as + 1; i < r->as + r->cnt; ++i) { for (i = r->as + 1; i < r->as + r->cnt; ++i) {
int span = a[i].y>>32&0xff; int span = a[i].y>>32&0xff;
int tl = (int32_t)a[i].x - (int32_t)a[i-1].x; int tl = (int32_t)a[i].x - (int32_t)a[i-1].x;
int ql = (int32_t)a[i].y - (int32_t)a[i-1].y; int ql = (int32_t)a[i].y - (int32_t)a[i-1].y;
r->fuzzy_blen += tl > ql? tl : ql; r->blen += tl > ql? tl : ql;
r->fuzzy_mlen += tl > span && ql > span? span : tl < ql? tl : ql; r->mlen += tl > span && ql > span? span : tl < ql? tl : ql;
} }
} }
@@ -35,7 +37,19 @@ static inline void mm_reg_set_coor(mm_reg1_t *r, int32_t qlen, const mm128_t *a)
mm_cal_fuzzy_len(r, a); mm_cal_fuzzy_len(r, a);
} }
mm_reg1_t *mm_gen_regs(void *km, int qlen, int n_u, uint64_t *u, mm128_t *a) // convert chains to hits static inline uint64_t hash64(uint64_t key)
{
key = (~key + (key << 21));
key = key ^ key >> 24;
key = ((key + (key << 3)) + (key << 8));
key = key ^ key >> 14;
key = ((key + (key << 2)) + (key << 4));
key = key ^ key >> 28;
key = (key + (key << 31));
return key;
}
mm_reg1_t *mm_gen_regs(void *km, uint32_t hash, int qlen, int n_u, uint64_t *u, mm128_t *a) // convert chains to hits
{ {
mm128_t *z, tmp; mm128_t *z, tmp;
mm_reg1_t *r; mm_reg1_t *r;
@@ -46,7 +60,9 @@ mm_reg1_t *mm_gen_regs(void *km, int qlen, int n_u, uint64_t *u, mm128_t *a) //
// sort by score // sort by score
z = (mm128_t*)kmalloc(km, n_u * 16); z = (mm128_t*)kmalloc(km, n_u * 16);
for (i = k = 0; i < n_u; ++i) { for (i = k = 0; i < n_u; ++i) {
z[i].x = u[i] >> 32; uint32_t h;
h = (uint32_t)hash64((hash64(a[k].x) + hash64(a[k].y)) ^ hash);
z[i].x = u[i] ^ h; // u[i] -- higher 32 bits: chain score; lower 32 bits: number of seeds in the chain
z[i].y = (uint64_t)k << 32 | (int32_t)u[i]; z[i].y = (uint64_t)k << 32 | (int32_t)u[i];
k += (int32_t)u[i]; k += (int32_t)u[i];
} }
@@ -60,9 +76,11 @@ mm_reg1_t *mm_gen_regs(void *km, int qlen, int n_u, uint64_t *u, mm128_t *a) //
mm_reg1_t *ri = &r[i]; mm_reg1_t *ri = &r[i];
ri->id = i; ri->id = i;
ri->parent = MM_PARENT_UNSET; ri->parent = MM_PARENT_UNSET;
ri->score = z[i].x; ri->score = ri->score0 = z[i].x >> 32;
ri->hash = (uint32_t)z[i].x;
ri->cnt = (int32_t)z[i].y; ri->cnt = (int32_t)z[i].y;
ri->as = z[i].y >> 32; ri->as = z[i].y >> 32;
ri->div = -1.0f;
mm_reg_set_coor(ri, qlen, a); mm_reg_set_coor(ri, qlen, a);
} }
kfree(km, z); kfree(km, z);
@@ -76,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->id = -1;
r2->sam_pri = 0; r2->sam_pri = 0;
r2->p = 0; r2->p = 0;
r2->split_inv = 0;
r2->cnt = r->cnt - n; r2->cnt = r->cnt - n;
r2->score = (int32_t)(r->score * ((float)r2->cnt / r->cnt) + .499); r2->score = (int32_t)(r->score * ((float)r2->cnt / r->cnt) + .499);
r2->as = r->as + n; r2->as = r->as + n;
@@ -87,55 +106,92 @@ void mm_split_reg(mm_reg1_t *r, mm_reg1_t *r2, int n, int qlen, mm128_t *a)
r->split |= 1, r2->split |= 2; r->split |= 1, r2->split |= 2;
} }
void mm_set_parent(void *km, float mask_level, int n, mm_reg1_t *r) // and compute mm_reg1_t::subsc void mm_set_parent(void *km, float mask_level, int n, mm_reg1_t *r, int sub_diff) // and compute mm_reg1_t::subsc
{ {
int i, j, k, *w; int i, j, k, *w;
uint64_t *cov;
if (n <= 0) return; if (n <= 0) return;
for (i = 0; i < n; ++i) r[i].id = i; for (i = 0; i < n; ++i) r[i].id = i;
cov = (uint64_t*)kmalloc(km, n * sizeof(uint64_t));
w = (int*)kmalloc(km, n * sizeof(int)); w = (int*)kmalloc(km, n * sizeof(int));
w[0] = 0, r[0].parent = 0; w[0] = 0, r[0].parent = 0;
for (i = 1, k = 1; i < n; ++i) { for (i = 1, k = 1; i < n; ++i) {
mm_reg1_t *ri = &r[i]; mm_reg1_t *ri = &r[i];
int si = ri->qs, ei = ri->qe; int si = ri->qs, ei = ri->qe, n_cov = 0, uncov_len = 0;
for (j = 0; j < k; ++j) { for (j = 0; j < k; ++j) { // traverse existing primary hits to find overlapping hits
mm_reg1_t *rp = &r[w[j]]; mm_reg1_t *rp = &r[w[j]];
int sj = rp->qs, ej = rp->qe; int sj = rp->qs, ej = rp->qe;
int min = ej - sj < ei - si? ej - sj : ei - si; if (ej <= si || sj >= ei) continue;
int ol = si < sj? (ei < sj? 0 : ei < ej? ei - sj : ej - sj) : (ej < si? 0 : ej < ei? ej - si : ei - si); if (sj < si) sj = si;
if (ol > mask_level * min) { if (ej > ei) ej = ei;
cov[n_cov++] = (uint64_t)sj<<32 | ej;
}
if (n_cov == 0) {
goto set_parent_test; // no overlapping primary hits; then i is a new primary hit
} else if (n_cov > 0) { // there are overlapping primary hits; find the length not covered by existing primary hits
int j, x = si;
radix_sort_64(cov, cov + n_cov);
for (j = 0; j < n_cov; ++j) {
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;
}
for (j = 0; j < k; ++j) { // traverse existing primary hits again
mm_reg1_t *rp = &r[w[j]];
int sj = rp->qs, ej = rp->qe, min, max, ol;
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; TODO: this can be simplified
if ((float)ol / min - (float)uncov_len / max > mask_level) {
int cnt_sub = 0;
ri->parent = rp->parent; ri->parent = rp->parent;
rp->subsc = rp->subsc > ri->score? rp->subsc : ri->score; rp->subsc = rp->subsc > ri->score? rp->subsc : ri->score;
if (rp->p && ri->p) if (ri->cnt >= rp->cnt) cnt_sub = 1;
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; 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;
}
if (cnt_sub) ++rp->n_sub;
break; break;
} }
} }
if (j == k) w[k++] = i, ri->parent = i; set_parent_test:
if (j == k) w[k++] = i, ri->parent = i, ri->n_sub = 0;
} }
kfree(km, cov);
kfree(km, w); 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;
uint64_t *aux; mm128_t *aux;
mm_reg1_t *t; mm_reg1_t *t;
if (n <= 1) return; if (n <= 1) return;
aux = (uint64_t*)kmalloc(km, n * 8); aux = (mm128_t*)kmalloc(km, n * 16);
t = (mm_reg1_t*)kmalloc(km, n * sizeof(mm_reg1_t)); t = (mm_reg1_t*)kmalloc(km, n * sizeof(mm_reg1_t));
for (i = n_aux = 0; i < n; ++i) { for (i = n_aux = 0; i < n; ++i) {
if (r[i].cnt > 0) { // squeeze out elements with cnt==0 (soft deleted) if (r[i].inv || r[i].cnt > 0) { // squeeze out elements with cnt==0 (soft deleted)
assert(r[i].p); if (r[i].p) {
aux[n_aux++] = (uint64_t)r[i].p->dp_max << 32 | i; 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) { } else if (r[i].p) {
kfree(km, r[i].p); free(r[i].p);
r[i].p = 0; r[i].p = 0;
} }
} }
radix_sort_64(aux, aux + n_aux); assert(has_cigar + no_cigar == 1);
radix_sort_128x(aux, aux + n_aux);
for (i = n_aux - 1; i >= 0; --i) for (i = n_aux - 1; i >= 0; --i)
t[n_aux - 1 - i] = r[(int32_t)aux[i]]; t[n_aux - 1 - i] = r[aux[i].y];
memcpy(r, t, sizeof(mm_reg1_t) * n_aux); memcpy(r, t, sizeof(mm_reg1_t) * n_aux);
*n_regs = n_aux; *n_regs = n_aux;
kfree(km, aux); kfree(km, aux);
@@ -177,29 +233,36 @@ void mm_sync_regs(void *km, int n_regs, mm_reg1_t *regs) // keep mm_reg1_t::{id,
mm_set_sam_pri(n_regs, regs); mm_set_sam_pri(n_regs, regs);
} }
void mm_select_sub(void *km, float mask_level, float pri_ratio, int best_n, int *n_, mm_reg1_t *r) void mm_select_sub(void *km, float pri_ratio, int min_diff, int best_n, int *n_, mm_reg1_t *r)
{ {
if (pri_ratio > 0.0f && *n_ > 0) { if (pri_ratio > 0.0f && *n_ > 0) {
int i, k, n = *n_, n_2nd = 0; int i, k, n = *n_, n_2nd = 0;
for (i = k = 0; i < n; ++i) for (i = k = 0; i < n; ++i) {
if (r[i].parent == i) r[k++] = r[i]; int p = r[i].parent;
else if (r[i].score >= r[r[i].parent].score * pri_ratio && n_2nd++ < best_n) r[k++] = r[i]; 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].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);
} else if (r[i].p) free(r[i].p);
}
if (k != n) mm_sync_regs(km, k, r); // removing hits requires sync() if (k != n) mm_sync_regs(km, k, r); // removing hits requires sync()
*n_ = k; *n_ = k;
} }
} }
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 { // NB: after this call, mm_reg1_t::parent can be -1 if its parent filtered out
int i, k; int i, k;
for (i = k = 0; i < *n_regs; ++i) { for (i = k = 0; i < *n_regs; ++i) {
mm_reg1_t *r = &regs[i]; mm_reg1_t *r = &regs[i];
int flt = 0; int flt = 0;
if (r->cnt < opt->min_cnt) flt = 1; 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->p->blen - r->p->n_ambi - r->p->n_diff < opt->min_chain_score) flt = 1; if (r->mlen < opt->min_chain_score) flt = 1;
else if (r->p->dp_max < opt->min_dp_max) 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) free(r->p);
} }
if (!flt) { if (!flt) {
@@ -247,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) { for (i = n_aux - 1; i >= 1; --i) {
mm_reg1_t *r0 = &regs[(int32_t)aux[i-1]], *r1 = &regs[(int32_t)aux[i]]; mm_reg1_t *r0 = &regs[(int32_t)aux[i-1]], *r1 = &regs[(int32_t)aux[i]];
mm128_t *a0e, *a1s; mm128_t *a0e, *a1s;
int max_gap, min_gap, sc_thres; int max_gap, min_gap, sc_thres, min_flank_len;
// test // test
if (r0->as + r0->cnt != r1->as) continue; // not adjacent in a[] if (r0->as + r0->cnt != r1->as) continue; // not adjacent in a[]
@@ -256,16 +319,17 @@ void mm_join_long(void *km, const mm_mapopt_t *opt, int qlen, int *n_regs_, mm_r
a1s = &a[r1->as]; a1s = &a[r1->as];
if (a1s->x <= a0e->x || (int32_t)a1s->y <= (int32_t)a0e->y) continue; // keep colinearity 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 = min_gap = (int32_t)a1s->y - (int32_t)a0e->y;
max_gap = max_gap > a1s->x - a0e->x? max_gap : a1s->x - a0e->x; max_gap = a0e->x + max_gap > a1s->x? max_gap : a1s->x - a0e->x;
min_gap = min_gap < a1s->x - a0e->x? min_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; 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); 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->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 min_flank_len = (int)(max_gap * opt->min_join_flank_ratio);
if (r1->re - r1->rs < max_gap>>1 || r1->qe - r1->qs < max_gap>>1) continue; 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 // all conditions satisfied; join
a[r1->as].y |= 1ULL<<40; a[r1->as].y |= MM_SEED_LONG_JOIN;
r0->cnt += r1->cnt, r0->score += r1->score; r0->cnt += r1->cnt, r0->score += r1->score;
mm_reg_set_coor(r0, qlen, a); mm_reg_set_coor(r0, qlen, a);
r1->cnt = 0; r1->cnt = 0;
@@ -282,24 +346,143 @@ void mm_join_long(void *km, const mm_mapopt_t *opt, int qlen, int *n_regs_, mm_r
r->parent = regs[r->parent].parent; 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); mm_sync_regs(km, *n_regs_, regs);
} }
} }
void mm_set_mapq(int n_regs, mm_reg1_t *regs) 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)
{
int s, i, j, acc_qlen[MM_MAX_SEG+1], qlen_sum = 0;
mm_seg_t *seg;
assert(n_segs <= MM_MAX_SEG);
for (s = 1, acc_qlen[0] = 0; s < n_segs; ++s)
acc_qlen[s] = acc_qlen[s-1] + qlens[s-1];
qlen_sum = acc_qlen[n_segs - 1] + qlens[n_segs - 1];
seg = (mm_seg_t*)kcalloc(km, n_segs, sizeof(mm_seg_t));
for (s = 0; s < n_segs; ++s) {
seg[s].u = (uint64_t*)kmalloc(km, n_regs0 * 8);
for (i = 0; i < n_regs0; ++i)
seg[s].u[i] = (uint64_t)regs0[i].score << 32;
}
for (i = 0; i < n_regs0; ++i) {
const mm_reg1_t *r = &regs0[i];
for (j = 0; j < r->cnt; ++j) {
int sid = (a[r->as + j].y&MM_SEED_SEG_MASK)>>MM_SEED_SEG_SHIFT;
++seg[sid].u[i];
++seg[sid].n_a;
}
}
for (s = 0; s < n_segs; ++s) {
mm_seg_t *sr = &seg[s];
for (i = 0, sr->n_u = 0; i < n_regs0; ++i) // squeeze out zero-length per-segment chains
if ((int32_t)sr->u[i] != 0)
sr->u[sr->n_u++] = sr->u[i];
sr->a = (mm128_t*)kmalloc(km, sr->n_a * sizeof(mm128_t));
sr->n_a = 0;
}
for (i = 0; i < n_regs0; ++i) {
const mm_reg1_t *r = &regs0[i];
for (j = 0; j < r->cnt; ++j) {
int sid = (a[r->as + j].y&MM_SEED_SEG_MASK)>>MM_SEED_SEG_SHIFT;
mm128_t a1 = a[r->as + j];
// on reverse strand, the segment position is:
// x_for_cat = qlen_sum - 1 - (int32_t)a1.y - 1 + q_span
// (int32_t)new_a1.y = qlens[sid] - (x_for_cat - acc_qlen[sid] + 1 - q_span) - 1 = (int32_t)a1.y - (qlen_sum - (qlens[sid] + acc_qlen[sid]))
a1.y -= a1.x>>63? qlen_sum - (qlens[sid] + acc_qlen[sid]) : acc_qlen[sid];
seg[sid].a[seg[sid].n_a++] = a1;
}
}
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) {
regs[s][i].seg_split = 1;
regs[s][i].seg_id = s;
}
}
return seg;
}
void mm_seg_free(void *km, int n_segs, mm_seg_t *segs)
{ {
int i; int i;
for (i = 0; i < n_segs; ++i) kfree(km, segs[i].u);
for (i = 0; i < n_segs; ++i) kfree(km, segs[i].a);
kfree(km, segs);
}
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;
float uniq_ratio;
int i;
for (i = 0; i < n_regs; ++i)
if (regs[i].parent == regs[i].id)
sum_sc += regs[i].score;
uniq_ratio = (float)sum_sc / (sum_sc + rep_len);
for (i = 0; i < n_regs; ++i) { for (i = 0; i < n_regs; ++i) {
mm_reg1_t *r = &regs[i]; mm_reg1_t *r = &regs[i];
if (r->parent == r->id) { if (r->inv) {
int mapq; r->mapq = 0;
} else if (r->parent == r->id) {
int mapq, subsc;
float pen_s1 = (r->score > 100? 1.0f : 0.01f * r->score) * uniq_ratio;
float pen_cm = r->cnt > 10? 1.0f : 0.1f * r->cnt;
pen_cm = pen_s1 < pen_cm? pen_s1 : pen_cm;
subsc = r->subsc > min_chain_sc? r->subsc : min_chain_sc;
if (r->p && r->p->dp_max2 > 0 && r->p->dp_max > 0) { if (r->p && r->p->dp_max2 > 0 && r->p->dp_max > 0) {
float identity = (float)(r->p->blen - r->p->n_diff - r->p->n_ambi) / (r->p->blen - r->p->n_ambi); float identity = (float)r->mlen / r->blen;
mapq = (int)(identity * 30.0 * (1. - (float)r->p->dp_max2 * r->subsc / r->p->dp_max / r->score) * logf(r->score)); float x = (float)r->p->dp_max2 * subsc / r->p->dp_max / r->score0;
} else mapq = (int)(30.0 * (1. - (float)r->subsc / r->score) * logf(r->score)); mapq = (int)(identity * pen_cm * q_coef * (1.0f - x * x) * logf((float)r->p->dp_max / match_sc));
if (!is_sr) {
int mapq_alt = (int)(6.02f * identity * identity * (r->p->dp_max - r->p->dp_max2) / match_sc + .499f); // BWA-MEM like mapQ, mostly for short reads
mapq = mapq < mapq_alt? mapq : mapq_alt; // in case the long-read heuristic fails
}
} else {
float x = (float)subsc / r->score0;
if (r->p) {
float identity = (float)r->mlen / r->blen;
mapq = (int)(identity * pen_cm * q_coef * (1.0f - x) * logf((float)r->p->dp_max / match_sc));
} else {
mapq = (int)(pen_cm * q_coef * (1.0f - x) * logf(r->score));
}
}
mapq -= (int)(4.343f * logf(r->n_sub + 1) + .499f);
mapq = mapq > 0? mapq : 0; mapq = mapq > 0? mapq : 0;
r->mapq = mapq < 60? mapq : 60; r->mapq = mapq < 60? mapq : 60;
if (r->p && r->p->dp_max > r->p->dp_max2 && r->mapq == 0) r->mapq = 1;
} else r->mapq = 0; } else r->mapq = 0;
} }
mm_set_inv_mapq(km, n_regs, regs);
} }
+197 -43
View File
@@ -1,8 +1,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#if defined(WIN32) || defined(_WIN32)
#include <io.h> // for open(2)
#else
#include <unistd.h> #include <unistd.h>
#endif
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#define __STDC_LIMIT_MACROS
#include "kthread.h" #include "kthread.h"
#include "bseq.h" #include "bseq.h"
#include "minimap.h" #include "minimap.h"
@@ -15,31 +20,47 @@
KHASH_INIT(idx, uint64_t, uint64_t, 1, idx_hash, idx_eq) KHASH_INIT(idx, uint64_t, uint64_t, 1, idx_hash, idx_eq)
typedef khash_t(idx) idxhash_t; 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)) #define kroundup64(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, (x)|=(x)>>32, ++(x))
mm_idx_t *mm_idx_init(int w, int k, int b, int is_hpc) typedef struct mm_idx_bucket_s {
mm128_v a; // (minimizer, position) array
int32_t n; // size of the _p_ array
uint64_t *p; // position array for minimizers appearing >1 times
void *h; // hash table indexing _p_ and minimizers appearing once
} mm_idx_bucket_t;
mm_idx_t *mm_idx_init(int w, int k, int b, int flag)
{ {
mm_idx_t *mi; mm_idx_t *mi;
if (k*2 < b) b = k * 2; if (k*2 < b) b = k * 2;
if (w < 1) w = 1; if (w < 1) w = 1;
mi = (mm_idx_t*)calloc(1, sizeof(mm_idx_t)); 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)); 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; return mi;
} }
void mm_idx_destroy(mm_idx_t *mi) void mm_idx_destroy(mm_idx_t *mi)
{ {
int i; uint32_t i;
if (mi == 0) return; if (mi == 0) return;
for (i = 0; i < 1<<mi->b; ++i) { 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].p);
free(mi->B[i].a.a); free(mi->B[i].a.a);
kh_destroy(idx, (idxhash_t*)mi->B[i].h); kh_destroy(idx, (idxhash_t*)mi->B[i].h);
} }
}
if (!mi->km) {
for (i = 0; i < mi->n_seq; ++i) for (i = 0; i < mi->n_seq; ++i)
free(mi->seq[i].name); free(mi->seq[i].name);
free(mi->seq); free(mi->B); free(mi->S); free(mi); free(mi->seq);
} else km_destroy(mi->km);
free(mi->B); free(mi->S); free(mi);
} }
const uint64_t *mm_idx_get(const mm_idx_t *mi, uint64_t minier, int *n) const uint64_t *mm_idx_get(const mm_idx_t *mi, uint64_t minier, int *n)
@@ -63,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) 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; 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) for (i = 0; i < mi->n_seq; ++i)
len += mi->seq[i].len; 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); 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; idxhash_t *h = (idxhash_t*)mi->B[i].h;
khint_t k; khint_t k;
if (h == 0) continue; if (h == 0) continue;
@@ -84,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); __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) 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; uint64_t i, st1, en1;
@@ -96,13 +146,13 @@ int mm_idx_getseq(const mm_idx_t *mi, uint32_t rid, uint32_t st, uint32_t en, ui
return en - st; return en - st;
} }
uint32_t mm_idx_cal_max_occ(const mm_idx_t *mi, float f) int32_t mm_idx_cal_max_occ(const mm_idx_t *mi, float f)
{ {
int i; int i;
size_t n = 0; size_t n = 0;
uint32_t thres; uint32_t thres;
khint_t *a, k; khint_t *a, k;
if (f <= 0.) return UINT32_MAX; if (f <= 0.) return INT32_MAX;
for (i = 0; i < 1<<mi->b; ++i) for (i = 0; i < 1<<mi->b; ++i)
if (mi->B[i].h) n += kh_size((idxhash_t*)mi->B[i].h); if (mi->B[i].h) n += kh_size((idxhash_t*)mi->B[i].h);
a = (uint32_t*)malloc(n * 4); a = (uint32_t*)malloc(n * 4);
@@ -125,7 +175,8 @@ uint32_t mm_idx_cal_max_occ(const mm_idx_t *mi, float f)
static void worker_post(void *g, long i, int tid) 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; idxhash_t *h;
mm_idx_t *mi = (mm_idx_t*)g; mm_idx_t *mi = (mm_idx_t*)g;
mm_idx_bucket_t *b = &mi->B[i]; mm_idx_bucket_t *b = &mi->B[i];
@@ -153,7 +204,7 @@ static void worker_post(void *g, long i, int tid)
int absent; int absent;
mm128_t *p = &b->a.a[j-1]; mm128_t *p = &b->a.a[j-1];
itr = kh_put(idx, h, p->x>>8>>mi->b<<1, &absent); 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) { if (n == 1) {
kh_key(h, itr) |= 1; kh_key(h, itr) |= 1;
kh_val(h, itr) = p->y; kh_val(h, itr) = p->y;
@@ -169,10 +220,10 @@ static void worker_post(void *g, long i, int tid)
} else ++n; } else ++n;
} }
b->h = h; b->h = h;
assert(b->n == start_p); assert(b->n == (int32_t)start_p);
// deallocate and clear b->a // deallocate and clear b->a
free(b->a.a); kfree(0, b->a.a);
b->a.n = b->a.m = 0, b->a.a = 0; b->a.n = b->a.m = 0, b->a.a = 0;
} }
@@ -190,7 +241,7 @@ static void mm_idx_post(mm_idx_t *mi, int n_threads)
#include "bseq.h" #include "bseq.h"
typedef struct { typedef struct {
int mini_batch_size, keep_name; int mini_batch_size;
uint64_t batch_size, sum_len; uint64_t batch_size, sum_len;
mm_bseq_file_t *fp; mm_bseq_file_t *fp;
mm_idx_t *mi; mm_idx_t *mi;
@@ -222,14 +273,15 @@ 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 s->seq = mm_bseq_read(p->fp, p->mini_batch_size, 0, &s->n_seq); // read a mini-batch
if (s->seq) { if (s->seq) {
uint32_t old_m, m; 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 assert((uint64_t)p->mi->n_seq + s->n_seq <= UINT32_MAX); // to prevent integer overflow
// make room for p->mi->seq // make room for p->mi->seq
old_m = p->mi->n_seq, m = p->mi->n_seq + s->n_seq; old_m = p->mi->n_seq, m = p->mi->n_seq + s->n_seq;
kroundup32(m); kroundup32(old_m); kroundup32(m); kroundup32(old_m);
if (old_m != m) if (old_m != m)
p->mi->seq = (mm_idx_seq_t*)realloc(p->mi->seq, m * sizeof(mm_idx_seq_t)); 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 // make room for p->mi->S
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; 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; old_max_len = (p->sum_len + 7) / 8;
max_len = (p->sum_len + sum_len + 7) / 8; max_len = (p->sum_len + sum_len + 7) / 8;
@@ -238,22 +290,25 @@ static void *worker_pipeline(void *shared, int step, void *in)
p->mi->S = (uint32_t*)realloc(p->mi->S, max_len * 4); 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)); memset(&p->mi->S[old_max_len], 0, 4 * (max_len - old_max_len));
} }
}
// populate p->mi->seq // populate p->mi->seq
for (i = 0; i < s->n_seq; ++i) { for (i = 0; i < s->n_seq; ++i) {
mm_idx_seq_t *seq = &p->mi->seq[p->mi->n_seq]; mm_idx_seq_t *seq = &p->mi->seq[p->mi->n_seq];
uint32_t j; uint32_t j;
if (p->keep_name) { if (!(p->mi->flag & MM_I_NO_NAME)) {
assert(strlen(s->seq[i].name) <= 254); // a long query name breaks BAM seq->name = (char*)kmalloc(p->mi->km, strlen(s->seq[i].name) + 1);
seq->name = strdup(s->seq[i].name); strcpy(seq->name, s->seq[i].name);
} else seq->name = 0; } else seq->name = 0;
seq->len = s->seq[i].l_seq; seq->len = s->seq[i].l_seq;
seq->offset = p->sum_len; seq->offset = p->sum_len;
// copy the sequence // copy the sequence
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 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; uint64_t o = p->sum_len + j;
int c = seq_nt4_table[(uint8_t)s->seq[i].seq[j]]; int c = seq_nt4_table[(uint8_t)s->seq[i].seq[j]];
mm_seq4_set(p->mi->S, o, c); mm_seq4_set(p->mi->S, o, c);
} }
}
// update p->sum_len and p->mi->n_seq // update p->sum_len and p->mi->n_seq
p->sum_len += seq->len; p->sum_len += seq->len;
s->seq[i].rid = p->mi->n_seq++; s->seq[i].rid = p->mi->n_seq++;
@@ -264,7 +319,10 @@ static void *worker_pipeline(void *shared, int step, void *in)
step_t *s = (step_t*)in; step_t *s = (step_t*)in;
for (i = 0; i < s->n_seq; ++i) { for (i = 0; i < s->n_seq; ++i) {
mm_bseq1_t *t = &s->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(t->seq); free(t->name);
} }
free(s->seq); s->seq = 0; free(s->seq); s->seq = 0;
@@ -272,21 +330,20 @@ static void *worker_pipeline(void *shared, int step, void *in)
} else if (step == 2) { // dispatch sketch to buckets } else if (step == 2) { // dispatch sketch to buckets
step_t *s = (step_t*)in; step_t *s = (step_t*)in;
mm_idx_add(p->mi, s->a.n, s->a.a); mm_idx_add(p->mi, s->a.n, s->a.a);
free(s->a.a); free(s); kfree(0, s->a.a); free(s);
} }
return 0; 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; pipeline_t pl;
if (fp == 0 || mm_bseq_eof(fp)) return 0;
memset(&pl, 0, sizeof(pipeline_t)); memset(&pl, 0, sizeof(pipeline_t));
pl.mini_batch_size = mini_batch_size < batch_size? mini_batch_size : batch_size; pl.mini_batch_size = (uint64_t)mini_batch_size < batch_size? mini_batch_size : batch_size;
pl.keep_name = keep_name;
pl.batch_size = batch_size; pl.batch_size = batch_size;
pl.fp = fp; pl.fp = fp;
if (pl.fp == 0) return 0; pl.mi = mm_idx_init(w, k, b, flag);
pl.mi = mm_idx_init(w, k, b, is_hpc);
kt_pipeline(n_threads < 3? n_threads : 3, worker_pipeline, &pl, 3); kt_pipeline(n_threads < 3? n_threads : 3, worker_pipeline, &pl, 3);
if (mm_verbose >= 3) if (mm_verbose >= 3)
@@ -299,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; 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_bseq_file_t *fp;
mm_idx_t *mi; mm_idx_t *mi;
fp = mm_bseq_open(fn); fp = mm_bseq_open(fn);
if (fp == 0) return 0; if (fp == 0) return 0;
mi = mm_idx_gen(fp, w, k, MM_IDX_DEF_B, 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); mm_bseq_close(fp);
return mi; 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 * * index I/O *
*************/ *************/
@@ -317,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) void mm_idx_dump(FILE *fp, const mm_idx_t *mi)
{ {
uint64_t sum_len = 0; uint64_t sum_len = 0;
uint32_t x[5]; uint32_t x[5], i;
int 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(MM_IDX_MAGIC, 1, 4, fp);
fwrite(x, 4, 5, fp); fwrite(x, 4, 5, fp);
for (i = 0; i < mi->n_seq; ++i) { for (i = 0; i < mi->n_seq; ++i) {
uint8_t l; if (mi->seq[i].name) {
l = strlen(mi->seq[i].name); uint8_t l = strlen(mi->seq[i].name);
fwrite(&l, 1, 1, fp); fwrite(&l, 1, 1, fp);
fwrite(mi->seq[i].name, 1, l, 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); fwrite(&mi->seq[i].len, 4, 1, fp);
sum_len += mi->seq[i].len; sum_len += mi->seq[i].len;
} }
@@ -347,15 +450,15 @@ void mm_idx_dump(FILE *fp, const mm_idx_t *mi)
fwrite(x, 8, 2, fp); fwrite(x, 8, 2, fp);
} }
} }
if (!(mi->flag & MM_I_NO_SEQ))
fwrite(mi->S, 4, (sum_len + 7) / 8, fp); fwrite(mi->S, 4, (sum_len + 7) / 8, fp);
fflush(fp); fflush(fp);
} }
mm_idx_t *mm_idx_load(FILE *fp) mm_idx_t *mm_idx_load(FILE *fp)
{ {
int i;
char magic[4]; char magic[4];
uint32_t x[5]; uint32_t x[5], i;
uint64_t sum_len = 0; uint64_t sum_len = 0;
mm_idx_t *mi; mm_idx_t *mi;
@@ -364,14 +467,16 @@ mm_idx_t *mm_idx_load(FILE *fp)
if (fread(x, 4, 5, fp) != 5) return 0; if (fread(x, 4, 5, fp) != 5) return 0;
mi = mm_idx_init(x[0], x[1], x[2], x[4]); mi = mm_idx_init(x[0], x[1], x[2], x[4]);
mi->n_seq = x[3]; mi->n_seq = x[3];
mi->seq = (mm_idx_seq_t*)calloc(mi->n_seq, sizeof(mm_idx_seq_t)); mi->seq = (mm_idx_seq_t*)kcalloc(mi->km, mi->n_seq, sizeof(mm_idx_seq_t));
for (i = 0; i < mi->n_seq; ++i) { for (i = 0; i < mi->n_seq; ++i) {
uint8_t l; uint8_t l;
mm_idx_seq_t *s = &mi->seq[i]; mm_idx_seq_t *s = &mi->seq[i];
fread(&l, 1, 1, fp); fread(&l, 1, 1, fp);
s->name = (char*)malloc(l + 1); if (l) {
s->name = (char*)kmalloc(mi->km, l + 1);
fread(s->name, 1, l, fp); fread(s->name, 1, l, fp);
s->name[l] = 0; s->name[l] = 0;
}
fread(&s->len, 4, 1, fp); fread(&s->len, 4, 1, fp);
s->offset = sum_len; s->offset = sum_len;
sum_len += s->len; sum_len += s->len;
@@ -397,26 +502,75 @@ mm_idx_t *mm_idx_load(FILE *fp)
kh_val(h, k) = x[1]; kh_val(h, k) = x[1];
} }
} }
if (!(mi->flag & MM_I_NO_SEQ)) {
mi->S = (uint32_t*)malloc((sum_len + 7) / 8 * 4); mi->S = (uint32_t*)malloc((sum_len + 7) / 8 * 4);
fread(mi->S, 4, (sum_len + 7) / 8, fp); fread(mi->S, 4, (sum_len + 7) / 8, fp);
}
return mi; return mi;
} }
int mm_idx_is_idx(const char *fn) int64_t mm_idx_is_idx(const char *fn)
{ {
int fd, is_idx = 0; int fd, is_idx = 0;
off_t ret; off_t ret, off_end;
char magic[4]; char magic[4];
if (strcmp(fn, "-") == 0) return 0; // read from pipe; not an index if (strcmp(fn, "-") == 0) return 0; // read from pipe; not an index
fd = open(fn, O_RDONLY); fd = open(fn, O_RDONLY);
if (fd < 0) return -1; // error if (fd < 0) return -1; // error
if ((ret = lseek(fd, 0, SEEK_END)) >= 4) { if ((off_end = lseek(fd, 0, SEEK_END)) >= 4) {
lseek(fd, 0, SEEK_SET); lseek(fd, 0, SEEK_SET);
ret = read(fd, magic, 4); ret = read(fd, magic, 4);
if (ret == 4 && strncmp(magic, MM_IDX_MAGIC, 4) == 0) if (ret == 4 && strncmp(magic, MM_IDX_MAGIC, 4) == 0)
is_idx = 1; is_idx = 1;
} }
close(fd); close(fd);
return is_idx; return is_idx? off_end : 0;
}
mm_idx_reader_t *mm_idx_reader_open(const char *fn, const mm_idxopt_t *opt, const char *fn_out)
{
int64_t is_idx;
mm_idx_reader_t *r;
is_idx = mm_idx_is_idx(fn);
if (is_idx < 0) return 0; // failed to open the index
r = (mm_idx_reader_t*)calloc(1, sizeof(mm_idx_reader_t));
r->is_idx = is_idx;
if (opt) r->opt = *opt;
else mm_idxopt_init(&r->opt);
if (r->is_idx) {
r->fp.idx = fopen(fn, "rb");
r->idx_size = is_idx;
} else r->fp.seq = mm_bseq_open(fn);
if (fn_out) r->fp_out = fopen(fn_out, "wb");
return r;
}
void mm_idx_reader_close(mm_idx_reader_t *r)
{
if (r->is_idx) fclose(r->fp.idx);
else mm_bseq_close(r->fp.seq);
if (r->fp_out) fclose(r->fp_out);
free(r);
}
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->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.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);
mi->index = r->n_parts++;
}
return mi;
}
int mm_idx_reader_eof(const mm_idx_reader_t *r) // TODO: in extremely rare cases, mm_bseq_eof() might not work
{
return r->is_idx? (feof(r->fp.idx) || ftell(r->fp.idx) == r->idx_size) : mm_bseq_eof(r->fp.seq);
} }
+113 -129
View File
@@ -1,175 +1,144 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <limits.h>
#include "kalloc.h" #include "kalloc.h"
/* The whole thing is: ("@" for the kheader_t of the block, "-" for free /* In kalloc, a *core* is a large chunk of contiguous memory. Each core is
* memory, and "+" for allocated memory. One char for one unit.) * associated with a master header, which keeps the size of the current core
* and the pointer to next core. Kalloc allocates small *blocks* of memory from
* the cores and organizes free memory blocks in a circular single-linked list.
* *
* This region is core 1. This region is core 2. * In the following diagram, "@" stands for the header of a free block (of type
* header_t), "#" for the header of an allocated block (of type size_t), "-"
* for free memory, and "+" for allocated memory.
* *
* @-------@++++++@++++++++++++@------------ @----------@++++++++++++@+++++++@------------ * master This region is core 1. master This region is core 2.
* | |
* *@-------#++++++#++++++++++++@-------- *@----------#++++++++++++#+++++++@------------
* | | | | * | | | |
* p=p->ptr->ptr->ptr->ptr p->ptr p->ptr->ptr p->ptr->ptr->ptr * p=p->ptr->ptr->ptr->ptr p->ptr p->ptr->ptr p->ptr->ptr->ptr
*/ */
#define PTR(p) ((size_t*)((size_t*)p)[1]) #define MIN_CORE_SIZE 0x80000
typedef struct _allocated_t { typedef struct header_t {
struct _allocated_t *next; size_t size;
size_t *ptr; struct header_t *ptr;
} allocated_t; } header_t;
typedef struct { typedef struct {
size_t base[2], *loop_head; header_t base, *loop_head, *core_head; /* base is a zero-sized block always kept in the loop */
allocated_t list_head, *list_tail;
size_t total_allocated;
} kmem_t; } kmem_t;
void *km_init() static void panic(const char *s)
{
return calloc(1, sizeof(kmem_t));
}
static void kerror(const char *s)
{ {
fprintf(stderr, "%s\n", s); fprintf(stderr, "%s\n", s);
exit(1); abort();
} }
static size_t *morecore(kmem_t *km, size_t nu) void *km_init(void)
{ {
size_t rnu, *up; return calloc(1, sizeof(kmem_t));
rnu = (nu + 0xfffff) & (~(size_t)0xfffff);
up = (size_t*)malloc(rnu * sizeof(size_t));
if (!up) { /* fail to allocate memory */
km_stat(km);
fprintf(stderr, "[morecore] %lu bytes requested but not available.\n", rnu * sizeof(size_t));
exit(1);
}
/* put the pointer in km->list_head */
if (km->list_tail == 0) km->list_tail = &km->list_head;
km->list_tail->ptr = up;
km->list_tail->next = (allocated_t*)calloc(1, sizeof(allocated_t));
km->list_tail = km->list_tail->next;
km->total_allocated += rnu * sizeof(size_t);
*up = rnu; /* the size of the current block, and in this case the block is the same as the new core */
kfree(km, up + 1); /* initialize the new "core" */
return km->loop_head;
} }
void km_destroy(void *_km) void km_destroy(void *_km)
{ {
kmem_t *km = (kmem_t*)_km; kmem_t *km = (kmem_t*)_km;
allocated_t *p, *q; header_t *p, *q;
if (km == 0) return; if (km == NULL) return;
p = &km->list_head; for (p = km->core_head; p != NULL;) {
do { q = p->ptr;
q = p->next; free(p);
free(p->ptr);
if (p != &km->list_head) free(p);
p = q; p = q;
} while (p && p->next); }
if (p != &km->list_head) free(p);
free(km); free(km);
} }
void kfree(void *_km, void *ap) static header_t *morecore(kmem_t *km, size_t nu)
{ {
size_t *p, *q; header_t *q;
size_t bytes, *p;
nu = (nu + 1 + (MIN_CORE_SIZE - 1)) / MIN_CORE_SIZE * MIN_CORE_SIZE; /* the first +1 for core header */
bytes = nu * sizeof(header_t);
q = (header_t*)malloc(bytes);
if (!q) panic("[morecore] insufficient memory");
q->ptr = km->core_head, q->size = nu, km->core_head = q;
p = (size_t*)(q + 1);
*p = nu - 1; /* the size of the free block; -1 because the first unit is used for the core header */
kfree(km, p + 1); /* initialize the new "core"; NB: the core header is not looped. */
return km->loop_head;
}
void kfree(void *_km, void *ap) /* kfree() also adds a new core to the circular list */
{
header_t *p, *q;
kmem_t *km = (kmem_t*)_km; kmem_t *km = (kmem_t*)_km;
if (!ap) return; if (!ap) return;
if (km == 0) { if (km == NULL) {
free(ap); free(ap);
return; return;
} }
p = (size_t*)ap - 1; /* *p is the size of the current block */ p = (header_t*)((size_t*)ap - 1);
p->size = *((size_t*)ap - 1);
/* Find the pointer that points to the block to be freed. The following loop can stop on two conditions: /* Find the pointer that points to the block to be freed. The following loop can stop on two conditions:
* *
* a) "p>q && p<q->ptr": @------@++++++++@+++++++@------- @---------------@+++++++@------- * a) "p>q && p<q->ptr": @------#++++++++#+++++++@------- @---------------#+++++++@-------
* (can also be in | | | -> | | * (can also be in | | | -> | |
* two cores) q p q->ptr q q->ptr * two cores) q p q->ptr q q->ptr
* *
* @-------- @+++++++++@-------- @-------- @------------------ * @-------- #+++++++++@-------- @-------- @------------------
* | | | -> | | * | | | -> | |
* q p q->ptr q q->ptr * q p q->ptr q q->ptr
* *
* b) "q>=q->ptr && (p>q || p<q->ptr)": @-------@+++++ @--------@+++++++ @-------@+++++ @---------------- * b) "q>=q->ptr && (p>q || p<q->ptr)": @-------#+++++ @--------#+++++++ @-------#+++++ @----------------
* | | | -> | | * | | | -> | |
* q->ptr q p q->ptr q * q->ptr q p q->ptr q
* *
* @+++++++@----- @++++++++@------- @------------- @++++++++@------- * #+++++++@----- #++++++++@------- @------------- #++++++++@-------
* | | | -> | | * | | | -> | |
* p q->ptr q q->ptr q * p q->ptr q q->ptr q
*/ */
for (q = km->loop_head; !(p > q && p < PTR(q)); q = PTR(q)) for (q = km->loop_head; !(p > q && p < q->ptr); q = q->ptr)
if (q >= PTR(q) && (p > q || p < PTR(q))) break; if (q >= q->ptr && (p > q || p < q->ptr)) break;
if (p + (*p) == PTR(q)) { /* two adjacent blocks, merge p and q->ptr (the 2nd and 4th cases) */ if (p + p->size == q->ptr) { /* two adjacent blocks, merge p and q->ptr (the 2nd and 4th cases) */
*p += *PTR(q); /* this is the new q->ptr size */ p->size += q->ptr->size;
p[1] = (size_t)PTR(PTR(q)); /* this is the new q->ptr->ptr */ p->ptr = q->ptr->ptr;
/* p is actually the new q->ptr. The actual change happens a few lines below. */ } else if (p + p->size > q->ptr && q->ptr >= p) {
} else if (p + (*p) > PTR(q) && PTR(q) >= p) { /* the end of the allocated block is in the next free block */ panic("[kfree] The end of the allocated block enters a free block.");
kerror("[kfree] The end of the allocated block enters a free block."); } else p->ptr = q->ptr; /* backup q->ptr */
} else p[1] = (size_t)PTR(q); /* backup q->ptr */
if (q + (*q) == p) { /* two adjacent blocks, merge q and p (the other two cases) */ if (q + q->size == p) { /* two adjacent blocks, merge q and p (the other two cases) */
*q += *p; q->size += p->size;
q[1] = (size_t)PTR(p); q->ptr = p->ptr;
km->loop_head = q; km->loop_head = q;
} else if (q + (*q) > p && p >= q) { /* the end of a free block in the allocated block */ } else if (q + q->size > p && p >= q) {
kerror("[kfree] The end of a free block enters the allocated block."); panic("[kfree] The end of a free block enters the allocated block.");
} else km->loop_head = p, q[1] = (size_t)p; /* in two cores, cannot be merged */ } else km->loop_head = p, q->ptr = p; /* in two cores, cannot be merged; create a new block in the list */
}
void *krealloc(void *_km, void *ap, size_t n_bytes)
{
kmem_t *km = (kmem_t*)_km;
size_t n_units, *p, *q;
if (n_bytes == 0) {
kfree(km, ap); return 0;
}
if (km == 0) return realloc(ap, n_bytes);
if (!ap) return kmalloc(km, n_bytes);
n_units = 1 + (n_bytes + sizeof(size_t) - 1) / sizeof(size_t);
p = (size_t*)ap - 1;
if (*p >= n_units) return ap; /* TODO: this prevents shrinking */
q = (size_t*)kmalloc(km, n_bytes);
memcpy(q, ap, (*p - 1) * sizeof(size_t));
kfree(km, ap);
return q;
} }
void *kmalloc(void *_km, size_t n_bytes) void *kmalloc(void *_km, size_t n_bytes)
{ {
kmem_t *km = (kmem_t*)_km; kmem_t *km = (kmem_t*)_km;
size_t n_units, *p, *q; size_t n_units;
header_t *p, *q;
if (n_bytes == 0) return 0; if (n_bytes == 0) return 0;
if (km == 0) return malloc(n_bytes); if (km == NULL) return malloc(n_bytes);
/* "n_units" means the number of units. The size of one unit equals to sizeof(kheader_t). n_units = (n_bytes + sizeof(size_t) + sizeof(header_t) - 1) / sizeof(header_t) + 1;
* "1" is the kheader_t of a block, which is always required. */
n_units = 1 + (n_bytes + sizeof(size_t) - 1) / sizeof(size_t);
if (n_units&1) ++n_units; /* make n_units an even number, or it will segfault if only one unit remains */
if (!(q = km->loop_head)) { /* the first time when kmalloc() is called, intialization */ if (!(q = km->loop_head)) /* the first time when kmalloc() is called, intialize it */
km->base[1] = (size_t)(km->loop_head = q = km->base); *q = 0; q = km->loop_head = km->base.ptr = &km->base;
} for (p = q->ptr;; q = p, p = p->ptr) { /* search for a suitable block */
for (p = PTR(q);; q = p, p = PTR(p)) { /* search for a suitable block */ if (p->size >= n_units) { /* p->size if the size of current block. This line means the current block is large enough. */
if (*p >= n_units) { /* p->size if the size of current block. This line means the current block is large enough. */ if (p->size == n_units) q->ptr = p->ptr; /* no need to split the block */
if (*p == n_units) q[1] = (size_t)PTR(p); /* no need to split the block */ else { /* split the block. NB: memory is allocated at the end of the block! */
else { /* split the block */ p->size -= n_units; /* reduce the size of the free block */
/* memory is allocated at the end of the block */ p += p->size; /* p points to the allocated block */
*p -= n_units; /* reduce the size of the free block */ *(size_t*)p = n_units; /* set the size */
p += *p; /* skip to the kheader_t of the allocated block */
*p = n_units; /* set the size */
} }
km->loop_head = q; /* set the end of chain */ km->loop_head = q; /* set the end of chain */
return p + 1; /* skip the kheader_t */ return (size_t*)p + 1;
} }
if (p == km->loop_head) { /* then ask for more "cores" */ if (p == km->loop_head) { /* then ask for more "cores" */
if ((p = morecore(km, n_units)) == 0) return 0; if ((p = morecore(km, n_units)) == 0) return 0;
@@ -182,33 +151,48 @@ void *kcalloc(void *_km, size_t count, size_t size)
kmem_t *km = (kmem_t*)_km; kmem_t *km = (kmem_t*)_km;
void *p; void *p;
if (size == 0 || count == 0) return 0; if (size == 0 || count == 0) return 0;
if (km == 0) return calloc(count, size); if (km == NULL) return calloc(count, size);
p = kmalloc(km, count * size); p = kmalloc(km, count * size);
memset(p, 0, count * size); memset(p, 0, count * size);
return p; return p;
} }
void km_stat(const void *_km) void *krealloc(void *_km, void *ap, size_t n_bytes) // TODO: this can be made more efficient in principle
{ {
kmem_t *km = (kmem_t*)_km; kmem_t *km = (kmem_t*)_km;
unsigned n_blocks, n_units; size_t n_units, *p, *q;
size_t max_block = 0, *p, *q;
float frag;
if (km == 0 || !(p = km->loop_head)) return; if (n_bytes == 0) {
n_blocks = n_units = 0; kfree(km, ap); return 0;
do { }
q = PTR(p); if (km == NULL) return realloc(ap, n_bytes);
if (*p > max_block) max_block = *p; if (ap == NULL) return kmalloc(km, n_bytes);
n_units += *p; n_units = (n_bytes + sizeof(size_t) + sizeof(header_t) - 1) / sizeof(header_t);
if (p + (*p) > q && q > p) p = (size_t*)ap - 1;
kerror("[kr_stat] The end of a free block enters another free block."); if (*p >= n_units) return ap; /* TODO: this prevents shrinking */
p = q; q = (size_t*)kmalloc(km, n_bytes);
++n_blocks; memcpy(q, ap, (*p - 1) * sizeof(header_t));
} while (p != km->loop_head); kfree(km, ap);
return q;
}
--n_blocks; void km_stat(const void *_km, km_stat_t *s)
frag = 1.0/1024.0 * n_units * sizeof(size_t) / n_blocks; {
fprintf(stderr, "[kr_stat] tot=%lu, free=%lu, n_block=%u, max_block=%lu, frag_len=%.3fK\n", kmem_t *km = (kmem_t*)_km;
km->total_allocated, n_units * sizeof(size_t), n_blocks, max_block * sizeof(size_t), frag); header_t *p;
memset(s, 0, sizeof(km_stat_t));
if (km == NULL || km->loop_head == NULL) return;
for (p = km->loop_head;; p = p->ptr) {
s->available += p->size * sizeof(header_t);
if (p->size != 0) ++s->n_blocks; /* &kmem_t::base is always one of the cores. It is zero-sized. */
if (p->ptr > p && p + p->size > p->ptr)
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) {
size_t size = p->size * sizeof(header_t);
++s->n_cores;
s->capacity += size;
s->largest = s->largest > size? s->largest : size;
}
} }
+6 -5
View File
@@ -1,14 +1,16 @@
#ifndef _KALLOC_H_ #ifndef _KALLOC_H_
#define _KALLOC_H_ #define _KALLOC_H_
#include <stdlib.h> #include <stddef.h> /* for size_t */
#define km_size(x) (*(((size_t*)(x))-1) * sizeof(size_t))
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
typedef struct {
size_t capacity, available, n_blocks, n_cores, largest;
} km_stat_t;
void *kmalloc(void *km, size_t size); void *kmalloc(void *km, size_t size);
void *krealloc(void *km, void *ptr, size_t size); void *krealloc(void *km, void *ptr, size_t size);
void *kcalloc(void *km, size_t count, size_t size); void *kcalloc(void *km, size_t count, size_t size);
@@ -16,8 +18,7 @@ void kfree(void *km, void *ptr);
void *km_init(void); void *km_init(void);
void km_destroy(void *km); void km_destroy(void *km);
void km_stat(const void *_km, km_stat_t *s);
void km_stat(const void *km); // TODO: return numbers instead of print to stderr
#ifdef __cplusplus #ifdef __cplusplus
} }
+2 -1
View File
@@ -3,11 +3,12 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdint.h>
#include "kalloc.h" #include "kalloc.h"
#define __KDQ_TYPE(type) \ #define __KDQ_TYPE(type) \
typedef struct { \ typedef struct { \
size_t front:58, bits:6, count, mask; \ uint64_t front:58, bits:6, count, mask; \
type *a; \ type *a; \
void *km; \ void *km; \
} kdq_##type##_t; } kdq_##type##_t;
+22 -2
View File
@@ -30,6 +30,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h>
typedef struct { typedef struct {
void *left, *right; void *left, *right;
@@ -39,6 +40,23 @@ typedef struct {
#define KSORT_SWAP(type_t, a, b) { register type_t t=(a); (a)=(b); (b)=t; } #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 ks_ksmall_##name(size_t n, type_t arr[], size_t kk) \
{ \ { \
type_t *low, *high, *k, *ll, *hh, *mid; \ type_t *low, *high, *k, *ll, *hh, *mid; \
@@ -78,6 +96,7 @@ typedef const char *ksstr_t;
#define KSORT_INIT_STR KSORT_INIT(str, ksstr_t, ks_lt_str) #define KSORT_INIT_STR KSORT_INIT(str, ksstr_t, ks_lt_str)
#define RS_MIN_SIZE 64 #define RS_MIN_SIZE 64
#define RS_MAX_BITS 8
#define KRADIX_SORT_INIT(name, rstype_t, rskey, sizeof_key) \ #define KRADIX_SORT_INIT(name, rstype_t, rskey, sizeof_key) \
typedef struct { \ typedef struct { \
@@ -98,7 +117,8 @@ typedef const char *ksstr_t;
{ \ { \
rstype_t *i; \ rstype_t *i; \
int size = 1<<n_bits, m = size - 1; \ int size = 1<<n_bits, m = size - 1; \
rsbucket_##name##_t *k, b[size], *be = b + size; \ rsbucket_##name##_t *k, b[1<<RS_MAX_BITS], *be = b + size; \
assert(n_bits <= RS_MAX_BITS); \
for (k = b; k != be; ++k) k->b = k->e = beg; \ for (k = b; k != be; ++k) k->b = k->e = beg; \
for (i = beg; i != end; ++i) ++b[rskey(*i)>>s&m].e; \ for (i = beg; i != end; ++i) ++b[rskey(*i)>>s&m].e; \
for (k = b + 1; k != be; ++k) \ for (k = b + 1; k != be; ++k) \
@@ -127,7 +147,7 @@ typedef const char *ksstr_t;
void radix_sort_##name(rstype_t *beg, rstype_t *end) \ void radix_sort_##name(rstype_t *beg, rstype_t *end) \
{ \ { \
if (end - beg <= RS_MIN_SIZE) rs_insertsort_##name(beg, end); \ if (end - beg <= RS_MIN_SIZE) rs_insertsort_##name(beg, end); \
else rs_sort_##name(beg, end, 8, sizeof_key * 8 - 8); \ else rs_sort_##name(beg, end, RS_MAX_BITS, (sizeof_key - 1) * RS_MAX_BITS); \
} }
#endif #endif
+38 -12
View File
@@ -12,6 +12,9 @@
#define KSW_EZ_APPROX_DROP 0x10 // approximate Z-drop; faster with sse #define KSW_EZ_APPROX_DROP 0x10 // approximate Z-drop; faster with sse
#define KSW_EZ_EXTZ_ONLY 0x40 // only perform extension #define KSW_EZ_EXTZ_ONLY 0x40 // only perform extension
#define KSW_EZ_REV_CIGAR 0x80 // reverse CIGAR in the output #define KSW_EZ_REV_CIGAR 0x80 // reverse CIGAR in the output
#define KSW_EZ_SPLICE_FOR 0x100
#define KSW_EZ_SPLICE_REV 0x200
#define KSW_EZ_SPLICE_FLANK 0x400
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -24,6 +27,7 @@ typedef struct {
int mte, mte_q; // max score when reaching the end of target int mte, mte_q; // max score when reaching the end of target
int score; // max score reaching both ends; may be KSW_NEG_INF int score; // max score reaching both ends; may be KSW_NEG_INF
int m_cigar, n_cigar; int m_cigar, n_cigar;
int reach_end;
uint32_t *cigar; uint32_t *cigar;
} ksw_extz_t; } ksw_extz_t;
@@ -39,19 +43,27 @@ typedef struct {
* @param mat m*m scoring mattrix in one-dimension array * @param mat m*m scoring mattrix in one-dimension array
* @param gapo gap open penalty; a gap of length l cost "-(gapo+l*gape)" * @param gapo gap open penalty; a gap of length l cost "-(gapo+l*gape)"
* @param gape gap extension penalty * @param gape gap extension penalty
* @param w band width * @param w band width (<0 to disable)
* @param zdrop off-diagonal drop-off to stop extension (positive) * @param zdrop off-diagonal drop-off to stop extension (positive; <0 to disable)
* @param flag flag (see KSW_EZ_* macros) * @param flag flag (see KSW_EZ_* macros)
* @param ez (out) scores and cigar * @param ez (out) scores and cigar
*/ */
void ksw_extz(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t q, int8_t e, int w, int zdrop, int flag, ksw_extz_t *ez); void ksw_extz(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t q, int8_t e, int w, int zdrop, int flag, ksw_extz_t *ez); int8_t q, int8_t e, int w, int zdrop, int flag, ksw_extz_t *ez);
void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez);
void ksw_extd(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, void ksw_extd(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t gapo, int8_t gape, int8_t gapo2, int8_t gape2, int w, int zdrop, int flag, ksw_extz_t *ez); int8_t gapo, int8_t gape, int8_t gapo2, int8_t gape2, int w, int zdrop, int flag, ksw_extz_t *ez);
void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t gapo, int8_t gape, int8_t gapo2, int8_t gape2, int w, int zdrop, int flag, ksw_extz_t *ez); int8_t gapo, int8_t gape, int8_t gapo2, int8_t gape2, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez);
void ksw_exts2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t gapo, int8_t gape, int8_t gapo2, int8_t noncan, int zdrop, int flag, ksw_extz_t *ez);
void ksw_extf2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t mch, int8_t mis, int8_t e, int w, int xdrop, ksw_extz_t *ez);
/** /**
* Global alignment * Global alignment
@@ -67,6 +79,9 @@ int ksw_gg(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *ta
int ksw_gg2(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t gapo, int8_t gape, int w, int *m_cigar_, int *n_cigar_, uint32_t **cigar_); int ksw_gg2(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t gapo, int8_t gape, int w, int *m_cigar_, int *n_cigar_, uint32_t **cigar_);
int ksw_gg2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t gapo, int8_t gape, int w, int *m_cigar_, int *n_cigar_, uint32_t **cigar_); int ksw_gg2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t gapo, int8_t gape, int w, int *m_cigar_, int *n_cigar_, uint32_t **cigar_);
void *ksw_ll_qinit(void *km, int size, int qlen, const uint8_t *query, int m, const int8_t *mat);
int ksw_ll_i16(void *q, int tlen, const uint8_t *target, int gapo, int gape, int *qe, int *te);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
@@ -101,21 +116,33 @@ 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 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 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}) // 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, const uint8_t *p, const int *off, int n_col, int i0, int j0, int *m_cigar_, int *n_cigar_, uint32_t **cigar_) 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 { // 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; int n_cigar = 0, m_cigar = *m_cigar_, i = i0, j = j0, r, state = 0;
uint32_t *cigar = *cigar_, tmp; uint32_t *cigar = *cigar_, tmp;
while (i >= 0 && j >= 0) { // at the beginning of the loop, _state_ tells us which state to check while (i >= 0 && j >= 0) { // at the beginning of the loop, _state_ tells us which state to check
if (is_rot) r = i + j, tmp = p[r * n_col + i - off[r]]; int force_state = -1;
else tmp = p[i * n_col + j - off[i]]; if (is_rot) {
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[(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[(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. 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 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 (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 if (state == 0) cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, 0, 1), --i, --j; // match
else if (state == 1 || state == 3) cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, 2, 1), --i; // deletion 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 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 (j >= 0) cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, 1, j + 1); // first insertion
if (!is_rev) if (!is_rev)
for (i = 0; i < n_cigar>>1; ++i) // reverse CIGAR for (i = 0; i < n_cigar>>1; ++i) // reverse CIGAR
@@ -127,7 +154,7 @@ static inline void ksw_reset_extz(ksw_extz_t *ez)
{ {
ez->max_q = ez->max_t = ez->mqe_t = ez->mte_q = -1; ez->max_q = ez->max_t = ez->mqe_t = ez->mte_q = -1;
ez->max = 0, ez->score = ez->mqe = ez->mte = KSW_NEG_INF; ez->max = 0, ez->score = ez->mqe = ez->mte = KSW_NEG_INF;
ez->n_cigar = 0, ez->zdropped = 0; ez->n_cigar = 0, ez->zdropped = 0, ez->reach_end = 0;
} }
static inline int ksw_apply_zdrop(ksw_extz_t *ez, int is_rot, int32_t H, int a, int b, int zdrop, int8_t e) static inline int ksw_apply_zdrop(ksw_extz_t *ez, int is_rot, int32_t H, int a, int b, int zdrop, int8_t e)
@@ -147,5 +174,4 @@ static inline int ksw_apply_zdrop(ksw_extz_t *ez, int is_rot, int32_t H, int a,
} }
return 0; return 0;
} }
#endif #endif
+97
View File
@@ -0,0 +1,97 @@
#ifdef KSW_CPU_DISPATCH
#include <stdlib.h>
#include "ksw2.h"
#define SIMD_SSE 0x1
#define SIMD_SSE2 0x2
#define SIMD_SSE3 0x4
#define SIMD_SSSE3 0x8
#define SIMD_SSE4_1 0x10
#define SIMD_SSE4_2 0x20
#define SIMD_AVX 0x40
#define SIMD_AVX2 0x80
#define SIMD_AVX512F 0x100
#ifndef _MSC_VER
// adapted from https://github.com/01org/linux-sgx/blob/master/common/inc/internal/linux/cpuid_gnu.h
void __cpuidex(int cpuid[4], int func_id, int subfunc_id)
{
#if defined(__x86_64__)
asm volatile ("cpuid"
: "=a" (cpuid[0]), "=b" (cpuid[1]), "=c" (cpuid[2]), "=d" (cpuid[3])
: "0" (func_id), "2" (subfunc_id));
#else // on 32bit, ebx can NOT be used as PIC code
asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1"
: "=a" (cpuid[0]), "=r" (cpuid[1]), "=c" (cpuid[2]), "=d" (cpuid[3])
: "0" (func_id), "2" (subfunc_id));
#endif
}
#endif
int x86_simd(void)
{
int flag = 0, cpuid[4], max_id;
__cpuidex(cpuid, 0, 0);
max_id = cpuid[0];
if (max_id == 0) return 0;
__cpuidex(cpuid, 1, 0);
if (cpuid[3]>>25&1) flag |= SIMD_SSE;
if (cpuid[3]>>26&1) flag |= SIMD_SSE2;
if (cpuid[2]>>0 &1) flag |= SIMD_SSE3;
if (cpuid[2]>>9 &1) flag |= SIMD_SSSE3;
if (cpuid[2]>>19&1) flag |= SIMD_SSE4_1;
if (cpuid[2]>>20&1) flag |= SIMD_SSE4_2;
if (cpuid[2]>>28&1) flag |= SIMD_AVX;
if (max_id >= 7) {
__cpuidex(cpuid, 7, 0);
if (cpuid[1]>>5 &1) flag |= SIMD_AVX2;
if (cpuid[1]>>16&1) flag |= SIMD_AVX512F;
}
return flag;
}
void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t q, int8_t e, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez)
{
extern void ksw_extz2_sse2(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t q, int8_t e, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez);
extern void ksw_extz2_sse41(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t q, int8_t e, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez);
unsigned simd;
simd = x86_simd();
if (simd & SIMD_SSE4_1)
ksw_extz2_sse41(km, qlen, query, tlen, target, m, mat, q, e, w, zdrop, end_bonus, flag, ez);
else if (simd & SIMD_SSE2)
ksw_extz2_sse2(km, qlen, query, tlen, target, m, mat, q, e, w, zdrop, end_bonus, flag, ez);
else abort();
}
void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int8_t q2, int8_t e2, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez)
{
extern void ksw_extd2_sse2(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int8_t q2, int8_t e2, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez);
extern void ksw_extd2_sse41(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int8_t q2, int8_t e2, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez);
unsigned simd;
simd = x86_simd();
if (simd & SIMD_SSE4_1)
ksw_extd2_sse41(km, qlen, query, tlen, target, m, mat, q, e, q2, e2, w, zdrop, end_bonus, flag, ez);
else if (simd & SIMD_SSE2)
ksw_extd2_sse2(km, qlen, query, tlen, target, m, mat, q, e, q2, e2, w, zdrop, end_bonus, flag, ez);
else abort();
}
void ksw_exts2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int8_t q2, int8_t noncan, int zdrop, int flag, ksw_extz_t *ez)
{
extern void ksw_exts2_sse2(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int8_t q2, int8_t noncan, int zdrop, int flag, ksw_extz_t *ez);
extern void ksw_exts2_sse41(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int8_t q2, int8_t noncan, int zdrop, int flag, ksw_extz_t *ez);
unsigned simd;
simd = x86_simd();
if (simd & SIMD_SSE4_1)
ksw_exts2_sse41(km, qlen, query, tlen, target, m, mat, q, e, q2, noncan, zdrop, flag, ez);
else if (simd & SIMD_SSE2)
ksw_exts2_sse2(km, qlen, query, tlen, target, m, mat, q, e, q2, noncan, zdrop, flag, ez);
else abort();
}
#endif
+40 -39
View File
@@ -1,16 +1,31 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h>
#include "ksw2.h" #include "ksw2.h"
#ifdef __SSE2__ #ifdef __SSE2__
#include <emmintrin.h> #include <emmintrin.h>
#ifdef KSW_SSE2_ONLY
#undef __SSE4_1__
#endif
#ifdef __SSE4_1__ #ifdef __SSE4_1__
#include <smmintrin.h> #include <smmintrin.h>
#endif #endif
#ifdef KSW_CPU_DISPATCH
#ifdef __SSE4_1__
void ksw_extd2_sse41(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int8_t q2, int8_t e2, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez)
#else
void ksw_extd2_sse2(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int8_t q2, int8_t e2, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez)
#endif
#else
void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int8_t q2, int8_t e2, int w, int zdrop, int flag, ksw_extz_t *ez) int8_t q, int8_t e, int8_t q2, int8_t e2, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez)
#endif // ~KSW_CPU_DISPATCH
{ {
#define __dp_code_block1 \ #define __dp_code_block1 \
z = _mm_load_si128(&s[t]); \ z = _mm_load_si128(&s[t]); \
@@ -42,11 +57,11 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
a2= _mm_sub_epi8(a2, tmp); \ a2= _mm_sub_epi8(a2, tmp); \
b2= _mm_sub_epi8(b2, tmp); b2= _mm_sub_epi8(b2, tmp);
int r, t, qe = q + e, n_col_, *off = 0, tlen_, qlen_, last_st, last_en, wl, wr, max_sc, min_sc, long_thres, long_diff; int r, t, qe = q + e, n_col_, *off = 0, *off_end = 0, tlen_, qlen_, last_st, last_en, wl, wr, max_sc, min_sc, long_thres, long_diff;
int with_cigar = !(flag&KSW_EZ_SCORE_ONLY), approx_max = !!(flag&KSW_EZ_APPROX_MAX); int with_cigar = !(flag&KSW_EZ_SCORE_ONLY), approx_max = !!(flag&KSW_EZ_APPROX_MAX);
int32_t *H = 0, H0 = 0, last_H0_t = 0; int32_t *H = 0, H0 = 0, last_H0_t = 0;
uint8_t *qr, *sf, *mem, *mem2 = 0; uint8_t *qr, *sf, *mem, *mem2 = 0;
__m128i q_, q2_, qe_, qe2_, zero_, sc_mch_, sc_mis_, m1_; __m128i q_, q2_, qe_, qe2_, zero_, sc_mch_, sc_mis_, m1_, sc_N_;
__m128i *u, *v, *x, *y, *x2, *y2, *s, *p = 0; __m128i *u, *v, *x, *y, *x2, *y2, *s, *p = 0;
ksw_reset_extz(ez); ksw_reset_extz(ez);
@@ -61,12 +76,14 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
qe2_ = _mm_set1_epi8(q2 + e2); qe2_ = _mm_set1_epi8(q2 + e2);
sc_mch_ = _mm_set1_epi8(mat[0]); sc_mch_ = _mm_set1_epi8(mat[0]);
sc_mis_ = _mm_set1_epi8(mat[1]); sc_mis_ = _mm_set1_epi8(mat[1]);
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 m1_ = _mm_set1_epi8(m - 1); // wildcard
if (w < 0) w = tlen > qlen? tlen : qlen; if (w < 0) w = tlen > qlen? tlen : qlen;
wl = wr = w; wl = wr = w;
tlen_ = (tlen + 15) / 16; tlen_ = (tlen + 15) / 16;
n_col_ = ((w + 1 < tlen? (w + 1 < qlen? w + 1 : qlen): tlen) + 15) / 16 + 1; n_col_ = qlen < tlen? qlen : tlen;
n_col_ = ((n_col_ < w + 1? n_col_ : w + 1) + 15) / 16 + 1;
qlen_ = (qlen + 15) / 16; qlen_ = (qlen + 15) / 16;
for (t = 1, max_sc = mat[0], min_sc = mat[1]; t < m * m; ++t) { for (t = 1, max_sc = mat[0], min_sc = mat[1]; t < m * m; ++t) {
max_sc = max_sc > mat[t]? max_sc : mat[t]; max_sc = max_sc > mat[t]? max_sc : mat[t];
@@ -94,9 +111,10 @@ 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; for (t = 0; t < tlen_ * 16; ++t) H[t] = KSW_NEG_INF;
} }
if (with_cigar) { 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); p = (__m128i*)(((size_t)mem2 + 15) >> 4 << 4);
off = (int*)kmalloc(km, (qlen + tlen - 1) * sizeof(int)); off = (int*)kmalloc(km, (qlen + tlen - 1) * sizeof(int) * 2);
off_end = off + qlen + tlen - 1;
} }
for (t = 0; t < qlen; ++t) qr[t] = query[qlen - 1 - t]; for (t = 0; t < qlen; ++t) qr[t] = query[qlen - 1 - t];
@@ -105,7 +123,7 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
for (r = 0, last_st = last_en = -1; r < qlen + tlen - 1; ++r) { for (r = 0, last_st = last_en = -1; r < qlen + tlen - 1; ++r) {
int st = 0, en = tlen - 1, st0, en0, st_, en_; int st = 0, en = tlen - 1, st0, en0, st_, en_;
int8_t x1, x21, v1; int8_t x1, x21, v1;
uint8_t *qrr = qr + (qlen - 1 - r), p_en0 = 0; uint8_t *qrr = qr + (qlen - 1 - r);
int8_t *u8 = (int8_t*)u, *v8 = (int8_t*)v, *x8 = (int8_t*)x, *x28 = (int8_t*)x2; int8_t *u8 = (int8_t*)u, *v8 = (int8_t*)v, *x8 = (int8_t*)x, *x28 = (int8_t*)x2;
__m128i x1_, x21_, v1_; __m128i x1_, x21_, v1_;
// find the boundaries // find the boundaries
@@ -145,10 +163,11 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
tmp = _mm_cmpeq_epi8(sq, st); tmp = _mm_cmpeq_epi8(sq, st);
#ifdef __SSE4_1__ #ifdef __SSE4_1__
tmp = _mm_blendv_epi8(sc_mis_, sc_mch_, tmp); tmp = _mm_blendv_epi8(sc_mis_, sc_mch_, tmp);
tmp = _mm_blendv_epi8(tmp, sc_N_, mask);
#else #else
tmp = _mm_or_si128(_mm_andnot_si128(tmp, sc_mis_), _mm_and_si128(tmp, sc_mch_)); tmp = _mm_or_si128(_mm_andnot_si128(tmp, sc_mis_), _mm_and_si128(tmp, sc_mch_));
tmp = _mm_or_si128(_mm_andnot_si128(mask, tmp), _mm_and_si128(mask, sc_N_));
#endif #endif
tmp = _mm_andnot_si128(mask, tmp);
_mm_storeu_si128((__m128i*)((int8_t*)s + t), tmp); _mm_storeu_si128((__m128i*)((int8_t*)s + t), tmp);
} }
} else { } else {
@@ -160,6 +179,7 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
x21_ = _mm_cvtsi32_si128((uint8_t)x21); x21_ = _mm_cvtsi32_si128((uint8_t)x21);
v1_ = _mm_cvtsi32_si128((uint8_t)v1); v1_ = _mm_cvtsi32_si128((uint8_t)v1);
st_ = st / 16, en_ = en / 16; st_ = st / 16, en_ = en / 16;
assert(en_ - st_ + 1 <= n_col_);
if (!with_cigar) { // score only if (!with_cigar) { // score only
for (t = st_; t <= en_; ++t) { for (t = st_; t <= en_; ++t) {
__m128i z, a, b, a2, b2, xt1, x2t1, vt1, ut, tmp; __m128i z, a, b, a2, b2, xt1, x2t1, vt1, ut, tmp;
@@ -198,19 +218,8 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
#endif #endif
} }
} else if (!(flag&KSW_EZ_RIGHT)) { // gap left-alignment } 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[r] = st, off_end[r] = en;
if (en0 < r && en0 < tlen - 1) { // to avoid backtracking out of the band; this assumes a fixed band
int8_t a, a2, z = ((uint8_t*)s)[en0];
a = x8[en0-1] + v8[en0-1];
p_en0 = a > z? 1 : 0;
z = a > z? a : z;
p_en0 |= a - (z - q) > 0? 1<<4 : 0;
a2 = x28[en0-1] + v8[en0-1];
p_en0 = a2 > z? 3 : p_en0;
z = a2 > z? a2 : z;
p_en0 |= a2 - (z - q2) > 0? 1<<6 : 0;
}
for (t = st_; t <= en_; ++t) { for (t = st_; t <= en_; ++t) {
__m128i d, z, a, b, a2, b2, xt1, x2t1, vt1, ut, tmp; __m128i d, z, a, b, a2, b2, xt1, x2t1, vt1, ut, tmp;
__dp_code_block1; __dp_code_block1;
@@ -256,19 +265,8 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
_mm_store_si128(&pr[t], d); _mm_store_si128(&pr[t], d);
} }
} else { // gap right-alignment } else { // gap right-alignment
__m128i *pr = p + r * n_col_ - st_; __m128i *pr = p + (size_t)r * n_col_ - st_;
off[r] = st; off[r] = st, off_end[r] = en;
if (en0 < r && en0 < tlen - 1) { // to avoid backtracking out of the band; this assumes a fixed band
int8_t a, a2, z = ((uint8_t*)s)[en0];
a = x8[en0-1] + v8[en0-1];
p_en0 = a >= z? 1 : 0;
z = a >= z? a : z;
p_en0 |= a - (z - q) >= 0? 1<<4 : 0;
a2 = x28[en0-1] + v8[en0-1];
p_en0 = a2 >= z? 3 : p_en0;
z = a2 >= z? a2 : z;
p_en0 |= a2 - (z - q2) >= 0? 1<<6 : 0;
}
for (t = st_; t <= en_; ++t) { for (t = st_; t <= en_; ++t) {
__m128i d, z, a, b, a2, b2, xt1, x2t1, vt1, ut, tmp; __m128i d, z, a, b, a2, b2, xt1, x2t1, vt1, ut, tmp;
__dp_code_block1; __dp_code_block1;
@@ -314,7 +312,6 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
_mm_store_si128(&pr[t], d); _mm_store_si128(&pr[t], d);
} }
} }
if (with_cigar && en0 < r && en0 < tlen - 1) ((uint8_t*)(p + r * n_col_))[en0 - st] = p_en0;
if (!approx_max) { // find the exact max with a 32-bit score array if (!approx_max) { // find the exact max with a 32-bit score array
int32_t max_H, max_t; int32_t max_H, max_t;
// compute H[], max_H and max_t // compute H[], max_H and max_t
@@ -383,10 +380,14 @@ void ksw_extd2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
if (!approx_max) kfree(km, H); if (!approx_max) kfree(km, H);
if (with_cigar) { // backtrack if (with_cigar) { // backtrack
int rev_cigar = !!(flag & KSW_EZ_REV_CIGAR); int rev_cigar = !!(flag & KSW_EZ_REV_CIGAR);
if (!ez->zdropped && !(flag&KSW_EZ_EXTZ_ONLY)) if (!ez->zdropped && !(flag&KSW_EZ_EXTZ_ONLY)) {
ksw_backtrack(km, 1, rev_cigar, (uint8_t*)p, off, n_col_*16, tlen-1, qlen-1, &ez->m_cigar, &ez->n_cigar, &ez->cigar); 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->max_t >= 0 && ez->max_q >= 0) } else if (!ez->zdropped && (flag&KSW_EZ_EXTZ_ONLY) && ez->mqe + end_bonus > (int)ez->max) {
ksw_backtrack(km, 1, rev_cigar, (uint8_t*)p, off, n_col_*16, ez->max_t, ez->max_q, &ez->m_cigar, &ez->n_cigar, &ez->cigar); 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) {
ksw_backtrack(km, 1, rev_cigar, 0, (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); kfree(km, mem2); kfree(km, off);
} }
} }
+376
View File
@@ -0,0 +1,376 @@
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "ksw2.h"
#ifdef __SSE2__
#include <emmintrin.h>
#ifdef KSW_SSE2_ONLY
#undef __SSE4_1__
#endif
#ifdef __SSE4_1__
#include <smmintrin.h>
#endif
#ifdef KSW_CPU_DISPATCH
#ifdef __SSE4_1__
void ksw_exts2_sse41(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int8_t q2, int8_t noncan, int zdrop, int flag, ksw_extz_t *ez)
#else
void ksw_exts2_sse2(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int8_t q2, int8_t noncan, int zdrop, int flag, ksw_extz_t *ez)
#endif
#else
void ksw_exts2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat,
int8_t q, int8_t e, int8_t q2, int8_t noncan, int zdrop, int flag, ksw_extz_t *ez)
#endif // ~KSW_CPU_DISPATCH
{
#define __dp_code_block1 \
z = _mm_load_si128(&s[t]); \
xt1 = _mm_load_si128(&x[t]); /* xt1 <- x[r-1][t..t+15] */ \
tmp = _mm_srli_si128(xt1, 15); /* tmp <- x[r-1][t+15] */ \
xt1 = _mm_or_si128(_mm_slli_si128(xt1, 1), x1_); /* xt1 <- x[r-1][t-1..t+14] */ \
x1_ = tmp; \
vt1 = _mm_load_si128(&v[t]); /* vt1 <- v[r-1][t..t+15] */ \
tmp = _mm_srli_si128(vt1, 15); /* tmp <- v[r-1][t+15] */ \
vt1 = _mm_or_si128(_mm_slli_si128(vt1, 1), v1_); /* vt1 <- v[r-1][t-1..t+14] */ \
v1_ = tmp; \
a = _mm_add_epi8(xt1, vt1); /* a <- x[r-1][t-1..t+14] + v[r-1][t-1..t+14] */ \
ut = _mm_load_si128(&u[t]); /* ut <- u[t..t+15] */ \
b = _mm_add_epi8(_mm_load_si128(&y[t]), ut); /* b <- y[r-1][t..t+15] + u[r-1][t..t+15] */ \
x2t1= _mm_load_si128(&x2[t]); \
tmp = _mm_srli_si128(x2t1, 15); \
x2t1= _mm_or_si128(_mm_slli_si128(x2t1, 1), x21_); \
x21_= tmp; \
a2 = _mm_add_epi8(x2t1, vt1); \
a2a = _mm_add_epi8(a2, _mm_load_si128(&acceptor[t]));
#define __dp_code_block2 \
_mm_store_si128(&u[t], _mm_sub_epi8(z, vt1)); /* u[r][t..t+15] <- z - v[r-1][t-1..t+14] */ \
_mm_store_si128(&v[t], _mm_sub_epi8(z, ut)); /* v[r][t..t+15] <- z - u[r-1][t..t+15] */ \
tmp = _mm_sub_epi8(z, q_); \
a = _mm_sub_epi8(a, tmp); \
b = _mm_sub_epi8(b, tmp); \
a2= _mm_sub_epi8(a2, _mm_sub_epi8(z, q2_));
int r, t, qe = q + e, n_col_, *off = 0, *off_end = 0, tlen_, qlen_, last_st, last_en, max_sc, min_sc, long_thres, long_diff;
int with_cigar = !(flag&KSW_EZ_SCORE_ONLY), approx_max = !!(flag&KSW_EZ_APPROX_MAX);
int32_t *H = 0, H0 = 0, last_H0_t = 0;
uint8_t *qr, *sf, *mem, *mem2 = 0;
__m128i q_, q2_, qe_, zero_, sc_mch_, sc_mis_, sc_N_, m1_;
__m128i *u, *v, *x, *y, *x2, *s, *p = 0, *donor, *acceptor;
ksw_reset_extz(ez);
if (m <= 1 || qlen <= 0 || tlen <= 0 || q2 <= q + e) return;
zero_ = _mm_set1_epi8(0);
q_ = _mm_set1_epi8(q);
q2_ = _mm_set1_epi8(q2);
qe_ = _mm_set1_epi8(q + e);
sc_mch_ = _mm_set1_epi8(mat[0]);
sc_mis_ = _mm_set1_epi8(mat[1]);
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;
n_col_ = ((qlen < tlen? qlen : tlen) + 15) / 16 + 1;
qlen_ = (qlen + 15) / 16;
for (t = 1, max_sc = mat[0], min_sc = mat[1]; t < m * m; ++t) {
max_sc = max_sc > mat[t]? max_sc : mat[t];
min_sc = min_sc < mat[t]? min_sc : mat[t];
}
if (-min_sc > 2 * (q + e)) return; // otherwise, we won't see any mismatches
long_thres = (q2 - q) / e - 1;
if (q2 > q + e + long_thres * e)
++long_thres;
long_diff = long_thres * e - (q2 - q);
mem = (uint8_t*)kcalloc(km, tlen_ * 9 + qlen_ + 1, 16);
u = (__m128i*)(((size_t)mem + 15) >> 4 << 4); // 16-byte aligned
v = u + tlen_, x = v + tlen_, y = x + tlen_, x2 = y + tlen_;
donor = x2 + tlen_, acceptor = donor + tlen_;
s = acceptor + tlen_, sf = (uint8_t*)(s + tlen_), qr = sf + tlen_ * 16;
memset(u, -q - e, tlen_ * 16 * 4); // this set u, v, x, y (because they are in the same array)
memset(x2, -q2, tlen_ * 16);
if (!approx_max) {
H = (int32_t*)kmalloc(km, tlen_ * 16 * 4);
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);
p = (__m128i*)(((size_t)mem2 + 15) >> 4 << 4);
off = (int*)kmalloc(km, (qlen + tlen - 1) * sizeof(int) * 2);
off_end = off + qlen + tlen - 1;
}
for (t = 0; t < qlen; ++t) qr[t] = query[qlen - 1 - t];
memcpy(sf, target, tlen);
// set the donor and acceptor arrays. TODO: this assumes 0/1/2/3 encoding!
if (flag & (KSW_EZ_SPLICE_FOR|KSW_EZ_SPLICE_REV)) {
int semi_cost = flag&KSW_EZ_SPLICE_FLANK? -noncan/2 : 0; // GTr or yAG is worth 0.5 bit; see PMID:18688272
memset(donor, -noncan, tlen_ * 16);
for (t = 0; t < tlen - 4; ++t) {
int can_type = 0; // type of canonical site: 0=none, 1=GT/AG only, 2=GTr/yAG
if ((flag & KSW_EZ_SPLICE_FOR) && target[t+1] == 2 && target[t+2] == 3) can_type = 1; // GTr...
if ((flag & KSW_EZ_SPLICE_REV) && target[t+1] == 1 && target[t+2] == 3) can_type = 1; // CTr...
if (can_type && (target[t+3] == 0 || target[t+3] == 2)) can_type = 2;
if (can_type) ((int8_t*)donor)[t] = can_type == 2? 0 : semi_cost;
}
memset(acceptor, -noncan, tlen_ * 16);
for (t = 2; t < tlen; ++t) {
int can_type = 0;
if ((flag & KSW_EZ_SPLICE_FOR) && target[t-1] == 0 && target[t] == 2) can_type = 1; // ...yAG
if ((flag & KSW_EZ_SPLICE_REV) && target[t-1] == 0 && target[t] == 1) can_type = 1; // ...yAC
if (can_type && (target[t-2] == 1 || target[t-2] == 3)) can_type = 2;
if (can_type) ((int8_t*)acceptor)[t] = can_type == 2? 0 : semi_cost;
}
}
for (r = 0, last_st = last_en = -1; r < qlen + tlen - 1; ++r) {
int st = 0, en = tlen - 1, st0, en0, st_, en_;
int8_t x1, x21, v1, *u8 = (int8_t*)u, *v8 = (int8_t*)v;
uint8_t *qrr = qr + (qlen - 1 - r);
__m128i x1_, x21_, v1_;
// find the boundaries
if (st < r - qlen + 1) st = r - qlen + 1;
if (en > r) en = r;
st0 = st, en0 = en;
st = st / 16 * 16, en = (en + 16) / 16 * 16 - 1;
// set boundary conditions
if (st > 0) {
if (st - 1 >= last_st && st - 1 <= last_en)
x1 = ((int8_t*)x)[st - 1], x21 = ((int8_t*)x2)[st - 1], v1 = v8[st - 1]; // (r-1,s-1) calculated in the last round
else x1 = -q - e, x21 = -q2, v1 = -q - e;
} else {
x1 = -q - e, x21 = -q2;
v1 = r == 0? -q - e : r < long_thres? -e : r == long_thres? long_diff : 0;
}
if (en >= r) {
((int8_t*)y)[r] = -q - e;
u8[r] = r == 0? -q - e : r < long_thres? -e : r == long_thres? long_diff : 0;
}
// loop fission: set scores first
if (!(flag & KSW_EZ_GENERIC_SC)) {
for (t = st0; t <= en0; t += 16) {
__m128i sq, st, tmp, mask;
sq = _mm_loadu_si128((__m128i*)&sf[t]);
st = _mm_loadu_si128((__m128i*)&qrr[t]);
mask = _mm_or_si128(_mm_cmpeq_epi8(sq, m1_), _mm_cmpeq_epi8(st, m1_));
tmp = _mm_cmpeq_epi8(sq, st);
#ifdef __SSE4_1__
tmp = _mm_blendv_epi8(sc_mis_, sc_mch_, tmp);
tmp = _mm_blendv_epi8(tmp, sc_N_, mask);
#else
tmp = _mm_or_si128(_mm_andnot_si128(tmp, sc_mis_), _mm_and_si128(tmp, sc_mch_));
tmp = _mm_or_si128(_mm_andnot_si128(mask, tmp), _mm_and_si128(mask, sc_N_));
#endif
_mm_storeu_si128((__m128i*)((int8_t*)s + t), tmp);
}
} else {
for (t = st0; t <= en0; ++t)
((uint8_t*)s)[t] = mat[sf[t] * m + qrr[t]];
}
// core loop
x1_ = _mm_cvtsi32_si128((uint8_t)x1);
x21_ = _mm_cvtsi32_si128((uint8_t)x21);
v1_ = _mm_cvtsi32_si128((uint8_t)v1);
st_ = st / 16, en_ = en / 16;
assert(en_ - st_ + 1 <= n_col_);
if (!with_cigar) { // score only
for (t = st_; t <= en_; ++t) {
__m128i z, a, b, a2, a2a, xt1, x2t1, vt1, ut, tmp;
__dp_code_block1;
#ifdef __SSE4_1__
z = _mm_max_epi8(z, a);
z = _mm_max_epi8(z, b);
z = _mm_max_epi8(z, a2a);
__dp_code_block2; // save u[] and v[]; update a, b and a2
_mm_store_si128(&x[t], _mm_sub_epi8(_mm_max_epi8(a, zero_), qe_));
_mm_store_si128(&y[t], _mm_sub_epi8(_mm_max_epi8(b, zero_), qe_));
tmp = _mm_load_si128(&donor[t]);
_mm_store_si128(&x2[t], _mm_sub_epi8(_mm_max_epi8(a2, tmp), q2_));
#else
tmp = _mm_cmpgt_epi8(a, z);
z = _mm_or_si128(_mm_andnot_si128(tmp, z), _mm_and_si128(tmp, a));
tmp = _mm_cmpgt_epi8(b, z);
z = _mm_or_si128(_mm_andnot_si128(tmp, z), _mm_and_si128(tmp, b));
tmp = _mm_cmpgt_epi8(a2a, z);
z = _mm_or_si128(_mm_andnot_si128(tmp, z), _mm_and_si128(tmp, a2a));
__dp_code_block2;
tmp = _mm_cmpgt_epi8(a, zero_);
_mm_store_si128(&x[t], _mm_sub_epi8(_mm_and_si128(tmp, a), qe_));
tmp = _mm_cmpgt_epi8(b, zero_);
_mm_store_si128(&y[t], _mm_sub_epi8(_mm_and_si128(tmp, b), qe_));
tmp = _mm_load_si128(&donor[t]); // TODO: check if this is correct
tmp = _mm_cmpgt_epi8(a2, tmp);
tmp = _mm_or_si128(_mm_andnot_si128(tmp, tmp), _mm_and_si128(tmp, a2));
_mm_store_si128(&x2[t], _mm_sub_epi8(tmp, q2_));
#endif
}
} else if (!(flag&KSW_EZ_RIGHT)) { // gap left-alignment
__m128i *pr = p + r * n_col_ - st_;
off[r] = st, off_end[r] = en;
for (t = st_; t <= en_; ++t) {
__m128i d, z, a, b, a2, a2a, xt1, x2t1, vt1, ut, tmp, tmp2;
__dp_code_block1;
#ifdef __SSE4_1__
d = _mm_and_si128(_mm_cmpgt_epi8(a, z), _mm_set1_epi8(1)); // d = a > z? 1 : 0
z = _mm_max_epi8(z, a);
d = _mm_blendv_epi8(d, _mm_set1_epi8(2), _mm_cmpgt_epi8(b, z)); // d = b > z? 2 : d
z = _mm_max_epi8(z, b);
d = _mm_blendv_epi8(d, _mm_set1_epi8(3), _mm_cmpgt_epi8(a2a, z)); // d = a2 > z? 3 : d
z = _mm_max_epi8(z, a2a);
#else // we need to emulate SSE4.1 intrinsics _mm_max_epi8() and _mm_blendv_epi8()
tmp = _mm_cmpgt_epi8(a, z);
d = _mm_and_si128(tmp, _mm_set1_epi8(1));
z = _mm_or_si128(_mm_andnot_si128(tmp, z), _mm_and_si128(tmp, a));
tmp = _mm_cmpgt_epi8(b, z);
d = _mm_or_si128(_mm_andnot_si128(tmp, d), _mm_and_si128(tmp, _mm_set1_epi8(2)));
z = _mm_or_si128(_mm_andnot_si128(tmp, z), _mm_and_si128(tmp, b));
tmp = _mm_cmpgt_epi8(a2a, z);
d = _mm_or_si128(_mm_andnot_si128(tmp, d), _mm_and_si128(tmp, _mm_set1_epi8(3)));
z = _mm_or_si128(_mm_andnot_si128(tmp, z), _mm_and_si128(tmp, a2a));
#endif
__dp_code_block2;
tmp = _mm_cmpgt_epi8(a, zero_);
_mm_store_si128(&x[t], _mm_sub_epi8(_mm_and_si128(tmp, a), qe_));
d = _mm_or_si128(d, _mm_and_si128(tmp, _mm_set1_epi8(0x08))); // d = a > 0? 1<<3 : 0
tmp = _mm_cmpgt_epi8(b, zero_);
_mm_store_si128(&y[t], _mm_sub_epi8(_mm_and_si128(tmp, b), qe_));
d = _mm_or_si128(d, _mm_and_si128(tmp, _mm_set1_epi8(0x10))); // d = b > 0? 1<<4 : 0
tmp2 = _mm_load_si128(&donor[t]);
tmp = _mm_cmpgt_epi8(a2, tmp2);
#ifdef __SSE4_1__
tmp2 = _mm_max_epi8(a2, tmp2);
#else
tmp2 = _mm_or_si128(_mm_andnot_si128(tmp, tmp2), _mm_and_si128(tmp, a2));
#endif
_mm_store_si128(&x2[t], _mm_sub_epi8(tmp2, q2_));
d = _mm_or_si128(d, _mm_and_si128(tmp, _mm_set1_epi8(0x20)));
_mm_store_si128(&pr[t], d);
}
} else { // gap right-alignment
__m128i *pr = p + r * n_col_ - st_;
off[r] = st, off_end[r] = en;
for (t = st_; t <= en_; ++t) {
__m128i d, z, a, b, a2, a2a, xt1, x2t1, vt1, ut, tmp, tmp2;
__dp_code_block1;
#ifdef __SSE4_1__
d = _mm_andnot_si128(_mm_cmpgt_epi8(z, a), _mm_set1_epi8(1)); // d = z > a? 0 : 1
z = _mm_max_epi8(z, a);
d = _mm_blendv_epi8(_mm_set1_epi8(2), d, _mm_cmpgt_epi8(z, b)); // d = z > b? d : 2
z = _mm_max_epi8(z, b);
d = _mm_blendv_epi8(_mm_set1_epi8(3), d, _mm_cmpgt_epi8(z, a2a)); // d = z > a2? d : 3
z = _mm_max_epi8(z, a2a);
#else // we need to emulate SSE4.1 intrinsics _mm_max_epi8() and _mm_blendv_epi8()
tmp = _mm_cmpgt_epi8(z, a);
d = _mm_andnot_si128(tmp, _mm_set1_epi8(1));
z = _mm_or_si128(_mm_and_si128(tmp, z), _mm_andnot_si128(tmp, a));
tmp = _mm_cmpgt_epi8(z, b);
d = _mm_or_si128(_mm_and_si128(tmp, d), _mm_andnot_si128(tmp, _mm_set1_epi8(2)));
z = _mm_or_si128(_mm_and_si128(tmp, z), _mm_andnot_si128(tmp, b));
tmp = _mm_cmpgt_epi8(z, a2a);
d = _mm_or_si128(_mm_and_si128(tmp, d), _mm_andnot_si128(tmp, _mm_set1_epi8(3)));
z = _mm_or_si128(_mm_and_si128(tmp, z), _mm_andnot_si128(tmp, a2a));
#endif
__dp_code_block2;
tmp = _mm_cmpgt_epi8(zero_, a);
_mm_store_si128(&x[t], _mm_sub_epi8(_mm_andnot_si128(tmp, a), qe_));
d = _mm_or_si128(d, _mm_andnot_si128(tmp, _mm_set1_epi8(0x08))); // d = a > 0? 1<<3 : 0
tmp = _mm_cmpgt_epi8(zero_, b);
_mm_store_si128(&y[t], _mm_sub_epi8(_mm_andnot_si128(tmp, b), qe_));
d = _mm_or_si128(d, _mm_andnot_si128(tmp, _mm_set1_epi8(0x10))); // d = b > 0? 1<<4 : 0
tmp2 = _mm_load_si128(&donor[t]);
tmp = _mm_cmpgt_epi8(tmp2, a2);
#ifdef __SSE4_1__
tmp2 = _mm_max_epi8(tmp2, a2);
#else
tmp2 = _mm_or_si128(_mm_andnot_si128(tmp, a2), _mm_and_si128(tmp, tmp2));
#endif
_mm_store_si128(&x2[t], _mm_sub_epi8(tmp2, q2_));
d = _mm_or_si128(d, _mm_andnot_si128(tmp, _mm_set1_epi8(0x20))); // d = a > 0? 1<<5 : 0
_mm_store_si128(&pr[t], d);
}
}
if (!approx_max) { // find the exact max with a 32-bit score array
int32_t max_H, max_t;
// compute H[], max_H and max_t
if (r > 0) {
int32_t HH[4], tt[4], en1 = st0 + (en0 - st0) / 4 * 4, i;
__m128i max_H_, max_t_;
max_H = H[en0] = en0 > 0? H[en0-1] + u8[en0] : H[en0] + v8[en0]; // special casing the last element
max_t = en0;
max_H_ = _mm_set1_epi32(max_H);
max_t_ = _mm_set1_epi32(max_t);
for (t = st0; t < en1; t += 4) { // this implements: H[t]+=v8[t]-qe; if(H[t]>max_H) max_H=H[t],max_t=t;
__m128i H1, tmp, t_;
H1 = _mm_loadu_si128((__m128i*)&H[t]);
t_ = _mm_setr_epi32(v8[t], v8[t+1], v8[t+2], v8[t+3]);
H1 = _mm_add_epi32(H1, t_);
_mm_storeu_si128((__m128i*)&H[t], H1);
t_ = _mm_set1_epi32(t);
tmp = _mm_cmpgt_epi32(H1, max_H_);
#ifdef __SSE4_1__
max_H_ = _mm_blendv_epi8(max_H_, H1, tmp);
max_t_ = _mm_blendv_epi8(max_t_, t_, tmp);
#else
max_H_ = _mm_or_si128(_mm_and_si128(tmp, H1), _mm_andnot_si128(tmp, max_H_));
max_t_ = _mm_or_si128(_mm_and_si128(tmp, t_), _mm_andnot_si128(tmp, max_t_));
#endif
}
_mm_storeu_si128((__m128i*)HH, max_H_);
_mm_storeu_si128((__m128i*)tt, max_t_);
for (i = 0; i < 4; ++i)
if (max_H < HH[i]) max_H = HH[i], max_t = tt[i] + i;
for (; t < en0; ++t) { // for the rest of values that haven't been computed with SSE
H[t] += (int32_t)v8[t];
if (H[t] > max_H)
max_H = H[t], max_t = t;
}
} else H[0] = v8[0] - qe, max_H = H[0], max_t = 0; // special casing r==0
// update ez
if (en0 == tlen - 1 && H[en0] > ez->mte)
ez->mte = H[en0], ez->mte_q = r - en;
if (r - st0 == qlen - 1 && H[st0] > ez->mqe)
ez->mqe = H[st0], ez->mqe_t = st0;
if (ksw_apply_zdrop(ez, 1, max_H, r, max_t, zdrop, 0)) break;
if (r == qlen + tlen - 2 && en0 == tlen - 1)
ez->score = H[tlen - 1];
} else { // find approximate max; Z-drop might be inaccurate, too.
if (r > 0) {
if (last_H0_t >= st0 && last_H0_t <= en0 && last_H0_t + 1 >= st0 && last_H0_t + 1 <= en0) {
int32_t d0 = v8[last_H0_t];
int32_t d1 = u8[last_H0_t + 1];
if (d0 > d1) H0 += d0;
else H0 += d1, ++last_H0_t;
} else if (last_H0_t >= st0 && last_H0_t <= en0) {
H0 += v8[last_H0_t];
} else {
++last_H0_t, H0 += u8[last_H0_t];
}
} else H0 = v8[0] - qe, last_H0_t = 0;
if ((flag & KSW_EZ_APPROX_DROP) && ksw_apply_zdrop(ez, 1, H0, r, last_H0_t, zdrop, 0)) break;
if (r == qlen + tlen - 2 && en0 == tlen - 1)
ez->score = H0;
}
last_st = st, last_en = en;
//for (t = st0; t <= en0; ++t) printf("(%d,%d)\t(%d,%d,%d,%d)\t%d\n", r, t, ((int8_t*)u)[t], ((int8_t*)v)[t], ((int8_t*)x)[t], ((int8_t*)y)[t], H[t]); // for debugging
}
kfree(km, mem);
if (!approx_max) kfree(km, H);
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, 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, 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);
}
}
#endif // __SSE2__
+38 -31
View File
@@ -1,14 +1,27 @@
#include <string.h> #include <string.h>
#include <assert.h>
#include "ksw2.h" #include "ksw2.h"
#ifdef __SSE2__ #ifdef __SSE2__
#include <emmintrin.h> #include <emmintrin.h>
#ifdef KSW_SSE2_ONLY
#undef __SSE4_1__
#endif
#ifdef __SSE4_1__ #ifdef __SSE4_1__
#include <smmintrin.h> #include <smmintrin.h>
#endif #endif
void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t q, int8_t e, int w, int zdrop, int flag, ksw_extz_t *ez) #ifdef KSW_CPU_DISPATCH
#ifdef __SSE4_1__
void ksw_extz2_sse41(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t q, int8_t e, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez)
#else
void ksw_extz2_sse2(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t q, int8_t e, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez)
#endif
#else
void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uint8_t *target, int8_t m, const int8_t *mat, int8_t q, int8_t e, int w, int zdrop, int end_bonus, int flag, ksw_extz_t *ez)
#endif // ~KSW_CPU_DISPATCH
{ {
#define __dp_code_block1 \ #define __dp_code_block1 \
z = _mm_add_epi8(_mm_load_si128(&s[t]), qe2_); \ z = _mm_add_epi8(_mm_load_si128(&s[t]), qe2_); \
@@ -33,11 +46,11 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
a = _mm_sub_epi8(a, z); \ a = _mm_sub_epi8(a, z); \
b = _mm_sub_epi8(b, z); b = _mm_sub_epi8(b, z);
int r, t, qe = q + e, n_col_, *off = 0, tlen_, qlen_, last_st, last_en, wl, wr, max_sc, min_sc; int r, t, qe = q + e, n_col_, *off = 0, *off_end = 0, tlen_, qlen_, last_st, last_en, wl, wr, max_sc, min_sc;
int with_cigar = !(flag&KSW_EZ_SCORE_ONLY), approx_max = !!(flag&KSW_EZ_APPROX_MAX); int with_cigar = !(flag&KSW_EZ_SCORE_ONLY), approx_max = !!(flag&KSW_EZ_APPROX_MAX);
int32_t *H = 0, H0 = 0, last_H0_t = 0; int32_t *H = 0, H0 = 0, last_H0_t = 0;
uint8_t *qr, *sf, *mem, *mem2 = 0; uint8_t *qr, *sf, *mem, *mem2 = 0;
__m128i q_, qe2_, zero_, flag1_, flag2_, flag8_, flag16_, sc_mch_, sc_mis_, m1_, max_sc_; __m128i q_, qe2_, zero_, flag1_, flag2_, flag8_, flag16_, sc_mch_, sc_mis_, sc_N_, m1_, max_sc_;
__m128i *u, *v, *x, *y, *s, *p = 0; __m128i *u, *v, *x, *y, *s, *p = 0;
ksw_reset_extz(ez); ksw_reset_extz(ez);
@@ -52,13 +65,15 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
flag16_ = _mm_set1_epi8(0x10); flag16_ = _mm_set1_epi8(0x10);
sc_mch_ = _mm_set1_epi8(mat[0]); sc_mch_ = _mm_set1_epi8(mat[0]);
sc_mis_ = _mm_set1_epi8(mat[1]); sc_mis_ = _mm_set1_epi8(mat[1]);
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 m1_ = _mm_set1_epi8(m - 1); // wildcard
max_sc_ = _mm_set1_epi8(mat[0] + (q + e) * 2); max_sc_ = _mm_set1_epi8(mat[0] + (q + e) * 2);
if (w < 0) w = tlen > qlen? tlen : qlen; if (w < 0) w = tlen > qlen? tlen : qlen;
wl = wr = w; wl = wr = w;
tlen_ = (tlen + 15) / 16; tlen_ = (tlen + 15) / 16;
n_col_ = ((w + 1 < tlen? (w + 1 < qlen? w + 1 : qlen): tlen) + 15) / 16 + 1; n_col_ = qlen < tlen? qlen : tlen;
n_col_ = ((n_col_ < w + 1? n_col_ : w + 1) + 15) / 16 + 1;
qlen_ = (qlen + 15) / 16; qlen_ = (qlen + 15) / 16;
for (t = 1, max_sc = mat[0], min_sc = mat[1]; t < m * m; ++t) { for (t = 1, max_sc = mat[0], min_sc = mat[1]; t < m * m; ++t) {
max_sc = max_sc > mat[t]? max_sc : mat[t]; max_sc = max_sc > mat[t]? max_sc : mat[t];
@@ -74,9 +89,10 @@ 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; for (t = 0; t < tlen_ * 16; ++t) H[t] = KSW_NEG_INF;
} }
if (with_cigar) { 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); p = (__m128i*)(((size_t)mem2 + 15) >> 4 << 4);
off = (int*)kmalloc(km, (qlen + tlen - 1) * sizeof(int)); off = (int*)kmalloc(km, (qlen + tlen - 1) * sizeof(int) * 2);
off_end = off + qlen + tlen - 1;
} }
for (t = 0; t < qlen; ++t) qr[t] = query[qlen - 1 - t]; for (t = 0; t < qlen; ++t) qr[t] = query[qlen - 1 - t];
@@ -85,7 +101,7 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
for (r = 0, last_st = last_en = -1; r < qlen + tlen - 1; ++r) { for (r = 0, last_st = last_en = -1; r < qlen + tlen - 1; ++r) {
int st = 0, en = tlen - 1, st0, en0, st_, en_; int st = 0, en = tlen - 1, st0, en0, st_, en_;
int8_t x1, v1; int8_t x1, v1;
uint8_t *qrr = qr + (qlen - 1 - r), *u8 = (uint8_t*)u, *v8 = (uint8_t*)v, *x8 = (uint8_t*)x, p_en0 = 0; uint8_t *qrr = qr + (qlen - 1 - r), *u8 = (uint8_t*)u, *v8 = (uint8_t*)v;
__m128i x1_, v1_; __m128i x1_, v1_;
// find the boundaries // find the boundaries
if (st < r - qlen + 1) st = r - qlen + 1; if (st < r - qlen + 1) st = r - qlen + 1;
@@ -115,10 +131,11 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
tmp = _mm_cmpeq_epi8(sq, st); tmp = _mm_cmpeq_epi8(sq, st);
#ifdef __SSE4_1__ #ifdef __SSE4_1__
tmp = _mm_blendv_epi8(sc_mis_, sc_mch_, tmp); tmp = _mm_blendv_epi8(sc_mis_, sc_mch_, tmp);
tmp = _mm_blendv_epi8(tmp, sc_N_, mask);
#else #else
tmp = _mm_or_si128(_mm_andnot_si128(tmp, sc_mis_), _mm_and_si128(tmp, sc_mch_)); tmp = _mm_or_si128(_mm_andnot_si128(tmp, sc_mis_), _mm_and_si128(tmp, sc_mch_));
tmp = _mm_or_si128(_mm_andnot_si128(mask, tmp), _mm_and_si128(mask, sc_N_));
#endif #endif
tmp = _mm_andnot_si128(mask, tmp);
_mm_storeu_si128((__m128i*)((uint8_t*)s + t), tmp); _mm_storeu_si128((__m128i*)((uint8_t*)s + t), tmp);
} }
} else { } else {
@@ -129,6 +146,7 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
x1_ = _mm_cvtsi32_si128(x1); x1_ = _mm_cvtsi32_si128(x1);
v1_ = _mm_cvtsi32_si128(v1); v1_ = _mm_cvtsi32_si128(v1);
st_ = st / 16, en_ = en / 16; st_ = st / 16, en_ = en / 16;
assert(en_ - st_ + 1 <= n_col_);
if (!with_cigar) { // score only if (!with_cigar) { // score only
for (t = st_; t <= en_; ++t) { for (t = st_; t <= en_; ++t) {
__m128i z, a, b, xt1, vt1, ut, tmp; __m128i z, a, b, xt1, vt1, ut, tmp;
@@ -151,15 +169,8 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
#endif #endif
} }
} else if (!(flag&KSW_EZ_RIGHT)) { // gap left-alignment } 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[r] = st, off_end[r] = en;
if (en0 < r && en0 < tlen - 1) { // to avoid backtracking out of the band; this assumes a fixed band
int8_t a, z = ((uint8_t*)s)[en0] + 2 * qe;
a = x8[en0-1] + v8[en0-1];
p_en0 = a > z? 1 : 0;
z = a > z? a : z;
p_en0 |= a - (z - q) > 0? 0x08 : 0;
}
for (t = st_; t <= en_; ++t) { for (t = st_; t <= en_; ++t) {
__m128i d, z, a, b, xt1, vt1, ut, tmp; __m128i d, z, a, b, xt1, vt1, ut, tmp;
__dp_code_block1; __dp_code_block1;
@@ -184,15 +195,8 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
_mm_store_si128(&pr[t], d); _mm_store_si128(&pr[t], d);
} }
} else { // gap right-alignment } else { // gap right-alignment
__m128i *pr = p + r * n_col_ - st_; __m128i *pr = p + (size_t)r * n_col_ - st_;
off[r] = st; off[r] = st, off_end[r] = en;
if (en0 < r && en0 < tlen - 1) {
int8_t a, z = ((uint8_t*)s)[en0] + 2 * qe;
a = x8[en0-1] + v8[en0-1];
p_en0 = a >= z? 1 : 0;
z = a >= z? a : z;
p_en0 |= a - (z - q) >= 0? 0x08 : 0;
}
for (t = st_; t <= en_; ++t) { for (t = st_; t <= en_; ++t) {
__m128i d, z, a, b, xt1, vt1, ut, tmp; __m128i d, z, a, b, xt1, vt1, ut, tmp;
__dp_code_block1; __dp_code_block1;
@@ -217,7 +221,6 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
_mm_store_si128(&pr[t], d); _mm_store_si128(&pr[t], d);
} }
} }
if (with_cigar && en0 < r && en0 < tlen - 1) ((uint8_t*)(p + r * n_col_))[en0 - st] = p_en0;
if (!approx_max) { // find the exact max with a 32-bit score array if (!approx_max) { // find the exact max with a 32-bit score array
int32_t max_H, max_t; int32_t max_H, max_t;
// compute H[], max_H and max_t // compute H[], max_H and max_t
@@ -288,10 +291,14 @@ void ksw_extz2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
if (!approx_max) kfree(km, H); if (!approx_max) kfree(km, H);
if (with_cigar) { // backtrack if (with_cigar) { // backtrack
int rev_cigar = !!(flag & KSW_EZ_REV_CIGAR); int rev_cigar = !!(flag & KSW_EZ_REV_CIGAR);
if (!ez->zdropped && !(flag&KSW_EZ_EXTZ_ONLY)) if (!ez->zdropped && !(flag&KSW_EZ_EXTZ_ONLY)) {
ksw_backtrack(km, 1, rev_cigar, (uint8_t*)p, off, n_col_*16, tlen-1, qlen-1, &ez->m_cigar, &ez->n_cigar, &ez->cigar); 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->max_t >= 0 && ez->max_q >= 0) } else if (!ez->zdropped && (flag&KSW_EZ_EXTZ_ONLY) && ez->mqe + end_bonus > (int)ez->max) {
ksw_backtrack(km, 1, rev_cigar, (uint8_t*)p, off, n_col_*16, ez->max_t, ez->max_q, &ez->m_cigar, &ez->n_cigar, &ez->cigar); 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) {
ksw_backtrack(km, 1, rev_cigar, 0, (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); kfree(km, mem2); kfree(km, off);
} }
} }
+147
View File
@@ -0,0 +1,147 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <emmintrin.h>
#include "ksw2.h"
#ifdef __GNUC__
#define LIKELY(x) __builtin_expect((x),1)
#define UNLIKELY(x) __builtin_expect((x),0)
#else
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#endif
typedef struct {
int qlen, slen;
uint8_t shift, mdiff, max, size;
__m128i *qp, *H0, *H1, *E, *Hmax;
} kswq_t;
/**
* Initialize the query data structure
*
* @param size Number of bytes used to store a score; valid valures are 1 or 2
* @param qlen Length of the query sequence
* @param query Query sequence
* @param m Size of the alphabet
* @param mat Scoring matrix in a one-dimension array
*
* @return Query data structure
*/
void *ksw_ll_qinit(void *km, int size, int qlen, const uint8_t *query, int m, const int8_t *mat)
{
kswq_t *q;
int slen, a, tmp, p;
size = size > 1? 2 : 1;
p = 8 * (3 - size); // # values per __m128i
slen = (qlen + p - 1) / p; // segmented length
q = (kswq_t*)kmalloc(km, sizeof(kswq_t) + 256 + 16 * slen * (m + 4)); // a single block of memory
q->qp = (__m128i*)(((size_t)q + sizeof(kswq_t) + 15) >> 4 << 4); // align memory
q->H0 = q->qp + slen * m;
q->H1 = q->H0 + slen;
q->E = q->H1 + slen;
q->Hmax = q->E + slen;
q->slen = slen; q->qlen = qlen; q->size = size;
// compute shift
tmp = m * m;
for (a = 0, q->shift = 127, q->mdiff = 0; a < tmp; ++a) { // find the minimum and maximum score
if (mat[a] < (int8_t)q->shift) q->shift = mat[a];
if (mat[a] > (int8_t)q->mdiff) q->mdiff = mat[a];
}
q->max = q->mdiff;
q->shift = 256 - q->shift; // NB: q->shift is uint8_t
q->mdiff += q->shift; // this is the difference between the min and max scores
// An example: p=8, qlen=19, slen=3 and segmentation:
// {{0,3,6,9,12,15,18,-1},{1,4,7,10,13,16,-1,-1},{2,5,8,11,14,17,-1,-1}}
if (size == 1) {
int8_t *t = (int8_t*)q->qp;
for (a = 0; a < m; ++a) {
int i, k, nlen = slen * p;
const int8_t *ma = mat + a * m;
for (i = 0; i < slen; ++i)
for (k = i; k < nlen; k += slen) // p iterations
*t++ = (k >= qlen? 0 : ma[query[k]]) + q->shift;
}
} else {
int16_t *t = (int16_t*)q->qp;
for (a = 0; a < m; ++a) {
int i, k, nlen = slen * p;
const int8_t *ma = mat + a * m;
for (i = 0; i < slen; ++i)
for (k = i; k < nlen; k += slen) // p iterations
*t++ = (k >= qlen? 0 : ma[query[k]]);
}
}
return q;
}
int ksw_ll_i16(void *q_, int tlen, const uint8_t *target, int _gapo, int _gape, int *qe, int *te)
{
kswq_t *q = (kswq_t*)q_;
int slen, i, gmax = 0, qlen8;
__m128i zero, gapoe, gape, *H0, *H1, *E, *Hmax;
uint16_t *H8;
#define __max_8(ret, xx) do { \
(xx) = _mm_max_epi16((xx), _mm_srli_si128((xx), 8)); \
(xx) = _mm_max_epi16((xx), _mm_srli_si128((xx), 4)); \
(xx) = _mm_max_epi16((xx), _mm_srli_si128((xx), 2)); \
(ret) = _mm_extract_epi16((xx), 0); \
} while (0)
// initialization
*qe = *te = -1;
zero = _mm_set1_epi32(0);
gapoe = _mm_set1_epi16(_gapo + _gape);
gape = _mm_set1_epi16(_gape);
H0 = q->H0; H1 = q->H1; E = q->E; Hmax = q->Hmax;
slen = q->slen, qlen8 = slen * 8;
memset(E, 0, slen * sizeof(__m128i));
memset(H0, 0, slen * sizeof(__m128i));
memset(Hmax, 0, slen * sizeof(__m128i));
// the core loop
for (i = 0; i < tlen; ++i) {
int j, k, imax;
__m128i e, h, f = zero, max = zero, *S = q->qp + target[i] * slen; // s is the 1st score vector
h = _mm_load_si128(H0 + slen - 1); // h={2,5,8,11,14,17,-1,-1} in the above example
h = _mm_slli_si128(h, 2);
for (j = 0; LIKELY(j < slen); ++j) {
h = _mm_adds_epi16(h, *S++);
e = _mm_load_si128(E + j);
h = _mm_max_epi16(h, e);
h = _mm_max_epi16(h, f);
max = _mm_max_epi16(max, h);
_mm_store_si128(H1 + j, h);
h = _mm_subs_epu16(h, gapoe);
e = _mm_subs_epu16(e, gape);
e = _mm_max_epi16(e, h);
_mm_store_si128(E + j, e);
f = _mm_subs_epu16(f, gape);
f = _mm_max_epi16(f, h);
h = _mm_load_si128(H0 + j);
}
for (k = 0; LIKELY(k < 8); ++k) {
f = _mm_slli_si128(f, 2);
for (j = 0; LIKELY(j < slen); ++j) {
h = _mm_load_si128(H1 + j);
h = _mm_max_epi16(h, f);
_mm_store_si128(H1 + j, h);
h = _mm_subs_epu16(h, gapoe);
f = _mm_subs_epu16(f, gape);
if(UNLIKELY(!_mm_movemask_epi8(_mm_cmpgt_epi16(f, h)))) goto end_loop_i16;
}
}
end_loop_i16:
__max_8(imax, max);
if (imax >= gmax) {
gmax = imax; *te = i;
memcpy(Hmax, H1, slen * sizeof(__m128i));
}
S = H1; H1 = H0; H0 = S;
}
for (i = 0, H8 = (uint16_t*)Hmax; i < qlen8; ++i)
if ((int)H8[i] == gmax) *qe = i / 8 + i % 8 * slen;
return gmax;
}
+12 -4
View File
@@ -1,6 +1,12 @@
#include <pthread.h> #include <pthread.h>
#include <stdlib.h> #include <stdlib.h>
#include <limits.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)
#endif
/************ /************
* kt_for() * * kt_for() *
@@ -52,12 +58,13 @@ void kt_for(int n_threads, void (*func)(void*,long,int), void *data, long n)
kt_for_t t; kt_for_t t;
pthread_t *tid; pthread_t *tid;
t.func = func, t.data = data, t.n_threads = n_threads, t.n = n; t.func = func, t.data = data, t.n_threads = n_threads, t.n = n;
t.w = (ktf_worker_t*)alloca(n_threads * sizeof(ktf_worker_t)); t.w = (ktf_worker_t*)calloc(n_threads, sizeof(ktf_worker_t));
tid = (pthread_t*)alloca(n_threads * sizeof(pthread_t)); tid = (pthread_t*)calloc(n_threads, sizeof(pthread_t));
for (i = 0; i < n_threads; ++i) for (i = 0; i < n_threads; ++i)
t.w[i].t = &t, t.w[i].i = i; t.w[i].t = &t, t.w[i].i = i;
for (i = 0; i < n_threads; ++i) pthread_create(&tid[i], 0, ktf_worker, &t.w[i]); for (i = 0; i < n_threads; ++i) pthread_create(&tid[i], 0, ktf_worker, &t.w[i]);
for (i = 0; i < n_threads; ++i) pthread_join(tid[i], 0); for (i = 0; i < n_threads; ++i) pthread_join(tid[i], 0);
free(tid); free(t.w);
} else { } else {
long j; long j;
for (j = 0; j < n; ++j) func(data, j, 0); for (j = 0; j < n; ++j) func(data, j, 0);
@@ -135,16 +142,17 @@ void kt_pipeline(int n_threads, void *(*func)(void*, int, void*), void *shared_d
pthread_mutex_init(&aux.mutex, 0); pthread_mutex_init(&aux.mutex, 0);
pthread_cond_init(&aux.cv, 0); pthread_cond_init(&aux.cv, 0);
aux.workers = (ktp_worker_t*)alloca(n_threads * sizeof(ktp_worker_t)); aux.workers = (ktp_worker_t*)calloc(n_threads, sizeof(ktp_worker_t));
for (i = 0; i < n_threads; ++i) { for (i = 0; i < n_threads; ++i) {
ktp_worker_t *w = &aux.workers[i]; ktp_worker_t *w = &aux.workers[i];
w->step = 0; w->pl = &aux; w->data = 0; w->step = 0; w->pl = &aux; w->data = 0;
w->index = aux.index++; w->index = aux.index++;
} }
tid = (pthread_t*)alloca(n_threads * sizeof(pthread_t)); tid = (pthread_t*)calloc(n_threads, sizeof(pthread_t));
for (i = 0; i < n_threads; ++i) pthread_create(&tid[i], 0, ktp_worker, &aux.workers[i]); for (i = 0; i < n_threads; ++i) pthread_create(&tid[i], 0, ktp_worker, &aux.workers[i]);
for (i = 0; i < n_threads; ++i) pthread_join(tid[i], 0); for (i = 0; i < n_threads; ++i) pthread_join(tid[i], 0);
free(tid); free(aux.workers);
pthread_mutex_destroy(&aux.mutex); pthread_mutex_destroy(&aux.mutex);
pthread_cond_destroy(&aux.cv); pthread_cond_destroy(&aux.cv);
+284 -142
View File
@@ -1,35 +1,69 @@
#include <getopt.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/resource.h>
#include <sys/time.h>
#include "bseq.h" #include "bseq.h"
#include "minimap.h" #include "minimap.h"
#include "mmpriv.h" #include "mmpriv.h"
#ifdef HAVE_GETOPT
#include <getopt.h>
#else
#include "getopt.h"
#endif
#define MM_VERSION "2.0-r191-dirty" #define MM_VERSION "2.11-r815-dirty"
#ifdef __linux__
#include <sys/resource.h>
#include <sys/time.h>
void liftrlimit() void liftrlimit()
{ {
#ifdef __linux__
struct rlimit r; struct rlimit r;
getrlimit(RLIMIT_AS, &r); getrlimit(RLIMIT_AS, &r);
r.rlim_cur = r.rlim_max; r.rlim_cur = r.rlim_max;
setrlimit(RLIMIT_AS, &r); setrlimit(RLIMIT_AS, &r);
#endif
} }
#else
void liftrlimit() {}
#endif
static struct option long_options[] = { static struct option long_options[] = {
{ "bucket-bits", required_argument, 0, 0 }, { "bucket-bits", required_argument, 0, 0 },
{ "mb-size", required_argument, 0, 'K' }, { "mb-size", required_argument, 0, 'K' },
{ "int-rname", no_argument, 0, 0 }, { "seed", required_argument, 0, 0 },
{ "no-kalloc", no_argument, 0, 0 }, { "no-kalloc", no_argument, 0, 0 },
{ "print-qname", no_argument, 0, 0 }, { "print-qname", no_argument, 0, 0 },
{ "no-self", no_argument, 0, 0 }, { "no-self", no_argument, 0, 'D' },
{ "print-seed", no_argument, 0, 0 }, { "print-seeds", no_argument, 0, 0 },
{ "max-chain-skip", required_argument, 0, 0 }, { "max-chain-skip", required_argument, 0, 0 },
{ "min-dp-len", required_argument, 0, 0 }, { "min-dp-len", required_argument, 0, 0 },
{ "print-aln-seq", no_argument, 0, 0 },
{ "splice", no_argument, 0, 0 },
{ "cost-non-gt-ag", required_argument, 0, 'C' },
{ "no-long-join", no_argument, 0, 0 },
{ "sr", no_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", 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' }, { "version", no_argument, 0, 'V' },
{ "min-count", required_argument, 0, 'n' }, { "min-count", required_argument, 0, 'n' },
{ "min-chain-score",required_argument, 0, 'm' }, { "min-chain-score",required_argument, 0, 'm' },
@@ -39,188 +73,296 @@ static struct option long_options[] = {
{ 0, 0, 0, 0} { 0, 0, 0, 0}
}; };
static inline int64_t mm_parse_num(const char *str)
{
double x;
char *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[]) int main(int argc, char *argv[])
{ {
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_mapopt_t opt;
int i, c, k = 15, w = -1, bucket_bits = MM_IDX_DEF_B, n_threads = 3, keep_name = 1, is_idx, is_hpc = 0, long_idx, idx_par_set = 0; mm_idxopt_t ipt;
int minibatch_size = 200000000; int i, c, n_threads = 3, n_parts, long_idx;
uint64_t batch_size = 4000000000ULL; char *fnw = 0, *rg = 0, *s;
mm_bseq_file_t *fp = 0; FILE *fp_help = stderr;
char *fnw = 0, *s; mm_idx_reader_t *idx_rdr;
FILE *fpr = 0, *fpw = 0; mm_idx_t *mi;
mm_verbose = 3;
liftrlimit(); liftrlimit();
mm_realtime0 = realtime(); mm_realtime0 = realtime();
mm_mapopt_init(&opt); mm_set_opt(0, &ipt, &opt);
while ((c = getopt_long(argc, argv, "aw:k:K:t:r:f:Vv:g:I:d:XT:s:x:Hcp:M:n:z:A:B:O:E:m:N:Q", long_options, &long_idx)) >= 0) { while ((c = getopt_long(argc, argv, opt_str, long_options, &long_idx)) >= 0) // apply option -x/preset first
if (c == 'w') w = atoi(optarg), idx_par_set = 1; if (c == 'x') {
else if (c == 'k') k = atoi(optarg), idx_par_set = 1; if (mm_set_opt(optarg, &ipt, &opt) < 0) {
else if (c == 'H') is_hpc = 1, idx_par_set = 1; fprintf(stderr, "[ERROR] unknown preset '%s'\n", optarg);
return 1;
}
break;
}
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.flag |= MM_I_HPC;
else if (c == 'd') fnw = optarg; // the above are indexing related options, except -I else if (c == 'd') fnw = optarg; // the above are indexing related options, except -I
else if (c == 'r') opt.bw = atoi(optarg); else if (c == 'r') opt.bw = (int)mm_parse_num(optarg);
else if (c == 'f') opt.mid_occ_frac = atof(optarg);
else if (c == 't') n_threads = atoi(optarg); else if (c == 't') n_threads = atoi(optarg);
else if (c == 'v') mm_verbose = atoi(optarg); else if (c == 'v') mm_verbose = atoi(optarg);
else if (c == 'g') opt.max_gap = atoi(optarg); else if (c == 'g') opt.max_gap = (int)mm_parse_num(optarg);
else if (c == 'G') mm_mapopt_max_intron_len(&opt, (int)mm_parse_num(optarg));
else if (c == 'F') opt.max_frag_len = (int)mm_parse_num(optarg);
else if (c == 'N') opt.best_n = atoi(optarg); else if (c == 'N') opt.best_n = atoi(optarg);
else if (c == 'p') opt.pri_ratio = atof(optarg); else if (c == 'p') opt.pri_ratio = atof(optarg);
else if (c == 'M') opt.mask_level = atof(optarg); else if (c == 'M') opt.mask_level = atof(optarg);
else if (c == 'c') opt.flag |= MM_F_CIGAR; 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 == 'a') opt.flag |= MM_F_OUT_SAM | MM_F_CIGAR;
else if (c == 'Q') opt.flag |= MM_F_NO_QUAL; 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 == 'T') opt.sdust_thres = atoi(optarg);
else if (c == 'n') opt.min_cnt = atoi(optarg); else if (c == 'n') opt.min_cnt = atoi(optarg);
else if (c == 'm') opt.min_chain_score = atoi(optarg); else if (c == 'm') opt.min_chain_score = atoi(optarg);
else if (c == 'A') opt.a = atoi(optarg); else if (c == 'A') opt.a = atoi(optarg);
else if (c == 'B') opt.b = 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 == 's') opt.min_dp_max = atoi(optarg);
else if (c == 0 && long_idx == 0) bucket_bits = atoi(optarg); // --bucket-bits else if (c == 'C') opt.noncan = atoi(optarg);
else if (c == 0 && long_idx == 2) keep_name = 0; // --int-rname else if (c == 'I') ipt.batch_size = mm_parse_num(optarg);
else if (c == 'K') opt.mini_batch_size = (int)mm_parse_num(optarg);
else if (c == 'R') rg = optarg;
else if (c == 'h') fp_help = stdout;
else if (c == '2') opt.flag |= MM_F_2_IO_THREADS;
else if (c == 0 && long_idx == 0) ipt.bucket_bits = atoi(optarg); // --bucket-bits
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 == 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 == 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 == 6) mm_dbg_flag |= MM_DBG_PRINT_QNAME | MM_DBG_PRINT_SEED; // --print-seed
else if (c == 0 && long_idx == 7) opt.max_chain_skip = atoi(optarg); // --max-chain-skip 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 == 8) opt.min_ksw_len = atoi(optarg); // --min-dp-len
else if (c == 'V') { 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
yes_or_no(&opt, MM_F_FRAG_MODE, long_idx, optarg, 1);
} else if (c == 0 && long_idx == 15) { // --secondary
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) {
opt.flag &= ~MM_F_OUT_CS_LONG;
} else if (strcmp(optarg, "long") == 0) {
opt.flag |= MM_F_OUT_CS_LONG;
} else if (strcmp(optarg, "none") == 0) {
opt.flag &= ~MM_F_OUT_CS;
} else if (mm_verbose >= 2) {
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
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)
fprintf(stderr, "[WARNING]\033[1;31m option -S is deprecated and may be removed in future. Please use --cs=long instead.\033[0m\n");
} else if (c == 'V') {
puts(MM_VERSION); puts(MM_VERSION);
return 0; return 0;
} else if (c == 'f') {
double x;
char *p;
x = strtod(optarg, &p);
if (x < 1.0) opt.mid_occ_frac = x, opt.mid_occ = 0;
else opt.mid_occ = (int)(x + .499);
if (*p == ',') opt.max_occ = (int)(strtod(p+1, &p) + .499);
} else if (c == 'u') {
if (*optarg == 'b') opt.flag |= MM_F_SPLICE_FOR|MM_F_SPLICE_REV; // both strands
else if (*optarg == 'f') opt.flag |= MM_F_SPLICE_FOR, opt.flag &= ~MM_F_SPLICE_REV; // match GT-AG
else if (*optarg == 'r') opt.flag |= MM_F_SPLICE_REV, opt.flag &= ~MM_F_SPLICE_FOR; // match CT-AC (reverse complement of GT-AG)
else if (*optarg == 'n') opt.flag &= ~(MM_F_SPLICE_FOR|MM_F_SPLICE_REV); // don't try to match the GT-AG signal
else {
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') { } else if (c == 'O') {
opt.q = opt.q2 = strtol(optarg, &s, 10); opt.q = opt.q2 = strtol(optarg, &s, 10);
if (*s == ',') opt.q2 = strtol(s + 1, &s, 10); if (*s == ',') opt.q2 = strtol(s + 1, &s, 10);
} else if (c == 'E') { } else if (c == 'E') {
opt.e = opt.e2 = strtol(optarg, &s, 10); opt.e = opt.e2 = strtol(optarg, &s, 10);
if (*s == ',') opt.e2 = strtol(s + 1, &s, 10); if (*s == ',') opt.e2 = strtol(s + 1, &s, 10);
} else if (c == 'I' || c == 'K') { }
double x; }
char *p; if ((opt.flag & MM_F_SPLICE) && (opt.flag & MM_F_FRAG_MODE)) {
x = strtod(optarg, &p); fprintf(stderr, "[ERROR]\033[1;31m --splice and --frag should not be specified at the same time.\033[0m\n");
if (*p == 'G' || *p == 'g') x *= 1e9; return 1;
else if (*p == 'M' || *p == 'm') x *= 1e6; }
else if (*p == 'K' || *p == 'k') x *= 1e3; if (!fnw && !(opt.flag&MM_F_CIGAR))
if (c == 'I') batch_size = (uint64_t)(x + .499); ipt.flag |= MM_I_NO_SEQ;
else minibatch_size = (uint64_t)(x + .499); if (mm_check_opt(&ipt, &opt) < 0)
} else if (c == 'x') { return 1;
if (strcmp(optarg, "ava-ont") == 0) {
opt.flag |= MM_F_AVA | MM_F_NO_SELF; if (argc == optind || fp_help == stdout) {
opt.min_chain_score = 100, opt.pri_ratio = 0.0f, opt.max_gap = 10000, opt.max_chain_skip = 25; fprintf(fp_help, "Usage: minimap2 [options] <target.fa>|<target.idx> [query.fa] [...]\n");
minibatch_size = 500000000; fprintf(fp_help, "Options:\n");
k = 15, w = 5; fprintf(fp_help, " Indexing:\n");
} else if (strcmp(optarg, "ava-pb") == 0) { fprintf(fp_help, " -H use homopolymer-compressed k-mer (preferrable for PacBio)\n");
opt.flag |= MM_F_AVA | MM_F_NO_SELF; fprintf(fp_help, " -k INT k-mer size (no larger than 28) [%d]\n", ipt.k);
opt.min_chain_score = 100, opt.pri_ratio = 0.0f, opt.max_gap = 10000, opt.max_chain_skip = 25; fprintf(fp_help, " -w INT minizer window size [%d]\n", ipt.w);
minibatch_size = 500000000; fprintf(fp_help, " -I NUM split index for every ~NUM input bases [4G]\n");
is_hpc = 1, k = 19, w = 5; fprintf(fp_help, " -d FILE dump index to FILE []\n");
} else if (strcmp(optarg, "map10k") == 0 || strcmp(optarg, "map-pb") == 0) { fprintf(fp_help, " Mapping:\n");
is_hpc = 1, k = 19; fprintf(fp_help, " -f FLOAT filter out top FLOAT fraction of repetitive minimizers [%g]\n", opt.mid_occ_frac);
} else if (strcmp(optarg, "map-ont") == 0) { fprintf(fp_help, " -g NUM stop chain enlongation if there are no minimizers in INT-bp [%d]\n", opt.max_gap);
is_hpc = 0, k = 15; fprintf(fp_help, " -G NUM max intron length (effective with -xsplice; changing -r) [200k]\n");
} else if (strcmp(optarg, "asm5") == 0) { fprintf(fp_help, " -F NUM max fragment length (effective with -xsr or in the fragment mode) [800]\n");
k = 19, w = 19; fprintf(fp_help, " -r NUM bandwidth used in chaining and DP-based alignment [%d]\n", opt.bw);
opt.a = 1, opt.b = 19, opt.q = 39, opt.q2 = 81, opt.e = 3, opt.e2 = 1, opt.zdrop = 200; fprintf(fp_help, " -n INT minimal number of minimizers on a chain [%d]\n", opt.min_cnt);
opt.min_dp_max = 200; fprintf(fp_help, " -m INT minimal chaining score (matching bases minus log gap penalty) [%d]\n", opt.min_chain_score);
} else if (strcmp(optarg, "asm10") == 0) { // fprintf(fp_help, " -T INT SDUST threshold; 0 to disable SDUST [%d]\n", opt.sdust_thres); // TODO: this option is never used; might be buggy
k = 19, w = 19; fprintf(fp_help, " -X skip self and dual mappings (for the all-vs-all mode)\n");
opt.a = 1, opt.b = 9, opt.q = 16, opt.q2 = 41, opt.e = 2, opt.e2 = 1, opt.zdrop = 200; fprintf(fp_help, " -p FLOAT min secondary-to-primary score ratio [%g]\n", opt.pri_ratio);
opt.min_dp_max = 200; fprintf(fp_help, " -N INT retain at most INT secondary alignments [%d]\n", opt.best_n);
fprintf(fp_help, " Alignment:\n");
fprintf(fp_help, " -A INT matching score [%d]\n", opt.a);
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[,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");
fprintf(fp_help, " -a output in the SAM format (PAF by default)\n");
fprintf(fp_help, " -Q don't output base quality in SAM\n");
fprintf(fp_help, " -L write CIGAR with >65535 ops at the CG tag\n");
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; 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]);
return 1;
}
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 { } else {
fprintf(stderr, "[E::%s] unknown preset '%s'\n", __func__, optarg); mm_write_sam_hdr(0, rg, MM_VERSION, argc, argv);
return 1; 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 (w < 0) w = (int)(.6666667 * k + .499);
if (argc == optind) {
fprintf(stderr, "Usage: minimap2 [options] <target.fa>|<target.idx> [query.fa] [...]\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " Indexing:\n");
fprintf(stderr, " -H use homopolymer-compressed k-mer\n");
fprintf(stderr, " -k INT k-mer size (no larger than 28) [%d]\n", k);
fprintf(stderr, " -w INT minizer window size [{-k}*2/3]\n");
fprintf(stderr, " -I NUM split index for every ~NUM input bases [4G]\n");
fprintf(stderr, " -d FILE dump index to FILE []\n");
fprintf(stderr, " Mapping:\n");
fprintf(stderr, " -f FLOAT filter out top FLOAT fraction of repetitive minimizers [%g]\n", opt.mid_occ_frac);
fprintf(stderr, " -g INT stop chain enlongation if there are no minimizers in INT-bp [%d]\n", opt.max_gap);
fprintf(stderr, " -r INT bandwidth used in chaining and DP-based alignment [%d]\n", opt.bw);
fprintf(stderr, " -n INT minimal number of minimizers on a chain [%d]\n", opt.min_cnt);
fprintf(stderr, " -m INT minimal chaining score (matching bases minus log gap penalty) [%d]\n", opt.min_chain_score);
// fprintf(stderr, " -T INT SDUST threshold; 0 to disable SDUST [%d]\n", opt.sdust_thres); // TODO: this option is never used; might be buggy
fprintf(stderr, " -X skip self and dual mappings (for the all-vs-all mode)\n");
fprintf(stderr, " -p FLOAT min secondary-to-primary score ratio [%g]\n", opt.pri_ratio);
fprintf(stderr, " -N INT retain at most INT secondary alignments [%d]\n", opt.best_n);
fprintf(stderr, " Alignment:\n");
fprintf(stderr, " -A INT matching score [%d]\n", opt.a);
fprintf(stderr, " -B INT mismatch penalty [%d]\n", opt.b);
fprintf(stderr, " -O INT[,INT] gap open penalty [%d,%d]\n", opt.q, opt.q2);
fprintf(stderr, " -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(stderr, " -z INT Z-drop score [%d]\n", opt.zdrop);
fprintf(stderr, " -s INT minimal peak DP alignment score [%d]\n", opt.min_dp_max);
fprintf(stderr, " Input/Output:\n");
fprintf(stderr, " -Q ignore base quality in the input\n");
fprintf(stderr, " -a output in the SAM format (PAF by default)\n");
fprintf(stderr, " -c output CIGAR in PAF\n");
fprintf(stderr, " -t INT number of threads [%d]\n", n_threads);
fprintf(stderr, " -K NUM minibatch size [200M]\n");
// fprintf(stderr, " -v INT verbose level [%d]\n", mm_verbose);
fprintf(stderr, " -V show version number\n");
fprintf(stderr, " Preset:\n");
fprintf(stderr, " -x STR preset (recommended to be applied before other options) []\n");
fprintf(stderr, " map10k/map-pb: -Hk19 (PacBio/ONT vs reference mapping)\n");
fprintf(stderr, " map-ont: -k15 (slightly more sensitive than 'map10k' for ONT vs reference)\n");
fprintf(stderr, " asm5: -k19 -w19 -A1 -B19 -O39,81 -E3,1 -s200 -z200 (asm to ref mapping; break at 5%% div.)\n");
fprintf(stderr, " asm10: -k19 -w19 -A1 -B9 -O16,41 -E2,1 -s200 -z200 (asm to ref mapping; break at 10%% div.)\n");
fprintf(stderr, " ava-pb: -Hk19 -w5 -Xp0 -m100 -g10000 -K500m --max-chain-skip 25 (PacBio read overlap)\n");
fprintf(stderr, " ava-ont: -k15 -w5 -Xp0 -m100 -g10000 -K500m --max-chain-skip 25 (ONT read overlap)\n");
fprintf(stderr, "\nSee `man ./minimap2.1' for detailed description of command-line options.\n");
return 1;
}
is_idx = mm_idx_is_idx(argv[optind]);
if (is_idx < 0) {
fprintf(stderr, "[E::%s] failed to open file '%s'\n", __func__, argv[optind]);
return 1;
}
if (is_idx) fpr = fopen(argv[optind], "rb");
else fp = mm_bseq_open(argv[optind]);
if (fnw) fpw = fopen(fnw, "wb");
for (;;) {
mm_idx_t *mi = 0;
if (fpr) {
mi = mm_idx_load(fpr);
if (idx_par_set && mm_verbose >= 2 && (mi->k != k || mi->w != w || mi->is_hpc != mi->is_hpc))
fprintf(stderr, "[W::%s::%.3f*%.2f] Indexing parameters on the command line (-k/-w/-H) overridden by parameters in the prebuilt index.\n",
__func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0));
} else if (!mm_bseq_eof(fp)) {
mi = mm_idx_gen(fp, w, k, bucket_bits, is_hpc, minibatch_size, n_threads, batch_size, keep_name);
}
if (mi == 0) break;
if (mm_verbose >= 3) if (mm_verbose >= 3)
fprintf(stderr, "[M::%s::%.3f*%.2f] loaded/built the index for %d target sequence(s)\n", fprintf(stderr, "[M::%s::%.3f*%.2f] loaded/built the index for %d target sequence(s)\n",
__func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0), mi->n_seq); __func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0), mi->n_seq);
if (fpw) {
mm_idx_dump(fpw, mi);
if (mm_verbose >= 3)
fprintf(stderr, "[M::%s::%.3f*%.2f] dumpped the (partial) index to disk\n", __func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0));
}
if (argc != optind + 1) mm_mapopt_update(&opt, mi); if (argc != optind + 1) mm_mapopt_update(&opt, mi);
if (mm_verbose >= 3) mm_idx_stat(mi); if (mm_verbose >= 3) mm_idx_stat(mi);
if (!(opt.flag & MM_F_FRAG_MODE)) {
for (i = optind + 1; i < argc; ++i) for (i = optind + 1; i < argc; ++i)
mm_map_file(mi, argv[i], &opt, n_threads, minibatch_size); mm_map_file(mi, argv[i], &opt, n_threads);
} else {
mm_map_file_frag(mi, argc - (optind + 1), (const char**)&argv[optind + 1], &opt, n_threads);
}
mm_idx_destroy(mi); mm_idx_destroy(mi);
} }
if (fpw) fclose(fpw); n_parts = idx_rdr->n_parts;
if (fpr) fclose(fpr); mm_idx_reader_close(idx_rdr);
if (fp) mm_bseq_close(fp);
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] Version: %s\n", __func__, MM_VERSION);
fprintf(stderr, "[M::%s] CMD:", __func__); fprintf(stderr, "[M::%s] CMD:", __func__);
for (i = 0; i < argc; ++i) for (i = 0; i < argc; ++i)
fprintf(stderr, " %s", argv[i]); fprintf(stderr, " %s", argv[i]);
fprintf(stderr, "\n[M::%s] Real time: %.3f sec; CPU: %.3f sec\n", __func__, realtime() - mm_realtime0, cputime()); fprintf(stderr, "\n[M::%s] Real time: %.3f sec; CPU: %.3f sec\n", __func__, realtime() - mm_realtime0, cputime());
}
return 0; return 0;
} }
+566 -241
View File
@@ -7,56 +7,11 @@
#include "sdust.h" #include "sdust.h"
#include "mmpriv.h" #include "mmpriv.h"
#include "bseq.h" #include "bseq.h"
#include "khash.h"
void mm_mapopt_init(mm_mapopt_t *opt)
{
memset(opt, 0, sizeof(mm_mapopt_t));
opt->max_occ_frac = 1e-5f;
opt->mid_occ_frac = 2e-4f;
opt->sdust_thres = 0;
opt->min_cnt = 3;
opt->min_chain_score = 40;
opt->bw = 500;
opt->max_gap = 5000;
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->min_dp_max = opt->min_chain_score;
opt->min_ksw_len = 200;
}
void mm_mapopt_update(mm_mapopt_t *opt, const mm_idx_t *mi)
{
opt->max_occ = mm_idx_cal_max_occ(mi, opt->max_occ_frac);
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; max_occ = %d\n", __func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0),
opt->mid_occ, opt->max_occ);
}
typedef struct {
uint32_t n:31, is_alloc:1;
uint32_t qpos;
union {
const uint64_t *cr;
uint64_t *r;
} x;
} mm_match_t;
struct mm_tbuf_s { struct mm_tbuf_s {
sdust_buf_t *sdb;
mm128_v mini;
void *km; void *km;
int rep_len, frag_gap;
}; };
mm_tbuf_t *mm_tbuf_init(void) mm_tbuf_t *mm_tbuf_init(void)
@@ -64,213 +19,371 @@ mm_tbuf_t *mm_tbuf_init(void)
mm_tbuf_t *b; mm_tbuf_t *b;
b = (mm_tbuf_t*)calloc(1, sizeof(mm_tbuf_t)); b = (mm_tbuf_t*)calloc(1, sizeof(mm_tbuf_t));
if (!(mm_dbg_flag & 1)) b->km = km_init(); if (!(mm_dbg_flag & 1)) b->km = km_init();
b->sdb = sdust_buf_init(b->km);
return b; return b;
} }
void mm_tbuf_destroy(mm_tbuf_t *b) void mm_tbuf_destroy(mm_tbuf_t *b)
{ {
if (b == 0) return; if (b == 0) return;
kfree(b->km, b->mini.a);
sdust_buf_destroy(b->sdb);
km_destroy(b->km); km_destroy(b->km);
free(b); free(b);
} }
static void mm_dust_minier(mm128_v *mini, 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; int n_dreg, j, k, u = 0;
const uint64_t *dreg; const uint64_t *dreg;
if (sdust_thres <= 0 || sdb == 0) return; 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); dreg = sdust_core((const uint8_t*)seq, l_seq, sdust_thres, 64, &n_dreg, sdb);
for (j = k = 0; j < mini->n; ++j) { // squeeze out minimizers that significantly overlap with LCRs for (j = k = 0; j < n; ++j) { // squeeze out minimizers that significantly overlap with LCRs
int32_t qpos = (uint32_t)mini->a[j].y>>1, span = mini->a[j].x&0xff; int32_t qpos = (uint32_t)a[j].y>>1, span = a[j].x&0xff;
int32_t s = qpos - (span - 1), e = s + span; int32_t s = qpos - (span - 1), e = s + span;
while (u < n_dreg && (uint32_t)dreg[u] <= s) ++u; while (u < n_dreg && (int32_t)dreg[u] <= s) ++u;
if (u < n_dreg && dreg[u]>>32 < e) { if (u < n_dreg && (int32_t)(dreg[u]>>32) < e) {
int v, l = 0; int v, l = 0;
for (v = u; v < n_dreg && dreg[v]>>32 < e; ++v) { // iterate over LCRs overlapping this minimizer for (v = u; v < n_dreg && (int32_t)(dreg[v]>>32) < e; ++v) { // iterate over LCRs overlapping this minimizer
int ss = s > dreg[v]>>32? s : dreg[v]>>32; int ss = s > (int32_t)(dreg[v]>>32)? s : dreg[v]>>32;
int ee = e < (uint32_t)dreg[v]? e : (uint32_t)dreg[v]; int ee = e < (int32_t)dreg[v]? e : (uint32_t)dreg[v];
l += ee - ss; l += ee - ss;
} }
if (l <= span>>1) mini->a[k++] = mini->a[j]; // keep the minimizer if less than half of it falls in masked region 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);
mini->n = k; return k; // the new size
}
#if 0
int mm_pair_thin_core(mm_tbuf_t *b, uint64_t x, int radius, int rel, int st0, int n, const uint64_t *z, uint64_v *a)
{
int i, st = st0, en = n, mid = en - 1;
while (st < en) {
uint64_t y;
mid = st + ((en - st) >> 1);
y = z[mid];
if (y < x && (x - y)>>1 > radius) st = mid + 1;
else if (y >= x && (y - x)>>1 > radius) en = mid;
else break;
}
if (st < en) {
for (en = mid + 1; en < n; ++en)
if (z[en] > x && (z[en] - x)>>1 > radius)
break;
for (st = mid - 1; st >= st0; --st)
if (z[st] < x && (x - z[st])>>1 > radius)
break;
++st;
for (i = st; i < en; ++i) {
uint64_t y = z[i];
if (((x ^ y) & 1) == rel) {
// printf("* %d,%d\n", (uint32_t)x>>1, (uint32_t)y>>1);
kv_push(uint64_t, b->km, *a, y);
}
}
return en;
} else return st < n && z[st] < x? st + 1 : en;
} }
void mm_pair_thin(mm_tbuf_t *b, int radius, mm_match_t *m1, mm_match_t *m2) 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)
{ {
mm_match_t *m[2]; int i, n, sum = 0;
const uint64_t *z[2]; mv->n = 0;
uint64_v a[2]; for (i = n = 0; i < n_segs; ++i) {
int i, n[2], k[2], u = 0, rel = (m1->qpos ^ m2->qpos) & 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
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;
}
}
m[0] = m1, m[1] = m2; #include "ksort.h"
for (i = 0; i < 2; ++i) { #define heap_lt(a, b) ((a).x > (b).x)
n[i] = m[i]->n; KSORT_INIT(heap, mm128_t, heap_lt)
z[i] = m[i]->x.cr;
k[i] = 0; typedef struct {
kv_init(a[i]); uint32_t n;
kv_resize(uint64_t, b->km, a[i], 256); uint32_t q_pos, q_span;
} uint32_t seg_id:31, is_tandem:1;
while (k[0] < n[0] && k[1] < n[1]) { const uint64_t *cr;
//printf("%d; %d,%d\n", u, k[0], k[1]); } mm_match_t;
int v = u^1, dist = (int)(m[v]->qpos>>1) - (int)(m[u]->qpos>>1);
uint64_t x = z[u][k[u]]; 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 uori = (x ^ m[u]->qpos) & 1, last;
int64_t tpos = x>>1 & 0x7fffffff;
tpos = uori == 0? tpos + dist : tpos - dist;
if (tpos < 0) tpos = 0;
x = x>>32<<32 | tpos<<1 | (x&1);
last = a[v].n;
k[v] = mm_pair_thin_core(b, x, radius, rel, k[v], n[v], z[v], &a[v]);
if (a[v].n > last) kv_push(uint64_t, b->km, a[u], z[u][k[u]]);
++k[u];
u ^= 1;
}
for (i = 0; i < 2; ++i)
m[i]->n = a[i].n, m[i]->x.r = a[i].a, m[i]->is_alloc = 1;
// printf("%d,%d; %d,%d\n", m[0]->qpos>>1, m[1]->qpos>>1, m[0]->n, m[1]->n);
}
#endif
mm_reg1_t *mm_map_frag(const mm_mapopt_t *opt, const mm_idx_t *mi, mm_tbuf_t *b, uint32_t m_st, uint32_t m_en, const char *qname, int qlen, const char *seq, int *n_regs)
{ {
int i, n = m_en - m_st, j, n_u; int rep_st = 0, rep_en = 0, n_m;
int64_t n_a; size_t i;
uint64_t *u; 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;
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;
} 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;
*_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; mm_match_t *m;
mm128_t *a; mm128_t *a;
mm_reg1_t *regs; 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));
// convert to local representation for (i = 0, *n_a = 0; i < n_m; ++i) {
m = (mm_match_t*)kmalloc(b->km, n * sizeof(mm_match_t));
for (i = 0; i < n; ++i) {
int t;
mm128_t *p = &b->mini.a[i + m_st];
m[i].is_alloc = 0;
m[i].qpos = (uint32_t)p->y;
m[i].x.cr = mm_idx_get(mi, p->x>>8, &t);
m[i].n = t;
}
#if 0
int last = -1, last2 = -1;
// pair k-mer thinning
for (i = 0; i < n; ++i) {
if (m[i].n >= opt->mid_occ && m[i].n < opt->max_occ) {
if (last2 < 0) last2 = i;
if (last < 0 || m[last].n < m[i].n) last = i;
if (last >= 0 && (m[last].qpos>>1) + (m[last].span>>1) <= m[i].qpos>>1) {
mm_pair_thin(b, opt->bw, &m[last], &m[i]);
last2 = last = -1;
} else if (last2 >= 0 && (m[last2].qpos>>1) + (m[last2].span>>1) <= m[i].qpos>>1) {
mm_pair_thin(b, opt->bw, &m[last2], &m[i]);
last2 = last = -1;
}
}
}
#endif
// fill the _a_ array
for (i = 0, n_a = 0; i < n; ++i) // find the length of a[]
if (m[i].n < opt->mid_occ) n_a += m[i].n;
a = (mm128_t*)kmalloc(b->km, n_a * sizeof(mm128_t));
for (i = j = 0; i < n; ++i) {
mm128_t *p = &b->mini.a[i + m_st];
mm_match_t *q = &m[i]; mm_match_t *q = &m[i];
const uint64_t *r = q->x.cr; const uint64_t *r = q->cr;
int k, q_span = p->x & 0xff; uint32_t k;
if (q->n >= opt->mid_occ) continue;
for (k = 0; k < q->n; ++k) { for (k = 0; k < q->n; ++k) {
const char *tname = mi->seq[r[k]>>32].name; int32_t is_self, rpos = (uint32_t)r[k] >> 1;
int32_t rpos = (uint32_t)r[k] >> 1;
mm128_t *p; mm128_t *p;
if (qname && (opt->flag&MM_F_NO_SELF) && strcmp(qname, tname) == 0 && rpos == (q->qpos>>1)) // avoid the diagonal if (skip_seed(opt->flag, r[k], q, qname, qlen, mi, &is_self)) continue;
continue; p = &a[(*n_a)++];
if (qname && (opt->flag&MM_F_AVA) && strcmp(qname, tname) > 0) // all-vs-all mode: map once if ((r[k]&1) == (q->q_pos&1)) { // forward strand
continue; p->x = (r[k]&0xffffffff00000000ULL) | rpos;
p = &a[j++]; p->y = (uint64_t)q->q_span << 32 | q->q_pos >> 1;
if ((r[k]&1) == (q->qpos&1)) { // forward strand
p->x = (r[k]&0xffffffff00000000ULL) | (uint32_t)r[k]>>1;
p->y = (uint64_t)q_span << 32 | q->qpos >> 1;
} else { // reverse strand } else { // reverse strand
p->x = 1ULL<<63 | (r[k]&0xffffffff00000000ULL) | (uint32_t)r[k]>>1; 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->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;
} }
n_a = j;
radix_sort_128x(a, a + n_a);
for (i = 0; i < n; ++i)
if (m[i].is_alloc) kfree(b->km, m[i].x.r);
kfree(b->km, m);
if (mm_dbg_flag & MM_DBG_PRINT_SEED) 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)
for (i = 0; i < n_a; ++i) {
fprintf(stderr, "SD\t%s\t%d\t%c\t%d\t%d\n", mi->seq[a[i].x<<1>>33].name, (int32_t)a[i].x, "+-"[a[i].x>>63], (int32_t)a[i].y, (int32_t)(a[i].y>>32&0xff)); 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);
n_u = mm_chain_dp(opt->max_gap, opt->bw, opt->max_chain_skip, opt->min_cnt, opt->min_chain_score, n_a, a, &u, b->km); if (n_segs <= 1) mm_select_sub(km, opt->pri_ratio, mi->k*2, opt->best_n, n_regs, regs);
regs = mm_gen_regs(b->km, qlen, n_u, u, a); 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);
*n_regs = n_u; if (!(opt->flag & (MM_F_SPLICE|MM_F_SR|MM_F_NO_LJOIN))) // long join not working well without primary chains
if (!(opt->flag & MM_F_AVA)) { // don't choose primary mapping(s) for read overlap mm_join_long(km, opt, qlen, n_regs, regs, a);
mm_set_parent(b->km, opt->mask_level, *n_regs, regs);
mm_select_sub(b->km, opt->mask_level, opt->pri_ratio, opt->best_n, n_regs, regs);
mm_join_long(b->km, opt, qlen, n_regs, regs, a); // TODO: this can be applied to all-vs-all in principle
} }
if (opt->flag & MM_F_CIGAR) { }
regs = mm_align_skeleton(b->km, opt, mi, qlen, seq, n_regs, regs, a); // this calls mm_filter_regs()
if (!(opt->flag & MM_F_AVA)) { 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)
mm_set_parent(b->km, opt->mask_level, *n_regs, regs); {
mm_select_sub(b->km, opt->mask_level, opt->pri_ratio, opt->best_n, n_regs, regs); if (!(opt->flag & MM_F_CIGAR)) return regs;
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); mm_set_sam_pri(*n_regs, regs);
} }
}
mm_set_mapq(*n_regs, regs);
// free
kfree(b->km, a);
kfree(b->km, u);
return regs; return regs;
} }
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 *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, 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, *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;
if (qlen_sum == 0 || n_segs <= 0 || n_segs > MM_MAX_SEG) return;
hash = qname? __ac_X31_hash_string(qname) : 0;
hash ^= __ac_Wang_hash(qlen_sum) + __ac_Wang_hash(opt->seed);
hash = __ac_Wang_hash(hash);
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);
for (i = 0; i < n_a; ++i)
fprintf(stderr, "SD\t%s\t%d\t%c\t%d\t%d\t%d\n", mi->seq[a[i].x<<1>>33].name, (int32_t)a[i].x, "+-"[a[i].x>>63], (int32_t)a[i].y, (int32_t)(a[i].y>>32&0xff),
i == 0? 0 : ((int32_t)a[i].y - (int32_t)a[i-1].y) - ((int32_t)a[i].x - (int32_t)a[i-1].x));
}
// set max chaining gap on the query and the reference sequence
if (is_sr)
max_chain_gap_qry = qlen_sum > opt->max_gap? qlen_sum : opt->max_gap;
else max_chain_gap_qry = opt->max_gap;
if (opt->max_gap_ref > 0) {
max_chain_gap_ref = opt->max_gap_ref; // always honor mm_mapopt_t::max_gap_ref if set
} else if (opt->max_frag_len > 0) {
max_chain_gap_ref = opt->max_frag_len - qlen_sum;
if (max_chain_gap_ref < opt->max_gap) max_chain_gap_ref = opt->max_gap;
} else max_chain_gap_ref = opt->max_gap;
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);
if (opt->max_occ > opt->mid_occ && rep_len > 0) {
int rechain = 0;
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 < (int)(u[i]>>32)) max = u[i]>>32, max_i = i, max_off = off;
off += (uint32_t)u[i];
}
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)
rechain = 1;
} else rechain = 1;
if (rechain) { // redo chaining with a higher max_occ threshold
kfree(b->km, a);
kfree(b->km, u);
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);
if (mm_dbg_flag & MM_DBG_PRINT_SEED)
for (j = 0; j < n_regs0; ++j)
for (i = regs0[j].as; i < regs0[j].as + regs0[j].cnt; ++i)
fprintf(stderr, "CN\t%d\t%s\t%d\t%c\t%d\t%d\t%d\n", j, mi->seq[a[i].x<<1>>33].name, (int32_t)a[i].x, "+-"[a[i].x>>63], (int32_t)a[i].y, (int32_t)(a[i].y>>32&0xff),
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], &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;
seg = mm_seg_gen(b->km, hash, n_segs, qlens, n_regs0, regs0, n_regs, regs, a); // split fragment chain to separate segment chains
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], &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_reg1_t *regs;
b->mini.n = 0; mm_map_frag(mi, 1, &qlen, &seq, n_regs, &regs, b, opt, qname);
mm_sketch(b->km, seq, l_seq, mi->w, mi->k, 0, mi->is_hpc, &b->mini);
if (opt->sdust_thres > 0)
mm_dust_minier(&b->mini, l_seq, seq, opt->sdust_thres, b->sdb);
regs = mm_map_frag(opt, mi, b, 0, b->mini.n, qname, l_seq, seq, n_regs);
return regs; return regs;
} }
@@ -279,39 +392,137 @@ mm_reg1_t *mm_map(const mm_idx_t *mi, int l_seq, const char *seq, int *n_regs, m
**************************/ **************************/
typedef struct { typedef struct {
int mini_batch_size, n_processed, n_threads; int mini_batch_size, n_processed, n_threads, n_fp;
const mm_mapopt_t *opt; const mm_mapopt_t *opt;
mm_bseq_file_t *fp; mm_bseq_file_t **fp;
const mm_idx_t *mi; const mm_idx_t *mi;
kstring_t str; kstring_t str;
int n_parts;
uint32_t *rid_shift;
FILE *fp_split, **fp_parts;
} pipeline_t; } pipeline_t;
typedef struct { typedef struct {
const pipeline_t *p; const pipeline_t *p;
int n_seq; int n_seq, n_frag;
mm_bseq1_t *seq; mm_bseq1_t *seq;
int *n_reg; int *n_reg, *seg_off, *n_seg, *rep_len, *frag_gap;
mm_reg1_t **reg; mm_reg1_t **reg;
mm_tbuf_t **buf; mm_tbuf_t **buf;
} step_t; } step_t;
static void worker_for(void *_data, long i, int tid) // kt_for() callback static void worker_for(void *_data, long i, int tid) // kt_for() callback
{ {
step_t *step = (step_t*)_data; step_t *s = (step_t*)_data;
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) if (mm_dbg_flag & MM_DBG_PRINT_QNAME)
fprintf(stderr, "QR\t%s\t%d\n", step->seq[i].name, tid); fprintf(stderr, "QR\t%s\t%d\t%d\n", s->seq[off].name, tid, s->seq[off].l_seq);
step->reg[i] = mm_map(step->p->mi, step->seq[i].l_seq, step->seq[i].seq, &step->n_reg[i], step->buf[tid], step->p->opt, step->seq[i].name); 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;
}
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], &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, &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)))) {
int k, t;
mm_revcomp_bseq(&s->seq[off + j]);
for (k = 0; k < s->n_reg[off + j]; ++k) {
mm_reg1_t *r = &s->reg[off + j][k];
t = r->qs;
r->qs = qlens[j] - r->qe;
r->qe = qlens[j] - t;
r->rev = !r->rev;
}
}
}
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) static void *worker_pipeline(void *shared, int step, void *in)
{ {
int i, j; int i, j, k;
pipeline_t *p = (pipeline_t*)shared; pipeline_t *p = (pipeline_t*)shared;
if (step == 0) { // step 0: read sequences 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_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; step_t *s;
s = (step_t*)calloc(1, sizeof(step_t)); s = (step_t*)calloc(1, sizeof(step_t));
s->seq = mm_bseq_read(p->fp, p->mini_batch_size, with_qual, &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) { if (s->seq) {
s->p = p; s->p = p;
for (i = 0; i < s->n_seq; ++i) for (i = 0; i < s->n_seq; ++i)
@@ -319,36 +530,76 @@ static void *worker_pipeline(void *shared, int step, void *in)
s->buf = (mm_tbuf_t**)calloc(p->n_threads, sizeof(mm_tbuf_t*)); s->buf = (mm_tbuf_t**)calloc(p->n_threads, sizeof(mm_tbuf_t*));
for (i = 0; i < p->n_threads; ++i) for (i = 0; i < p->n_threads; ++i)
s->buf[i] = mm_tbuf_init(); s->buf[i] = mm_tbuf_init();
s->n_reg = (int*)calloc(s->n_seq, sizeof(int)); 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*)); 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)) {
s->n_seg[s->n_frag] = i - j;
s->seg_off[s->n_frag++] = j;
j = i;
}
return s; return s;
} else free(s); } else free(s);
} else if (step == 1) { // step 1: map } else if (step == 1) { // step 1: map
kt_for(p->n_threads, worker_for, in, ((step_t*)in)->n_seq); 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; return in;
} else if (step == 2) { // step 2: output } else if (step == 2) { // step 2: output
void *km = 0;
step_t *s = (step_t*)in; step_t *s = (step_t*)in;
const mm_idx_t *mi = p->mi; const mm_idx_t *mi = p->mi;
for (i = 0; i < p->n_threads; ++i) mm_tbuf_destroy(s->buf[i]); for (i = 0; i < p->n_threads; ++i) mm_tbuf_destroy(s->buf[i]);
free(s->buf); free(s->buf);
for (i = 0; i < s->n_seq; ++i) { if ((p->opt->flag & MM_F_OUT_CS) && !(mm_dbg_flag & MM_DBG_NO_KALLOC)) km = km_init();
for (k = 0; k < s->n_frag; ++k) {
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]; mm_bseq1_t *t = &s->seq[i];
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) { for (j = 0; j < s->n_reg[i]; ++j) {
mm_reg1_t *r = &s->reg[i][j]; mm_reg1_t *r = &s->reg[i][j];
if (p->opt->flag & MM_F_OUT_SAM) mm_write_sam(&p->str, mi, t, r); mm_err_fwrite(r, sizeof(mm_reg1_t), 1, p->fp_split);
else mm_write_paf(&p->str, mi, t, r); if (p->opt->flag & MM_F_CIGAR) {
puts(p->str.s); mm_err_fwrite(&r->p->capacity, 4, 1, p->fp_split);
free(r->p); mm_err_fwrite(r->p, r->p->capacity, 4, p->fp_split);
} }
if (s->n_reg[i] == 0 && (p->opt->flag & MM_F_OUT_SAM)) {
mm_write_sam(&p->str, 0, t, 0);
puts(p->str.s);
} }
} 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, -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, 0, 0, p->opt->flag);
mm_err_puts(p->str.s);
}
}
for (i = seg_st; i < seg_en; ++i) {
for (j = 0; j < s->n_reg[i]; ++j) free(s->reg[i][j].p);
free(s->reg[i]); free(s->reg[i]);
free(s->seq[i].seq); free(s->seq[i].name); free(s->seq[i].seq); free(s->seq[i].name);
if (s->seq[i].qual) free(s->seq[i].qual); if (s->seq[i].qual) free(s->seq[i].qual);
} }
free(s->reg); free(s->n_reg); free(s->seq); }
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) 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); fprintf(stderr, "[M::%s::%.3f*%.2f] mapped %d sequences\n", __func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0), s->n_seq);
free(s); free(s);
@@ -356,21 +607,95 @@ static void *worker_pipeline(void *shared, int step, void *in)
return 0; return 0;
} }
int mm_map_file(const mm_idx_t *idx, const char *fn, const mm_mapopt_t *opt, int n_threads, int mini_batch_size) 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, pl_threads;
pipeline_t pl; pipeline_t pl;
if (n_segs < 1) return -1;
memset(&pl, 0, sizeof(pipeline_t)); memset(&pl, 0, sizeof(pipeline_t));
pl.fp = mm_bseq_open(fn); pl.n_fp = n_segs;
pl.fp = open_bseqs(pl.n_fp, fn);
if (pl.fp == 0) return -1; if (pl.fp == 0) return -1;
pl.opt = opt, pl.mi = idx; pl.opt = opt, pl.mi = idx;
pl.n_threads = n_threads, pl.mini_batch_size = mini_batch_size; pl.n_threads = n_threads > 1? n_threads : 1;
if (opt->flag & MM_F_OUT_SAM) { pl.mini_batch_size = opt->mini_batch_size;
uint32_t i; if (opt->split_prefix)
for (i = 0; i < idx->n_seq; ++i) pl.fp_split = mm_split_init(opt->split_prefix, idx);
printf("@SQ\tSN:%s\tLN:%d\n", idx->seq[i].name, idx->seq[i].len); 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);
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;
}
int mm_map_file(const mm_idx_t *idx, const char *fn, const mm_mapopt_t *opt, int n_threads)
{
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;
} }
kt_pipeline(n_threads == 1? 1 : 2, worker_pipeline, &pl, 3); 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); free(pl.str.s);
mm_bseq_close(pl.fp); 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; return 0;
} }
+278 -69
View File
@@ -5,35 +5,52 @@
#include <stdio.h> #include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
#define MM_IDX_DEF_B 14 #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_F_NO_SELF 0x01 #define MM_I_HPC 0x1
#define MM_F_AVA 0x02 #define MM_I_NO_SEQ 0x2
#define MM_F_CIGAR 0x04 #define MM_I_NO_NAME 0x4
#define MM_F_OUT_SAM 0x08
#define MM_F_NO_QUAL 0x10
#define MM_IDX_MAGIC "MMI\2" #define MM_IDX_MAGIC "MMI\2"
#define MM_MAX_SEG 255
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
typedef struct { // emulate 128-bit integers and arrays
uint64_t x, y; typedef struct { uint64_t x, y; } mm128_t;
} mm128_t;
typedef struct { size_t n, m; mm128_t *a; } mm128_v; typedef struct { size_t n, m; mm128_t *a; } mm128_v;
typedef struct { size_t n, m; uint64_t *a; } uint64_v;
typedef struct { size_t n, m; uint32_t *a; } uint32_v;
typedef struct {
mm128_v a; // (minimizer, position) array
int32_t n; // size of the _p_ array
uint64_t *p; // position array for minimizers appearing >1 times
void *h; // hash table indexing _p_ and minimizers appearing once
} mm_idx_bucket_t;
// minimap2 index
typedef struct { typedef struct {
char *name; // name of the db sequence char *name; // name of the db sequence
uint64_t offset; // offset in mm_idx_t::S uint64_t offset; // offset in mm_idx_t::S
@@ -41,101 +58,293 @@ typedef struct {
} mm_idx_seq_t; } mm_idx_seq_t;
typedef struct { typedef struct {
int32_t b, w, k, is_hpc; int32_t b, w, k, flag;
uint32_t n_seq; // number of reference sequences uint32_t n_seq; // number of reference sequences
int32_t index;
mm_idx_seq_t *seq; // sequence name, length and offset mm_idx_seq_t *seq; // sequence name, length and offset
uint32_t *S; // 4-bit packed sequence uint32_t *S; // 4-bit packed sequence
mm_idx_bucket_t *B; // index struct mm_idx_bucket_s *B; // index (hidden)
void *km, *h;
} mm_idx_t; } mm_idx_t;
// minimap2 alignment
typedef struct { typedef struct {
uint32_t capacity; uint32_t capacity; // the capacity of cigar[]
int32_t dp_score, dp_max, dp_max2; int32_t dp_score, dp_max, dp_max2; // DP score; score of the max-scoring segment; score of the best alternate mappings
uint32_t blen; uint32_t n_ambi:30, trans_strand:2; // number of ambiguous bases; transcript strand: 0 for unknown, 1 for +, 2 for -
uint32_t n_diff, n_ambi; uint32_t n_cigar; // number of cigar operations in cigar[]
uint32_t n_cigar;
uint32_t cigar[]; uint32_t cigar[];
} mm_extra_t; } mm_extra_t;
typedef struct { typedef struct {
int32_t id; int32_t id; // ID for internal uses (see also parent below)
uint32_t cnt:31, rev:1; int32_t cnt; // number of minimizers; if on the reverse strand
uint32_t rid:31, rep:1; int32_t rid; // reference index; if this is an alignment from inversion rescue
int32_t score; int32_t score; // DP alignment score
int32_t qs, qe, rs, re; int32_t qs, qe, rs, re; // query start and end; reference start and end
int32_t parent, subsc; int32_t parent, subsc; // parent==id if primary; best alternate mapping score
int32_t as; int32_t as; // offset in the a[] array (for internal uses only)
int32_t fuzzy_mlen, fuzzy_blen; int32_t mlen, blen; // seeded exact match length; seeded alignment block length
uint32_t mapq:8, split:2, sam_pri:1, n_sub:21; // TODO: n_sub is not used for now 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_extra_t *p;
} mm_reg1_t; } mm_reg1_t;
// indexing and mapping options
typedef struct { typedef struct {
float max_occ_frac; short k, w, flag, bucket_bits;
float mid_occ_frac; int mini_batch_size;
uint64_t batch_size;
} mm_idxopt_t;
typedef struct {
int seed;
int sdust_thres; // score threshold for SDUST; 0 to disable int sdust_thres; // score threshold for SDUST; 0 to disable
int flag; // see MM_F_* macros int flag; // see MM_F_* macros
int bw; // bandwidth int bw; // bandwidth
int max_gap; // break a chain if there are no minimizers in a max_gap window int max_gap, max_gap_ref; // break a chain if there are no minimizers in a max_gap window
int max_frag_len;
int max_chain_skip; int max_chain_skip;
int min_cnt; int min_cnt; // min number of minimizers on each chain
int min_chain_score; int min_chain_score; // min chaining score
float mask_level; float mask_level;
float pri_ratio; float pri_ratio;
int best_n; int best_n; // top best_n chains are subjected to DP alignment
int max_join_long, max_join_short; int max_join_long, max_join_short;
int min_join_flank_sc; 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 a, b, q, e, q2, e2; // matching score, mismatch, gap-open and gap-ext penalties
int zdrop; int sc_ambi; // score when one or both bases are "N"
int min_dp_max; int noncan; // cost of non-canonical splicing sites
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 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 max_occ; int pe_ori, pe_bonus;
int mid_occ;
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; } mm_mapopt_t;
extern int mm_verbose, mm_dbg_flag; // index reader
extern double mm_realtime0; typedef struct {
int is_idx, n_parts;
int64_t idx_size;
mm_idxopt_t opt;
FILE *fp_out;
union {
struct mm_bseq_file_s *seq;
FILE *idx;
} fp;
} mm_idx_reader_t;
struct mm_tbuf_s; // memory buffer for thread-local storage during mapping
typedef struct mm_tbuf_s mm_tbuf_t; typedef struct mm_tbuf_s mm_tbuf_t;
struct mm_bseq_file_s; // global variables
extern int mm_verbose, mm_dbg_flag; // verbose level: 0 for no info, 1 for error, 2 for warning, 3 for message (default); debugging flag
extern double mm_realtime0; // wall-clock timer
#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) * Set default or preset parameters
*
* @param preset NULL to set all parameters as default; otherwise apply preset to affected parameters
* @param io pointer to indexing parameters
* @param mo pointer to mapping parameters
*
* @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);
// compute minimizers /**
void mm_sketch(void *km, const char *str, int len, int w, int k, uint32_t rid, int is_hpc, mm128_v *p); * Update mm_mapopt_t::mid_occ via mm_mapopt_t::mid_occ_frac
*
* If mm_mapopt_t::mid_occ is 0, this function sets it to a number such that no
* more than mm_mapopt_t::mid_occ_frac of minimizers in the index have a higher
* occurrence.
*
* @param opt mapping parameters
* @param mi minimap2 index
*/
void mm_mapopt_update(mm_mapopt_t *opt, const mm_idx_t *mi);
// minimizer indexing void mm_mapopt_max_intron_len(mm_mapopt_t *opt, int max_intron_len);
mm_idx_t *mm_idx_init(int w, int k, int b, int is_hpc);
void mm_idx_destroy(mm_idx_t *mi);
mm_idx_t *mm_idx_gen(struct mm_bseq_file_s *fp, int w, int k, int b, int is_hpc, int mini_batch_size, int n_threads, uint64_t batch_size, int keep_name);
uint32_t mm_idx_cal_max_occ(const mm_idx_t *mi, float f);
void mm_idx_stat(const mm_idx_t *idx);
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);
mm_idx_t *mm_idx_build(const char *fn, int w, int k, int is_hpc, int n_threads); /**
int mm_idx_is_idx(const char *fn); * Initialize an index reader
*
* @param fn index or fasta/fastq file name (this function tests the file type)
* @param opt indexing parameters
* @param fn_out if not NULL, write built index to this file
*
* @return an index reader on success; NULL if fail to open _fn_
*/
mm_idx_reader_t *mm_idx_reader_open(const char *fn, const mm_idxopt_t *opt, const char *fn_out);
// minimizer index I/O /**
void mm_idx_dump(FILE *fp, const mm_idx_t *mi); * Read/build an index
*
* If the input file is an index file, this function reads one part of the
* index and returns. If the input file is a sequence file (fasta or fastq),
* this function constructs the index for about mm_idxopt_t::batch_size bases.
* Importantly, for a huge collection of sequences, this function may only
* return an index for part of sequences. It needs to be repeatedly called
* to traverse the entire index/sequence file.
*
* @param r index reader
* @param n_threads number of threads for constructing index
*
* @return an index on success; NULL if reaching the end of the input file
*/
mm_idx_t *mm_idx_reader_read(mm_idx_reader_t *r, int n_threads);
/**
* Destroy/deallocate an index reader
*
* @param r index reader
*/
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); mm_idx_t *mm_idx_load(FILE *fp);
// mapping /**
void mm_mapopt_init(mm_mapopt_t *opt); * Append an index (or one part of a full index) to file
void mm_mapopt_update(mm_mapopt_t *opt, const mm_idx_t *mi); *
* @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
*
* @param mi minimap2 index
*/
void mm_idx_stat(const mm_idx_t *idx);
/**
* Destroy/deallocate an index
*
* @param r minimap2 index
*/
void mm_idx_destroy(mm_idx_t *mi);
/**
* Initialize a thread-local buffer for mapping
*
* Each mapping thread requires a buffer specific to the thread (see mm_map()
* below). The primary purpose of this buffer is to reduce frequent heap
* allocations across threads. A buffer shall not be used by two or more
* threads.
*
* @return pointer to a thread-local buffer
*/
mm_tbuf_t *mm_tbuf_init(void); mm_tbuf_t *mm_tbuf_init(void);
/**
* Destroy/deallocate a thread-local buffer for mapping
*
* @param b the buffer
*/
void mm_tbuf_destroy(mm_tbuf_t *b); void mm_tbuf_destroy(mm_tbuf_t *b);
/**
* Align a query sequence against an index
*
* This function possibly finds multiple alignments of the query sequence.
* The returned array and the mm_reg1_t::p field of each element are allocated
* with malloc().
*
* @param mi minimap2 index
* @param l_seq length of the query sequence
* @param seq the query sequence
* @param n_regs number of hits (out)
* @param b thread-local buffer; two mm_map() calls shall not use one buffer at the same time!
* @param opt mapping parameters
* @param name query name, used for all-vs-all overlapping and debugging
*
* @return an array of hits which need to be deallocated with free() together
* with mm_reg1_t::p of each element. The size is written to _n_regs_.
*/
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); 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);
int mm_map_file(const mm_idx_t *idx, const char *fn, const mm_mapopt_t *opt, int n_threads, int tbatch_size); 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
*
* @param idx minimap2 index
* @param fn fasta/fastq file name
* @param opt mapping parameters
* @param n_threads number of threads
*
* @return 0 on success; -1 if _fn_ can't be read
*/
int mm_map_file(const mm_idx_t *idx, const char *fn, const mm_mapopt_t *opt, int n_threads);
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 flag, int n_threads);
#ifdef __cplusplus #ifdef __cplusplus
} }
+323 -63
View File
@@ -1,4 +1,4 @@
.TH minimap2 1 "19 July 2017" "minimap2-2.0-r190-dirty" "Bioinformatics tools" .TH minimap2 1 "20 June 2018" "minimap2-2.11 (r797)" "Bioinformatics tools"
.SH NAME .SH NAME
.PP .PP
minimap2 - mapping and alignment between collections of DNA sequences 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 may be ending with k/K/m/M/g/G. NB: mapping quality is incorrect given a
multi-part index. multi-part index.
.TP .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 .BI -d \ FILE
Save the minimizer index of Save the minimizer index of
.I target.fa .I target.fa
@@ -115,18 +123,35 @@ provided as the target sequences, options
will be effectively overridden by the options stored in the index file. will be effectively overridden by the options stored in the index file.
.SS Mapping options .SS Mapping options
.TP 10 .TP 10
.BI -f \ FLOAT .BI -f \ FLOAT | INT1 [, INT2 ]
Ignore top If fraction, ignore top
.I FLOAT .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 .TP
.BI -g \ INT .BI -g \ INT
Stop chain enlongation if there are no minimizers in Stop chain enlongation if there are no minimizers within
.IR INT -bp .IR INT -bp
[10000]. [10000].
.TP .TP
.BI -r \ INT .BI -r \ INT
Bandwidth used in chaining and DP-based alignment [1000]. This option Bandwidth used in chaining and DP-based alignment [500]. This option
approximately controls the maximum gap size. approximately controls the maximum gap size.
.TP .TP
.BI -n \ INT .BI -n \ INT
@@ -137,25 +162,45 @@ number of minimizers [3]
.BI -m \ INT .BI -m \ INT
Discard chains with chaining score Discard chains with chaining score
.RI < INT .RI < INT
[40]. Chaining score equals the approximate number of matching bases (exact if [40]. Chaining score equals the approximate number of matching bases minus a
not using concave gap penalty. It is computed with dynamic programming.
.BR -H ) .TP
minus base-2 logarithm gap penalty. It is computed with dynamic programming. .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 .TP
.B -X .B -X
Perform all-vs-all mapping. In this mode, if the query sequence name is Equivalent to
lexicographically larger than the target sequence name, the hits between them .RB ' -DP
will be suppressed; if the query sequence name is the same as the target name, .BR --dual = no
diagonal minimizer hits will also be suppressed. .BR --no-long-join '.
Primarily used for all-vs-all read overlapping.
.TP .TP
.BI -p \ FLOAT .BI -p \ FLOAT
Minimal secondary-to-primary score ratio to output secondary mappings [0.8]. Minimal secondary-to-primary score ratio to output secondary mappings [0.8].
Between two chains overlaping over half of the shorter chain (controled by Between two chains overlaping over half of the shorter chain (controlled by
.BR --mask-level ), .BR --mask-level ),
the chain with a lower score is secondary to the chain with a higher score. the chain with a lower score is secondary to the chain with a higher score.
If the ratio of the scores is below If the ratio of the scores is below
.IR FLOAT , .IR FLOAT ,
the secondary chain will not be outputted or extended with DP alignment later. the secondary chain will not be outputted or extended with DP alignment later.
This option has no effect when
.B -X
is applied.
.TP .TP
.BI -N \ INT .BI -N \ INT
Output at most Output at most
@@ -164,6 +209,23 @@ secondary alignments [5]. This option has no effect when
.B -X .B -X
is applied. is applied.
.TP .TP
.BI -G \ NUM
Maximum gap on the reference (effective with
.BR -xsplice / --splice ).
This option also changes the chaining and alignment band width to
.IR NUM .
Increasing this option slows down spliced alignment. [200k]
.TP
.BI -F \ NUM
Maximum fragment length (aka insert size; effective with
.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 .BI --max-chain-skip \ INT
A heuristics that stops chaining early [50]. Minimap2 uses dynamic programming A heuristics that stops chaining early [50]. Minimap2 uses dynamic programming
for chaining. The time complexity is quadratic in the number of seeds. This for chaining. The time complexity is quadratic in the number of seeds. This
@@ -171,6 +233,42 @@ option makes minimap2 exits the inner loop if it repeatedly sees seeds already
on chains. Set on chains. Set
.I INT .I INT
to a large number to switch off this heurstics. to a large number to switch off this heurstics.
.TP
.B --no-long-join
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
.B --sr
Enable short-read alignment heuristics. In the short-read mode, minimap2
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
.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 .SS Alignment options
.TP 10 .TP 10
.BI -A \ INT .BI -A \ INT
@@ -190,29 +288,133 @@ Gap extension penalty [2,1]. A gap of length
.I k .I k
costs costs
.RI min{ O1 + k * E1 , O2 + k * E2 }. .RI min{ O1 + k * E1 , O2 + k * E2 }.
In the splice mode, the second gap penalties are not used.
.TP .TP
.BI -z \ INT .BI -C \ INT
Break an alignment if the running score drops too quickly along the diagonal of Cost for a non-canonical GT-AG splicing (effective with
the DP matrix (diagonal X-drop, or Z-drop) [400]. Increasing the value improves .BR --splice )
the contiguity of the alignment at the cost of poor alignment in the middle [0]
(e.g. caused by a long inversion). .TP
.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 .TP
.BI -s \ INT .BI -s \ INT
Minimal peak DP alignment score to output [40]. The peak score is computed from Minimal peak DP alignment score to output [40]. The peak score is computed from
the final CIGAR. It is the score of the max scoring segment in the alignment the final CIGAR. It is the score of the max scoring segment in the alignment
and may be different from the total alignment score. and may be different from the total alignment score.
.TP
.BI -u \ CHAR
How to find canonical splicing sites GT-AG -
.BR f :
transcript strand;
.BR b :
both strands;
.BR n :
no attempt to match GT-AG [n]
.TP
.BI --end-bonus \ INT
Score bonus when alignment extends to the end of the query sequence [0].
.TP
.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 [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
.BR --splice .
However, the SIRV control does not honor this trend
(only ~60%). This option reduces accuracy. If you are benchmarking minimap2
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 .SS Input/output options
.TP 10 .TP 10
.B -Q
Ignore base quality in the input file.
.TP
.B -a .B -a
Generate CIGAR and output alignments in the SAM format. Minimap2 outputs in PAF Generate CIGAR and output alignments in the SAM format. Minimap2 outputs in PAF
by default. by default.
.TP .TP
.B -Q
Ignore base quality in the input file.
.TP
.B -L
Write CIGAR with >65535 operators at the CG tag. Older tools are unable to
convert alignments with >65535 CIGAR ops to BAM. This option makes minimap2 SAM
compatible with older tools. Newer tools recognizes this tag and reconstruct
the real CIGAR in memory.
.TP
.BI -R \ STR
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 .B -c
Generate CIGAR. In PAF, the CIGAR is written to the `cg' custom tag. Generate CIGAR. In PAF, the CIGAR is written to the `cg' custom tag.
.TP .TP
.BI --cs[= STR ]
Output the
.B cs
tag.
.I STR
can be either
.I short
or
.IR long .
If no
.I STR
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
.BI --seed \ INT
Integer seed for randomizing equally best hits. Minimap2 hashes
.I INT
and read name when choosing between equally best hits. [11]
.TP
.BI -t \ INT .BI -t \ INT
Number of threads [3]. Minimap2 uses at most three threads when indexing target Number of threads [3]. Minimap2 uses at most three threads when indexing target
sequences, and uses up to sequences, and uses up to
@@ -220,21 +422,25 @@ sequences, and uses up to
threads when mapping (the extra thread is for I/O, which is frequently idle and threads when mapping (the extra thread is for I/O, which is frequently idle and
takes little CPU time). takes little CPU time).
.TP .TP
.B -2
Use two I/O threads during mapping. By default, minimap2 uses one I/O thread.
When I/O is slow (e.g. piping to gzip, or reading from a slow pipe), the I/O
thread may become the bottleneck. Apply this option to use one thread for input
and another thread for output, at the cost of increased peak RAM.
.TP
.BI -K \ NUM .BI -K \ NUM
Number of bases loaded into memory to process in a mini-batch [200M]. Number of bases loaded into memory to process in a mini-batch [500M].
Similar to option Similar to option
.BR -I , .BR -I ,
K/M/G/k/m/g suffix is accepted. A large K/M/G/k/m/g suffix is accepted. A large
.I NUM .I NUM
helps load balancing in the multi-threading mode, at the cost of increased helps load balancing in the multi-threading mode, at the cost of increased
memory. Preset memory.
.B ava-pb
and
.B ava-ont
use
.BR -K500m .
.TP .TP
.B -V .BR --secondary = yes | no
Whether to output secondary alignments [yes]
.TP
.B --version
Print version number to stdout Print version number to stdout
.SS Preset options .SS Preset options
.TP 10 .TP 10
@@ -249,37 +455,73 @@ are:
.RS .RS
.TP 8 .TP 8
.B map-pb .B map-pb
PacBio/Oxford Nanopore read to reference mapping (-Hk19) PacBio/Oxford Nanopore read to reference mapping
.TP .RB ( -Hk19 )
.B map10k
The same as
.B map-pb
(-Hk19)
.TP .TP
.B map-ont .B map-ont
Slightly more sensitive for Oxford Nanopore to reference mapping (-k15). For Slightly more sensitive for Oxford Nanopore to reference mapping
PacBio reads, HPC minimizers consistently leads to faster performance and more .RB ( -k15 ).
sensitive results in comparison to normal minimizers. For Oxford Nanopore data, For PacBio reads, HPC minimizers consistently leads to faster performance and
normal minimizers are better, though not much. The effectiveness of HPC is more sensitive results in comparison to normal minimizers. For Oxford Nanopore
determined by the sequencing error mode. data, normal minimizers are better, though not much. The effectiveness of HPC
is determined by the sequencing error mode.
.TP .TP
.B asm5 .B asm5
Long assembly to reference mapping (-k19 -w19 -A1 -B19 -O39,81 -E3,1 -s200 -z200). Long assembly to reference mapping
.RB ( -k19
.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 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%. divergence. Only use this preset if the average divergence is far below 5%.
.TP .TP
.B asm10 .B asm10
Long assembly to reference mapping (-k19 -w19 -A1 -B9 -O16,41 -E2,1 -s200 -z200). Up Long assembly to reference mapping
to 10% sequence divergence. .RB ( -k19
.TP 8 .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 .B ava-pb
PacBio all-vs-all overlap mapping (-Hk19 -w5 -Xp0 -m100 -K500m -g10000 --max-chain-skip 25) PacBio all-vs-all overlap mapping
.TP 8 .RB ( -Hk19
.B -Xw5 -m100 -g10000 --max-chain-skip
.BR 25 ).
.TP
.B ava-ont .B ava-ont
Oxford Nanopore all-vs-all overlap mapping (-k15 -w5 -Xp0 -m100 -K500m -g10000 Oxford Nanopore all-vs-all overlap mapping
--max-chain-skip 25). Similarly, the major difference from .RB ( -k15
.B -Xw5 -m100 -g10000 -r2000 --max-chain-skip
.BR 25 ).
Similarly, the major difference from
.B ava-pb .B ava-pb
is that this preset is not using HPC minimizers. is that this preset is not using HPC minimizers.
.TP
.B splice
Long-read spliced alignment
.RB ( -k15
.B -w5 --splice -g2000 -G200k -A1 -B2 -O2,32 -E1,0 -C9 -z200 -ub
.BR --splice-flank=yes ).
In the splice mode, 1) long deletions are taken as introns and represented as
the
.RB ` N '
CIGAR operator; 2) long insertions are disabled; 3) deletion and insertion gap
costs are different during chaining; 4) the computation of the
.RB ` ms '
tag ignores introns to demote hits to pseudogenes.
.TP
.B sr
Short single-end reads without splicing
.RB ( -k21
.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 .RE
.SS Miscellaneous options .SS Miscellaneous options
.TP 10 .TP 10
@@ -292,7 +534,7 @@ multi-threading mode.
.B --print-qname .B --print-qname
Print query names to stderr, mostly to see which query is crashing minimap2. Print query names to stderr, mostly to see which query is crashing minimap2.
.TP .TP
.B --print-seed .B --print-seeds
Print seed positions to stderr, for debugging only. Print seed positions to stderr, for debugging only.
.SH OUTPUT FORMAT .SH OUTPUT FORMAT
.PP .PP
@@ -331,33 +573,51 @@ cb | cb | cb
r | c | l . r | c | l .
Tag Type Description Tag Type Description
_ _
tp A Type of aln: P/primary, S/secondary and I,i/inversion
cm i Number of minimizers on the chain cm i Number of minimizers on the chain
s1 i Chaining score s1 i Chaining score
s2 i Chaining score of the best secondary chain s2 i Chaining score of the best secondary chain
NM i Total number of mismatches and gaps in the alignment 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 AS i DP alignment score
ms i DP score of the max scoring segment in the alignment ms i DP score of the max scoring segment in the alignment
nn i Number of ambiguous bases 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) cg Z CIGAR string (only in PAF)
cs Z Difference string
dv f Approximate per-base sequence divergence
.TE
.PP
The
.B cs
tag encodes difference sequences in the short form or the entire query
.I AND
reference sequences in the long form. It consists of a series of operations:
.TS
center box;
cb | cb |cb
r | l | l .
Op Regex Description
_
= [ACGTN]+ Identical sequence (long form)
: [0-9]+ Identical sequence length
* [acgtn][acgtn] Substitution: ref to query
+ [acgtn]+ Insertion to the reference
- [acgtn]+ Deletion from the reference
~ [acgtn]{2}[0-9]+[acgtn]{2} Intron length and splice signal
.TE .TE
.SH LIMITATIONS .SH LIMITATIONS
.TP 2 .TP 2
* *
At the alignment phase, minimap2 performs global alignments between minimizer Minimap2 may produce suboptimal alignments through long low-complexity regions
hits. If the positions of these minimizer hits are incorrect, the final where seed positions may be suboptimal. This should not be a big concern
alignment may be suboptimal or unnecessarily fragmented. because even the optimal alignment may be wrong in such regions.
.TP .TP
* *
Minimap2 may produce poor alignments that may need post-filtering. We are still Minimap2 requires SSE2 or NEON instructions to compile. It is possible to add
exploring a reliable and consistent way to report good alignments. non-SSE2/NEON support, but it would make minimap2 slower by several times.
.TP
*
Minimap2 does not work well with Illumina short reads as of now.
.TP
*
Minimap2 requires SSE2 instructions to compile. It is possible to add
non-SSE2 support, but it would make minimap2 slower by several times.
.SH SEE ALSO .SH SEE ALSO
.PP .PP
miniasm(1), minimap(1), bwa(1). miniasm(1), minimap(1), bwa(1).
+135 -5
View File
@@ -1,19 +1,119 @@
#include <sys/resource.h> #include <stdlib.h>
#include <sys/time.h> #include "mmpriv.h"
#include "minimap.h"
int mm_verbose = 3; int mm_verbose = 1;
int mm_dbg_flag = 0; int mm_dbg_flag = 0;
double mm_realtime0; double mm_realtime0;
#if defined(WIN32) || defined(_WIN32)
#include <windows.h>
struct timezone
{
__int32 tz_minuteswest; /* minutes W of Greenwich */
int tz_dsttime; /* type of dst correction */
};
/*
* gettimeofday.c
* Win32 gettimeofday() replacement
* taken from PostgreSQL, according to
* https://stackoverflow.com/questions/1676036/what-should-i-use-to-replace-gettimeofday-on-windows
*
* src/port/gettimeofday.c
*
* Copyright (c) 2003 SRA, Inc.
* Copyright (c) 2003 SKC, Inc.
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose, without fee, and without a
* written agreement is hereby granted, provided that the above
* copyright notice and this paragraph and the following two
* paragraphs appear in all copies.
*
* IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
* INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
* LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
* DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS
* IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
* SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* FILETIME of Jan 1 1970 00:00:00. */
static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
/*
* timezone information is stored outside the kernel so tzp isn't used anymore.
*
* Note: this function is not for Win32 high precision timing purpose. See
* elapsed_time().
*/
int gettimeofday(struct timeval * tp, struct timezone *tzp)
{
FILETIME file_time;
SYSTEMTIME system_time;
ULARGE_INTEGER ularge;
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
ularge.LowPart = file_time.dwLowDateTime;
ularge.HighPart = file_time.dwHighDateTime;
tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
return 0;
}
// taken from https://stackoverflow.com/questions/5272470/c-get-cpu-usage-on-linux-and-windows
double cputime() double cputime()
{
HANDLE hProcess = GetCurrentProcess();
FILETIME ftCreation, ftExit, ftKernel, ftUser;
SYSTEMTIME stKernel;
SYSTEMTIME stUser;
GetProcessTimes(hProcess, &ftCreation, &ftExit, &ftKernel, &ftUser);
FileTimeToSystemTime(&ftKernel, &stKernel);
FileTimeToSystemTime(&ftUser, &stUser);
double kernelModeTime = ((stKernel.wHour * 60.) + stKernel.wMinute * 60.) + stKernel.wSecond * 1. + stKernel.wMilliseconds / 1000.;
double userModeTime = ((stUser.wHour * 60.) + stUser.wMinute * 60.) + stUser.wSecond * 1. + stUser.wMilliseconds / 1000.;
return kernelModeTime + userModeTime;
}
long peakrss(void) { return 0; }
#else
#include <sys/resource.h>
#include <sys/time.h>
double cputime(void)
{ {
struct rusage r; struct rusage r;
getrusage(RUSAGE_SELF, &r); 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); return r.ru_utime.tv_sec + r.ru_stime.tv_sec + 1e-6 * (r.ru_utime.tv_usec + r.ru_stime.tv_usec);
} }
double realtime() 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)
{ {
struct timeval tp; struct timeval tp;
struct timezone tzp; struct timezone tzp;
@@ -21,6 +121,36 @@ double realtime()
return tp.tv_sec + tp.tv_usec * 1e-6; 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" #include "ksort.h"
#define sort_key_128x(a) ((a).x) #define sort_key_128x(a) ((a).x)
+179
View File
@@ -0,0 +1,179 @@
## <a name="started"></a>Getting Started
```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
```
## Table of Contents
- [Getting Started](#started)
- [Introduction](#intro)
- [Evaluation](#eval)
- [Evaluating mapping accuracy with simulated reads](#mapeval)
- [Evaluating read overlap sensitivity](#oveval)
- [Calling Variants from Assemblies](#asmvar)
## <a name="intro"></a>Introduction
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
[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
-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) 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);
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]);
}
+2223
View File
File diff suppressed because it is too large Load Diff
+56 -9
View File
@@ -11,11 +11,26 @@
#define MM_DBG_NO_KALLOC 0x1 #define MM_DBG_NO_KALLOC 0x1
#define MM_DBG_PRINT_QNAME 0x2 #define MM_DBG_PRINT_QNAME 0x2
#define MM_DBG_PRINT_SEED 0x4 #define MM_DBG_PRINT_SEED 0x4
#define MM_DBG_PRINT_ALN_SEQ 0x8
#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))
#ifndef kroundup32 #ifndef kroundup32
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) #define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
#endif #endif
#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 #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@@ -28,28 +43,60 @@ typedef struct __kstring_t {
} kstring_t; } kstring_t;
#endif #endif
typedef struct {
int n_u, n_a;
uint64_t *u;
mm128_t *a;
} mm_seg_t;
double cputime(void); double cputime(void);
double realtime(void); double realtime(void);
long peakrss(void);
void radix_sort_128x(mm128_t *beg, mm128_t *end); void radix_sort_128x(mm128_t *beg, mm128_t *end);
void radix_sort_64(uint64_t *beg, uint64_t *end); void radix_sort_64(uint64_t *beg, uint64_t *end);
uint32_t ks_ksmall_uint32_t(size_t n, uint32_t arr[], size_t kk); uint32_t ks_ksmall_uint32_t(size_t n, uint32_t arr[], size_t kk);
void mm_write_paf(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r); void mm_sketch(void *km, const char *str, int len, int w, int k, uint32_t rid, int is_hpc, mm128_v *p);
void mm_write_sam(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r);
int mm_chain_dp(int max_dist, int bw, int max_skip, int min_cnt, int min_sc, int64_t n, mm128_t *a, uint64_t **_u, void *km); void mm_write_sam_hdr(const mm_idx_t *mi, const char *rg, const char *ver, int argc, char *argv[]);
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);
void mm_write_sam(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, const mm_reg1_t *r, int n_regs, const mm_reg1_t *regs);
void mm_write_sam2(kstring_t *s, const mm_idx_t *mi, const mm_bseq1_t *t, int seg_idx, int reg_idx, int n_seg, const int *n_regs, const mm_reg1_t *const* regs, void *km, int opt_flag);
void mm_idxopt_init(mm_idxopt_t *opt);
const uint64_t *mm_idx_get(const mm_idx_t *mi, uint64_t minier, int *n);
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, 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, int qlen, int n_u, uint64_t *u, 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); void mm_split_reg(mm_reg1_t *r, mm_reg1_t *r2, int n, int qlen, mm128_t *a);
void mm_sync_regs(void *km, int n_regs, mm_reg1_t *regs); void mm_sync_regs(void *km, int n_regs, mm_reg1_t *regs);
int mm_squeeze_a(void *km, int n_regs, mm_reg1_t *regs, mm128_t *a);
int mm_set_sam_pri(int n, mm_reg1_t *r); 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); 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 mask_level, float pri_ratio, int best_n, int *n_, mm_reg1_t *r); void mm_select_sub(void *km, float pri_ratio, int min_diff, int best_n, 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_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(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_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_hit_sort(void *km, int *n_regs, mm_reg1_t *r);
void mm_set_mapq(int n_regs, mm_reg1_t *regs); 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 #ifdef __cplusplus
} }
+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;
}
+177
View File
@@ -0,0 +1,177 @@
#include <stdlib.h>
#include <math.h>
#include "mmpriv.h"
#include "kvec.h"
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)
{
if (pri_ratio > 0.0f && *n_ > 0) {
int i, k, n = *n_, n_2nd = 0;
int max_dist = n_segs == 2? qlens[0] + qlens[1] + max_gap_ref : 0;
for (i = k = 0; i < n; ++i) {
int to_keep = 0;
if (r[i].parent == i) { // primary
to_keep = 1;
} else if (r[i].score + min_diff >= r[r[i].parent].score) {
to_keep = 1;
} else {
mm_reg1_t *p = &r[r[i].parent], *q = &r[i];
if (p->rev == q->rev && p->rid == q->rid && q->re - p->rs < max_dist && p->re - q->rs < max_dist) { // child and parent are close on the ref
if (q->score >= p->score * pri1)
to_keep = 1;
} else {
int is_par_both = (n_segs == 2 && p->qs < qlens[0] && p->qe > qlens[0]);
int is_chi_both = (n_segs == 2 && q->qs < qlens[0] && q->qe > qlens[0]);
if (is_chi_both || is_chi_both == is_par_both) {
if (q->score >= p->score * pri_ratio)
to_keep = 1;
} else { // the remaining case: is_chi_both == 0 && is_par_both == 1
if (q->score >= p->score * pri2)
to_keep = 1;
}
}
}
if (to_keep && r[i].parent != i) {
if (n_2nd++ >= best_n) to_keep = 0; // don't keep if there are too many secondary hits
}
if (to_keep) r[k++] = r[i];
else if (r[i].p) free(r[i].p);
}
if (k != n) mm_sync_regs(km, k, r); // removing hits requires sync()
*n_ = k;
}
}
void mm_set_pe_thru(const int *qlens, int *n_regs, mm_reg1_t **regs)
{
int s, i, n_pri[2], pri[2];
n_pri[0] = n_pri[1] = 0;
pri[0] = pri[1] = -1;
for (s = 0; s < 2; ++s)
for (i = 0; i < n_regs[s]; ++i)
if (regs[s][i].id == regs[s][i].parent)
++n_pri[s], pri[s] = i;
if (n_pri[0] == 1 && n_pri[1] == 1) {
mm_reg1_t *p = &regs[0][pri[0]];
mm_reg1_t *q = &regs[1][pri[1]];
if (p->rid == q->rid && p->rev == q->rev && abs(p->rs - q->rs) < 3 && abs(p->re - p->re) < 3
&& ((p->qs == 0 && qlens[1] - q->qe == 0) || (q->qs == 0 && qlens[0] - p->qe == 0)))
{
p->pe_thru = q->pe_thru = 1;
}
}
}
#include "ksort.h"
typedef struct {
int s, rev;
uint64_t key;
mm_reg1_t *r;
} pair_arr_t;
#define sort_key_pair(a) ((a).key)
KRADIX_SORT_INIT(pair, pair_arr_t, sort_key_pair, 8)
void mm_pair(void *km, int max_gap_ref, int pe_bonus, int sub_diff, int match_sc, const int *qlens, int *n_regs, mm_reg1_t **regs)
{
int i, j, s, n, last[2], dp_thres, segs = 0, max_idx[2];
int64_t max;
pair_arr_t *a;
kvec_t(uint64_t) sc = {0,0,0};
a = (pair_arr_t*)kmalloc(km, (n_regs[0] + n_regs[1]) * sizeof(pair_arr_t));
for (s = n = 0, dp_thres = 0; s < 2; ++s) {
int max = 0;
for (i = 0; i < n_regs[s]; ++i) {
a[n].s = s;
a[n].r = &regs[s][i];
a[n].rev = a[n].r->rev;
a[n].key = (uint64_t)a[n].r->rid << 32 | a[n].r->rs<<1 | (s^a[n].rev);
max = max > a[n].r->p->dp_max? max : a[n].r->p->dp_max;
++n;
segs |= 1<<s;
}
dp_thres += max;
}
if (segs != 3) {
kfree(km, a); // only one end is mapped
return;
}
dp_thres -= pe_bonus;
if (dp_thres < 0) dp_thres = 0;
radix_sort_pair(a, a + n);
max = -1;
max_idx[0] = max_idx[1] = -1;
last[0] = last[1] = -1;
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;
if (last[a[i].rev] < 0) continue;
r = a[i].r;
q = a[last[a[i].rev]].r;
if (r->rid != q->rid || r->rs - q->re > max_gap_ref) continue;
for (j = last[a[i].rev]; j >= 0; --j) {
int64_t score;
if (a[j].rev != a[i].rev || a[j].s == a[i].s) continue;
q = a[j].r;
if (r->rid != q->rid || r->rs - q->re > max_gap_ref) break;
if (r->p->dp_max + q->p->dp_max < dp_thres) continue;
score = (int64_t)(r->p->dp_max + q->p->dp_max) << 32 | (r->hash + q->hash);
if (score > max)
max = score, max_idx[a[j].s] = j, max_idx[a[i].s] = i;
kv_push(uint64_t, km, sc, score);
}
} else { // forward first read or reverse second read
last[a[i].rev] = i;
}
}
if (sc.n > 1)
radix_sort_64(sc.a, sc.a + sc.n);
if (sc.n > 0 && max > 0) { // found at least one pair
int n_sub = 0, mapq_pe;
mm_reg1_t *r[2];
r[0] = a[max_idx[0]].r, r[1] = a[max_idx[1]].r;
r[0]->proper_frag = r[1]->proper_frag = 1;
for (s = 0; s < 2; ++s) {
if (r[s]->id != r[s]->parent) { // then lift to primary and update parent
mm_reg1_t *p = &regs[s][r[s]->parent];
for (i = 0; i < n_regs[s]; ++i)
if (regs[s][i].parent == p->id)
regs[s][i].parent = r[s]->id;
p->mapq = 0;
}
if (!r[s]->sam_pri) { // then sync sam_pri
for (i = 0; i < n_regs[s]; ++i)
regs[s][i].sam_pri = 0;
r[s]->sam_pri = 1;
}
}
mapq_pe = r[0]->mapq > r[1]->mapq? r[0]->mapq : r[1]->mapq;
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;
mapq_pe_alt = (int)(6.02f * ((max>>32) - (sc.a[sc.n - 2]>>32)) / match_sc - 4.343f * logf(n_sub)); // n_sub > 0 because it counts the optimal, too
mapq_pe = mapq_pe < mapq_pe_alt? mapq_pe : mapq_pe_alt;
}
if (r[0]->mapq < mapq_pe) r[0]->mapq = (int)(.2f * r[0]->mapq + .8f * mapq_pe + .499f);
if (r[1]->mapq < mapq_pe) r[1]->mapq = (int)(.2f * r[1]->mapq + .8f * mapq_pe + .499f);
if (sc.n == 1) {
if (r[0]->mapq < 2) r[0]->mapq = 2;
if (r[1]->mapq < 2) r[1]->mapq = 2;
} 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;
}
}
kfree(km, a);
kfree(km, sc.a);
mm_set_pe_thru(qlens, n_regs, regs);
}
+170
View File
@@ -0,0 +1,170 @@
==============================
Mappy: Minimap2 Python Binding
==============================
Mappy provides a convenient interface to `minimap2
<https://github.com/lh3/minimap2>`_, a fast and accurate C program to align
genomic and transcribe nucleotide sequences.
Installation
------------
Mappy depends on `zlib <http://zlib.net>`_. It can be installed with `pip
<https://en.wikipedia.org/wiki/Pip_(package_manager)>`_:
.. code:: shell
pip install --user mappy
or from the minimap2 github repo (`Cython <http://cython.org>`_ required):
.. code:: shell
git clone https://github.com/lh3/minimap2
cd minimap2
python setup.py install
Usage
-----
The following Python script demonstrates the key functionality of mappy:
.. code:: python
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))
APIs
----
Mappy implements two classes and one global function.
Class mappy.Aligner
~~~~~~~~~~~~~~~~~~~
.. code:: python
mappy.Aligner(fn_idx_in, preset=None, ...)
This constructor accepts the following arguments:
* **fn_idx_in**: index or sequence file name. Minimap2 automatically tests the
file type. If a sequence file is provided, minimap2 builds an index. The
sequence file can be optionally gzip'd.
* **preset**: minimap2 preset. Currently, minimap2 supports the following
presets: **sr** for single-end short reads; **map-pb** for PacBio
read-to-reference mapping; **map-ont** for Oxford Nanopore read mapping;
**splice** for long-read spliced alignment; **asm5** for assembly-to-assembly
alignment; **asm10** for full genome alignment of closely related species. Note
that the Python module does not support all-vs-all read overlapping.
* **k**: k-mer length, no larger than 28
* **w**: minimizer window size, no larger than 255
* **min_cnt**: mininum number of minimizers on a chain
* **min_chain_score**: minimum chaing score
* **bw**: chaining and alignment band width
* **best_n**: max number of alignments to return
* **n_threads**: number of indexing threads; 3 by default
* **fn_idx_out**: name of file to which the index is written
.. code:: python
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. 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
~~~~~~~~~~~~~~~~~~~~~
This class describes an alignment. An object of this class has the following
properties:
* **ctg**: name of the reference sequence the query is mapped to
* **ctg_len**: total length of the reference sequence
* **r_st** and **r_en**: start and end positions on the reference
* **q_st** and **q_en**: start and end positions on the query
* **strand**: +1 if on the forward strand; -1 if on the reverse strand
* **mapq**: mapping quality
* **blen**: length of the alignment, including both alignment matches and gaps
but excluding ambiguous bases.
* **mlen**: length of the matching bases in the alignment, excluding ambiguous
base matches.
* **NM**: number of mismatches, gaps and ambiguous poistions in the alignment
* **trans_strand**: transcript strand. +1 if on the forward strand; -1 if on the
reverse strand; 0 if unknown
* **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
numbers give the length and the operator of each CIGAR operation.
An :code:`Alignment` object can be converted to a string with :code:`str()` in
the following format:
::
q_st q_en strand ctg ctg_len r_st r_en mlen blen mapq cg:Z:cigar_str
It is effectively the PAF format without the QueryName and QueryLength columns
(the first two columns in PAF).
Miscellaneous Functions
~~~~~~~~~~~~~~~~~~~~~~~
.. code:: python
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. 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`.
+140
View File
@@ -0,0 +1,140 @@
#ifndef CMAPPY_H
#define CMAPPY_H
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include "minimap.h"
#include "kseq.h"
KSEQ_DECLARE(gzFile)
typedef struct {
const char *ctg;
int32_t ctg_start, ctg_end;
int32_t qry_start, qry_end;
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;
static inline void mm_reg2hitpy(const mm_idx_t *mi, mm_reg1_t *r, mm_hitpy_t *h)
{
h->ctg = mi->seq[r->rid].name;
h->ctg_len = mi->seq[r->rid].len;
h->ctg_start = r->rs, h->ctg_end = r->re;
h->qry_start = r->qs, h->qry_end = r->qe;
h->strand = r->rev? -1 : 1;
h->mapq = r->mapq;
h->mlen = r->mlen;
h->blen = r->blen;
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;
}
static inline void mm_free_reg1(mm_reg1_t *r)
{
free(r->p);
}
static inline kseq_t *mm_fastx_open(const char *fn)
{
gzFile fp;
fp = fn && strcmp(fn, "-") != 0? gzopen(fn, "r") : gzdopen(fileno(stdin), "r");
return kseq_init(fp);
}
static inline void mm_fastx_close(kseq_t *ks)
{
gzFile fp;
fp = ks->f->f;
kseq_destroy(ks);
gzclose(fp);
}
static inline int mm_verbose_level(int v)
{
if (v >= 0) mm_verbose = v;
return mm_verbose;
}
static inline void mm_reset_timer(void)
{
extern double realtime(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
+128
View File
@@ -0,0 +1,128 @@
from libc.stdint cimport int8_t, uint8_t, int32_t, int64_t, uint32_t, uint64_t
cdef extern from "minimap.h":
#
# Options
#
ctypedef struct mm_idxopt_t:
short k, w, flag, bucket_bits
int mini_batch_size
uint64_t batch_size
ctypedef struct mm_mapopt_t:
int seed
int sdust_thres
int flag
int bw
int max_gap, max_gap_ref
int max_frag_len
int max_chain_skip
int min_cnt
int min_chain_score
float mask_level
float pri_ratio
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, 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
int mm_set_opt(char *preset, mm_idxopt_t *io, mm_mapopt_t *mo)
int mm_verbose
#
# Indexing
#
ctypedef struct mm_idx_seq_t:
char *name
uint64_t offset
uint32_t len
ctypedef struct mm_idx_bucket_t:
pass
ctypedef struct mm_idx_t:
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
mm_idx_reader_t *mm_idx_reader_open(const char *fn, const mm_idxopt_t *opt, const char *fn_out)
mm_idx_t *mm_idx_reader_read(mm_idx_reader_t *r, int n_threads)
void mm_idx_reader_close(mm_idx_reader_t *r)
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)
#
ctypedef struct mm_reg1_t:
pass
ctypedef struct mm_tbuf_t:
pass
mm_tbuf_t *mm_tbuf_init()
void mm_tbuf_destroy(mm_tbuf_t *b)
#
# Helper header (because it is hard to expose mm_reg1_t with Cython)
#
cdef extern from "cmappy.h":
ctypedef struct mm_hitpy_t:
const char *ctg
int32_t ctg_start, ctg_end
int32_t qry_start, qry_end
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
char *s
ctypedef struct kstream_t:
pass
ctypedef struct kseq_t:
kstring_t name, comment, seq, qual
int last_char
kstream_t *f
kseq_t *mm_fastx_open(const char *fn)
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()
+212
View File
@@ -0,0 +1,212 @@
from libc.stdint cimport uint8_t, int8_t
from libc.stdlib cimport free
cimport cmappy
import sys
__version__ = '2.11'
cmappy.mm_reset_timer()
cdef class Alignment:
cdef int _ctg_len, _r_st, _r_en
cdef int _q_st, _q_en
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, 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
self._NM, self._mlen, self._blen = NM, mlen, blen
self._mapq = mapq
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
@property
def ctg_len(self): return self._ctg_len
@property
def r_st(self): return self._r_st
@property
def r_en(self): return self._r_en
@property
def strand(self): return self._strand
@property
def trans_strand(self): return self._trans_strand
@property
def blen(self): return self._blen
@property
def mlen(self): return self._mlen
@property
def NM(self): return self._NM
@property
def is_primary(self): return (self._is_primary != 0)
@property
def q_st(self): return self._q_st
@property
def q_en(self): return self._q_en
@property
def mapq(self): return self._mapq
@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))
def __str__(self):
if self._strand > 0: strand = '+'
elif self._strand < 0: strand = '-'
else: strand = '?'
if self._is_primary != 0: tp = 'tp:A:P'
else: tp = 'tp:A:S'
if self._trans_strand > 0: ts = 'ts:A:+'
elif self._trans_strand < 0: ts = 'ts:A:-'
else: ts = 'ts:A:.'
return "\t".join([str(self._q_st), str(self._q_en), strand, self._ctg, str(self._ctg_len), str(self._r_st), str(self._r_en),
str(self._mlen), str(self._blen), str(self._mapq), tp, ts, "cg:Z:" + self.cigar_str])
cdef class ThreadBuffer:
cdef cmappy.mm_tbuf_t *_b
def __cinit__(self):
self._b = cmappy.mm_tbuf_init()
def __dealloc__(self):
cmappy.mm_tbuf_destroy(self._b)
cdef class Aligner:
cdef cmappy.mm_idx_t *_idx
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, 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
self.map_opt.flag |= 4 # always perform alignment
self.idx_opt.batch_size = 0x7fffffffffffffffL # always build a uni-part index
if k is not None: self.idx_opt.k = k
if w is not None: self.idx_opt.w = w
if min_cnt is not None: self.map_opt.min_cnt = min_cnt
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.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:
r = cmappy.mm_idx_reader_open(str.encode(fn_idx_in), &self.idx_opt, NULL)
else:
r = cmappy.mm_idx_reader_open(str.encode(fn_idx_in), &self.idx_opt, fn_idx_out)
if r is not NULL:
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:
cmappy.mm_idx_destroy(self._idx)
def __bool__(self):
return (self._idx != NULL)
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
_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)
cigar = []
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, h.seg_id)
cmappy.mm_free_reg1(&regs[i])
free(regs)
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
while cmappy.kseq_read(ks) >= 0:
if ks.qual.l > 0: qual = ks.qual.s if isinstance(ks.qual.s, str) else ks.qual.s.decode()
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()
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)
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env python
import sys, getopt
import mappy as mp
def main(argv):
opts, args = getopt.getopt(argv[1:], "x:n:m:k:w:r:")
if len(args) < 2:
print("Usage: minimap2.py [options] <ref.fa>|<ref.mmi> <query.fq>")
print("Options:")
print(" -x STR preset: sr, map-pb, map-ont, asm5, asm10 or splice")
print(" -n INT mininum number of minimizers")
print(" -m INT mininum chaining score")
print(" -k INT k-mer length")
print(" -w INT minimizer window length")
print(" -r INT band width")
sys.exit(1)
preset, min_cnt, min_sc, k, w, bw = None, None, None, None, None, None
for opt, arg in opts:
if opt == '-x': preset = arg
elif opt == '-n': min_cnt = int(arg)
elif opt == '-m': min_chain_score = int(arg)
elif opt == '-r': bw = int(arg)
elif opt == '-k': k = int(arg)
elif opt == '-w': w = int(arg)
a = mp.Aligner(args[0], preset=preset, min_cnt=min_cnt, min_chain_score=min_sc, k=k, w=w, bw=bw)
if not a: raise Exception("ERROR: failed to load/build index file '{}'".format(args[0]))
for name, seq, qual in mp.fastx_read(args[1]): # read one sequence
for h in a.map(seq): # traverse hits
print('{}\t{}\t{}'.format(name, len(seq), h))
if __name__ == "__main__":
main(sys.argv)
+5 -4
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 = (sdust_buf_t*)kcalloc(km, 1, sizeof(sdust_buf_t));
buf->km = km; buf->km = km;
buf->w = kdq_init(int, buf->km); buf->w = kdq_init(int, buf->km);
kdq_resize(int, buf->w, 8);
return buf; 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) 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; 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); s = *kdq_shift(int, w);
*rw -= --cw[s]; *rw -= --cw[s];
if (*L > kdq_size(w)) if (*L > (int)kdq_size(w))
--*L, *rv -= --cv[s]; --*L, *rv -= --cv[s];
} }
kdq_push(int, w, t); 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]++; r += c[t]++;
new_r = r, new_l = kdq_size(w) - i - 1; new_r = r, new_l = kdq_size(w) - i - 1;
if (new_r * 10 > T * new_l) { 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]; perf_intv_t *p = &P->a[j];
if (max_r == 0 || p->r * max_l > max_r * p->l) if (max_r == 0 || p->r * max_l > max_r * p->l)
max_r = p->r, max_l = p->l; max_r = p->r, max_l = p->l;
@@ -176,7 +177,7 @@ uint64_t *sdust(void *km, const uint8_t *seq, int l_seq, int T, int W, int *n)
#ifdef _SDUST_MAIN #ifdef _SDUST_MAIN
#include <zlib.h> #include <zlib.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include "getopt.h"
#include "kseq.h" #include "kseq.h"
KSEQ_INIT(gzFile, gzread) KSEQ_INIT(gzFile, gzread)
+55
View File
@@ -0,0 +1,55 @@
try:
from setuptools import setup, Extension
except ImportError:
from distutils.core import setup
from distutils.extension import Extension
cmdclass = {}
try:
from Cython.Build import build_ext
except ImportError: # without Cython
module_src = 'python/mappy.c'
else: # with Cython
module_src = 'python/mappy.pyx'
cmdclass['build_ext'] = build_ext
import sys
sys.path.append('python')
def readme():
with open('python/README.rst') as f:
return f.read()
setup(
name = 'mappy',
version = '2.11',
url = 'https://github.com/lh3/minimap2',
description = 'Minimap2 python binding',
long_description = readme(),
author = 'Heng Li',
author_email = 'lh3@me.com',
license = 'MIT',
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', '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', '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 = ['-DHAVE_KALLOC', '-msse4.1'], # WARNING: ancient x86_64 CPUs don't have SSE4
include_dirs = ['.'],
libraries = ['z', 'm', 'pthread'])],
classifiers = [
'Development Status :: 5 - Production/Stable',
'License :: OSI Approved :: MIT License',
'Operating System :: POSIX',
'Programming Language :: C',
'Programming Language :: Cython',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Intended Audience :: Science/Research',
'Topic :: Scientific/Engineering :: Bio-Informatics'],
cmdclass = cmdclass)
+12 -12
View File
@@ -2,8 +2,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#define __STDC_LIMIT_MACROS
#include "kvec.h" #include "kvec.h"
#include "minimap.h" #include "mmpriv.h"
unsigned char seq_nt4_table[256] = { unsigned char seq_nt4_table[256] = {
0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
@@ -11,9 +12,9 @@ unsigned char seq_nt4_table[256] = {
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 0, 4, 1, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 1, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 0, 4, 1, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 1, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
@@ -77,11 +78,10 @@ void mm_sketch(void *km, const char *str, int len, int w, int k, uint32_t rid, i
{ {
uint64_t shift1 = 2 * (k - 1), mask = (1ULL<<2*k) - 1, kmer[2] = {0,0}; uint64_t shift1 = 2 * (k - 1), mask = (1ULL<<2*k) - 1, kmer[2] = {0,0};
int i, j, l, buf_pos, min_pos, kmer_span = 0; int i, j, l, buf_pos, min_pos, kmer_span = 0;
mm128_t *buf, min = { UINT64_MAX, UINT64_MAX }; mm128_t buf[256], min = { UINT64_MAX, UINT64_MAX };
tiny_queue_t tq; tiny_queue_t tq;
assert(len > 0 && w > 0 && k > 0 && k <= 28); // 56 bits for k-mer; could use long k-mers, but 28 enough in practice assert(len > 0 && (w > 0 && w < 256) && (k > 0 && k <= 28)); // 56 bits for k-mer; could use long k-mers, but 28 enough in practice
buf = (mm128_t*)alloca(w * 16);
memset(buf, 0xff, w * 16); memset(buf, 0xff, w * 16);
memset(&tq, 0, sizeof(tiny_queue_t)); memset(&tq, 0, sizeof(tiny_queue_t));
kv_resize(mm128_t, km, *p, p->n + len/w); kv_resize(mm128_t, km, *p, p->n + len/w);
@@ -102,34 +102,34 @@ void mm_sketch(void *km, const char *str, int len, int w, int k, uint32_t rid, i
tq_push(&tq, skip_len); tq_push(&tq, skip_len);
kmer_span += skip_len; kmer_span += skip_len;
if (tq.count > k) kmer_span -= tq_shift(&tq); if (tq.count > k) kmer_span -= tq_shift(&tq);
if (kmer_span >= 256) continue; // make sure $kmer_span does not take more than 8 bits
} else kmer_span = l + 1 < k? l + 1 : k; } else kmer_span = l + 1 < k? l + 1 : k;
kmer[0] = (kmer[0] << 2 | c) & mask; // forward k-mer kmer[0] = (kmer[0] << 2 | c) & mask; // forward k-mer
kmer[1] = (kmer[1] >> 2) | (3ULL^c) << shift1; // reverse k-mer kmer[1] = (kmer[1] >> 2) | (3ULL^c) << shift1; // reverse k-mer
if (kmer[0] == kmer[1]) continue; // skip "symmetric k-mers" as we don't know it strand if (kmer[0] == kmer[1]) continue; // skip "symmetric k-mers" as we don't know it strand
z = kmer[0] < kmer[1]? 0 : 1; // strand z = kmer[0] < kmer[1]? 0 : 1; // strand
if (++l >= k) { ++l;
if (l >= k && kmer_span < 256) {
info.x = hash64(kmer[z], mask) << 8 | kmer_span; info.x = hash64(kmer[z], mask) << 8 | kmer_span;
info.y = (uint64_t)rid<<32 | (uint32_t)i<<1 | z; info.y = (uint64_t)rid<<32 | (uint32_t)i<<1 | z;
} }
} else l = 0, tq.count = tq.front = 0, kmer_span = 0; } 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 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) 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]); 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) 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 (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 (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; min = info, min_pos = buf_pos;
} else if (buf_pos == min_pos) { // old min has moved outside the window } 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 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 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) for (j = 0; j <= buf_pos; ++j)
if (min.x >= buf[j].x) min = buf[j], min_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 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]); 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) 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 GTTTATGTAGCTTATTCTATCCAAAGCAATGCACTGAAAATGTCTCGACGGGCCCACACG
CCCCATAAACAAATAGGTTTGGTCCTAGCCTTTCTATTAGCTCTTAGTGAGGTTACACAT CCCCATAAACAAATAGGTTTGGTCCTAGCCTTTCTATTAGCTCTTAGTGAGGTTACACAT
GCAAGCATCCCCGCCCCAGTGAGTCGCCCTCCAAGTCACTCTGACTAAGAGGAGCAAGCA GCAAGCATCCCCGCCCCAGTGAGTCGCCCTCCAAGTCACTCTGACTAAGAGGAGCAAGCA
+4
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
>q2
GGACATCCCGATGGTGCAGTCCTACCTGTACGAAAGGAC
+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
+2
View File
@@ -0,0 +1,2 @@
>t2
GGACATCCCGATGGTGCAGgtGCTATTAAAGGTTCGTTTGTTCAACGATTAAagTCCTACCTGTACGAAAGGAC
+21
View File
@@ -0,0 +1,21 @@
.SUFFIXES: .gp .tex .eps .pdf .eps.gz
.eps.pdf:
epstopdf --outfile $@ $<
.eps.gz.pdf:
gzip -dc $< | epstopdf --filter > $@
.pdf.eps:
pdftops -eps $< $@
all:minimap2.pdf
roc-color.eps:roc.gp
gnuplot roc.gp
minimap2.pdf:minimap2.tex minimap2.bib roc-color.pdf
pdflatex minimap2; bibtex minimap2; pdflatex minimap2; pdflatex minimap2;
clean:
rm -fr *.toc *.aux *.bbl *.blg *.idx *.log *.out *~ minimap2.pdf
+930
View File
@@ -0,0 +1,930 @@
\newcommand\classname{bioinfo}
\newcommand\lastmodifieddate{2003/02/08}
\newcommand\versionnumber{0.1}
% Are we printing crop marks?
\newif\if@cropmarkson \@cropmarksontrue
\NeedsTeXFormat{LaTeX2e}[2001/06/01]
\ProvidesClass{\classname}[\lastmodifieddate\space\versionnumber]
\setlength{\paperheight}{11truein}
\setlength{\paperwidth}{8.5truein}
\newif\if@final
\DeclareOption{draft}{\PassOptionsToPackage{draft}{graphicx}}
\DeclareOption{a4paper}{\PassOptionsToPackage{a4}{crop}}
\DeclareOption{centre}{\PassOptionsToPackage{center}{crop}}
\DeclareOption{crop}{\PassOptionsToPackage{cam}{crop}\global\@cropmarksontrue}
\DeclareOption{nocrop}{\PassOptionsToPackage{off}{crop}\global\@cropmarksonfalse}
\DeclareOption{info}{\PassOptionsToPackage{info}{crop}}
\DeclareOption{noinfo}{\PassOptionsToPackage{noinfo}{crop}}
\DeclareOption{final}{\global\@finaltrue}
\ExecuteOptions{a4paper,nocrop,centre,info}
\ProcessOptions
% Load all necessary packages
\RequirePackage{inputenc,crop,graphicx,amsmath,array,color,amssymb,flushend,stfloats,amsthm,chngpage,times}
%\RequirePackage[LY1]{fontenc}
%\RequirePackage[LY1,mtbold]{mathtime}
\def\authoraffliate{\fontfamily{phv}\selectfont}
\def\helvetica{\fontfamily{phv}\selectfont}
\def\helveticaitalic{\fontfamily{phv}\itshape\selectfont}
\def\helveticabold{\fontfamily{phv}\bfseries\selectfont}
\def\helveticabolditalic{\fontfamily{phv}\bfseries\itshape\selectfont}
% Not sure if needed.
\newcommand\@ptsize{0}
% Set twoside printing
\@twosidetrue
% Marginal notes are on the outside edge
\@mparswitchfalse
\reversemarginpar
\renewcommand\normalsize{%
\@setfontsize\normalsize{9}{11}%
\abovedisplayskip 10\p@ \@plus2\p@ \@minus5\p@
\abovedisplayshortskip \z@ \@plus3\p@
\belowdisplayshortskip 6\p@ \@plus3\p@ \@minus3\p@
\belowdisplayskip \abovedisplayskip
\let\@listi\@listI}
\normalsize
\let\@bls\baselineskip
\newcommand\small{%
\@setfontsize\small{9}{11}%
\abovedisplayskip 11\p@ minus 3\p@
\belowdisplayskip \abovedisplayskip
\abovedisplayshortskip \z@ plus 2\p@
\belowdisplayshortskip 4\p@ plus 2\p@ minus2\p@
\def\@listi{\topsep 4.5\p@ plus 2\p@ minus 1\p@
\itemsep \parsep
\topsep 4\p@ plus 2\p@ minus 2\p@}}
\newcommand\footnotesize{%
\@setfontsize\footnotesize{8}{10}%
\abovedisplayskip 6\p@ minus 3\p@
\belowdisplayskip\abovedisplayskip
\abovedisplayshortskip \z@ plus 3\p@
\belowdisplayshortskip 6\p@ plus 3\p@ minus 3\p@
\def\@listi{\topsep 3\p@ plus 1\p@ minus 1\p@
\parsep 2\p@ plus 1\p@ minus 1\p@\itemsep \parsep}}
\def\scriptsize{\@setfontsize\scriptsize{7pt}{9pt}}
\def\tiny{\@setfontsize\tiny{5pt}{7pt}}
\def\large{\@setfontsize\large{11.5pt}{12pt}}
\def\Large{\@setfontsize\Large{14pt}{16}}
\def\LARGE{\@setfontsize\LARGE{15pt}{17pt}}
\def\huge{\@setfontsize\huge{22pt}{22pt}}
\def\Huge{\@setfontsize\Huge{30pt}{30pt}}
\DeclareOldFontCommand{\rm}{\normalfont\rmfamily}{\mathrm}
\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf}
\DeclareOldFontCommand{\tt}{\normalfont\ttfamily}{\mathtt}
\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf}
\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit}
\DeclareOldFontCommand{\sl}{\normalfont\slshape}{\@nomath\sl}
\DeclareOldFontCommand{\sc}{\normalfont\scshape}{\@nomath\sc}
% Line spacing
\setlength\lineskip{1\p@}
\setlength\normallineskip{1\p@}
\renewcommand\baselinestretch{}
% Paragraph dimensions and inter-para spacing
\setlength\parskip{0\p@}
\setlength\parindent{3mm}
% Set inter-para skips
\setlength\smallskipamount{3\p@ \@plus 1\p@ \@minus 1\p@}
\setlength\medskipamount{6\p@ \@plus 2\p@}
\setlength\bigskipamount{12\p@ \@plus 4\p@ \@minus 4\p@}
% Page break penalties
\@lowpenalty 51
\@medpenalty 151
\@highpenalty 301
% Disallow widows and orphans
\clubpenalty 10000
\widowpenalty 10000
% Disable page breaks before equations, allow pagebreaks after
% equations and discourage widow lines before equations.
\displaywidowpenalty 100
\predisplaypenalty 10000
\postdisplaypenalty 2500
% Allow breaking the page in the middle of a paragraph
\interlinepenalty 0
% Disallow breaking the page after a hyphenated line
\brokenpenalty 10000
% Hyphenation; don't split words into less than three characters
\lefthyphenmin=3
\righthyphenmin=3
%
% Set page layout dimensions
%
\setlength\headheight{16\p@} % height of running head
\setlength\topmargin{2.9pc} % head margin
\addtolength\topmargin{-1in} % subtract out the 1 inch driver margin
\setlength\topskip{10\p@} % height of first line of text
\setlength\headsep{19\p@} % space below running head --
\setlength\footskip{34\p@} % space above footer line
\setlength\maxdepth{.5\topskip} % pages can be short or deep by half a line?
\setlength\textwidth{42pc} % text measure excluding margins
\setlength\textheight{58\baselineskip} % 54 lines on a full page,
\addtolength\textheight{\topskip} % including the first
% line on the page
% Set the margins
\setlength\marginparsep{3\p@}
\setlength\marginparpush{3\p@}
\setlength\marginparwidth{35\p@}
\setlength\oddsidemargin{4.5pc}
\addtolength\oddsidemargin{-1in} % subtract out the 1 inch driver margin
\setlength\@tempdima{\paperwidth}
\addtolength\@tempdima{-\textwidth}
\addtolength\@tempdima{-4.5pc}
\setlength\evensidemargin{\@tempdima}
\addtolength\evensidemargin{-1in}
\setlength\columnsep{1.5pc} % space between columns for double-column text
\setlength\columnseprule{0\p@} % width of rule between two columns
% Footnotes
\setlength\footnotesep{9\p@} % space between footnotes
% space between text and footnote
\setlength{\skip\footins}{12\p@ \@plus 6\p@ \@minus 1\p@}
% Float placement parameters
% The total number of floats that can be allowed on a page.
\setcounter{totalnumber}{10}
% The maximum number of floats at the top and bottom of a page.
\setcounter{topnumber}{5}
\setcounter{bottomnumber}{5}
% The maximum part of the top or bottom of a text page that can be
% occupied by floats. This is set so that at least four lines of text
% fit on the page.
\renewcommand\topfraction{.9}
\renewcommand\bottomfraction{.9}
% The minimum amount of a text page that must be occupied by text.
% This should accomodate four lines of text.
\renewcommand\textfraction{.06}
% The minimum amount of a float page that must be occupied by floats.
\renewcommand\floatpagefraction{.94}
% The same parameters repeated for double column output
\renewcommand\dbltopfraction{.9}
\renewcommand\dblfloatpagefraction{.9}
% Space between floats
\setlength\floatsep {12\p@ \@plus 2\p@ \@minus 2\p@}
% Space between floats and text
\setlength\textfloatsep{20\p@ \@plus 2\p@ \@minus 4\p@}
% Space above and below an inline figure
\setlength\intextsep {18\p@ \@plus 2\p@ \@minus 2\p@}
% For double column floats
\setlength\dblfloatsep {12\p@ \@plus 2\p@ \@minus 2\p@}
\setlength\dbltextfloatsep{20\p@ \@plus 2\p@ \@minus 4\p@}
% Space left at top, bottom and inbetween floats on a float page.
\setlength\@fptop{0\p@} % no space above float page figures
\setlength\@fpsep{12\p@ \@plus 1fil}
\setlength\@fpbot{0\p@}
% The same for double column
\setlength\@dblfptop{0\p@}
\setlength\@dblfpsep{12\p@ \@plus 1fil}
\setlength\@dblfpbot{0\p@}
% Override settings in mathtime back to TeX defaults
\DeclareMathSizes{5} {5} {5} {5}
\DeclareMathSizes{6} {6} {5} {5}
\DeclareMathSizes{7} {7} {5} {5}
\DeclareMathSizes{8} {8} {6} {5}
\DeclareMathSizes{9} {9} {6.5} {5}
\DeclareMathSizes{10} {10} {7.5} {5}
\DeclareMathSizes{12} {12} {9} {7}
% Page styles
\def\ps@headings
{%
\def\@oddfoot{\vbox to 12.5\p@{\hbox{\rule{\textwidth}{0.5\p@}}\vss
\hbox to \textwidth{\hfill\helveticabold\small\thepage}%
}}%
\def\@evenfoot{\vbox to 12.5\p@{\rule{\textwidth}{0.5\p@}\vss
\hbox to \textwidth{\helveticabold\small\thepage\hfill}%
}}%
\def\@evenhead{\vbox{\hbox to \textwidth{\fontsize{8}{10}\selectfont
\helveticabold{\fontshape{it}\selectfont
\strut\leftmark}\hfill}\vspace{6.5\p@}\rule{\textwidth}{0.5\p@}}}%
\def\@oddhead{\vbox{\hbox to \textwidth{\hfill\fontsize{8}{10}\selectfont
\helveticabold{\fontshape{it}\selectfont\strut\rightmark}}%
\vspace{6.5\p@}\rule{\textwidth}{0.5\p@}}}%
\def\titlemark##1{\markboth{##1}{##1}}%
\def\authormark##1{\gdef\leftmark{##1}}%
}
\def\ps@opening
{%
\def\@oddfoot{\vbox to 13\p@{\hbox{\rule{\textwidth}{1\p@}}\vss
\hbox to \textwidth{\helvetica
\fontsize{7}{9}\fontshape{n}\selectfont%
\hfill\small\helveticabold\thepage}%
}}%
\def\@evenfoot{\vbox to 13\p@{\rule{\textwidth}\vss
\hbox to \textwidth{\helvetica\thepage\hfill
\fontsize{7}{9}\fontshape{n}\selectfont}%
}}%
\let\@evenhead\relax
\let\@oddhead\relax}
% Page range
\newif\iflastpagegiven \lastpagegivenfalse
\newcommand\firstpage[1]{%
\gdef\@firstpage{#1}%
\ifnum\@firstpage>\c@page
\setcounter{page}{#1}%
\ClassWarning{BIO}{Increasing pagenumber to \@firstpage}%
\else \ifnum\@firstpage<\c@page
\ClassWarning{BIO}{Firstpage lower than pagenumber}\fi\fi
\xdef\@firstpage{\the\c@page}%
}
\def\@firstpage{1}
\def\pagenumbering#1{%
\global\c@page \@ne
\gdef\thepage{\csname @#1\endcsname \c@page}%
\gdef\thefirstpage{%
\csname @#1\endcsname \@firstpage}%
\gdef\thelastpage{%
\csname @#1\endcsname \@lastpage}%
}
\newcommand\lastpage[1]{\xdef\@lastpage{#1}%
\global\lastpagegiventrue}
\def\@lastpage{0}
\def\setlastpage{\iflastpagegiven\else
\edef\@tempa{@lastpage@}%
\expandafter
\ifx \csname \@tempa \endcsname \relax
\gdef\@lastpage{0}%
\else
\xdef\@lastpage{\@nameuse{@lastpage@}}%
\fi
\fi }
\def\writelastpage{%
\iflastpagegiven \else
\immediate\write\@auxout%
{\string\global\string\@namedef{@lastpage@}{\the\c@page}}%
\fi
}
\def\thepagerange{%
\ifnum\@lastpage =0 {\ \bf ???} \else
\ifnum\@lastpage = \@firstpage \ \thefirstpage\else
\thefirstpage--\thelastpage \fi\fi}
\AtBeginDocument{\setlastpage
\pagenumbering{arabic}%
}
\AtEndDocument{%
\writelastpage
\if@final
\clearemptydoublepage
\else
\clearpage
\fi}
%
% Sectional units
%
% Counters
\newcounter{section}
\newcounter{subsection}[section]
\newcounter{subsubsection}[subsection]
\newcounter{paragraph}[subsubsection]
\newcounter{subparagraph}[paragraph]
\newcounter{figure}
\newcounter{table}
% Form of the numbers
\newcommand\thepage{\arabic{page}}
\renewcommand\thesection{\arabic{section}}
\renewcommand\thesubsection{{\thesection.\arabic{subsection}}}
\renewcommand\thesubsubsection{{\thesubsection.\arabic{subsubsection}}}
\renewcommand\theparagraph{\thesubsubsection.\arabic{paragraph}}
\renewcommand\thesubparagraph{\theparagraph.\arabic{subparagraph}}
\renewcommand\theequation{\arabic{equation}}
% Form of the words
\newcommand\contentsname{Contents}
\newcommand\listfigurename{List of Figures}
\newcommand\listtablename{List of Tables}
\newcommand\partname{Part}
\newcommand\appendixname{Appendix}
\newcommand\abstractname{Abstract}
\newcommand\refname{References}
\newcommand\bibname{References}
\newcommand\indexname{Index}
\newcommand\figurename{Fig.}
\newcommand\tablename{Table}
% Clearemptydoublepage should really clear the running heads too
\newcommand{\clearemptydoublepage}{\newpage{\pagestyle{empty}\cleardoublepage}}
% Frontmatter, mainmatter and backmatter
\newif\if@mainmatter \@mainmattertrue
\newcommand\frontmatter{%
\clearpage
\@mainmatterfalse
\pagenumbering{roman}}
\newcommand\mainmatter{%
\clearpage
\@mainmattertrue
\pagenumbering{arabic}}
\newcommand\backmatter{%
\clearpage
\@mainmatterfalse}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TITLE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newlength{\dropfromtop}
\setlength{\dropfromtop}{\z@}
% Application Notes
\newif\if@appnotes
\newcommand{\application}{%
% \setlength{\dropfromtop}{-2.25pc}%
\global\@appnotestrue}
\long\def\title{\@ifnextchar[{\short@title}{\@@title}}
\def\short@title[#1]{\titlemark{#1}\@@@title}
\def\@@title#1{\authormark{#1}\@@@title{#1}}
\long\def\@@@title#1{\gdef\@title{#1}}
\long\def\author{\@ifnextchar[{\short@uthor}{\@uthor}}
\def\short@uthor[#1]{\authormark{#1}\@@author}
\def\@uthor#1{\authormark{#1}\@@author{#1}}
\long\def\@@author#1{\gdef\@author{#1}}
\def\vol#1{\global\def\@vol{#1}}
\def\issue#1{\global\def\@issue{#1}}
\def\address#1{\global\def\@issue{#1}}
\def\history#1{\global\def\@history{#1}}
\def\editor#1{\global\def\@editor{#1}}
\def\pubyear#1{\global\def\@pubyear{#1}}
\def\copyrightyear#1{\global\def\@copyrightyear{#1}}
\def\address#1{\global\def\@address{#1}}
\def\DOI#1{\global\def\@DOI{#1}}
\definecolor{gray}{cmyk}{0, 0, 0, 0.15}
\newlength{\extraspace}
\setlength{\extraspace}{\z@}
\newcommand\maketitle{\par
\begingroup
\renewcommand\thefootnote{\@fnsymbol\c@footnote}%
\def\@makefnmark{\rlap{\@textsuperscript{\normalfont\@thefnmark}}}%
\long\def\@makefntext##1{\parindent 3mm\noindent
% \@textsuperscript{\normalfont\@thefnmark}\raggedright##1}%
\@textsuperscript{\normalfont\@thefnmark}##1}%
\if@twocolumn
\ifnum \col@number=\@ne
\@maketitle
\else
\twocolumn[\@maketitle]%
\fi
\else
\newpage
\global\@topnum\z@ % Prevents figures from going at top of page.
\@maketitle
\fi
\thispagestyle{opening}\@thanks
\endgroup
\setcounter{footnote}{0}%
\global\let\thanks\relax
\global\let\maketitle\relax
\global\let\@maketitle\relax
\global\let\@address\@empty
\global\let\@history\@empty
\global\let\@editor\@empty
\global\let\@thanks\@empty
\global\let\@author\@empty
\global\let\@date\@empty
\global\let\@title\@empty
\global\let\@pubyear\@empty
\global\let\address\relax
\global\let\history\relax
\global\let\editor\relax
\global\let\title\relax
\global\let\author\relax
\global\let\date\relax
\global\let\pubyear\relax
\global\let\@copyrightline\@empty
\global\let\and\relax
\@afterindentfalse\@afterheading
}
\newlength{\aboveskipchk}%for checking oddpage or evenpage top skip
\setlength{\aboveskipchk}{\z@}%
\def\@maketitle{%
\let\footnote\thanks
\clearemptydoublepage
\checkoddpage\ifcpoddpage\setlength{\aboveskipchk}{-3pc}\else\setlength{\aboveskipchk}{-5pc}\fi%for checking oddpage or evenpage top skip%%
\vspace*{\aboveskipchk}%
\vspace{\dropfromtop}%
\hbox to \textwidth{%
{\helvetica\itshape\bfseries\fontsize{19}{12}\selectfont {\color{gray}TECHNICAL REPORT}
\hfil
\if@appnotes APPLICATIONS NOTE\hfil\fi
}%
\enskip \parbox[b]{11.3pc}{%
\helvetica
\flushright\fontsize{8}{10}\fontshape{it}\selectfont
\hfill
}}
\rule{\textwidth}{1\p@}\par%
\helvetica
\hbox to \textwidth{%
\parbox[t]{41pc}{%
\vspace*{1sp}
{\helveticabold\fontsize{16}{21}\selectfont\raggedright \@title \par}%
\vspace{4.5\p@}
{\authoraffliate\fontsize{11}{13}\selectfont\raggedright \@author \par}%
\vspace{4\p@}
{\authoraffliate\fontsize{9}{11}\selectfont\raggedright \@address \par}%
\vspace{4\p@}
%{\helvetica\fontsize{8}{10}\selectfont\raggedright \@history \par}
%\vspace{24\p@}
%{\helvetica\fontsize{10}{12}\selectfont\raggedright \@editor \par}
%\vspace{20\p@}
}%
}
\vspace{4.5\p@}%
\rule{\textwidth}{1\p@}%
\vspace{12\p@ plus 6\p@ minus 6\p@}%
\vspace{\extraspace}
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%% Abstract %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newcommand{\absection}[1]{%
\par\noindent{\bfseries #1}\space\ignorespaces}
\newenvironment{abstract}{%
\begingroup
\let\section\absection
\fontfamily{\sfdefault}\fontsize{8}{11}\sffamily\selectfont
{\fontseries{b}\selectfont ABSTRACT}\par}
{\endgroup\bigskip\@afterheading\@afterindentfalse\vskip 12pt plus 3pt minus 1pt}
% Section macros
% Lowest level heading that takes a number by default
\setcounter{secnumdepth}{3}
\renewcommand{\@seccntformat}[1]{\csname the#1\endcsname\quad}
\def\section{%
\@startsection{section}{1}{\z@}
{-22\p@ plus -3\p@}{3\p@}
{\reset@font\raggedright\helveticabold\fontsize{10}{12}\selectfont\MakeUppercase}}
\def\subsection{%
\@startsection{subsection}{2}{\z@}
{-11\p@ plus -2\p@}{3\p@}
{\reset@font\raggedright\mathversion{bold}\fontseries{b}\fontsize{10}{12}\selectfont}}
\def\subsubsection{%
\@startsection{subsubsection}{3}{\z@}
%{-11\p@ plus -1\p@}{-1em}
{-11\p@ plus -1\p@}{0.001em}
{\reset@font\normalfont\normalsize\itshape}}
\def\textcolon{\text{\rm :}}
\def\paragraph{%
\@startsection{paragraph}{4}{\z@}
{-6\p@}
{-.4em}
{\reset@font\itshape}}
% ********************
% Figures and tables *
% ********************
% Table and array parameters
\setlength\arraycolsep{.5em}
\setlength\tabcolsep{.5em}
\setlength\arrayrulewidth{.5pt}
\setlength\doublerulesep{2.5pt}
\setlength\extrarowheight{\z@}
\renewcommand\arraystretch{1}
\newlength{\abovecaptionskip}
\newlength{\belowcaptionskip}
\setlength{\abovecaptionskip}{13pt}
\setlength{\belowcaptionskip}{10.5pt}
\long\def\@makecaption#1#2{\vspace{\abovecaptionskip}%
\begingroup
\footnotesize
\textbf{#1.}\enskip{#2}\par
\endgroup}
\long\def\@tablecaption#1#2{%
\begingroup
\footnotesize
\textbf{#1.}\enskip{#2\strut\par}
\endgroup\vspace{\belowcaptionskip}}
% Table rules
\def\toprule{\noalign{\ifnum0=`}\fi\hrule \@height 0.5pt \hrule \@height 6pt \@width 0pt \futurelet
\@tempa\@xhline}
\def\midrule{\noalign{\ifnum0=`}\fi \hrule \@height 6.75pt \@width 0pt \hrule \@height 0.5pt
\hrule \@height 6pt \@width 0pt \futurelet \@tempa\@xhline}
\def\botrule{\noalign{\ifnum0=`}\fi \hrule \@height 5.75pt \@width 0pt \hrule \@height 0.5pt \futurelet
\@tempa\@xhline}
\def\hrulefill{\leavevmode\leaders\hrule height .5pt\hfill\kern\z@}
\def\thefigure{\@arabic\c@figure}
\def\fps@figure{tbp}
\def\ftype@figure{1}
\def\ext@figure{lof}
\def\fnum@figure{\figurename~\thefigure}
\def\figure{\@float{figure}}
\let\endfigure\end@float
\@namedef{figure*}{\@dblfloat{figure}}
\@namedef{endfigure*}{\end@dblfloat}
\def\thetable{\@arabic\c@table}
\def\fps@table{tbp}
\def\ftype@table{2}
\def\ext@table{lot}
\def\fnum@table{Table~\thetable}
\def\table{\let\@makecaption\@tablecaption\let\source\tablesource\@float{table}}
\def\endtable{\end@float}
\@namedef{table*}{\let\@makecaption\@tablecaption\@dblfloat{table}}
\@namedef{endtable*}{\end@dblfloat}
\newif\if@rotate \@rotatefalse
\newif\if@rotatecenter \@rotatecenterfalse
\def\rotatecenter{\global\@rotatecentertrue}
\def\rotateendcenter{\global\@rotatecenterfalse}
\def\rotate{\global\@rotatetrue}
\def\endrotate{\global\@rotatefalse}
\newdimen\rotdimen
\def\rotstart#1{\special{ps: gsave currentpoint currentpoint translate
#1 neg exch neg exch translate}}
\def\rotfinish{\special{ps: currentpoint grestore moveto}}
\def\rotl#1{\rotdimen=\ht#1\advance\rotdimen by \dp#1
\hbox to \rotdimen{\vbox to\wd#1{\vskip \wd#1
\rotstart{270 rotate}\box #1\vss}\hss}\rotfinish}
\def\rotr#1{\rotdimen=\ht #1\advance\rotdimen by \dp#1
\hbox to \rotdimen{\vbox to \wd#1{\vskip \wd#1
\rotstart{90 rotate}\box #1\vss}\hss}\rotfinish}
\newdimen\tempdime
\newbox\temptbox
% From ifmtarg.sty
% Copyright Peter Wilson and Donald Arseneau, 2000
\begingroup
\catcode`\Q=3
\long\gdef\@ifmtarg#1{\@xifmtarg#1QQ\@secondoftwo\@firstoftwo\@nil}
\long\gdef\@xifmtarg#1#2Q#3#4#5\@nil{#4}
\long\gdef\@ifnotmtarg#1{\@xifmtarg#1QQ\@firstofone\@gobble\@nil}
\endgroup
\def\tablesize{\@setfontsize\tablesize{8\p@}{10\p@}}
\newenvironment{processtable}[3]{\setbox\temptbox=\hbox{{\tablesize #2}}%
\tempdime\wd\temptbox\@processtable{#1}{#2}{#3}{\tempdime}}
{\relax}
\newcommand{\@processtable}[4]{%
\if@rotate
\setbox4=\vbox to \hsize{\vss\hbox to \textheight{%
\begin{minipage}{#4}%
\@ifmtarg{#1}{}{\caption{#1}}{\tablesize #2}%
\vskip7\p@\noindent
\parbox{#4}{\fontsize{7}{9}\selectfont #3\par}%
\end{minipage}}\vss}%
\rotr{4}
\else
\hbox to \hsize{\hss\begin{minipage}[t]{#4}%
\vskip2.9pt
\@ifmtarg{#1}{}{\caption{#1}}{\tablesize #2}%
\vskip6\p@\noindent
\parbox{#4}{\fontsize{7}{9}\selectfont #3\par}%
\end{minipage}\hss}\fi}%
\newcolumntype{P}[1]{>{\raggedright\let\\\@arraycr\hangindent1em}p{#1}}
% ******************************
% List numbering and lettering *
% ******************************
\def\labelenumi{{\rm\arabic{enumi}.}}
\def\theenumi{\arabic{enumi}}
\def\labelenumii{{\rm\alph{enumii}.}}
\def\theenumii{\alph{enumii}}
\def\p@enumii{\theenumi}
\def\labelenumiii{{\rm(\arabic{enumiii})}}
\def\theenumiii{\roman{enumiii}}
\def\p@enumiii{\theenumi(\theenumii)}
\def\labelenumiv{{\rm(\arabic{enumiv})}}
\def\theenumiv{\Alph{enumiv}}
\def\p@enumiv{\p@enumiii\theenumiii}
\def\labelitemi{{\small$\bullet$}}
\def\labelitemii{{\small$\bullet$}}
\def\labelitemiii{{\small$\bullet$}}
\def\labelitemiv{{\small$\bullet$}}
\def\@listI{\leftmargin\leftmargini \topsep\medskipamount}
\let\@listi\@listI
\@listi
\def\@listii{\topsep\z@\leftmargin\leftmarginii}
\def\@listiii{\leftmargin\leftmarginiii \topsep\z@}
\def\@listiv{\leftmargin\leftmarginiv \topsep\z@}
\def\@listv{\leftmargin\leftmarginv \topsep\z@}
\def\@listvi{\leftmargin\leftmarginvi \topsep\z@}
\setlength{\leftmargini}{3mm}
\setlength{\leftmarginii}{\z@}
\setlength{\leftmarginiii}{\z@}
\setlength{\leftmarginiv}{\z@}
% Changes to the list parameters for enumerate
\def\enumargs{%
\partopsep \z@
\itemsep 3\p@
\parsep \z@
\labelsep 0.5em
\listparindent \parindent
\itemindent \z@
\topsep 11\p@
}
\def\enumerate{%
\@ifnextchar[{\@numerate}{\@numerate[0]}}
\def\@numerate[#1]{%
\ifnum \@enumdepth >3 \@toodeep\else
\advance\@enumdepth \@ne
\edef\@enumctr{enum\romannumeral\the\@enumdepth}
\list{\csname label\@enumctr\endcsname}{%
\enumargs
\setlength{\leftmargin}{\csname leftmargin\romannumeral\the\@enumdepth\endcsname}
\usecounter{\@enumctr}
\settowidth\labelwidth{#1}
\addtolength{\leftmargin}{\labelwidth}
\addtolength{\leftmargin}{\labelsep}
\def\makelabel##1{\hss \llap{##1}}}%
\fi
}
\let\endenumerate\endlist
% Changes to the list parameters for itemize
\def\itemargs{%
\partopsep \z@
\itemsep 3\p@
\parsep \z@
\labelsep 0.5em
\rightmargin \z@
\listparindent \parindent
\itemindent \z@
\topsep11\p@
}
\def\itemize{%
\@ifnextchar[{\@itemize}{\@itemize[$\bullet$]}}
\def\@itemize[#1]{%
\ifnum \@itemdepth >3 \@toodeep\else
\advance\@itemdepth \@ne
\edef\@itemctr{item\romannumeral\the\@itemdepth}
\list{\csname label\@itemctr\endcsname}{%
\itemargs
\setlength{\leftmargin}{\csname leftmargin\romannumeral\the\@itemdepth\endcsname}
\settowidth\labelwidth{#1}
\addtolength{\leftmargin}{\labelwidth}
\addtolength{\leftmargin}{\labelsep}
\def\makelabel##1{\hss \llap{##1}}}%
\fi
}
\let\enditemize\endlist
\newenvironment{unlist}{%
\begin{list}{}%
{\setlength{\labelwidth}{\z@}%
\setlength{\labelsep}{\z@}%
\setlength{\topsep}{\medskipamount}%
\setlength{\itemsep}{3\p@}%
\setlength{\leftmargin}{2em}%
\setlength{\itemindent}{-2em}}}
{\end{list}}
% ***********************
% Quotes and Quotations *
% ***********************
\def\quotation{\par\begin{list}{}{
\setlength{\topsep}{\medskipamount}
\setlength{\leftmargin}{2em}%
\setlength{\rightmargin}{\z@}%
\setlength\labelwidth{0pt}%
\setlength\labelsep{0pt}%
\listparindent\parindent}%
\item[]}
\def\endquotation{\end{list}}
\let\quote\quotation
\let\endquote\endquotation
\skip\@mpfootins = \skip\footins
\fboxsep=6\p@
\fboxrule=1\p@
% *******************
% Table of contents *
% *******************
\newcommand\@pnumwidth{4em}
\newcommand\@tocrmarg{2.55em plus 1fil}
\newcommand\@dotsep{1000}
\setcounter{tocdepth}{4}
\def\numberline#1{\hbox to \@tempdima{{#1}}}
\def\@authortocline#1#2#3#4#5{%
\vskip 1.5\p@
\ifnum #1>\c@tocdepth \else
{\leftskip #2\relax \rightskip \@tocrmarg \parfillskip -\rightskip
\parindent #2\relax\@afterindenttrue
\interlinepenalty\@M
\leavevmode
\@tempdima #3\relax
\advance\leftskip \@tempdima \null\nobreak\hskip -\leftskip
{\itshape #4}\nobreak
\leaders\hbox{$\m@th
\mkern \@dotsep mu\hbox{.}\mkern \@dotsep
mu$}\hfill
\nobreak
\hb@xt@\@pnumwidth{\hfil}%
\par}%
\fi}
\newcommand*\l@author{\@authortocline{2}{0pt}{30pt}}
\newcommand*\l@section{\@dottedtocline{3}{11pt}{20pt}}
\newcommand*\l@subsection{\@dottedtocline{4}{31pt}{29pt}}
\newcommand*\l@subsubsection[2]{}
% ***********
% Footnotes *
% ***********
\def\footnoterule{\noindent\rule{\columnwidth}{0.5pt}}
\def\@makefnmark{\@textsuperscript{\normalfont\@thefnmark}}%
\newcommand\@makefntext[1]{\noindent{\@makefnmark}\enskip#1}
% ***********
% References *
% ***********
\providecommand{\newblock}{}
\newenvironment{thebibliography}{%
\section{\bibname}%
\begingroup
\small
\begin{list}{}{%
\setlength{\topsep}{\z@}%
\setlength{\labelsep}{\z@}%
\settowidth{\labelwidth}{\z@}%
\setlength{\leftmargin}{4mm}%
\setlength{\itemindent}{-4mm}}\small}
{\end{list}\endgroup}
\RequirePackage{natbib}
% **********
% Appendix *
% **********
\newif\ifappend % Are we in the Appendix?
\def\appendix{\par
\setcounter{section}{0}
\setcounter{subsection}{0}
\appendtrue
}
%Math parameters
\setlength{\jot}{5\p@}
\mathchardef\@m=1500 % adapted value
\def\frenchspacing{\sfcode`\.\@m \sfcode`\?\@m \sfcode`\!\@m
\sfcode`\:\@m \sfcode`\;\@m \sfcode`\,\@m}
% Theorems
\def\th@plain{%
%% \let\thm@indent\noindent % no indent
\thm@headfont{\quad\scshape}% heading font is bold
\thm@notefont{\upshape\mdseries}% same as heading font
\thm@headpunct{.}% no period after heading
\thm@headsep 5\p@ plus\p@ minus\p@\relax
%% \let\thm@swap\@gobble
%% \thm@preskip\topsep
%% \thm@postskip\theorempreskipamount
\itshape % body font
}
\vbadness=9999
\tolerance=9999
\doublehyphendemerits=10000
\doublehyphendemerits 640000 % corresponds to badness 800
\finalhyphendemerits 1000000 % corresponds to badness 1000
\flushbottom
\frenchspacing
\ps@headings
\twocolumn
% Screen PDF compatability
\newcommand{\medline}[1]{%
\unskip\unskip\ignorespaces}
%%%%for smaller size text
\newenvironment{methods}{%
\begingroup
\def\section{%
\@startsection{section}{1}{\z@}
{-24\p@ plus -3\p@}{4\p@}
{\reset@font\raggedright\helveticabold\fontsize{10}{12}\selectfont\MakeUppercase}}
\def\subsection{%
\@startsection{subsection}{2}{\z@}
{-5\p@ plus -2\p@}{4\p@}
{\reset@font\raggedright\mathversion{bold}\fontseries{b}\fontsize{10}{12}\selectfont}}
\def\subsubsection{%
\@startsection{subsubsection}{3}{\z@}
% {-6\p@ plus -1\p@}{-1em}
{-6\p@ plus -1\p@}{0.001em}
{\reset@font\normalfont\normalsize\itshape}}
\footnotesize
\par}
{\par\endgroup\bigskip\@afterheading\@afterindentfalse}
\graphicspath{{g:/artwork/oup/bioinfo/}}
\language=2
\hyphenation{Figure Table Figures Tables}
\newcommand{\href}[2]{#2}
\renewenvironment{proof}[1][\proofname]{\par
\normalfont \topsep6\p@\@plus6\p@\relax
\labelsep 0.5em
\trivlist
\item[\hskip\labelsep\hskip1em\textsc{#1}.]\ignorespaces
}{\endtrivlist\@endpefalse}
%%Different Bonds
\def\sbond{\ensuremath{\raise.25ex\hbox{${-}\!\!\!\!{-}$}}\kern -.9pt}
\def\dbond{\ensuremath{\raise.25ex\hbox{=$\!$=}}}
\def\tbond{\ensuremath{\raise.20ex\hbox{${\equiv}\!\!\!{\equiv}$}}}
% Author queries
%\fboxsep=4\p@
%\fboxrule=0.5\p@
\newcommand{\query}[2][0pt]{}%
% \marginpar{\vspace*{#1}%
% {\parbox{\marginparwidth}{%
% \raggedright\fontsize{6}{8}\selectfont
% #2}}}}
\renewcommand{\dag}{{\mathversion{normal}$^{\dagger}$}}
\endinput
+17
View File
@@ -0,0 +1,17 @@
Q 60 32681 57 0.001744133
Q 39 3 1 0.001774569
Q 38 3 1 0.001804999
Q 35 5 1 0.001835311
Q 34 31 2 0.001894692
Q 20 11 2 0.001955154
Q 19 4 1 0.001985460
Q 15 29 5 0.002136296
Q 14 6 1 0.002166417
Q 10 11 1 0.002196193
Q 6 11 2 0.002256442
Q 5 1 1 0.002286864
Q 4 1 1 0.002317285
Q 3 36 15 0.002771602
Q 2 5 2 0.002832085
Q 1 12 9 0.003105023
Q 0 220 83 0.005594194
+28
View File
@@ -0,0 +1,28 @@
Q 42 16872292 669 0.000039651 16872292
Q 40 835329 636 0.000073697 17707621
Q 31 6544 2 0.000073783 17714165
Q 30 8882 6 0.000074084 17723047
Q 27 68499 9 0.000074305 17791546
Q 26 132041 81 0.000078277 17923587
Q 25 129378 96 0.000083033 18052965
Q 24 92056 382 0.000103665 18145021
Q 23 14341 402 0.000125720 18159362
Q 22 132838 146 0.000132789 18292200
Q 21 122274 124 0.000138641 18414474
Q 18 112183 103 0.000143361 18526657
Q 17 126981 213 0.000153804 18653638
Q 16 16356 208 0.000164810 18669994
Q 15 42804 782 0.000206223 18712798
Q 14 16026 318 0.000223025 18728824
Q 12 170250 814 0.000264087 18899074
Q 11 48351 1409 0.000337777 18947425
Q 8 1843 311 0.000354156 18949268
Q 7 62266 4435 0.000586276 19011534
Q 6 413997 50057 0.003150647 19425531
Q 5 404 58 0.003153568 19425935
Q 4 704 154 0.003161381 19426639
Q 3 1473 681 0.003196193 19428112
Q 2 17541 16462 0.004039875 19445653
Q 1 534344 354879 0.021693547 19979997
Q 0 11939 9917 0.022176642 19991936
U 8064
+52
View File
@@ -0,0 +1,52 @@
Q 60 18784147 3 0.000000160 18784147
Q 52 19002 1 0.000000213 18803149
Q 50 7152 2 0.000000319 18810301
Q 49 6797 1 0.000000372 18817098
Q 48 52188 2 0.000000477 18869286
Q 47 48775 3 0.000000634 18918061
Q 46 19447 2 0.000000739 18937508
Q 45 25983 3 0.000000896 18963491
Q 44 13455 1 0.000000949 18976946
Q 43 14573 2 0.000001053 18991519
Q 42 8697 4 0.000001263 19000216
Q 41 8645 2 0.000001368 19008861
Q 40 176603 75 0.000005264 19185464
Q 38 2503 2 0.000005368 19187967
Q 37 4117 3 0.000005523 19192084
Q 36 2924 16 0.000006356 19195008
Q 35 2323 8 0.000006772 19197331
Q 34 2344 10 0.000007292 19199675
Q 33 4279 6 0.000007603 19203954
Q 32 2092 4 0.000007810 19206046
Q 31 2625 11 0.000008382 19208671
Q 30 2828 13 0.000009057 19211499
Q 29 1581 1 0.000009108 19213080
Q 28 1543 6 0.000009420 19214623
Q 27 70916 223 0.000020948 19285539
Q 26 1288 16 0.000021777 19286827
Q 25 25551 122 0.000028065 19312378
Q 24 14345 84 0.000032390 19326723
Q 23 7308 87 0.000036878 19334031
Q 22 8358 125 0.000043325 19342389
Q 21 4836 71 0.000046983 19347225
Q 20 5888 123 0.000053325 19353113
Q 19 4656 83 0.000057600 19357769
Q 18 3948 87 0.000062081 19361717
Q 17 4418 114 0.000067954 19366135
Q 16 4226 131 0.000074702 19370361
Q 15 5760 164 0.000083144 19376121
Q 14 4697 257 0.000096384 19380818
Q 13 5246 313 0.000112503 19386064
Q 12 4170 241 0.000124908 19390234
Q 11 4095 304 0.000140557 19394329
Q 10 3857 360 0.000159087 19398186
Q 9 5300 438 0.000181617 19403486
Q 8 4206 572 0.000211050 19407692
Q 7 4676 787 0.000251541 19412368
Q 6 3923 688 0.000286924 19416291
Q 5 3294 708 0.000323333 19419585
Q 4 2936 693 0.000358965 19422521
Q 3 3928 816 0.000400897 19426449
Q 2 2613 810 0.000442533 19429062
Q 1 3515 1188 0.000503587 19432577
Q 0 567423 376636 0.019321100 20000000
+55
View File
@@ -0,0 +1,55 @@
Q 60 31721 27 0.000851171
Q 59 54 4 0.000975610
Q 58 29 5 0.001131933
Q 57 21 2 0.001194030
Q 56 14 4 0.001319137
Q 55 22 6 0.001506544
Q 54 12 4 0.001631475
Q 53 16 3 0.001724733
Q 51 10 1 0.001755541
Q 50 10 1 0.001786330
Q 49 11 3 0.001879699
Q 47 8 2 0.001941869
Q 46 17 1 0.001972140
Q 44 8 3 0.002065534
Q 43 10 1 0.002096174
Q 42 13 1 0.002126595
Q 41 14 3 0.002219444
Q 40 13 2 0.002281036
Q 38 17 4 0.002404747
Q 37 15 4 0.002528484
Q 36 12 1 0.002558742
Q 35 19 3 0.002650783
Q 34 12 3 0.002743313
Q 33 7 1 0.002773882
Q 32 21 3 0.002865508
Q 31 11 2 0.002926799
Q 30 14 3 0.003018891
Q 29 17 1 0.003048401
Q 28 11 2 0.003109549
Q 27 20 5 0.003262998
Q 26 11 1 0.003292948
Q 25 14 4 0.003415725
Q 24 16 5 0.003569212
Q 23 43 6 0.003750426
Q 21 15 1 0.003779664
Q 20 29 7 0.003992943
Q 19 22 2 0.004052089
Q 18 28 4 0.004172204
Q 16 25 5 0.004323390
Q 15 24 5 0.004474480
Q 14 25 5 0.004625204
Q 13 23 3 0.004714365
Q 12 22 1 0.004741963
Q 11 32 11 0.005075674
Q 10 35 7 0.005285315
Q 9 32 12 0.005648503
Q 8 33 8 0.005888126
Q 7 39 7 0.006095506
Q 6 42 14 0.006515953
Q 5 38 15 0.006966725
Q 4 37 12 0.007325113
Q 3 49 18 0.007862737
Q 2 63 21 0.008486434
Q 1 55 27 0.009292156
Q 0 153 77 0.011576593
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Std;
my %opts = (n=>33088, s=>100);
getopts('n:', \%opts);
my $pseudo = .5;
my $tot = $pseudo;
my $err = $pseudo;
my $tot_last_out = -$opts{s};
my $state = 0;
my $mapq = 0;
while (<>) {
chomp;
if (/^Q\t(\d+)\t(\d+)\t(\d+)/) {
$tot += $2;
$err += $3;
if ($tot - $tot_last_out >= $opts{s}) {
print join("\t", $1, $err/$tot, $tot / $opts{n}), "\n";
$tot_last_out = $tot;
$state = 0;
} else {
$state = 1;
$mapq = $1;
}
}
}
if ($state) {
print join("\t", $mapq, $err/$tot, $tot / $opts{n}), "\n";
}
+4
View File
@@ -0,0 +1,4 @@
Q 40 31897 63 0.001975107
Q 3 423 267 0.010210396
Q 2 162 120 0.013853827
Q 1 188 172 0.019038874
+10
View File
@@ -0,0 +1,10 @@
./pbsim --prefix pb-1 --depth 0.1 --sample-fastq m131017_060208_42213_c100579642550000001823095604021496_s1_p0.1.subreads.fastq --length-min 1000 --length-max 30000 --seed 11 hs38.fa
bin/mason_variator -ir hs38.fa -s 1 -ov hs38-s1.vcf --snp-rate 1e-3 --small-indel-rate 2e-4 --sv-indel-rate 0 --sv-inversion-rate 0 --sv-translocation-rate 0 --sv-duplication-rate 0 --max-small-indel-size 10
bin/mason_simulator -ir hs38.fa -iv hs38-s1.vcf -n 1000000 --seed 1 -o s1_1.fq -or s1_2.fq -oa s1.sam --illumina-prob-mismatch-scale 2.5
bin/mason_variator -ir hs38.fa -s 2 -ov hs38-s2.vcf --snp-rate 1e-3 --small-indel-rate 2e-4 --sv-indel-rate 0 --sv-inversion-rate 0 --sv-translocation-rate 0 --sv-duplication-rate 0 --max-small-indel-size 10
bin/mason_simulator -ir hs38.fa -iv hs38-s2.vcf -n 1000000 --seed 2 -o mason-s2_1.fq -or mason-s2_2.fq -oa mason-s2.sam --illumina-prob-mismatch-scale 2.5 --illumina-read-length 150
bin/mason_variator -ir hs38.fa -s 3 -ov hs38-s3.vcf --snp-rate 1e-3 --small-indel-rate 2e-4 --sv-indel-rate 0 --sv-inversion-rate 0 --sv-translocation-rate 0 --sv-duplication-rate 0 --max-small-indel-size 10
bin/mason_simulator -ir hs38.fa -iv hs38-s3.vcf -n 10000000 --seed 3 -o mason-s3_1.fq -or mason-s3_2.fq -oa mason-s3.sam --illumina-prob-mismatch-scale 2.5 --illumina-read-length 150
+49
View File
@@ -0,0 +1,49 @@
Q 60 32070 190 0.005924540
Q 59 62 2 0.005975352
Q 58 37 5 0.006123908
Q 57 40 7 0.006333633
Q 56 39 6 0.006512032
Q 55 32 2 0.006567534
Q 54 54 2 0.006618420
Q 53 33 4 0.006735255
Q 52 39 2 0.006788866
Q 51 48 3 0.006871264
Q 50 34 2 0.006925634
Q 49 32 3 0.007011070
Q 48 35 2 0.007064967
Q 47 36 4 0.007179896
Q 46 23 1 0.007205495
Q 45 25 1 0.007230614
Q 44 17 3 0.007318716
Q 43 17 2 0.007376121
Q 42 31 5 0.007522016
Q 41 25 4 0.007638486
Q 40 26 4 0.007754541
Q 39 35 2 0.007807258
Q 37 18 4 0.007924896
Q 36 13 3 0.008013162
Q 35 15 2 0.008070411
Q 34 20 3 0.008156805
Q 33 11 1 0.008184501
Q 32 15 3 0.008272003
Q 31 25 1 0.008296107
Q 29 8 1 0.008324472
Q 28 7 2 0.008383452
Q 27 9 2 0.008441894
Q 26 30 2 0.008494888
Q 23 2 1 0.008524710
Q 22 11 3 0.008612846
Q 20 23 3 0.008697760
Q 19 6 1 0.008726479
Q 18 8 1 0.008754658
Q 16 6 1 0.008783354
Q 13 2 1 0.008813108
Q 12 4 2 0.008872604
Q 11 7 2 0.008931275
Q 10 4 3 0.009021009
Q 9 6 4 0.009140436
Q 8 6 3 0.009229559
Q 7 5 1 0.009258419
Q 6 8 3 0.009346925
Q 4 8 5 0.009495872
Q 3 17 8 0.009732801
+340
View File
@@ -0,0 +1,340 @@
@article{Chaisson:2012aa,
Author = {Chaisson, Mark J and Tesler, Glenn},
Journal = {BMC Bioinformatics},
Pages = {238},
Title = {{Mapping single molecule sequencing reads using basic local alignment with successive refinement (BLASR): application and theory}},
Volume = {13},
Year = {2012}}
@article{Liu:2016ab,
Author = {Liu, Bo and others},
Journal = {Bioinformatics},
Pages = {1625-31},
Title = {{rHAT}: fast alignment of noisy long reads with regional hashing},
Volume = {32},
Year = {2016}}
@article{Liu:2017aa,
Author = {Liu, Bo and others},
Journal = {Bioinformatics},
Pages = {192-201},
Title = {{LAMSA}: fast split read alignment with long approximate matches},
Volume = {33},
Year = {2017}}
@article{Lin:2017aa,
Author = {Lin, Hsin-Nan and Hsu, Wen-Lian},
Journal = {Bioinformatics},
Title = {Kart: a divide-and-conquer algorithm for {NGS} read alignment},
Year = {2017}}
@article{Li:2013aa,
Author = {Li, Heng},
Journal = {arXiv:1303.3997},
Title = {Aligning sequence reads, clone sequences and assembly contigs with {BWA-MEM}},
archivePrefix = "arXiv",
eprint = {1303.3997},
primaryClass = "q-bio",
Year = {2013}}
@article{Sovic:2016aa,
Author = {Sovi{\'c}, Ivan and others},
Journal = {Nat Commun},
Pages = {11307},
Title = {Fast and sensitive mapping of nanopore sequencing reads with {GraphMap}},
Volume = {7},
Year = {2016}}
@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{Li:2016aa,
Author = {Li, Heng},
Journal = {Bioinformatics},
Pages = {2103-10},
Title = {Minimap and miniasm: fast mapping and de novo assembly for noisy long sequences},
Volume = {32},
Year = {2016}}
@misc{Ruan:2016,
title = {Ultra-fast de novo assembler using long noisy reads},
author = {Jue Ruan},
journal = {Unpulished},
howpublished = {\href{https://github.com/ruanjue/smartdenovo}{https://github.com/ruanjue/smartdenovo}},
year = {2016}}
@article{Miller:1988aa,
Author = {Miller, W and Myers, E W},
Journal = {Bull Math Biol},
Number = {2},
Pages = {97-120},
Title = {Sequence comparison with concave weighting functions},
Volume = {50},
Year = {1988}}
@article{Gotoh:1990aa,
Author = {Gotoh, O},
Journal = {Bull Math Biol},
Pages = {359-73},
Title = {Optimal sequence alignment allowing for long gaps},
Volume = {52},
Year = {1990}}
@article{Wu:1996aa,
Author = {Wu, Sun and others},
Journal = {Algorithmica},
Pages = {50-67},
Title = {A subquadratic algorithm for approximate limited expression matching},
Volume = {15},
Year = {1996}}
@article{Daily:2016aa,
Author = {Daily, Jeff},
Journal = {BMC Bioinformatics},
Month = {Feb},
Pages = {81},
Title = {Parasail: {SIMD C} library for global, semi-global, and local pairwise sequence alignments},
Volume = {17},
Year = {2016}}
@article{Sedlazeck169557,
author = {Sedlazeck, Fritz J and others},
title = {Accurate detection of complex structural variations using single molecule sequencing},
note = {doi:10.1101/169557},
journal = {bioRxiv},
year = {2017}}
@article{Altschul:1997vn,
Author = {Altschul, S F and others},
Journal = {Nucleic Acids Res},
Pages = {3389-402},
Title = {Gapped {BLAST} and {PSI-BLAST}: a new generation of protein database search programs},
Volume = {25},
Year = {1997}}
@article{Sosic:2017aa,
Author = {{\v S}o{\v s}i\'{c}, Martin and {\v S}ikic, Mile},
Journal = {Bioinformatics},
Pages = {1394-1395},
Title = {Edlib: a {C/C++} library for fast, exact sequence alignment using edit distance},
Volume = {33},
Year = {2017}}
@article{Abouelhoda:2005aa,
Author = {Mohamed Ibrahim Abouelhoda and Enno Ohlebusch},
Journal = {J. Discrete Algorithms},
Pages = {321-41},
Title = {Chaining algorithms for multiple genome comparison},
Volume = {3},
Year = {2005}}
@article{Ono:2013aa,
Author = {Ono, Yukiteru and others},
Journal = {Bioinformatics},
Pages = {119-21},
Title = {{PBSIM}: {PacBio} reads simulator--toward accurate genome assembly},
Volume = {29},
Year = {2013}}
@article {Jain128835,
author = {Jain, Miten and others},
title = {Nanopore sequencing and assembly of a human genome with ultra-long reads},
year = {2017},
note = {doi:10.1101/128835},
publisher = {Cold Spring Harbor Labs Journals},
journal = {bioRxiv}}
@article{Lau:2016aa,
Author = {Lau, Bayo and others},
Journal = {Bioinformatics},
Pages = {3829-3832},
Title = {{LongISLND}: in silico sequencing of lengthy and noisy datatypes},
Volume = {32},
Year = {2016}}
@article{Robinson:2011aa,
Author = {Robinson, James T and others},
Journal = {Nat Biotechnol},
Pages = {24-6},
Title = {Integrative genomics viewer},
Volume = {29},
Year = {2011}}
@article{Gotoh:1982aa,
Author = {Gotoh, O},
Journal = {J Mol Biol},
Pages = {705-8},
Title = {An improved algorithm for matching biological sequences},
Volume = {162},
Year = {1982}}
@article{Altschul:1986aa,
Author = {Altschul, S F and Erickson, B W},
Journal = {Bull Math Biol},
Pages = {603-16},
Title = {Optimal sequence alignment using affine gap costs},
Volume = {48},
Year = {1986}}
@article{Wu:2005vn,
Author = {Wu, Thomas D and Watanabe, Colin K},
Journal = {Bioinformatics},
Pages = {1859-75},
Title = {{GMAP}: a genomic mapping and alignment program for {mRNA} and {EST} sequences},
Volume = {21},
Year = {2005}}
@article{Iwata:2012aa,
Author = {Iwata, Hiroaki and Gotoh, Osamu},
Journal = {Nucleic Acids Res},
Pages = {e161},
Title = {Benchmarking spliced alignment programs including {Spaln2}, an extended version of {Spaln} that incorporates additional species-specific features},
Volume = {40},
Year = {2012}}
@article{Dobin:2013kx,
Author = {Dobin, Alexander and others},
Journal = {Bioinformatics},
Pages = {15-21},
Title = {{STAR}: ultrafast universal {RNA-seq} aligner},
Volume = {29},
Year = {2013}}
@article{Byrne:2017aa,
Author = {Byrne, Ashley and others},
Journal = {Nat Commun},
Pages = {16027},
Title = {Nanopore long-read {RNAseq} reveals widespread transcriptional variation among the surface receptors of individual {B} cells},
Volume = {8},
Year = {2017}}
@article{Roberts:2004fv,
Author = {Roberts, Michael and others},
Journal = {Bioinformatics},
Pages = {3363-9},
Title = {Reducing storage requirements for biological sequence comparison},
Volume = {20},
Year = {2004}}
@article{Zhang:2006aa,
Author = {Zhang, Miao and Gish, Warren},
Journal = {Bioinformatics},
Pages = {13-20},
Title = {Improved spliced alignment from an information theoretic approach},
Volume = {22},
Year = {2006}}
@article{Li:2007aa,
Author = {Li, Heng and others},
Journal = {BMC Bioinformatics},
Pages = {349},
Title = {A cross-species alignment tool {(CAT)}},
Volume = {8},
Year = {2007}}
@article{Farrar:2007hs,
Author = {Farrar, Michael},
Journal = {Bioinformatics},
Pages = {156-61},
Title = {{Striped Smith-Waterman speeds database searches six times over other SIMD implementations}},
Volume = {23},
Year = {2007}}
@techreport{Holtgrewe:2010aa,
Address = {Freie Universit{\"a}t Berlin},
Author = {Holtgrewe, M.},
Institution = {Institut f{\"u}r Mathematik und Informatik},
Number = {TR-B-10-06},
Title = {Mason -- a read simulator for second generation sequencing data},
Year = {2010}}
@article{Zaharia:2011aa,
Author = {Zaharia, Matei and others},
Journal = {arXiv:1111:5572},
Title = {Faster and More Accurate Sequence Alignment with {SNAP}},
Year = {2011}}
@article{Irimia:2008aa,
Author = {Irimia, Manuel and Roy, Scott William},
Journal = {PLoS Genet},
Pages = {e1000148},
Title = {Evolutionary convergence on highly-conserved 3' intron structures in intron-poor eukaryotes and insights into the ancestral eukaryotic genome},
Volume = {4},
Year = {2008}}
@article{Depristo:2011vn,
Author = {Depristo, Mark A and others},
Journal = {Nat Genet},
Pages = {491-8},
Title = {A framework for variation discovery and genotyping using next-generation {DNA} sequencing data},
Volume = {43},
Year = {2011}}
@article{Kurtz:2004zr,
Author = {Kurtz, Stefan and others},
Journal = {Genome Biol},
Pages = {R12},
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}}
+724
View File
@@ -0,0 +1,724 @@
\documentclass{bioinfo}
\copyrightyear{2018}
\pubyear{2018}
\usepackage{graphicx}
\usepackage{hyperref}
\usepackage{url}
\usepackage{amsmath}
\usepackage[ruled,vlined]{algorithm2e}
\newcommand\mycommfont[1]{\footnotesize\rmfamily{\it #1}}
\SetCommentSty{mycommfont}
\SetKwComment{Comment}{$\triangleright$\ }{}
\usepackage{natbib}
\bibliographystyle{apalike}
\DeclareMathOperator*{\argmax}{argmax}
\begin{document}
\firstpage{1}
\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}
\maketitle
\begin{abstract}
\section{Motivation:} Recent advances in sequencing technologies promise
ultra-long reads of $\sim$100 kilo bases (kb) in average, full-length mRNA or
cDNA reads in high throughput and genomic contigs over 100 mega bases (Mb) in
length. Existing alignment programs are unable or inefficient to process such data
at scale, which presses for the development of new alignment algorithms.
\section{Results:} Minimap2 is a general-purpose alignment program to map DNA or long
mRNA sequences against a large reference database. It works with accurate short
reads of $\ge$100bp in length, $\ge$1kb genomic reads at error rate $\sim$15\%,
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 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}
\section{Contact:} hengli@broadinstitute.org
\end{abstract}
\section{Introduction}
Single Molecule Real-Time (SMRT) sequencing technology and Oxford Nanopore
technologies (ONT) produce reads over 10kbp in length at an error rate
$\sim$15\%. Several aligners have been developed for such
data~\citep{Chaisson:2012aa,Li:2013aa,Liu:2016ab,Sovic:2016aa,Liu:2017aa,Lin:2017aa,Sedlazeck169557}.
Most of them were five times as slow as mainstream short-read
aligners~\citep{Langmead:2012fk,Li:2013aa} in terms of the number of bases
mapped per second. We speculated there could be substantial room for speedup on
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{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.
Both SMRT and ONT have been applied to the sequencing of spliced mRNAs (RNA-seq). While
traditional mRNA aligners work~\citep{Wu:2005vn,Iwata:2012aa}, they are not
optimized for long noisy sequence reads and are tens of times slower than
dedicated long-read aligners. When developing minimap2 initially for aligning
genomic DNA only, we realized minor modifications could enable the base
algorithm to map mRNAs as well. Minimap2 becomes a first RNA-seq aligner
specifically designed for long noisy reads. We have also extended the original
algorithm to map short reads at a speed faster than several mainstream
short-read mappers.
In this article, we will describe the minimap2 algorithm and its applications
to different types of input sequences. We will evaluate the performance and
accuracy of minimap2 on several simulated and real data sets and demonstrate
the versatility of minimap2.
\begin{methods}
\section{Methods}
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, 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
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
chaining, the ability to produce base-level alignment and the support of
spliced alignment.
\subsection{Chaining}
\subsubsection{Chaining}
An \emph{anchor} is a 3-tuple $(x,y,w)$, indicating interval $[x-w+1,x]$ on the
reference matching interval $[y-w+1,y]$ on the query. Given a list of anchors
sorted by ending reference position $x$, let $f(i)$ be the maximal chaining
score up to the $i$-th anchor in the list. $f(i)$ can be calculated with
dynamic programming:
\begin{equation}\label{eq:chain}
f(i)=\max\big\{\max_{i>j\ge 1} \{ f(j)+\alpha(j,i)-\beta(j,i) \},w_i\big\}
\end{equation}
where $\alpha(j,i)=\min\big\{\min\{y_i-y_j,x_i-x_j\},w_i\big\}$ is the number of
matching bases between the two anchors. $\beta(j,i)>0$ is the gap cost. It
equals $\infty$ if $y_j\ge y_i$ or $\max\{y_i-y_j,x_i-x_j\}>G$ (i.e. the
distance between two anchors is too large); otherwise
\begin{equation}\label{eq:chain-gap}
\beta(j,i)=\gamma_c\big((y_i-y_j)-(x_i-x_j)\big)
\end{equation}
In implementation, a gap of length $l$ costs
\[
\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 $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
accelerate chaining.
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(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)+\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
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~\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
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,
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}
Minimap2 performs DP-based global alignment between adjacent anchors in a
chain. It uses a 2-piece affine gap cost~\citep{Gotoh:1990aa}:
\begin{equation}\label{eq:2-piece}
\gamma_a(l)=\min\{q+|l|\cdot e,\tilde{q}+|l|\cdot\tilde{e}\}
\end{equation}
Without losing generality, we always assume $q+e<\tilde{q}+\tilde{e}$.
On the condition that $e>\tilde{e}$, it applies cost $q+|l|\cdot e$ to gaps
shorter than $\lceil(\tilde{q}-q)/(e-\tilde{e})\rceil$ and applies
$\tilde{q}+|l|\cdot\tilde{e}$ to longer gaps. This scheme helps to recover
longer insertions and deletions~(INDELs).
The equation to compute the optimal alignment under $\gamma_a(\cdot)$ is
\begin{equation}\label{eq:ae86}
\left\{\begin{array}{l}
H_{ij} = \max\{H_{i-1,j-1}+s(i,j),E_{ij},F_{ij},\tilde{E}_{ij},\tilde{F}_{ij}\}\\
E_{i+1,j}= \max\{H_{ij}-q,E_{ij}\}-e\\
F_{i,j+1}= \max\{H_{ij}-q,F_{ij}\}-e\\
\tilde{E}_{i+1,j}= \max\{H_{ij}-\tilde{q},\tilde{E}_{ij}\}-\tilde{e}\\
\tilde{F}_{i,j+1}= \max\{H_{ij}-\tilde{q},\tilde{F}_{ij}\}-\tilde{e}
\end{array}\right.
\end{equation}
where $s(i,j)$ is the score between the $i$-th reference base and $j$-th query
base. Eq.~(\ref{eq:ae86}) is a natural extension to the equation under affine
gap cost~\citep{Gotoh:1982aa,Altschul:1986aa}.
\subsubsection{The Suzuki-Kasahara formulation}
When we allow gaps longer than several hundred base pairs, nucleotide-level
alignment is much slower than chaining. SSE acceleration is critical to the
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{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}-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
\begin{equation}\label{eq:suzuki}
\left\{\begin{array}{lll}
z_{ij}&=&\max\{s(i,j),x_{i-1,j}+v_{i-1,j},y_{i,j-1}+u_{i,j-1},\\
&&\tilde{x}_{i-1,j}+v_{i-1,j},\tilde{y}_{i,j-1}+u_{i,j-1}\}\\
u_{ij}&=&z_{ij}-v_{i-1,j}\\
v_{ij}&=&z_{ij}-u_{i,j-1}\\
x_{ij}&=&\max\{0,x_{i-1,j}+v_{i-1,j}-z_{ij}+q\}-q-e\\
y_{ij}&=&\max\{0,y_{i,j-1}+u_{i,j-1}-z_{ij}+q\}-q-e\\
\tilde{x}_{ij}&=&\max\{0,\tilde{x}_{i-1,j}+v_{i-1,j}-z_{ij}+\tilde{q}\}-\tilde{q}-\tilde{e}\\
\tilde{y}_{ij}&=&\max\{0,\tilde{y}_{i,j-1}+u_{i,j-1}-z_{ij}+\tilde{q}\}-\tilde{q}-\tilde{e}
\end{array}\right.
\end{equation}
where $z_{ij}$ is a temporary variable that does not need to be stored.
An important property of Eq.~(\ref{eq:suzuki}) is that all values are bounded
by scoring parameters. To see that,
\[
x_{ij}=E_{i+1,j}-H_{ij}=\max\{-q,E_{ij}-H_{ij}\}-e
\]
With $E_{ij}\le H_{ij}$, we have
\[
-q-e\le x_{ij}\le\max\{-q,0\}-e=-e
\]
and similar inequations for $y_{ij}$, $\tilde{x}_{ij}$ and $\tilde{y}_{ij}$.
In addition,
\[
u_{ij}=z_{ij}-v_{i-1,j}\ge\max\{x_{i-1,j},\tilde{x}_{i-1,j}\}\ge-q-e
\]
As the maximum value of $z_{ij}=H_{ij}-H_{i-1,j-1}$ is $M$, the maximal
matching score, we can derive
\[
u_{ij}\le M-v_{i-1,j}\le M+q+e
\]
In conclusion, in Eq.~(\ref{eq:suzuki}), $x$ and $y$ are bounded by $[-q-e,-e]$,
$\tilde{x}$ and $\tilde{y}$ by $[-\tilde{q}-\tilde{e},-\tilde{e}]$, and $u$ and
$v$ by $[-q-e,M+q+e]$. When $-128\le-q-e<M+q+e\le127$, each of them can be stored as
a 8-bit integer. This enables 16-way SSE vectorization regardless of the peak
score of the alignment.
For a more efficient SSE implementation, we transform the row-column coordinate
to the diagonal-antidiagonal coordinate by letting $r\gets i+j$ and $t\gets i$.
Eq.~(\ref{eq:suzuki}) becomes:
\begin{equation*}
\left\{\begin{array}{lll}
z_{rt}&=&\max\{s(t,r-t),x_{r-1,t-1}+v_{r-1,t-1},y_{r-1,t}\\
&&+u_{r-1,t},\tilde{x}_{r-1,t-1}+v_{r-1,t-1},\tilde{y}_{r-1,t}+u_{r-1,t}\}\\
u_{rt}&=&z_{rt}-v_{r-1,t-1}\\
v_{rt}&=&z_{rt}-u_{r-1,t}\\
x_{rt}&=&\max\{0,x_{r-1,t-1}+v_{r-1,t-1}-z_{rt}+q\}-q-e\\
y_{rt}&=&\max\{0,y_{r-1,t}+u_{r-1,t}-z_{rt}+q\}-q-e\\
\tilde{x}_{rt}&=&\max\{0,\tilde{x}_{r-1,t-1}+v_{r-1,t-1}-z_{rt}+\tilde{q}\}-\tilde{q}-\tilde{e}\\
\tilde{y}_{rt}&=&\max\{0,\tilde{y}_{r-1,t}+u_{r-1,t}-z_{rt}+\tilde{q}\}-\tilde{q}-\tilde{e}
\end{array}\right.
\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 (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 are
\[
\left\{\begin{array}{l}
x_{r-1,-1}=y_{r-1,r}=-q-e\\
\tilde{x}_{r-1,-1}=\tilde{y}_{r-1,r}=-\tilde{q}-\tilde{e}\\
u_{r-1,r}=v_{r-1,-1}=\eta(r)\\
\end{array}\right.
\]
where
\[
\eta(r)=\left\{\begin{array}{ll}
-q-e & (r=0) \\
-e & (r<\lceil\frac{\tilde{q}-q}{e-\tilde{e}}-1\rceil) \\
r\cdot(e-\tilde{e})-(\tilde{q}-q)-\tilde{e} & (r=\lceil\frac{\tilde{q}-q}{e-\tilde{e}}-1\rceil) \\
-\tilde{e} & (r>\lceil\frac{\tilde{q}-q}{e-\tilde{e}}-1\rceil)
\end{array}\right.
\]
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 the time.
\subsubsection{The Z-drop heuristic}
With global alignment, minimap2 may force to align unrelated sequences between
two adjacent anchors. To avoid such an artifact, we compute accumulative
alignment score along the alignment path and break the alignment where the
score drops too fast in the diagonal direction. More precisely, let $S(i,j)$ be
the alignment score along the alignment path ending at cell $(i,j)$ in the DP
matrix. We break the alignment if there exist $(i',j')$ and $(i,j)$, $i'<i$ and
$j'<j$, such that
\[
S(i',j')-S(i,j)>Z+e\cdot|(i-i')-(j-j')|
\]
where $e$ is the gap extension cost and $Z$ is an arbitrary threshold.
This strategy is first used in BWA-MEM. It is similar to X-drop employed in
BLAST~\citep{Altschul:1997vn}, but unlike X-drop, it would not break the
alignment in the presence of a single long gap.
When minimap2 breaks a global alignment between two anchors, it performs local
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
mode, the chaining gap cost distinguishes insertions to and deletions from the
reference: $\gamma_c(l)$ in Eq.~(\ref{eq:chain-gap}) takes the form of
\[
\gamma_c(l)=\left\{\begin{array}{ll}
0.01\cdot\bar{w}\cdot l+0.5\log_2 l & (l>0) \\
\min\{0.01\cdot\bar{w}\cdot|l|,\log_2|l|\} & (l<0)
\end{array}\right.
\]
Similarly, the gap cost function used for DP-based alignment is changed to
\[
\gamma_a(l)=\left\{\begin{array}{ll}
q+l\cdot e & (l>0) \\
\min\{q+|l|\cdot e,\tilde{q}\} & (l<0)
\end{array}\right.
\]
In alignment, a deletion no shorter than $\lceil(\tilde{q}-q)/e\rceil$ is
regarded as an intron, which pays no cost to gap extensions.
To pinpoint precise splicing junctions, minimap2 introduces reference-dependent
cost to penalize non-canonical splicing:
\begin{equation}\label{eq:splice}
\left\{\begin{array}{l}
H_{ij} = \max\{H_{i-1,j-1}+s(i,j),E_{ij},F_{ij},\tilde{E}_{ij}-a(i)\}\\
E_{i+1,j}= \max\{H_{ij}-q,E_{ij}\}-e\\
F_{i,j+1}= \max\{H_{ij}-q,F_{ij}\}-e\\
\tilde{E}_{i+1,j}= \max\{H_{ij}-d(i)-\tilde{q},\tilde{E}_{ij}\}\\
\end{array}\right.
\end{equation}
Let $T$ be the reference sequence. $d(i)$ is computed as
\[d(i)=\left\{\begin{array}{ll}
0 & \mbox{if $T[i+1,i+3]$ is ${\tt GTA}$ or ${\tt GTG}$} \\
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 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}$} \\
p/2 & \mbox{if $T[i-2,i]$ is ${\tt AAG}$ or ${\tt GAG}$} \\
p & \mbox{otherwise}
\end{array}\right.\]
models the acceptor signal. Eq.~(\ref{eq:splice}) is close to an equation in
\citet{Zhang:2006aa} except that we allow insertions immediately followed by
deletions and vice versa; in addition, we use the Suzuki-Kasahara diagonal
formulation in actual implementation.
If RNA-seq reads are not sequenced from stranded libraries, the read strand
relative to the underlying transcript is unknown. By default, minimap2 aligns
each chain twice, first assuming ${\tt GT}$--${\tt AG}$ as the splicing signal
and then assuming ${\tt CT}$--${\tt AC}$, the reverse complement of ${\tt
GT}$--${\tt AG}$, as the splicing signal. The alignment with a higher score is
taken as the final alignment. This procedure also infers the relative strand of
reads that span canonical splicing sites.
In the spliced alignment mode, minimap2 further increases the density of
minimizers and disables banded alignment. Together with the two-round DP-based
alignment, spliced alignment is several times slower than genomic DNA
alignment.
\subsection{Aligning short paired-end reads}
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 ($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} \\
\min\{0.01\cdot\bar{w}\cdot|l|,\log_2|l|\} & \mbox{otherwise}
\end{array}\right.
\]
After identifying primary chains (Section~\ref{sec:primary}), we split each
fragment chain into two read chains and perform alignment for each read as in
Section~\ref{sec:genomic}. Finally, we pair hits of each read end to find
consistent paired-end alignments.
\end{methods}
\section{Results}
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 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.
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}
As a sanity check, we evaluated minimap2 on simulated human reads along with
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; \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
higher power to distinguish unique and repetitive hits, and achieves overall
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, 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 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.
Kart, minialign and minimap2 are over 70 times faster than others. We have also
examined tens of $\ge$100bp INDELs in IGV~\citep{Robinson:2011aa} and can
confirm the observation by~\citet{Sedlazeck169557} that BWA-MEM often breaks
them into shorter gaps. The issue is much alleviated with minimap2, thanks
to the 2-piece affine gap cost.
\subsection{Aligning long spliced reads}
We evaluated minimap2 on SIRV control data~(AC:SRR5286959;
\citealp{Byrne:2017aa}) where the truth is known. Minimap2 predicted 59\,918
introns from 11\,018 reads. 93.8\% of splice juctions are precise. We examined
wrongly predicted junctions and found the majority were caused by clustered
splicing signals (e.g. two adjacent ${\tt GT}$ sites). When INDEL sequencing
errors are frequent, it is difficult to find precise splicing sites in this
case. If we allow up to 10bp distance from true splicing sites, 98.4\% of
aligned introns are approximately correct. It is worth noting that for SIRV, we
asked minimap2 to model the ${\tt GT..AG}$ splicing signal only without extra
bases. This is because SIRV does not honor the evolutionarily prevalent signal
${\tt GT[A/G]..[C/T]AG}$~\citep{Irimia:2008aa}.
\begin{table}[!tb]
\processtable{Evaluation of junction accuracy on 2D ONT reads}
{\footnotesize\label{tab:intron}
\begin{tabular}{p{3.1cm}rrrr}
\toprule
& GMAP & minimap2 & SpAln & STAR\\
\midrule
Run time (CPU min) & 631 & 15.9 & 2\,076 & 33.9 \\
Peak RAM (GByte) & 8.9 & 14.5 & 3.2 & 29.2\vspace{1em}\\
\# aligned reads & 103\,669 & 104\,199 & 103\,711 & 26\,479 \\
\# chimeric alignments & 1\,904 & 1\,488 & 0 & 0 \\
\# non-spliced alignments & 15\,854 & 14\,798 & 17\,033 & 10\,545\vspace{1em}\\
\# aligned introns & 692\,275 & 693\,553 & 692\,945 & 78\,603 \\
\# novel introns & 11\,239 & 3\,113 & 8\,550 & 1\,214 \\
\% exact introns & 83.8\% & 94.0\% & 87.9\% & 55.2\% \\
\% approx. introns & 91.8\% & 96.9\% & 92.5\% & 82.4\% \\
\botrule
\end{tabular}
}{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
\href{http://bit.ly/star-pb}{http://bit.ly/star-pb}). The alignments were
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. 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;
\citealp{Wu:2005vn}), minimap2, SpAln~(v2.3.1; \citealp{Iwata:2012aa}) and
STAR~(v2.5.3a; \citealp{Dobin:2013kx}). In general, minimap2 is more
consistent with existing annotations (Table~\ref{tab:intron}): it finds
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 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
able to improve their accuracy further.
%\begin{table}[!tb]
%\processtable{Evaluation of junction accuracy on SMRT Iso-Seq reads}
%{\footnotesize
%\begin{tabular}{lrrrr}
%\toprule
% & GMAP & minimap2 & SpAln & STAR \\ % one GMAP thread took 14 days to align a tiny fraction of reads
%\midrule
%Run time (CPU min) & - & 243 & 2,352 & 1,647 \\
%\# aligned reads & 1,113,502 & 1,123,025 & 1,094,092 & 682,452 \\
%\# chimeric alignments & 48,927 & 33,091 & 0 & 0 \\
%\# non-spliced alignments & 334,097 & 339,081 & 291,447 & 272,536 \vspace{1em}\\
%\# aligned introns & 8,922,221 & 9,071,755 & 9,208,564 & 3,029,121 \\
%\# novel introns & 48,927 & 42,773 & 82,230 & 17,791 \\
%\% exact introns & 90.6\% & 94.9\% & 91.7\% & 84.7\% \\
%\% approx. introns & 94.0\% & 96.9\% & 93.4\% & 93.8\% \\
%\botrule
%\end{tabular}
%}{}
%\end{table}
\subsection{Aligning short genomic reads}
We evaluated minimap2 along with Bowtie2~(v2.3.3; \citealt{Langmead:2012fk}), BWA-MEM and
SNAP (v1.0beta23; \citealt{Zaharia:2011aa}). Minimap2 is 3--4 times as fast as Bowtie2 and
BWA-MEM, but is 1.3 times slower than SNAP. Minimap2 is more accurate on this
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 implement a similar heuristic
in minimap2 in future.
To evaluate the accuracy of minimap2 on real data, we aligned human reads
(AC:ERR1341796) with BWA-MEM and minimap2, and called SNPs and small INDELs
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
dataset~\citep{Li223297} for
ERR1341796. In this evaluation, minimap2 has higher SNP false negative rate
(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{Aligning long-read assemblies}
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}
Minimap2 is a versatile mapper and pairwise aligner for nucleotide sequences.
It works with short reads, assembly contigs and long noisy genomic and RNA-seq
reads, and can be used as a read mapper, long-read overlapper or a full-genome
aligner. Minimap2 is also accurate and efficient, often outperforming other
domain-specific alignment tools in terms of both speed and accuracy.
The capability of minimap2 comes from a fast base-level alignment algorithm and
an accurate chaining algorithm. When aligning long query sequences, base-level
alignment is often the performance bottleneck. The Suzuki-Kasahara algorithm
greatly alleviates the bottleneck and enables DP-based splice alignment
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 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 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.
Modern mainstream aligners often use a full-text index, such as suffix array or
FM-index, to index reference sequences. An advantage of this approach is that
we can use exact seeds of arbitrary lengths, which helps to increase seed
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
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 minimap2 users who have greatly helped to
suggest features and to fix various issues.
\paragraph{Funding\textcolon} NHGRI 1R01HG010040-01
\bibliography{minimap2}
\end{document}
+62
View File
@@ -0,0 +1,62 @@
Q 60 18579866 27 0.000001453 18579866
Q 59 27087 4 0.000001666 18606953
Q 58 21435 1 0.000001718 18628388
Q 57 45663 3 0.000001874 18674051
Q 56 36031 2 0.000001978 18710082
Q 55 18499 2 0.000002082 18728581
Q 54 14754 2 0.000002187 18743335
Q 53 25541 2 0.000002291 18768876
Q 52 26397 5 0.000002554 18795273
Q 51 15090 3 0.000002711 18810363
Q 50 13425 11 0.000003294 18823788
Q 49 15175 2 0.000003397 18838963
Q 48 19407 4 0.000003606 18858370
Q 47 11538 16 0.000004452 18869908
Q 46 12558 17 0.000005349 18882466
Q 45 40362 28 0.000006817 18922828
Q 44 10465 13 0.000007500 18933293
Q 43 10098 20 0.000008552 18943391
Q 42 10682 19 0.000009549 18954073
Q 41 9823 11 0.000010125 18963896
Q 40 9685 16 0.000010963 18973581
Q 39 10273 18 0.000011905 18983854
Q 38 9515 18 0.000012847 18993369
Q 37 9474 27 0.000014261 19002843
Q 36 10430 25 0.000015568 19013273
Q 35 9241 34 0.000017348 19022514
Q 34 9162 31 0.000018968 19031676
Q 33 10164 49 0.000021532 19041840
Q 32 9152 55 0.000024408 19050992
Q 31 9252 35 0.000026233 19060244
Q 30 9872 55 0.000029103 19070116
Q 29 8938 65 0.000032496 19079054
Q 28 8951 73 0.000036306 19088005
Q 27 9949 95 0.000041261 19097954
Q 26 9784 97 0.000046316 19107738
Q 25 10126 97 0.000051366 19117864
Q 24 11260 123 0.000057765 19129124
Q 23 10047 114 0.000063691 19139171
Q 22 9661 123 0.000070083 19148832
Q 21 10339 168 0.000078813 19159171
Q 20 17928 193 0.000088804 19177099
Q 19 9842 193 0.000098817 19186941
Q 18 14737 247 0.000111605 19201678
Q 17 10218 238 0.000123934 19211896
Q 16 10271 242 0.000136457 19222167
Q 15 12241 333 0.000153683 19234408
Q 14 9189 336 0.000171070 19243597
Q 13 9493 515 0.000197734 19253090
Q 12 11502 743 0.000236185 19264592
Q 11 8211 507 0.000262390 19272803
Q 10 9133 606 0.000293695 19281936
Q 9 10014 931 0.000341801 19291950
Q 8 8436 698 0.000377816 19300386
Q 7 8443 705 0.000414163 19308829
Q 6 10203 944 0.000462808 19319032
Q 5 6936 756 0.000501760 19325968
Q 4 6732 843 0.000545190 19332700
Q 3 8215 1104 0.000602040 19340915
Q 2 21201 5440 0.000882342 19362116
Q 1 82328 22186 0.002019600 19444444
Q 0 553853 371953 0.020562901 19998297
U 1703
+12
View File
@@ -0,0 +1,12 @@
Q 60 32084 0 0.000000000 32084
Q 24 318 2 0.000061725 32402
Q 11 98 2 0.000123077 32500
Q 8 37 2 0.000184405 32537
Q 7 37 3 0.000276294 32574
Q 6 40 3 0.000367940 32614
Q 5 34 2 0.000428816 32648
Q 4 37 5 0.000581306 32685
Q 3 28 6 0.000764222 32713
Q 2 38 6 0.000946536 32751
Q 1 50 21 0.001585318 32801
Q 0 286 150 0.006105117 33087
+13
View File
@@ -0,0 +1,13 @@
Q 60 32477 0 0.000000000 32477
Q 22 16 1 0.000030776 32493
Q 21 44 1 0.000061468 32537
Q 19 73 1 0.000091996 32610
Q 14 66 1 0.000122414 32676
Q 10 26 3 0.000214054 32702
Q 8 14 1 0.000244529 32716
Q 7 13 2 0.000305539 32729
Q 6 47 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
+1288
View File
File diff suppressed because it is too large Load Diff
+803
View File
@@ -0,0 +1,803 @@
%%
%% This is file `natbib.sty',
%% generated with the docstrip utility.
%%
%% The original source files were:
%%
%% natbib.dtx (with options: `package,all')
%% =============================================
%% IMPORTANT NOTICE:
%%
%% This program can be redistributed and/or modified under the terms
%% of the LaTeX Project Public License Distributed from CTAN
%% archives in directory macros/latex/base/lppl.txt; either
%% version 1 of the License, or any later version.
%%
%% This is a generated file.
%% It may not be distributed without the original source file natbib.dtx.
%%
%% Full documentation can be obtained by LaTeXing that original file.
%% Only a few abbreviated comments remain here to describe the usage.
%% =============================================
%% Copyright 1993-2000 Patrick W Daly
%% Max-Planck-Institut f\"ur Aeronomie
%% Max-Planck-Str. 2
%% D-37191 Katlenburg-Lindau
%% Germany
%% E-mail: daly@linmpi.mpg.de
\NeedsTeXFormat{LaTeX2e}[1995/06/01]
\ProvidesPackage{natbib}
[2000/07/24 7.0a (PWD)]
% This package reimplements the LaTeX \cite command to be used for various
% citation styles, both author-year and numerical. It accepts BibTeX
% output intended for many other packages, and therefore acts as a
% general, all-purpose citation-style interface.
%
% With standard numerical .bst files, only numerical citations are
% possible. With an author-year .bst file, both numerical and
% author-year citations are possible.
%
% If author-year citations are selected, \bibitem must have one of the
% following forms:
% \bibitem[Jones et al.(1990)]{key}...
% \bibitem[Jones et al.(1990)Jones, Baker, and Williams]{key}...
% \bibitem[Jones et al., 1990]{key}...
% \bibitem[\protect\citeauthoryear{Jones, Baker, and Williams}{Jones
% et al.}{1990}]{key}...
% \bibitem[\protect\citeauthoryear{Jones et al.}{1990}]{key}...
% \bibitem[\protect\astroncite{Jones et al.}{1990}]{key}...
% \bibitem[\protect\citename{Jones et al., }1990]{key}...
% \harvarditem[Jones et al.]{Jones, Baker, and Williams}{1990}{key}...
%
% This is either to be made up manually, or to be generated by an
% appropriate .bst file with BibTeX.
% Author-year mode || Numerical mode
% Then, \citet{key} ==>> Jones et al. (1990) || Jones et al. [21]
% \citep{key} ==>> (Jones et al., 1990) || [21]
% Multiple citations as normal:
% \citep{key1,key2} ==>> (Jones et al., 1990; Smith, 1989) || [21,24]
% or (Jones et al., 1990, 1991) || [21,24]
% or (Jones et al., 1990a,b) || [21,24]
% \cite{key} is the equivalent of \citet{key} in author-year mode
% and of \citep{key} in numerical mode
% Full author lists may be forced with \citet* or \citep*, e.g.
% \citep*{key} ==>> (Jones, Baker, and Williams, 1990)
% Optional notes as:
% \citep[chap. 2]{key} ==>> (Jones et al., 1990, chap. 2)
% \citep[e.g.,][]{key} ==>> (e.g., Jones et al., 1990)
% \citep[see][pg. 34]{key}==>> (see Jones et al., 1990, pg. 34)
% (Note: in standard LaTeX, only one note is allowed, after the ref.
% Here, one note is like the standard, two make pre- and post-notes.)
% \citealt{key} ==>> Jones et al. 1990
% \citealt*{key} ==>> Jones, Baker, and Williams 1990
% \citealp{key} ==>> Jones et al., 1990
% \citealp*{key} ==>> Jones, Baker, and Williams, 1990
% Additional citation possibilities (both author-year and numerical modes)
% \citeauthor{key} ==>> Jones et al.
% \citeauthor*{key} ==>> Jones, Baker, and Williams
% \citeyear{key} ==>> 1990
% \citeyearpar{key} ==>> (1990)
% \citetext{priv. comm.} ==>> (priv. comm.)
% Note: full author lists depends on whether the bib style supports them;
% if not, the abbreviated list is printed even when full requested.
%
% For names like della Robbia at the start of a sentence, use
% \Citet{dRob98} ==>> Della Robbia (1998)
% \Citep{dRob98} ==>> (Della Robbia, 1998)
% \Citeauthor{dRob98} ==>> Della Robbia
%
%
% Citation aliasing is achieved with
% \defcitealias{key}{text}
% \citetalias{key} ==>> text
% \citepalias{key} ==>> (text)
%
% Defining the citation style of a given bib style:
% Use \bibpunct (in the preamble only) with 6 mandatory arguments:
% 1. opening bracket for citation
% 2. closing bracket
% 3. citation separator (for multiple citations in one \cite)
% 4. the letter n for numerical styles, s for superscripts
% else anything for author-year
% 5. punctuation between authors and date
% 6. punctuation between years (or numbers) when common authors missing
% One optional argument is the character coming before post-notes. It
% appears in square braces before all other arguments. May be left off.
% Example (and default) \bibpunct[, ]{(}{)}{;}{a}{,}{,}
%
% To make this automatic for a given bib style, named newbib, say, make
% a local configuration file, natbib.cfg, with the definition
% \newcommand{\bibstyle@newbib}{\bibpunct...}
% Then the \bibliographystyle{newbib} will cause \bibstyle@newbib to
% be called on THE NEXT LATEX RUN (via the aux file).
%
% Such preprogrammed definitions may be invoked in the text (preamble only)
% by calling \citestyle{newbib}. This is only useful if the style specified
% differs from that in \bibliographystyle.
%
% With \citeindextrue and \citeindexfalse, one can control whether the
% \cite commands make an automatic entry of the citation in the .idx
% indexing file. For this, \makeindex must also be given in the preamble.
%
% LaTeX2e Options: (for selecting punctuation)
% round - round parentheses are used (default)
% square - square brackets are used [option]
% curly - curly braces are used {option}
% angle - angle brackets are used <option>
% colon - multiple citations separated by colon (default)
% comma - separated by comma
% authoryear - selects author-year citations (default)
% numbers- selects numerical citations
% super - numerical citations as superscripts
% sort - sorts multiple citations according to order in ref. list
% sort&compress - like sort, but also compresses numerical citations
% longnamesfirst - makes first citation full author list
% sectionbib - puts bibliography in a \section* instead of \chapter*
% Punctuation so selected dominates over any predefined ones.
% LaTeX2e options are called as, e.g.
% \usepackage[square,comma]{natbib}
% LaTeX the source file natbib.dtx to obtain more details
% or the file natnotes.tex for a brief reference sheet.
%-----------------------------------------------------------
\@ifclassloaded{aguplus}{\PackageError{natbib}
{The aguplus class already includes natbib coding,\MessageBreak
so you should not add it explicitly}
{Type <Return> for now, but then later remove\MessageBreak
the command \protect\usepackage{natbib} from the document}
\endinput}{}
\@ifclassloaded{nlinproc}{\PackageError{natbib}
{The nlinproc class already includes natbib coding,\MessageBreak
so you should not add it explicitly}
{Type <Return> for now, but then later remove\MessageBreak
the command \protect\usepackage{natbib} from the document}
\endinput}{}
\@ifclassloaded{egs}{\PackageError{natbib}
{The egs class already includes natbib coding,\MessageBreak
so you should not add it explicitly}
{Type <Return> for now, but then later remove\MessageBreak
the command \protect\usepackage{natbib} from the document}
\endinput}{}
% Define citation punctuation for some author-year styles
% One may add and delete at this point
% Or put additions into local configuration file natbib.cfg
\newcommand\bibstyle@chicago{\bibpunct{(}{)}{;}{a}{,}{,}}
\newcommand\bibstyle@named{\bibpunct{[}{]}{;}{a}{,}{,}}
\newcommand\bibstyle@agu{\bibpunct{[}{]}{;}{a}{,}{,~}}%Amer. Geophys. Union
\newcommand\bibstyle@egs{\bibpunct{(}{)}{;}{a}{,}{,}}%Eur. Geophys. Soc.
\newcommand\bibstyle@agsm{\bibpunct{(}{)}{,}{a}{}{,}\gdef\harvardand{\&}}
\newcommand\bibstyle@kluwer{\bibpunct{(}{)}{,}{a}{}{,}\gdef\harvardand{\&}}
\newcommand\bibstyle@dcu{\bibpunct{(}{)}{;}{a}{;}{,}\gdef\harvardand{and}}
\newcommand\bibstyle@aa{\bibpunct{(}{)}{;}{a}{}{,}} %Astronomy & Astrophysics
\newcommand\bibstyle@pass{\bibpunct{(}{)}{;}{a}{,}{,}}%Planet. & Space Sci
\newcommand\bibstyle@anngeo{\bibpunct{(}{)}{;}{a}{,}{,}}%Annales Geophysicae
\newcommand\bibstyle@nlinproc{\bibpunct{(}{)}{;}{a}{,}{,}}%Nonlin.Proc.Geophys.
% Define citation punctuation for some numerical styles
\newcommand\bibstyle@cospar{\bibpunct{/}{/}{,}{n}{}{}%
\gdef\NAT@biblabelnum##1{##1.}}
\newcommand\bibstyle@esa{\bibpunct{(Ref.~}{)}{,}{n}{}{}%
\gdef\NAT@biblabelnum##1{##1.\hspace{1em}}}
\newcommand\bibstyle@nature{\bibpunct{}{}{,}{s}{}{\textsuperscript{,}}%
\gdef\NAT@biblabelnum##1{##1.}}
% The standard LaTeX styles
\newcommand\bibstyle@plain{\bibpunct{[}{]}{,}{n}{}{,}}
\let\bibstyle@alpha=\bibstyle@plain
\let\bibstyle@abbrv=\bibstyle@plain
\let\bibstyle@unsrt=\bibstyle@plain
% The author-year modifications of the standard styles
\newcommand\bibstyle@plainnat{\bibpunct{[}{]}{,}{a}{,}{,}}
\let\bibstyle@abbrvnat=\bibstyle@plainnat
\let\bibstyle@unsrtnat=\bibstyle@plainnat
\newif\ifNAT@numbers \NAT@numbersfalse
\newif\ifNAT@super \NAT@superfalse
\DeclareOption{numbers}{\NAT@numberstrue
\ExecuteOptions{square,comma,nobibstyle}}
\DeclareOption{super}{\NAT@supertrue\NAT@numberstrue
\renewcommand\NAT@open{}\renewcommand\NAT@close{}
\ExecuteOptions{nobibstyle}}
\DeclareOption{authoryear}{\NAT@numbersfalse
\ExecuteOptions{round,colon,bibstyle}}
\DeclareOption{round}{%
\renewcommand\NAT@open{(} \renewcommand\NAT@close{)}
\ExecuteOptions{nobibstyle}}
\DeclareOption{square}{%
\renewcommand\NAT@open{[} \renewcommand\NAT@close{]}
\ExecuteOptions{nobibstyle}}
\DeclareOption{angle}{%
\renewcommand\NAT@open{$<$} \renewcommand\NAT@close{$>$}
\ExecuteOptions{nobibstyle}}
\DeclareOption{curly}{%
\renewcommand\NAT@open{\{} \renewcommand\NAT@close{\}}
\ExecuteOptions{nobibstyle}}
\DeclareOption{comma}{\renewcommand\NAT@sep{,}
\ExecuteOptions{nobibstyle}}
\DeclareOption{colon}{\renewcommand\NAT@sep{;}
\ExecuteOptions{nobibstyle}}
\DeclareOption{nobibstyle}{\let\bibstyle=\@gobble}
\DeclareOption{bibstyle}{\let\bibstyle=\@citestyle}
\newif\ifNAT@openbib \NAT@openbibfalse
\DeclareOption{openbib}{\NAT@openbibtrue}
\DeclareOption{sectionbib}{\def\NAT@sectionbib{on}}
\def\NAT@sort{0}
\DeclareOption{sort}{\def\NAT@sort{1}}
\DeclareOption{sort&compress}{\def\NAT@sort{2}}
\@ifpackageloaded{cite}{\PackageWarningNoLine{natbib}
{The `cite' package should not be used\MessageBreak
with natbib. Use option `sort' instead}\ExecuteOptions{sort}}{}
\newif\ifNAT@longnames\NAT@longnamesfalse
\DeclareOption{longnamesfirst}{\NAT@longnamestrue}
\DeclareOption{nonamebreak}{\def\NAT@nmfmt#1{\mbox{\NAT@up#1}}}
\def\NAT@nmfmt#1{{\NAT@up#1}}
\renewcommand\bibstyle[1]{\@ifundefined{bibstyle@#1}{\relax}
{\csname bibstyle@#1\endcsname}}
\AtBeginDocument{\global\let\bibstyle=\@gobble}
\let\@citestyle\bibstyle
\newcommand\citestyle[1]{\@citestyle{#1}\let\bibstyle\@gobble}
\@onlypreamble{\citestyle}\@onlypreamble{\@citestyle}
\newcommand\bibpunct[7][, ]%
{\gdef\NAT@open{#2}\gdef\NAT@close{#3}\gdef
\NAT@sep{#4}\global\NAT@numbersfalse\ifx #5n\global\NAT@numberstrue
\else
\ifx #5s\global\NAT@numberstrue\global\NAT@supertrue
\fi\fi
\gdef\NAT@aysep{#6}\gdef\NAT@yrsep{#7}%
\gdef\NAT@cmt{#1}%
\global\let\bibstyle\@gobble
}
\@onlypreamble{\bibpunct}
\newcommand\NAT@open{(} \newcommand\NAT@close{)}
\newcommand\NAT@sep{;}
\ProcessOptions
\newcommand\NAT@aysep{,} \newcommand\NAT@yrsep{,}
\newcommand\NAT@cmt{, }
\newcommand\NAT@cite%
[3]{\ifNAT@swa\NAT@@open\if*#2*\else#2\ \fi
#1\if*#3*\else\NAT@cmt#3\fi\NAT@@close\else#1\fi\endgroup}
\newcommand\NAT@citenum%
[3]{\ifNAT@swa\NAT@@open\if*#2*\else#2\ \fi
#1\if*#3*\else\NAT@cmt#3\fi\NAT@@close\else#1\fi\endgroup}
\newcommand\NAT@citesuper[3]{\ifNAT@swa
\unskip\hspace{1\p@}\textsuperscript{#1}%
\if*#3*\else\ (#3)\fi\else #1\fi\endgroup}
\providecommand
\textsuperscript[1]{\mbox{$^{\mbox{\scriptsize#1}}$}}
\providecommand\@firstofone[1]{#1}
\newcommand\NAT@citexnum{}
\def\NAT@citexnum[#1][#2]#3{%
\NAT@sort@cites{#3}%
\let\@citea\@empty
\@cite{\def\NAT@num{-1}\let\NAT@last@yr\relax\let\NAT@nm\@empty
\@for\@citeb:=\NAT@cite@list\do
{\edef\@citeb{\expandafter\@firstofone\@citeb}%
\if@filesw\immediate\write\@auxout{\string\citation{\@citeb}}\fi
\@ifundefined{b@\@citeb\@extra@b@citeb}{%
{\reset@font\bfseries?}
\NAT@citeundefined\PackageWarning{natbib}%
{Citation `\@citeb' on page \thepage \space undefined}}%
{\let\NAT@last@num\NAT@num\let\NAT@last@nm\NAT@nm
\NAT@parse{\@citeb}%
\ifNAT@longnames\@ifundefined{bv@\@citeb\@extra@b@citeb}{%
\let\NAT@name=\NAT@all@names
\global\@namedef{bv@\@citeb\@extra@b@citeb}{}}{}%
\fi
\ifNAT@full\let\NAT@nm\NAT@all@names\else
\let\NAT@nm\NAT@name\fi
\ifNAT@swa
\ifnum\NAT@ctype>1\relax\@citea
\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\ifnum\NAT@ctype=2\relax\NAT@test{\NAT@ctype}%
\else\NAT@alias
\fi\hyper@natlinkend\else
\ifnum\NAT@sort>1
\begingroup\catcode`\_=8
\ifcat _\ifnum\z@<0\NAT@num _\else A\fi
\global\let\NAT@nm=\NAT@num \else \gdef\NAT@nm{-2}\fi
\ifcat _\ifnum\z@<0\NAT@last@num _\else A\fi
\global\@tempcnta=\NAT@last@num \global\advance\@tempcnta by\@ne
\else \global\@tempcnta\m@ne\fi
\endgroup
\ifnum\NAT@nm=\@tempcnta
\ifx\NAT@last@yr\relax
\edef\NAT@last@yr{\@citea \mbox{\noexpand\citenumfont{\NAT@num}}}%
\else
\edef\NAT@last@yr{--\penalty\@m\mbox{\noexpand\citenumfont{\NAT@num}}}%
\fi
\else
\NAT@last@yr \@citea \mbox{\citenumfont{\NAT@num}}%
\let\NAT@last@yr\relax
\fi
\else
\@citea \mbox{\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
{\citenumfont{\NAT@num}}\hyper@natlinkend}%
\fi
\fi
\def\@citea{\NAT@sep\penalty\@m\NAT@space}%
\else
\ifcase\NAT@ctype\relax
\ifx\NAT@last@nm\NAT@nm \NAT@yrsep\penalty\@m\NAT@space\else
\@citea \NAT@test{1}\ \NAT@@open
\if*#1*\else#1\ \fi\fi \NAT@mbox{%
\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
{\citenumfont{\NAT@num}}\hyper@natlinkend}%
\def\@citea{\NAT@@close\NAT@sep\penalty\@m\ }%
\or\@citea
\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@test{\NAT@ctype}\hyper@natlinkend
\def\@citea{\NAT@sep\penalty\@m\ }%
\or\@citea
\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@test{\NAT@ctype}\hyper@natlinkend
\def\@citea{\NAT@sep\penalty\@m\ }%
\or\@citea
\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@alias\hyper@natlinkend
\def\@citea{\NAT@sep\penalty\@m\ }%
\fi
\fi
}}%
\ifnum\NAT@sort>1\relax\NAT@last@yr\fi
\ifNAT@swa\else\ifnum\NAT@ctype=0\if*#2*\else
\NAT@cmt#2\fi \NAT@@close\fi\fi}{#1}{#2}}
\newcommand\NAT@test[1]{\ifnum#1=1 \ifx\NAT@nm\NAT@noname
{\reset@font\bfseries(author?)}\PackageWarning{natbib}
{Author undefined for citation`\@citeb'
\MessageBreak
on page \thepage}\else \NAT@nm \fi
\else \if\relax\NAT@date\relax
{\reset@font\bfseries(year?)}\PackageWarning{natbib}
{Year undefined for citation`\@citeb'
\MessageBreak
on page \thepage}\else \NAT@date \fi \fi}
\let\citenumfont=\relax
\newcommand\NAT@citex{}
\def\NAT@citex%
[#1][#2]#3{%
\NAT@sort@cites{#3}%
\let\@citea\@empty
\@cite{\let\NAT@nm\@empty\let\NAT@year\@empty
\@for\@citeb:=\NAT@cite@list\do
{\edef\@citeb{\expandafter\@firstofone\@citeb}%
\if@filesw\immediate\write\@auxout{\string\citation{\@citeb}}\fi
\@ifundefined{b@\@citeb\@extra@b@citeb}{\@citea%
{\reset@font\bfseries ?}\NAT@citeundefined
\PackageWarning{natbib}%
{Citation `\@citeb' on page \thepage \space undefined}\def\NAT@date{}}%
{\let\NAT@last@nm=\NAT@nm\let\NAT@last@yr=\NAT@year
\NAT@parse{\@citeb}%
\ifNAT@longnames\@ifundefined{bv@\@citeb\@extra@b@citeb}{%
\let\NAT@name=\NAT@all@names
\global\@namedef{bv@\@citeb\@extra@b@citeb}{}}{}%
\fi
\ifNAT@full\let\NAT@nm\NAT@all@names\else
\let\NAT@nm\NAT@name\fi
\ifNAT@swa\ifcase\NAT@ctype
\if\relax\NAT@date\relax
\@citea\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@nmfmt{\NAT@nm}\NAT@date\hyper@natlinkend
\else
\ifx\NAT@last@nm\NAT@nm\NAT@yrsep
\ifx\NAT@last@yr\NAT@year
\hyper@natlinkstart{\@citeb\@extra@b@citeb}\NAT@exlab
\hyper@natlinkend
\else\unskip\
\hyper@natlinkstart{\@citeb\@extra@b@citeb}\NAT@date
\hyper@natlinkend
\fi
\else\@citea\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@nmfmt{\NAT@nm}%
\hyper@natlinkbreak{\NAT@aysep\ }{\@citeb\@extra@b@citeb}%
\NAT@date\hyper@natlinkend
\fi
\fi
\or\@citea\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@nmfmt{\NAT@nm}\hyper@natlinkend
\or\@citea\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@date\hyper@natlinkend
\or\@citea\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@alias\hyper@natlinkend
\fi \def\@citea{\NAT@sep\ }%
\else\ifcase\NAT@ctype
\if\relax\NAT@date\relax
\@citea\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@nmfmt{\NAT@nm}\hyper@natlinkend
\else
\ifx\NAT@last@nm\NAT@nm\NAT@yrsep
\ifx\NAT@last@yr\NAT@year
\hyper@natlinkstart{\@citeb\@extra@b@citeb}\NAT@exlab
\hyper@natlinkend
\else\unskip\
\hyper@natlinkstart{\@citeb\@extra@b@citeb}\NAT@date
\hyper@natlinkend
\fi
\else\@citea\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@nmfmt{\NAT@nm}%
\hyper@natlinkbreak{\ \NAT@@open\if*#1*\else#1\ \fi}%
{\@citeb\@extra@b@citeb}%
\NAT@date\hyper@natlinkend\fi
\fi
\or\@citea\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@nmfmt{\NAT@nm}\hyper@natlinkend
\or\@citea\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@date\hyper@natlinkend
\or\@citea\hyper@natlinkstart{\@citeb\@extra@b@citeb}%
\NAT@alias\hyper@natlinkend
\fi \if\relax\NAT@date\relax\def\@citea{\NAT@sep\ }%
\else\def\@citea{\NAT@@close\NAT@sep\ }\fi
\fi
}}\ifNAT@swa\else\if*#2*\else\NAT@cmt#2\fi
\if\relax\NAT@date\relax\else\NAT@@close\fi\fi}{#1}{#2}}
\newif\ifNAT@par \NAT@partrue
\newcommand\NAT@@open{\ifNAT@par\NAT@open\fi}
\newcommand\NAT@@close{\ifNAT@par\NAT@close\fi}
\newcommand\NAT@alias{\@ifundefined{al@\@citeb\@extra@b@citeb}{%
{\reset@font\bfseries(alias?)}\PackageWarning{natbib}
{Alias undefined for citation `\@citeb'
\MessageBreak on page \thepage}}{\@nameuse{al@\@citeb\@extra@b@citeb}}}
\let\NAT@up\relax
\newcommand\NAT@Up[1]{{\let\protect\@unexpandable@protect\let~\relax
\expandafter\NAT@deftemp#1}\expandafter\NAT@UP\NAT@temp}
\newcommand\NAT@deftemp[1]{\xdef\NAT@temp{#1}}
\newcommand\NAT@UP[1]{\let\@tempa\NAT@UP\ifcat a#1\MakeUppercase{#1}%
\let\@tempa\relax\else#1\fi\@tempa}
\newcommand\shortcites[1]{%
\@bsphack\@for\@citeb:=#1\do
{\edef\@citeb{\expandafter\@firstofone\@citeb}%
\global\@namedef{bv@\@citeb\@extra@b@citeb}{}}\@esphack}
\newcommand\NAT@biblabel[1]{\hfill}
\newcommand\NAT@biblabelnum[1]{\bibnumfmt{#1}}
\newcommand\bibnumfmt[1]{[#1]}
\def\@tempa#1{[#1]}
\ifx\@tempa\@biblabel\let\@biblabel\@empty\fi
\newcommand\NAT@bibsetnum[1]{\settowidth\labelwidth{\@biblabel{#1}}%
\setlength{\leftmargin}{\labelwidth}\addtolength{\leftmargin}{\labelsep}%
\setlength{\itemsep}{\bibsep}\setlength{\parsep}{\z@}%
\ifNAT@openbib
\addtolength{\leftmargin}{4mm}%
\setlength{\itemindent}{-4mm}%
\setlength{\listparindent}{\itemindent}%
\setlength{\parsep}{0pt}%
\fi
}
\newlength{\bibhang}
\setlength{\bibhang}{1em}
\newlength{\bibsep}
{\@listi \global\bibsep\itemsep \global\advance\bibsep by\parsep}
\newcommand\NAT@bibsetup%
[1]{\setlength{\leftmargin}{\bibhang}\setlength{\itemindent}{-\leftmargin}%
\setlength{\itemsep}{\bibsep}\setlength{\parsep}{\z@}}
\newcommand\NAT@set@cites{\ifNAT@numbers
\ifNAT@super \let\@cite\NAT@citesuper
\def\NAT@mbox##1{\unskip\nobreak\hspace{1\p@}\textsuperscript{##1}}%
\let\citeyearpar=\citeyear
\let\NAT@space\relax\else
\let\NAT@mbox=\mbox
\let\@cite\NAT@citenum \def\NAT@space{ }\fi
\let\@citex\NAT@citexnum
\ifx\@biblabel\@empty\let\@biblabel\NAT@biblabelnum\fi
\let\@bibsetup\NAT@bibsetnum
\def\natexlab##1{}%
\else
\let\@cite\NAT@cite
\let\@citex\NAT@citex
\let\@biblabel\NAT@biblabel
\let\@bibsetup\NAT@bibsetup
\def\natexlab##1{##1}%
\fi}
\AtBeginDocument{\NAT@set@cites}
\AtBeginDocument{\ifx\SK@def\@undefined\else
\ifx\SK@cite\@empty\else
\SK@def\@citex[#1][#2]#3{\SK@\SK@@ref{#3}\SK@@citex[#1][#2]{#3}}\fi
\ifx\SK@citeauthor\@undefined\def\HAR@checkdef{}\else
\let\citeauthor\SK@citeauthor
\let\citefullauthor\SK@citefullauthor
\let\citeyear\SK@citeyear\fi
\fi}
\AtBeginDocument{\@ifpackageloaded{hyperref}{%
\ifnum\NAT@sort=2\def\NAT@sort{1}\fi}{}}
\newif\ifNAT@full\NAT@fullfalse
\newif\ifNAT@swa
\DeclareRobustCommand\citet
{\begingroup\NAT@swafalse\def\NAT@ctype{0}\NAT@partrue
\@ifstar{\NAT@fulltrue\NAT@citetp}{\NAT@fullfalse\NAT@citetp}}
\newcommand\NAT@citetp{\@ifnextchar[{\NAT@@citetp}{\NAT@@citetp[]}}
\newcommand\NAT@@citetp{}
\def\NAT@@citetp[#1]{\@ifnextchar[{\@citex[#1]}{\@citex[][#1]}}
\DeclareRobustCommand\citep
{\begingroup\NAT@swatrue\def\NAT@ctype{0}\NAT@partrue
\@ifstar{\NAT@fulltrue\NAT@citetp}{\NAT@fullfalse\NAT@citetp}}
\DeclareRobustCommand\cite
{\begingroup\def\NAT@ctype{0}\NAT@partrue\NAT@swatrue
\@ifstar{\NAT@fulltrue\NAT@cites}{\NAT@fullfalse\NAT@cites}}
\newcommand\NAT@cites{\@ifnextchar [{\NAT@@citetp}{%
\ifNAT@numbers\else
\NAT@swafalse
\fi
\NAT@@citetp[]}}
\DeclareRobustCommand\citealt
{\begingroup\NAT@swafalse\def\NAT@ctype{0}\NAT@parfalse
\@ifstar{\NAT@fulltrue\NAT@citetp}{\NAT@fullfalse\NAT@citetp}}
\DeclareRobustCommand\citealp
{\begingroup\NAT@swatrue\def\NAT@ctype{0}\NAT@parfalse
\@ifstar{\NAT@fulltrue\NAT@citetp}{\NAT@fullfalse\NAT@citetp}}
\DeclareRobustCommand\citeauthor
{\begingroup\NAT@swafalse\def\NAT@ctype{1}\NAT@parfalse
\@ifstar{\NAT@fulltrue\NAT@citetp}{\NAT@fullfalse\NAT@citetp}}
\DeclareRobustCommand\Citet
{\begingroup\NAT@swafalse\def\NAT@ctype{0}\NAT@partrue
\let\NAT@up\NAT@Up
\@ifstar{\NAT@fulltrue\NAT@citetp}{\NAT@fullfalse\NAT@citetp}}
\DeclareRobustCommand\Citep
{\begingroup\NAT@swatrue\def\NAT@ctype{0}\NAT@partrue
\let\NAT@up\NAT@Up
\@ifstar{\NAT@fulltrue\NAT@citetp}{\NAT@fullfalse\NAT@citetp}}
\DeclareRobustCommand\Citealt
{\begingroup\NAT@swafalse\def\NAT@ctype{0}\NAT@parfalse
\let\NAT@up\NAT@Up
\@ifstar{\NAT@fulltrue\NAT@citetp}{\NAT@fullfalse\NAT@citetp}}
\DeclareRobustCommand\Citealp
{\begingroup\NAT@swatrue\def\NAT@ctype{0}\NAT@parfalse
\let\NAT@up\NAT@Up
\@ifstar{\NAT@fulltrue\NAT@citetp}{\NAT@fullfalse\NAT@citetp}}
\DeclareRobustCommand\Citeauthor
{\begingroup\NAT@swafalse\def\NAT@ctype{1}\NAT@parfalse
\let\NAT@up\NAT@Up
\@ifstar{\NAT@fulltrue\NAT@citetp}{\NAT@fullfalse\NAT@citetp}}
\DeclareRobustCommand\citeyear
{\begingroup\NAT@swafalse\def\NAT@ctype{2}\NAT@parfalse\NAT@citetp}
\DeclareRobustCommand\citeyearpar
{\begingroup\NAT@swatrue\def\NAT@ctype{2}\NAT@partrue\NAT@citetp}
\newcommand\citetext[1]{\NAT@open#1\NAT@close}
\DeclareRobustCommand\citefullauthor
{\citeauthor*}
\newcommand\defcitealias[2]{%
\@ifundefined{al@#1\@extra@b@citeb}{}
{\PackageWarning{natbib}{Overwriting existing alias for citation #1}}
\@namedef{al@#1\@extra@b@citeb}{#2}}
\DeclareRobustCommand\citetalias{\begingroup
\NAT@swafalse\def\NAT@ctype{3}\NAT@parfalse\NAT@citetp}
\DeclareRobustCommand\citepalias{\begingroup
\NAT@swatrue\def\NAT@ctype{3}\NAT@partrue\NAT@citetp}
\renewcommand\nocite[1]{\@bsphack
\@for\@citeb:=#1\do{%
\edef\@citeb{\expandafter\@firstofone\@citeb}%
\if@filesw\immediate\write\@auxout{\string\citation{\@citeb}}\fi
\if*\@citeb\else
\@ifundefined{b@\@citeb\@extra@b@citeb}{%
\NAT@citeundefined \PackageWarning{natbib}%
{Citation `\@citeb' undefined}}{}\fi}%
\@esphack}
\newcommand\NAT@parse[1]{{%
\let\protect=\@unexpandable@protect\let~\relax
\let\active@prefix=\@gobble
\xdef\NAT@temp{\csname b@#1\@extra@b@citeb\endcsname}}%
\expandafter\NAT@split\NAT@temp
\expandafter\NAT@parse@date\NAT@date??????@@%
\ifciteindex\NAT@index\fi
}
\newcommand\NAT@split[4]{%
\gdef\NAT@num{#1}\gdef\NAT@name{#3}\gdef\NAT@date{#2}%
\gdef\NAT@all@names{#4}%
\ifx\NAT@noname\NAT@all@names \gdef\NAT@all@names{#3}\fi}
\newcommand\NAT@parse@date{}
\def\NAT@parse@date#1#2#3#4#5#6@@{%
\ifnum\the\catcode`#1=11\def\NAT@year{}\def\NAT@exlab{#1}\else
\ifnum\the\catcode`#2=11\def\NAT@year{#1}\def\NAT@exlab{#2}\else
\ifnum\the\catcode`#3=11\def\NAT@year{#1#2}\def\NAT@exlab{#3}\else
\ifnum\the\catcode`#4=11\def\NAT@year{#1#2#3}\def\NAT@exlab{#4}\else
\def\NAT@year{#1#2#3#4}\def\NAT@exlab{{#5}}\fi\fi\fi\fi}
\newcommand\NAT@index{}
\let\NAT@makeindex=\makeindex
\renewcommand\makeindex{\NAT@makeindex
\renewcommand\NAT@index{\@bsphack\begingroup
\def~{\string~}\@wrindex{\NAT@idxtxt}}}
\newcommand\NAT@idxtxt{\NAT@name\ \NAT@open\NAT@date\NAT@close}
\@ifundefined{@indexfile}{}{\let\NAT@makeindex\relax\makeindex}
\newif\ifciteindex \citeindexfalse
\newcommand\citeindextype{default}
\newcommand\NAT@index@alt{{\let\protect=\noexpand\let~\relax
\xdef\NAT@temp{\NAT@idxtxt}}\expandafter\NAT@exp\NAT@temp\@nil}
\newcommand\NAT@exp{}
\def\NAT@exp#1\@nil{\mbox{}\index[\citeindextype]{#1}}
\AtBeginDocument{%
\@ifpackageloaded{index}{\let\NAT@index=\NAT@index@alt}{}}
\newcommand\NAT@ifcmd{\futurelet\NAT@temp\NAT@ifxcmd}
\newcommand\NAT@ifxcmd{\ifx\NAT@temp\relax\else\expandafter\NAT@bare\fi}
\def\NAT@bare#1(#2)#3(@)#4\@nil#5{%
\if @#2
\expandafter\NAT@apalk#1, , \@nil{#5}\else
\stepcounter{NAT@ctr}%
\NAT@wrout{\arabic {NAT@ctr}}{#2}{#1}{#3}{#5}
\fi
}
\newcommand\NAT@wrout[5]{%
\if@filesw
{\let\protect\noexpand\let~\relax
\immediate
\write\@auxout{\string\bibcite{#5}{{#1}{#2}{{#3}}{{#4}}}}}\fi
\ignorespaces}
\def\NAT@noname{{}}
\renewcommand\bibitem{%
\@ifnextchar[{\@lbibitem}{%
\global\NAT@stdbsttrue
\stepcounter{NAT@ctr}\@lbibitem[\arabic{NAT@ctr}]}}
\def\@lbibitem[#1]#2{%
\if\relax\@extra@b@citeb\relax\else
\@ifundefined{br@#2\@extra@b@citeb}{}{%
\@namedef{br@#2}{\@nameuse{br@#2\@extra@b@citeb}}}\fi
\@ifundefined{b@#2\@extra@b@citeb}{\def\NAT@num{}}{\NAT@parse{#2}}%
\item[\hfil\hyper@natanchorstart{#2\@extra@b@citeb}\@biblabel{\NAT@num}%
\hyper@natanchorend]%
\NAT@ifcmd#1(@)(@)\@nil{#2}}
\ifx\SK@lbibitem\@undefined\else
\let\SK@lbibitem\@lbibitem
\def\@lbibitem[#1]#2{%
\SK@lbibitem[#1]{#2}\SK@\SK@@label{#2}\ignorespaces}\fi
\newif\ifNAT@stdbst \NAT@stdbstfalse
\AtEndDocument
{\ifNAT@stdbst\if@filesw\immediate\write\@auxout{\string
\global\string\NAT@numberstrue}\fi\fi
}
\providecommand\bibcite{}
\renewcommand\bibcite[2]{\@ifundefined{b@#1\@extra@binfo}\relax
{\NAT@citemultiple
\PackageWarningNoLine{natbib}{Citation `#1' multiply defined}}%
\global\@namedef{b@#1\@extra@binfo}{#2}}
\AtEndDocument{\NAT@swatrue\let\bibcite\NAT@testdef}
\newcommand\NAT@testdef[2]{%
\def\NAT@temp{#2}\expandafter \ifx \csname b@#1\@extra@binfo\endcsname
\NAT@temp \else \ifNAT@swa \NAT@swafalse
\PackageWarningNoLine{natbib}{Citation(s) may have
changed.\MessageBreak
Rerun to get citations correct}\fi\fi}
\newcommand\NAT@apalk{}
\def\NAT@apalk#1, #2, #3\@nil#4{\if\relax#2\relax
\global\NAT@stdbsttrue
\NAT@wrout{#1}{}{}{}{#4}\else
\stepcounter{NAT@ctr}%
\NAT@wrout{\arabic {NAT@ctr}}{#2}{#1}{}{#4}\fi}
\newcommand\citeauthoryear{}
\def\citeauthoryear#1#2#3(@)(@)\@nil#4{\stepcounter{NAT@ctr}\if\relax#3\relax
\NAT@wrout{\arabic {NAT@ctr}}{#2}{#1}{}{#4}\else
\NAT@wrout{\arabic {NAT@ctr}}{#3}{#2}{#1}{#4}\fi}
\newcommand\citestarts{\NAT@open}
\newcommand\citeends{\NAT@close}
\newcommand\betweenauthors{and}
\newcommand\astroncite{}
\def\astroncite#1#2(@)(@)\@nil#3{\stepcounter{NAT@ctr}\NAT@wrout{\arabic
{NAT@ctr}}{#2}{#1}{}{#3}}
\newcommand\citename{}
\def\citename#1#2(@)(@)\@nil#3{\expandafter\NAT@apalk#1#2, \@nil{#3}}
\newcommand\harvarditem[4][]%
{\if\relax#1\relax\bibitem[#2(#3)]{#4}\else
\bibitem[#1(#3)#2]{#4}\fi }
\newcommand\harvardleft{\NAT@open}
\newcommand\harvardright{\NAT@close}
\newcommand\harvardyearleft{\NAT@open}
\newcommand\harvardyearright{\NAT@close}
\AtBeginDocument{\providecommand{\harvardand}{and}}
\newcommand\harvardurl[1]{\textbf{URL:} \textit{#1}}
\providecommand\bibsection{}
\@ifundefined{chapter}%
{\renewcommand\bibsection{\section*{\refname
\@mkboth{\MakeUppercase{\refname}}{\MakeUppercase{\refname}}}}}
{\@ifundefined{NAT@sectionbib}%
{\renewcommand\bibsection{\chapter*{\bibname
\@mkboth{\MakeUppercase{\bibname}}{\MakeUppercase{\bibname}}}}}
{\renewcommand\bibsection{\section*{\bibname
\ifx\@mkboth\@gobbletwo\else\markright{\MakeUppercase{\bibname}}\fi}}}}
\@ifclassloaded{amsart}%
{\renewcommand\bibsection{\section*{\refname}}}{}
\@ifclassloaded{amsbook}%
{\renewcommand\bibsection{\chapter*{\bibname}}}{}
\@ifundefined{bib@heading}{}{\let\bibsection\bib@heading}
\newcounter{NAT@ctr}
\renewenvironment{thebibliography}[1]{%
\bibsection
\vspace{1\p@}\parindent \z@\bibpreamble\bibfont\list
{\@biblabel{\arabic{NAT@ctr}}}{\@bibsetup{#1}%
\setcounter{NAT@ctr}{0}}%
\ifNAT@openbib
\renewcommand\newblock{\par}
\else
\renewcommand\newblock{\hskip .11em \@plus.33em \@minus.07em}%
\fi
\sloppy\clubpenalty4000\widowpenalty4000
\sfcode`\.=1000\relax
\let\citeN\cite \let\shortcite\cite
\let\citeasnoun\cite\fontsize{7}{9}\selectfont
}{\def\@noitemerr{%
\PackageWarning{natbib}
{Empty `thebibliography' environment}}%
\endlist\vskip-\lastskip}
\let\bibfont\relax
\let\bibpreamble\relax
\providecommand\reset@font{\relax}
\providecommand\bibname{Bibliography}
\providecommand\refname{References}
\newcommand\NAT@citeundefined{\gdef \NAT@undefined {%
\PackageWarningNoLine{natbib}{There were undefined citations}}}
\let \NAT@undefined \relax
\newcommand\NAT@citemultiple{\gdef \NAT@multiple {%
\PackageWarningNoLine{natbib}{There were multiply defined citations}}}
\let \NAT@multiple \relax
\AtEndDocument{\NAT@undefined\NAT@multiple}
\providecommand\@mkboth[2]{}
\providecommand\MakeUppercase{\uppercase}
\providecommand{\@extra@b@citeb}{}
\gdef\@extra@binfo{}
\providecommand\hyper@natanchorstart[1]{}
\providecommand\hyper@natanchorend{}
\providecommand\hyper@natlinkstart[1]{}
\providecommand\hyper@natlinkend{}
\providecommand\hyper@natlinkbreak[2]{#1}
\@ifundefined{bbl@redefine}{}{%
\bbl@redefine\nocite#1{%
\@safe@activestrue\org@nocite{#1}\@safe@activesfalse}%
\bbl@redefine\@lbibitem[#1]#2{%
\@safe@activestrue\org@@lbibitem[#1]{#2}\@safe@activesfalse}%
}
\AtBeginDocument{\@ifundefined{bbl@redefine}{}{%
\bbl@redefine\@citex[#1][#2]#3{%
\@safe@activestrue\org@@citex[#1][#2]{#3}\@safe@activesfalse}%
\bbl@redefine\NAT@testdef#1#2{%
\@safe@activestrue\org@NAT@testdef{#1}{#2}\@safe@activesfalse}%
\@ifundefined{org@@lbibitem}{%
\bbl@redefine\@lbibitem[#1]#2{%
\@safe@activestrue\org@@lbibitem[#1]{#2}\@safe@activesfalse}}{}%
}}
\ifnum\NAT@sort>0
\newcommand\NAT@sort@cites[1]{%
\@tempcntb\m@ne
\let\@celt\delimiter
\def\NAT@num@list{}%
\def\NAT@cite@list{}%
\def\NAT@nonsort@list{}%
\@for \@citeb:=#1\do{\NAT@make@cite@list}%
\edef\NAT@cite@list{\NAT@cite@list\NAT@nonsort@list}%
\edef\NAT@cite@list{\expandafter\NAT@xcom\NAT@cite@list @@}}
\begingroup \catcode`\_=8
\gdef\NAT@make@cite@list{%
\edef\@citeb{\expandafter\@firstofone\@citeb}%
\@ifundefined{b@\@citeb\@extra@b@citeb}{\def\NAT@num{A}}%
{\NAT@parse{\@citeb}}%
\ifcat _\ifnum\z@<0\NAT@num _\else A\fi
\@tempcnta\NAT@num \relax
\ifnum \@tempcnta>\@tempcntb
\edef\NAT@num@list{\NAT@num@list \@celt{\NAT@num}}%
\edef\NAT@cite@list{\NAT@cite@list\@citeb,}%
\@tempcntb\@tempcnta
\else
\let\NAT@@cite@list=\NAT@cite@list \def\NAT@cite@list{}%
\edef\NAT@num@list{\expandafter\NAT@num@celt \NAT@num@list \@gobble @}%
{\let\@celt=\NAT@celt\NAT@num@list}%
\fi
\else
\edef\NAT@nonsort@list{\NAT@nonsort@list\@citeb,}%
\fi}
\endgroup
\def\NAT@celt#1{\ifnum #1<\@tempcnta
\xdef\NAT@cite@list{\NAT@cite@list\expandafter\NAT@nextc\NAT@@cite@list @@}%
\xdef\NAT@@cite@list{\expandafter\NAT@restc\NAT@@cite@list}%
\else
\xdef\NAT@cite@list{\NAT@cite@list\@citeb,\NAT@@cite@list}\let\@celt\@gobble%
\fi}
\def\NAT@num@celt#1#2{\ifx \@celt #1%
\ifnum #2<\@tempcnta
\@celt{#2}%
\expandafter\expandafter\expandafter\NAT@num@celt
\else
\@celt{\number\@tempcnta}\@celt{#2}%
\fi\fi}
\def\NAT@nextc#1,#2@@{#1,}
\def\NAT@restc#1,#2{#2}
\def\NAT@xcom#1,@@{#1}
\else
\newcommand\NAT@sort@cites[1]{\edef\NAT@cite@list{#1}}\fi
\InputIfFileExists{natbib.cfg}
{\typeout{Local config file natbib.cfg used}}{}
%%
%% <<<<< End of generated file <<<<<<
%%
%% End of file `natbib.sty'.
+38
View File
@@ -0,0 +1,38 @@
Q 60 23616 0 0.000000000
Q 45 3520 1 0.000036851
Q 41 1840 1 0.000069023
Q 37 328 2 0.000136500
Q 36 276 1 0.000169033
Q 35 480 1 0.000199601
Q 33 375 2 0.000262855
Q 31 178 2 0.000326659
Q 30 153 5 0.000487551
Q 29 200 1 0.000516696
Q 27 100 3 0.000611601
Q 26 93 3 0.000706056
Q 25 75 2 0.000768393
Q 24 82 1 0.000798314
Q 23 80 6 0.000987387
Q 22 71 6 0.001175835
Q 21 76 7 0.001394921
Q 20 63 9 0.001676897
Q 19 55 4 0.001800322
Q 18 62 8 0.002048987
Q 17 55 7 0.002265718
Q 16 60 10 0.002575539
Q 15 82 9 0.002850877
Q 14 67 7 0.003063745
Q 13 62 11 0.003401042
Q 12 64 13 0.003799084
Q 11 56 5 0.003947900
Q 10 58 17 0.004468303
Q 9 70 22 0.005139796
Q 8 23 9 0.005414604
Q 7 41 17 0.005933068
Q 6 42 18 0.006480881
Q 5 33 9 0.006751757
Q 4 29 9 0.007022948
Q 3 27 15 0.007478764
Q 2 23 10 0.007781024
Q 1 9 2 0.007840364
Q 0 13 8 0.008083105
+60
View File
@@ -0,0 +1,60 @@
set t po eps enh co so "Helvetica,26"
set style line 1 lt 1 pt 1 lc rgb "#e41a1c" lw 2;
set style line 2 lt 1 pt 2 lc rgb "#377eb8" lw 2;
set style line 3 lt 1 pt 3 lc rgb "#4daf4a" lw 2;
set style line 4 lt 1 pt 4 lc rgb "#984ea3" lw 2;
set style line 5 lt 1 pt 6 lc rgb "#ff7f00" lw 2;
set style line 6 lt 1 pt 8 lc rgb "#f781bf" lw 2;
set out "roc-color.eps"
set pointsize 2.0
set size 1.59,1.04
set multiplot layout 1,2
set label "(a)" at graph -0.245,1.06 font "Helvetica-bold,40"
set xlab "Error rate of mapped PacBio reads"
set ylab "Fraction of mapped reads" off +1.8
set ytics 0.02
set yran [0.9:1]
set size 0.8,1
set log x
set format x "10^{%L}"
set key bot right
plot "<./eval2roc.pl blasr-mc.eval" u 2:3 t "blasr-mc" w lp ls 4, \
"<./eval2roc.pl bwa.eval" u 2:3 t "bwa-mem" w lp ls 2, \
"<./eval2roc.pl graphmap.eval" u 2:3 t "graphmap" w lp ls 3, \
"<./eval2roc.pl minialign.eval" u 2:3 t "minialign" w lp ls 1, \
"<./eval2roc.pl mm2.eval" u 2:3 t "minimap2" w lp ls 6, \
"<./eval2roc.pl ngmlr.eval" u 2:3 t "ngm-lr" w lp ls 5
unset label
set origin 0.8,0
set size 0.79,1
set label "(b)" at graph -0.245,1.06 font "Helvetica-bold,40"
set xlab "Error rate of mapped short reads"
set key top left
plot "<./eval2roc.pl -n2e7 bowtie2-s3.sam.eval" u 2:3 t "bowtie2" w lp ls 5, \
"<./eval2roc.pl -n2e7 bwa-s3.sam.eval" u 2:3 t "bwa-mem" w lp ls 2, \
"<./eval2roc.pl -n2e7 mm2-s3.sam.eval" u 2:3 t "minimap2" w lp ls 6, \
"<./eval2roc.pl -n2e7 snap-s3.sam.eval" u 2:3 t "snap" w lp ls 3
#unset log
#unset format
#unset key
#set log y
#set ylab "Accumulative mapping error rate" off +0
#set xlab "Mapping quality"
#set yran [1e-5:0.1]
#set ytics 1e-5,0.1
#set format y "10^{%L}"
#set xran [60:0] reverse
#plot "<./eval2roc.pl blasr-mc.eval" u 1:2 w lp ls 4, \
# "<./eval2roc.pl bwa.eval" u 1:2 t "bwa-mem" w lp ls 2, \
# "<./eval2roc.pl graphmap.eval" u 1:2 t "graphmap" w lp ls 3, \
# "<./eval2roc.pl minialign.eval" u 1:2 t "minialign" w lp ls 1, \
# "<./eval2roc.pl mm2.eval" u 1:2 t "minimap2" w lp ls 6, \
# "<./eval2roc.pl ngmlr.eval" u 1:2 t "ngm-lr" w lp ls 5
+62
View File
@@ -0,0 +1,62 @@
Q 60 18993268 10320 0.000543350 18993268
Q 59 33156 216 0.000553756 19026424
Q 58 29982 295 0.000568365 19056406
Q 57 9412 278 0.000582666 19065818
Q 56 11012 228 0.000594281 19076830
Q 55 9968 235 0.000606283 19086798
Q 54 8602 292 0.000621301 19095400
Q 53 6094 259 0.000634662 19101494
Q 52 5026 257 0.000647946 19106520
Q 51 4278 224 0.000659522 19110798
Q 50 3682 178 0.000668708 19114480
Q 49 2750 156 0.000676772 19117230
Q 48 2314 112 0.000682548 19119544
Q 47 2056 96 0.000687495 19121600
Q 46 1658 62 0.000690677 19123258
Q 45 1492 74 0.000694493 19124750
Q 44 1150 56 0.000697379 19125900
Q 43 1062 48 0.000699850 19126962
Q 42 976 60 0.000702951 19127938
Q 41 884 36 0.000704800 19128822
Q 40 708 52 0.000707493 19129530
Q 39 870 26 0.000708819 19130400
Q 38 598 26 0.000710156 19130998
Q 37 542 34 0.000711913 19131540
Q 36 846 50 0.000714495 19132386
Q 35 590 50 0.000717087 19132976
Q 34 550 42 0.000719261 19133526
Q 33 2174 66 0.000722628 19135700
Q 32 876 86 0.000727089 19136576
Q 31 638 104 0.000732500 19137214
Q 30 1718 196 0.000742675 19138932
Q 29 91022 968 0.000789497 19229954
Q 28 12864 781 0.000829556 19242818
Q 27 5806 427 0.000851489 19248624
Q 26 25274 728 0.000888144 19273898
Q 25 7418 680 0.000923070 19281316
Q 24 11800 701 0.000958839 19293116
Q 23 57328 3933 0.001159250 19350444
Q 22 7662 846 0.001202494 19358106
Q 21 5924 617 0.001233989 19364030
Q 20 4623 574 0.001263330 19368653
Q 19 4988 942 0.001311627 19373641
Q 18 3968 793 0.001352282 19377609
Q 17 3630 681 0.001387166 19381239
Q 16 2921 513 0.001413422 19384160
Q 15 2716 424 0.001435095 19386876
Q 14 2366 365 0.001453744 19389242
Q 13 2169 412 0.001474828 19391411
Q 12 2077 360 0.001493233 19393488
Q 11 2016 441 0.001515815 19395504
Q 10 2292 738 0.001553682 19397796
Q 9 4165 1832 0.001647772 19401961
Q 8 3963 1862 0.001743385 19405924
Q 7 3927 1793 0.001835408 19409851
Q 6 3572 1639 0.001919497 19413423
Q 5 3270 1533 0.001998126 19416693
Q 4 3046 1610 0.002080718 19419739
Q 3 251447 125550 0.008436553 19671186
Q 2 24390 13537 0.009113417 19695576
Q 1 124406 86780 0.013434624 19819982
Q 0 171254 153874 0.021016609 19991236
U 8764