mem-search.cu 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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. #include "mem-search.h"
  28. #include "options.h"
  29. #include "pipeline.h"
  30. #include "util.h"
  31. #include <nvbio/basic/numbers.h>
  32. #include <nvbio/basic/algorithms.h>
  33. #include <nvbio/basic/timer.h>
  34. #include <nvbio/io/sequence/sequence_mmap.h>
  35. #include <thrust/iterator/counting_iterator.h>
  36. #include <thrust/iterator/transform_iterator.h>
  37. #include <thrust/sort.h>
  38. using namespace nvbio;
  39. static io::SequenceData *load_genome(const char *name)
  40. {
  41. if (command_line_options.genome_use_mmap)
  42. {
  43. // try loading via mmap first
  44. io::SequenceDataMMAP *mmap_loader = io::map_sequence_file( name );
  45. if (mmap_loader)
  46. return mmap_loader;
  47. }
  48. // fall back to file name
  49. io::SequenceDataHost *file_loader = io::load_sequence_file( DNA, name );
  50. if (file_loader == NULL)
  51. {
  52. log_error(stderr, "could not load genome %s\n");
  53. exit(1);
  54. }
  55. return file_loader;
  56. }
  57. static io::FMIndexData *load_index(const char *name, uint32 flags)
  58. {
  59. if (command_line_options.genome_use_mmap)
  60. {
  61. // try loading via mmap first
  62. io::FMIndexDataMMAP *mmap_loader = new io::FMIndexDataMMAP();
  63. if (mmap_loader->load(name))
  64. return mmap_loader;
  65. delete mmap_loader;
  66. }
  67. // fall back to file name
  68. io::FMIndexDataHost *file_loader = new io::FMIndexDataHost();
  69. if (!file_loader->load(name, flags))
  70. {
  71. log_error(stderr, "could not load index %s\n");
  72. exit(1);
  73. }
  74. return file_loader;
  75. }
  76. void mem_init(struct pipeline_state *pipeline)
  77. {
  78. // load the genome on the host
  79. pipeline->mem.reference_data_host = load_genome(command_line_options.genome_file_name);
  80. // copy genome data to device
  81. pipeline->mem.reference_data_device = new io::SequenceDataDevice(*pipeline->mem.reference_data_host);
  82. // this specifies which portions of the FM index data to load
  83. const uint32 fm_flags = io::FMIndexData::FORWARD |
  84. io::FMIndexData::REVERSE |
  85. io::FMIndexData::SA;
  86. // load the genome on the host
  87. pipeline->mem.fmindex_data_host = load_index(command_line_options.genome_file_name, fm_flags);
  88. // copy genome data to device
  89. pipeline->mem.fmindex_data_device = new io::FMIndexDataDevice(*pipeline->mem.fmindex_data_host, fm_flags);
  90. pipeline->mem.f_index = pipeline->mem.fmindex_data_device->index();
  91. pipeline->mem.r_index = pipeline->mem.fmindex_data_device->rindex();
  92. }
  93. // search MEMs for all reads in batch
  94. void mem_search(struct pipeline_state *pipeline, const io::SequenceDataDevice *reads)
  95. {
  96. ScopedTimer<float> timer( &pipeline->stats.search_time ); // keep track of the time spent here
  97. struct mem_state *mem = &pipeline->mem;
  98. const uint32 n_reads = reads->size();
  99. // reset the filter
  100. mem->mem_filter = mem_state::mem_filter_type();
  101. const io::SequenceDataAccess<DNA_N> read_access( *reads );
  102. // search for MEMs
  103. mem->mem_filter.rank(
  104. mem->f_index,
  105. mem->r_index,
  106. read_access.sequence_string_set(),
  107. command_line_options.min_intv,
  108. command_line_options.max_intv,
  109. command_line_options.min_seed_len,
  110. command_line_options.split_len,
  111. command_line_options.split_width);
  112. log_verbose(stderr, "%.1f average ranges\n", float(mem->mem_filter.n_ranges()) / float(n_reads));
  113. log_verbose(stderr, "%.1f average MEMs\n", float(mem->mem_filter.n_mems()) / float(n_reads));
  114. optional_device_synchronize();
  115. nvbio::cuda::check_error("mem-search kernel");
  116. }
  117. // given the first read in a chunk, determine a suitably sized chunk of reads
  118. // (for which we can locate all MEMs in one go), updating pipeline::chunk
  119. void fit_read_chunk(
  120. struct pipeline_state *pipeline,
  121. const io::SequenceDataDevice *reads,
  122. const uint32 read_begin) // first read in the chunk
  123. {
  124. const ScopedTimer<float> timer( &pipeline->stats.search_time ); // keep track of the time spent here
  125. struct mem_state *mem = &pipeline->mem;
  126. const uint32 max_hits = command_line_options.mems_batch;
  127. //
  128. // use a binary search to locate the ending read forming a chunk with up to max_hits
  129. //
  130. pipeline->chunk.read_begin = read_begin;
  131. // skip pathological cases
  132. if (mem->mem_filter.n_ranges() == 0)
  133. {
  134. pipeline->chunk.read_end = reads->size();
  135. pipeline->chunk.mem_begin = 0u;
  136. pipeline->chunk.mem_end = 0u;
  137. return;
  138. }
  139. // determine the index of the first hit in the chunk
  140. pipeline->chunk.mem_begin = mem->mem_filter.first_hit( read_begin );
  141. // perform a binary search to find the end of our batch
  142. pipeline->chunk.read_end = nvbio::string_batch_bound( mem->mem_filter, pipeline->chunk.mem_begin + max_hits );
  143. // determine the index of the ending hit in the chunk
  144. pipeline->chunk.mem_end = mem->mem_filter.first_hit( pipeline->chunk.read_end );
  145. // check whether we couldn't produce a non-empty batch
  146. if (pipeline->chunk.mem_begin == pipeline->chunk.mem_end &&
  147. pipeline->chunk.mem_end < mem->mem_filter.n_mems())
  148. {
  149. const uint32 mem_count = mem->mem_filter.first_hit( read_begin+1 ) - pipeline->chunk.mem_begin;
  150. log_error(stderr,"read %u/%u has too many MEMs (%u), exceeding maximum batch size\n", read_begin, reads->size(), mem_count );
  151. exit(1);
  152. }
  153. }
  154. // a functor to extract the reference location from a mem
  155. struct mem_loc_functor
  156. {
  157. typedef mem_state::mem_type argument_type;
  158. typedef uint64 result_type;
  159. NVBIO_HOST_DEVICE
  160. uint64 operator() (const argument_type mem) const
  161. {
  162. return uint64( mem.index_pos() ) | (uint64( mem.string_id() ) << 32);
  163. }
  164. };
  165. // a functor to extract the reference left coordinate from a mem
  166. struct mem_left_coord_functor
  167. {
  168. typedef mem_state::mem_type argument_type;
  169. typedef uint64 result_type;
  170. NVBIO_HOST_DEVICE
  171. uint64 operator() (const argument_type mem) const
  172. {
  173. return uint64( mem.span().x ) | (uint64( mem.string_id() ) << 32);
  174. }
  175. };
  176. // locate all mems in the range defined by pipeline::chunk
  177. void mem_locate(struct pipeline_state *pipeline, const io::SequenceDataDevice *reads)
  178. {
  179. const ScopedTimer<float> timer( &pipeline->stats.locate_time ); // keep track of the time spent here
  180. struct mem_state *mem = &pipeline->mem;
  181. struct chains_state<device_tag> *chn = &pipeline->chn;
  182. if (chn->mems.size() < command_line_options.mems_batch)
  183. {
  184. chn->mems.resize( command_line_options.mems_batch );
  185. chn->mems_index.resize( command_line_options.mems_batch );
  186. chn->mems_chain.resize( command_line_options.mems_batch );
  187. }
  188. const uint32 n_mems = pipeline->chunk.mem_end - pipeline->chunk.mem_begin;
  189. // skip pathological cases
  190. if (n_mems == 0u)
  191. return;
  192. chn->n_mems = n_mems;
  193. mem->mem_filter.locate(
  194. pipeline->chunk.mem_begin,
  195. pipeline->chunk.mem_end,
  196. chn->mems.begin() );
  197. // sort the mems by reference location
  198. nvbio::vector<device_tag,uint64> loc( n_mems );
  199. thrust::transform(
  200. chn->mems.begin(),
  201. chn->mems.begin() + n_mems,
  202. loc.begin(),
  203. mem_left_coord_functor() );
  204. //mem_loc_functor() );
  205. thrust::copy(
  206. thrust::make_counting_iterator<uint32>(0u),
  207. thrust::make_counting_iterator<uint32>(0u) + n_mems,
  208. chn->mems_index.begin() );
  209. // TODO: this is slow, switch to nvbio::cuda::SortEnactor
  210. thrust::sort_by_key(
  211. loc.begin(),
  212. loc.begin() + n_mems,
  213. chn->mems_index.begin() );
  214. optional_device_synchronize();
  215. nvbio::cuda::check_error("mem-locate kernel");
  216. pipeline->stats.n_mems += n_mems; // keep track of the number of mems produced
  217. }