Compare commits

...
49 Commits
Author SHA1 Message Date
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
33 changed files with 3015 additions and 317 deletions
-4
View File
@@ -10,10 +10,6 @@ matrix:
python: "2.7" python: "2.7"
before_install: pip install cython before_install: pip install cython
script: python setup.py build_ext script: python setup.py build_ext
- language: python
python: "3.3"
before_install: pip install cython
script: python setup.py build_ext
- language: python - language: python
python: "3.5" python: "3.5"
before_install: pip install cython before_install: pip install cython
+30 -4
View File
@@ -1,17 +1,24 @@
CFLAGS= -g -Wall -O2 -Wc++-compat CFLAGS= -g -Wall -O2 -Wc++-compat
CPPFLAGS= -DHAVE_KALLOC CPPFLAGS= -DHAVE_KALLOC
INCLUDES= INCLUDES=
OBJS= kthread.o kalloc.o misc.o bseq.o sketch.o sdust.o index.o chain.o align.o hit.o map.o format.o pe.o esterr.o ksw2_ll_sse.o OBJS= kthread.o kalloc.o misc.o bseq.o sketch.o sdust.o options.o index.o chain.o align.o hit.o map.o format.o pe.o esterr.o ksw2_ll_sse.o
PROG= minimap2 PROG= minimap2
PROG_EXTRA= sdust minimap2-lite PROG_EXTRA= sdust minimap2-lite
LIBS= -lm -lz -lpthread LIBS= -lm -lz -lpthread
ifeq ($(arm_neon),)
ifeq ($(sse2only),) ifeq ($(sse2only),)
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 OBJS+=ksw2_extz2_sse41.o ksw2_extd2_sse41.o ksw2_exts2_sse41.o ksw2_extz2_sse2.o ksw2_extd2_sse2.o ksw2_exts2_sse2.o ksw2_dispatch.o
else else
OBJS+=ksw2_extz2_sse.o ksw2_extd2_sse.o ksw2_exts2_sse.o OBJS+=ksw2_extz2_sse.o ksw2_extd2_sse.o ksw2_exts2_sse.o
endif endif
else
OBJS+=ksw2_extz2_neon.o ksw2_extd2_neon.o ksw2_exts2_neon.o
CFLAGS+=-D_FILE_OFFSET_BITS=64 -mfpu=neon -fsigned-char
INCLUDES+=-I sse2neon
endif
.PHONY:all extra clean depend
.SUFFIXES:.c .o .SUFFIXES:.c .o
.c.o: .c.o:
@@ -33,6 +40,8 @@ libminimap2.a:$(OBJS)
sdust:sdust.c getopt.o 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) $< getopt.o kalloc.o -o $@ -lz $(CC) -D_SDUST_MAIN $(CFLAGS) $< getopt.o kalloc.o -o $@ -lz
# SSE-specific targets on x86/x86_64
ksw2_extz2_sse41.o:ksw2_extz2_sse.c ksw2.h kalloc.h ksw2_extz2_sse41.o:ksw2_extz2_sse.c ksw2.h kalloc.h
$(CC) -c -msse4 $(CFLAGS) $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@ $(CC) -c -msse4 $(CFLAGS) $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@
@@ -54,8 +63,21 @@ ksw2_exts2_sse2.o:ksw2_exts2_sse.c ksw2.h kalloc.h
ksw2_dispatch.o:ksw2_dispatch.c ksw2.h ksw2_dispatch.o:ksw2_dispatch.c ksw2.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -DKSW_CPU_DISPATCH $(INCLUDES) $< -o $@ $(CC) -c $(CFLAGS) $(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 build dist mappy.so mappy.c python/mappy.c mappy.egg* rm -fr gmon.out *.o a.out $(PROG) $(PROG_EXTRA) *~ *.a *.dSYM build dist mappy*.so mappy.c python/mappy.c mappy.egg*
depend: depend:
(LC_ALL=C; export LC_ALL; makedepend -Y -- $(CFLAGS) $(CPPFLAGS) -- *.c) (LC_ALL=C; export LC_ALL; makedepend -Y -- $(CFLAGS) $(CPPFLAGS) -- *.c)
@@ -65,6 +87,7 @@ depend:
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 kvec.h kalloc.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: kalloc.h mmpriv.h minimap.h bseq.h format.o: kalloc.h mmpriv.h minimap.h bseq.h
getopt.o: getopt.h getopt.o: getopt.h
@@ -75,9 +98,12 @@ ksw2_extd2_sse.o: ksw2.h kalloc.h
ksw2_exts2_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
ksw2_ll_sse.o: ksw2.h kalloc.h ksw2_ll_sse.o: ksw2.h kalloc.h
kthread.o: kthread.h
main.o: bseq.h minimap.h mmpriv.h getopt.h 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: kthread.h kvec.h kalloc.h sdust.h mmpriv.h minimap.h bseq.h khash.h
misc.o: minimap.h ksort.h map.o: ksort.h
misc.o: mmpriv.h minimap.h bseq.h ksort.h
options.o: mmpriv.h minimap.h bseq.h
pe.o: mmpriv.h minimap.h bseq.h kvec.h kalloc.h ksort.h 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
+54
View File
@@ -1,3 +1,57 @@
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) Release 2.6-r623 (12 December 2017)
----------------------------------- -----------------------------------
+9 -9
View File
@@ -65,18 +65,21 @@ Detailed evaluations are available from the [minimap2 preprint][preprint].
### <a name="install"></a>Installation ### <a name="install"></a>Installation
Minimap2 only works on x86-64 CPUs. You can acquire precompiled binaries from Minimap2 is optimized for x86-64 CPUs. You can acquire precompiled binaries from
the [release page][release] with: the [release page][release] with:
```sh ```sh
curl -L https://github.com/lh3/minimap2/releases/download/v2.6/minimap2-2.6_x64-linux.tar.bz2 \ curl -L https://github.com/lh3/minimap2/releases/download/v2.8/minimap2-2.8_x64-linux.tar.bz2 \
| tar -jxvf - | tar -jxvf -
./minimap2-2.6_x64-linux/minimap2 ./minimap2-2.8_x64-linux/minimap2
``` ```
If you want to compile from the source, you need to have a C compiler, GNU make 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 and zlib development files installed. Then type `make` in the source code
directory to compile. If you see compilation errors, try `make sse2only=1` directory to compile. If you see compilation errors, try `make sse2only=1`
to disable SSE4 code, which will make minimap2 slightly slower. to disable SSE4 code, which will make minimap2 slightly slower.
Minimap2 also works with ARM CPUs supporting the NEON instruction sets. To
compile, use `make arm_neon=1`.
### <a name="general"></a>General usage ### <a name="general"></a>General usage
Without any options, minimap2 takes a reference database and a query sequence Without any options, minimap2 takes a reference database and a query sequence
@@ -352,12 +355,9 @@ mappy` or [from BioConda][mappyconda] via `conda install -c bioconda mappy`.
regions where seed positions may be suboptimal. This should not be a big 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. concern because even the optimal alignment may be wrong in such regions.
* Minimap2 requires SSE2 instructions to compile. It is possible to add * Minimap2 requires SSE2 instructions on x86 CPUs or NEON on ARM CPUs. It is
non-SSE2 support, but it would make minimap2 slower by several times. possible to add non-SIMD support, but it would make minimap2 slower by
several times.
In general, minimap2 is a young project with most code written since June, 2017.
It may have bugs and room for improvements. Bug reports and suggestions are
warmly welcomed.
+16 -1
View File
@@ -210,6 +210,13 @@ static void mm_align_pair(void *km, const mm_mapopt_t *opt, int qlen, const uint
ksw_extz2_sse(km, qlen, qseq, tlen, tseq, 5, mat, opt->q, opt->e, w, zdrop, end_bonus, flag, ez); 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, zdrop, end_bonus, 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)
@@ -492,6 +499,14 @@ static void mm_align1(void *km, const mm_mapopt_t *opt, const mm_idx_t *mi, int
re0 = re0 > re1? re0 : re1; 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);
@@ -689,7 +704,7 @@ mm_reg1_t *mm_align_skeleton(void *km, const mm_mapopt_t *opt, const mm_idx_t *m
regs[i].p->trans_strand = opt->flag&MM_F_SPLICE_FOR? 1 : 2; 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 (r2.cnt > 0) regs = mm_insert_reg(&r2, i, &n_regs, regs);
if (!(opt->flag&MM_F_SPLICE) && !(opt->flag&MM_F_SR) && i > 0) { // don't try inversion alignment for -xsplice or -xsr if (!(opt->flag&(MM_F_SPLICE|MM_F_SR)) && !(opt->flag&(MM_F_FOR_ONLY|MM_F_REV_ONLY)) && i > 0) { // don't try inversion alignment for -xsplice or -xsr, or --for-only/rev-only
if (mm_align1_inv(km, opt, mi, qlen, qseq0, &regs[i-1], &regs[i], &r2, &ez)) { 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); regs = mm_insert_reg(&r2, i, &n_regs, regs);
++i; // skip the inserted INV alignment ++i; // skip the inserted INV alignment
+9 -3
View File
@@ -2,6 +2,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#define __STDC_LIMIT_MACROS
#include "bseq.h" #include "bseq.h"
#include "kvec.h" #include "kvec.h"
#include "kseq.h" #include "kseq.h"
@@ -114,10 +115,15 @@ mm_bseq1_t *mm_bseq_read_frag(int n_fp, mm_bseq_file_t **fp, int chunk_size, int
*n_ = 0; *n_ = 0;
if (n_fp < 1) return 0; if (n_fp < 1) return 0;
while (1) { while (1) {
int n_read = 0;
for (i = 0; i < n_fp; ++i) for (i = 0; i < n_fp; ++i)
if (kseq_read(fp[i]->ks) < 0) if (kseq_read(fp[i]->ks) >= 0)
break; ++n_read;
if (i != n_fp) break; // some file reaches the end 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); if (a.m == 0) kv_resize(mm_bseq1_t, 0, a, 256);
for (i = 0; i < n_fp; ++i) { for (i = 0; i < n_fp; ++i) {
mm_bseq1_t *s; mm_bseq1_t *s;
+3 -1
View File
@@ -390,8 +390,10 @@ mm_seg_t *mm_seg_gen(void *km, uint32_t hash, int n_segs, const int *qlens, int
for (s = 0; s < n_segs; ++s) { 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); 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; n_regs[s] = seg[s].n_u;
for (i = 0; i < n_regs[s]; ++i) for (i = 0; i < n_regs[s]; ++i) {
regs[s][i].seg_split = 1; regs[s][i].seg_split = 1;
regs[s][i].seg_id = s;
}
} }
return seg; return seg;
} }
+45 -1
View File
@@ -7,6 +7,7 @@
#endif #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"
@@ -329,7 +330,7 @@ mm_idx_t *mm_idx_gen(mm_bseq_file_t *fp, int w, int k, int b, int flag, int mini
return pl.mi; return pl.mi;
} }
mm_idx_t *mm_idx_build(const char *fn, int w, int k, int flag, 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;
@@ -340,6 +341,49 @@ mm_idx_t *mm_idx_build(const char *fn, int w, int k, int flag, int n_threads) //
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 *
*************/ *************/
+17
View File
@@ -40,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; \
+4 -4
View File
@@ -116,7 +116,7 @@ static inline uint32_t *ksw_push_cigar(void *km, int *n_cigar, int *m_cigar, uin
// bit 0-2: which type gets the max - 0 for H, 1 for E, 2 for F, 3 for \tilde{E} and 4 for \tilde{F} // bit 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, int with_N, const uint8_t *p, const int *off, const int *off_end, int n_col, int i0, int j0, static inline void ksw_backtrack(void *km, int is_rot, int is_rev, int min_intron_len, const uint8_t *p, const int *off, const int *off_end, int n_col, int i0, int j0,
int *m_cigar_, int *n_cigar_, uint32_t **cigar_) 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;
@@ -138,11 +138,11 @@ static inline void ksw_backtrack(void *km, int is_rot, int is_rev, int with_N, c
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 (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 && !with_N)) 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 && with_N) cigar = ksw_push_cigar(km, &n_cigar, &m_cigar, cigar, 3, 1), --i; // intron 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
+2 -2
View File
@@ -367,9 +367,9 @@ void ksw_exts2_sse(void *km, int qlen, const uint8_t *query, int tlen, const uin
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, 1, (uint8_t*)p, off, off_end, n_col_*16, tlen-1, qlen-1, &ez->m_cigar, &ez->n_cigar, &ez->cigar); ksw_backtrack(km, 1, rev_cigar, long_thres, (uint8_t*)p, off, off_end, n_col_*16, tlen-1, qlen-1, &ez->m_cigar, &ez->n_cigar, &ez->cigar);
else if (ez->max_t >= 0 && ez->max_q >= 0) else if (ez->max_t >= 0 && ez->max_q >= 0)
ksw_backtrack(km, 1, rev_cigar, 1, (uint8_t*)p, off, off_end, n_col_*16, ez->max_t, ez->max_q, &ez->m_cigar, &ez->n_cigar, &ez->cigar); ksw_backtrack(km, 1, rev_cigar, long_thres, (uint8_t*)p, off, off_end, n_col_*16, ez->max_t, ez->max_q, &ez->m_cigar, &ez->n_cigar, &ez->cigar);
kfree(km, mem2); kfree(km, off); kfree(km, mem2); kfree(km, off);
} }
} }
+1
View File
@@ -2,6 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <limits.h> #include <limits.h>
#include <stdint.h> #include <stdint.h>
#include "kthread.h"
#if (defined(WIN32) || defined(_WIN32)) && defined(_MSC_VER) #if (defined(WIN32) || defined(_WIN32)) && defined(_MSC_VER)
#define __sync_fetch_and_add(ptr, addend) _InterlockedExchangeAdd((void*)ptr, addend) #define __sync_fetch_and_add(ptr, addend) _InterlockedExchangeAdd((void*)ptr, addend)
+45 -20
View File
@@ -6,7 +6,7 @@
#include "mmpriv.h" #include "mmpriv.h"
#include "getopt.h" #include "getopt.h"
#define MM_VERSION "2.6-r623" #define MM_VERSION "2.8-r672"
#ifdef __linux__ #ifdef __linux__
#include <sys/resource.h> #include <sys/resource.h>
@@ -28,7 +28,7 @@ static struct option long_options[] = {
{ "seed", required_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-seeds", 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 },
@@ -37,14 +37,19 @@ static struct option long_options[] = {
{ "cost-non-gt-ag", required_argument, 0, 'C' }, { "cost-non-gt-ag", required_argument, 0, 'C' },
{ "no-long-join", no_argument, 0, 0 }, { "no-long-join", no_argument, 0, 0 },
{ "sr", no_argument, 0, 0 }, { "sr", no_argument, 0, 0 },
{ "frag", optional_argument, 0, 0 }, { "frag", required_argument, 0, 0 },
{ "secondary", optional_argument, 0, 0 }, { "secondary", required_argument, 0, 0 },
{ "cs", optional_argument, 0, 0 }, { "cs", optional_argument, 0, 0 },
{ "end-bonus", required_argument, 0, 0 }, { "end-bonus", required_argument, 0, 0 },
{ "no-pairing", no_argument, 0, 0 }, { "no-pairing", no_argument, 0, 0 },
{ "splice-flank", optional_argument, 0, 0 }, { "splice-flank", required_argument, 0, 0 },
{ "idx-no-seq", no_argument, 0, 0 }, { "idx-no-seq", no_argument, 0, 0 },
{ "end-seed-pen", required_argument, 0, 0 }, // 21 { "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
{ "help", no_argument, 0, 'h' }, { "help", no_argument, 0, 'h' },
{ "max-intron-len", required_argument, 0, 'G' }, { "max-intron-len", required_argument, 0, 'G' },
{ "version", no_argument, 0, 'V' }, { "version", no_argument, 0, 'V' },
@@ -67,9 +72,22 @@ static inline int64_t mm_parse_num(const char *str)
return (int64_t)(x + .499); 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 = "2aSw:k:K:t:r:f:Vv:g:G:I:d:XT:s:x:Hcp:M:n:z:A:B:O:E:m:N:Qu:R:hF:LC:"; const char *opt_str = "2aSDw:k:K:t:r:f:Vv:g:G:I:d:XT:s:x:Hcp:M:n:z:A:B:O:E:m:N:Qu:R:hF:LC:";
mm_mapopt_t opt; mm_mapopt_t opt;
mm_idxopt_t ipt; mm_idxopt_t ipt;
int i, c, n_threads = 3, long_idx; int i, c, n_threads = 3, long_idx;
@@ -108,7 +126,9 @@ int main(int argc, char *argv[])
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_OUT_CG | 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 == 'Y') opt.flag |= MM_F_SOFTCLIP;
@@ -130,11 +150,10 @@ int main(int argc, char *argv[])
else if (c == 0 && long_idx == 2) opt.seed = atoi(optarg); // --seed 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, n_threads = 1; // --print-seed
else if (c == 0 && long_idx == 7) opt.max_chain_skip = atoi(optarg); // --max-chain-skip else if (c == 0 && long_idx == 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 == 0 && long_idx == 9) mm_dbg_flag |= MM_DBG_PRINT_QNAME | MM_DBG_PRINT_ALN_SEQ; // --print-aln-seq else if (c == 0 && long_idx == 9) mm_dbg_flag |= MM_DBG_PRINT_QNAME | MM_DBG_PRINT_ALN_SEQ, n_threads = 1; // --print-aln-seq
else if (c == 0 && long_idx ==10) opt.flag |= MM_F_SPLICE; // --splice else if (c == 0 && long_idx ==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 ==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 ==13) opt.flag |= MM_F_SR; // --sr
@@ -142,14 +161,12 @@ int main(int argc, char *argv[])
else if (c == 0 && long_idx ==18) opt.flag |= MM_F_INDEPEND_SEG; // --no-pairing 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 ==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 ==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 == 14) { // --frag else if (c == 0 && long_idx == 14) { // --frag
if (optarg == 0 || strcmp(optarg, "yes") == 0 || strcmp(optarg, "y") == 0) yes_or_no(&opt, MM_F_FRAG_MODE, long_idx, optarg, 1);
opt.flag |= MM_F_FRAG_MODE;
else opt.flag &= ~MM_F_FRAG_MODE;
} else if (c == 0 && long_idx == 15) { // --secondary } else if (c == 0 && long_idx == 15) { // --secondary
if (optarg == 0 || strcmp(optarg, "yes") == 0 || strcmp(optarg, "y") == 0) yes_or_no(&opt, MM_F_NO_PRINT_2ND, long_idx, optarg, 0);
opt.flag &= ~MM_F_NO_PRINT_2ND;
else opt.flag |= MM_F_NO_PRINT_2ND;
} else if (c == 0 && long_idx == 16) { // --cs } else if (c == 0 && long_idx == 16) { // --cs
opt.flag |= MM_F_OUT_CS | MM_F_CIGAR; opt.flag |= MM_F_OUT_CS | MM_F_CIGAR;
if (optarg == 0 || strcmp(optarg, "short") == 0) { if (optarg == 0 || strcmp(optarg, "short") == 0) {
@@ -162,9 +179,11 @@ int main(int argc, char *argv[])
fprintf(stderr, "[WARNING]\033[1;31m --cs only takes 'short' or 'long'. Invalid values are assumed to be 'short'.\033[0m\n"); 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 } else if (c == 0 && long_idx == 19) { // --splice-flank
if (optarg == 0 || strcmp(optarg, "yes") == 0 || strcmp(optarg, "y") == 0) yes_or_no(&opt, MM_F_SPLICE_FLANK, long_idx, optarg, 1);
opt.flag |= MM_F_SPLICE_FLANK; } else if (c == 0 && long_idx == 24) { // --heap-sort
else opt.flag &= ~MM_F_SPLICE_FLANK; 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') { } else if (c == 'S') {
opt.flag |= MM_F_OUT_CS | MM_F_CIGAR | MM_F_OUT_CS_LONG; opt.flag |= MM_F_OUT_CS | MM_F_CIGAR | MM_F_OUT_CS_LONG;
if (mm_verbose >= 2) if (mm_verbose >= 2)
@@ -202,6 +221,8 @@ int main(int argc, char *argv[])
} }
if (!fnw && !(opt.flag&MM_F_CIGAR)) if (!fnw && !(opt.flag&MM_F_CIGAR))
ipt.flag |= MM_I_NO_SEQ; ipt.flag |= MM_I_NO_SEQ;
if (mm_check_opt(&ipt, &opt) < 0)
return 1;
if (argc == optind || fp_help == stdout) { if (argc == optind || fp_help == stdout) {
fprintf(fp_help, "Usage: minimap2 [options] <target.fa>|<target.idx> [query.fa] [...]\n"); fprintf(fp_help, "Usage: minimap2 [options] <target.fa>|<target.idx> [query.fa] [...]\n");
@@ -250,14 +271,18 @@ int main(int argc, char *argv[])
fprintf(fp_help, " map-ont: -k15 (Oxford Nanopore vs reference mapping)\n"); fprintf(fp_help, " map-ont: -k15 (Oxford Nanopore vs reference mapping)\n");
fprintf(fp_help, " asm5: -k19 -w19 -A1 -B19 -O39,81 -E3,1 -s200 -z200 (asm to ref mapping; break at 5%% div.)\n"); fprintf(fp_help, " asm5: -k19 -w19 -A1 -B19 -O39,81 -E3,1 -s200 -z200 (asm to ref mapping; break at 5%% div.)\n");
fprintf(fp_help, " asm10: -k19 -w19 -A1 -B9 -O16,41 -E2,1 -s200 -z200 (asm to ref mapping; break at 10%% div.)\n"); fprintf(fp_help, " asm10: -k19 -w19 -A1 -B9 -O16,41 -E2,1 -s200 -z200 (asm to ref mapping; break at 10%% div.)\n");
fprintf(fp_help, " ava-pb: -Hk19 -w5 -Xp0 -m100 -g10000 --max-chain-skip 25 (PacBio read overlap)\n"); fprintf(fp_help, " ava-pb: -Hk19 -Xw5 -m100 -g10000 --max-chain-skip 25 (PacBio read overlap)\n");
fprintf(fp_help, " ava-ont: -k15 -w5 -Xp0 -m100 -g10000 --max-chain-skip 25 (ONT read overlap)\n"); fprintf(fp_help, " ava-ont: -k15 -Xw5 -m100 -g10000 --max-chain-skip 25 (ONT read overlap)\n");
fprintf(fp_help, " splice: long-read spliced alignment (see minimap2.1 for details)\n"); fprintf(fp_help, " splice: long-read spliced alignment (see minimap2.1 for details)\n");
fprintf(fp_help, " sr: short single-end reads without splicing (see minimap2.1 for details)\n"); fprintf(fp_help, " sr: short single-end reads without splicing (see minimap2.1 for details)\n");
fprintf(fp_help, "\nSee `man ./minimap2.1' for detailed description of command-line options.\n"); fprintf(fp_help, "\nSee `man ./minimap2.1' for detailed description of command-line options.\n");
return fp_help == stdout? 0 : 1; 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); idx_rdr = mm_idx_reader_open(argv[optind], &ipt, fnw);
if (idx_rdr == 0) { if (idx_rdr == 0) {
fprintf(stderr, "[ERROR] failed to open file '%s'\n", argv[optind]); fprintf(stderr, "[ERROR] failed to open file '%s'\n", argv[optind]);
+167 -181
View File
@@ -9,119 +9,6 @@
#include "bseq.h" #include "bseq.h"
#include "khash.h" #include "khash.h"
void mm_mapopt_init(mm_mapopt_t *opt)
{
memset(opt, 0, sizeof(mm_mapopt_t));
opt->seed = 11;
opt->mid_occ_frac = 2e-4f;
opt->sdust_thres = 0; // no SDUST masking
opt->min_cnt = 3;
opt->min_chain_score = 40;
opt->bw = 500;
opt->max_gap = 5000;
opt->max_gap_ref = -1;
opt->max_chain_skip = 25;
opt->mask_level = 0.5f;
opt->pri_ratio = 0.8f;
opt->best_n = 5;
opt->max_join_long = 20000;
opt->max_join_short = 2000;
opt->min_join_flank_sc = 1000;
opt->a = 2, opt->b = 4, opt->q = 4, opt->e = 2, opt->q2 = 24, opt->e2 = 1;
opt->zdrop = 400;
opt->end_bonus = -1;
opt->min_dp_max = opt->min_chain_score * opt->a;
opt->min_ksw_len = 200;
opt->anchor_ext_len = 20, opt->anchor_ext_shift = 6;
opt->mini_batch_size = 500000000;
opt->pe_ori = 0; // FF
opt->pe_bonus = 33;
}
void mm_mapopt_update(mm_mapopt_t *opt, const mm_idx_t *mi)
{
if ((opt->flag & MM_F_SPLICE_FOR) && (opt->flag & MM_F_SPLICE_REV))
opt->flag |= MM_F_SPLICE;
if (opt->mid_occ <= 0)
opt->mid_occ = mm_idx_cal_max_occ(mi, opt->mid_occ_frac);
if (mm_verbose >= 3)
fprintf(stderr, "[M::%s::%.3f*%.2f] mid_occ = %d\n", __func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0), opt->mid_occ);
}
void mm_mapopt_max_intron_len(mm_mapopt_t *opt, int max_intron_len)
{
if ((opt->flag & MM_F_SPLICE) && max_intron_len > 0)
opt->max_gap_ref = opt->bw = max_intron_len;
}
int mm_set_opt(const char *preset, mm_idxopt_t *io, mm_mapopt_t *mo)
{
if (preset == 0) {
mm_idxopt_init(io);
mm_mapopt_init(mo);
} else if (strcmp(preset, "ava-ont") == 0) {
io->flag = 0, io->k = 15, io->w = 5;
mo->flag |= MM_F_AVA | MM_F_NO_SELF;
mo->min_chain_score = 100, mo->pri_ratio = 0.0f, mo->max_gap = 10000, mo->max_chain_skip = 25;
} else if (strcmp(preset, "ava-pb") == 0) {
io->flag |= MM_I_HPC, io->k = 19, io->w = 5;
mo->flag |= MM_F_AVA | MM_F_NO_SELF;
mo->min_chain_score = 100, mo->pri_ratio = 0.0f, mo->max_gap = 10000, mo->max_chain_skip = 25;
} else if (strcmp(preset, "map10k") == 0 || strcmp(preset, "map-pb") == 0) {
io->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 = 200;
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 = 200;
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;
mo->pe_ori = 0<<1|1; // FR
mo->a = 2, mo->b = 8, mo->q = 12, mo->e = 2, mo->q2 = 24, mo->e2 = 1;
mo->zdrop = 100;
mo->end_bonus = 10;
mo->max_frag_len = 800;
mo->max_gap = 100;
mo->bw = 100;
mo->pri_ratio = 0.5f;
mo->min_cnt = 2;
mo->min_chain_score = 25;
mo->min_dp_max = 40;
mo->best_n = 20;
mo->mid_occ = 1000;
mo->max_occ = 5000;
mo->mini_batch_size = 50000000;
} else if (strcmp(preset, "splice") == 0 || strcmp(preset, "cdna") == 0) {
io->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;
} else return -1;
return 0;
}
typedef struct {
uint32_t n;
uint32_t qpos;
uint32_t seg_id;
const uint64_t *cr;
} mm_match_t;
struct mm_tbuf_s { struct mm_tbuf_s {
void *km; void *km;
}; };
@@ -181,88 +68,189 @@ static void collect_minimizers(void *km, const mm_mapopt_t *opt, const mm_idx_t
} }
} }
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, #include "ksort.h"
int *n_mini_pos, uint64_t **mini_pos) #define heap_lt(a, b) ((a).x > (b).x)
{ KSORT_INIT(heap, mm128_t, heap_lt)
int rep_st = 0, rep_en = 0, i;
mm_match_t *m;
mm128_t *a;
typedef struct {
uint32_t n;
uint32_t q_pos, q_span;
uint32_t seg_id:31, is_tandem:1;
const uint64_t *cr;
} mm_match_t;
static mm_match_t *collect_matches(void *km, int *_n_m, int max_occ, const mm_idx_t *mi, const mm128_v *mv, int64_t *n_a, int *rep_len, int *n_mini_pos, uint64_t **mini_pos)
{
int i, rep_st = 0, rep_en = 0, n_m;
mm_match_t *m;
*n_mini_pos = 0; *n_mini_pos = 0;
*mini_pos = (uint64_t*)kmalloc(km, mv->n * sizeof(uint64_t)); *mini_pos = (uint64_t*)kmalloc(km, mv->n * sizeof(uint64_t));
m = (mm_match_t*)kmalloc(km, mv->n * sizeof(mm_match_t)); m = (mm_match_t*)kmalloc(km, mv->n * sizeof(mm_match_t));
for (i = 0; i < mv->n; ++i) { for (i = 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; int t;
mm128_t *p = &mv->a[i]; cr = mm_idx_get(mi, p->x>>8, &t);
m[i].qpos = (uint32_t)p->y; if (t >= max_occ) {
m[i].cr = mm_idx_get(mi, p->x>>8, &t); int en = (q_pos >> 1) + 1, st = en - q_span;
m[i].n = t;
m[i].seg_id = p->y >> 32;
}
for (i = 0, *n_a = 0; i < mv->n; ++i) // find the length of a[]
if (m[i].n < max_occ) *n_a += m[i].n;
a = (mm128_t*)kmalloc(km, *n_a * sizeof(mm128_t));
for (i = *rep_len = 0, *n_a = 0; i < mv->n; ++i) {
mm128_t *p = &mv->a[i];
mm_match_t *q = &m[i];
const uint64_t *r = q->cr;
int k, q_span = p->x & 0xff, is_tandem = 0;
if (q->n >= max_occ) {
int en = (q->qpos>>1) + 1, st = en - q_span;
if (st > rep_en) { if (st > rep_en) {
*rep_len += rep_en - rep_st; *rep_len += rep_en - rep_st;
rep_st = st, rep_en = en; rep_st = st, rep_en = en;
} else rep_en = en; } else rep_en = en;
continue; } else {
} mm_match_t *q = &m[n_m++];
(*mini_pos)[(*n_mini_pos)++] = (uint64_t)q_span<<32 | q->qpos>>1; q->q_pos = q_pos, q->q_span = q_span, q->cr = cr, q->n = t, q->seg_id = p->y >> 32;
if (i > 0 && p->x>>8 == mv->a[i - 1].x>>8) is_tandem = 1; q->is_tandem = 0;
if (i < mv->n - 1 && p->x>>8 == mv->a[i + 1].x>>8) is_tandem = 1; if (i > 0 && p->x>>8 == mv->a[i - 1].x>>8) q->is_tandem = 1;
for (k = 0; k < q->n; ++k) { if (i < mv->n - 1 && p->x>>8 == mv->a[i + 1].x>>8) q->is_tandem = 1;
int32_t rpos = (uint32_t)r[k] >> 1; *n_a += q->n;
mm128_t *p; (*mini_pos)[(*n_mini_pos)++] = (uint64_t)q_span<<32 | q_pos>>1;
if (qname && (opt->flag&(MM_F_NO_SELF|MM_F_AVA))) {
const char *tname = mi->seq[r[k]>>32].name;
int cmp;
cmp = strcmp(qname, tname);
if ((opt->flag&MM_F_NO_SELF) && cmp == 0 && rpos == (q->qpos>>1)) // avoid the diagonal
continue;
if ((opt->flag&MM_F_AVA) && cmp > 0) // all-vs-all mode: map once
continue;
}
p = &a[(*n_a)++];
if ((r[k]&1) == (q->qpos&1)) { // forward strand
p->x = (r[k]&0xffffffff00000000ULL) | rpos;
p->y = (uint64_t)q_span << 32 | q->qpos >> 1;
} else { // reverse strand
p->x = 1ULL<<63 | (r[k]&0xffffffff00000000ULL) | rpos;
p->y = (uint64_t)q_span << 32 | (qlen - ((q->qpos>>1) + 1 - q_span) - 1);
}
p->y |= (uint64_t)q->seg_id << MM_SEED_SEG_SHIFT;
if (is_tandem) p->y |= MM_SEED_TANDEM;
} }
} }
*rep_len += rep_en - rep_st; *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 && 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)) continue;
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, 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, k, n_m;
mm_match_t *m;
mm128_t *a;
m = collect_matches(km, &n_m, max_occ, mi, mv, n_a, rep_len, n_mini_pos, mini_pos);
a = (mm128_t*)kmalloc(km, *n_a * sizeof(mm128_t));
for (i = 0, *n_a = 0; i < n_m; ++i) {
mm_match_t *q = &m[i];
const uint64_t *r = q->cr;
for (k = 0; k < q->n; ++k) {
int32_t is_self, rpos = (uint32_t)r[k] >> 1;
mm128_t *p;
if (skip_seed(opt->flag, r[k], q, qname, qlen, mi, &is_self)) continue;
p = &a[(*n_a)++];
if ((r[k]&1) == (q->q_pos&1)) { // forward strand
p->x = (r[k]&0xffffffff00000000ULL) | rpos;
p->y = (uint64_t)q->q_span << 32 | q->q_pos >> 1;
} else { // reverse strand
p->x = 1ULL<<63 | (r[k]&0xffffffff00000000ULL) | rpos;
p->y = (uint64_t)q->q_span << 32 | (qlen - ((q->q_pos>>1) + 1 - q->q_span) - 1);
}
p->y |= (uint64_t)q->seg_id << MM_SEED_SEG_SHIFT;
if (q->is_tandem) p->y |= MM_SEED_TANDEM;
if (is_self) p->y |= MM_SEED_SELF;
}
}
kfree(km, m);
radix_sort_128x(a, a + (*n_a));
return a; return a;
} }
static void chain_post(const mm_mapopt_t *opt, int max_chain_gap_ref, const mm_idx_t *mi, void *km, int qlen, int n_segs, const int *qlens, int *n_regs, mm_reg1_t *regs, mm128_t *a) static void chain_post(const mm_mapopt_t *opt, int max_chain_gap_ref, const mm_idx_t *mi, void *km, int qlen, int n_segs, const int *qlens, int *n_regs, mm_reg1_t *regs, mm128_t *a)
{ {
if (!(opt->flag & MM_F_AVA)) { // don't choose primary mapping(s) for read overlap if (!(opt->flag & MM_F_ALL_CHAINS)) { // don't choose primary mapping(s)
mm_set_parent(km, opt->mask_level, *n_regs, regs, opt->a * 2 + opt->b); mm_set_parent(km, opt->mask_level, *n_regs, regs, opt->a * 2 + opt->b);
if (n_segs <= 1) mm_select_sub(km, opt->pri_ratio, mi->k*2, opt->best_n, n_regs, regs); if (n_segs <= 1) mm_select_sub(km, opt->pri_ratio, mi->k*2, opt->best_n, n_regs, regs);
else mm_select_sub_multi(km, opt->pri_ratio, 0.2f, 0.7f, max_chain_gap_ref, mi->k*2, opt->best_n, n_segs, qlens, n_regs, regs); else mm_select_sub_multi(km, opt->pri_ratio, 0.2f, 0.7f, max_chain_gap_ref, mi->k*2, opt->best_n, n_segs, qlens, n_regs, regs);
if (!(opt->flag & MM_F_SPLICE) && !(opt->flag & MM_F_SR) && !(opt->flag & MM_F_NO_LJOIN)) if (!(opt->flag & (MM_F_SPLICE|MM_F_SR|MM_F_NO_LJOIN))) // long join not working well without primary chains
mm_join_long(km, opt, qlen, n_regs, regs, a); mm_join_long(km, opt, qlen, n_regs, regs, a);
} }
} }
static mm_reg1_t *align_regs(const mm_mapopt_t *opt, const mm_idx_t *mi, void *km, int qlen, const char *seq, const char *qual, int *n_regs, mm_reg1_t *regs, mm128_t *a) static mm_reg1_t *align_regs(const mm_mapopt_t *opt, const mm_idx_t *mi, void *km, int qlen, const char *seq, int *n_regs, mm_reg1_t *regs, mm128_t *a)
{ {
if (!(opt->flag & MM_F_CIGAR)) return regs; 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() regs = mm_align_skeleton(km, opt, mi, qlen, seq, n_regs, regs, a); // this calls mm_filter_regs()
if (!(opt->flag & MM_F_AVA)) { 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_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_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);
@@ -270,7 +258,7 @@ static mm_reg1_t *align_regs(const mm_mapopt_t *opt, const mm_idx_t *mi, void *k
return regs; return regs;
} }
void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **seqs, const char **quals, int *n_regs, mm_reg1_t **regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *qname) void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **seqs, int *n_regs, mm_reg1_t **regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *qname)
{ {
int i, j, rep_len, qlen_sum, n_regs0, n_mini_pos; 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); int max_chain_gap_qry, max_chain_gap_ref, is_splice = !!(opt->flag & MM_F_SPLICE), is_sr = !!(opt->flag & MM_F_SR);
@@ -292,8 +280,8 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **
hash = __ac_Wang_hash(hash); hash = __ac_Wang_hash(hash);
collect_minimizers(b->km, opt, mi, n_segs, qlens, seqs, &mv); collect_minimizers(b->km, opt, mi, n_segs, qlens, seqs, &mv);
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 (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);
radix_sort_128x(a, a + n_a); 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) { if (mm_dbg_flag & MM_DBG_PRINT_SEED) {
fprintf(stderr, "RS\t%d\n", rep_len); fprintf(stderr, "RS\t%d\n", rep_len);
@@ -333,8 +321,8 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **
kfree(b->km, a); kfree(b->km, a);
kfree(b->km, u); kfree(b->km, u);
kfree(b->km, mini_pos); kfree(b->km, mini_pos);
a = collect_seed_hits(b->km, opt, opt->max_occ, mi, qname, &mv, qlen_sum, &n_a, &rep_len, &n_mini_pos, &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);
radix_sort_128x(a, a + n_a); 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); 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);
} }
} }
@@ -351,7 +339,7 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **
if (!is_sr) mm_est_err(mi, qlen_sum, n_regs0, regs0, a, n_mini_pos, mini_pos); if (!is_sr) mm_est_err(mi, qlen_sum, n_regs0, regs0, a, n_mini_pos, mini_pos);
if (n_segs == 1) { // uni-segment if (n_segs == 1) { // uni-segment
regs0 = align_regs(opt, mi, b->km, qlens[0], seqs[0], quals? quals[0] : 0, &n_regs0, regs0, a); regs0 = align_regs(opt, mi, b->km, qlens[0], seqs[0], &n_regs0, regs0, a);
mm_set_mapq(n_regs0, regs0, opt->min_chain_score, opt->a, rep_len, is_sr); mm_set_mapq(n_regs0, regs0, opt->min_chain_score, opt->a, rep_len, is_sr);
n_regs[0] = n_regs0, regs[0] = regs0; n_regs[0] = n_regs0, regs[0] = regs0;
} else { // multi-segment } else { // multi-segment
@@ -360,7 +348,7 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **
free(regs0); free(regs0);
for (i = 0; i < n_segs; ++i) { 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 mm_set_parent(b->km, opt->mask_level, n_regs[i], regs[i], opt->a * 2 + opt->b); // update mm_reg1_t::parent
regs[i] = align_regs(opt, mi, b->km, qlens[i], seqs[i], quals? quals[i] : 0, &n_regs[i], regs[i], seg[i].a); regs[i] = align_regs(opt, mi, b->km, qlens[i], seqs[i], &n_regs[i], regs[i], seg[i].a);
mm_set_mapq(n_regs[i], regs[i], opt->min_chain_score, opt->a, rep_len, is_sr); mm_set_mapq(n_regs[i], regs[i], opt->min_chain_score, opt->a, rep_len, is_sr);
} }
mm_seg_free(b->km, n_segs, seg); mm_seg_free(b->km, n_segs, seg);
@@ -388,7 +376,7 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **
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 *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;
mm_map_frag(mi, 1, &qlen, &seq, 0, n_regs, &regs, b, opt, qname); mm_map_frag(mi, 1, &qlen, &seq, n_regs, &regs, b, opt, qname);
return regs; return regs;
} }
@@ -416,11 +404,10 @@ typedef struct {
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 *s = (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, is_sr = !!(s->p->opt->flag & MM_F_SR); int qlens[MM_MAX_SEG], j, off = s->seg_off[i], pe_ori = s->p->opt->pe_ori;
const char *qseqs[MM_MAX_SEG], *quals[MM_MAX_SEG]; const char *qseqs[MM_MAX_SEG];
mm_tbuf_t *b = s->buf[tid]; mm_tbuf_t *b = s->buf[tid];
assert(s->n_seg[i] <= MM_MAX_SEG); assert(s->n_seg[i] <= MM_MAX_SEG);
memset(quals, 0, sizeof(char*) * 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\t%d\n", s->seq[off].name, tid, s->seq[off].l_seq); fprintf(stderr, "QR\t%s\t%d\t%d\n", s->seq[off].name, tid, s->seq[off].l_seq);
for (j = 0; j < s->n_seg[i]; ++j) { for (j = 0; j < s->n_seg[i]; ++j) {
@@ -428,13 +415,12 @@ static void worker_for(void *_data, long i, int tid) // kt_for() callback
mm_revcomp_bseq(&s->seq[off + j]); mm_revcomp_bseq(&s->seq[off + j]);
qlens[j] = s->seq[off + j].l_seq; qlens[j] = s->seq[off + j].l_seq;
qseqs[j] = s->seq[off + j].seq; qseqs[j] = s->seq[off + j].seq;
quals[j] = is_sr? s->seq[off + j].qual : 0;
} }
if (s->p->opt->flag & MM_F_INDEPEND_SEG) { if (s->p->opt->flag & MM_F_INDEPEND_SEG) {
for (j = 0; j < s->n_seg[i]; ++j) for (j = 0; j < s->n_seg[i]; ++j)
mm_map_frag(s->p->mi, 1, &qlens[j], &qseqs[j], &quals[j], &s->n_reg[off+j], &s->reg[off+j], b, s->p->opt, s->seq[off+j].name); 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);
} else { } else {
mm_map_frag(s->p->mi, s->n_seg[i], qlens, qseqs, quals, &s->n_reg[off], &s->reg[off], b, s->p->opt, s->seq[off].name); mm_map_frag(s->p->mi, s->n_seg[i], qlens, qseqs, &s->n_reg[off], &s->reg[off], b, s->p->opt, s->seq[off].name);
} }
for (j = 0; j < s->n_seg[i]; ++j) // flip the query strand and coordinate to the original read strand 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)))) { if (s->n_seg[i] == 2 && ((j == 0 && (pe_ori>>1&1)) || (j == 1 && (pe_ori&1)))) {
+25 -3
View File
@@ -5,8 +5,8 @@
#include <stdio.h> #include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
#define MM_F_NO_SELF 0x001 #define MM_F_NO_DIAG 0x001 // no exact diagonal hit
#define MM_F_AVA 0x002 #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_CIGAR 0x004
#define MM_F_OUT_SAM 0x008 #define MM_F_OUT_SAM 0x008
#define MM_F_NO_QUAL 0x010 #define MM_F_NO_QUAL 0x010
@@ -25,6 +25,10 @@
#define MM_F_INDEPEND_SEG 0x20000 #define MM_F_INDEPEND_SEG 0x20000
#define MM_F_SPLICE_FLANK 0x40000 #define MM_F_SPLICE_FLANK 0x40000
#define MM_F_SOFTCLIP 0x80000 #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_I_HPC 0x1 #define MM_I_HPC 0x1
#define MM_I_NO_SEQ 0x2 #define MM_I_NO_SEQ 0x2
@@ -78,7 +82,7 @@ typedef struct {
int32_t mlen, blen; // seeded exact match length; seeded alignment block length int32_t mlen, blen; // seeded exact match length; seeded alignment block length
int32_t n_sub; // number of suboptimal mappings int32_t n_sub; // number of suboptimal mappings
int32_t score0; // initial chaining score (before chain merging/spliting) 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, dummy:16; 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, dummy:8;
uint32_t hash; uint32_t hash;
float div; float div;
mm_extra_t *p; mm_extra_t *p;
@@ -155,6 +159,7 @@ extern double mm_realtime0; // wall-clock timer
* @return 0 if success; -1 if _present_ unknown * @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_set_opt(const char *preset, mm_idxopt_t *io, mm_mapopt_t *mo);
int mm_check_opt(const mm_idxopt_t *io, const mm_mapopt_t *mo);
/** /**
* Update mm_mapopt_t::mid_occ via mm_mapopt_t::mid_occ_frac * Update mm_mapopt_t::mid_occ via mm_mapopt_t::mid_occ_frac
@@ -207,6 +212,21 @@ void mm_idx_reader_close(mm_idx_reader_t *r);
int mm_idx_reader_eof(const mm_idx_reader_t *r); int mm_idx_reader_eof(const mm_idx_reader_t *r);
/**
* 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 * Print index statistics to stderr
* *
@@ -260,6 +280,8 @@ void mm_tbuf_destroy(mm_tbuf_t *b);
*/ */
mm_reg1_t *mm_map(const mm_idx_t *mi, int l_seq, const char *seq, int *n_regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *name); mm_reg1_t *mm_map(const mm_idx_t *mi, int l_seq, const char *seq, int *n_regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *name);
void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **seqs, int *n_regs, mm_reg1_t **regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *qname);
/** /**
* Align a fasta/fastq file and print alignments to stdout * Align a fasta/fastq file and print alignments to stdout
* *
+54 -18
View File
@@ -1,4 +1,4 @@
.TH minimap2 1 "12 December 2017" "minimap2-2.6 (r623)" "Bioinformatics tools" .TH minimap2 1 "1 February 2018" "minimap2-2.8 (r672)" "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
@@ -129,7 +129,7 @@ Ignore top
fraction of most frequent minimizers [0.0002] fraction of most frequent minimizers [0.0002]
.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
@@ -148,11 +148,28 @@ Discard chains with chaining score
[40]. Chaining score equals the approximate number of matching bases minus a [40]. Chaining score equals the approximate number of matching bases minus a
concave gap penalty. It is computed with dynamic programming. concave gap penalty. It is computed with dynamic programming.
.TP .TP
.B -D
If query sequence name/length are identical to the target name/length, ignore
diagonal anchors. This option also reduces DP-based extension along the
diagonal.
.TP
.B -P
Retain all chains and don't attempt to set primary chains. Options
.B -p
and
.B -N
have no effect when this option is in use.
.TP
.BR --dual = yes | no
During chaining, whether to skip pairs wherein the query name is
lexicographically greater than the target name [yes]
.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].
@@ -162,6 +179,9 @@ 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
@@ -179,9 +199,14 @@ Increasing this option slows down spliced alignment. [200k]
.TP .TP
.BI -F \ NUM .BI -F \ NUM
Maximum fragment length (aka insert size; effective with Maximum fragment length (aka insert size; effective with
.BR -xsr / --frag) .BR -xsr / --frag = yes )
[800] [800]
.TP .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
@@ -204,8 +229,20 @@ 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 if no good chain is found. In addition, minimap2 attempts to patch gaps between
seeds with ungapped alignment. seeds with ungapped alignment.
.TP .TP
.BR --frag [= no | yes ] .BR --frag = no | yes
Whether to enable the fragment mode [no] 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
@@ -255,14 +292,13 @@ no attempt to match GT-AG [n]
.BI --end-bonus \ INT .BI --end-bonus \ INT
Score bonus when alignment extends to the end of the query sequence [0]. Score bonus when alignment extends to the end of the query sequence [0].
.TP .TP
.BR --splice-flank [= yes | no ] .BR --splice-flank = yes | no
Assume the next base to a Assume the next base to a
.B GT .B GT
donor site tends to be A/G (91% in human and 92% in mouse) and the preceding donor site tends to be A/G (91% in human and 92% in mouse) and the preceding
base to a base to a
.B AG .B AG
acceptor tends to be C/T [yes with acceptor tends to be C/T [no].
.BR --splice ].
This trend is evolutionarily conservative, all the way to S. cerevisiae This trend is evolutionarily conservative, all the way to S. cerevisiae
(PMID:18688272). Specifying this option generally leads to higher junction (PMID:18688272). Specifying this option generally leads to higher junction
accuracy by several percents, so it is applied by default with accuracy by several percents, so it is applied by default with
@@ -352,7 +388,7 @@ K/M/G/k/m/g suffix is accepted. A large
helps load balancing in the multi-threading mode, at the cost of increased helps load balancing in the multi-threading mode, at the cost of increased
memory. memory.
.TP .TP
.BR --secondary [= yes | no ] .BR --secondary = yes | no
Whether to output secondary alignments [yes] Whether to output secondary alignments [yes]
.TP .TP
.B --version .B --version
@@ -399,13 +435,13 @@ Up to 10% sequence divergence.
.B ava-pb .B ava-pb
PacBio all-vs-all overlap mapping PacBio all-vs-all overlap mapping
.RB ( -Hk19 .RB ( -Hk19
.B -w5 -Xp0 -m100 -g10000 --max-chain-skip .B -Xw5 -m100 -g10000 --max-chain-skip
.BR 25 ). .BR 25 ).
.TP .TP
.B ava-ont .B ava-ont
Oxford Nanopore all-vs-all overlap mapping Oxford Nanopore all-vs-all overlap mapping
.RB ( -k15 .RB ( -k15
.B -w5 -Xp0 -m100 -g10000 --max-chain-skip .B -Xw5 -m100 -g10000 --max-chain-skip
.BR 25 ). .BR 25 ).
Similarly, the major difference from Similarly, the major difference from
.B ava-pb .B ava-pb
@@ -427,8 +463,8 @@ tag ignores introns to demote hits to pseudogenes.
.B sr .B sr
Short single-end reads without splicing Short single-end reads without splicing
.RB ( -k21 .RB ( -k21
.B -w11 --sr --frag -A2 -B8 -O12,32 -E2,1 -r50 -p.5 -N20 -f1000,5000 -n2 -m20 .B -w11 --sr --frag=yes -A2 -B8 -O12,32 -E2,1 -r50 -p.5 -N20 -f1000,5000 -n2 -m20
.B -s40 -g200 -2K50m .B -s40 -g200 -2K50m --heap-sort=yes
.BR --secondary=no ). .BR --secondary=no ).
.RE .RE
.SS Miscellaneous options .SS Miscellaneous options
@@ -522,8 +558,8 @@ where seed positions may be suboptimal. This should not be a big concern
because even the optimal alignment may be wrong in such regions. because even the optimal alignment may be wrong in such regions.
.TP .TP
* *
Minimap2 requires SSE2 instructions to compile. It is possible to add Minimap2 requires SSE2 or NEON instructions to compile. It is possible to add
non-SSE2 support, but it would make minimap2 slower by several times. non-SSE2/NEON 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).
+1 -1
View File
@@ -1,4 +1,4 @@
#include "minimap.h" #include "mmpriv.h"
int mm_verbose = 1; int mm_verbose = 1;
int mm_dbg_flag = 0; int mm_dbg_flag = 0;
+258
View File
@@ -0,0 +1,258 @@
/*******************************
* Command line option parsing *
*******************************/
var getopt = function(args, ostr) {
var oli; // option letter list index
if (typeof(getopt.place) == 'undefined')
getopt.ind = 0, getopt.arg = null, getopt.place = -1;
if (getopt.place == -1) { // update scanning pointer
if (getopt.ind >= args.length || args[getopt.ind].charAt(getopt.place = 0) != '-') {
getopt.place = -1;
return null;
}
if (getopt.place + 1 < args[getopt.ind].length && args[getopt.ind].charAt(++getopt.place) == '-') { // found "--"
++getopt.ind;
getopt.place = -1;
return null;
}
}
var optopt = args[getopt.ind].charAt(getopt.place++); // character checked for validity
if (optopt == ':' || (oli = ostr.indexOf(optopt)) < 0) {
if (optopt == '-') return null; // if the user didn't specify '-' as an option, assume it means null.
if (getopt.place < 0) ++getopt.ind;
return '?';
}
if (oli+1 >= ostr.length || ostr.charAt(++oli) != ':') { // don't need argument
getopt.arg = null;
if (getopt.place < 0 || getopt.place >= args[getopt.ind].length) ++getopt.ind, getopt.place = -1;
} else { // need an argument
if (getopt.place >= 0 && getopt.place < args[getopt.ind].length)
getopt.arg = args[getopt.ind].substr(getopt.place);
else if (args.length <= ++getopt.ind) { // no arg
getopt.place = -1;
if (ostr.length > 0 && ostr.charAt(0) == ':') return ':';
return '?';
} else getopt.arg = args[getopt.ind]; // white space
getopt.place = -1;
++getopt.ind;
}
return optopt;
}
/***********************
* Interval operations *
***********************/
Interval = {};
Interval.sort = function(a)
{
if (typeof a[0] == 'number')
a.sort(function(x, y) { return x - y });
else a.sort(function(x, y) { return x[0] != y[0]? x[0] - y[0] : x[1] - y[1] });
}
Interval.merge = function(a, sorted)
{
if (typeof sorted == 'undefined') sorted = true;
if (!sorted) Interval.sort(a);
var k = 0;
for (var i = 1; i < a.length; ++i) {
if (a[k][1] >= a[i][0])
a[k][1] = a[k][1] > a[i][1]? a[k][1] : a[i][1];
else a[++k] = a[i].slice(0);
}
a.length = k + 1;
}
Interval.dedup = function(a, sorted)
{
if (typeof sorted == 'undefined') sorted = true;
if (!sorted) Interval.sort(a);
var k = 0;
for (var i = 1; i < a.length; ++i)
if (a[k][0] != a[i][0] || a[k][1] != a[i][1])
a[++k] = a[i].slice(0);
a.length = k + 1;
}
Interval.index_end = function(a, sorted)
{
if (a.length == 0) return;
if (typeof sorted == 'undefined') sorted = true;
if (!sorted) Interval.sort(a);
a[0].push(0);
var k = 0, k_en = a[0][1];
for (var i = 1; i < a.length; ++i) {
if (k_en <= a[i][0]) {
for (++k; k < i; ++k)
if (a[k][1] > a[i][0])
break;
k_en = a[k][1];
}
a[i].push(k);
}
}
Interval.find_intv = function(a, x)
{
var left = -1, right = a.length;
if (typeof a[0] == 'number') {
while (right - left > 1) {
var mid = left + ((right - left) >> 1);
if (a[mid] > x) right = mid;
else if (a[mid] < x) left = mid;
else return mid;
}
} else {
while (right - left > 1) {
var mid = left + ((right - left) >> 1);
if (a[mid][0] > x) right = mid;
else if (a[mid][0] < x) left = mid;
else return mid;
}
}
return left;
}
Interval.find_ovlp = function(a, st, en)
{
if (a.length == 0 || st >= en) return [];
var l = Interval.find_intv(a, st);
var k = l < 0? 0 : a[l][a[l].length - 1];
var b = [];
for (var i = k; i < a.length; ++i) {
if (a[i][0] >= en) break;
else if (st < a[i][1])
b.push(a[i]);
}
return b;
}
/*****************
* Main function *
*****************/
function read_bed(fn, to_merge, to_dedup)
{
var file = new File(fn);
var buf = new Bytes();
var h = {};
while (file.readline(buf) >= 0) {
var t = buf.toString().split("\t");
if (h[t[0]] == null)
h[t[0]] = [];
var bst = parseInt(t[1]);
var ben = parseInt(t[2]);
if (t.length >= 12 && /^\d+$/.test(t[9])) {
t[9] = parseInt(t[9]);
var sz = t[10].split(",");
var st = t[11].split(",");
for (var i = 0; i < t[9]; ++i) {
st[i] = parseInt(st[i]);
sz[i] = parseInt(sz[i]);
h[t[0]].push([bst + st[i], bst + st[i] + sz[i], 0, 0, 0]);
}
} else {
h[t[0]].push([bst, ben, 0, 0, 0]);
}
}
buf.destroy();
file.close();
for (var chr in h) {
if (to_merge) Interval.merge(h[chr], false);
else if (to_dedup) Interval.dedup(h[chr], false);
else Interval.sort(h[chr]);
Interval.index_end(h[chr]);
}
return h;
}
function main(args)
{
var c, print_len = false, to_merge = true, to_dedup = false, fn_excl = null;
while ((c = getopt(args, "pde:")) != null) {
if (c == 'p') print_len = true;
else if (c == 'd') to_dedup = true, to_merge = false;
else if (c == 'e') fn_excl = getopt.arg;
}
if (args.length - getopt.ind < 2) {
print("Usage: k8 cnt-feat.js [options] <target.bed> <feature.bed>");
print("Options:");
print(" -e FILE exclude features overlapping regions in BED FILE []");
print(" -p print number of covered bases for each feature");
exit(1);
}
var excl = fn_excl != null? read_bed(fn_excl, true, false) : null;
var target = read_bed(args[getopt.ind], to_merge, to_dedup);
var file, buf = new Bytes();
var tot_len = 0, hit_len = 0;
file = args[getopt.ind+1] != '-'? new File(args[getopt.ind+1]) : new File();
while (file.readline(buf) >= 0) {
var t = buf.toString().split("\t");
var a = [];
var bst = parseInt(t[1]);
var ben = parseInt(t[2]);
if (t.length >= 12 && /^\d+$/.test(t[9])) { // BED12
t[9] = parseInt(t[9]);
var sz = t[10].split(",");
var st = t[11].split(",");
for (var i = 0; i < t[9]; ++i) {
st[i] = parseInt(st[i]);
sz[i] = parseInt(sz[i]);
a.push([bst + st[i], bst + st[i] + sz[i], false]);
}
} else a.push([bst, ben, false]); // 3-column BED
var feat_len = 0;
for (var i = 0; i < a.length; ++i) {
if (excl != null && excl[t[0]] != null) {
var oe = Interval.find_ovlp(excl[t[0]], a[i][0], a[i][1]);
if (oe.length > 0)
continue;
}
a[i][2] = true;
feat_len += a[i][1] - a[i][0];
}
tot_len += feat_len;
if (target[t[0]] == null) continue;
var b = [];
for (var i = 0; i < a.length; ++i) {
if (!a[i][2]) continue;
var o = Interval.find_ovlp(target[t[0]], a[i][0], a[i][1]);
for (var j = 0; j < o.length; ++j) {
var max_st = o[j][0] > a[i][0]? o[j][0] : a[i][0];
var min_en = o[j][1] < a[i][1]? o[j][1] : a[i][1];
b.push([max_st, min_en]);
o[j][2] += min_en - max_st;
++o[j][3];
if (max_st == o[j][0] && min_en == o[j][1])
++o[j][4];
}
}
// find the length covered
var feat_hit_len = 0;
if (b.length > 0) {
b.sort(function(a,b) {return a[0]-b[0]});
var st = b[0][0], en = b[0][1];
for (var i = 1; i < b.length; ++i) {
if (b[i][0] <= en) en = en > b[i][1]? en : b[i][1];
else feat_hit_len += en - st, st = b[i][0], en = b[i][1];
}
feat_hit_len += en - st;
}
hit_len += feat_hit_len;
if (print_len) print('F', t.slice(0, 4).join("\t"), feat_len, feat_hit_len);
}
file.close();
buf.destroy();
warn("# feature bases: " + tot_len);
warn("# feature bases overlapping targets: " + hit_len + ' (' + (100.0 * hit_len / tot_len).toFixed(2) + '%)');
}
main(arguments);
+150
View File
@@ -0,0 +1,150 @@
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, fn_ucsc_fai = null, is_short = false;
while ((c = getopt(arguments, "u:s")) != null) {
if (c == 'u') fn_ucsc_fai = getopt.arg;
else if (c == 's') is_short = true;
}
if (getopt.ind == arguments.length) {
print("Usage: k8 gff2bed.js [-u ucsc-genome.fa.fai] <in.gff>");
exit(1);
}
var ens2ucsc = {};
if (fn_ucsc_fai != null) {
var buf = new Bytes();
var file = new File(fn_ucsc_fai);
while (file.readline(buf) >= 0) {
var t = buf.toString().split("\t");
var s = t[0];
if (/_(random|alt|decoy)$/.test(s)) {
s = s.replace(/_(random|alt|decoy)$/, '');
s = s.replace(/^chr\S+_/, '');
} else {
s = s.replace(/^chrUn_/, '');
}
s = s.replace(/v(\d+)/, ".$1");
if (s != t[0]) ens2ucsc[s] = t[0];
}
file.close();
buf.destroy();
}
var colors = {
'protein_coding':'0,128,255',
'lincRNA':'0,192,0',
'snRNA':'0,192,0',
'miRNA':'0,192,0',
'misc_RNA':'0,192,0'
};
function print_bed12(exons, cds_st, cds_en, is_short)
{
if (exons.length == 0) return;
var name = is_short? exons[0][7] + "|" + exons[0][5] : exons[0].slice(4, 7).join("|");
var a = exons.sort(function(a,b) {return a[1]-b[1]});
var sizes = [], starts = [], st, en;
st = a[0][1];
en = a[a.length - 1][2];
if (cds_st == 1<<30) cds_st = st;
if (cds_en == 0) cds_en = en;
if (cds_st < st || cds_en > en)
throw Error("inconsistent thick start or end for transcript " + a[0][4]);
for (var i = 0; i < a.length; ++i) {
sizes.push(a[i][2] - a[i][1]);
starts.push(a[i][1] - st);
}
var color = colors[a[0][5]];
if (color == null) color = '196,196,196';
print(a[0][0], st, en, name, 1000, a[0][3], cds_st, cds_en, color, a.length, sizes.join(",") + ",", starts.join(",") + ",");
}
var re_gtf = /(transcript_id|transcript_type|transcript_biotype|gene_name|transcript_name) "([^"]+)";/g;
var re_gff3 = /(transcript_id|transcript_type|transcript_biotype|gene_name|transcript_name)=([^;]+)/g;
var buf = new Bytes();
var file = new File(arguments[getopt.ind]);
var exons = [], cds_st = 1<<30, cds_en = 0, last_id = null;
while (file.readline(buf) >= 0) {
var t = buf.toString().split("\t");
if (t[0].charAt(0) == '#') continue;
if (t[2] != "CDS" && t[2] != "exon") continue;
t[3] = parseInt(t[3]) - 1;
t[4] = parseInt(t[4]);
var id = null, type = "", gname = "N/A", biotype = "", m, tname = "N/A";
while ((m = re_gtf.exec(t[8])) != null) {
if (m[1] == "transcript_id") id = m[2];
else if (m[1] == "transcript_type") type = m[2];
else if (m[1] == "transcript_biotype") biotype = m[2];
else if (m[1] == "gene_name") name = m[2];
else if (m[1] == "transcript_name") tname = m[2];
}
while ((m = re_gff3.exec(t[8])) != null) {
if (m[1] == "transcript_id") id = m[2];
else if (m[1] == "transcript_type") type = m[2];
else if (m[1] == "transcript_biotype") biotype = m[2];
else if (m[1] == "gene_name") name = m[2];
else if (m[1] == "transcript_name") tname = m[2];
}
if (type == "" && biotype != "") type = biotype;
if (id == null) throw Error("No transcript_id");
if (id != last_id) {
print_bed12(exons, cds_st, cds_en, is_short);
exons = [], cds_st = 1<<30, cds_en = 0;
last_id = id;
}
if (t[2] == "CDS") {
cds_st = cds_st < t[3]? cds_st : t[3];
cds_en = cds_en > t[4]? cds_en : t[4];
} else if (t[2] == "exon") {
if (fn_ucsc_fai != null) {
if (ens2ucsc[t[0]] != null)
t[0] = ens2ucsc[t[0]];
else if (/^[A-Z]+\d+\.\d+$/.test(t[0]))
t[0] = t[0].replace(/([A-Z]+\d+)\.(\d+)/, "chrUn_$1v$2");
}
exons.push([t[0], t[3], t[4], t[6], id, type, name, tname]);
}
}
if (last_id != null)
print_bed12(exons, cds_st, cds_en, is_short);
file.close();
buf.destroy();
+105
View File
@@ -0,0 +1,105 @@
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, min_ovlp = 2000, min_frac = 0.95, min_mapq = 10;
while ((c = getopt(arguments, "q:l:f:")) != null) {
if (c == 'q') min_mapq = parseInt(getopt.arg);
else if (c == 'l') min_ovlp = parseInt(getopt.arg);
else if (c == 'f') min_frac = parseFloat(getopt.arg);
}
if (arguments.length - getopt.ind < 2) {
print("Usage: sort -k6,6 -k8,8n to-ref.paf | k8 ov-eval.js [options] - <ovlp.paf>");
print("Options:");
print(" -l INT min overlap length [2000]");
print(" -q INT min mapping quality [10]");
print(" -f FLOAT min fraction of mapped length [0.95]");
exit(1);
}
var buf = new Bytes();
var file = arguments[getopt.ind] == '-'? new File() : new File(arguments[getopt.ind]);
var a = [], h = {};
while (file.readline(buf) >= 0) {
var t = buf.toString().split("\t");
var is_pri = false;
if (parseInt(t[11]) < min_mapq) continue;
for (var i = 12; i < t.length; ++i)
if (t[i] == 'tp:A:P')
is_pri = true;
if (!is_pri) continue;
for (var i = 1; i <= 3; ++i)
t[i] = parseInt(t[i]);
for (var i = 6; i <= 8; ++i)
t[i] = parseInt(t[i]);
if (t[3] - t[2] < min_ovlp || t[8] - t[7] < min_ovlp || (t[3] - t[2]) / t[1] < min_frac)
continue;
var ctg = t[5], st = t[7], en = t[8];
while (a.length > 0) {
if (a[0][0] == ctg && a[0][2] > st)
break;
else a.shift();
}
for (var j = 0; j < a.length; ++j) {
if (a[j][3] == t[0]) continue;
var len = (en > a[j][2]? a[j][2] : en) - st;
if (len >= min_ovlp) {
var key = a[j][3] < t[0]? a[j][3] + "\t" + t[0] : t[0] + "\t" + a[j][3];
h[key] = len;
}
}
a.push([ctg, st, en, t[0]]);
}
file.close();
file = new File(arguments[getopt.ind + 1]);
while (file.readline(buf) >= 0) {
var t = buf.toString().split("\t");
var key = t[0] < t[5]? t[0] + "\t" + t[5] : t[5] + "\t" + t[0];
if (h[key] > 0) h[key] = -h[key];
}
file.close();
buf.destroy();
var n_ovlp = 0, n_missing = 0;
for (var key in h) {
++n_ovlp;
if (h[key] > 0) ++n_missing;
}
print(n_ovlp + " overlaps inferred from the reference mapping");
print(n_missing + " missed by the read overlapper");
print((100 * (1 - n_missing / n_ovlp)).toFixed(2) + "% sensitivity");
+4 -4
View File
@@ -40,9 +40,9 @@ var re_cs = /([:=*+-])(\d+|[A-Za-z]+)/g;
var c, min_cov_len = 10000, min_var_len = 50000, gap_thres = 50, min_mapq = 5; var c, min_cov_len = 10000, min_var_len = 50000, gap_thres = 50, min_mapq = 5;
while ((c = getopt(arguments, "l:L:g:q:")) != null) { while ((c = getopt(arguments, "l:L:g:q:")) != null) {
if (c == 'l') min_cov_len = parseInt(getopt.arg); if (c == 'l') min_cov_len = parseInt(getopt.arg);
else if (c == 'L') min_var_len = parseInt(optarg.arg); else if (c == 'L') min_var_len = parseInt(getopt.arg);
else if (c == 'g') gap_thres = parseInt(optarg.arg); else if (c == 'g') gap_thres = parseInt(getopt.arg);
else if (c == 'q') min_mapq = parseInt(optarg.arg); else if (c == 'q') min_mapq = parseInt(getopt.arg);
} }
if (arguments.length == getopt.ind) { if (arguments.length == getopt.ind) {
@@ -55,7 +55,7 @@ if (arguments.length == getopt.ind) {
exit(1); exit(1);
} }
var file = new File(arguments[getopt.ind]); var file = arguments[getopt.ind] == '-'? new File() : new File(arguments[getopt.ind]);
var buf = new Bytes(); var buf = new Bytes();
var tot_len = 0, n_sub = [0, 0, 0], n_ins = [0, 0, 0, 0], n_del = [0, 0, 0, 0]; var tot_len = 0, n_sub = [0, 0, 0], n_ins = [0, 0, 0, 0], n_del = [0, 0, 0, 0];
+25 -4
View File
@@ -60,15 +60,29 @@ function print_lines(a, fmt) {
function main(args) { function main(args) {
var re = /(\d+)([MIDNSH])/g; var re = /(\d+)([MIDNSH])/g;
var c, fmt = "bed"; var c, fmt = "bed", fn_name_conv = null;
while ((c = getopt(args, "f:")) != null) { while ((c = getopt(args, "f:n:")) != null) {
if (c == 'f') fmt = getopt.arg; if (c == 'f') fmt = getopt.arg;
else if (c == 'n') fn_name_conv = getopt.arg;
} }
if (getopt.ind == args.length) { if (getopt.ind == args.length) {
warn("Usage: k8 splice2bed.js <in.paf>"); warn("Usage: k8 splice2bed.js <in.paf>");
exit(1); exit(1);
} }
var conv = null;
if (fn_name_conv != null) {
conv = new Map();
var file = new File(fn_name_conv);
var buf = new Bytes();
while (file.readline(buf) >= 0) {
var t = buf.toString().split("\t");
conv.put(t[0], t[1]);
}
buf.destroy();
file.close();
}
var file = new File(args[getopt.ind]); var file = new File(args[getopt.ind]);
var buf = new Bytes(); var buf = new Bytes();
var a = []; var a = [];
@@ -77,11 +91,17 @@ function main(args) {
if (line.charAt(0) == '@') continue; // skip SAM header lines if (line.charAt(0) == '@') continue; // skip SAM header lines
var t = line.split("\t"); var t = line.split("\t");
var is_pri = false, cigar = null, a1; var is_pri = false, cigar = null, a1;
var qname = conv != null? conv.get(t[0]) : null;
if (qname != null) t[0] = qname;
if (t.length >= 10 && t[4] != '+' && t[4] != '-' && /^\d+/.test(t[1])) { // SAM
var flag = parseInt(t[1]);
if (flag&1) t[0] += '/' + (flag>>6&3);
}
if (a.length && a[0][3] != t[0]) { if (a.length && a[0][3] != t[0]) {
print_lines(a, fmt); print_lines(a, fmt);
a = []; a = [];
} }
if (t.length >= 12 && (t[4] == '+' || t[4] == '-')) { if (t.length >= 12 && (t[4] == '+' || t[4] == '-')) { // PAF
for (var i = 12; i < t.length; ++i) { for (var i = 12; i < t.length; ++i) {
if (t[i].substr(0, 5) == 'cg:Z:') { if (t[i].substr(0, 5) == 'cg:Z:') {
cigar = t[i].substr(5); cigar = t[i].substr(5);
@@ -90,7 +110,7 @@ function main(args) {
} }
} }
a1 = [t[5], t[7], t[8], t[0], Math.floor(t[9]/t[10]*1000), t[4]]; a1 = [t[5], t[7], t[8], t[0], Math.floor(t[9]/t[10]*1000), t[4]];
} else if (t.length >= 10) { } else if (t.length >= 10) { // SAM
var flag = parseInt(t[1]); var flag = parseInt(t[1]);
if ((flag&4) || a[2] == '*') continue; if ((flag&4) || a[2] == '*') continue;
cigar = t[5]; cigar = t[5];
@@ -122,6 +142,7 @@ function main(args) {
print_lines(a, fmt); print_lines(a, fmt);
buf.destroy(); buf.destroy();
file.close(); file.close();
if (conv != null) conv.destroy();
} }
main(arguments); main(arguments);
+1
View File
@@ -16,6 +16,7 @@
#define MM_SEED_LONG_JOIN (1ULL<<40) #define MM_SEED_LONG_JOIN (1ULL<<40)
#define MM_SEED_IGNORE (1ULL<<41) #define MM_SEED_IGNORE (1ULL<<41)
#define MM_SEED_TANDEM (1ULL<<42) #define MM_SEED_TANDEM (1ULL<<42)
#define MM_SEED_SELF (1ULL<<43)
#define MM_SEED_SEG_SHIFT 48 #define MM_SEED_SEG_SHIFT 48
#define MM_SEED_SEG_MASK (0xffULL<<(MM_SEED_SEG_SHIFT)) #define MM_SEED_SEG_MASK (0xffULL<<(MM_SEED_SEG_SHIFT))
+140
View File
@@ -0,0 +1,140 @@
#include <stdio.h>
#include "mmpriv.h"
void mm_mapopt_init(mm_mapopt_t *opt)
{
memset(opt, 0, sizeof(mm_mapopt_t));
opt->seed = 11;
opt->mid_occ_frac = 2e-4f;
opt->sdust_thres = 0; // no SDUST masking
opt->min_cnt = 3;
opt->min_chain_score = 40;
opt->bw = 500;
opt->max_gap = 5000;
opt->max_gap_ref = -1;
opt->max_chain_skip = 25;
opt->mask_level = 0.5f;
opt->pri_ratio = 0.8f;
opt->best_n = 5;
opt->max_join_long = 20000;
opt->max_join_short = 2000;
opt->min_join_flank_sc = 1000;
opt->a = 2, opt->b = 4, opt->q = 4, opt->e = 2, opt->q2 = 24, opt->e2 = 1;
opt->zdrop = 400;
opt->end_bonus = -1;
opt->min_dp_max = opt->min_chain_score * opt->a;
opt->min_ksw_len = 200;
opt->anchor_ext_len = 20, opt->anchor_ext_shift = 6;
opt->mini_batch_size = 500000000;
opt->pe_ori = 0; // FF
opt->pe_bonus = 33;
}
void mm_mapopt_update(mm_mapopt_t *opt, const mm_idx_t *mi)
{
if ((opt->flag & MM_F_SPLICE_FOR) && (opt->flag & MM_F_SPLICE_REV))
opt->flag |= MM_F_SPLICE;
if (opt->mid_occ <= 0)
opt->mid_occ = mm_idx_cal_max_occ(mi, opt->mid_occ_frac);
if (mm_verbose >= 3)
fprintf(stderr, "[M::%s::%.3f*%.2f] mid_occ = %d\n", __func__, realtime() - mm_realtime0, cputime() / (realtime() - mm_realtime0), opt->mid_occ);
}
void mm_mapopt_max_intron_len(mm_mapopt_t *opt, int max_intron_len)
{
if ((opt->flag & MM_F_SPLICE) && max_intron_len > 0)
opt->max_gap_ref = opt->bw = max_intron_len;
}
int mm_set_opt(const char *preset, mm_idxopt_t *io, mm_mapopt_t *mo)
{
if (preset == 0) {
mm_idxopt_init(io);
mm_mapopt_init(mo);
} else if (strcmp(preset, "ava-ont") == 0) {
io->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;
} 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 = 200;
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 = 200;
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 = 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;
} else return -1;
return 0;
}
int mm_check_opt(const mm_idxopt_t *io, const mm_mapopt_t *mo)
{
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;
}
return 0;
}
+8 -2
View File
@@ -81,10 +81,13 @@ This constructor accepts the following arguments:
.. code:: python .. code:: python
mappy.Aligner.map(seq) mappy.Aligner.map(seq, seq2=None)
This method aligns :code:`seq` against the index. It is a generator, *yielding* This method aligns :code:`seq` against the index. It is a generator, *yielding*
a series of :code:`mappy.Alignment` objects. a series of :code:`mappy.Alignment` objects. If :code:`seq2` is present, mappy
performs paired-end alignment, assuming the two ends are in the FR orientation.
Alignments of the two ends can be distinguished by the :code:`read_num` field
(see below).
Class mappy.Alignment Class mappy.Alignment
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
@@ -118,6 +121,9 @@ properties:
* **is_primary**: if the alignment is primary (typically the best and the first * **is_primary**: if the alignment is primary (typically the best and the first
to generate) 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_str**: CIGAR string
* **cigar**: CIGAR returned as an array of shape :code:`(n_cigar,2)`. The two * **cigar**: CIGAR returned as an array of shape :code:`(n_cigar,2)`. The two
+33
View File
@@ -15,6 +15,7 @@ typedef struct {
int32_t blen, mlen, NM, ctg_len; int32_t blen, mlen, NM, ctg_len;
uint8_t mapq, is_primary; uint8_t mapq, is_primary;
int8_t strand, trans_strand; int8_t strand, trans_strand;
int32_t seg_id;
int32_t n_cigar32; int32_t n_cigar32;
uint32_t *cigar32; uint32_t *cigar32;
} mm_hitpy_t; } mm_hitpy_t;
@@ -32,6 +33,7 @@ static inline void mm_reg2hitpy(const mm_idx_t *mi, mm_reg1_t *r, mm_hitpy_t *h)
h->NM = r->blen - r->mlen + r->p->n_ambi; h->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->trans_strand = r->p->trans_strand == 1? 1 : r->p->trans_strand == 2? -1 : 0;
h->is_primary = (r->id == r->parent); h->is_primary = (r->id == r->parent);
h->seg_id = r->seg_id;
h->n_cigar32 = r->p->n_cigar; h->n_cigar32 = r->p->n_cigar;
h->cigar32 = r->p->cigar; h->cigar32 = r->p->cigar;
} }
@@ -68,4 +70,35 @@ static inline void mm_reset_timer(void)
mm_realtime0 = realtime(); 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)
{
if (seq2 == 0) {
return 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]);
return regs[0];
}
}
#endif #endif
+2 -1
View File
@@ -79,7 +79,6 @@ cdef extern from "minimap.h":
mm_tbuf_t *mm_tbuf_init() mm_tbuf_t *mm_tbuf_init()
void mm_tbuf_destroy(mm_tbuf_t *b) void mm_tbuf_destroy(mm_tbuf_t *b)
mm_reg1_t *mm_map(const mm_idx_t *mi, int l_seq, const char *seq, int *n_regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *name)
# #
# Helper header (because it is hard to expose mm_reg1_t with Cython) # Helper header (because it is hard to expose mm_reg1_t with Cython)
@@ -92,11 +91,13 @@ cdef extern from "cmappy.h":
int32_t blen, mlen, NM, ctg_len int32_t blen, mlen, NM, ctg_len
uint8_t mapq, is_primary uint8_t mapq, is_primary
int8_t strand, trans_strand int8_t strand, trans_strand
int32_t seg_id
int32_t n_cigar32 int32_t n_cigar32
uint32_t *cigar32 uint32_t *cigar32
void mm_reg2hitpy(const mm_idx_t *mi, mm_reg1_t *r, mm_hitpy_t *h) void mm_reg2hitpy(const mm_idx_t *mi, mm_reg1_t *r, mm_hitpy_t *h)
void mm_free_reg1(mm_reg1_t *r) 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)
ctypedef struct kstring_t: ctypedef struct kstring_t:
unsigned l, m unsigned l, m
+10 -4
View File
@@ -10,9 +10,10 @@ cdef class Alignment:
cdef int _NM, _mlen, _blen cdef int _NM, _mlen, _blen
cdef int8_t _strand, _trans_strand cdef int8_t _strand, _trans_strand
cdef uint8_t _mapq, _is_primary cdef uint8_t _mapq, _is_primary
cdef int _seg_id
cdef _ctg, _cigar # these are python objects cdef _ctg, _cigar # these are python objects
def __cinit__(self, ctg, cl, cs, ce, strand, qs, qe, mapq, cigar, is_primary, mlen, blen, NM, trans_strand): def __cinit__(self, ctg, cl, cs, ce, strand, qs, qe, mapq, cigar, is_primary, mlen, blen, NM, trans_strand, seg_id):
self._ctg = ctg if isinstance(ctg, str) else ctg.decode() self._ctg = ctg if isinstance(ctg, str) else ctg.decode()
self._ctg_len, self._r_st, self._r_en = cl, cs, ce self._ctg_len, self._r_st, self._r_en = cl, cs, ce
self._strand, self._q_st, self._q_en = strand, qs, qe self._strand, self._q_st, self._q_en = strand, qs, qe
@@ -21,6 +22,7 @@ cdef class Alignment:
self._cigar = cigar self._cigar = cigar
self._is_primary = is_primary self._is_primary = is_primary
self._trans_strand = trans_strand self._trans_strand = trans_strand
self._seg_id = seg_id
@property @property
def ctg(self): return self._ctg def ctg(self): return self._ctg
@@ -64,6 +66,9 @@ cdef class Alignment:
@property @property
def cigar(self): return self._cigar def cigar(self): return self._cigar
@property
def read_num(self): return self._seg_id + 1
@property @property
def cigar_str(self): def cigar_str(self):
return "".join(map(lambda x: str(x[0]) + 'MIDNSH'[x[1]], self._cigar)) return "".join(map(lambda x: str(x[0]) + 'MIDNSH'[x[1]], self._cigar))
@@ -125,7 +130,7 @@ cdef class Aligner:
def __bool__(self): def __bool__(self):
return (self._idx != NULL) return (self._idx != NULL)
def map(self, seq, buf=None): def map(self, seq, seq2=None, buf=None):
cdef cmappy.mm_reg1_t *regs cdef cmappy.mm_reg1_t *regs
cdef cmappy.mm_hitpy_t h cdef cmappy.mm_hitpy_t h
cdef ThreadBuffer b cdef ThreadBuffer b
@@ -134,7 +139,8 @@ cdef class Aligner:
if self._idx is NULL: return None if self._idx is NULL: return None
if buf is None: b = ThreadBuffer() if buf is None: b = ThreadBuffer()
else: b = buf else: b = buf
regs = cmappy.mm_map(self._idx, len(seq), str.encode(seq), &n_regs, b._b, &self.map_opt, NULL) if seq2 is None: regs = cmappy.mm_map_aux(self._idx, str.encode(seq), NULL, &n_regs, b._b, &self.map_opt)
else: regs = cmappy.mm_map_aux(self._idx, str.encode(seq), str.encode(seq2), &n_regs, b._b, &self.map_opt)
for i in range(n_regs): for i in range(n_regs):
cmappy.mm_reg2hitpy(self._idx, &regs[i], &h) cmappy.mm_reg2hitpy(self._idx, &regs[i], &h)
@@ -142,7 +148,7 @@ cdef class Aligner:
for k in range(h.n_cigar32): for k in range(h.n_cigar32):
c = h.cigar32[k] c = h.cigar32[k]
cigar.append([c>>4, c&0xf]) cigar.append([c>>4, c&0xf])
yield Alignment(h.ctg, h.ctg_len, h.ctg_start, h.ctg_end, h.strand, h.qry_start, h.qry_end, h.mapq, cigar, h.is_primary, h.mlen, h.blen, h.NM, h.trans_strand) yield Alignment(h.ctg, h.ctg_len, h.ctg_start, h.ctg_end, h.strand, h.qry_start, h.qry_end, h.mapq, cigar, h.is_primary, h.mlen, h.blen, h.NM, h.trans_strand, h.seg_id)
cmappy.mm_free_reg1(&regs[i]) cmappy.mm_free_reg1(&regs[i])
free(regs) free(regs)
+3 -3
View File
@@ -23,7 +23,7 @@ def readme():
setup( setup(
name = 'mappy', name = 'mappy',
version = '2.6', version = '2.8',
url = 'https://github.com/lh3/minimap2', url = 'https://github.com/lh3/minimap2',
description = 'Minimap2 python binding', description = 'Minimap2 python binding',
long_description = readme(), long_description = readme(),
@@ -33,13 +33,13 @@ setup(
keywords = 'sequence-alignment', keywords = 'sequence-alignment',
scripts = ['python/minimap2.py'], scripts = ['python/minimap2.py'],
ext_modules = [Extension('mappy', ext_modules = [Extension('mappy',
sources = [module_src, 'align.c', 'bseq.c', 'chain.c', 'format.c', 'hit.c', 'index.c', 'pe.c', sources = [module_src, 'align.c', 'bseq.c', 'chain.c', 'format.c', 'hit.c', 'index.c', 'pe.c', 'options.c',
'ksw2_extd2_sse.c', 'ksw2_exts2_sse.c', 'ksw2_extz2_sse.c', 'ksw2_ll_sse.c', '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'], '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', 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', 'ksw2.h', 'kthread.h', 'kvec.h', 'mmpriv.h', 'sdust.h',
'python/cmappy.h', 'python/cmappy.pxd'], 'python/cmappy.h', 'python/cmappy.pxd'],
extra_compile_args = ['-msse4'], # WARNING: ancient x86_64 CPUs don't have SSE4 extra_compile_args = ['-DHAVE_KALLOC', '-msse4'], # WARNING: ancient x86_64 CPUs don't have SSE4
include_dirs = ['.'], include_dirs = ['.'],
libraries = ['z', 'm', 'pthread'])], libraries = ['z', 'm', 'pthread'])],
classifiers = [ classifiers = [
+10 -9
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,
@@ -113,26 +114,26 @@ void mm_sketch(void *km, const char *str, int len, int w, int k, uint32_t rid, i
} }
} else l = 0, tq.count = tq.front = 0, kmer_span = 0; } 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 && buf[j].y != UINT64_MAX) 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 && buf[j].y != UINT64_MAX) 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 && min.y != UINT64_MAX) 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 && buf[j].y != UINT64_MAX) 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)
if (min.x == buf[j].x && min.y != buf[j].y && buf[j].y != UINT64_MAX) 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]);
} }
} }
if (++buf_pos == w) buf_pos = 0; if (++buf_pos == w) buf_pos = 0;
+1689
View File
File diff suppressed because it is too large Load Diff
+17 -9
View File
@@ -268,18 +268,10 @@
Title = {Mason -- a read simulator for second generation sequencing data}, Title = {Mason -- a read simulator for second generation sequencing data},
Year = {2010}} Year = {2010}}
@article{Langmead:2012fk,
Author = {Langmead, Ben and Salzberg, Steven L},
Journal = {Nat Methods},
Pages = {357-9},
Title = {Fast gapped-read alignment with Bowtie 2},
Volume = {9},
Year = {2012}}
@article{Zaharia:2011aa, @article{Zaharia:2011aa,
Author = {Zaharia, Matei and others}, Author = {Zaharia, Matei and others},
Journal = {arXiv:1111:5572}, Journal = {arXiv:1111:5572},
Title = {Faster and More Accurate Sequence Alignment with SNAP}, Title = {Faster and More Accurate Sequence Alignment with {SNAP}},
Year = {2011}} Year = {2011}}
@article{Irimia:2008aa, @article{Irimia:2008aa,
@@ -305,3 +297,19 @@
Title = {Versatile and open software for comparing large genomes}, Title = {Versatile and open software for comparing large genomes},
Volume = {5}, Volume = {5},
Year = {2004}} 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}}
+65 -16
View File
@@ -13,7 +13,6 @@
\usepackage{natbib} \usepackage{natbib}
\bibliographystyle{apalike} \bibliographystyle{apalike}
\usepackage{hyperref}
\DeclareMathOperator*{\argmax}{argmax} \DeclareMathOperator*{\argmax}{argmax}
@@ -138,7 +137,7 @@ $h=50$; even if the heuristic fails, the optimal chain is often close.
\subsubsection{Backtracking} \subsubsection{Backtracking}
Let $P(i)$ be the index of the best predecessor of anchor $i$. It equals 0 if Let $P(i)$ be the index of the best predecessor of anchor $i$. It equals 0 if
$f(i)=w_i$ or $\argmax_j\{f(j)+\eta(j,i)-\gamma(j,i)\}$ otherwise. For each $f(i)=w_i$ or $\argmax_j\{f(j)+\alpha(j,i)-\beta(j,i)\}$ otherwise. For each
anchor $i$ in the descending order of $f(i)$, we apply $P(\cdot)$ repeatedly to 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 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 reach an already `used' $i$. This way we find all chains with no anchors used
@@ -157,6 +156,53 @@ 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) not choose a more sophisticated data structure (e.g. range tree or k-d tree)
because this step is not the performance bottleneck. because this step is not the performance bottleneck.
\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, 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 ONT reads, though.
\subsection{Aligning genomic DNA}\label{sec:genomic} \subsection{Aligning genomic DNA}\label{sec:genomic}
\subsubsection{Alignment with 2-piece affine gap cost} \subsubsection{Alignment with 2-piece affine gap cost}
@@ -397,7 +443,7 @@ consistent paired-end alignments.
\section{Results} \section{Results}
\subsection{Aligning long genomic reads} \subsection{Aligning long genomic reads}\label{sec:long-genomic}
\begin{figure}[!tb] \begin{figure}[!tb]
\centering \centering
@@ -500,14 +546,18 @@ more junctions with a higher percentage being exactly or approximately correct.
Minimap2 is over 40 times faster than GMAP and SpAln. While STAR is close to Minimap2 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. minimap2 in speed, it does not work well with noisy reads.
We have also evaluated spliced aligners on public Iso-Seq data (human Alzheimer We have also evaluated spliced aligners on a human Nanopore Direct RNA-seq
brain from \href{http://bit.ly/isoseqpub}{http://bit.ly/isoseqpub}). The dataset (\href{http://bit.ly/na12878ont}{http://bit.ly/na12878ont}). Minimap2
observation is similar: minimap2 is faster at higher junction accuracy. aligned 10 million reads in $<$1 wall-clock hour using 16 CPU cores. 94.2\% of
On a private Nanopore Direct RNA data set with $\sim$17\% sequencing error rate aligned splice junctions consistent with gene annotations. In comparison,
(N. Loman, personal communication), minimap2 aligned 96\,467 introns GMAP under option `-k 14 -n 0 --min-intronlength 30 --cross-species' is 160
from 37\,068 mapped reads with 95.4\% of them consistent with human gene times slower; 68.7\% of GMAP junctions are found in known gene annotations. The
annotations. In comparison, only 74.8\% of GMAP introns found in known gene percentage increases to 84.1\% if an aligned junction within 10bp from an
annotations. 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 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 showing the best setting we have experimented, but their developers should be
@@ -551,8 +601,7 @@ 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 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 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 to high quality. This allowed us to construct an independent truth variant
data set dataset~\citep{Li223297} for
(\href{https://github.com/lh3/CHM-eval}{https://github.com/lh3/CHM-eval}) for
ERR1341796. In this evaluation, minimap2 has higher SNP false negative rate ERR1341796. In this evaluation, minimap2 has higher SNP false negative rate
(FNR; 2.5\% of minimap2 vs 2.2\% of BWA-MEM), but fewer false positive SNPs per (FNR; 2.5\% of minimap2 vs 2.2\% of BWA-MEM), but fewer false positive SNPs per
million bases (FPPM; 3.0 vs 3.9), lower 2--50bp INDEL FNR (7.3\% vs 7.5\%) and million bases (FPPM; 3.0 vs 3.9), lower 2--50bp INDEL FNR (7.3\% vs 7.5\%) and
@@ -597,7 +646,7 @@ uniqueness and reduce unsuccessful extensions. Minimap2 indexes reference
k-mers with a hash table instead. Such fixed-length seeds are inferior to 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 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 practice. When a query sequence has multiple seed hits, we can afford to skip
some highly repetitive seeds without affecting the final accuracy. This further highly repetitive seeds without affecting the final accuracy. This further
alleviates the concern with the uniqueness of seeds. Hash table is the ideal alleviates the concern with the uniqueness of seeds. Hash table is the ideal
data structure for mapping long query sequences. data structure for mapping long query sequences.
@@ -605,8 +654,8 @@ data structure for mapping long query sequences.
We owe a debt of gratitude to H. Suzuki and M. Kasahara for releasing their 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. masterpiece and insightful notes before formal publication. We thank M.
Schatz, P. Rescheneder and F. Sedlazeck for pointing out the limitation of Schatz, P. Rescheneder and F. Sedlazeck for pointing out the limitation of
BWA-MEM. We are also grateful to early minimap2 testers who have greatly helped BWA-MEM. We are also grateful to minimap2 users who have greatly helped to
to suggest features and to fix various issues. suggest features and to fix various issues.
\bibliography{minimap2} \bibliography{minimap2}