NVBIO
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
mem_inl.h
Go to the documentation of this file.
1 /*
2  * nvbio
3  * Copyright (c) 2011-2014, NVIDIA CORPORATION. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the NVIDIA CORPORATION nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #pragma once
29 
30 namespace nvbio {
31 
32 // find all MEMs covering a given base
33 //
34 // \return the right-most position covered by a MEM
35 //
36 template <typename pattern_type, typename fm_index_type, typename delegate_type>
39  const uint32 pattern_len,
40  const pattern_type pattern,
41  const uint32 x,
42  const fm_index_type f_index,
43  const fm_index_type r_index,
44  delegate_type& handler,
45  const uint32 min_intv,
46  const uint32 min_span)
47 {
48  typedef typename fm_index_type::index_type coord_type;
49  typedef typename fm_index_type::range_type range_type;
50 
51  uint4 right_mems[1024];
52 
53  nvbio::vector_view<uint4*> right_ranges( 0, &right_mems[0] );
54 
55  // find how far can we extend right starting from x
56  //
57  {
58  // extend forward, using the reverse index
59  range_type f_range = make_vector( coord_type(0u), f_index.length() );
60  range_type r_range = make_vector( coord_type(0u), r_index.length() );
61 
62  range_type prev_range = f_range;
63 
64  uint32 i;
65  for (i = x; i < pattern_len; ++i)
66  {
67  const uint8 c = pattern[i];
68  if (c > 3) // there is an N here. no match
69  {
70  prev_range = f_range;
71  break;
72  }
73 
74  // search c in the FM-index
75  extend_forward( f_index, r_index, f_range, r_range, c );
76 
77  // check if the range is too small
78  if (1u + f_range.y - f_range.x < min_intv)
79  break;
80 
81  // store the range
82  if (f_range.y - f_range.x != prev_range.y - prev_range.x)
83  {
84  // do not add the empty span
85  if (i > x)
86  right_ranges.push_back( make_vector( prev_range.x, prev_range.y, coord_type(x), coord_type(i) ) );
87 
88  prev_range = f_range;
89  }
90  }
91  // check if we still need to save one range
92  if (right_ranges.size() && (right_ranges.back().y - right_ranges.back().x) != (prev_range.y - prev_range.x))
93  right_ranges.push_back( make_vector( prev_range.x, prev_range.y, coord_type(x), coord_type(i) ) );
94  }
95 
96  // no valid match covering x
97  if (right_ranges.size() == 0u)
98  return x;
99 
100  // save the result value for later
101  const uint32 rightmost_base = right_ranges.back().w;
102 
103  // now extend backwards, using the forward index
104  //
105  // we basically loop through all MEMs ending in [x,x+n_ranges) starting
106  // from the end of the range and walking backwards - and for each of them we:
107  //
108  // - find their starting point through backwards search
109  //
110  // - and add them to the output only if they extend further left than
111  // any of the previously found ones
112  //
113 
114  // keep track of the left-most coordinate covered by a MEM
115  uint32 leftmost_coordinate = x+1;
116 
117  for (int32 j = right_ranges.size()-1; j >= 0; --j)
118  {
119  range_type f_range = make_vector( right_ranges[j].x, right_ranges[j].y );
120  const uint32 r = right_ranges[j].w;
121 
122  // extend from r to the left as much possible
123  const fm_index_type& index = f_index;
124 
125  int32 l;
126 
127  for (l = int32(x) - 1; l >= 0; --l)
128  {
129  const uint8 c = pattern[l];
130  if (c > 3) // there is an N here. no match
131  break;
132 
133  // search c in the FM-index
134  const range_type c_rank = rank(
135  index,
136  make_vector( f_range.x-1, f_range.y ),
137  c );
138 
139  const range_type new_range = make_vector(
140  index.L2(c) + c_rank.x + 1,
141  index.L2(c) + c_rank.y );
142 
143  // stop if the range became too small
144  if (1u + new_range.y - new_range.x < min_intv)
145  break;
146 
147  // update the range
148  f_range = new_range;
149  }
150 
151  // only output the range if it's not contained in any other MEM
152  if (uint32(l+1) < leftmost_coordinate && uint32(l+1) < r)
153  {
154  // save the range, together with its span
155  const uint2 pattern_span = make_uint2( uint32(l+1), r );
156 
157  // keep the MEM only if it is above a certain length
158  if (pattern_span.y - pattern_span.x >= min_span)
159  {
160  // pass all results to the delegate
161  handler.output( f_range, pattern_span );
162 
163  // update the left-most covered coordinate
164  leftmost_coordinate = uint32(l+1);
165  }
166  }
167  }
168 
169  // return the right-most end of the MEMs covering x
170  return rightmost_base;
171 }
172 
173 // find all k-MEMs covering a given base, for each k
174 //
175 // \return the right-most position covered by a MEM
176 //
177 template <typename pattern_type, typename fm_index_type, typename delegate_type>
180  const uint32 pattern_len,
181  const pattern_type pattern,
182  const uint32 x,
183  const fm_index_type f_index,
184  const fm_index_type r_index,
185  delegate_type& handler,
186  const uint32 min_intv,
187  const uint32 min_span)
188 {
189  typedef typename fm_index_type::index_type coord_type;
190  typedef typename fm_index_type::range_type range_type;
191 
192  uint4 mems1[1024];
193  uint4 mems2[1024];
194 
195  nvbio::vector_view<uint4*> prev( 0, &mems1[0] );
196  nvbio::vector_view<uint4*> curr( 0, &mems2[0] );
197 
198  // find how far can we extend right starting from x
199  //
200  {
201  // extend forward, using the reverse index
202  range_type f_range = make_vector( coord_type(0u), f_index.length() );
203  range_type r_range = make_vector( coord_type(0u), r_index.length() );
204 
205  range_type prev_range = f_range;
206 
207  uint32 i;
208  for (i = x; i < pattern_len; ++i)
209  {
210  const uint8 c = pattern[i];
211  if (c > 3) // there is an N here. no match
212  {
213  prev_range = f_range;
214  break;
215  }
216 
217  // search c in the FM-index
218  extend_forward( f_index, r_index, f_range, r_range, c );
219 
220  // check if the range is too small
221  if (1u + f_range.y - f_range.x < min_intv)
222  break;
223 
224  // store the range
225  if (f_range.y - f_range.x != prev_range.y - prev_range.x)
226  {
227  // do not add the empty span
228  if (i > x)
229  curr.push_back( make_vector( prev_range.x, prev_range.y, coord_type(x), coord_type(i) ) );
230 
231  prev_range = f_range;
232  }
233  }
234  // check if we still need to save one range
235  if (curr.size() && (curr.back().y - curr.back().x) != (prev_range.y - prev_range.x))
236  curr.push_back( make_vector( prev_range.x, prev_range.y, coord_type(x), coord_type(i) ) );
237 
238  // swap prev and curr
239  nvbio::vector_view<uint4*> tmp = prev;
240  prev = curr;
241  curr = tmp;
242  }
243 
244  // no valid match covering x
245  if (prev.size() == 0u)
246  return x;
247 
248  // save the result value for later
249  const uint32 rightmost_base = prev.back().w;
250 
251  uint32 out_n = 0;
252  uint32 out_i = x+1;
253 
254  for (int32 i = int32(x) - 1; i >= -1; --i)
255  {
256  // backward search for MEMs
257  const uint8 c = i < 0 ? 4u : pattern[i]; // c > 3 if i < 0 or pattern[i] is an ambiguous base
258 
259  // reset the output buffer size
260  curr.resize( 0 );
261 
262  for (int32 j = prev.size()-1; j >= 0; --j)
263  {
264  const range_type f_range = make_vector( prev[j].x, prev[j].y );
265  const uint32 r_span = prev[j].w;
266 
267  // search c in the FM-index
268  const range_type c_rank = c < 4u ?
269  rank( f_index, make_vector( f_range.x-1, f_range.y ), c ) :
270  make_vector( coord_type(0), coord_type(0) );
271 
272  const range_type new_range = make_vector(
273  f_index.L2(c) + c_rank.x + 1,
274  f_index.L2(c) + c_rank.y );
275 
276  // keep the hit if reaching the beginning or an ambiguous base or the intv is small enough
277  if (c > 3u || c_rank.y - c_rank.x < min_intv)
278  {
279  // test curr_n > 0 to make sure there are no longer matches
280  if (curr.size() == 0)
281  {
282  if (out_n == 0 || i+1 < out_i) // skip contained matches
283  {
284  // save the range, together with its span
285  const uint2 pattern_span = make_uint2( i+1, r_span );
286 
287  // keep the MEM only if it is above a certain length
288  if (pattern_span.y - pattern_span.x >= min_span)
289  {
290  // pass all results to the delegate
291  handler.output( f_range, pattern_span );
292 
293  out_n++;
294  out_i = i+1;
295  }
296  }
297  } // otherwise the match is contained in another longer match
298  }
299  else if (curr.size() == 0 || (new_range.y - new_range.x) != (curr.back().y - curr.back().x))
300  {
301  // save the range, together with its span
302  curr.push_back( make_vector(
303  new_range.x,
304  new_range.y,
305  coord_type( i+1 ),
306  coord_type( r_span ) ) );
307  }
308  }
309  // check whether there's no more work left
310  if (curr.size() == 0)
311  break;
312 
313  // swap prev and curr
314  nvbio::vector_view<uint4*> tmp = prev;
315  prev = curr;
316  curr = tmp;
317  }
318 
319  // return the right-most end of the MEMs covering x
320  return rightmost_base;
321 }
322 
323 namespace mem {
324 
325 // return the size of a given range
326 template <typename rank_type>
328 {
329  typedef rank_type argument_type;
331 
333  uint64 operator() (const rank_type range) const { return range.range_size(); }
334 };
335 
336 // return the string-id of a given MEM rank
337 template <typename rank_type>
339 {
340  typedef rank_type argument_type;
342 
344  uint32 operator() (const rank_type range) const { return range.z; }
345 };
346 
347 // return the span of a given range
348 template <typename rank_type>
350 {
351  typedef rank_type argument_type;
353 
355  uint64 operator() (const rank_type range) const
356  {
357  return (range.w >> 16u) - (range.w & 0xFFu);
358  }
359 };
360 
361 // a simple mem handler
362 template <typename coord_type>
364 {
365  static const uint32 MAX_SIZE = 1024;
366 
369 
370  // constructor
372  mem_handler(const uint32 _string_id, const uint32 _max_intv) :
373  string_id(_string_id),
374  max_intv(_max_intv),
375  n_mems(0) {}
376 
377  // constructor
379  void output(const range_type range, const uint2 span)
380  {
381  if (n_mems >= MAX_SIZE)
382  return;
383 
384  // check whether the SA range is small enough
385  if (1u + range.y - range.x <= max_intv)
386  {
387  mems[ n_mems++ ] = rank_type(
388  range,
389  string_id,
390  span );
391  }
392  }
393 
398 };
399 
400 template <typename index_type, typename string_set_type>
402 {
403  typedef typename index_type::index_type coord_type;
404  typedef typename index_type::range_type range_type;
406 
407  // constructor
410  const index_type _f_index,
411  const index_type _r_index,
412  const string_set_type _string_set,
413  const uint32 _min_intv,
414  const uint32 _max_intv,
415  const uint32 _min_span,
416  VectorArrayView<mem_type> _mem_arrays) :
417  f_index ( _f_index ),
418  r_index ( _r_index ),
419  string_set ( _string_set ),
420  min_intv ( _min_intv ),
421  max_intv ( _max_intv ),
422  min_span ( _min_span ),
423  mem_arrays ( _mem_arrays ) {}
424 
425  // functor operator
427  void operator() (const uint32 string_id) const
428  {
429  // fetch the pattern
430  typename string_set_type::string_type pattern = string_set[ string_id ];
431 
432  // compute its length
433  const uint32 pattern_len = nvbio::length( pattern );
434 
435  // build a MEM handler
436  mem_handler<coord_type> handler( string_id, max_intv );
437 
438  // and collect all MEMs
439  for (uint32 x = 0; x < pattern_len;)
440  {
441  // find MEMs covering x and move to the next uncovered position along the pattern
442  const uint32 y = find_kmems(
443  pattern_len,
444  pattern,
445  x,
446  f_index,
447  r_index,
448  handler,
449  min_intv,
450  min_span );
451 
452  x = nvbio::max( y, x+1u );
453  }
454 
455  // output the array of results
456  if (handler.n_mems)
457  {
458  mem_type* output = mem_arrays.alloc( string_id, handler.n_mems );
459  if (output != NULL)
460  {
461  // output in reverse order, i.e. sorted by the starting coordinate
462  for (uint32 i = 0; i < handler.n_mems; ++i)
463  output[i] = handler.mems[ handler.n_mems - i - 1u ];
464  }
465  }
466  }
467 
468  const index_type f_index;
469  const index_type r_index;
470  const string_set_type string_set;
475 };
476 
477 // find all MEMs covering a given base
478 //
479 // \return the right-most position covered by a MEM
480 //
481 template <typename pattern_type, typename fm_index_type, typename output_vector>
484  const uint32 pattern_len,
485  const pattern_type pattern,
486  const uint32 string_id,
487  const uint32 x,
488  const fm_index_type f_index,
489  const fm_index_type r_index,
490  output_vector& output,
491  const uint32 min_intv)
492 {
493  typedef typename fm_index_type::index_type coord_type;
494  typedef typename fm_index_type::range_type range_type;
495  typedef MEMRange<coord_type> mem_type;
496 
497  // find how far can we extend right starting from x
498  //
499 
500  // internal output
501  mem_type mems[1024];
502  vector_view<mem_type*> mems_vec( 0u, mems );
503 
504  // extend forward, using the reverse index
505  range_type f_range = make_vector( coord_type(0u), f_index.length() );
506  range_type r_range = make_vector( coord_type(0u), r_index.length() );
507 
508  range_type prev_range = f_range;
509 
510  uint32 i;
511  for (i = x; i < pattern_len; ++i)
512  {
513  const uint8 c = pattern[i];
514  if (c > 3) // there is an N here. no match
515  {
516  prev_range = f_range;
517  break;
518  }
519 
520  // search c in the FM-index
521  extend_forward( f_index, r_index, f_range, r_range, c );
522 
523  // check if the range is too small
524  if (1u + f_range.y - f_range.x < min_intv)
525  break;
526 
527  // store the range
528  if (f_range.y - f_range.x != prev_range.y - prev_range.x)
529  {
530  // do not add the empty span
531  if (i > x)
532  mems_vec.push_back( mem_type( prev_range, string_id, x, i ) );
533 
534  prev_range = f_range;
535  }
536  }
537  // check if we still need to save one range
538  if (mems_vec.size() && (mems_vec.back().range().y - mems_vec.back().range().x) != (prev_range.y - prev_range.x))
539  mems_vec.push_back( mem_type( prev_range, string_id, x, i ) );
540 
541  // no valid match covering x
542  if (mems_vec.size() == 0u)
543  return x;
544 
545  // output them in reverse order, i.e. from largest to smallest
546  for (uint32 i = 0; i < mems_vec.size(); ++i)
547  {
548  mem_type mem = mems_vec[ mems_vec.size() - i - 1u ];
549 
550  // add a special marker to identify the rightmost MEM covering a given position later on
551  if (i == 0)
552  mem.set_group_flag();
553 
554  output.push_back( mem );
555  }
556 
557  // return the rightmost coordinate
558  return mems_vec.back().span().y;
559 }
560 
561 template <typename index_type, typename string_set_type>
563 {
564  typedef typename index_type::index_type coord_type;
565  typedef typename index_type::range_type range_type;
567 
568  // constructor
571  const index_type _f_index,
572  const index_type _r_index,
573  const string_set_type _string_set,
574  const uint32 _min_intv,
575  const uint32 _max_intv,
576  const uint32 _min_span,
577  VectorArrayView<mem_type> _mem_arrays) :
578  f_index ( _f_index ),
579  r_index ( _r_index ),
580  string_set ( _string_set ),
581  min_intv ( _min_intv ),
582  max_intv ( _max_intv ),
583  min_span ( _min_span ),
584  mem_arrays ( _mem_arrays ) {}
585 
586  // functor operator
588  void operator() (const uint32 string_id) const
589  {
590  // fetch the pattern
591  typename string_set_type::string_type pattern = string_set[ string_id ];
592 
593  // compute its length
594  const uint32 pattern_len = nvbio::length( pattern );
595 
596  // build a MEM handler
597  mem_type mems[1024];
598  vector_view<mem_type*> mems_vec( 0u, mems );
599 
600  // and collect all MEMs
601  for (uint32 x = 0; x < pattern_len;)
602  {
603  const uint32 y = right_kmems(
604  pattern_len,
605  pattern,
606  string_id,
607  x,
608  f_index,
609  r_index,
610  mems_vec,
611  min_intv );
612 
613  x = nvbio::max( y, x+1u );
614  }
615 
616  // output the array of results
617  if (mems_vec.size())
618  {
619  mem_type* output = mem_arrays.alloc( string_id, mems_vec.size() );
620  if (output != NULL)
621  {
622  for (uint32 i = 0; i < mems_vec.size(); ++i)
623  output[i] = mems_vec[i];
624  }
625  }
626  }
627 
628  const index_type f_index;
629  const index_type r_index;
630  const string_set_type string_set;
635 };
636 
637 template <typename index_type, typename string_set_type>
639 {
640  typedef typename index_type::index_type coord_type;
641  typedef typename index_type::range_type range_type;
643 
644  // constructor
647  const index_type _f_index,
648  const index_type _r_index,
649  const string_set_type _string_set,
650  const uint32 _split_len,
651  const uint32 _split_width,
652  const uint32 _min_intv,
653  const uint32 _max_intv,
654  const uint32 _min_span,
655  VectorArrayView<mem_type> _in_mem_arrays,
656  VectorArrayView<mem_type> _out_mem_arrays) :
657  f_index ( _f_index ),
658  r_index ( _r_index ),
659  string_set ( _string_set ),
660  split_len ( _split_len ),
661  split_width ( _split_width ),
662  min_intv ( _min_intv ),
663  max_intv ( _max_intv ),
664  min_span ( _min_span ),
665  in_mem_arrays ( _in_mem_arrays ),
666  out_mem_arrays ( _out_mem_arrays ) {}
667 
668  // functor operator
670  void operator() (const uint32 string_id) const
671  {
672  // fetch the pattern
673  typename string_set_type::string_type pattern = string_set[ string_id ];
674 
675  // compute its length
676  const uint32 pattern_len = nvbio::length( pattern );
677 
678  const uint32 n_mems = in_mem_arrays.size( string_id );
679  const mem_type* in_mems = in_mem_arrays[ string_id ];
680 
681  // build a MEM handler
682  mem_type mems[2048];
683  vector_view<mem_type*> mems_vec( 0u, mems );
684 
685  // and collect all MEMs
686  #if 0
687  for (uint32 group_begin = 0; group_begin < n_mems;)
688  {
689  // find the end of the group as well as the longest MEM in the group
690  uint32 group_end;
691  uint32 max_i = 0u;
692  uint32 max = 0u;
693 
694  for (group_end = group_begin; group_end < n_mems; ++group_end)
695  {
696  const mem_type mem = in_mems[ group_end ];
697 
698  if (max <= mem.span().y - mem.span().x)
699  {
700  max = mem.span().y - mem.span().x;
701  max_i = group_end;
702  }
703  if (group_end > group_begin && mem.group_flag())
704  break;
705  }
706 
707  // process all MEMs in the group splitting the longest one
708  for (uint32 i = group_begin; i < group_end; ++i)
709  {
710  const mem_type mem = in_mems[i];
711 
712  const uint32 n_occ = 1u + mem.range().y - mem.range().x;
713 
714  if (i == max_i &&
715  mem.span().y - mem.span().x >= split_len &&
716  n_occ <= split_width)
717  {
718  const uint32 y = right_kmems(
719  pattern_len,
720  pattern,
721  string_id,
722  (mem.span().x + mem.span().y) / 2u,
723  f_index,
724  r_index,
725  mems_vec,
726  n_occ+1u );
727  }
728  else
729  mems_vec.push_back( mem );
730  }
731 
732  // advance to the next group
733  group_begin = group_end;
734  }
735  #else
736  for (uint32 i = 0; i < n_mems; ++i)
737  {
738  const mem_type mem = in_mems[i];
739 
740  if (mem.span().y - mem.span().x >= split_len &&
741  mem.range_size() <= split_width)
742  {
743  const uint32 y = right_kmems(
744  pattern_len,
745  pattern,
746  string_id,
747  (mem.span().x + mem.span().y) / 2u,
748  f_index,
749  r_index,
750  mems_vec,
751  mem.range_size() + 1u );
752  }
753  else
754  mems_vec.push_back( mem );
755  }
756  #endif
757 
758  // output the array of results
759  if (mems_vec.size())
760  {
761  mem_type* output = out_mem_arrays.alloc( string_id, mems_vec.size() );
762  if (output != NULL)
763  {
764  for (uint32 i = 0; i < mems_vec.size(); ++i)
765  output[i] = mems_vec[i];
766  }
767  }
768  }
769 
770  const index_type f_index;
771  const index_type r_index;
772  const string_set_type string_set;
780 };
781 
782 // A functor to extend a MEM to the left
783 //
784 template <typename index_type, typename string_set_type>
786 {
787  typedef typename index_type::index_type coord_type;
788  typedef typename index_type::range_type range_type;
790 
791  // functor typedefs
794 
795  // constructor
798  const index_type _f_index,
799  const index_type _r_index,
800  const string_set_type _string_set,
801  const uint32 _min_intv,
802  const uint32 _max_intv,
803  const uint32 _min_span) :
804  f_index ( _f_index ),
805  r_index ( _r_index ),
806  string_set ( _string_set ),
807  min_intv ( _min_intv ),
808  max_intv ( _max_intv ),
809  min_span ( _min_span ) {}
810 
811  // functor operator
813  mem_type operator() (const mem_type mem) const
814  {
815  range_type f_range = mem.range();
816  const uint32 string_id = mem.string_id();
817  const uint32 x = mem.span().x;
818  const uint32 r = mem.span().y;
819 
820  // fetch the pattern
821  typename string_set_type::string_type pattern = string_set[ string_id ];
822 
823  // extend from x to the left as much possible
824  const index_type& index = f_index;
825 
826  int32 l;
827 
828  for (l = int32(x) - 1; l >= 0; --l)
829  {
830  const uint8 c = pattern[l];
831  if (c > 3) // there is an N here. no match
832  break;
833 
834  // search c in the FM-index
835  const range_type c_rank = rank(
836  index,
837  make_vector( f_range.x-1, f_range.y ),
838  c );
839 
840  const range_type new_range = make_vector(
841  index.L2(c) + c_rank.x + 1,
842  index.L2(c) + c_rank.y );
843 
844  // stop if the range became too small
845  if (1u + new_range.y - new_range.x < min_intv)
846  break;
847 
848  // update the range
849  f_range = new_range;
850  }
851 
852  return mem_type(
853  f_range,
854  string_id,
855  make_uint2( l+1, r ),
856  mem.group_flag() );
857  }
858 
859  const index_type f_index;
860  const index_type r_index;
861  const string_set_type string_set;
865 };
866 
867 template <typename coord_type>
869 {
873 
876 
877  // constructor
880  const uint32 _n_ranges,
881  const uint64* _slots,
882  const rank_type* _ranges) :
883  n_ranges ( _n_ranges ),
884  slots ( _slots ),
885  ranges ( _ranges ) {}
886 
887  // functor operator
889  mem_type operator() (const uint64 output_index) const
890  {
891  // find the text q-gram slot corresponding to this output index
892  const uint32 slot = uint32( upper_bound(
893  output_index,
894  slots,
895  n_ranges ) - slots );
896 
897  // locate the hit position
898  const rank_type range = ranges[ slot ];
899  const uint64 base_slot = slot ? slots[ slot-1 ] : 0u;
900  const uint32 local_index = output_index - base_slot;
901 
902  // and write out the MEM occurrence
903  return mem_type(
904  make_vector(
905  coord_type( range.range().x + local_index ), // SA coordinate for this occurrence
906  coord_type( 0u ), // unused
907  coord_type( range.string_id() ), // string-id
908  range.coords.w ) ); // packed pattern span
909  }
910 
912  const uint64* slots;
914 };
915 
916 
917 template <typename index_type>
919 {
920  typedef typename index_type::index_type coord_type;
921  typedef typename index_type::range_type range_type;
923 
926 
927  // constructor
929  locate_ssa_results(const index_type _index) : index( _index ) {}
930 
931  // functor operator
933  mem_type operator() (const mem_type mem) const
934  {
935  const range_type r = locate_ssa_iterator( index, mem.coords.x );
936 
937  return mem_type(
938  make_vector(
939  coord_type( r.x ),
940  coord_type( r.y ),
941  mem.coords.z,
942  mem.coords.w ) );
943  }
944 
945  const index_type index;
946 };
947 
948 template <typename index_type>
950 {
951  typedef typename index_type::index_type coord_type;
952  typedef typename index_type::range_type range_type;
954 
957 
958  // constructor
960  lookup_ssa_results(const index_type _index) : index( _index ) {}
961 
962  // functor operator
964  mem_type operator() (const mem_type mem) const
965  {
966  const coord_type loc = lookup_ssa_iterator( index, make_vector( mem.coords.x, mem.coords.y ) );
967  return mem_type(
968  loc,
969  uint32( mem.coords.z ),
970  uint32( mem.coords.w ) & 0xFFu,
971  uint32( mem.coords.w ) >> 16u );
972  }
973 
974  const index_type index;
975 };
976 
977 
978 // device kernel to reorder a vector array of mem-ranges
979 template <typename rank_type>
980 __global__
982  const uint32 n_items, // # of input items
983  VectorArrayView<rank_type> ranges, // input vector array
984  const uint32 max_intv, // max SA interval size
985  const uint32 min_span, // minimum pattern span
986  const uint32 reverse) // reverse the output
987 {
988  const uint32 i = threadIdx.x + blockIdx.x * blockDim.x;
989  if (i >= n_items)
990  return;
991 
992  // fetch the i-th vector and discard any MEMs which don't pass our search criteria
993  const uint32 vec_size = ranges.size( i );
994  rank_type* vec = ranges[i];
995 
996  // keep track of the leftmost MEM coordinate
997  uint32 leftmost_coordinate = uint32(-1);
998 
999  uint32 group_begin = 0u;
1000  uint32 n_output = 0u;
1001 
1002  for (uint32 j = 0; j < vec_size; ++j)
1003  {
1004  rank_type rank = vec[j];
1005 
1006  // check the special marker to see if this is the initial MEM for a group
1007  if (rank.group_flag())
1008  {
1009  // reverse the order of the MEMs in the previous group, so so as to sort them by left coordinate
1010  if (reverse)
1011  {
1012  for (uint32 k = 0; k < (n_output - group_begin)/2; ++k)
1013  {
1014  const rank_type tmp = vec[ group_begin + k ];
1015  vec[ group_begin + k ] = vec[ n_output - k - 1u ];
1016  vec[ n_output - k - 1u ] = tmp;
1017  }
1018  }
1019 
1020  // reset the leftmost MEM coordinate
1021  leftmost_coordinate = uint32(-1);
1022 
1023  // reset the group start
1024  group_begin = n_output;
1025  }
1026 
1027  const uint2 range = rank.range();
1028  const uint2 span = rank.span();
1029 
1030  // check whether we want to keep this MEM rank
1031  if (span.x < leftmost_coordinate && // make sure it's not contained in a previous MEM
1032  span.y - span.x >= min_span && // make sure its span is long enough
1033  1u + range.y - range.x <= max_intv) // make sure it doesn't have too many occurrences
1034  {
1035  // make sure the first output entry in each group has the group flag set
1036  if (leftmost_coordinate == uint32(-1))
1037  rank.set_group_flag();
1038 
1039  // output this entry
1040  vec[ n_output++ ] = rank;
1041 
1042  // update the leftmost coordinate
1043  leftmost_coordinate = span.x;
1044  }
1045  }
1046 
1047  // write out the new vector size
1048  ranges.m_sizes[i] = n_output;
1049 }
1050 
1051 // device function to reorder a vector array of mem-ranges
1052 template <typename rank_type>
1054  const device_tag system_tag, // system tag
1055  const uint32 n_items, // # of input items
1056  DeviceVectorArray<rank_type>& ranges, // input vector array
1057  const uint32 max_intv, // max SA interval size
1058  const uint32 min_span, // minimum pattern span
1059  const bool reverse) // reverse the output
1060 {
1061  const uint32 block_dim = 128;
1062  const uint32 n_blocks = util::divide_ri( n_items, block_dim );
1063 
1064  discard_ranges_kernel<<<n_blocks,block_dim>>>(
1065  n_items,
1066  nvbio::plain_view( ranges ),
1067  max_intv,
1068  min_span,
1069  reverse );
1070 
1071  // reset the pool size
1072  {
1073  thrust::device_vector<uint8> d_temp_storage;
1074 
1075  // compute the number of output MEM ranges
1076  // NOTE: this reduction is necessary as discard_ranges might have changed the
1077  // number of ranges effectively in use
1078  const uint32 n_ranges = cuda::reduce(
1079  n_items,
1080  ranges.m_sizes.begin(),
1081  thrust::plus<uint32>(),
1082  d_temp_storage );
1083 
1084  // reset the pool size
1085  ranges.m_pool[0] = n_ranges;
1086  }
1087 }
1088 
1089 // copy the array of ranges bound to index i into the proper position of the output
1090 template <typename rank_type>
1093  const uint32 i, // vector index to copy
1094  const VectorArrayView<rank_type> in_ranges, // input vector array
1095  const uint32* slots, // output slots
1096  vector_view<rank_type*,uint64> out_ranges) // output arena
1097 {
1098  const uint32 slot = slots[i];
1099 
1100  const uint32 n_src = in_ranges.size( i );
1101  const rank_type* src = in_ranges[i];
1102  rank_type* dst = out_ranges + slot;
1103 
1104  for (uint32 j = 0; j < n_src; ++j)
1105  dst[j] = src[j];
1106 }
1107 
1108 // device kernel to reorder a vector array of mem-ranges
1109 template <typename rank_type>
1110 __global__
1112  const uint32 n_items, // # of input items
1113  const VectorArrayView<rank_type> in_ranges, // input vector array
1114  const uint32* slots, // output slots
1115  vector_view<rank_type*,uint64> out_ranges) // output arena
1116 {
1117  const uint32 i = threadIdx.x + blockIdx.x * blockDim.x;
1118  if (i >= n_items)
1119  return;
1120 
1121  copy_ranges( i, in_ranges, slots, out_ranges );
1122 }
1123 
1124 // device function to reorder a vector array of mem-ranges
1125 template <typename rank_type>
1127  const device_tag system_tag, // system tag
1128  const uint32 n_items, // # of input items
1129  const VectorArrayView<rank_type> in_ranges, // input vector array
1130  const uint32* slots, // output slots
1131  vector_view<rank_type*,uint64> out_ranges) // output arena
1132 {
1133  const uint32 block_dim = 128;
1134  const uint32 n_blocks = util::divide_ri( n_items, block_dim );
1135 
1136  reorder_ranges_kernel<<<n_blocks,block_dim>>>(
1137  n_items,
1138  in_ranges,
1139  slots,
1140  out_ranges );
1141 }
1142 // host function to reorder a vector array of mem-ranges
1143 template <typename rank_type>
1145  const host_tag system_tag, // system tag
1146  const uint32 n_items, // # of input items
1147  const VectorArrayView<rank_type> in_ranges, // input vector array
1148  const uint32* slots, // output slots
1149  vector_view<rank_type*,uint64> out_ranges) // output arena
1150 {
1151  #pragma omp parallel for
1152  for (int i = 0; i < int( n_items ); ++i)
1153  copy_ranges( i, in_ranges, slots, out_ranges );
1154 }
1155 
1156 } // namespace mem
1157 
1158 
1159 // enact the filter on an FM-index and a string-set
1160 //
1161 // \param fm_index the FM-index
1162 // \param string-set the query string-set
1163 //
1164 // \return the total number of hits
1165 //
1166 template <typename fm_index_type>
1167 template <typename string_set_type>
1169  const fm_index_type& f_index,
1170  const fm_index_type& r_index,
1171  const string_set_type& string_set,
1172  const uint32 min_intv,
1173  const uint32 max_intv,
1174  const uint32 min_span,
1175  const uint32 split_len,
1176  const uint32 split_width)
1177 {
1178  // save the query
1179  m_n_queries = string_set.size();
1180  m_f_index = f_index;
1181  m_r_index = r_index;
1182  m_n_occurrences = 0;
1183 
1184  const uint32 max_string_length = 256; // TODO: compute this
1185 
1186  m_mem_ranges.resize( m_n_queries, max_string_length * m_n_queries );
1187 
1188  // search the strings in the index, obtaining a set of ranges
1190  thrust::make_counting_iterator<uint32>(0u),
1191  thrust::make_counting_iterator<uint32>(0u) + m_n_queries,
1193  m_f_index,
1194  m_r_index,
1195  string_set,
1196  min_intv,
1197  max_intv,
1198  min_span,
1199  nvbio::plain_view( m_mem_ranges ) )
1200  );
1201 
1202  // fetch the number of output MEM ranges
1203  const uint32 n_ranges = m_mem_ranges.allocated_size();
1204 
1205  // reserve enough storage for the ranges
1206  m_slots.resize( n_ranges );
1207 
1208  if (n_ranges)
1209  {
1210  // reorder the arena by string-id
1211  thrust::host_vector<rank_type> arena( n_ranges );
1212  thrust::host_vector<uint32> slots( m_n_queries );
1213 
1214  // scan the mem-range array sizes to get the new array slots
1216  m_mem_ranges.m_sizes.begin(),
1217  m_mem_ranges.m_sizes.begin() + m_n_queries,
1218  slots.begin() );
1219 
1220  // and put everything in place
1222  host_tag(),
1223  m_n_queries,
1224  nvbio::plain_view( m_mem_ranges ),
1225  nvbio::plain_view( slots ),
1226  nvbio::plain_view( arena ) );
1227 
1228  // swap the new array indices and the arena
1229  m_mem_ranges.m_index.swap( slots );
1230  m_mem_ranges.m_arena.swap( arena );
1231 
1232  // and now scan the range sizes
1234  thrust::make_transform_iterator( m_mem_ranges.m_arena.begin(), mem::range_size<rank_type>() ),
1235  thrust::make_transform_iterator( m_mem_ranges.m_arena.begin(), mem::range_size<rank_type>() ) + n_ranges,
1236  m_slots.begin() );
1237 
1238  // fetch the total number of MEMs
1239  m_n_occurrences = m_slots[ n_ranges - 1u ];
1240  }
1241  return m_n_occurrences;
1242 }
1243 
1244 // find the starting position for the MEM ranks corresponding to a given string
1245 //
1246 template <typename fm_index_type>
1248 {
1249  // fetch the total number of MEM ranges
1250  if (string_id >= m_n_queries)
1251  return m_n_occurrences;
1252 
1253  const uint32 first_rank = m_mem_ranges.m_index[ string_id ];
1254 
1255  // and find the corresponding MEM hits start
1256  return first_rank ? m_slots[ first_rank-1u ] : 0u;
1257 }
1258 
1259 // enumerate all mems in a given range
1260 //
1261 // \tparam mems_iterator a mem_type iterator
1262 //
1263 // \param begin the beginning of the mems sequence to locate, in [0,n_mems)
1264 // \param end the end of the mems sequence to locate, in [0,n_mems]
1265 //
1266 template <typename fm_index_type>
1267 template <typename mems_iterator>
1269  const uint64 begin,
1270  const uint64 end,
1271  mems_iterator mems)
1272 {
1273  const uint32 n_hits = end - begin;
1274 
1275  // fetch the number of output MEM ranges
1276  const uint32 n_ranges = m_mem_ranges.allocated_size();
1277 
1278  // fill the output hits with (SA,string-id) coordinates
1280  thrust::make_counting_iterator<uint64>(0u) + begin,
1281  thrust::make_counting_iterator<uint64>(0u) + end,
1282  mems,
1284  n_ranges,
1285  nvbio::plain_view( m_slots ),
1286  nvbio::plain_view( m_mem_ranges.m_arena ) ) );
1287 
1288  // locate the SSA iterators
1290  mems,
1291  mems + n_hits,
1292  mems,
1294 
1295  // and perform the final lookup
1297  mems,
1298  mems + n_hits,
1299  mems,
1301 }
1302 
1303 // enact the filter on an FM-index and a string-set
1304 //
1305 // \param fm_index the FM-index
1306 // \param string-set the query string-set
1307 //
1308 // \return the total number of hits
1309 //
1310 template <typename fm_index_type>
1311 template <typename string_set_type>
1313  const fm_index_type& f_index,
1314  const fm_index_type& r_index,
1315  const string_set_type& string_set,
1316  const uint32 min_intv,
1317  const uint32 max_intv,
1318  const uint32 min_span,
1319  const uint32 split_len,
1320  const uint32 split_width)
1321 {
1322  // save the query
1323  m_n_queries = string_set.size();
1324  m_f_index = f_index;
1325  m_r_index = r_index;
1326  m_n_occurrences = 0;
1327 
1328  const uint32 max_string_length = 256; // TODO: compute this
1329 
1330  m_mem_ranges.resize( m_n_queries, max_string_length * m_n_queries );
1331 
1332  // search the strings in the index, obtaining a set of ranges
1333  {
1335  thrust::make_counting_iterator<uint32>(0u),
1336  thrust::make_counting_iterator<uint32>(0u) + m_n_queries,
1338  m_f_index,
1339  m_r_index,
1340  string_set,
1341  min_intv,
1342  max_intv,
1343  min_span,
1344  nvbio::plain_view( m_mem_ranges ) )
1345  );
1346 
1347  // fetch the number of output MEM ranges
1348  const uint32 n_ranges = m_mem_ranges.allocated_size();
1349 
1350  // extend them left
1352  m_mem_ranges.m_arena.begin(),
1353  m_mem_ranges.m_arena.begin() + n_ranges,
1354  m_mem_ranges.m_arena.begin(),
1356  m_f_index,
1357  m_r_index,
1358  string_set,
1359  min_intv,
1360  max_intv,
1361  min_span )
1362  );
1363 
1364  const bool do_split = split_len < uint32(-1);
1365 
1366  // and discard MEM ranges we don't want to keep
1368  device_tag(),
1369  m_n_queries,
1370  m_mem_ranges,
1371  max_intv,
1372  min_span,
1373  do_split ? false : true );
1374 
1375  // split long MEMs
1376  if (do_split)
1377  {
1378  DeviceVectorArray<rank_type> unsplit_mem_ranges;
1379 
1380  m_mem_ranges.swap( unsplit_mem_ranges );
1381  m_mem_ranges.resize( m_n_queries, max_string_length * m_n_queries );
1382 
1384  thrust::make_counting_iterator<uint32>(0u),
1385  thrust::make_counting_iterator<uint32>(0u) + m_n_queries,
1387  m_f_index,
1388  m_r_index,
1389  string_set,
1390  split_len,
1391  split_width,
1392  min_intv,
1393  max_intv,
1394  min_span,
1395  nvbio::plain_view( unsplit_mem_ranges ),
1396  nvbio::plain_view( m_mem_ranges ) )
1397  );
1398 
1399  // fetch the number of output MEM ranges
1400  const uint32 n_ranges = m_mem_ranges.allocated_size();
1401 
1402  // extend them left
1404  m_mem_ranges.m_arena.begin(),
1405  m_mem_ranges.m_arena.begin() + n_ranges,
1406  m_mem_ranges.m_arena.begin(),
1408  m_f_index,
1409  m_r_index,
1410  string_set,
1411  min_intv,
1412  max_intv,
1413  min_span )
1414  );
1415 
1416  // and discard MEM ranges we don't want to keep
1418  device_tag(),
1419  m_n_queries,
1420  m_mem_ranges,
1421  max_intv,
1422  min_span,
1423  true );
1424  }
1425  }
1426 
1427  // fetch the number of MEM ranges
1428  const uint32 n_ranges = m_mem_ranges.allocated_size();
1429 
1430  // reserve enough storage for the ranges
1431  m_slots.resize( n_ranges );
1432 
1433  if (n_ranges)
1434  {
1435  // reorder the arena by string-id
1436  thrust::device_vector<rank_type> arena( n_ranges );
1437  thrust::device_vector<uint32> slots( m_n_queries );
1438 
1439  // scan the mem-range array sizes to get the new array slots
1441  m_n_queries,
1442  m_mem_ranges.m_sizes.begin(),
1443  slots.begin(),
1444  thrust::plus<uint32>(),
1445  0u,
1446  d_temp_storage );
1447 
1448  // and put everything in place
1450  device_tag(),
1451  m_n_queries,
1452  nvbio::plain_view( m_mem_ranges ),
1453  nvbio::plain_view( slots ),
1454  nvbio::plain_view( arena ) );
1455 
1456  // swap the new array indices and the arena
1457  m_mem_ranges.m_index.swap( slots );
1458  m_mem_ranges.m_arena.swap( arena );
1459 
1460  // and now scan the range sizes
1462  n_ranges,
1463  thrust::make_transform_iterator( m_mem_ranges.m_arena.begin(), mem::range_size<rank_type>() ),
1464  m_slots.begin(),
1465  thrust::plus<uint64>(),
1466  d_temp_storage );
1467 
1468  // fetch the total number of MEMs
1469  m_n_occurrences = m_slots[ n_ranges - 1u ];
1470  }
1471  return m_n_occurrences;
1472 }
1473 
1474 // find the starting position for the MEM ranks corresponding to a given string
1475 //
1476 template <typename fm_index_type>
1478 {
1479  // fetch the total number of MEM ranges
1480  if (string_id >= m_n_queries)
1481  return m_n_occurrences;
1482 
1483  const uint32 first_rank = m_mem_ranges.m_index[ string_id ];
1484 
1485  // and find the corresponding MEM hits start
1486  return first_rank ? m_slots[ first_rank-1u ] : 0u;
1487 
1488 }
1489 
1490 // enumerate all mems in a given range
1491 //
1492 // \tparam mems_iterator a mem_type iterator
1493 //
1494 // \param begin the beginning of the mems sequence to locate, in [0,n_mems)
1495 // \param end the end of the mems sequence to locate, in [0,n_mems]
1496 //
1497 template <typename fm_index_type>
1498 template <typename mems_iterator>
1500  const uint64 begin,
1501  const uint64 end,
1502  mems_iterator mems)
1503 {
1504  const uint32 n_hits = end - begin;
1505 
1506  // fetch the number of output MEM ranges
1507  const uint32 n_ranges = m_mem_ranges.allocated_size();
1508 
1509  // fill the output hits with (SA,string-id) coordinates
1511  thrust::make_counting_iterator<uint64>(0u) + begin,
1512  thrust::make_counting_iterator<uint64>(0u) + end,
1513  device_iterator( mems ),
1515  n_ranges,
1516  nvbio::plain_view( m_slots ),
1517  nvbio::plain_view( m_mem_ranges.m_arena ) ) );
1518 
1519  // locate the SSA iterators
1521  device_iterator( mems ),
1522  device_iterator( mems ) + n_hits,
1523  device_iterator( mems ),
1525 
1526  // and perform the final lookup
1528  device_iterator( mems ),
1529  device_iterator( mems ) + n_hits,
1530  device_iterator( mems ),
1532 }
1533 
1534 // find the index i of the furthermost string such that filter.first_hit( j ) <= mem_count for each j < i
1535 //
1536 template <typename system_tag, typename fm_index_type>
1538 {
1539  return uint32( thrust::upper_bound(
1540  thrust::make_permutation_iterator(
1541  filter.m_slots.begin(),
1542  filter.m_mem_ranges.m_index.begin() ),
1543  thrust::make_permutation_iterator(
1544  filter.m_slots.begin(),
1545  filter.m_mem_ranges.m_index.begin() ) + filter.m_n_queries,
1546  mem_count ) - thrust::make_permutation_iterator(
1547  filter.m_slots.begin(),
1548  filter.m_mem_ranges.m_index.begin() ) );
1549 }
1550 
1551 } // namespace nvbio