minibwa 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +119 -0
- data/ext/minibwa/extconf.rb +105 -0
- data/ext/minibwa/mb_buffer.c +113 -0
- data/ext/minibwa/mb_hit.c +192 -0
- data/ext/minibwa/mb_index.c +462 -0
- data/ext/minibwa/mb_index_build.c +174 -0
- data/ext/minibwa/mb_options.c +301 -0
- data/ext/minibwa/minibwa/LICENSE.txt +37 -0
- data/ext/minibwa/minibwa/align.c +930 -0
- data/ext/minibwa/minibwa/bseq.h +45 -0
- data/ext/minibwa/minibwa/bwt.c +715 -0
- data/ext/minibwa/minibwa/bwt.h +86 -0
- data/ext/minibwa/minibwa/cs.c +161 -0
- data/ext/minibwa/minibwa/format.c +356 -0
- data/ext/minibwa/minibwa/index.c +342 -0
- data/ext/minibwa/minibwa/kalloc.c +224 -0
- data/ext/minibwa/minibwa/kalloc.h +54 -0
- data/ext/minibwa/minibwa/ketopt.h +123 -0
- data/ext/minibwa/minibwa/kommon.c +374 -0
- data/ext/minibwa/minibwa/kommon.h +85 -0
- data/ext/minibwa/minibwa/kseq.h +256 -0
- data/ext/minibwa/minibwa/ksort.h +163 -0
- data/ext/minibwa/minibwa/ksw2.h +220 -0
- data/ext/minibwa/minibwa/ksw2_extd2_sse.c +403 -0
- data/ext/minibwa/minibwa/ksw2_extz2_sse.c +296 -0
- data/ext/minibwa/minibwa/ksw2_ll_sse.c +341 -0
- data/ext/minibwa/minibwa/kthread.h +15 -0
- data/ext/minibwa/minibwa/l2bit.c +479 -0
- data/ext/minibwa/minibwa/l2bit.h +72 -0
- data/ext/minibwa/minibwa/lchain.c +231 -0
- data/ext/minibwa/minibwa/libsais.c +6985 -0
- data/ext/minibwa/minibwa/libsais.h +106 -0
- data/ext/minibwa/minibwa/libsais64.c +7064 -0
- data/ext/minibwa/minibwa/libsais64.h +81 -0
- data/ext/minibwa/minibwa/map-algo.c +769 -0
- data/ext/minibwa/minibwa/mbpriv.h +148 -0
- data/ext/minibwa/minibwa/minibwa.h +176 -0
- data/ext/minibwa/minibwa/options.c +116 -0
- data/ext/minibwa/minibwa/pe.c +559 -0
- data/ext/minibwa/minibwa/s2n-lite.h +59 -0
- data/ext/minibwa/minibwa/seed.c +354 -0
- data/ext/minibwa/minibwa.c +67 -0
- data/ext/minibwa/minibwa.h +51 -0
- data/lib/minibwa/hit.rb +110 -0
- data/lib/minibwa/index.rb +77 -0
- data/lib/minibwa/options.rb +235 -0
- data/lib/minibwa/sam.rb +85 -0
- data/lib/minibwa/version.rb +6 -0
- data/lib/minibwa.rb +11 -0
- metadata +88 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
#include <string.h>
|
|
2
|
+
#include <assert.h>
|
|
3
|
+
#include <stdio.h>
|
|
4
|
+
#include "mbpriv.h"
|
|
5
|
+
#include "kalloc.h"
|
|
6
|
+
#include "ksort.h"
|
|
7
|
+
|
|
8
|
+
#define key_sai0(a) ((a).x[0])
|
|
9
|
+
KRADIX_SORT_INIT(mb_sai0, mb_sai_t, key_sai0, 8)
|
|
10
|
+
|
|
11
|
+
#define key_sais(a) ((a).size)
|
|
12
|
+
KRADIX_SORT_INIT(mb_sais, mb_sai_t, key_sais, 8)
|
|
13
|
+
|
|
14
|
+
#define key_saii(a) ((a).info)
|
|
15
|
+
KRADIX_SORT_INIT(mb_saii, mb_sai_t, key_saii, 8)
|
|
16
|
+
|
|
17
|
+
#define key_anchor(a) ((a).tpos)
|
|
18
|
+
KRADIX_SORT_INIT(mb_anchor, mb_anchor_t, key_anchor, 8)
|
|
19
|
+
|
|
20
|
+
/***********
|
|
21
|
+
* Seeding *
|
|
22
|
+
***********/
|
|
23
|
+
|
|
24
|
+
void mb_seed_intv(void *km, const mb_bwt_t *bwt, int32_t len, const uint8_t *seq, int32_t min_len, int32_t max_sub_occ, mb_sai_v *v)
|
|
25
|
+
{
|
|
26
|
+
int64_t x = 0, i, n_a0;
|
|
27
|
+
mb_sai_t p;
|
|
28
|
+
|
|
29
|
+
v->n = 0;
|
|
30
|
+
do { // pass 1: standard SMEMs
|
|
31
|
+
x = mb_bwt_smem(bwt, len, seq, x, min_len, 1, &p);
|
|
32
|
+
if (p.size > 0) {
|
|
33
|
+
Kgrow(km, mb_sai_t, v->a, v->n, v->m);
|
|
34
|
+
v->a[v->n++] = p;
|
|
35
|
+
}
|
|
36
|
+
} while (x < len);
|
|
37
|
+
|
|
38
|
+
n_a0 = v->n;
|
|
39
|
+
for (i = 0; i < n_a0; ++i) { // pass 2: sub-SMEMs
|
|
40
|
+
int32_t sub_min_len;
|
|
41
|
+
uint32_t st = v->a[i].info>>32, en = (uint32_t)v->a[i].info;
|
|
42
|
+
if (en - st < min_len * 2 || v->a[i].size > max_sub_occ)
|
|
43
|
+
continue;
|
|
44
|
+
x = st;
|
|
45
|
+
sub_min_len = (en - st) / 2 > min_len? (en - st) / 2 : min_len;
|
|
46
|
+
do { // if two SMEMs have large overlaps, we may find the same sub intervals in both. A rare case not worth optimizing
|
|
47
|
+
x = mb_bwt_smem(bwt, en, seq, x, sub_min_len, v->a[i].size + 1, &p);
|
|
48
|
+
if (p.size > v->a[i].size) {
|
|
49
|
+
Kgrow(km, mb_sai_t, v->a, v->n, v->m);
|
|
50
|
+
v->a[v->n++] = p;
|
|
51
|
+
}
|
|
52
|
+
} while (x < en);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
void mb_seed_intv_batch(void *km, const mb_bwt_t *bwt, int32_t n_seq, const int32_t *len, uint8_t *const* seq, int32_t min_len, int32_t max_sub_occ, mb_sai_v *v)
|
|
57
|
+
{ // identical to mb_seed_intv() though the order of intervals is often different
|
|
58
|
+
const int max_batch_size = 50;
|
|
59
|
+
mb_smem_entry_t *s;
|
|
60
|
+
int32_t i, j, n_s, *nv;
|
|
61
|
+
|
|
62
|
+
// first pass: standard SMEMs
|
|
63
|
+
s = Kcalloc(km, mb_smem_entry_t, max_batch_size);
|
|
64
|
+
nv = Kcalloc(km, int32_t, n_seq);
|
|
65
|
+
for (i = 0; i < n_seq; ++i) v[i].n = 0;
|
|
66
|
+
for (i = 0; i < n_seq; i += max_batch_size) {
|
|
67
|
+
int32_t en = i + max_batch_size < n_seq? i + max_batch_size : n_seq;
|
|
68
|
+
for (j = i; j < en; ++j) {
|
|
69
|
+
mb_smem_entry_t *t = &s[j - i];
|
|
70
|
+
t->min_len = min_len;
|
|
71
|
+
t->min_occ = 1;
|
|
72
|
+
t->st = 0, t->en = len[j];
|
|
73
|
+
t->q = seq[j];
|
|
74
|
+
t->v = &v[j];
|
|
75
|
+
}
|
|
76
|
+
mb_bwt_smem_batch(km, bwt, en - i, s);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// second pass; sub-SMEMs
|
|
80
|
+
for (i = 0; i < n_seq; ++i) nv[i] = v[i].n;
|
|
81
|
+
for (i = n_s = 0; i < n_seq; ++i) {
|
|
82
|
+
for (j = 0; j < nv[i]; ++j) {
|
|
83
|
+
mb_smem_entry_t *t;
|
|
84
|
+
uint32_t st = v[i].a[j].info>>32, en = (uint32_t)v[i].a[j].info;
|
|
85
|
+
if (en - st < min_len * 2 || v[i].a[j].size > max_sub_occ)
|
|
86
|
+
continue;
|
|
87
|
+
t = &s[n_s++];
|
|
88
|
+
t->min_len = (en - st) / 2 > min_len? (en - st) / 2 : min_len;
|
|
89
|
+
t->min_occ = v[i].a[j].size + 1;
|
|
90
|
+
t->st = st, t->en = en;
|
|
91
|
+
t->q = seq[i];
|
|
92
|
+
t->v = &v[i];
|
|
93
|
+
if (n_s == max_batch_size) {
|
|
94
|
+
mb_bwt_smem_batch(km, bwt, n_s, s);
|
|
95
|
+
n_s = 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (n_s > 0)
|
|
100
|
+
mb_bwt_smem_batch(km, bwt, n_s, s);
|
|
101
|
+
kfree(km, nv);
|
|
102
|
+
kfree(km, s);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/*****************************
|
|
106
|
+
* Seed/anchor deduplication *
|
|
107
|
+
*****************************/
|
|
108
|
+
|
|
109
|
+
static void mb_seed_sort_dedup(mb_sai_v *u)
|
|
110
|
+
{
|
|
111
|
+
int64_t i, i0, j;
|
|
112
|
+
if (u->n <= 1) return;
|
|
113
|
+
// sort by ::{x[0],size,info}
|
|
114
|
+
radix_sort_mb_sai0(u->a, u->a + u->n); // sort by ::x[0]
|
|
115
|
+
for (i = 1, i0 = 0; i <= u->n; ++i) {
|
|
116
|
+
if (i == u->n || u->a[i].x[0] != u->a[i0].x[0]) {
|
|
117
|
+
if (i - i0 > 1) {
|
|
118
|
+
int64_t k, k0, n1 = i - i0;
|
|
119
|
+
mb_sai_t *a1 = &u->a[i0];
|
|
120
|
+
radix_sort_mb_sais(&u->a[i0], &u->a[i]); // sort by ::size
|
|
121
|
+
kom_reverse(mb_sai_t, n1, a1);
|
|
122
|
+
for (k = i0 + 1, k0 = i0; k <= i; ++k) {
|
|
123
|
+
if (k == i || u->a[k0].size != u->a[k].size) {
|
|
124
|
+
if (k - k0 > 1)
|
|
125
|
+
radix_sort_mb_saii(&u->a[k0], &u->a[k]); // sort by ::info
|
|
126
|
+
k0 = k;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
i0 = i;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// dedup
|
|
134
|
+
for (i = 1, j = 0; i < u->n; ++i)
|
|
135
|
+
if (!(u->a[i].x[0] == u->a[j].x[0] && u->a[i].size == u->a[j].size && u->a[i].info == u->a[j].info))
|
|
136
|
+
u->a[++j] = u->a[i];
|
|
137
|
+
u->n = j + 1;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Remove duplicated anchors. The two-round seeding algorithm may lead an
|
|
141
|
+
* anchor precisely contained in a longer anchor. This routine filters out the
|
|
142
|
+
* shorter anchor. This wouldn't happen to minimap2. */
|
|
143
|
+
static void mb_anchor_dedup(mb_anchor_v *v) // NB: assuming sorted by tpos
|
|
144
|
+
{
|
|
145
|
+
const int max_back = 100; // to avoid quadratic behavior in the worst case
|
|
146
|
+
int64_t i, j, k;
|
|
147
|
+
for (i = 1; i < v->n; ++i) {
|
|
148
|
+
mb_anchor_t *ai = &v->a[i];
|
|
149
|
+
int64_t tsj, tsi = ai->tpos + 1 - ai->len;
|
|
150
|
+
int32_t qsj, qsi = ai->qpos + 1 - ai->len;
|
|
151
|
+
for (j = i - 1, k = 0; j >= 0 && k < max_back; --j, ++k) {
|
|
152
|
+
mb_anchor_t *aj = &v->a[j];
|
|
153
|
+
if (aj->sid != ai->sid) break;
|
|
154
|
+
if (aj->tpos < tsi) break;
|
|
155
|
+
tsj = aj->tpos + 1 - aj->len;
|
|
156
|
+
qsj = aj->qpos + 1 - aj->len;
|
|
157
|
+
if (tsj >= tsi) { // then j is contained in i
|
|
158
|
+
if (tsj - tsi == qsj - qsi) aj->flt = 1;
|
|
159
|
+
} else if (ai->tpos == aj->tpos) { // then i is contained in j
|
|
160
|
+
if (ai->qpos == aj->qpos) ai->flt = 1;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
for (i = j = 0; i < v->n; ++i)
|
|
165
|
+
if (!v->a[i].flt) v->a[j++] = v->a[i];
|
|
166
|
+
v->n = j;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/************************
|
|
170
|
+
* Get contig positions *
|
|
171
|
+
************************/
|
|
172
|
+
|
|
173
|
+
typedef struct { int64_t st, en; } anchor_aux_t;
|
|
174
|
+
typedef struct { int64_t a, i; } sa_aux_t;
|
|
175
|
+
|
|
176
|
+
static void process_batch(void *km, const mb_idx_t *idx, const anchor_aux_t *aux, int32_t m, const sa_aux_t *b, uint64_t *a, int32_t qlen, l2b_meth_t mt, const mb_sai_v *u, mb_anchor_v *v)
|
|
177
|
+
{
|
|
178
|
+
int64_t j, k;
|
|
179
|
+
for (k = 0; k < m; ++k) a[k] = b[k].a;
|
|
180
|
+
mb_bwt_sa_batch(km, idx->bwt, m, a);
|
|
181
|
+
for (k = 0; k < m; ++k) {
|
|
182
|
+
const anchor_aux_t *p = &aux[b[k].i];
|
|
183
|
+
for (j = p->st; j < p->en; ++j) {
|
|
184
|
+
int32_t qs = u->a[j].info>>32, qe = (int32_t)u->a[j].info;
|
|
185
|
+
int32_t rev, len = qe - qs;
|
|
186
|
+
int64_t tid, cst;
|
|
187
|
+
l2b_meth_t mt_anchor;
|
|
188
|
+
const l2b_ctg_t *ctg;
|
|
189
|
+
mb_anchor_t *q;
|
|
190
|
+
if (mt != L2B_METH_NONE) {
|
|
191
|
+
tid = l2b_intv2cid_meth(idx->l2b, a[k], a[k] + len, &mt_anchor, &cst, &rev);
|
|
192
|
+
if (tid < 0) continue;
|
|
193
|
+
// R1(C2T): keep c2t_f(copy0) and g2a_r(copy2); R2(G2A): keep g2a_f(copy1) and c2t_r(copy3)
|
|
194
|
+
if ((mt_anchor == mt) != (rev == 0)) continue; // filter
|
|
195
|
+
} else {
|
|
196
|
+
tid = l2b_intv2cid(idx->l2b, a[k], a[k] + len, &cst, &rev);
|
|
197
|
+
if (tid < 0) continue;
|
|
198
|
+
}
|
|
199
|
+
rev = !!rev;
|
|
200
|
+
ctg = &idx->l2b->ctg[tid];
|
|
201
|
+
Kgrow(km, mb_anchor_t, v->a, v->n, v->m);
|
|
202
|
+
q = &v->a[v->n++];
|
|
203
|
+
memset(q, 0, sizeof(*q));
|
|
204
|
+
q->sid = tid << 1 | rev;
|
|
205
|
+
q->len = len;
|
|
206
|
+
q->qpos = rev? qlen - 1 - qs : qs + len - 1;
|
|
207
|
+
q->tpos = ctg->off * 2 + ctg->len * rev + cst + len - 1; // for sorting; will be adjusted later
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
static void mb_anchor_split_meth(void *km, const l2b_t *l2b, int32_t min_len, int32_t qlen, const uint8_t *qseq0, l2b_meth_t mt0, mb_anchor_v *v)
|
|
213
|
+
{
|
|
214
|
+
int64_t i, m_a = v->n * 2, n_a = 0;
|
|
215
|
+
int32_t max_len = 0;
|
|
216
|
+
mb_anchor_t *a;
|
|
217
|
+
uint8_t *tseq, *qseq2[2];
|
|
218
|
+
|
|
219
|
+
for (i = 0; i < v->n; ++i)
|
|
220
|
+
if (max_len < v->a[i].len)
|
|
221
|
+
max_len = v->a[i].len;
|
|
222
|
+
tseq = Kmalloc(km, uint8_t, max_len + qlen * 2);
|
|
223
|
+
qseq2[0] = tseq + max_len;
|
|
224
|
+
qseq2[1] = qseq2[0] + qlen;
|
|
225
|
+
memcpy(qseq2[0], qseq0, qlen);
|
|
226
|
+
for (i = 0; i < qlen; ++i)
|
|
227
|
+
qseq2[1][qlen - 1 - i] = qseq0[i] > 3? 4 : 3 - qseq0[i];
|
|
228
|
+
|
|
229
|
+
a = Kmalloc(km, mb_anchor_t, m_a);
|
|
230
|
+
for (i = 0; i < v->n; ++i) {
|
|
231
|
+
mb_anchor_t *p, *q = &v->a[i];
|
|
232
|
+
const l2b_ctg_t *ctg = &l2b->ctg[q->sid>>1];
|
|
233
|
+
int32_t rev = q->sid&1;
|
|
234
|
+
int64_t tpos = q->tpos - (ctg->off * 2 + ctg->len * rev); // NB: requiring concatenated ::tpos!!
|
|
235
|
+
int64_t ts = tpos + 1 - q->len;
|
|
236
|
+
int32_t qs = q->qpos + 1 - q->len, j, j0;
|
|
237
|
+
uint8_t t_allow, q_allow;
|
|
238
|
+
l2b_meth_t mt;
|
|
239
|
+
const uint8_t *qseq = qseq2[rev] + qs;
|
|
240
|
+
l2b_getseq(l2b, q->sid>>1, ts, ts + q->len, tseq);
|
|
241
|
+
mt = q->sid&1? l2b_meth_rev(mt0) : mt0;
|
|
242
|
+
t_allow = mt == L2B_METH_C2T? 1 : 2;
|
|
243
|
+
q_allow = mt == L2B_METH_C2T? 3 : 0;
|
|
244
|
+
for (j0 = j = 0; j <= q->len; ++j) {
|
|
245
|
+
if (j == q->len || tseq[j] == 4 || qseq[j] == 4 || (tseq[j] != qseq[j] && !(tseq[j] == t_allow && qseq[j] == q_allow))) {
|
|
246
|
+
if (j - j0 >= min_len) {
|
|
247
|
+
Kgrow(km, mb_anchor_t, a, n_a, m_a);
|
|
248
|
+
p = &a[n_a++];
|
|
249
|
+
*p = *q;
|
|
250
|
+
p->len = j - j0;
|
|
251
|
+
p->qpos = q->qpos - (q->len - j);
|
|
252
|
+
p->tpos = q->tpos - (q->len - j);
|
|
253
|
+
}
|
|
254
|
+
j0 = j + 1;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
kfree(km, tseq);
|
|
259
|
+
Kgrow(km, mb_anchor_t, v->a, n_a, v->m);
|
|
260
|
+
memcpy(v->a, a, n_a * sizeof(mb_anchor_t));
|
|
261
|
+
v->n = n_a;
|
|
262
|
+
kfree(km, a);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* Converting seed intervals to anchors. This function batches small SA
|
|
266
|
+
* intervals and calls mb_bwt_sa_batch() in process_batch(). With prefetch, the
|
|
267
|
+
* strategy noticeably improves the performance. */
|
|
268
|
+
double mb_anchor(void *km, const mb_idx_t *idx, mb_sai_v *u, int32_t min_len, int32_t qlen, const uint8_t *qseq, l2b_meth_t mt, int32_t max_occ, mb_anchor_v *v)
|
|
269
|
+
{
|
|
270
|
+
const int batch_size = 20;
|
|
271
|
+
int32_t n_aux, m, m_a;
|
|
272
|
+
int64_t i, i0, j, k;
|
|
273
|
+
uint64_t *a;
|
|
274
|
+
double seed_ratio = 1.0;
|
|
275
|
+
sa_aux_t *b;
|
|
276
|
+
anchor_aux_t *aux;
|
|
277
|
+
|
|
278
|
+
v->n = 0;
|
|
279
|
+
if (u->n == 0) return seed_ratio; // no anchors
|
|
280
|
+
mb_seed_sort_dedup(u);
|
|
281
|
+
|
|
282
|
+
for (i = 0, k = 0; i < u->n; ++i) // pre-calculate the size of v->a
|
|
283
|
+
k += u->a[i].size < max_occ? u->a[i].size : max_occ;
|
|
284
|
+
Kgrow(km, mb_anchor_t, v->a, k - 1, v->m); // preallocate
|
|
285
|
+
|
|
286
|
+
for (i = 1, i0 = 0, n_aux = 0; i <= u->n; ++i) // pre-compute n_aux
|
|
287
|
+
if (i == u->n || u->a[i].x[0] != u->a[i0].x[0] || u->a[i].size != u->a[i0].size)
|
|
288
|
+
++n_aux, i0 = i;
|
|
289
|
+
aux = Kmalloc(km, anchor_aux_t, n_aux);
|
|
290
|
+
for (i = 1, i0 = 0, n_aux = 0; i <= u->n; ++i) // populate aux[]
|
|
291
|
+
if (i == u->n || u->a[i].x[0] != u->a[i0].x[0] || u->a[i].size != u->a[i0].size)
|
|
292
|
+
aux[n_aux].st = i0, aux[n_aux++].en = i, i0 = i;
|
|
293
|
+
|
|
294
|
+
m_a = max_occ > batch_size? max_occ : batch_size; // max size of a[] and b[]
|
|
295
|
+
a = Kmalloc(km, uint64_t, m_a);
|
|
296
|
+
b = Kmalloc(km, sa_aux_t, m_a);
|
|
297
|
+
for (i = 0, m = 0; i < n_aux; ++i) {
|
|
298
|
+
const anchor_aux_t *p = &aux[i];
|
|
299
|
+
const mb_sai_t *q = &u->a[p->st];
|
|
300
|
+
if (q->size + m > batch_size) {
|
|
301
|
+
process_batch(km, idx, aux, m, b, a, qlen, mt, u, v);
|
|
302
|
+
m = 0;
|
|
303
|
+
}
|
|
304
|
+
if (q->size <= max_occ) { // get SA for all of them
|
|
305
|
+
for (j = 0; j < q->size; ++j)
|
|
306
|
+
b[m].a = q->x[0] + j, b[m++].i = i;
|
|
307
|
+
} else { // sample up to max_occ
|
|
308
|
+
int32_t n = 0;
|
|
309
|
+
for (j = 0; j < q->size && n < max_occ; ++n) {
|
|
310
|
+
int32_t step = (q->size - j) / (max_occ - n);
|
|
311
|
+
if (step < 1) step = 1;
|
|
312
|
+
b[m].a = q->x[0] + j, b[m++].i = i;
|
|
313
|
+
j += step;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
assert(m <= m_a); // shouldn't happen!
|
|
317
|
+
}
|
|
318
|
+
process_batch(km, idx, aux, m, b, a, qlen, mt, u, v);
|
|
319
|
+
kfree(km, b);
|
|
320
|
+
kfree(km, a);
|
|
321
|
+
kfree(km, aux);
|
|
322
|
+
|
|
323
|
+
if (mt != L2B_METH_NONE && v->n > 0) {
|
|
324
|
+
int64_t t0, t1;
|
|
325
|
+
for (i = 0, t0 = 0; i < v->n; ++i) t0 += v->a[i].len;
|
|
326
|
+
mb_anchor_split_meth(km, idx->l2b, min_len, qlen, qseq, mt, v);
|
|
327
|
+
for (i = 0, t1 = 0; i < v->n; ++i) t1 += v->a[i].len;
|
|
328
|
+
seed_ratio = (double)t1 / t0;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
radix_sort_mb_anchor(v->a, v->a + v->n);
|
|
332
|
+
for (i = 0; i < v->n; ++i) { // adjust mb_anchor_t::tpos
|
|
333
|
+
mb_anchor_t *q = &v->a[i];
|
|
334
|
+
const l2b_ctg_t *ctg = &idx->l2b->ctg[q->sid>>1];
|
|
335
|
+
q->tpos -= ctg->off * 2 + ctg->len * (q->sid&1);
|
|
336
|
+
}
|
|
337
|
+
mb_anchor_dedup(v);
|
|
338
|
+
return seed_ratio;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
void mb_anchor_sort(const l2b_t *l2b, int64_t n_a, mb_anchor_t *a)
|
|
342
|
+
{
|
|
343
|
+
int64_t i;
|
|
344
|
+
if (n_a <= 1) return;
|
|
345
|
+
for (i = 0; i < n_a; ++i) {
|
|
346
|
+
const l2b_ctg_t *ctg = &l2b->ctg[a[i].sid>>1];
|
|
347
|
+
a[i].tpos += ctg->off * 2 + ctg->len * (a[i].sid&1);
|
|
348
|
+
}
|
|
349
|
+
radix_sort_mb_anchor(a, a + n_a);
|
|
350
|
+
for (i = 0; i < n_a; ++i) {
|
|
351
|
+
const l2b_ctg_t *ctg = &l2b->ctg[a[i].sid>>1];
|
|
352
|
+
a[i].tpos -= ctg->off * 2 + ctg->len * (a[i].sid&1);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#include "minibwa.h"
|
|
2
|
+
|
|
3
|
+
VALUE rb_mMinibwa;
|
|
4
|
+
VALUE rb_cMinibwaOptions;
|
|
5
|
+
VALUE rb_cMinibwaIndex;
|
|
6
|
+
VALUE rb_cMinibwaBuffer;
|
|
7
|
+
VALUE rb_cMinibwaHit;
|
|
8
|
+
VALUE rb_eMinibwaError;
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* Document-module: Minibwa
|
|
12
|
+
*
|
|
13
|
+
* Ruby bindings for minibwa, a short-read aligner.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* Document-class: Minibwa::Error
|
|
18
|
+
*
|
|
19
|
+
* Raised when minibwa cannot build, load, or use an index.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/* ------------------------------------------------------------------ */
|
|
23
|
+
/* TypedData unwrapping helpers */
|
|
24
|
+
/* ------------------------------------------------------------------ */
|
|
25
|
+
|
|
26
|
+
mb_opt_t *
|
|
27
|
+
rb_minibwa_get_opt(VALUE self)
|
|
28
|
+
{
|
|
29
|
+
mb_opt_t *opt;
|
|
30
|
+
TypedData_Get_Struct(self, mb_opt_t, &rb_minibwa_options_type, opt);
|
|
31
|
+
return opt;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
mb_idx_t *
|
|
35
|
+
rb_minibwa_get_idx(VALUE self)
|
|
36
|
+
{
|
|
37
|
+
mb_idx_t *idx;
|
|
38
|
+
TypedData_Get_Struct(self, mb_idx_t, &rb_minibwa_idx_type, idx);
|
|
39
|
+
return idx;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
mb_tbuf_t *
|
|
43
|
+
rb_minibwa_get_tbuf(VALUE self)
|
|
44
|
+
{
|
|
45
|
+
if (NIL_P(self)) return NULL;
|
|
46
|
+
mb_tbuf_t *b;
|
|
47
|
+
TypedData_Get_Struct(self, mb_tbuf_t, &rb_minibwa_buffer_type, b);
|
|
48
|
+
return b;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* ------------------------------------------------------------------ */
|
|
52
|
+
/* Init */
|
|
53
|
+
/* ------------------------------------------------------------------ */
|
|
54
|
+
|
|
55
|
+
RUBY_FUNC_EXPORTED void
|
|
56
|
+
Init_minibwa(void)
|
|
57
|
+
{
|
|
58
|
+
rb_mMinibwa = rb_define_module("Minibwa");
|
|
59
|
+
|
|
60
|
+
rb_eMinibwaError = rb_define_class_under(rb_mMinibwa, "Error", rb_eStandardError);
|
|
61
|
+
|
|
62
|
+
rb_minibwa_init_options();
|
|
63
|
+
rb_minibwa_init_index();
|
|
64
|
+
rb_minibwa_init_buffer();
|
|
65
|
+
rb_minibwa_init_hit();
|
|
66
|
+
rb_minibwa_init_index_build();
|
|
67
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Shared header for the Ruby binding: module/class handles, the per-file
|
|
3
|
+
* initializers called from Init_minibwa(), and the helpers that cross
|
|
4
|
+
* translation units.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#ifndef RB_MINIBWA_H
|
|
8
|
+
/* NOT "MINIBWA_H": that is upstream's guard. Sharing it would make
|
|
9
|
+
* whichever header is included second expand to nothing. */
|
|
10
|
+
#define RB_MINIBWA_H 1
|
|
11
|
+
|
|
12
|
+
#include "ruby.h"
|
|
13
|
+
#include "ruby/thread.h"
|
|
14
|
+
|
|
15
|
+
/* Upstream public API. Spelled with the directory prefix because this file
|
|
16
|
+
* shares its basename with it, and a bare "minibwa.h" from extension sources
|
|
17
|
+
* resolves to this file first. */
|
|
18
|
+
#include "minibwa/minibwa.h"
|
|
19
|
+
|
|
20
|
+
/* Module, classes and exception, defined in minibwa.c */
|
|
21
|
+
extern VALUE rb_mMinibwa;
|
|
22
|
+
extern VALUE rb_cMinibwaOptions;
|
|
23
|
+
extern VALUE rb_cMinibwaIndex;
|
|
24
|
+
extern VALUE rb_cMinibwaBuffer;
|
|
25
|
+
extern VALUE rb_cMinibwaHit;
|
|
26
|
+
extern VALUE rb_eMinibwaError;
|
|
27
|
+
|
|
28
|
+
/* Per-file initializers, called in this order from Init_minibwa() */
|
|
29
|
+
void rb_minibwa_init_options(void);
|
|
30
|
+
void rb_minibwa_init_index(void);
|
|
31
|
+
void rb_minibwa_init_buffer(void);
|
|
32
|
+
void rb_minibwa_init_hit(void);
|
|
33
|
+
void rb_minibwa_init_index_build(void);
|
|
34
|
+
|
|
35
|
+
/* TypedData unwrapping. Each raises TypeError on a mismatched receiver;
|
|
36
|
+
* rb_minibwa_get_tbuf() maps Qnil to NULL, which mb_map() accepts. */
|
|
37
|
+
mb_opt_t *rb_minibwa_get_opt(VALUE self);
|
|
38
|
+
mb_idx_t *rb_minibwa_get_idx(VALUE self);
|
|
39
|
+
mb_tbuf_t *rb_minibwa_get_tbuf(VALUE self);
|
|
40
|
+
|
|
41
|
+
/* TypedData type descriptors, defined in their respective files */
|
|
42
|
+
extern const rb_data_type_t rb_minibwa_options_type;
|
|
43
|
+
extern const rb_data_type_t rb_minibwa_idx_type;
|
|
44
|
+
extern const rb_data_type_t rb_minibwa_buffer_type;
|
|
45
|
+
|
|
46
|
+
/* mb_hit.c: conversion to Ruby. Both take ownership -- they free hit->p
|
|
47
|
+
* for every hit, and rb_minibwa_hit_ary_new() also frees the array. */
|
|
48
|
+
VALUE rb_minibwa_hit_new(const mb_idx_t *idx, mb_hit_t *hit);
|
|
49
|
+
VALUE rb_minibwa_hit_ary_new(const mb_idx_t *idx, mb_hit_t *hits, int32_t n_hit);
|
|
50
|
+
|
|
51
|
+
#endif /* RB_MINIBWA_H */
|
data/lib/minibwa/hit.rb
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Minibwa
|
|
4
|
+
# A single alignment of one query against the reference.
|
|
5
|
+
#
|
|
6
|
+
# The fields are filled in by mb_hit.c. This file reopens the class for the
|
|
7
|
+
# values that can be derived without touching C: CIGAR formatting, the
|
|
8
|
+
# soft-clipped CIGAR rebuilt from qs/qe -- upstream only reports the aligned
|
|
9
|
+
# region -- the strand character, and #inspect.
|
|
10
|
+
#
|
|
11
|
+
# @!attribute [r] tid
|
|
12
|
+
# @return [Integer] target contig ID.
|
|
13
|
+
# @!attribute [r] ts
|
|
14
|
+
# @return [Integer] zero-based target start.
|
|
15
|
+
# @!attribute [r] te
|
|
16
|
+
# @return [Integer] zero-based target end.
|
|
17
|
+
# @!attribute [r] qs
|
|
18
|
+
# @return [Integer] zero-based query start.
|
|
19
|
+
# @!attribute [r] qe
|
|
20
|
+
# @return [Integer] zero-based query end.
|
|
21
|
+
# @!attribute [r] score
|
|
22
|
+
# @return [Integer] alignment score.
|
|
23
|
+
# @!attribute [r] score0
|
|
24
|
+
# @return [Integer] best-chain score before post-processing.
|
|
25
|
+
# @!attribute [r] mlen
|
|
26
|
+
# @return [Integer] number of matching bases.
|
|
27
|
+
# @!attribute [r] blen
|
|
28
|
+
# @return [Integer] alignment block length.
|
|
29
|
+
# @!attribute [r] mapq
|
|
30
|
+
# @return [Integer] mapping quality.
|
|
31
|
+
# @!attribute [r] cnt
|
|
32
|
+
# @return [Integer] number of seed hits.
|
|
33
|
+
# @!attribute [r] n_sub
|
|
34
|
+
# @return [Integer] number of suboptimal hits.
|
|
35
|
+
# @!attribute [r] subsc
|
|
36
|
+
# @return [Integer] suboptimal alignment score.
|
|
37
|
+
# @!attribute [r] hash
|
|
38
|
+
# @return [Integer] upstream hash value for tie-breaking.
|
|
39
|
+
# @!attribute [r] rev
|
|
40
|
+
# @return [Boolean] whether the hit is on the reverse strand.
|
|
41
|
+
# @!attribute [r] proper_pair
|
|
42
|
+
# @return [Boolean] whether the hit is part of a proper pair.
|
|
43
|
+
# @!attribute [r] sam_pri
|
|
44
|
+
# @return [Boolean] whether the hit is primary in SAM output.
|
|
45
|
+
# @!attribute [r] flt
|
|
46
|
+
# @return [Boolean] whether the hit is filtered.
|
|
47
|
+
# @!attribute [r] inv
|
|
48
|
+
# @return [Boolean] whether the hit represents an inversion.
|
|
49
|
+
# @!attribute [r] split
|
|
50
|
+
# @return [Boolean] whether the hit is split.
|
|
51
|
+
# @!attribute [r] split_inv
|
|
52
|
+
# @return [Boolean] whether the split hit is inverted.
|
|
53
|
+
# @!attribute [r] rescued
|
|
54
|
+
# @return [Boolean] whether paired-end rescue found the hit.
|
|
55
|
+
# @!attribute [r] frac_high
|
|
56
|
+
# @return [Boolean] whether the high-frequency seed fraction is high.
|
|
57
|
+
# @!attribute [r] seed_ratio
|
|
58
|
+
# @return [Boolean] whether the seed ratio flag is set.
|
|
59
|
+
# @!attribute [r] ctg
|
|
60
|
+
# @return [String, nil] target contig name, when available.
|
|
61
|
+
# @!attribute [r] dp_score
|
|
62
|
+
# @return [Integer] dynamic-programming score.
|
|
63
|
+
# @!attribute [r] dp_max0
|
|
64
|
+
# @return [Integer] first dynamic-programming maximum.
|
|
65
|
+
# @!attribute [r] dp_max
|
|
66
|
+
# @return [Integer] dynamic-programming maximum.
|
|
67
|
+
# @!attribute [r] dp_max2
|
|
68
|
+
# @return [Integer] second dynamic-programming maximum.
|
|
69
|
+
# @!attribute [r] n_ambi
|
|
70
|
+
# @return [Integer] number of ambiguous reference bases.
|
|
71
|
+
# @!attribute [r] cs_flag
|
|
72
|
+
# @return [Integer] CS tag flags.
|
|
73
|
+
# @!attribute [r] cigar
|
|
74
|
+
# @return [Array<Array(Integer, Integer)>] CIGAR operations as [length, op] pairs.
|
|
75
|
+
class Hit
|
|
76
|
+
# CIGAR operation characters indexed by operation code.
|
|
77
|
+
CIGAR_STR = 'MIDNSHP=XB'
|
|
78
|
+
|
|
79
|
+
# Returns the CIGAR string for the aligned region (no soft/hard clips).
|
|
80
|
+
def cigar_str
|
|
81
|
+
@cigar.map { |len, op| "#{len}#{CIGAR_STR[op]}" }.join
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Returns the full CIGAR string including soft clips.
|
|
85
|
+
#
|
|
86
|
+
# The query length is required because the Hit only stores the aligned
|
|
87
|
+
# region (qs/qe); the clips are derived from the difference.
|
|
88
|
+
#
|
|
89
|
+
# hit.full_cigar_str(query.length) # => "2S35M3S"
|
|
90
|
+
def full_cigar_str(query_len)
|
|
91
|
+
Sam.build_full_cigar(self, query_len)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Returns the strand as '+' or '-'.
|
|
95
|
+
def strand
|
|
96
|
+
rev ? '-' : '+'
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Returns the target (reference) span length.
|
|
100
|
+
def tlen
|
|
101
|
+
te - ts
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Returns a compact representation of the most commonly inspected fields.
|
|
105
|
+
def inspect
|
|
106
|
+
attrs = %i[tid ctg ts te qs qe strand score mapq mlen blen]
|
|
107
|
+
"#<#{self.class.name} #{attrs.map { |a| "#{a}=#{send(a).inspect}" }.join(' ')}>"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Minibwa
|
|
4
|
+
# A minibwa index -- the FM-index plus the 2-bit reference -- and the object
|
|
5
|
+
# you map reads against.
|
|
6
|
+
#
|
|
7
|
+
# The class comes from mb_index.c and mb_index_build.c; this file reopens it
|
|
8
|
+
# for checking paths and permissions
|
|
9
|
+
# before calling in. That check is not cosmetic: upstream reports bad input
|
|
10
|
+
# to the index builder with kom_assert(), which abort()s the whole process
|
|
11
|
+
# instead of returning an error.
|
|
12
|
+
class Index
|
|
13
|
+
# IMPORTANT: alias the raw C methods BEFORE redefining them.
|
|
14
|
+
# Singleton methods (Index.load, etc.)
|
|
15
|
+
class << self
|
|
16
|
+
# @api private
|
|
17
|
+
alias _build build
|
|
18
|
+
# @api private
|
|
19
|
+
alias _load load
|
|
20
|
+
# @api private
|
|
21
|
+
alias _load_mmap load_mmap
|
|
22
|
+
private :_build, :_load, :_load_mmap
|
|
23
|
+
end
|
|
24
|
+
private_class_method :_build, :_load, :_load_mmap
|
|
25
|
+
|
|
26
|
+
# Instance methods (index.map, index.map_batch)
|
|
27
|
+
# @api private
|
|
28
|
+
alias _map map
|
|
29
|
+
# @api private
|
|
30
|
+
alias _map_batch map_batch
|
|
31
|
+
private :_map, :_map_batch
|
|
32
|
+
|
|
33
|
+
class << self
|
|
34
|
+
# Validates that the FASTA file exists and is readable before calling
|
|
35
|
+
# the C extension (which would abort() on bad input).
|
|
36
|
+
def build(fasta, prefix, **kwargs)
|
|
37
|
+
raise Error, "cannot read FASTA file: #{fasta}" unless File.readable?(fasta)
|
|
38
|
+
|
|
39
|
+
dir = File.dirname(prefix)
|
|
40
|
+
raise Error, "cannot write to directory: #{dir}" unless File.writable?(dir)
|
|
41
|
+
|
|
42
|
+
_build(fasta, prefix, **kwargs)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Wraps Index.load with path validation.
|
|
46
|
+
def load(prefix, **kwargs)
|
|
47
|
+
check_prefix(prefix)
|
|
48
|
+
_load(prefix, **kwargs)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Wraps Index.load_mmap with path validation.
|
|
52
|
+
def load_mmap(prefix, **kwargs)
|
|
53
|
+
check_prefix(prefix)
|
|
54
|
+
_load_mmap(prefix, **kwargs)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def check_prefix(prefix)
|
|
60
|
+
mbw = "#{prefix}.mbw"
|
|
61
|
+
return if File.readable?(mbw)
|
|
62
|
+
|
|
63
|
+
raise Error, "index not found: #{mbw}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Convenience method: map a single sequence with keyword arguments.
|
|
68
|
+
def map(seq, name: nil, opt: nil, buf: nil, meth: 0)
|
|
69
|
+
_map(seq, name: name, opt: opt, buf: buf, meth: meth)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Convenience method: map a batch with keyword arguments.
|
|
73
|
+
def map_batch(seqs, names: nil, opt: nil, buf: nil)
|
|
74
|
+
_map_batch(seqs, names: names, opt: opt, buf: buf)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|